state_machine 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.rdoc +26 -0
  2. data/README.rdoc +254 -46
  3. data/Rakefile +29 -3
  4. data/examples/AutoShop_state.png +0 -0
  5. data/examples/Car_state.jpg +0 -0
  6. data/examples/Vehicle_state.png +0 -0
  7. data/lib/state_machine.rb +161 -116
  8. data/lib/state_machine/assertions.rb +21 -0
  9. data/lib/state_machine/callback.rb +168 -0
  10. data/lib/state_machine/eval_helpers.rb +67 -0
  11. data/lib/state_machine/event.rb +135 -101
  12. data/lib/state_machine/extensions.rb +83 -0
  13. data/lib/state_machine/guard.rb +115 -0
  14. data/lib/state_machine/integrations/active_record.rb +242 -0
  15. data/lib/state_machine/integrations/data_mapper.rb +198 -0
  16. data/lib/state_machine/integrations/data_mapper/observer.rb +153 -0
  17. data/lib/state_machine/integrations/sequel.rb +169 -0
  18. data/lib/state_machine/machine.rb +746 -352
  19. data/lib/state_machine/transition.rb +104 -212
  20. data/test/active_record.log +34865 -0
  21. data/test/classes/switch.rb +11 -0
  22. data/test/data_mapper.log +14015 -0
  23. data/test/functional/state_machine_test.rb +249 -15
  24. data/test/sequel.log +3835 -0
  25. data/test/test_helper.rb +3 -12
  26. data/test/unit/assertions_test.rb +13 -0
  27. data/test/unit/callback_test.rb +189 -0
  28. data/test/unit/eval_helpers_test.rb +92 -0
  29. data/test/unit/event_test.rb +247 -113
  30. data/test/unit/guard_test.rb +420 -0
  31. data/test/unit/integrations/active_record_test.rb +515 -0
  32. data/test/unit/integrations/data_mapper_test.rb +407 -0
  33. data/test/unit/integrations/sequel_test.rb +244 -0
  34. data/test/unit/invalid_transition_test.rb +1 -1
  35. data/test/unit/machine_test.rb +1056 -98
  36. data/test/unit/state_machine_test.rb +14 -113
  37. data/test/unit/transition_test.rb +269 -495
  38. metadata +44 -30
  39. data/test/app_root/app/models/auto_shop.rb +0 -34
  40. data/test/app_root/app/models/car.rb +0 -19
  41. data/test/app_root/app/models/highway.rb +0 -3
  42. data/test/app_root/app/models/motorcycle.rb +0 -3
  43. data/test/app_root/app/models/switch.rb +0 -23
  44. data/test/app_root/app/models/switch_observer.rb +0 -20
  45. data/test/app_root/app/models/toggle_switch.rb +0 -2
  46. data/test/app_root/app/models/vehicle.rb +0 -78
  47. data/test/app_root/config/environment.rb +0 -7
  48. data/test/app_root/db/migrate/001_create_switches.rb +0 -12
  49. data/test/app_root/db/migrate/002_create_auto_shops.rb +0 -13
  50. data/test/app_root/db/migrate/003_create_highways.rb +0 -11
  51. data/test/app_root/db/migrate/004_create_vehicles.rb +0 -16
  52. data/test/factory.rb +0 -77
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Pfeifer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-10-26 00:00:00 -04:00
12
+ date: 2008-12-14 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,40 +22,47 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
+ - examples/Car_state.jpg
26
+ - examples/Vehicle_state.png
27
+ - examples/AutoShop_state.png
25
28
  - lib/state_machine
26
29
  - lib/state_machine/event.rb
30
+ - lib/state_machine/guard.rb
31
+ - lib/state_machine/integrations
32
+ - lib/state_machine/integrations/data_mapper
33
+ - lib/state_machine/integrations/data_mapper/observer.rb
34
+ - lib/state_machine/integrations/sequel.rb
35
+ - lib/state_machine/integrations/active_record.rb
36
+ - lib/state_machine/integrations/data_mapper.rb
37
+ - lib/state_machine/eval_helpers.rb
27
38
  - lib/state_machine/transition.rb
39
+ - lib/state_machine/callback.rb
40
+ - lib/state_machine/extensions.rb
41
+ - lib/state_machine/assertions.rb
28
42
  - lib/state_machine/machine.rb
29
43
  - lib/state_machine.rb
30
- - test/app_root
31
- - test/app_root/app
32
- - test/app_root/app/models
33
- - test/app_root/app/models/highway.rb
34
- - test/app_root/app/models/switch.rb
35
- - test/app_root/app/models/motorcycle.rb
36
- - test/app_root/app/models/toggle_switch.rb
37
- - test/app_root/app/models/vehicle.rb
38
- - test/app_root/app/models/car.rb
39
- - test/app_root/app/models/switch_observer.rb
40
- - test/app_root/app/models/auto_shop.rb
41
- - test/app_root/config
42
- - test/app_root/config/environment.rb
43
- - test/app_root/db
44
- - test/app_root/db/migrate
45
- - test/app_root/db/migrate/002_create_auto_shops.rb
46
- - test/app_root/db/migrate/003_create_highways.rb
47
- - test/app_root/db/migrate/004_create_vehicles.rb
48
- - test/app_root/db/migrate/001_create_switches.rb
44
+ - test/sequel.log
45
+ - test/active_record.log
46
+ - test/test_helper.rb
49
47
  - test/functional
50
48
  - test/functional/state_machine_test.rb
51
- - test/test_helper.rb
52
- - test/factory.rb
49
+ - test/data_mapper.log
50
+ - test/classes
51
+ - test/classes/switch.rb
53
52
  - test/unit
54
- - test/unit/transition_test.rb
53
+ - test/unit/integrations
54
+ - test/unit/integrations/data_mapper_test.rb
55
+ - test/unit/integrations/active_record_test.rb
56
+ - test/unit/integrations/sequel_test.rb
57
+ - test/unit/state_machine_test.rb
58
+ - test/unit/callback_test.rb
55
59
  - test/unit/invalid_transition_test.rb
56
- - test/unit/machine_test.rb
60
+ - test/unit/transition_test.rb
61
+ - test/unit/eval_helpers_test.rb
57
62
  - test/unit/event_test.rb
58
- - test/unit/state_machine_test.rb
63
+ - test/unit/guard_test.rb
64
+ - test/unit/assertions_test.rb
65
+ - test/unit/machine_test.rb
59
66
  - CHANGELOG.rdoc
60
67
  - init.rb
61
68
  - LICENSE
@@ -86,11 +93,18 @@ rubyforge_project: pluginaweek
86
93
  rubygems_version: 1.2.0
87
94
  signing_key:
88
95
  specification_version: 2
89
- summary: Adds support for creating state machines for attributes within a model
96
+ summary: Adds support for creating state machines for attributes on any Ruby class
90
97
  test_files:
91
98
  - test/functional/state_machine_test.rb
92
- - test/unit/transition_test.rb
99
+ - test/unit/integrations/data_mapper_test.rb
100
+ - test/unit/integrations/active_record_test.rb
101
+ - test/unit/integrations/sequel_test.rb
102
+ - test/unit/state_machine_test.rb
103
+ - test/unit/callback_test.rb
93
104
  - test/unit/invalid_transition_test.rb
94
- - test/unit/machine_test.rb
105
+ - test/unit/transition_test.rb
106
+ - test/unit/eval_helpers_test.rb
95
107
  - test/unit/event_test.rb
96
- - test/unit/state_machine_test.rb
108
+ - test/unit/guard_test.rb
109
+ - test/unit/assertions_test.rb
110
+ - test/unit/machine_test.rb
@@ -1,34 +0,0 @@
1
- class AutoShop < ActiveRecord::Base
2
- state_machine :state, :initial => 'available' do
3
- after_transition :from => 'available', :do => :increment_customers
4
- after_transition :from => 'busy', :do => :decrement_customers
5
-
6
- event :tow_vehicle do
7
- transition :to => 'busy', :from => 'available'
8
- end
9
-
10
- event :fix_vehicle do
11
- transition :to => 'available', :from => 'busy'
12
- end
13
- end
14
-
15
- # Is the Auto Shop available for new customers?
16
- def available?
17
- state == 'available'
18
- end
19
-
20
- # Is the Auto Shop currently not taking new customers?
21
- def busy?
22
- state == 'busy'
23
- end
24
-
25
- # Increments the number of customers in service
26
- def increment_customers
27
- update_attribute(:num_customers, num_customers + 1)
28
- end
29
-
30
- # Decrements the number of customers in service
31
- def decrement_customers
32
- update_attribute(:num_customers, num_customers - 1)
33
- end
34
- end
@@ -1,19 +0,0 @@
1
- class Car < Vehicle
2
- state_machine :state do
3
- event :reverse do
4
- transition :to => 'backing_up', :from => %w(parked idling first_gear)
5
- end
6
-
7
- event :park do
8
- transition :to => 'parked', :from => 'backing_up'
9
- end
10
-
11
- event :idle do
12
- transition :to => 'idling', :from => 'backing_up'
13
- end
14
-
15
- event :shift_up do
16
- transition :to => 'first_gear', :from => 'backing_up'
17
- end
18
- end
19
- end
@@ -1,3 +0,0 @@
1
- class Highway < ActiveRecord::Base
2
- has_many :vehicles
3
- end
@@ -1,3 +0,0 @@
1
- class Motorcycle < Vehicle
2
- state_machine :state, :initial => 'idling'
3
- end
@@ -1,23 +0,0 @@
1
- class Switch < ActiveRecord::Base
2
- # Tracks the callbacks that were invoked
3
- attr_reader :callbacks
4
-
5
- # Dynamic sets the initial state
6
- attr_accessor :initial_state
7
-
8
- # Whether or not validations should fail
9
- attr_accessor :fail_validation
10
- validate Proc.new {|switch| switch.errors.add_to_base 'is invalid' if switch.fail_validation}
11
-
12
- # Whether or not saves should fail
13
- attr_accessor :fail_save
14
- before_save Proc.new {|switch| !switch.fail_save}
15
-
16
- # Arbitrary data associated with the switch
17
- attr_accessor :data
18
-
19
- def initialize(attributes = nil)
20
- @callbacks = []
21
- super
22
- end
23
- end
@@ -1,20 +0,0 @@
1
- class SwitchObserver < ActiveRecord::Observer
2
- cattr_accessor :notifications
3
- self.notifications = []
4
-
5
- def before_turn_on(switch, from_state, to_state)
6
- notifications << ['before_turn_on', switch, from_state, to_state]
7
- end
8
-
9
- def after_turn_on(switch, from_state, to_state)
10
- notifications << ['after_turn_on', switch, from_state, to_state]
11
- end
12
-
13
- def before_transition(switch, attribute, event, from_state, to_state)
14
- notifications << ['before_transition', switch, attribute, event, from_state, to_state]
15
- end
16
-
17
- def after_transition(switch, attribute, event, from_state, to_state)
18
- notifications << ['after_transition', switch, attribute, event, from_state, to_state]
19
- end
20
- end
@@ -1,2 +0,0 @@
1
- class ToggleSwitch < Switch
2
- end
@@ -1,78 +0,0 @@
1
- class Vehicle < ActiveRecord::Base
2
- belongs_to :auto_shop
3
- belongs_to :highway
4
-
5
- attr_accessor :force_idle
6
- attr_accessor :callbacks
7
-
8
- # Defines the state machine for the state of the vehicled
9
- state_machine :state, :initial => Proc.new {|vehicle| vehicle.force_idle ? 'idling' : 'parked'} do
10
- before_transition :from => 'parked', :do => :put_on_seatbelt
11
- before_transition :to => 'stalled', :do => :increase_insurance_premium
12
- after_transition :to => 'parked', :do => lambda {|vehicle| vehicle.update_attribute(:seatbelt_on, false)}
13
- after_transition :on => 'crash', :do => :tow!
14
- after_transition :on => 'repair', :do => :fix!
15
-
16
- # Callback tracking for initial state callbacks
17
- after_transition :to => 'parked', :do => lambda {|vehicle| (vehicle.callbacks ||= []) << 'before_enter_parked'}
18
- before_transition :to => 'idling', :do => lambda {|vehicle| (vehicle.callbacks ||= []) << 'before_enter_idling'}
19
-
20
- event :park do
21
- transition :to => 'parked', :from => %w(idling first_gear)
22
- end
23
-
24
- event :ignite do
25
- transition :to => 'stalled', :from => 'stalled'
26
- transition :to => 'idling', :from => 'parked'
27
- end
28
-
29
- event :idle do
30
- transition :to => 'idling', :from => 'first_gear'
31
- end
32
-
33
- event :shift_up do
34
- transition :to => 'first_gear', :from => 'idling'
35
- transition :to => 'second_gear', :from => 'first_gear'
36
- transition :to => 'third_gear', :from => 'second_gear'
37
- end
38
-
39
- event :shift_down do
40
- transition :to => 'second_gear', :from => 'third_gear'
41
- transition :to => 'first_gear', :from => 'second_gear'
42
- end
43
-
44
- event :crash do
45
- transition :to => 'stalled', :from => %w(first_gear second_gear third_gear), :if => lambda {|vehicle| vehicle.auto_shop.available?}
46
- end
47
-
48
- event :repair do
49
- transition :to => 'parked', :from => 'stalled', :if => :auto_shop_busy?
50
- end
51
- end
52
-
53
- # Tows the vehicle to the auto shop
54
- def tow!
55
- auto_shop.tow_vehicle!
56
- end
57
-
58
- # Fixes the vehicle; it will no longer be in the auto shop
59
- def fix!
60
- auto_shop.fix_vehicle!
61
- end
62
-
63
- private
64
- # Safety first! Puts on our seatbelt
65
- def put_on_seatbelt
66
- self.seatbelt_on = true
67
- end
68
-
69
- # We crashed! Increase the insurance premium on the vehicle
70
- def increase_insurance_premium
71
- update_attribute(:insurance_premium, self.insurance_premium + 100)
72
- end
73
-
74
- # Is the auto shop currently servicing another customer?
75
- def auto_shop_busy?
76
- auto_shop.busy?
77
- end
78
- end
@@ -1,7 +0,0 @@
1
- require 'config/boot'
2
-
3
- Rails::Initializer.run do |config|
4
- config.cache_classes = false
5
- config.whiny_nils = true
6
- config.active_record.observers = :switch_observer
7
- end
@@ -1,12 +0,0 @@
1
- class CreateSwitches < ActiveRecord::Migration
2
- def self.up
3
- create_table :switches do |t|
4
- t.string :state, :null => false
5
- t.string :kind
6
- end
7
- end
8
-
9
- def self.down
10
- drop_table :switches
11
- end
12
- end
@@ -1,13 +0,0 @@
1
- class CreateAutoShops < ActiveRecord::Migration
2
- def self.up
3
- create_table :auto_shops do |t|
4
- t.string :name, :null => false
5
- t.integer :num_customers, :null => false
6
- t.string :state, :null => false
7
- end
8
- end
9
-
10
- def self.down
11
- drop_table :auto_shops
12
- end
13
- end
@@ -1,11 +0,0 @@
1
- class CreateHighways < ActiveRecord::Migration
2
- def self.up
3
- create_table :highways do |t|
4
- t.string :name, :null => false
5
- end
6
- end
7
-
8
- def self.down
9
- drop_table :highways
10
- end
11
- end
@@ -1,16 +0,0 @@
1
- class CreateVehicles < ActiveRecord::Migration
2
- def self.up
3
- create_table :vehicles do |t|
4
- t.references :highway, :null => false
5
- t.references :auto_shop, :null => false
6
- t.boolean :seatbelt_on, :null => false
7
- t.integer :insurance_premium, :null => false
8
- t.string :state, :null => false
9
- t.string :type
10
- end
11
- end
12
-
13
- def self.down
14
- drop_table :vehicles
15
- end
16
- end
data/test/factory.rb DELETED
@@ -1,77 +0,0 @@
1
- module Factory
2
- # Build actions for the model
3
- def self.build(model, &block)
4
- name = model.to_s.underscore
5
-
6
- define_method("#{name}_attributes", block)
7
- define_method("valid_#{name}_attributes") {|*args| valid_attributes_for(model, *args)}
8
- define_method("new_#{name}") {|*args| new_record(model, *args)}
9
- define_method("create_#{name}") {|*args| create_record(model, *args)}
10
- end
11
-
12
- # Get valid attributes for the model
13
- def valid_attributes_for(model, attributes = {})
14
- name = model.to_s.underscore
15
- send("#{name}_attributes", attributes)
16
- attributes.stringify_keys!
17
- attributes
18
- end
19
-
20
- # Build an unsaved record
21
- def new_record(model, *args)
22
- attributes = valid_attributes_for(model, *args)
23
- record = model.new(attributes)
24
- attributes.each {|attr, value| record.send("#{attr}=", value) if model.accessible_attributes && !model.accessible_attributes.include?(attr) || model.protected_attributes && model.protected_attributes.include?(attr)}
25
- record
26
- end
27
-
28
- # Build and save/reload a record
29
- def create_record(model, *args)
30
- record = new_record(model, *args)
31
- record.save!
32
- record.reload
33
- record
34
- end
35
-
36
- build AutoShop do |attributes|
37
- attributes.reverse_merge!(
38
- :name => "Joe's Auto Body",
39
- :num_customers => 0
40
- )
41
- end
42
-
43
- build Car do |attributes|
44
- valid_vehicle_attributes(attributes)
45
- end
46
-
47
- build Highway do |attributes|
48
- attributes.reverse_merge!(
49
- :name => 'Route 66'
50
- )
51
- end
52
-
53
- build Motorcycle do |attributes|
54
- valid_car_attributes(attributes)
55
- end
56
-
57
- build Switch do |attributes|
58
- attributes.reverse_merge!(
59
- :state => 'off'
60
- )
61
- end
62
-
63
- build ToggleSwitch do |attributes|
64
- attributes.reverse_merge!(
65
- :state => 'off'
66
- )
67
- end
68
-
69
- build Vehicle do |attributes|
70
- attributes[:highway] = create_highway unless attributes.include?(:highway)
71
- attributes[:auto_shop] = create_auto_shop unless attributes.include?(:auto_shop)
72
- attributes.reverse_merge!(
73
- :seatbelt_on => false,
74
- :insurance_premium => 50
75
- )
76
- end
77
- end