Module: ObjectTracker

Defined in:
lib/object_tracker.rb,
lib/object_tracker/version.rb,
lib/object_tracker/tracker_method.rb

Defined Under Namespace

Classes: TrackerMethod

Constant Summary collapse

VERSION =
'2.0.0'.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_tracker_mod(trackers, mod: Module.new, before: nil, after: nil) ⇒ Object

Parameters:

  • trackers (Array<TrackerMethod>)
  • :mod (Hash)

    a customizable set of options

  • :before (Hash)

    a customizable set of options

  • :after (Hash)

    a customizable set of options



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/object_tracker.rb', line 76

def self.build_tracker_mod(trackers, mod: Module.new, before: nil, after: nil)
  ObjectTracker.tracker_hooks[:before] << before if before
  ObjectTracker.tracker_hooks[:after] << after if after
  Array(trackers).each do |tracker|
    mod.module_eval "      def \#{tracker.name}(*args)\n        ObjectTracker.call_tracker_hooks(:before, \"\#{tracker.display_name}\", self, *args)\n        result, message = ObjectTracker.call_with_tracking(\"\#{tracker.display_name}\", args, \"\#{tracker.source}\") { super }\n        puts message\n        result\n      ensure\n        ObjectTracker.call_tracker_hooks(:after, \"\#{tracker.display_name}\", self, *args)\n      end\n    RUBY\n  end\n  mod\nend\n", __FILE__, __LINE__

.call(obj, method_names = [], except: [], **options) ⇒ Object

Note:

Alias to .()

Utilities (not extended or mixed in)

Tracks method calls to the given object

Parameters:

  • obj (Object)

    class or instance to track

  • method_names (Array<Symbol>) (defaults to: [])

    method names to track

  • :except (Hash)

    a customizable set of options

  • :before (Hash)

    a customizable set of options

  • :after (Hash)

    a customizable set of options



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/object_tracker.rb', line 37

def self.call(obj, method_names = [], except: [], **options)
  class_methods = []
  inst_methods = []
  reserved = obj.respond_to?(:reserved_tracker_methods) ? obj.reserved_tracker_methods : ObjectTracker.reserved_tracker_methods
  obj_instance = obj.respond_to?(:allocate) ? obj.allocate : obj
  if Array(method_names).any?
    Array(method_names).each do |method_name|
      if obj.methods.include?(method_name)
        class_methods << TrackerMethod.new(obj, method_name)
      elsif obj.respond_to?(:instance_method)
        inst_methods << TrackerMethod.new(obj_instance, method_name)
      end
    end
  else
    if obj.respond_to?(:instance_methods)
      (obj.instance_methods - reserved - Array(except)).each do |method_name|
        inst_methods << TrackerMethod.new(obj_instance, method_name)
      end
    end
    (obj.methods - reserved - Array(except)).each do |method_name|
      class_methods << TrackerMethod.new(obj, method_name)
    end
  end
  obj.send :extend, ObjectTracker.define_tracker_mod(obj, :TrackerExt, ObjectTracker.build_tracker_mod(class_methods, options))
  if inst_methods.any?
    obj.send :prepend, ObjectTracker.define_tracker_mod(obj, :InstanceTrackerExt, ObjectTracker.build_tracker_mod(inst_methods, options))
  end
  obj
end

.call_tracker_hooks(key, method_name, context, *args) ⇒ Object

Note:

If we don’t rescue, watch out for segfaults o.0



95
96
97
98
99
# File 'lib/object_tracker.rb', line 95

def self.call_tracker_hooks(key, method_name, context, *args)
  tracker_hooks[key].each do |hook|
    hook.call(context, method_name, *args) rescue nil
  end
end

.call_with_tracking(method_name, args, source) ⇒ Object



101
102
103
104
105
106
107
108
109
110
# File 'lib/object_tracker.rb', line 101

def self.call_with_tracking(method_name, args, source)
  result = nil
  msg = %Q(   * called "#{method_name}" )
  msg << "with " << ObjectTracker.format_args(args) unless args.empty?
  msg << "[#{source}]"
  bm = Benchmark.measure do
    result = yield rescue nil
  end
  [result, msg << " (%.5f)" % bm.real]
end

.define_tracker_mod(context, name, mod) ⇒ Object



67
68
69
70
# File 'lib/object_tracker.rb', line 67

def self.define_tracker_mod(context, name, mod)
  context = context.class unless context.respond_to?(:const_set)
  context.const_set name, mod
end

.format_args(args) ⇒ Object



112
113
114
115
116
117
118
119
120
# File 'lib/object_tracker.rb', line 112

def self.format_args(args)
  result = "["
  args.each do |arg|
    result << (arg ? arg.to_s : "nil")
    result << ", "
  end
  result.sub! /,\s\z/, ""
  result << "] "
end

.reserved_tracker_methodsObject



17
18
19
20
21
22
23
# File 'lib/object_tracker.rb', line 17

def reserved_tracker_methods
  @__reserved_methods ||= begin
    names = [:__send__]
    names.concat [:default_scope, :current_scope=] if defined?(Rails)
    names
  end
end

.tracker_hooksObject



122
123
124
# File 'lib/object_tracker.rb', line 122

def self.tracker_hooks
  @__tracker_hooks ||= Hash.new { |me, key| me[key] = [] }
end

Instance Method Details

#track_all!(method_names = [], **options) ⇒ Object

Parameters:

  • method_names (Array<Symbol>) (defaults to: [])

    method names to track

  • :except (Hash)

    a customizable set of options

  • :before (Hash)

    a customizable set of options

  • :after (Hash)

    a customizable set of options



11
12
13
14
# File 'lib/object_tracker.rb', line 11

def track_all!(method_names = [], **options)
  ObjectTracker.(self, method_names, **options)
  self
end