Domanda

Di recente abbiamo riscontrato un problema a causa del quale non è stato possibile eseguire un processo di back-end dopo una serie di commit. Eravamo bravi ragazzini e abbiamo eseguito rake test dopo ogni check-in ma, a causa di alcune stranezze nel caricamento della libreria di Rails, si è verificato solo quando l'abbiamo eseguito direttamente da Mongrel in modalità di produzione .

Ho rintracciato il bug ed era dovuto a una nuova gemma di Rails che sovrascriveva un metodo nella classe String in un modo che ha interrotto un uso limitato nel codice di runtime di Rails.

Comunque, per farla breve, c'è un modo, in fase di esecuzione, di chiedere a Ruby dove è stato definito un metodo? Qualcosa come whereami (: foo) che restituisce /path/to/some/file.rb line # 45 ? In questo caso, dirmi che è stato definito nella classe String non sarebbe utile, perché era sovraccarico di alcune librerie.

Non posso garantire la vita dei sorgenti nel mio progetto, quindi cercare 'def foo' non mi darà necessariamente ciò di cui ho bisogno, per non parlare se ne ho molti def foo , a volte non so fino al momento dell'esecuzione quale potrei usare.

È stato utile?

Soluzione

È molto tardi, ma ecco come puoi trovare dove è definito un metodo:

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>

Se usi Ruby 1.9+, puoi usare 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]

Nota che questo non funzionerà su tutto, come il codice compilato nativo. La classe del metodo ha anche alcune funzioni ordinate, come Metodo # proprietario che restituisce il file dove il metodo è definito.

EDIT: vedi anche __file__ e __line__ e le note per REE nell'altra risposta, sono anche utili. - wg

Altri suggerimenti

Puoi effettivamente andare un po 'oltre la soluzione sopra. Per Ruby 1.8 Enterprise Edition, esistono i metodi __file__ e __line__ sulle istanze 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

Per Ruby 1.9 e oltre, esiste source_location (grazie 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]

Sto arrivando tardi a questa discussione e sono sorpreso che nessuno abbia menzionato Metodo # proprietario .

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

Copia della mia risposta da una nuova domanda simile che aggiunge nuove informazioni a questo problema.

Ruby 1.9 ha un metodo chiamato posizione_origine :

  

Restituisce il nome del file di origine Ruby e il numero di riga contenente questo metodo o zero se questo metodo non è stato definito in Ruby (ovvero nativo)

Questo è stato riportato in 1.8.7 da questo gioiello:

Quindi puoi richiedere il metodo:

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

E poi chiedi il source_location di quel metodo:

m.source_location

Questo restituirà un array con nome file e numero riga. Ad esempio per ActiveRecord :: Base # convalida questo restituisce:

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]

Per le classi e i moduli, Ruby non offre supporto integrato, ma esiste un eccellente Gist che si basa su source_location per restituire il file per un determinato metodo o il primo file per una classe se no metodo specificato:

In azione:

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]

Su Mac con TextMate installato, questo fa apparire l'editor nella posizione specificata.

Questo può aiutare ma dovresti codificarlo tu stesso. Incollato dal blog:

  

Ruby fornisce un method_added ()   callback che viene invocato ogni volta a   il metodo viene aggiunto o ridefinito in a   classe. Fa parte della classe Module,   e ogni classe è un modulo. Ci sono   chiamati anche due callback correlati   method_removed () e   method_undefined ().

http://scie.nti.st / 2008/9/17 / making-metodi-immutabile-in-ruby

Se riesci a bloccare il metodo, otterrai una backtrace che ti dirà esattamente dove si trova.

Sfortunatamente, se non riesci a bloccarlo, non puoi scoprire dove è stato definito. Se provi a scimmiottare con il metodo sovrascrivendolo o sovrascrivendolo, qualsiasi arresto proviene dal tuo metodo sovrascritto o sovrascritto e non sarà di alcuna utilità.

Metodi utili per bloccare i metodi:

  1. Passa nil dove lo proibisce - molte volte il metodo genererà un ArgumentError o il sempre presente NoMethodError su un classe zero.
  2. Se hai una conoscenza approfondita del metodo e sai che il metodo a sua volta chiama qualche altro metodo, puoi sovrascrivere l'altro metodo e sollevare al suo interno.

Forse il #source_location può aiutare a trovare da dove viene il metodo.

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]

o

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

Ritorna

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

Risposta molto tardi :) Ma le risposte precedenti non mi hanno aiutato

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

Potresti essere in grado di fare qualcosa del genere:

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

Quindi assicurati che foo_finder sia caricato prima con qualcosa come

ruby -r foo_finder.rb railsapp

(Ho solo incasinato le rotaie, quindi non so esattamente, ma immagino che ci sia un modo per avviarlo in questo modo.)

Questo ti mostrerà tutte le ridefinizioni di String # foo. Con un po 'di meta-programmazione, potresti generalizzarlo per qualunque funzione tu voglia. Ma deve essere caricato PRIMA del file che esegue effettivamente la ridefinizione.

Puoi sempre avere una traccia di dove ti trovi usando caller () .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top