Class: ClassSource::Locator

Inherits:
Object
  • Object
show all
Defined in:
lib/class_source/locator.rb

Overview

A helper class responsible for tracing the evaluation of files to discover class declarations points

Instance Method Summary collapse

Constructor Details

#initialize(target_class, options = {}) ⇒ Locator

Returns a new instance of Locator.



7
8
9
10
# File 'lib/class_source/locator.rb', line 7

def initialize(target_class, options={})
  @klass = target_class
  @options=options
end

Instance Method Details

#dynamic_class_declared(id, classname, file, line) ⇒ Object

A heuristic for seeing that a class has been declared dynamically (e.g. using Class.new)



72
73
74
75
# File 'lib/class_source/locator.rb', line 72

def dynamic_class_declared(id, classname, file, line)
  return unless id == :new && classname == Class 
  File.read(file).lines.to_a[line-1][/[A-Z][\w_:]*/, 0]
end

#evaluate_code_in_a_fork(options) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/class_source/locator.rb', line 43

def evaluate_code_in_a_fork(options)
  t = Tempfile.new('class_creation_events')
  fork do
    declarations = files(options).inject({}) do |declarations, source_file|
      trace_declarations(source_file, declarations)
    end
    YAML.dump(declarations, t)
  end
  Process.wait
  t.close
  Declarations.save YAML.load_file(t.path)
end

#files(options = {}) ⇒ Array

Returns An array of file paths where the class was declared.

Returns:

  • (Array)

    An array of file paths where the class was declared.



24
25
26
27
28
# File 'lib/class_source/locator.rb', line 24

def files(options={})
  @source_files ||= methods.locations.map(&:first).uniq
  return @source_files + [@options[:file]] if @options[:file]
  @source_files
end

#methodsClassSource::MethodIndex

Returns A pointer to the method index for tracking down files.

Returns:



19
20
21
# File 'lib/class_source/locator.rb', line 19

def methods
  MethodIndex.new(@klass)
end

#silence_warningsObject

Need one of these, re-evaluating code is a noisy business



87
88
89
90
91
92
# File 'lib/class_source/locator.rb', line 87

def silence_warnings
  old_verbose, $VERBOSE = $VERBOSE, nil
  yield
ensure
  $VERBOSE = old_verbose
end

#source_locations(options = {}) ⇒ Array

Returns An array of [file_path, line_number] tuples describing where the class was declared.

Returns:

  • (Array)

    An array of [file_path, line_number] tuples describing where the class was declared.



32
33
34
35
36
37
38
39
40
# File 'lib/class_source/locator.rb', line 32

def source_locations(options={})
  return @locations if @locations
  evaluate_code_in_a_fork(options)
  @locations = if !Declarations[@klass.name].nil?
    Declarations[@klass.name].uniq 
  else
    Scanner.new(@klass, files).locations || [] 
  end
end

#standard_class_declared(event, binding) ⇒ Object

A fast way to spot a normal class declaration (e.g. class MyNewClass)



79
80
81
82
83
# File 'lib/class_source/locator.rb', line 79

def standard_class_declared(event, binding)
  return unless event == 'class'
  event_class = eval( "Module.nesting", binding )
  event_class.first
end

#to_aArray

Returns An array of [file_path, line_number] tuples describing where the class was declared.

Returns:

  • (Array)

    An array of [file_path, line_number] tuples describing where the class was declared.



13
14
15
# File 'lib/class_source/locator.rb', line 13

def to_a
  source_locations
end

#trace_declarations(source_file, declarations) ⇒ Object

Traces the evaluation of a file looking for class declarations



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/class_source/locator.rb', line 58

def trace_declarations(source_file, declarations)
  set_trace_func lambda { |event, file, line, id, binding, classname|
    defined_class = standard_class_declared(event, binding) || dynamic_class_declared(id, classname, file, line)
    break unless defined_class
    defined_class_name = defined_class.is_a?(String) ? defined_class : defined_class.name
    declarations[defined_class_name] ||= []
    declarations[defined_class_name] << [ file, line ]
  }
  silence_warnings { load source_file }
  declarations
end