Получить местоположение класса из объекта класса
У меня есть куча кода для просмотра, и теперь это время отладки. Поскольку я никогда не был поклонником отладчика Ruby, я ищу способ пройти код и прочитать его.
То, что я пытаюсь сделать, это получить местоположение файла, в котором определяется класс загрузки:
Foo::Bar.create(:param) # how can I know file location in runtime?
Для небольших, лучше организованных проектов я бы просто искал class Bar
, но здесь это невозможно, так как существует много классов с именем Bar
, и, что еще хуже, некоторые из них находятся под одним и тем же пространством имен, Я знаю, что это может случиться.
Примечание. Я использую Ruby 1.8.7.
Ответы
Ответ 1
Для Methods
и Procs
Ruby 1.9 имеет метод под названием source_location:
Возвращает исходное имя файла Ruby и номер строки, содержащий этот метод, или nil, если этот метод не определен в Ruby (то есть native)
Итак, вы можете запросить метод:
m = Foo::Bar.method(:create)
И затем попросите source_location
этого метода:
m.source_location
Это вернет массив с именем файла и номером строки.
Например, для 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
для возврата файла для данного метода или первого файла для класса, если не указан метод:
EDIT: Для Ruby 1.8.7 есть жемчуг, который поддерживает source_location
:
Ответ 2
FYI, в консоли Rails или сеансах отладки приложений Rails вы можете узнать расположение диска на диске, где определен этот конкретный класс. как
> show-source Job
это даст вам
From: /home/john/projects/iisifix/app/models/job.rb @ line 13:
Class name: Job
Number of monkeypatches: 6. Use the `-a` option to display all available monkeypatches
Number of lines: 66
class Job < ApplicationRecord
belongs_to :quote_request
belongs_to :garage
Ответ 3
Вот простой пример, показывающий, как я отслеживаю места в коде. Если мне нужно знать местоположение в модуле:
class Foo
attr_reader :initialize_loc
def initialize
@initialize_loc = [__FILE__, __LINE__]
# do more stuff...
end
end
Если мне нужно знать, где что-то произошло:
require_relative 't1'
foo = Foo.new
# do lots of stuff until you want to know where something was initialized.
puts 'foo initialized at %s:%s' % foo.initialize_loc
Когда я запускаю код, я получаю:
FooBar:Desktop foobar ruby t2.rb
foo initilized at /Users/foobar/Desktop/t1.rb:4
Если я не хочу связываться с исходным кодом модуля и хочу, чтобы отладчик переходил, когда мне это нужно, у меня будет отладчик:
require_relative 't1'
require 'ruby-debug'
debugger
foo = Foo.new
# do lots of stuff until you want to know where something was initilized.
puts 'foo initilized at %s:%s' % foo.initialize_loc
Выполнение остановится, и я перейду в отладчик в строке сразу после debugger
:
[0, 9] in t2.rb
1 require_relative 't1'
2 require 'ruby-debug'
3
4 debugger
=> 5 foo = Foo.new
6 # do lots of stuff until you want to know where something was initilized.
7 puts 'foo initilized at %s:%s' % foo.initialize_loc
8
t2.rb:5
foo = Foo.new
(rdb:1)
Простой s
"переместит" меня в следующую строку кода, которая будет в блоке initialize
для Foo
:
(rdb:1) s
[-1, 8] in /Users/foobar/Desktop/t1.rb
1 class Foo
2 attr_reader :initialize_loc
3 def initialize
=> 4 @initialize_loc = [__FILE__, __LINE__]
5 # do more stuff...
6 end
7 end
8
/Users/foobar/Desktop/t1.rb:4
@initialize_loc = [__FILE__, __LINE__]
(rdb:1)
Помимо этого, используя инструменты, такие как grep -rn target_to_find path_to_search
, чтобы рекурсивно искать каталоги и перечислить имена файлов и номера строк, соответствующих цели, помогут вам найти то, что вы ищете.
Или, используя :vim /target_to_find/ path_to_search
изнутри Vim, вернет файлы, которые вы ищете.
Ответ 4
Плохая новость! Я предполагаю, что во время выполнения нет способа узнать, какой файл создает или определяет класс в Ruby 1.8.7.
Если проект имеет некоторую структуру, такую как рельсы, вы сможете угадать его.
Но в Ruby несколько файлов могут определять методы для одного и того же класса
Класс может быть определен даже во время выполнения (метапрограммирование).
Это означает, что может быть более одного места, где определяется класс.
И то, что вы ищете, может распространяться более чем на один файл.
Думаю, вам придется искать все определения Bar и посмотреть, находятся ли они внутри модуля Foo, или начать с поиска всех определений Foo и проверить, что внутри.
Если код беспорядок, я не вижу простого пути, вам придется следовать точке формы spaguetti для poi.
Хороший редактор и многократный поиск файлов могут помочь, но вам нужно будет прочитать код.
EDIT: Некоторые хорошие новости. В Ruby 1.9 есть source_location
и выглядит как backport of it for 1.8.7. Однако, если определение было сделано во время выполнения с помощью eval или я не уверен, что это сработает. Я думаю, что самым простым решением является хороший редактор, такой как Rubymine, который обычно может указать вам, где был определен код.
Ответ 5
Откровенно говоря, учитывая вашу описанную организацию кода, я думаю, что ruby-debug - это простой путь к обнаружению места назначения вашего сайта: просто установите точку останова и входите. Если у вас действительно аллергия на отладчик, вы можете использовать инструмент сайт вызова с Kernel#set_trace_func
.
$max_trace = 10
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
$max_trace -= 1
set_trace_func(nil) unless $max_trace > 0
}