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,12 @@
1
+ { en: {
2
+ activerecord: {
3
+ errors: {
4
+ messages: {
5
+ invalid: StateMachines::Machine.default_messages[:invalid],
6
+ invalid_event: StateMachines::Machine.default_messages[:invalid_event] % ['%{state}'],
7
+ invalid_transition: StateMachines::Machine.default_messages[:invalid_transition] % ['%{event}']
8
+ }
9
+ }
10
+ }
11
+ } }
12
+
@@ -0,0 +1,7 @@
1
+ module StateMachines
2
+ module Integrations
3
+ module ActiveRecord
4
+ VERSION = '0.0.1'
5
+ end
6
+ end
7
+ end
data/log/.gitkeep ADDED
File without changes
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'state_machines/integrations/active_record/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'state_machines-activerecord'
8
+ spec.version = StateMachines::Integrations::ActiveRecord::VERSION
9
+ spec.authors = ['Abdelkader Boudih', 'Aaron Pfeifer']
10
+ spec.email = %w(terminale@gmail.com aaron@pluginaweek.org)
11
+ spec.summary = %q(State machines Active Record Integration)
12
+ spec.description = %q(Adds support for creating state machines for attributes on ActiveRecord)
13
+ spec.homepage = 'https://github.com/seuros/state_machines-activemodel'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(/^test\//)
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'state_machines-activemodel', '~> 0.0.3'
21
+ spec.add_dependency 'activerecord' , '~> 4.1'
22
+ spec.add_development_dependency 'rake', '~> 10.3'
23
+ spec.add_development_dependency 'sqlite3', '~> 1.3'
24
+ spec.add_development_dependency 'appraisal', '>= 1'
25
+ spec.add_development_dependency 'minitest' , '>= 5.4.0'
26
+ spec.add_development_dependency 'minitest-reporters'
27
+ end
data/test/files/en.yml ADDED
@@ -0,0 +1,5 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_transition: "cannot transition"
@@ -0,0 +1,11 @@
1
+ ActiveRecord::Base.connection.create_table(:posts, :force => true) do |t|
2
+ t.string :title
3
+ t.string :content
4
+ t.string :state
5
+ end
6
+
7
+ class Post < ActiveRecord::Base
8
+ state_machine initial: :draft do
9
+
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'test_helper'
2
+
3
+ class IntegrationTest < BaseTestCase
4
+ def test_should_have_an_integration_name
5
+ assert_equal :active_record, StateMachines::Integrations::ActiveRecord.integration_name
6
+ end
7
+
8
+ def test_should_match_if_class_inherits_from_active_record
9
+ assert StateMachines::Integrations::ActiveRecord.matches?(new_model)
10
+ end
11
+
12
+ def test_should_not_match_if_class_does_not_inherit_from_active_record
13
+ refute StateMachines::Integrations::ActiveRecord.matches?(Class.new)
14
+ end
15
+
16
+ def test_should_have_defaults
17
+ assert_equal({:action => :save}, StateMachines::Integrations::ActiveRecord.defaults)
18
+ end
19
+
20
+ def test_should_have_a_locale_path
21
+ assert_not_nil StateMachines::Integrations::ActiveRecord.locale_path
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineByDefaultTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model)
7
+ end
8
+
9
+ def test_should_use_save_as_action
10
+ assert_equal :save, @machine.action
11
+ end
12
+
13
+ def test_should_use_transactions
14
+ assert_equal true, @machine.use_transactions
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineErrorsTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model)
7
+ @record = @model.new
8
+ end
9
+
10
+ def test_should_be_able_to_describe_current_errors
11
+ @record.errors.add(:id, 'cannot be blank')
12
+ @record.errors.add(:state, 'is invalid')
13
+ assert_equal ['Id cannot be blank', 'State is invalid'], @machine.errors_for(@record).split(', ').sort
14
+ end
15
+
16
+ def test_should_describe_as_halted_with_no_errors
17
+ assert_equal 'Transition halted', @machine.errors_for(@record)
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineMultipleTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ connection.add_column table_name, :status, :string
7
+ end
8
+ @state_machine = StateMachines::Machine.new(@model, :initial => :parked)
9
+ @status_machine = StateMachines::Machine.new(@model, :status, :initial => :idling)
10
+ end
11
+
12
+ def test_should_should_initialize_each_state
13
+ record = @model.new
14
+ assert_equal 'parked', record.state
15
+ assert_equal 'idling', record.status
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineNestedActionTest < BaseTestCase
4
+ def setup
5
+ @callbacks = []
6
+
7
+ @model = new_model
8
+ @machine = StateMachines::Machine.new(@model)
9
+ @machine.event :ignite do
10
+ transition :parked => :idling
11
+ end
12
+
13
+ @record = @model.new(:state => 'parked')
14
+ end
15
+
16
+ def test_should_allow_transition_prior_to_creation_if_skipping_action
17
+ record = @record
18
+ @model.before_create { record.ignite(false) }
19
+ result = @record.save
20
+
21
+ assert_equal true, result
22
+ assert_equal "idling", @record.state
23
+ @record.reload
24
+ assert_equal "idling", @record.state
25
+ end
26
+
27
+ def test_should_allow_transition_after_creation
28
+ record = @record
29
+ @model.after_create { record.ignite }
30
+ result = @record.save
31
+
32
+ assert_equal true, result
33
+ assert_equal "idling", @record.state
34
+ @record.reload
35
+ assert_equal "idling", @record.state
36
+ end
37
+ end
38
+
@@ -0,0 +1,14 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineUnmigratedTest < BaseTestCase
4
+ def setup
5
+ @model = new_model(false)
6
+
7
+ # Drop the table so that it definitely doesn't exist
8
+ @model.connection.drop_table(@model.table_name) if @model.table_exists?
9
+ end
10
+
11
+ def test_should_allow_machine_creation
12
+ assert_nothing_raised { StateMachines::Machine.new(@model) }
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithAliasedAttributeTest < BaseTestCase
4
+ def setup
5
+ @model = new_model do
6
+ alias_attribute :vehicle_status, :state
7
+ end
8
+
9
+ @machine = StateMachines::Machine.new(@model, :status, :attribute => :vehicle_status)
10
+ @machine.state :parked
11
+
12
+ @record = @model.new
13
+ end
14
+
15
+ def test_should_check_custom_attribute_for_predicate
16
+ @record.vehicle_status = nil
17
+ refute @record.status?(:parked)
18
+
19
+ @record.vehicle_status = 'parked'
20
+ assert @record.status?(:parked)
21
+ end
22
+ end
23
+
@@ -0,0 +1,172 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithCallbacksTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
7
+ @machine.other_states :idling
8
+ @machine.event :ignite
9
+
10
+ @record = @model.new(:state => 'parked')
11
+ @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
12
+ end
13
+
14
+ def test_should_run_before_callbacks
15
+ called = false
16
+ @machine.before_transition { called = true }
17
+
18
+ @transition.perform
19
+ assert called
20
+ end
21
+
22
+ def test_should_pass_record_to_before_callbacks_with_one_argument
23
+ record = nil
24
+ @machine.before_transition { |arg| record = arg }
25
+
26
+ @transition.perform
27
+ assert_equal @record, record
28
+ end
29
+
30
+ def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
31
+ callback_args = nil
32
+ @machine.before_transition { |*args| callback_args = args }
33
+
34
+ @transition.perform
35
+ assert_equal [@record, @transition], callback_args
36
+ end
37
+
38
+ def test_should_run_before_callbacks_outside_the_context_of_the_record
39
+ context = nil
40
+ @machine.before_transition { context = self }
41
+
42
+ @transition.perform
43
+ assert_equal self, context
44
+ end
45
+
46
+ def test_should_run_after_callbacks
47
+ called = false
48
+ @machine.after_transition { called = true }
49
+
50
+ @transition.perform
51
+ assert called
52
+ end
53
+
54
+ def test_should_pass_record_to_after_callbacks_with_one_argument
55
+ record = nil
56
+ @machine.after_transition { |arg| record = arg }
57
+
58
+ @transition.perform
59
+ assert_equal @record, record
60
+ end
61
+
62
+ def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
63
+ callback_args = nil
64
+ @machine.after_transition { |*args| callback_args = args }
65
+
66
+ @transition.perform
67
+ assert_equal [@record, @transition], callback_args
68
+ end
69
+
70
+ def test_should_run_after_callbacks_outside_the_context_of_the_record
71
+ context = nil
72
+ @machine.after_transition { context = self }
73
+
74
+ @transition.perform
75
+ assert_equal self, context
76
+ end
77
+
78
+ def test_should_run_after_callbacks_if_model_callback_added_prior_to_state_machine_definition
79
+ model = new_model do
80
+ after_save { nil }
81
+ end
82
+ machine = StateMachines::Machine.new(model, :initial => :parked)
83
+ machine.other_states :idling
84
+ machine.event :ignite
85
+ after_called = false
86
+ machine.after_transition { after_called = true }
87
+
88
+ record = model.new(:state => 'parked')
89
+ transition = StateMachines::Transition.new(record, machine, :ignite, :parked, :idling)
90
+ transition.perform
91
+ assert_equal true, after_called
92
+ end
93
+
94
+ def test_should_run_around_callbacks
95
+ before_called = false
96
+ after_called = false
97
+ ensure_called = 0
98
+ @machine.around_transition do |block|
99
+ before_called = true
100
+ begin
101
+ block.call
102
+ ensure
103
+ ensure_called += 1
104
+ end
105
+ after_called = true
106
+ end
107
+
108
+ @transition.perform
109
+ assert before_called
110
+ assert after_called
111
+ assert_equal ensure_called, 1
112
+ end
113
+
114
+ def test_should_include_transition_states_in_known_states
115
+ @machine.before_transition :to => :first_gear, :do => lambda {}
116
+
117
+ assert_equal [:parked, :idling, :first_gear], @machine.states.map { |state| state.name }
118
+ end
119
+
120
+ def test_should_allow_symbolic_callbacks
121
+ callback_args = nil
122
+
123
+ klass = class << @record;
124
+ self;
125
+ end
126
+ klass.send(:define_method, :after_ignite) do |*args|
127
+ callback_args = args
128
+ end
129
+
130
+ @machine.before_transition(:after_ignite)
131
+
132
+ @transition.perform
133
+ assert_equal [@transition], callback_args
134
+ end
135
+
136
+ def test_should_allow_string_callbacks
137
+ class << @record
138
+ attr_reader :callback_result
139
+ end
140
+
141
+ @machine.before_transition('@callback_result = [1, 2, 3]')
142
+ @transition.perform
143
+
144
+ assert_equal [1, 2, 3], @record.callback_result
145
+ end
146
+
147
+ def test_should_run_in_expected_order
148
+ expected = [
149
+ :before_transition, :before_validation, :after_validation,
150
+ :before_save, :before_create, :after_create, :after_save,
151
+ :after_transition
152
+ ]
153
+
154
+ callbacks = []
155
+ @model.before_validation { callbacks << :before_validation }
156
+ @model.after_validation { callbacks << :after_validation }
157
+ @model.before_save { callbacks << :before_save }
158
+ @model.before_create { callbacks << :before_create }
159
+ @model.after_create { callbacks << :after_create }
160
+ @model.after_save { callbacks << :after_save }
161
+ @model.after_commit { callbacks << :after_commit }
162
+ expected << :after_commit
163
+
164
+ @machine.before_transition { callbacks << :before_transition }
165
+ @machine.after_transition { callbacks << :after_transition }
166
+
167
+ @transition.perform
168
+
169
+ assert_equal expected, callbacks
170
+ end
171
+ end
172
+
@@ -0,0 +1,44 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MachineWithColumnStateAttributeTest < BaseTestCase
4
+ def setup
5
+ @model = new_model
6
+ @machine = StateMachines::Machine.new(@model, :initial => :parked)
7
+ @machine.other_states(:idling)
8
+
9
+ @record = @model.new
10
+ end
11
+
12
+ def test_should_not_override_the_column_reader
13
+ @record[:state] = 'parked'
14
+ assert_equal 'parked', @record.state
15
+ end
16
+
17
+ def test_should_not_override_the_column_writer
18
+ @record.state = 'parked'
19
+ assert_equal 'parked', @record[:state]
20
+ end
21
+
22
+ def test_should_have_an_attribute_predicate
23
+ assert @record.respond_to?(:state?)
24
+ end
25
+
26
+ def test_should_test_for_existence_on_predicate_without_parameters
27
+ assert @record.state?
28
+
29
+ @record.state = nil
30
+ refute @record.state?
31
+ end
32
+
33
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
34
+ refute @record.state?(:idling)
35
+ end
36
+
37
+ def test_should_return_true_for_predicate_if_matches_current_value
38
+ assert @record.state?(:parked)
39
+ end
40
+
41
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
42
+ assert_raises(IndexError) { @record.state?(:invalid) }
43
+ end
44
+ end