lib/surrounded/context.rb in surrounded-0.8.3 vs lib/surrounded/context.rb in surrounded-0.8.4

- old
+ new

@@ -1,48 +1,29 @@ require 'set' require 'surrounded/context/role_map' +require 'surrounded/context/role_builders' +require 'surrounded/context/initializing' require 'surrounded/access_control' require 'surrounded/shortcuts' require 'surrounded/east_oriented' -# Some features are only available in versions of Ruby -# where this method is true -unless defined?(module_method_rebinding?) - def module_method_rebinding? - return @__module_method_rebinding__ if defined?(@__module_method_rebinding__) - sample_method = Enumerable.instance_method(:to_a) - @__module_method_rebinding__ = begin - !!sample_method.bind(Object.new) - rescue TypeError - false - end - end -end - # Extend your classes with Surrounded::Context to handle their # initialization and application of behaviors to the role players # passed into the constructor. # # The purpose of this module is to help you create context objects # which encapsulate the interaction and behavior of objects inside. module Surrounded module Context def self.extended(base) + base.extend RoleBuilders, Initializing base.class_eval { @triggers = Set.new include InstanceMethods } end - def new(*args, &block) - instance = allocate - instance.send(:preinitialize) - instance.send(:initialize, *args, &block) - instance.send(:postinitialize) - instance - end - # Provides a Set of all available trigger methods where # behaviors will be applied to the roles before execution # and removed afterward. def triggers @triggers.dup @@ -85,68 +66,10 @@ def __apply_role_policy @__apply_role_policy ||= :trigger end - # Shorthand for creating an instance level initialize method which - # handles the mapping of the given arguments to their named role. - def initialize(*setup_args) - private_attr_reader(*setup_args) - - class_eval " - def initialize(#{setup_args.join(',')}) - preinitialize - arguments = method(__method__).parameters.map{|arg| eval(arg[1].to_s) } - @role_map = RoleMap.new - map_roles(#{setup_args}.zip(arguments)) - postinitialize - end - ", __FILE__, __LINE__ - end - - # Create a named behavior for a role using the standard library SimpleDelegator. - def wrap(name, &block) - require 'delegate' - wrapper_name = name.to_s.gsub(/(?:^|_)([a-z])/){ $1.upcase } - klass = private_const_set(wrapper_name, Class.new(SimpleDelegator, &block)) - klass.send(:include, Surrounded) - end - alias_method :wrapper, :wrap - - if module_method_rebinding? - # Create an object which will bind methods to the role player - def interface(name, &block) - class_basename = name.to_s.gsub(/(?:^|_)([a-z])/){ $1.upcase } - interface_name = class_basename + 'Interface' - - behavior = private_const_set(interface_name, Module.new(&block)) - - require 'surrounded/context/negotiator' - undef_method(name) - define_method(name) do - instance_variable_set("@#{name}", Negotiator.new(role_map.assigned_player(name), behavior)) - end - end - end - - # Define behaviors for your role players - def role(name, type=nil, &block) - role_type = type || default_role_type - if role_type == :module - mod_name = name.to_s.gsub(/(?:^|_)([a-z])/){ $1.upcase } - mod = Module.new(&block) - mod.send(:include, ::Surrounded) - private_const_set(mod_name, mod) - else - meth = method(role_type) - meth.call(name, &block) - end - rescue NameError => e - raise e.extend(InvalidRoleType) - end - alias_method :role_methods, :role - # Creates a context instance method which will apply behaviors to role players # before execution and remove the behaviors after execution. # # Alternatively you may define your own methods then declare them as triggers # afterward. @@ -186,26 +109,30 @@ store_trigger(name) end end def define_trigger_wrap_method(name) - class_eval %{ - def #{name} + mod = Module.new + line = __LINE__ + mod.class_eval %{ + def #{name}(*args, &block) begin apply_roles if __apply_role_policy == :trigger #{trigger_return_content(name)} ensure remove_roles if __apply_role_policy == :trigger end end - }, __FILE__, __LINE__ + }, __FILE__, line + const_set("SurroundedTrigger#{name.to_s.upcase.sub(/\?\z/,'Query')}", mod) + include mod end - def trigger_return_content(name) - %{self.send("__trigger_#{name}")} + def trigger_return_content(name, *args, &block) + %{self.send("__trigger_#{name}", *args, &block)} end # === Utility shortcuts # Set a named constant and make it private @@ -223,15 +150,19 @@ private(*method_names) end # Conditional const_get for a named role behavior def role_const(name) - if const_defined?(name) + if role_const_defined?(name) const_get(name) end end + def role_const_defined?(name) + const_defined?(name, false) + end + module InstanceMethods # Check whether a given name is a role inside the context. # The provided block is used to evaluate whether or not the caller # is allowed to inquire about the roles. def role?(name, &block) @@ -264,39 +195,46 @@ @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) + if self.respond_to?("map_role_#{role}") + self.send("map_role_#{role}", object) + else + map_role(role, role_behavior_name(role), object) + map_role_collection(role, role_behavior_name(role), object) + end + end + end - singular_role_name = singularize_name(role) - singular_behavior_name = singularize_name(role_behavior_name(role)) - - if object.respond_to?(:each_with_index) && role_const_defined?(singular_behavior_name) - object.each_with_index do |item, index| - map_role(:"#{singular_role_name}_#{index + 1}", singular_behavior_name, item) - end + def map_role_collection(role, mod_name, collection) + singular_role_name = singularize_name(role) + singular_behavior_name = singularize_name(role_behavior_name(role)) + if collection.respond_to?(:each_with_index) && role_const_defined?(singular_behavior_name) + collection.each_with_index do |item, index| + map_role(:"#{singular_role_name}_#{index + 1}", singular_behavior_name, item) end end end def map_role(role, mod_name, object) instance_variable_set("@#{role}", object) - role_map.update(role, mod_name, object) + role_map.update(role, role_module_basename(mod_name), object) end def __apply_role_policy @__apply_role_policy end def add_interface(role, behavior, object) - applicator = behavior.is_a?(Class) ? method(:add_class_interface) : method(:add_module_interface) + if behavior && role_const_defined?(behavior) + applicator = role_const(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) if behavior - role_player.send(:store_context, self){} - role_player + role_player = applicator.call(object, role_const(behavior)) + map_role(role, behavior, role_player) + end + role_player || object end def add_module_interface(obj, mod) adder_name = module_extension_methods.find{|meth| obj.respond_to?(meth) } return obj if !adder_name @@ -306,38 +244,38 @@ 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) } - object.send(:remove_context) do; end + if behavior && role_const_defined?(behavior) + remover_name = (module_removal_methods + unwrap_methods).find{|meth| object.respond_to?(meth) } + end if remover_name role_player = object.send(remover_name) end - return role_player || object + role_player || object end def apply_roles - traverse_map method(:add_interface) + role_map.each do |role, mod_name, object| + player = add_interface(role, mod_name, object) + player.send(:store_context, self) do; end + end end def remove_roles - traverse_map method(:remove_interface) - end - - def traverse_map(applicator) - role_map.each do |role, mod_name, object| - if role_const_defined?(mod_name) - applicator.call(role, role_const(mod_name), object) + role_map.each do |role, mod_name, player| + if player.respond_to?(:remove_context, true) + player.send(:remove_context) do; end end + remove_interface(role, mod_name, player) end end # List of possible methods to use to add behavior to an object from a module. def module_extension_methods @@ -370,10 +308,10 @@ def role_const(name) self.class.send(:role_const, name) end def role_const_defined?(name) - self.class.const_defined?(name) + self.class.send(:role_const_defined?, name) end def singularize_name(name) if name.respond_to?(:singularize) name.singularize \ No newline at end of file