# Calls Module#defined(file, line, method) after a class or module has been defined or redefined
module Defined
autoload :Version, 'defined/version'
class << self
def disable!
set_trace_func nil
@enabled = false
end
def enable!
set_trace_func method(:trace_function).to_proc
@enabled = true
end
def enabled?
@enabled ||= false
end
# An array of classes and modules that are currently being defined
def definitions
@definitions ||= []
end
protected
def definition?(event, method, klass, keyword_event, method_event)
event == keyword_event || (event == method_event && definition_method?(method, klass))
end
# Checks if the event is the end of a class or module definition
def definition_end?(event, method, klass)
!definitions.empty? && definition?(event, method, klass, 'end', 'c-return')
end
# Checks if the event is a call to Module.new, Class.new, or /(class|instance|module)_(eval|exec)/
def definition_method?(method, klass)
(method == :initialize && klass.is_a?(Module)) || method.to_s =~ /^(class|instance|module)_(eval|exec)$/
end
# Checks if the event is the start of a class or module definition
def definition_start?(event, method, klass)
definition?(event, method, klass, 'class', 'c-call')
end
# Checks if the event is the end of a class or module definition, and if so
# it calls Module#defined(file, line, method) if implemented
def trace_function(event, file, line, method, binding, klass)
if definition_start?(event, method, klass)
definitions << eval('self', binding)
elsif definition_end?(event, method, klass)
object = definitions.pop
method ||= object.class.name.downcase.to_sym
object.defined(file, line, method) if object.respond_to?(:defined)
end
end
end
end