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,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