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