module Antidote::ClassMethods

Public Instance Methods

annotate(*variables, return_type, &method_name) click to toggle source
# File lib/antidote.rb, line 14
def annotate(*variables, return_type, &method_name)
  method_name = method_name.call.to_s
  add_type_constraints(method_name, variables, return_type)
  redefine_instance_method(method_name)
end
annotate_class_method(*variables, return_type, &method_name) click to toggle source
# File lib/antidote.rb, line 20
def annotate_class_method(*variables, return_type, &method_name)
  method_name = "self.#{method_name.call.to_s}"
  add_type_constraints(method_name, variables, return_type)
  redefine_class_method(method_name)
end

Private Instance Methods

add_type_constraints(method_name, variables, return_type) click to toggle source
# File lib/antidote.rb, line 28
def add_type_constraints(method_name, variables, return_type)
  variables.each { |variable| @@type_constraints[method_name] << variable }
  @@type_constraints[method_name] << {__returns: return_type}
end
redefine_class_method(method_name) click to toggle source
# File lib/antidote.rb, line 33
def redefine_class_method(method_name)
  method_name = method_name.split(".").last
  new_name_for_old_method = "#{method_name}_old".to_sym
  self.singleton_class.send(:alias_method, new_name_for_old_method, method_name)

  define_singleton_method(method_name) do |*args|
    args.each_with_index do |argument, index|
      class_method = "self.#{method_name}"
      actual = argument.class
      expected = @@type_constraints[class_method][index]
      parameters = self.method(new_name_for_old_method).parameters.map(&:last)
      variable_name = parameters[index]
      @__return_type = @@type_constraints[class_method].last[:__returns]

      unless actual == expected
        raise VariableTypeError, [actual, expected, variable_name, class_method]
      end
    end

    if (klass = self.send(new_name_for_old_method, *args).class) != @__return_type 
      raise ReturnTypeError, [klass, @__return_type, class_method]
    else
      self.send(new_name_for_old_method, *args)
    end
  end
end
redefine_instance_method(method_name) click to toggle source
# File lib/antidote.rb, line 60
def redefine_instance_method(method_name)
  new_name_for_old_method = "#{method_name}_old".to_sym
  alias_method(new_name_for_old_method, method_name)

  define_method(method_name) do |*args|
    args.each_with_index do |argument, index|
      actual = argument.class
      expected = @@type_constraints[method_name][index]
      parameters = self.method(new_name_for_old_method).parameters.map(&:last)
      variable_name = parameters[index]
      @__return_type = @@type_constraints[method_name].last[:__returns]

      unless actual == expected
        raise VariableTypeError, [actual, expected, variable_name, method_name]
      end
    end

    if (klass = self.send(new_name_for_old_method, *args).class) != @__return_type 
      raise ReturnTypeError, [klass, @__return_type, method_name]
    else
      self.send(new_name_for_old_method, *args)
    end
  end
end