lib/casting/context.rb in casting-0.7.2 vs lib/casting/context.rb in casting-1.0.0
- old
+ new
@@ -5,52 +5,55 @@
# You *must* include the following module and use it for refinements, and
# you *must* set the current context for the thread:
#
# class SomeContext
# using Casting::Context
-# include Casting::Context
+# extend Casting::Context
#
# initialize(:some, :thing)
# # doing that defines your constructor but would cause it too look for
# # modules named Some and Thing
# module Some; end
# module Thing; end
#
# # if you want different module names (why would you?) then you'd need
# # to do all this:
# def initialize(some, thing)
-# @assignments = []
# assign [some, SomeRole], [thing, OtherRole]
# Thread.current[:context] = self
# end
# attr_reader :some, :thing, :assignments
#
# module SomeRole; end
# module OtherRole; end
# end
#
-# In order to use this the objects sent into the context contstructor *must*
-# `include Casting::Client` so that the `cast` method is available to them
-#
module Casting
module Context
def self.extended(base)
base.send(:include, InstanceMethods)
end
- def initialize(*setup_args)
+ def initialize(*setup_args, &block)
attr_reader(*setup_args)
private(*setup_args)
+ if block
+ define_method(:__custom_initialize, &block)
+ else
+ define_method(:__custom_initialize) do; end
+ end
+
mod = Module.new
line = __LINE__; string = %<
def initialize(#{setup_args.map{|name| "#{name}:" }.join(',')})
@assignments = []
#{setup_args.map do |name|
["assign(",name,", '",name,"')"].join
end.join("\n")}
+ __custom_initialize
Thread.current[:context] = self
end
attr_reader :assignments
>
mod.class_eval string, __FILE__, line
@@ -60,11 +63,15 @@
module InstanceMethods
def context
self
end
-
+
+ def assignments
+ @assignments ||= []
+ end
+
# Keep track of objects and their behaviors
def assign(object, role_name)
instance_variable_set("@#{role_name}", object)
self.assignments << [object, self.role_for(role_name)]
end
@@ -72,19 +79,23 @@
def contains?(obj)
assignments.map(&:first).include?(obj)
end
# Execute the behavior from the role on the specifed object
- def dispatch(object, method_name, *args, &block)
- object.cast(method_name, context.role_implementing(object, method_name), *args, &block)
+ def dispatch(object, method_name, ...)
+ if object.respond_to?(:cast)
+ object.cast(method_name, context.role_implementing(object, method_name), ...)
+ else
+ Casting::Delegation.prepare(method_name, object).to(role_implementing(object, method_name)).with(...).call
+ end
end
-
+
# Find the first assigned role which implements a response for the given method name
def role_implementing(object, method_name)
assigned_roles(object).find{|role| role.method_defined?(method_name) } || raise(NoMethodError, "unknown method '#{method_name}' expected for #{object}")
end
-
+
# Get the roles for the given object
def assigned_roles(object)
assignments.select{|pair|
pair.first == object
}.map(&:last)
@@ -94,11 +105,11 @@
# This role constant for special_person is SpecialPerson.
def role_for(name)
role_name = name.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }
self.class.const_get(role_name)
rescue NameError
- Module
+ Module.new
end
end
refine Object do
def context
@@ -113,13 +124,13 @@
def role(role_name)
context.send(role_name)
end
# Execute the named method on the object plaing the name role
- def tell(role_name, method_name, *args, &block)
+ def tell(role_name, method_name, ...)
if context == self || context.contains?(self)
- context.dispatch(role(role_name), method_name, *args, &block)
+ context.dispatch(role(role_name), method_name, ...)
end
end
end
end
-end
\ No newline at end of file
+end