Skip to content

Commit

Permalink
Simplify access to state machines via #state_machine(:attribute) with…
Browse files Browse the repository at this point in the history
…out generating dupes
  • Loading branch information
obrie committed Mar 22, 2009
1 parent dd659a6 commit 3816088
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rdoc
@@ -1,5 +1,6 @@
== master

* Simplify access to state machines via #state_machine(:attribute) without generating dupes
* Fix assumptions that dm-validations is always available in DataMapper integration
* Automatically define DataMapper properties for machine attributes if they don't exist
* Add Transition#qualified_event, #qualified_from_name, and #qualified_to_name
Expand Down
8 changes: 5 additions & 3 deletions lib/state_machine/event_collection.rb
Expand Up @@ -5,7 +5,9 @@ def initialize #:nodoc:
super(:index => [:name, :qualified_name])
end

# Gets the list of events that can be fired on the given object
# Gets the list of events that can be fired on the given object. This
# will return their *unqalified* names (i.e. will not included the
# machine's namespace).
#
# == Examples
#
Expand All @@ -21,7 +23,7 @@ def initialize #:nodoc:
# end
# end
#
# events = Vehicle.state_machines[:state].events
# events = Vehicle.state_machine(:state).events
#
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
# events.valid_for(vehicle) # => [#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>]
Expand Down Expand Up @@ -50,7 +52,7 @@ def valid_for(object)
# end
# end
#
# events = Vehicle.state_machines[:state].events
# events = Vehicle.state_machine.events
#
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
# events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
Expand Down
4 changes: 2 additions & 2 deletions lib/state_machine/integrations/active_record.rb
Expand Up @@ -244,7 +244,7 @@ def define_state_predicate
# we need to be able to call +super+
owner_class.class_eval do
define_method("#{attribute}?") do |*args|
args.empty? ? super(*args) : self.class.state_machines[attribute].states.matches?(self, *args)
args.empty? ? super(*args) : self.class.state_machine(attribute).states.matches?(self, *args)
end
end
end
Expand Down Expand Up @@ -288,7 +288,7 @@ def define_scope(name, scope)
# Created the scope and then override it with state translation
owner_class.named_scope(name)
owner_class.scopes[name] = lambda do |klass, *states|
machine_states = klass.state_machines[attribute].states
machine_states = klass.state_machine(attribute).states
values = states.flatten.map {|state| machine_states.fetch(state).value}

::ActiveRecord::NamedScope::Scope.new(klass, scope.call(values))
Expand Down
2 changes: 1 addition & 1 deletion lib/state_machine/integrations/data_mapper/observer.rb
Expand Up @@ -169,7 +169,7 @@ def add_transition_callback(type, *args, &block)

# Add the transition callback to each class being observed
observing.each do |klass|
state_machines = attribute ? [klass.state_machines[attribute]] : klass.state_machines.values
state_machines = attribute ? [klass.state_machine(attribute)] : klass.state_machines.values
state_machines.each {|machine| machine.send("#{type}_transition", *transition_args, &block)}
end if observing
end
Expand Down
19 changes: 10 additions & 9 deletions lib/state_machine/machine.rb
Expand Up @@ -138,7 +138,7 @@ module StateMachine
# end
#
# [Vehicle, Switch, Project].each do |klass|
# klass.state_machines.each do |machine|
# klass.state_machines.each do |attribute, machine|
# machine.before_transition klass.method(:before_transition)
# end
# end
Expand Down Expand Up @@ -216,10 +216,11 @@ def find_or_create(owner_class, *args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
attribute = args.first || :state

# Attempts to find an existing machine
# Find an existing machine
if owner_class.respond_to?(:state_machines) && machine = owner_class.state_machines[attribute]
# Create a copy of the state machine if it's being created by a subclass
unless machine.owner_class == owner_class
# Only create a new copy if changes are being made to the machine in
# a subclass
if machine.owner_class != owner_class && (options.any? || block_given?)
machine = machine.clone
machine.initial_state = options[:initial] if options.include?(:initial)
machine.owner_class = owner_class
Expand Down Expand Up @@ -406,7 +407,7 @@ def define_instance_method(method, &block)

@instance_helper_module.class_eval do
define_method(method) do |*args|
block.call(self.class.state_machines[attribute], self, *args)
block.call(self.class.state_machine(attribute), self, *args)
end
end
end
Expand All @@ -430,7 +431,7 @@ def define_class_method(method, &block)

@class_helper_module.class_eval do
define_method(method) do |*args|
block.call(self.state_machines[attribute], self, *args)
block.call(self.state_machine(attribute), self, *args)
end
end
end
Expand All @@ -450,7 +451,7 @@ def define_class_method(method, &block)
# end
#
# vehicle = Vehicle.new
# Vehicle.state_machines[:state].initial_state(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=true>
# Vehicle.state_machine.initial_state(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=true>
#
# With a dynamic initial state:
#
Expand All @@ -465,10 +466,10 @@ def define_class_method(method, &block)
# vehicle = Vehicle.new
#
# vehicle.force_idle = true
# Vehicle.state_machines[:state].initial_state(vehicle) # => #<StateMachine::State name=:idling value="idling" initial=false>
# Vehicle.state_machine.initial_state(vehicle) # => #<StateMachine::State name=:idling value="idling" initial=false>
#
# vehicle.force_idle = false
# Vehicle.state_machines[:state].initial_state(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=false>
# Vehicle.state_machine.initial_state(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=false>
def initial_state(object)
states.fetch(@initial_state.is_a?(Proc) ? @initial_state.call(object) : @initial_state)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/state_machine/state.rb
Expand Up @@ -156,7 +156,7 @@ def context(&block)
# not possible with lambdas in Ruby 1.8.6.
owner_class.class_eval <<-end_eval, __FILE__, __LINE__
def #{method}(*args, &block)
self.class.state_machines[#{attribute.inspect}].states.match(self).call(self, #{method.inspect}, *args, &block)
self.class.state_machine(#{attribute.inspect}).states.match(self).call(self, #{method.inspect}, *args, &block)
end
end_eval
end
Expand Down
4 changes: 2 additions & 2 deletions lib/state_machine/state_collection.rb
Expand Up @@ -20,7 +20,7 @@ def initialize #:nodoc:
# end
# end
#
# states = Vehicle.state_machines[:state].states
# states = Vehicle.state_machine.states
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
#
# states.matches?(vehicle, :parked) # => true
Expand All @@ -43,7 +43,7 @@ def matches?(object, name)
# end
# end
#
# states = Vehicle.state_machines[:state].states
# states = Vehicle.state_machine.states
#
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
# states.match(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=true>
Expand Down
17 changes: 15 additions & 2 deletions test/unit/machine_test.rb
Expand Up @@ -1406,7 +1406,7 @@ def self.matches?(klass)
@base_machine.after_transition(lambda {})

@klass = Class.new(@base_class)
@machine = StateMachine::Machine.find_or_create(@klass, :status)
@machine = StateMachine::Machine.find_or_create(@klass, :status) {}
end

def test_should_accept_a_block
Expand All @@ -1418,7 +1418,20 @@ def test_should_accept_a_block
assert called
end

def test_should_create_a_new_machine
def test_should_not_create_a_new_machine_if_no_block_or_options
machine = StateMachine::Machine.find_or_create(Class.new(@base_class), :status)

assert_same machine, @base_machine
end

def test_should_create_a_new_machine_if_given_options
machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)

assert_not_nil machine
assert_not_same machine, @base_machine
end

def test_should_create_a_new_machine_if_given_block
assert_not_nil @machine
assert_not_same @machine, @base_machine
end
Expand Down

0 comments on commit 3816088

Please sign in to comment.