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