Gostaria de saber se posso obter o código-fonte de um método em tempo real e se posso obter em qual arquivo esse método está.
gostar
A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE
Respostas:
Use source_location
:
class A
def foo
end
end
file, line = A.instance_method(:foo).source_location
# or
file, line = A.new.method(:foo).source_location
puts "Method foo is defined in #{file}, line #{line}"
# => "Method foo is defined in temp.rb, line 2"
Observe que para métodos embutidos, source_location
retorna nil
. Se quiser verificar o código-fonte C (divirta-se!), Você terá que procurar o arquivo C correto (eles são mais ou menos organizados por classe) e encontrar o rb_define_method
para o método (no final do arquivo )
No Ruby 1.8, este método não existe, mas você pode usar esta joia .
Nenhuma das respostas até agora mostra como exibir o código-fonte de um método em tempo real ...
Na verdade, é muito fácil se você usar a incrível joia 'method_source' de John Mair (o criador do Pry): O método deve ser implementado em Ruby (não em C) e deve ser carregado de um arquivo (não em irb).
Aqui está um exemplo exibindo o código-fonte do método no console Rails com method_source:
$ rails console
> require 'method_source'
> I18n::Backend::Simple.instance_method(:lookup).source.display
def lookup(locale, key, scope = [], options = {})
init_translations unless initialized?
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
keys.inject(translations) do |result, _key|
_key = _key.to_sym
return nil unless result.is_a?(Hash) && result.has_key?(_key)
result = result[_key]
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
result
end
end
=> nil
Veja também:
source
. Isso funciona conforme o esperado.
[1] pry(main)> RSpec.method(:class_exec).source
MethodSource::SourceNotFoundError: Could not locate source for class_exec!
from /home/vagrant/.bundle/foo/ruby/2.5.0/gems/method_source-0.9.2/lib/method_source.rb:24:in `source_helper'
RSpec.method(:to_json).source_location
funciona bem
Aqui está como imprimir o código-fonte do Ruby:
puts File.read(OBJECT_TO_GET.method(:METHOD_FROM).source_location[0])
Sem dependências
method = SomeConstant.method(:some_method_name)
file_path, line = method.source_location
# puts 10 lines start from the method define
IO.readlines(file_path)[line-1, 10]
Se você quiser usar isso de forma mais conveniente, pode abrir a Method
classe:
# ~/.irbrc
class Method
def source(limit=10)
file, line = source_location
if file && line
IO.readlines(file)[line-1,limit]
else
nil
end
end
end
E então é só ligar method.source
Com o Pry você pode usar o show-method
para ver o código-fonte de um método, e você pode até mesmo ver algum código-fonte ruby c com pry-doc
instalado, de acordo com o documento do pry em codde-browing
Observe que também podemos visualizar métodos C (do Ruby Core) usando o plugin pry-doc; também mostramos a sintaxe alternativa para o método show:
pry(main)> show-method Array#select From: array.c in Ruby Core (C Method): Number of lines: 15 static VALUE rb_ary_select(VALUE ary) { VALUE result; long i; RETURN_ENUMERATOR(ary, 0, 0); result = rb_ary_new2(RARRAY_LEN(ary)); for (i = 0; i < RARRAY_LEN(ary); i++) { if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) { rb_ary_push(result, rb_ary_elt(ary, i)); } } return result; }
source
método dentro da Method
classe. Seria ainda melhor se processasse o texto e novo quando parar de imprimir porque chegou ao fim do método.
Eu criei a gema "ri_for" para este propósito
>> require 'ri_for'
>> A.ri_for :foo
... produz a fonte (e localização, se você estiver no 1.9).
GL. -r
Tive que implementar um recurso semelhante (pegar a fonte de um bloco) como parte do Wrong e você pode ver como (e talvez até mesmo reutilizar o código) em chunk.rb (que depende do RubyParser de Ryan Davis, bem como de alguns código de glomming do arquivo fonte ). Você teria que modificá-lo para usar Method#source_location
e talvez ajustar algumas outras coisas para que inclua ou não o def
.
BTW, acho que o Rubinius tem esse recurso embutido. Por algum motivo, ele foi deixado de fora do MRI (a implementação padrão do Ruby), daí este hack.
Oooh, eu gosto de algumas das coisas em method_source ! É como usar eval para dizer se uma expressão é válida (e continuar glomming as linhas de origem até parar de receber erros de análise, como o Chunk faz) ...
String#include?
. Até agoraString.instance_method(:include?).source_location
retornanil
.