state_machines-activerecord 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +20 -0
  4. data/Appraisals +11 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE.txt +23 -0
  7. data/README.md +65 -0
  8. data/Rakefile +9 -0
  9. data/gemfiles/active_record_4.1.gemfile +13 -0
  10. data/gemfiles/active_record_4.2.gemfile +13 -0
  11. data/lib/state_machines-activerecord.rb +1 -0
  12. data/lib/state_machines/integrations/active_record.rb +588 -0
  13. data/lib/state_machines/integrations/active_record/locale.rb +12 -0
  14. data/lib/state_machines/integrations/active_record/version.rb +7 -0
  15. data/log/.gitkeep +0 -0
  16. data/state_machines-activerecord.gemspec +27 -0
  17. data/test/files/en.yml +5 -0
  18. data/test/files/models/post.rb +11 -0
  19. data/test/integration_test.rb +23 -0
  20. data/test/machine_by_default_test.rb +16 -0
  21. data/test/machine_errors_test.rb +19 -0
  22. data/test/machine_multiple_test.rb +17 -0
  23. data/test/machine_nested_action_test.rb +38 -0
  24. data/test/machine_unmigrated_test.rb +14 -0
  25. data/test/machine_with_aliased_attribute_test.rb +23 -0
  26. data/test/machine_with_callbacks_test.rb +172 -0
  27. data/test/machine_with_column_state_attribute_test.rb +44 -0
  28. data/test/machine_with_complex_pluralization_scopes_test.rb +16 -0
  29. data/test/machine_with_conflicting_predicate_test.rb +18 -0
  30. data/test/machine_with_conflicting_state_name_test.rb +29 -0
  31. data/test/machine_with_custom_attribute_test.rb +21 -0
  32. data/test/machine_with_default_scope_test.rb +18 -0
  33. data/test/machine_with_different_column_default_test.rb +27 -0
  34. data/test/machine_with_different_integer_column_default_test.rb +29 -0
  35. data/test/machine_with_dirty_attribute_and_custom_attributes_during_loopback_test.rb +24 -0
  36. data/test/machine_with_dirty_attribute_and_state_events_test.rb +20 -0
  37. data/test/machine_with_dirty_attributes_and_custom_attribute_test.rb +32 -0
  38. data/test/machine_with_dirty_attributes_during_loopback_test.rb +22 -0
  39. data/test/machine_with_dirty_attributes_test.rb +35 -0
  40. data/test/machine_with_dynamic_initial_state_test.rb +99 -0
  41. data/test/machine_with_event_attributes_on_autosave_test.rb +48 -0
  42. data/test/machine_with_event_attributes_on_custom_action_test.rb +41 -0
  43. data/test/machine_with_event_attributes_on_save_bang_test.rb +82 -0
  44. data/test/machine_with_event_attributes_on_save_test.rb +184 -0
  45. data/test/machine_with_event_attributes_on_validation_test.rb +126 -0
  46. data/test/machine_with_events_test.rb +13 -0
  47. data/test/machine_with_failed_action_test.rb +40 -0
  48. data/test/machine_with_failed_after_callbacks_test.rb +35 -0
  49. data/test/machine_with_failed_before_callbacks_test.rb +36 -0
  50. data/test/machine_with_initialized_state_test.rb +35 -0
  51. data/test/machine_with_internationalization_test.rb +180 -0
  52. data/test/machine_with_loopback_test.rb +22 -0
  53. data/test/machine_with_non_column_state_attribute_defined_test.rb +35 -0
  54. data/test/machine_with_non_column_state_attribute_undefined_test.rb +33 -0
  55. data/test/machine_with_same_column_default_test.rb +26 -0
  56. data/test/machine_with_scopes_and_joins_test.rb +37 -0
  57. data/test/machine_with_scopes_and_owner_subclass_test.rb +27 -0
  58. data/test/machine_with_scopes_test.rb +70 -0
  59. data/test/machine_with_state_driven_validations_test.rb +30 -0
  60. data/test/machine_with_states_test.rb +13 -0
  61. data/test/machine_with_static_initial_state_test.rb +166 -0
  62. data/test/machine_with_transactions_test.rb +26 -0
  63. data/test/machine_with_validations_and_custom_attribute_test.rb +21 -0
  64. data/test/machine_with_validations_test.rb +47 -0
  65. data/test/machine_without_database_test.rb +20 -0
  66. data/test/machine_without_transactions_test.rb +26 -0
  67. data/test/model_test.rb +12 -0
  68. data/test/test_helper.rb +42 -0
  69. metadata +264 -0
@@ -0,0 +1,37 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithScopesAndJoinsTest < BaseTestCase
4
+ def setup
5
+ @company = new_model(:company)
6
+ MachineWithScopesAndJoinsTest.const_set('Company', @company)
7
+
8
+ @vehicle = new_model(:vehicle) do
9
+ connection.add_column table_name, :company_id, :integer
10
+ belongs_to :company, :class_name => 'MachineWithScopesAndJoinsTest::Company'
11
+ end
12
+ MachineWithScopesAndJoinsTest.const_set('Vehicle', @vehicle)
13
+
14
+ @company_machine = StateMachines::Machine.new(@company, :initial => :active)
15
+ @vehicle_machine = StateMachines::Machine.new(@vehicle, :initial => :parked)
16
+ @vehicle_machine.state :idling
17
+
18
+ @ford = @company.create
19
+ @mustang = @vehicle.create(:company => @ford)
20
+ end
21
+
22
+ def test_should_find_records_in_with_scope
23
+ assert_equal [@mustang], @vehicle.with_states(:parked).joins(:company).where("#{@company.table_name}.state = \"active\"")
24
+ end
25
+
26
+ def test_should_find_records_in_without_scope
27
+ assert_equal [@mustang], @vehicle.without_states(:idling).joins(:company).where("#{@company.table_name}.state = \"active\"")
28
+ end
29
+
30
+ def teardown
31
+ MachineWithScopesAndJoinsTest.class_eval do
32
+ remove_const('Vehicle')
33
+ remove_const('Company')
34
+ end
35
+ ActiveSupport::Dependencies.clear
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :state)
7
+
8
+ @subclass = Class.new(@model)
9
+ @subclass_machine = @subclass.state_machine(:state) {}
10
+ @subclass_machine.state :parked, :idling, :first_gear
11
+ end
12
+
13
+ def test_should_only_include_records_with_subclass_states_in_with_scope
14
+ parked = @subclass.create :state => 'parked'
15
+ idling = @subclass.create :state => 'idling'
16
+
17
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling).all
18
+ end
19
+
20
+ def test_should_only_include_records_without_subclass_states_in_without_scope
21
+ parked = @subclass.create :state => 'parked'
22
+ idling = @subclass.create :state => 'idling'
23
+ first_gear = @subclass.create :state => 'first_gear'
24
+
25
+ assert_equal [parked, idling], @subclass.without_states(:first_gear).all
26
+ end
27
+ end
@@ -0,0 +1,70 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithScopesTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model)
7
+ @machine.state :parked, :first_gear
8
+ @machine.state :idling, :value => -> { 'idling' }
9
+ end
10
+
11
+ def test_should_create_singular_with_scope
12
+ assert @model.respond_to?(:with_state)
13
+ end
14
+
15
+ def test_should_only_include_records_with_state_in_singular_with_scope
16
+ parked = @model.create :state => 'parked'
17
+ @model.create :state => 'idling'
18
+
19
+ assert_equal [parked], @model.with_state(:parked).all
20
+ end
21
+
22
+ def test_should_create_plural_with_scope
23
+ assert @model.respond_to?(:with_states)
24
+ end
25
+
26
+ def test_should_only_include_records_with_states_in_plural_with_scope
27
+ parked = @model.create :state => 'parked'
28
+ idling = @model.create :state => 'idling'
29
+
30
+ assert_equal [parked, idling], @model.with_states(:parked, :idling).all
31
+ end
32
+
33
+ def test_should_allow_lookup_by_string_name
34
+ parked = @model.create :state => 'parked'
35
+ idling = @model.create :state => 'idling'
36
+
37
+ assert_equal [parked, idling], @model.with_states('parked', 'idling').all
38
+ end
39
+
40
+ def test_should_create_singular_without_scope
41
+ assert @model.respond_to?(:without_state)
42
+ end
43
+
44
+ def test_should_only_include_records_without_state_in_singular_without_scope
45
+ parked = @model.create :state => 'parked'
46
+ idling = @model.create :state => 'idling'
47
+
48
+ assert_equal [parked], @model.without_state(:idling).all
49
+ end
50
+
51
+ def test_should_create_plural_without_scope
52
+ assert @model.respond_to?(:without_states)
53
+ end
54
+
55
+ def test_should_only_include_records_without_states_in_plural_without_scope
56
+ parked = @model.create :state => 'parked'
57
+ idling = @model.create :state => 'idling'
58
+ first_gear = @model.create :state => 'first_gear'
59
+
60
+ assert_equal [parked, idling], @model.without_states(:first_gear).all
61
+ end
62
+
63
+ def test_should_allow_chaining_scopes
64
+ parked = @model.create :state => 'parked'
65
+ idling = @model.create :state => 'idling'
66
+
67
+ assert_equal [idling], @model.without_state(:parked).with_state(:idling).all
68
+ end
69
+ end
70
+
@@ -0,0 +1,30 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ attr_accessor :seatbelt
7
+ end
8
+
9
+ @machine = StateMachines::Machine.new(@model)
10
+ @machine.state :first_gear, :second_gear do
11
+ validates_presence_of :seatbelt
12
+ end
13
+ @machine.other_states :parked
14
+ end
15
+
16
+ def test_should_be_valid_if_validation_fails_outside_state_scope
17
+ record = @model.new(:state => 'parked', :seatbelt => nil)
18
+ assert record.valid?
19
+ end
20
+
21
+ def test_should_be_invalid_if_validation_fails_within_state_scope
22
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
23
+ refute record.valid?
24
+ end
25
+
26
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
27
+ record = @model.new(:state => 'second_gear', :seatbelt => true)
28
+ assert record.valid?
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithStatesTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model)
7
+ @machine.state :first_gear
8
+ end
9
+
10
+ def test_should_humanize_name
11
+ assert_equal 'first gear', @machine.state(:first_gear).human_name
12
+ end
13
+ end
@@ -0,0 +1,166 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithStaticInitialStateTest < BaseTestCase
4
+ def setup
5
+ @model = new_model(:vehicle) do
6
+ attr_accessor :value
7
+ end
8
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
9
+ end
10
+
11
+ def test_should_set_initial_state_on_created_object
12
+ record = @model.new
13
+ assert_equal 'parked', record.state
14
+ end
15
+
16
+ def test_should_set_initial_state_with_nil_attributes
17
+ record = @model.new(nil)
18
+ assert_equal 'parked', record.state
19
+ end
20
+
21
+ def test_should_still_set_attributes
22
+ record = @model.new(:value => 1)
23
+ assert_equal 1, record.value
24
+ end
25
+
26
+ def test_should_still_allow_initialize_blocks
27
+ block_args = nil
28
+ record = @model.new do |*args|
29
+ block_args = args
30
+ end
31
+
32
+ assert_equal [record], block_args
33
+ end
34
+
35
+ def test_should_set_attributes_prior_to_initialize_block
36
+ state = nil
37
+ @model.new do |record|
38
+ state = record.state
39
+ end
40
+
41
+ assert_equal 'parked', state
42
+ end
43
+
44
+ def test_should_set_attributes_prior_to_after_initialize_hook
45
+ state = nil
46
+ @model.after_initialize do |record|
47
+ state = record.state
48
+ end
49
+ @model.new
50
+ assert_equal 'parked', state
51
+ end
52
+
53
+ def test_should_set_initial_state_before_setting_attributes
54
+ @model.class_eval do
55
+ attr_accessor :state_during_setter
56
+
57
+ remove_method :value=
58
+ define_method(:value=) do |value|
59
+ self.state_during_setter = state
60
+ end
61
+ end
62
+ record = @model.new
63
+ record.value = 1
64
+ assert_equal 'parked', record.state_during_setter
65
+ end
66
+
67
+ def test_should_not_set_initial_state_after_already_initialized
68
+ record = @model.new(:value => 1)
69
+ assert_equal 'parked', record.state
70
+
71
+ record.state = 'idling'
72
+ record.attributes = {}
73
+ assert_equal 'idling', record.state
74
+ end
75
+
76
+ def test_should_persist_initial_state
77
+ record = @model.new
78
+ record.save
79
+ record.reload
80
+ assert_equal 'parked', record.state
81
+ end
82
+
83
+ def test_should_persist_initial_state_on_dup
84
+ record = @model.create.dup
85
+ record.save
86
+ record.reload
87
+ assert_equal 'parked', record.state
88
+ end
89
+
90
+ def test_should_use_stored_values_when_loading_from_database
91
+ @machine.state :idling
92
+
93
+ record = @model.find(@model.create(:state => 'idling').id)
94
+ assert_equal 'idling', record.state
95
+ end
96
+
97
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
98
+ @machine.state nil
99
+
100
+ record = @model.find(@model.create(:state => nil).id)
101
+ assert_nil record.state
102
+ end
103
+
104
+ def test_should_use_stored_values_when_loading_for_many_association
105
+ @machine.state :idling
106
+
107
+ @model.connection.add_column @model.table_name, :owner_id, :integer
108
+ @model.reset_column_information
109
+ MachineWithStaticInitialStateTest.const_set('Vehicle', @model)
110
+
111
+ owner_model = new_model(:owner) do
112
+ has_many :vehicles, :class_name => 'MachineWithStaticInitialStateTest::Vehicle'
113
+ end
114
+ MachineWithStaticInitialStateTest.const_set('Owner', owner_model)
115
+
116
+ owner = owner_model.create
117
+ record = @model.create(:state => 'idling', :owner_id => owner.id)
118
+ assert_equal 'idling', owner.vehicles[0].state
119
+ end
120
+
121
+ def test_should_use_stored_values_when_loading_for_one_association
122
+ @machine.state :idling
123
+
124
+ @model.connection.add_column @model.table_name, :owner_id, :integer
125
+ @model.reset_column_information
126
+ MachineWithStaticInitialStateTest.const_set('Vehicle', @model)
127
+
128
+ owner_model = new_model(:owner) do
129
+ has_one :vehicle, :class_name => 'MachineWithStaticInitialStateTest::Vehicle'
130
+ end
131
+ MachineWithStaticInitialStateTest.const_set('Owner', owner_model)
132
+
133
+ owner = owner_model.create
134
+ record = @model.create(:state => 'idling', :owner_id => owner.id)
135
+ assert_equal 'idling', owner.vehicle.state
136
+ end
137
+
138
+ def test_should_use_stored_values_when_loading_for_belongs_to_association
139
+ @machine.state :idling
140
+
141
+ MachineWithStaticInitialStateTest.const_set('Vehicle', @model)
142
+
143
+ driver_model = new_model(:driver) do
144
+ connection.add_column table_name, :vehicle_id, :integer
145
+
146
+ belongs_to :vehicle, :class_name => 'MachineWithStaticInitialStateTest::Vehicle'
147
+ end
148
+
149
+ MachineWithStaticInitialStateTest.const_set('Driver', driver_model)
150
+
151
+ record = @model.create(:state => 'idling')
152
+ driver = driver_model.create(:vehicle_id => record.id)
153
+ assert_equal 'idling', driver.vehicle.state
154
+ end
155
+
156
+ def teardown
157
+ MachineWithStaticInitialStateTest.class_eval do
158
+ remove_const('Vehicle') if defined?(MachineWithStaticInitialStateTest::Vehicle)
159
+ remove_const('Owner') if defined?(MachineWithStaticInitialStateTest::Owner)
160
+ remove_const('Driver') if defined?(MachineWithStaticInitialStateTest::Driver)
161
+ end
162
+ ActiveSupport::Dependencies.clear
163
+ super
164
+ end
165
+ end
166
+
@@ -0,0 +1,26 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithTransactionsTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :use_transactions => true)
7
+ end
8
+
9
+ def test_should_rollback_transaction_if_false
10
+ @machine.within_transaction(@model.new) do
11
+ @model.create
12
+ false
13
+ end
14
+
15
+ assert_equal 0, @model.count
16
+ end
17
+
18
+ def test_should_not_rollback_transaction_if_true
19
+ @machine.within_transaction(@model.new) do
20
+ @model.create
21
+ true
22
+ end
23
+
24
+ assert_equal 1, @model.count
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :status, :attribute => :state)
7
+ @machine.state :parked
8
+
9
+ @record = @model.new
10
+ end
11
+
12
+ def test_should_add_validation_errors_to_custom_attribute
13
+ @record.state = 'invalid'
14
+
15
+ refute @record.valid?
16
+ assert_equal ['State is invalid'], @record.errors.full_messages
17
+
18
+ @record.state = 'parked'
19
+ assert @record.valid?
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithValidationsTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model)
7
+ @machine.state :parked
8
+
9
+ @record = @model.new
10
+ end
11
+
12
+ def test_should_invalidate_using_errors
13
+ I18n.backend = I18n::Backend::Simple.new
14
+ @record.state = 'parked'
15
+
16
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
17
+ assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
18
+ end
19
+
20
+ def test_should_auto_prefix_custom_attributes_on_invalidation
21
+ @machine.invalidate(@record, :event, :invalid)
22
+
23
+ assert_equal ['State event is invalid'], @record.errors.full_messages
24
+ end
25
+
26
+ def test_should_clear_errors_on_reset
27
+ @record.state = 'parked'
28
+ @record.errors.add(:state, 'is invalid')
29
+
30
+ @machine.reset(@record)
31
+ assert_equal [], @record.errors.full_messages
32
+ end
33
+
34
+ def test_should_be_valid_if_state_is_known
35
+ @record.state = 'parked'
36
+
37
+ assert @record.valid?
38
+ end
39
+
40
+ def test_should_not_be_valid_if_state_is_unknown
41
+ @record.state = 'invalid'
42
+
43
+ refute @record.valid?
44
+ assert_equal ['State is invalid'], @record.errors.full_messages
45
+ end
46
+ end
47
+