require 'konstructor/version'
require 'konstructor/exceptions'
require 'konstructor/simple_method_hook'
require 'konstructor/factory'
module Konstructor
module KonstructorMethod
private
# konstructor -> nil
# konstructor(symbol, ...) -> nil
# konstructor(string, ...) -> nil
#
# If used without params, declares next method as constructor.
#
# module SomeClass
# attr_reader :val
#
# konstructor
# def create(val)
# @val = val
# end
# end
#
# If names are given, call can be placed anywhere, only methods with
# those names will be declared as constructors.
#
# module SomeClass
# attr_reader :val
#
# def create(val)
# @val = val
# end
#
# konstructor :create, :recreate
#
# def recreate(val)
# @val = val * 2
# end
# end
#
# then:
#
# SomeClass.new.val
# => nil
# SomeClass.create(3).val
# => 3
# SomeClass.recreate(3).val
# => 6
#
# Can be used multiple times with various arguments,
# all calls add up without overwriting each other.
#
# Can raise several errors inheriting from Konstructor::Error
# ReservedNameError
# DeclaringInheritedError
# IncludingInModuleError
def konstructor(*several_variants) # :doc:
Konstructor.declare(self, several_variants)
nil
end
end
DEFAULT_NAMES = [:initialize]
RESERVED_NAMES = [:new, :initialize]
class << self
def reserved?(name)
RESERVED_NAMES.include?(name.to_sym)
end
def default?(name)
DEFAULT_NAMES.include?(name.to_sym)
end
# Once method is a konstructor, it is always a konstructor, this differs
# from the way private, protected works. If overriding method isn't repeatedly
# marked as private it becomes public.
def declared?(klass, method_name)
konstructor = get_factory(klass)
if konstructor
konstructor.declared?(method_name.to_sym)
else
false
end
end
def declare(klass, new_method_names)
setup_method_added_hook(klass)
get_or_init_factory(klass).declare(new_method_names)
end
def method_added_to_klass(klass, method_name)
get_or_init_factory(klass).method_added_to_klass(method_name)
end
def is?(klass, method_name)
default?(method_name) || declared?(klass, method_name)
end
private
def get_factory(klass)
klass.instance_variable_get(:@konstructor)
end
def init_factory(klass)
# using variable @konstructor to minimize footprint, although saving factory there
klass.instance_variable_set(:@konstructor, Factory.new(klass))
end
def get_or_init_factory(klass)
get_factory(klass) || init_factory(klass)
end
def setup_method_added_hook(klass)
SimpleMethodHook.setup(klass)
end
# Overriden append_features prevents default behavior
# of including all the constants, variables to the base class.
# It adds only one method 'konstructor'.
def append_features(klass)
unless klass.is_a? Class
raise IncludingInModuleError, klass
end
klass.extend(KonstructorMethod)
end
end
end