Вопрос

Недавно у нас возникла проблема, когда после серии коммитов не удалось запустить внутренний процесс.Так вот, мы были хорошими маленькими мальчиками и девочками и бежали rake test после каждой регистрации, но из-за некоторых странностей в загрузке библиотеки Rails это происходило только тогда, когда мы запускали ее непосредственно из Mongrel в рабочем режиме.

Я отследил ошибку, и это было связано с тем, что новый Rails gem перезаписал метод в классе String таким образом, что нарушил одно узкое применение в коде Rails среды выполнения.

В любом случае, короче говоря, есть ли способ во время выполнения спросить Ruby, где был определен метод?Что -то вроде whereami( :foo ) это возвращает /path/to/some/file.rb line #45?В этом случае сообщение мне, что оно было определено в классе String, было бы бесполезным, потому что оно было перегружено какой-то библиотекой.

Я не могу гарантировать, что исходный код присутствует в моем проекте, поэтому готовлюсь к 'def foo' не обязательно даст мне то, что мне нужно, не говоря уже о том, есть ли у меня многие def foos, иногда я не знаю до времени выполнения, какой из них я могу использовать.

Это было полезно?

Решение

Это действительно поздно, но вот как вы можете найти, где определен метод:

http://gist.github.com/76951

# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
  def crime
  end
end

class Fixnum
  include Perpetrator
end

p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>

Если вы используете Ruby 1.9+, вы можете использовать <код> source_location

require 'csv'

p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>

CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]

Обратите внимание, что это не сработает во всем, например, в скомпилированном коде. Класс метода также имеет несколько полезных функций, например метод # owner , который возвращает файл, в котором метод определен.

РЕДАКТИРОВАТЬ: см. также __ file __ и __ line __ и примечания для REE в другом ответе, они тоже удобны. - РГ

Другие советы

На самом деле вы можете пойти немного дальше, чем решение выше. Для Ruby 1.8 Enterprise Edition есть методы __ file __ и __ line __ в экземплярах Method :

require 'rubygems'
require 'activesupport'

m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>

m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64

Для Ruby 1.9 и выше есть source_location (спасибо Джонатан!):

require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago>    # comes from the Numeric module

m.source_location   # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]

Я опаздываю в эту ветку и удивляюсь, что никто не упомянул Method # owner .

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A

Копирую мой ответ с более нового похожий вопрос это добавляет новую информацию к этой проблеме.

Рубин 1.9 вызвал ли метод source_location исходное местоположение:

Возвращает исходное имя файла Ruby и номер строки, содержащие этот метод, или nil, если этот метод не был определен в Ruby (т. е.родной)

Это было перенесено обратно в 1.8.7 этим драгоценным камнем:

Таким образом, вы можете запросить этот метод:

m = Foo::Bar.method(:create)

А затем попросите source_location этого метода:

m.source_location

Это вернет массив с именем файла и номером строки.Например, для ActiveRecord::Base#validates это возвращает:

ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

Для классов и модулей Ruby не предлагает встроенной поддержки, но там есть отличная суть, которая основывается на source_location вернуть файл для данного метода или первый файл для класса, если метод не был указан:

В действии:

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

На компьютерах Mac с установленным TextMate при этом также появляется редактор в указанном месте.

Это может помочь, но вам придется кодировать его самостоятельно. Вставлено из блога:

  

Ruby предоставляет метод method_added ()   обратный вызов, который вызывается каждый раз, когда   метод добавлен или переопределен в пределах   учебный класс. Это часть класса Module,   и каждый класс является модулем. Есть   также два связанных обратных вызовов   method_removed () и   method_undefined ().

http://scie.nti.st / 2008/9/17 / приготовления-метода-неизменяемая-в-рубинового

Если вы можете вызвать сбой метода, вы получите обратную трассировку, которая точно скажет вам, где он находится.

К сожалению, если вы не можете разбить его, вы не сможете узнать, где он был определен. Если вы попытаетесь изменить метод, перезаписав его или переопределив, любой сбой произойдет из-за перезаписанного или переопределенного метода, и он не будет использоваться.

Полезные способы сбоя методов:

<Ол>
  • Передайте nil там, где это запрещено - в большинстве случаев метод вызывает ArgumentError или вездесущий NoMethodError ноль класс.
  • Если вы обладаете внутренними знаниями о методе и знаете, что метод в свою очередь вызывает какой-то другой метод, то вы можете перезаписать другой метод и вызвать его внутри.
  • Может быть, тот #source_location может помочь найти, откуда взялся этот метод.

    бывший:

    ModelName.method(:has_one).source_location
    

    Возврат

    [project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
    

    или

    ModelName.new.method(:valid?).source_location
    

    Возврат

    [project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]
    

    Очень поздний ответ :) Но более ранние ответы мне не помогли

    set_trace_func proc{ |event, file, line, id, binding, classname|
      printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
    }
    # call your method
    set_trace_func nil
    

    Возможно, вы смогли бы сделать что-то вроде этого:

    foo_finder.rb:

     class String
       def String.method_added(name)
         if (name==:foo)
            puts "defining #{name} in:\n\t"
            puts caller.join("\n\t")
         end
       end
     end
    

    Затем убедитесь, что foo_finder сначала загружен чем-то вроде

    ruby -r foo_finder.rb railsapp
    

    (Я только возился с rails, так что точно не знаю, но я предполагаю, что есть способ запустить это примерно так.)

    Это покажет вам все переопределения строки #foo .С помощью небольшого метапрограммирования вы могли бы обобщить его для любой функции, которую вы хотите.Но это действительно должно быть загружено ДО файла, который фактически выполняет повторное определение.

    Вы всегда можете узнать, где находитесь, используя caller () .

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top