Получить местоположение класса из объекта класса

У меня есть куча кода для просмотра, и теперь это время отладки. Поскольку я никогда не был поклонником отладчика 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
}