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