state_machines-mongoid 0.0.0 → 0.1.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +20 -0
  4. data/Appraisals +8 -0
  5. data/Gemfile +0 -2
  6. data/LICENSE.txt +1 -0
  7. data/README.md +43 -4
  8. data/Rakefile +8 -1
  9. data/gemfiles/active_model_4.1.gemfile +7 -0
  10. data/gemfiles/active_model_4.2.gemfile +7 -0
  11. data/lib/state_machines-mongoid.rb +1 -0
  12. data/lib/state_machines/integrations/mongoid.rb +401 -0
  13. data/lib/state_machines/integrations/mongoid/locale.rb +11 -0
  14. data/lib/state_machines/integrations/mongoid/version.rb +7 -0
  15. data/lib/state_machines/state_machines-mongoid.rb +1 -0
  16. data/state_machines-mongoid.gemspec +18 -12
  17. data/test/files/en.yml +5 -0
  18. data/test/integration_test.rb +23 -0
  19. data/test/machine_by_default_test.rb +16 -0
  20. data/test/machine_errors_test.rb +20 -0
  21. data/test/machine_multiple_test.rb +17 -0
  22. data/test/machine_nested_action_test.rb +39 -0
  23. data/test/machine_with_aliased_attribute_test.rb +22 -0
  24. data/test/machine_with_aliased_field_test.rb +22 -0
  25. data/test/machine_with_callbacks_test.rb +167 -0
  26. data/test/machine_with_column_state_attribute_test.rb +44 -0
  27. data/test/machine_with_complex_pluralization_scopes_test.rb +16 -0
  28. data/test/machine_with_conflicting_predicate_test.rb +27 -0
  29. data/test/machine_with_conflicting_state_name_test.rb +29 -0
  30. data/test/machine_with_custom_attribute_test.rb +21 -0
  31. data/test/machine_with_default_scope_test.rb +14 -0
  32. data/test/machine_with_different_column_default_test.rb +26 -0
  33. data/test/machine_with_different_integer_column_default_test.rb +27 -0
  34. data/test/machine_with_different_same_default_test.rb +26 -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 +50 -0
  42. data/test/machine_with_event_attributes_on_custom_action_test.rb +40 -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 +146 -0
  45. data/test/machine_with_event_attributes_on_validation_test.rb +115 -0
  46. data/test/machine_with_events_test.rb +13 -0
  47. data/test/machine_with_failed_action_test.rb +39 -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_field_test.rb +16 -0
  51. data/test/machine_with_initialized_state_test.rb +35 -0
  52. data/test/machine_with_internationalization_test.rb +175 -0
  53. data/test/machine_with_loopback_test.rb +26 -0
  54. data/test/machine_with_non_column_state_attribute_defined_test.rb +35 -0
  55. data/test/machine_with_non_column_state_attribute_undefined_test.rb +33 -0
  56. data/test/machine_with_scopes_and_owner_subclass_test.rb +42 -0
  57. data/test/machine_with_scopes_test.rb +69 -0
  58. data/test/machine_with_state_driven_validations_test.rb +33 -0
  59. data/test/machine_with_state_test.rb +13 -0
  60. data/test/machine_with_static_initial_state_test.rb +162 -0
  61. data/test/machine_with_validations_and_custom_attribute_test.rb +21 -0
  62. data/test/machine_with_validations_test.rb +46 -0
  63. data/test/machine_without_field_test.rb +14 -0
  64. data/test/test_helper.rb +90 -0
  65. metadata +170 -9
  66. data/lib/state_machines/mongoid.rb +0 -7
  67. data/lib/state_machines/mongoid/version.rb +0 -5
@@ -0,0 +1,36 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
4
+ def setup
5
+ @callbacks = []
6
+
7
+ @model = new_model
8
+ @machine = StateMachines::Machine.new(@model)
9
+ @machine.state :parked, :idling
10
+ @machine.event :ignite
11
+ @machine.before_transition {@callbacks << :before_1; false}
12
+ @machine.before_transition {@callbacks << :before_2}
13
+ @machine.after_transition {@callbacks << :after}
14
+ @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
15
+
16
+ @record = @model.new(:state => 'parked')
17
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
18
+ @result = @transition.perform
19
+ end
20
+
21
+ def test_should_not_be_successful
22
+ assert !@result
23
+ end
24
+
25
+ def test_should_not_change_current_state
26
+ assert_equal 'parked', @record.state
27
+ end
28
+
29
+ def test_should_not_run_action
30
+ assert @record.new_record?
31
+ end
32
+
33
+ def test_should_not_run_further_callbacks
34
+ assert_equal [:before_1], @callbacks
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithFieldTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ field :status, :type => Integer
7
+ end
8
+ StateMachines::Machine.new(@model, :status)
9
+ end
10
+
11
+ def test_should_not_redefine_field
12
+ field = @model.fields['status']
13
+ refute_nil field
14
+ assert_equal Integer, field.type
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithInitializedStateTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
7
+ @machine.state :idling
8
+ end
9
+
10
+ def test_should_allow_nil_initial_state_when_static
11
+ @machine.state nil
12
+
13
+ record = @model.new(:state => nil)
14
+ assert_nil record.state
15
+ end
16
+
17
+ def test_should_allow_nil_initial_state_when_dynamic
18
+ @machine.state nil
19
+
20
+ @machine.initial_state = lambda {:parked}
21
+ record = @model.new(:state => nil)
22
+ assert_nil record.state
23
+ end
24
+
25
+ def test_should_allow_different_initial_state_when_static
26
+ record = @model.new(:state => 'idling')
27
+ assert_equal 'idling', record.state
28
+ end
29
+
30
+ def test_should_allow_different_initial_state_when_dynamic
31
+ @machine.initial_state = lambda {:parked}
32
+ record = silence_warnings { @model.new(:state => 'idling') }
33
+ assert_equal 'idling', record.state
34
+ end
35
+ end
@@ -0,0 +1,175 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithInternationalizationTest < BaseTestCase
4
+ def setup
5
+ I18n.backend = I18n::Backend::Simple.new
6
+
7
+ # Initialize the backend
8
+ StateMachines::Machine.new(new_model)
9
+ @model = new_model
10
+ end
11
+
12
+ def test_should_use_defaults
13
+ I18n.backend.store_translations(:en, {
14
+ :mongoid => {:errors => {:messages => {:invalid_transition => 'cannot %{event}'}}}
15
+ })
16
+
17
+ machine = StateMachines::Machine.new(@model)
18
+ machine.state :parked, :idling
19
+ machine.event :ignite
20
+
21
+ record = @model.new(:state => 'idling')
22
+
23
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
24
+ assert_equal ['State cannot transition via "ignite"'], record.errors.full_messages
25
+ end
26
+
27
+ def test_should_allow_customized_error_key
28
+ I18n.backend.store_translations(:en, {
29
+ :mongoid => {:errors => {:messages => {:bad_transition => 'cannot %{event}'}}}
30
+ })
31
+
32
+ machine = StateMachines::Machine.new(@model, :messages => {:invalid_transition => :bad_transition})
33
+ machine.state :parked, :idling
34
+
35
+ record = @model.new(:state => 'idling')
36
+
37
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
38
+ assert_equal ['State cannot ignite'], record.errors.full_messages
39
+ end
40
+
41
+ def test_should_allow_customized_error_string
42
+ machine = StateMachines::Machine.new(@model, :messages => {:invalid_transition => 'cannot %{event}'})
43
+ machine.state :parked, :idling
44
+
45
+ record = @model.new(:state => 'idling')
46
+
47
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
48
+ assert_equal ['State cannot ignite'], record.errors.full_messages
49
+ end
50
+
51
+ def test_should_allow_customized_state_key_scoped_to_class_and_machine
52
+ I18n.backend.store_translations(:en, {
53
+ :mongoid => {:state_machines => {:'mongoid_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
54
+ })
55
+
56
+ machine = StateMachines::Machine.new(@model)
57
+ machine.state :parked
58
+
59
+ assert_equal 'shutdown', machine.state(:parked).human_name
60
+ end
61
+
62
+ def test_should_allow_customized_state_key_scoped_to_class
63
+ I18n.backend.store_translations(:en, {
64
+ :mongoid => {:state_machines => {:'mongoid_test/foo' => {:states => {:parked => 'shutdown'}}}}
65
+ })
66
+
67
+ machine = StateMachines::Machine.new(@model)
68
+ machine.state :parked
69
+
70
+ assert_equal 'shutdown', machine.state(:parked).human_name
71
+ end
72
+
73
+ def test_should_allow_customized_state_key_scoped_to_machine
74
+ I18n.backend.store_translations(:en, {
75
+ :mongoid => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
76
+ })
77
+
78
+ machine = StateMachines::Machine.new(@model)
79
+ machine.state :parked
80
+
81
+ assert_equal 'shutdown', machine.state(:parked).human_name
82
+ end
83
+
84
+ def test_should_allow_customized_state_key_unscoped
85
+ I18n.backend.store_translations(:en, {
86
+ :mongoid => {:state_machines => {:states => {:parked => 'shutdown'}}}
87
+ })
88
+
89
+ machine = StateMachines::Machine.new(@model)
90
+ machine.state :parked
91
+
92
+ assert_equal 'shutdown', machine.state(:parked).human_name
93
+ end
94
+
95
+ def test_should_support_nil_state_key
96
+ I18n.backend.store_translations(:en, {
97
+ :mongoid => {:state_machines => {:states => {:nil => 'empty'}}}
98
+ })
99
+
100
+ machine = StateMachines::Machine.new(@model)
101
+
102
+ assert_equal 'empty', machine.state(nil).human_name
103
+ end
104
+
105
+ def test_should_allow_customized_event_key_scoped_to_class_and_machine
106
+ I18n.backend.store_translations(:en, {
107
+ :mongoid => {:state_machines => {:'mongoid_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
108
+ })
109
+
110
+ machine = StateMachines::Machine.new(@model)
111
+ machine.event :park
112
+
113
+ assert_equal 'stop', machine.event(:park).human_name
114
+ end
115
+
116
+ def test_should_allow_customized_event_key_scoped_to_class
117
+ I18n.backend.store_translations(:en, {
118
+ :mongoid => {:state_machines => {:'mongoid_test/foo' => {:events => {:park => 'stop'}}}}
119
+ })
120
+
121
+ machine = StateMachines::Machine.new(@model)
122
+ machine.event :park
123
+
124
+ assert_equal 'stop', machine.event(:park).human_name
125
+ end
126
+
127
+ def test_should_allow_customized_event_key_scoped_to_machine
128
+ I18n.backend.store_translations(:en, {
129
+ :mongoid => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
130
+ })
131
+
132
+ machine = StateMachines::Machine.new(@model)
133
+ machine.event :park
134
+
135
+ assert_equal 'stop', machine.event(:park).human_name
136
+ end
137
+
138
+ def test_should_allow_customized_event_key_unscoped
139
+ I18n.backend.store_translations(:en, {
140
+ :mongoid => {:state_machines => {:events => {:park => 'stop'}}}
141
+ })
142
+
143
+ machine = StateMachines::Machine.new(@model)
144
+ machine.event :park
145
+
146
+ assert_equal 'stop', machine.event(:park).human_name
147
+ end
148
+
149
+ def test_should_only_add_locale_once_in_load_path
150
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{mongoid/locale\.rb$}}.length
151
+
152
+ # Create another Mongoid model that will triger the i18n feature
153
+ new_model
154
+
155
+ assert_equal 1, I18n.load_path.select {|path| path =~ %r{mongoid/locale\.rb$}}.length
156
+ end
157
+
158
+ def test_should_prefer_other_locales_first
159
+ @original_load_path = I18n.load_path
160
+ I18n.backend = I18n::Backend::Simple.new
161
+ I18n.load_path = [File.dirname(__FILE__) + '/files/en.yml']
162
+
163
+ machine = StateMachines::Machine.new(@model)
164
+ machine.state :parked, :idling
165
+ machine.event :ignite
166
+
167
+ record = @model.new(:state => 'idling')
168
+
169
+ machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
170
+ assert_equal ['State cannot transition'], record.errors.full_messages
171
+ ensure
172
+ I18n.load_path = @original_load_path
173
+ end
174
+
175
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithLoopbackTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ field :updated_at, :type => Time
7
+
8
+ before_update do |record|
9
+ record.updated_at = Time.now
10
+ end
11
+ end
12
+
13
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
14
+ @machine.event :park
15
+
16
+ @record = @model.create(:updated_at => Time.now - 1)
17
+ @transition = StateMachines::Transition.new(@record, @machine, :park, :parked, :parked)
18
+
19
+ @timestamp = @record.updated_at
20
+ @transition.perform
21
+ end
22
+
23
+ def test_should_update_record
24
+ refute_equal @timestamp, @record.updated_at
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ def status
7
+ self['status']
8
+ end
9
+
10
+ def status=(value)
11
+ self['status'] = value
12
+ end
13
+ end
14
+
15
+ @machine = StateMachines::Machine.new(@model, :status, :initial => :parked)
16
+ @machine.other_states(:idling)
17
+ @record = @model.new
18
+ end
19
+
20
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
21
+ assert !@record.status?(:idling)
22
+ end
23
+
24
+ def test_should_return_true_for_predicate_if_matches_current_value
25
+ assert @record.status?(:parked)
26
+ end
27
+
28
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
29
+ assert_raises(IndexError) { @record.status?(:invalid) }
30
+ end
31
+
32
+ def test_should_set_initial_state_on_created_object
33
+ assert_equal 'parked', @record.status
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ def initialize
7
+ # Skip attribute initialization
8
+ @initialized_state_machines = true
9
+ super
10
+ end
11
+ end
12
+
13
+ @machine = StateMachines::Machine.new(@model, :status, :initial => :parked)
14
+ @machine.other_states(:idling)
15
+ @record = @model.new
16
+ end
17
+
18
+ def test_should_define_a_new_key_for_the_attribute
19
+ refute_nil @model.fields['status']
20
+ end
21
+
22
+ def test_should_define_a_reader_attribute_for_the_attribute
23
+ assert @record.respond_to?(:status)
24
+ end
25
+
26
+ def test_should_define_a_writer_attribute_for_the_attribute
27
+ assert @record.respond_to?(:status=)
28
+ end
29
+
30
+ def test_should_define_an_attribute_predicate
31
+ assert @record.respond_to?(:status?)
32
+ end
33
+ end
@@ -0,0 +1,42 @@
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
+ MongoidTest.const_set('Foo', @model)
9
+
10
+ # Remove the #name override so that Mongoid picks up the subclass name
11
+ # properly
12
+ class << @model; remove_method(:name); end
13
+ @subclass = MongoidTest.class_eval <<-end_eval
14
+ class SubFoo < MongoidTest::Foo
15
+ self
16
+ end
17
+ end_eval
18
+ @subclass_machine = @subclass.state_machine(:state) {}
19
+ @subclass_machine.state :parked, :idling, :first_gear
20
+ end
21
+
22
+ def test_should_only_include_records_with_subclass_states_in_with_scope
23
+ parked = @subclass.create :state => 'parked'
24
+ idling = @subclass.create :state => 'idling'
25
+
26
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling).to_a
27
+ end
28
+
29
+ def test_should_only_include_records_without_subclass_states_in_without_scope
30
+ parked = @subclass.create :state => 'parked'
31
+ idling = @subclass.create :state => 'idling'
32
+ first_gear = @subclass.create :state => 'first_gear'
33
+
34
+ assert_equal [parked, idling], @subclass.without_states(:first_gear).to_a
35
+ end
36
+
37
+ def teardown
38
+ MongoidTest.send(:remove_const, 'SubFoo')
39
+ MongoidTest.send(:remove_const, 'Foo')
40
+ super
41
+ end
42
+ end
@@ -0,0 +1,69 @@
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 => lambda {'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).to_a
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).to_a
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').to_a
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).to_a
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).to_a
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