2008-10-06 18 views
290

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

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

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

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

+1

В Ruby 1.8.7, специальный метод был добавлен специально, чтобы найти эту информацию (и она все еще присутствует в 1.9.3) ... подробнее в моем ответе ниже. – 2012-02-20 04:11:43

ответ

373

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

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) 
#<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] 

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

EDIT: Также см. __file__ и __line__ и примечания для REE в другом ответе, они также удобны. - Рабочая группа

+1

source_location, похоже, работает для 1.8.7-p334, используя activesupport-2.3.14 – MaasSql 2012-07-17 18:03:33

+0

после нахождения метода, попробуйте метод `owner` метода – Juguang 2013-12-03 11:20:27

+0

. Какое число второе в` 2.method (: crime) `? – stack1 2015-03-02 21:24:47

6

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

Ruby provides a method_added() callback that is invoked every time a method is added or redefined within a class. It’s part of the Module class, and every Class is a Module. There are also two related callbacks called method_removed() and method_undefined().

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby

4

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

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

Полезные способы методов разбивающихся:

  1. Pass nil, где он запрещает это - очень много времени метод возбудит ArgumentError или вездесущий NoMethodError на нулевой класс.
  2. Если у вас есть внутреннее знание метода, и вы знаете, что метод, в свою очередь, вызывает какой-то другой метод, тогда вы можете переписать другой метод и поднять его внутри.
+0

Если у вас есть доступ к коду, вы можете так же легко вставить `require 'ruby-debug'; debugger` в вашем коде, где вы хотите зайти. – 2012-10-22 15:23:52

2

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

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 

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

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

2

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

+0

Это полезно, чтобы найти то, что вас зовут, но не хорошо, когда вы пытаетесь найти, где что-то определено. – 2012-10-22 15:21:16

3

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

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 
78

Вы можете на самом деле пойти немного дальше, чем выше решение. Для 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 (спасибо Jonathan!):

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] 
34

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

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

Копирование моего ответа от новее similar question, который добавляет новую информацию к этой проблеме.

Рубин 1,9 имеет метод, называемый source_location:

Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)

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

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

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

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

m.source_location 

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

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

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

В действии:

where_is(ActiveRecord::Base, :validates) 

# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81] 

На Mac с установленным TextMate это также выдает редактор в указанном месте.

3

Возможно, #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]