Permalink
Browse files

Make it easier to override state-driven behaviors

  • Loading branch information...
1 parent b197fa5 commit a167529c6096e215de11d3b93f05688f28df3f64 @obrie obrie committed Apr 3, 2009
View
@@ -1,5 +1,6 @@
== master
+* Make it easier to override state-driven behaviors
* Rollback state changes when the action fails during transitions
* Use :messages instead of :invalid_message for customizing validation errors
* Use more human-readable validation errors
@@ -121,14 +121,11 @@ def initialize(options = {}, &block)
options = {}
end
- # The actual method to invoke must be defined
raise ArgumentError, ':do callback must be specified' unless @method
- # Add defaults
options = {:bind_to_object => self.class.bind_to_object, :terminator => self.class.terminator}.merge(options)
- # Proxy the method so that it's bound to the object. Note that this only
- # applies to lambda callbacks. All other callbacks ignore this option.
+ # Proxy lambda blocks so that they're bound to the object
@method = bound_method(@method) if options.delete(:bind_to_object) && @method.is_a?(Proc)
@terminator = options.delete(:terminator)
@@ -77,8 +77,8 @@ def method_missing(*args, &block)
# Replace the configuration condition with the one configured for this
# proxy, merging together any existing conditions
options[:if] = lambda do |*args|
- # Block may be executed within the context of the actual object, so it'll
- # either be the first argument or the executing context
+ # Block may be executed within the context of the actual object, so
+ # it'll either be the first argument or the executing context
object = args.first || self
proxy.evaluate_method(object, proxy_condition) &&
View
@@ -145,32 +145,20 @@ def context(&block)
# Evaluate the method definitions
context = ConditionProxy.new(owner_class, lambda {|object| object.send("#{attribute}_name") == name})
context.class_eval(&block)
-
- # Define all of the methods that were created in the module so that they
- # don't override the core behavior (i.e. calling the state method)
context.instance_methods.each do |method|
- unless owner_class.instance_methods.include?(method)
- # Calls the method defined by the current state of the machine. This
- # is done using string evaluation so that any block passed into the
- # method can then be passed to the state's context method, which is
- # not possible with lambdas in Ruby 1.8.6.
- owner_class.class_eval <<-end_eval, __FILE__, __LINE__
- def #{method}(*args, &block)
- self.class.state_machine(#{attribute.inspect}).states.match(self).call(self, #{method.inspect}, *args, &block)
- end
- end_eval
- end
-
- # Track the method defined for the context so that it can be invoked
- # at a later point in time
methods[method.to_sym] = context.instance_method(method)
+
+ # Calls the method defined by the current state of the machine
+ context.class_eval <<-end_eval, __FILE__, __LINE__
+ def #{method}(*args, &block)
+ self.class.state_machine(#{attribute.inspect}).states.match(self).call(self, #{method.inspect}, *args, &block)
+ end
+ end_eval
end
# Include the context so that it can be bound to the owner class (the
# context is considered an ancestor, so it's allowed to be bound)
- owner_class.class_eval do
- include context
- end
+ owner_class.class_eval { include context }
context
end
@@ -172,23 +172,35 @@ class TrafficLight
end
state :stop do
- def color
- 'red'
+ def color(transform)
+ value = 'red'
+
+ if block_given?
+ yield value
+ else
+ value.send(transform)
+ end
+
+ value
end
end
state :proceed do
- def color
+ def color(transform)
'green'
end
end
state :caution do
- def color
+ def color(transform)
'yellow'
end
end
end
+
+ def color(transform = :to_s)
+ super
+ end
end
class VehicleTest < Test::Unit::TestCase
@@ -860,6 +872,15 @@ def setup
def test_should_use_stop_color
assert_equal 'red', @light.color
end
+
+ def test_should_pass_arguments_through
+ assert_equal 'RED', @light.color(:upcase!)
+ end
+
+ def test_should_pass_block_through
+ color = @light.color {|value| value.upcase!}
+ assert_equal 'RED', color
+ end
end
class TrafficLightProceedTest < Test::Unit::TestCase

0 comments on commit a167529

Please sign in to comment.