state_machines-activerecord 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+