Jak znaleźć, gdzie metoda jest zdefiniowana w czasie wykonywania?
Ostatnio pojawił się problem, w którym po wystąpieniu serii commitów proces backendu nie został uruchomiony. Byliśmy grzecznymi chłopcami i dziewczynkami i biegaliśmy rake test
po każdej odprawie, ale ze względu na pewne dziwactwa w ładowaniu biblioteki Rails, stało się to tylko wtedy, gdy uruchomiliśmy ją bezpośrednio z Kundla w trybie produkcyjnym.
Wyśledziłem błąd i było to spowodowane nowym gem Rails nadpisującym metodę w klasie String w sposób, który złamał jedno wąskie użycie w runtime Rails kod.
W każdym razie, krótko mówiąc, czy istnieje sposób, aby zapytać Rubiego, gdzie została zdefiniowana metoda? Coś w stylu whereami( :foo )
, który zwraca /path/to/some/file.rb line #45
? W tym przypadku powiedzenie mi, że jest zdefiniowany w łańcuchu klasy byłoby nieprzydatne, ponieważ zostało przeciążone przez jakąś bibliotekę.
Nie mogę zagwarantować, że źródło żyje w moim projekcie, więc granie dla 'def foo'
niekoniecznie da mi to, czego potrzebuję, nie wspominając o tym, czy mam Wiele def foo
's, czasami Nie wiem aż do czasu uruchomienia, które może używam.
10 answers
Jest to naprawdę późno, ale oto jak możesz znaleźć, gdzie zdefiniowana jest metoda:
# 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>
Jeśli jesteś na Ruby 1.9+, możesz użyć 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]
Zauważ, że to nie będzie działać na wszystkim, tak jak natywny skompilowany kod. Klasa metody ma również kilka ciekawych funkcji, takich jak metoda # owner, która zwraca plik, w którym jest zdefiniowana metoda.
EDIT: Zobacz też __file__
i __line__
oraz notatki dla REE w druga odpowiedź, są też przydatne. -- wg
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 03:34:36
Można rzeczywiście pójść nieco dalej niż rozwiązanie powyżej. Dla Ruby 1.8 Enterprise Edition istnieją metody __file__
i __line__
na instancjach 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
Dla Ruby 1.9 i dalej, jest source_location
(dzięki 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]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-15 10:20:18
Spóźnię się na ten wątek i jestem zaskoczony, że nikt nie wspomniał o Method#owner
.
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-20 04:07:21
Skopiowanie mojej odpowiedzi z nowszego podobnego pytania , które dodaje nowe informacje do tego problemu.
Ruby 1.9 has method called source_location :
Zwraca źródłową nazwę pliku Ruby i numer linii zawierający tę metodę lub nil, jeśli ta metoda nie została zdefiniowana w Ruby (np. natywny)
To zostało przeniesione do 1.8.7 przez ten klejnot:
Więc ty można zażądać metody:
m = Foo::Bar.method(:create)
A następnie poproś o source_location
tej metody:
m.source_location
Zwróci tablicę z nazwą pliku i numerem linii.
Np. ActiveRecord::Base#validates
zwraca:
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]
Dla klas i modułów, Ruby Nie oferuje wbudowanego wsparcia, ale istnieje doskonały Gist, który opiera się na source_location
, aby zwrócić plik dla danej metody lub pierwszy plik dla klasy, jeśli żadna metoda nie została określona:
W akcji:
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]
Na komputerach Mac z zainstalowanym programem TextMate pojawia się również edytor w podanej lokalizacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:14
To może pomóc, ale będziesz musiał sam to zakodować. Wklejony z bloga:
Ruby dostarcza method_added() wywołanie zwrotne, które jest wywoływane za każdym razem, gdy metoda jest dodawana lub redefiniowana w ramach klasy. To część klasy Module, a każda klasa jest modułem. Są również dwa powiązane wywołania zwane method_removed () oraz method_undefined ().
Http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 20:01:29
Jeśli uda Ci się rozbić metodę, otrzymasz backtrace, który powie Ci dokładnie, gdzie ona jest.
Niestety, jeśli nie możesz go rozbić, nie możesz dowiedzieć się, gdzie został zdefiniowany. Jeśli spróbujesz użyć metody przez nadpisanie jej lub nadpisanie, każda awaria będzie wynikać z nadpisanej lub nadpisanej metody i nie będzie ona w żadnym wypadku użyta.
Użyteczne sposoby rozbijania metod:
- Pass
nil
where it zakazuje-często metoda wychowaArgumentError
lub zawsze obecnyNoMethodError
na zerowej klasie. - Jeśli posiadasz wewnętrzną wiedzę na temat metody i wiesz, że metoda z kolei wywołuje inną metodę, możesz ją nadpisać i podnieść wewnątrz niej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 15:22:03
Być może #source_location
może pomóc dowiedzieć się, skąd pochodzi metoda.
Ex:
ModelName.method(:has_one).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
Lub
ModelName.new.method(:valid?).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 15:11:15
Bardzo późna odpowiedź :) ale wcześniejsze odpowiedzi mi nie pomogły
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-14 18:36:18
Możesz zrobić coś takiego:
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
Następnie upewnij się, że foo_finder jest załadowany jako pierwszy z czymś takim jak
ruby -r foo_finder.rb railsapp
(tylko namieszałem w railach, więc nie wiem dokładnie, ale wyobrażam sobie, że jest sposób, aby zacząć to tak.)
To pokaże Ci wszystkie re-definicje łańcucha # foo. Z odrobiną meta-programowania, możesz uogólnić go dla dowolnej funkcji, którą chcesz. Ale musi być załadowany przed plikiem, który właściwie to zmienia definicję.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 20:16:47
Zawsze możesz sprawdzić, gdzie jesteś, używając caller()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-31 23:34:37