lib/surrounded/context.rb in surrounded-0.1.0 vs lib/surrounded/context.rb in surrounded-0.2.0
- old
+ new
@@ -1,86 +1,181 @@
require 'set'
+require 'surrounded/context/role_map'
module Surrounded
module Context
def self.extended(base)
base.send(:include, InstanceMethods)
+ base.singleton_class.send(:alias_method, :setup, :initialize)
end
def triggers
@triggers.dup
end
+ def policy
+ @policy ||= :trigger
+ end
+
private
- def setup(*setup_args)
- attr_reader(*setup_args)
- private(*setup_args)
+ def apply_roles_on(which)
+ @policy = which
+ end
- define_method(:initialize){ |*args|
- Hash[setup_args.zip(args)].each{ |role, object|
+ def initialize(*setup_args)
+ private_attr_reader(*setup_args)
- role_module_name = Context.classify_string(role)
- klass = self.class
+ # I want this to work so I can set the arity on initialize:
+ # class_eval %Q<
+ # def initialize(#{*setup_args})
+ # arguments = parameters.map{|arg| eval(arg[1].to_s) }
+ # map_roles(setup_args.zip(arguments))
+ # apply_roles if policy == :initialize
+ # end
+ # >
- if mod = klass.const_defined?(role_module_name) && !mod.is_a?(Class)
- object = Context.modify(object, klass.const_get(role_module_name))
- end
+ define_method(:initialize){ |*args|
+ map_roles(setup_args.zip(args))
- roles[role.to_s] = object
- instance_variable_set("@#{role}", object)
- }
+ apply_roles if policy == :initialize
}
end
+ def private_attr_reader(*method_names)
+ attr_reader(*method_names)
+ private(*method_names)
+ end
+
def trigger(name, *args, &block)
store_trigger(name)
define_method(:"trigger_#{name}", *args, &block)
private :"trigger_#{name}"
define_method(name, *args){
begin
- (Thread.current[:context] ||= []).unshift(self)
+ apply_roles if policy == :trigger
+
self.send("trigger_#{name}", *args)
+
ensure
- (Thread.current[:context] ||= []).shift
+ remove_roles if policy == :trigger
end
}
end
def store_trigger(name)
@triggers ||= Set.new
@triggers << name
end
- def self.classify_string(string)
- string.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }
- end
-
- def self.modify(obj, mod)
- return obj if mod.is_a?(Class)
- if obj.respond_to?(:cast_as)
- obj.cast_as(mod)
- else
- obj.extend(mod)
- end
- end
-
module InstanceMethods
def role?(name, &block)
+ return false unless role_map.role?(name)
accessor = eval('self', block.binding)
- roles.values.include?(accessor) && roles[name.to_s]
+ role_map.role_player?(accessor) && role_map.assigned_player(name)
end
def triggers
self.class.triggers
end
private
- def roles
- @roles ||= {}
+ def role_map
+ @role_map ||= RoleMap.new
+ end
+
+ def map_roles(role_object_array)
+ role_object_array.each do |role, object|
+ map_role(role, role_behavior_name(role), object)
+ end
+ end
+
+ def map_role(role, mod_name, object)
+ instance_variable_set("@#{role}", object)
+ role_map.update(role, mod_name, object)
+ end
+
+ def policy
+ @policy ||= self.class.policy
+ end
+
+ def add_interface(role, behavior, object)
+ applicator = behavior.is_a?(Class) ? method(:add_class_interface) : method(:add_module_interface)
+
+ role_player = applicator.call(object, behavior)
+ map_role(role, role_module_basename(behavior), role_player)
+ role_player.store_context(self)
+ role_player
+ end
+
+ def add_module_interface(obj, mod)
+ adder_name = module_extension_methods.find{|meth| obj.respond_to?(meth) }
+ return obj if !adder_name
+
+ obj.method(adder_name).call(mod)
+ obj
+ end
+
+ def add_class_interface(obj, klass)
+ wrapper_name = wrap_methods.find{|meth| klass.respond_to?(meth) }
+ return obj if !wrapper_name
+
+ klass.method(wrapper_name).call(obj)
+ end
+
+ def remove_interface(role, behavior, object)
+ remover_name = (module_removal_methods + unwrap_methods).find{|meth| object.respond_to?(meth) }
+ return object if !remover_name
+
+ object.remove_context
+ role_player = object.method(remover_name).call
+
+ map_role(role, role_module_basename(behavior), role_player)
+
+ role_player
+ end
+
+ def apply_roles
+ traverse_map method(:add_interface)
+ end
+
+ def remove_roles
+ traverse_map method(:remove_interface)
+ end
+
+ def traverse_map(applicator)
+ role_map.each do |role, mod_name, object|
+ if self.class.const_defined?(mod_name)
+ applicator.call(role, self.class.const_get(mod_name), object)
+ end
+ end
+ end
+
+ def module_extension_methods
+ [:cast_as, :extend]
+ end
+
+ def wrap_methods
+ [:new]
+ end
+
+ def module_removal_methods
+ [:uncast]
+ end
+
+ def unwrap_methods
+ [:__getobj__]
+ end
+
+ def role_behavior_name(role)
+ role.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }.sub(/_\d+/,'')
+ end
+
+ def role_module_basename(mod)
+ mod.to_s.split('::').last
end
end
end
end
\ No newline at end of file