module RubyInterface::ClassMethods

Public Instance Methods

defines(*args) click to toggle source
# File lib/ruby_interface.rb, line 19
def defines(*args)
  @methods_to_define ||= []
  @methods_to_define += args
end
track_required_methods(child) click to toggle source
# File lib/ruby_interface.rb, line 24
def track_required_methods(child)
  missing_methods = @methods_to_define - child.instance_methods(false)

  return if missing_methods.empty?

  raise_class_definition_error child, missing_methods
end

Private Instance Methods

anonymous_class_definition(child) click to toggle source
# File lib/ruby_interface.rb, line 48
def anonymous_class_definition(child)
  define_class_hook child
  redefine_eval_and_exec child
end
define_class_hook(child) click to toggle source
# File lib/ruby_interface.rb, line 53
def define_class_hook(child)
  klass = self
  child.define_singleton_method :anonymous_class_defined do
    klass.track_required_methods(child)
    revert_eval_and_exec
  end
end
named_class_definition(child) click to toggle source
# File lib/ruby_interface.rb, line 83
def named_class_definition(child)
  trace = TracePoint.new(:end) do |point|
    next unless point.self == child
    trace.disable
    track_required_methods child
  end
  trace.enable
end
raise_class_definition_error(child, missing_methods) click to toggle source
# File lib/ruby_interface.rb, line 34
def raise_class_definition_error(child, missing_methods)
  message = "Expected #{child.name || 'anonymous class'} to define "
  message << missing_methods.map { |method| "##{method}" }.join(', ')

  raise NotImplementedError, message
rescue NotImplementedError => exception
  directory = File.dirname(__FILE__)
  better_backtrace = exception.backtrace.drop_while do |element|
    element.include? directory
  end
  exception.set_backtrace better_backtrace
  raise exception
end
redefine_eval_and_exec(child) click to toggle source
# File lib/ruby_interface.rb, line 61
def redefine_eval_and_exec(child)
  klass = self

  [:class_exec, :class_eval].each do |method|
    old_method = "_old_#{method}".to_sym
    child.singleton_class.send(:alias_method, old_method, method)

    child.define_singleton_method method do |*args, &block|
      send old_method, *args, &block
      klass.track_required_methods(child)
      revert_eval_and_exec
    end
  end
end
revert_eval_and_exec() click to toggle source
# File lib/ruby_interface.rb, line 76
def revert_eval_and_exec
  [:class_exec, :class_eval].each do |method|
    old_method = "_old_#{method}".to_sym
    singleton_class.send(:alias_method, method, old_method)
  end
end