lib/spaced.rb in spaced-0.2.0 vs lib/spaced.rb in spaced-0.3.0
- old
+ new
@@ -1,8 +1,9 @@
# frozen_string_literal: true
require_relative "spaced/version"
+require "forwardable"
module Spaced
def self.included(base)
base.extend ClassMethods
end
@@ -13,24 +14,49 @@
attr_reader :parent
end
module ClassMethods
def namespace(name, klass = nil, &)
- unless klass
+ if klass
+ raise "#{klass} must be a subclass of Spaced::Base" unless klass < Spaced::Base
+ else
class_name = name.to_s.split("_").collect(&:capitalize).join
klass = eval <<-RUBY, binding, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
#{self}::#{class_name} = Class.new(Base, &) # Parent::Namespace = Class.new(Base, &)
RUBY
end
inst_name = :"@#{name}"
+
+ # Define the memoized namespace method.
define_method name do
if instance_variable_defined?(inst_name)
instance_variable_get inst_name
else
cls = klass.new
cls.instance_variable_set :@parent, self
instance_variable_set inst_name, cls
+ end
+ end
+
+ # Define the bang and predicate methods.
+ methods = klass.instance_methods(false)
+
+ if methods.include?(:call) || methods.include?(:predicate)
+ extend Forwardable
+ def_delegator :"#{name}", :call, :"#{name}!" if methods.include?(:call)
+ def_delegator :"#{name}", :predicate, :"#{name}?" if methods.include?(:predicate)
+ else
+ unless methods.include?(:call)
+ define_method :"#{name}!" do
+ raise NoMethodError, "undefined method `#{name}!' for #<#{klass}>. Have you defined `#{klass}#call`?"
+ end
+ end
+
+ unless methods.include?(:predicate)
+ define_method :"#{name}?" do
+ raise NoMethodError, "undefined method `#{name}?' for #<#{klass}>. Have you defined `#{klass}#predicate`?"
+ end
end
end
end
end
end