Class: Hometown::CreationTracer

Inherits:
Object
  • Object
show all
Defined in:
lib/hometown/creation_tracer.rb

Constant Summary collapse

HOMETOWN_TRACE_ON_INSTANCE =
:@__hometown_creation_backtrace

Instance Method Summary collapse

Constructor Details

#initializeCreationTracer

Returns a new instance of CreationTracer.



3
4
5
# File 'lib/hometown/creation_tracer.rb', line 3

def initialize
  @tracing_classes = {}
end

Instance Method Details

#add_trace_for(instance) ⇒ Object



48
49
50
51
# File 'lib/hometown/creation_tracer.rb', line 48

def add_trace_for(instance)
  trace = Hometown::Trace.new(instance.class, scrubbed_caller)
  instance.instance_variable_set(HOMETOWN_TRACE_ON_INSTANCE, trace)
end

#find_trace_for(instance) ⇒ Object



58
59
60
# File 'lib/hometown/creation_tracer.rb', line 58

def find_trace_for(instance)
  instance.instance_variable_get(HOMETOWN_TRACE_ON_INSTANCE)
end

#install_traced_new(clazz) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/hometown/creation_tracer.rb', line 31

def install_traced_new(clazz)
  clazz.instance_eval do
    class << self
      def new_traced(*args, &blk)
        instance = new_untraced(*args, &blk)
        self.instance_hooks.each { |hook| hook.call(instance) }
        instance
      end

      alias_method :new_untraced, :new
      alias_method :new, :new_traced
    end
  end
end

#on_creation_add_trace_for_instance(clazz) ⇒ Object



27
28
29
# File 'lib/hometown/creation_tracer.rb', line 27

def on_creation_add_trace_for_instance(clazz)
  update_on_instance_created(clazz, method(:add_trace_for))
end

#patch(clazz, other_instance_hook = nil) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
# File 'lib/hometown/creation_tracer.rb', line 7

def patch(clazz, other_instance_hook=nil)
  if !patched?(clazz)
    remember_patched(clazz)
    on_creation_add_trace_for_instance(clazz)
    install_traced_new(clazz)
  end

  # Critical that we only add other instance hooks after our primary
  # creation hook is registered above!
  update_on_instance_created(clazz, other_instance_hook)
end

#patched?(clazz) ⇒ Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/hometown/creation_tracer.rb', line 19

def patched?(clazz)
  @tracing_classes.include?(clazz)
end

#remember_patched(clazz) ⇒ Object



23
24
25
# File 'lib/hometown/creation_tracer.rb', line 23

def remember_patched(clazz)
  @tracing_classes[clazz] = true
end

#scrubbed_callerObject



53
54
55
56
# File 'lib/hometown/creation_tracer.rb', line 53

def scrubbed_caller
  backtrace = caller.dup
  backtrace.reject { |line| %r{/lib/hometown/creation_tracer.rb}.match(line) }
end

#update_on_instance_created(clazz, on_instance_created) ⇒ Object

This hook allows other tracing in Hometown to get a whack at an object after it’s been created without forcing them to patch new themselves



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/hometown/creation_tracer.rb', line 64

def update_on_instance_created(clazz, on_instance_created)
  return unless on_instance_created
  clazz.instance_eval do
    def instance_hooks
      hooks = (self.ancestors + [self]).map do |target|
        target.instance_variable_get(:@instance_hooks)
      end

      hooks.flatten!
      hooks.compact!
      hooks.uniq!
      hooks
    end

    @instance_hooks ||= []
    @instance_hooks << on_instance_created
  end
end