module Abstractable

abstract method support module.

Constants

VERSION

Public Class Methods

find_not_implemented_info(klass) click to toggle source

Shortcut to NotImplementedInfoFinder.new.find(klass)

# File lib/abstractable.rb, line 79
def self.find_not_implemented_info(klass)
  NotImplementedInfoFinder.new.find(klass)
end
find_not_implemented_info_from_singleton(klass) click to toggle source

Shortcut to NotImplementedInfoFinder.new.find_from_singleton(klass)

# File lib/abstractable.rb, line 84
def self.find_not_implemented_info_from_singleton(klass)
  NotImplementedInfoFinder.new.find_from_singleton(klass)
end

Public Instance Methods

abstract(*names, &block) click to toggle source

Define abstract methods.

Example:

abstract :execute           # one symbol
abstract :method1, :method2 # multiple symbol
# in block
abstract do
  def method3; end
  def method4; end
end
# File lib/abstractable.rb, line 34
def abstract(*names, &block)
  add_abstract_methods(*names.compact)
  add_abstract_methods_by_block(&block) if block_given?
end
abstract_methods(all = true) click to toggle source

abstract_methods(all=true) -> array

Returns an array containing the names of abstract methods in the receiver. if set true to args include ancestors abstract methods. (default true)

# File lib/abstractable.rb, line 45
def abstract_methods(all = true)
  do_abstract_methods(all, self, (ancestors - [self]))
end
abstract_singleton_methods(all = true) click to toggle source

abstract_singleton_methods(all=true) -> array

Returns an array containing the names of abstract methods in the receivers singleton class. if set true to args include ancestor singleton classes abstract methods. (default true)

# File lib/abstractable.rb, line 55
def abstract_singleton_methods(all = true)
  do_abstract_methods(all, singleton_class, (ancestors - [self]).map(&:singleton_class))
end
allocate(*args, &block) click to toggle source

allocate after unimplemented abstract methods validation.

Calls superclass method
# File lib/abstractable.rb, line 19
def allocate(*args, &block)
  validate_on_create(:allocate)
  super(*args, &block)
end
included(base) click to toggle source
# File lib/abstractable.rb, line 8
def included(base)
  base.extend(Abstractable)
end
new(*args, &block) click to toggle source

new after unimplemented abstract methods validation.

Calls superclass method
# File lib/abstractable.rb, line 13
def new(*args, &block)
  validate_on_create(:new)
  super(*args, &block)
end
required_validate?() click to toggle source

required_validate? -> true or false

Returns true if required unimplemented abstract methods validation.

if validated or if defined environment variable ABSTRACTABLE_IGNORE_VALIDATE then always return true.

# File lib/abstractable.rb, line 74
def required_validate?
  !ENV["ABSTRACTABLE_IGNORE_VALIDATE"] && @implemented_abstract_methods != abstract_methods
end
validate_not_implemented_abstract_methods() click to toggle source

Unimplemented abstract methods validation.

if found unimplemented methods then throw NotImplementedError.

# File lib/abstractable.rb, line 62
def validate_not_implemented_abstract_methods
  not_impl_info = Abstractable.find_not_implemented_info(self)
  fail NotImplementedError, build_error_message(not_impl_info) unless not_impl_info.empty?
  @implemented_abstract_methods = abstract_methods
end

Private Instance Methods

add_abstract_method(method) click to toggle source
# File lib/abstractable.rb, line 138
def add_abstract_method(method)
  fail ArgumentError, "wrong type argument #{method} (should be Symbol) " unless method.is_a? Symbol
  individual_abstract_methods << method
  define_abstract_skeleton(method)
end
add_abstract_methods(*methods) click to toggle source
# File lib/abstractable.rb, line 134
def add_abstract_methods(*methods)
  methods.each { |method| add_abstract_method(method) }
end
add_abstract_methods_by_block(&block) click to toggle source
# File lib/abstractable.rb, line 128
def add_abstract_methods_by_block(&block)
  old_instance_methods = instance_methods(false)
  block.call
  add_abstract_methods(*(instance_methods(false) - old_instance_methods))
end
build_error_message(not_implemented_infos) click to toggle source
# File lib/abstractable.rb, line 113
def build_error_message(not_implemented_infos)
  messages = ["following abstract methods are not implemented."]
  formatter = Proc.new { |klass, methods| "#{methods} defined in #{klass}" }
  messages.push(*not_implemented_infos.map(&formatter)).join("\n")
end
define_abstract_skeleton(method) click to toggle source
# File lib/abstractable.rb, line 119
def define_abstract_skeleton(method)
  this = self
  class_eval do
    define_method method do |*_|
      fail NotImplementedError, "#{method} is abstract method defined in #{this}, and must implement."
    end
  end
end
do_abstract_methods(all = true, klass, ancestors_of_klass) click to toggle source
# File lib/abstractable.rb, line 94
def do_abstract_methods(all = true, klass, ancestors_of_klass)
  result = []
  individual_abstract_methods_reader = lambda { |clazz| clazz.class_eval { individual_abstract_methods } }
  if klass.is_a? Abstractable
    result.push(*individual_abstract_methods_reader.call(klass))
    return result unless all
  end
  ancestors_of_klass.each_with_object(result) do |ancestor, array|
    array.push(*individual_abstract_methods_reader.call(ancestor)) if ancestor.is_a? Abstractable
  end
end
individual_abstract_methods() click to toggle source
# File lib/abstractable.rb, line 90
def individual_abstract_methods
  @individual_abstract_methods ||= []
end
method_undefined(method) click to toggle source

called when the method is undef.

# File lib/abstractable.rb, line 145
def method_undefined(method)
  individual_abstract_methods.delete(method)
end
validate_on_create(create_method_name) click to toggle source
# File lib/abstractable.rb, line 106
def validate_on_create(create_method_name)
  validate_not_implemented_abstract_methods if required_validate?
  if is_a?(Abstractable) && 0 < abstract_methods(false).size
    fail WrongOperationError, "#{self} has abstract methods. and therefore can't call #{create_method_name}."
  end
end