lib/surrounded/context.rb in surrounded-0.7.3 vs lib/surrounded/context.rb in surrounded-0.8.0

- old
+ new

@@ -3,20 +3,28 @@ require 'surrounded/access_control' require 'surrounded/shortcuts' # Some features are only available in versions of Ruby # where this method is true -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 +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.class_eval { @triggers = Set.new @@ -30,26 +38,34 @@ 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 end private + # Set the default type of implementation for role methods for all contexts. def self.default_role_type @default_role_type ||= :module end class << self attr_writer :default_role_type end # Additional Features + + # Provide the ability to create access control methods for your triggers. def protect_triggers; self.extend(::Surrounded::AccessControl); end + + # Automatically create class methods for each trigger method. def shortcut_triggers; self.extend(::Surrounded::Shortcuts); end def private_const_set(name, const) const = const_set(name, const) private_constant name.to_sym @@ -58,36 +74,41 @@ def default_role_type @default_role_type ||= Surrounded::Context.default_role_type end + # Set the default type of implementation for role method for an individual context. def default_role_type=(type) @default_role_type = type 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 } private_const_set(mod_name, Module.new(&block)) @@ -106,10 +127,12 @@ 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(',')}) @@ -125,10 +148,26 @@ def private_attr_reader(*method_names) attr_reader(*method_names) private(*method_names) end + # 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. + # + # Example: + # trigger :some_event do + # # code here + # end + # + # def some_event + # # code here + # end + # trigger :some_event + # def trigger(*names, &block) if block.nil? names.each do |name| define_trigger_method(name, &block) end @@ -178,16 +217,24 @@ end }, __FILE__, __LINE__ 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) return false unless role_map.role?(name) - accessor = eval('self', block.binding) + accessor = block.binding.eval('self') role_map.role_player?(accessor) && role_map.assigned_player(name) end + # Check if a given object is a role player in the context. + def role_player?(obj) + role_map.role_player?(obj) + end + def triggers self.class.triggers end private @@ -222,11 +269,11 @@ 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) if behavior - role_player.store_context(self) + role_player.send(:store_context, self){} role_player end def add_module_interface(obj, mod) adder_name = module_extension_methods.find{|meth| obj.respond_to?(meth) } @@ -245,11 +292,11 @@ 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 + object.send(:remove_context) do; end role_player = object.method(remover_name).call map_role(role, role_module_basename(behavior), role_player) if behavior role_player @@ -269,21 +316,25 @@ applicator.call(role, role_const(mod_name), object) end end end + # List of possible methods to use to add behavior to an object from a module. def module_extension_methods [:cast_as, :extend] end + # List of possible methods to use to add behavior to an object from a wrapper. def wrap_methods [:new] end + # List of possible methods to use to remove behavior from an object with a module. def module_removal_methods [:uncast] end + # List of possible methods to use to remove behavior from an object with a wrapper. def unwrap_methods [:__getobj__] end def role_behavior_name(role) \ No newline at end of file