Add simple support for ActiveModel's StateMachine for ActiveRecord
This commit is contained in:
parent
55d1d12c32
commit
aad5a30bf2
|
@ -5,12 +5,9 @@ module ActiveModel
|
||||||
autoload :State, 'active_model/state_machine/state'
|
autoload :State, 'active_model/state_machine/state'
|
||||||
autoload :StateTransition, 'active_model/state_machine/state_transition'
|
autoload :StateTransition, 'active_model/state_machine/state_transition'
|
||||||
|
|
||||||
class InvalidTransition < Exception
|
extend ActiveSupport::Concern
|
||||||
end
|
|
||||||
|
|
||||||
def self.included(base)
|
class InvalidTransition < Exception
|
||||||
require 'active_model/state_machine/machine'
|
|
||||||
base.extend ClassMethods
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
require 'active_model/state_machine/state_transition'
|
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
module StateMachine
|
module StateMachine
|
||||||
class Event
|
class Event
|
||||||
|
@ -53,12 +51,12 @@ module ActiveModel
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def transitions(trans_opts)
|
def transitions(trans_opts)
|
||||||
Array(trans_opts[:from]).each do |s|
|
Array(trans_opts[:from]).each do |s|
|
||||||
@transitions << StateTransition.new(trans_opts.merge({:from => s.to_sym}))
|
@transitions << StateTransition.new(trans_opts.merge({:from => s.to_sym}))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
require 'active_model/state_machine/state'
|
|
||||||
require 'active_model/state_machine/event'
|
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
module StateMachine
|
module StateMachine
|
||||||
class Machine
|
class Machine
|
||||||
|
@ -57,22 +54,22 @@ module ActiveModel
|
||||||
"@#{@name}_current_state"
|
"@#{@name}_current_state"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def state(name, options = {})
|
def state(name, options = {})
|
||||||
@states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
|
@states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def event(name, options = {}, &block)
|
def event(name, options = {}, &block)
|
||||||
(@events[name] ||= Event.new(self, name)).update(options, &block)
|
(@events[name] ||= Event.new(self, name)).update(options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def event_fired_callback
|
def event_fired_callback
|
||||||
@event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
|
@event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
|
||||||
end
|
end
|
||||||
|
|
||||||
def event_failed_callback
|
def event_failed_callback
|
||||||
@event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
|
@event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ module ActiveModel
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(obj, *args)
|
def execute(obj, *args)
|
||||||
case @on_transition
|
case @on_transition
|
||||||
when Symbol, String
|
when Symbol, String
|
||||||
|
|
|
@ -65,6 +65,7 @@ module ActiveRecord
|
||||||
autoload :SchemaDumper, 'active_record/schema_dumper'
|
autoload :SchemaDumper, 'active_record/schema_dumper'
|
||||||
autoload :Serialization, 'active_record/serialization'
|
autoload :Serialization, 'active_record/serialization'
|
||||||
autoload :SessionStore, 'active_record/session_store'
|
autoload :SessionStore, 'active_record/session_store'
|
||||||
|
autoload :StateMachine, 'active_record/state_machine'
|
||||||
autoload :TestCase, 'active_record/test_case'
|
autoload :TestCase, 'active_record/test_case'
|
||||||
autoload :Timestamp, 'active_record/timestamp'
|
autoload :Timestamp, 'active_record/timestamp'
|
||||||
autoload :Transactions, 'active_record/transactions'
|
autoload :Transactions, 'active_record/transactions'
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
module ActiveRecord
|
||||||
|
module StateMachine #:nodoc:
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include ActiveModel::StateMachine
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_validation :set_initial_state
|
||||||
|
validates_presence_of :state
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def write_state(state_machine, state)
|
||||||
|
update_attributes! :state => state.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_state(state_machine)
|
||||||
|
self.state.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_initial_state
|
||||||
|
self.state ||= self.class.state_machine.initial_state.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,42 @@
|
||||||
|
require 'cases/helper'
|
||||||
|
require 'models/traffic_light'
|
||||||
|
|
||||||
|
class StateMachineTest < ActiveRecord::TestCase
|
||||||
|
def setup
|
||||||
|
@light = TrafficLight.create!
|
||||||
|
end
|
||||||
|
|
||||||
|
test "states initial state" do
|
||||||
|
assert @light.off?
|
||||||
|
assert_equal :off, @light.current_state
|
||||||
|
end
|
||||||
|
|
||||||
|
test "transition to a valid state" do
|
||||||
|
@light.reset
|
||||||
|
assert @light.red?
|
||||||
|
assert_equal :red, @light.current_state
|
||||||
|
|
||||||
|
@light.green_on
|
||||||
|
assert @light.green?
|
||||||
|
assert_equal :green, @light.current_state
|
||||||
|
end
|
||||||
|
|
||||||
|
test "transition does not persist state" do
|
||||||
|
@light.reset
|
||||||
|
assert_equal :red, @light.current_state
|
||||||
|
@light.reload
|
||||||
|
assert_equal "off", @light.state
|
||||||
|
end
|
||||||
|
|
||||||
|
test "transition does persists state" do
|
||||||
|
@light.reset!
|
||||||
|
assert_equal :red, @light.current_state
|
||||||
|
@light.reload
|
||||||
|
assert_equal "red", @light.state
|
||||||
|
end
|
||||||
|
|
||||||
|
test "transition to an invalid state" do
|
||||||
|
assert_raise(ActiveModel::StateMachine::InvalidTransition) { @light.yellow_on }
|
||||||
|
assert_equal :off, @light.current_state
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,27 @@
|
||||||
|
class TrafficLight < ActiveRecord::Base
|
||||||
|
include ActiveRecord::StateMachine
|
||||||
|
|
||||||
|
state_machine do
|
||||||
|
state :off
|
||||||
|
|
||||||
|
state :red
|
||||||
|
state :green
|
||||||
|
state :yellow
|
||||||
|
|
||||||
|
event :red_on do
|
||||||
|
transitions :to => :red, :from => [:yellow]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :green_on do
|
||||||
|
transitions :to => :green, :from => [:red]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :yellow_on do
|
||||||
|
transitions :to => :yellow, :from => [:green]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :reset do
|
||||||
|
transitions :to => :red, :from => [:off]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -448,6 +448,13 @@ ActiveRecord::Schema.define do
|
||||||
t.integer :pet_id, :integer
|
t.integer :pet_id, :integer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table :traffic_lights, :force => true do |t|
|
||||||
|
t.string :location
|
||||||
|
t.string :state
|
||||||
|
t.datetime :created_at
|
||||||
|
t.datetime :updated_at
|
||||||
|
end
|
||||||
|
|
||||||
create_table :treasures, :force => true do |t|
|
create_table :treasures, :force => true do |t|
|
||||||
t.column :name, :string
|
t.column :name, :string
|
||||||
t.column :looter_id, :integer
|
t.column :looter_id, :integer
|
||||||
|
|
Loading…
Reference in New Issue