state_machines-audit_trail 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +17 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +167 -0
- data/Rakefile +9 -0
- data/lib/state_machines-audit_trail.rb +2 -0
- data/lib/state_machines/audit_trail.rb +16 -0
- data/lib/state_machines/audit_trail/backend.rb +56 -0
- data/lib/state_machines/audit_trail/backend/active_record.rb +23 -0
- data/lib/state_machines/audit_trail/backend/mongoid.rb +13 -0
- data/lib/state_machines/audit_trail/railtie.rb +5 -0
- data/lib/state_machines/audit_trail/transition_auditing.rb +59 -0
- data/lib/state_machines/audit_trail/version.rb +5 -0
- data/lib/state_machines/audit_trail_generator.rb +29 -0
- data/spec/helpers/active_record.rb +222 -0
- data/spec/helpers/mongoid.rb +79 -0
- data/spec/helpers/mongoid.yml +6 -0
- data/spec/lib/state_machines/audit_trail/backend/active_record_spec.rb +225 -0
- data/spec/lib/state_machines/audit_trail/backend/mongoid_spec.rb +98 -0
- data/spec/lib/state_machines/audit_trail_generator_spec.rb +54 -0
- data/spec/lib/state_machines/audit_trail_spec.rb +8 -0
- data/spec/spec_helper.rb +8 -0
- data/state_machines-audit_trail.gemspec +36 -0
- metadata +237 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
### Setup test database
|
4
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
5
|
+
|
6
|
+
class ARModelStateTransition < ActiveRecord::Base
|
7
|
+
belongs_to :ar_model
|
8
|
+
end
|
9
|
+
class ARModelNoInitialStateTransition < ActiveRecord::Base
|
10
|
+
belongs_to :ar_model_no_initial
|
11
|
+
end
|
12
|
+
|
13
|
+
class ARModelWithContextStateTransition < ActiveRecord::Base
|
14
|
+
belongs_to :ar_model_with_context
|
15
|
+
end
|
16
|
+
|
17
|
+
class ARModelWithMultipleContextStateTransition < ActiveRecord::Base
|
18
|
+
belongs_to :ar_model_with_multiple_context
|
19
|
+
end
|
20
|
+
|
21
|
+
class ARModelWithMultipleStateMachinesFirstTransition < ActiveRecord::Base
|
22
|
+
belongs_to :ar_model_with_multiple_state_machines
|
23
|
+
end
|
24
|
+
|
25
|
+
class ARModelWithMultipleStateMachinesSecondTransition < ActiveRecord::Base
|
26
|
+
belongs_to :ar_model_with_multiple_state_machines
|
27
|
+
end
|
28
|
+
|
29
|
+
class ARModelWithMultipleStateMachinesThirdTransition < ActiveRecord::Base
|
30
|
+
belongs_to :ar_model_with_multiple_state_machines
|
31
|
+
end
|
32
|
+
|
33
|
+
class ARModel < ActiveRecord::Base
|
34
|
+
|
35
|
+
state_machine :state, initial: :waiting do # log initial state?
|
36
|
+
audit_trail
|
37
|
+
|
38
|
+
event :start do
|
39
|
+
transition [:waiting, :stopped] => :started
|
40
|
+
end
|
41
|
+
|
42
|
+
event :stop do
|
43
|
+
transition :started => :stopped
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ARModelNoInitial < ActiveRecord::Base
|
49
|
+
|
50
|
+
state_machine :state, initial: :waiting do # log initial state?
|
51
|
+
audit_trail initial: false
|
52
|
+
|
53
|
+
event :start do
|
54
|
+
transition [:waiting, :stopped] => :started
|
55
|
+
end
|
56
|
+
|
57
|
+
event :stop do
|
58
|
+
transition :started => :stopped
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
#
|
63
|
+
class ARModelWithContext < ActiveRecord::Base
|
64
|
+
state_machine :state, initial: :waiting do # log initial state?
|
65
|
+
audit_trail context: :context
|
66
|
+
|
67
|
+
event :start do
|
68
|
+
transition [:waiting, :stopped] => :started
|
69
|
+
end
|
70
|
+
|
71
|
+
event :stop do
|
72
|
+
transition :started => :stopped
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def context
|
77
|
+
'Some context'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class ARModelWithMultipleContext < ActiveRecord::Base
|
82
|
+
state_machine :state, initial: :waiting do # log initial state?
|
83
|
+
audit_trail context: [:context, :second_context, :context_with_args]
|
84
|
+
|
85
|
+
event :start do
|
86
|
+
transition [:waiting, :stopped] => :started
|
87
|
+
end
|
88
|
+
|
89
|
+
event :stop do
|
90
|
+
transition :started => :stopped
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def context
|
95
|
+
'Some context'
|
96
|
+
end
|
97
|
+
|
98
|
+
def second_context
|
99
|
+
'Extra context'
|
100
|
+
end
|
101
|
+
|
102
|
+
def context_with_args(transition)
|
103
|
+
id = transition.args.last.delete(:id) if transition.args.present?
|
104
|
+
id
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
class ARModelDescendant < ARModel
|
110
|
+
end
|
111
|
+
|
112
|
+
class ARModelDescendantWithOwnStateMachines < ARModel
|
113
|
+
state_machine :state, :initial => :new do
|
114
|
+
audit_trail
|
115
|
+
|
116
|
+
event :complete do
|
117
|
+
transition [:new] => :completed
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class ARModelWithMultipleStateMachines < ActiveRecord::Base
|
123
|
+
|
124
|
+
state_machine :first, :initial => :beginning do
|
125
|
+
audit_trail
|
126
|
+
|
127
|
+
event :begin_first do
|
128
|
+
transition :beginning => :end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
state_machine :second do
|
133
|
+
audit_trail
|
134
|
+
|
135
|
+
event :begin_second do
|
136
|
+
transition nil => :beginning_second
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
state_machine :third, :action => nil do
|
141
|
+
audit_trail
|
142
|
+
|
143
|
+
event :begin_third do
|
144
|
+
transition nil => :beginning_third
|
145
|
+
end
|
146
|
+
|
147
|
+
event :end_third do
|
148
|
+
transition :beginning_third => :done_third
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
module SomeNamespace
|
154
|
+
class ARModelStateTransition < ActiveRecord::Base
|
155
|
+
belongs_to :test_model
|
156
|
+
end
|
157
|
+
|
158
|
+
class ARModel < ActiveRecord::Base
|
159
|
+
|
160
|
+
state_machine :state, initial: :waiting do # log initial state?
|
161
|
+
audit_trail
|
162
|
+
|
163
|
+
event :start do
|
164
|
+
transition [:waiting, :stopped] => :started
|
165
|
+
end
|
166
|
+
|
167
|
+
event :stop do
|
168
|
+
transition :started => :stopped
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
#
|
175
|
+
# Generate tables
|
176
|
+
#
|
177
|
+
def create_model_table(owner_class, multiple_state_machines = false)
|
178
|
+
ActiveRecord::Base.connection.create_table(owner_class.name.tableize) do |t|
|
179
|
+
t.string :state unless multiple_state_machines
|
180
|
+
t.string :type
|
181
|
+
|
182
|
+
if multiple_state_machines
|
183
|
+
t.string :first
|
184
|
+
t.string :second
|
185
|
+
t.string :third
|
186
|
+
end
|
187
|
+
|
188
|
+
t.timestamps null: false
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
create_model_table(ARModel)
|
193
|
+
create_model_table(ARModelNoInitial)
|
194
|
+
create_model_table(ARModelWithContext)
|
195
|
+
create_model_table(ARModelWithMultipleContext)
|
196
|
+
create_model_table(ARModelWithMultipleStateMachines, true)
|
197
|
+
|
198
|
+
|
199
|
+
def create_transition_table(owner_class, state, add_context = false)
|
200
|
+
class_name = "#{owner_class.name}#{state.to_s.camelize}Transition"
|
201
|
+
ActiveRecord::Base.connection.create_table(class_name.tableize) do |t|
|
202
|
+
|
203
|
+
# t.references :"#{owner_class.name.pluralize.demodulize.tableize}"
|
204
|
+
t.integer owner_class.name.foreign_key
|
205
|
+
t.string :event
|
206
|
+
t.string :from
|
207
|
+
t.string :to
|
208
|
+
|
209
|
+
t.string :context if add_context
|
210
|
+
t.string :second_context if add_context
|
211
|
+
t.string :context_with_args if add_context
|
212
|
+
t.datetime :created_at
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
create_transition_table(ARModel, :state)
|
217
|
+
create_transition_table(ARModelNoInitial, :state)
|
218
|
+
create_transition_table(ARModelWithContext, :state, true)
|
219
|
+
create_transition_table(ARModelWithMultipleContext, :state, true)
|
220
|
+
create_transition_table(ARModelWithMultipleStateMachines, :first)
|
221
|
+
create_transition_table(ARModelWithMultipleStateMachines, :second)
|
222
|
+
create_transition_table(ARModelWithMultipleStateMachines, :third)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
### Setup test database
|
4
|
+
Mongoid.load!(File.expand_path('../mongoid.yml', __FILE__), :test)
|
5
|
+
|
6
|
+
# We probably want to provide a generator for this model and the accompanying migration.
|
7
|
+
class MongoidTestModelStateTransition
|
8
|
+
include Mongoid::Document
|
9
|
+
include Mongoid::Timestamps
|
10
|
+
belongs_to :mongoid_test_model
|
11
|
+
|
12
|
+
field :event, type: String
|
13
|
+
field :from, type: String
|
14
|
+
field :to, type: String
|
15
|
+
end
|
16
|
+
|
17
|
+
class MongoidTestModelWithMultipleStateMachinesFirstTransition
|
18
|
+
include Mongoid::Document
|
19
|
+
include Mongoid::Timestamps
|
20
|
+
belongs_to :mongoid_test_model
|
21
|
+
|
22
|
+
field :event, type: String
|
23
|
+
field :from, type: String
|
24
|
+
field :to, type: String
|
25
|
+
end
|
26
|
+
|
27
|
+
class MongoidTestModelWithMultipleStateMachinesSecondTransition
|
28
|
+
include Mongoid::Document
|
29
|
+
include Mongoid::Timestamps
|
30
|
+
belongs_to :mongoid_test_model
|
31
|
+
|
32
|
+
field :event, type: String
|
33
|
+
field :from, type: String
|
34
|
+
field :to, type: String
|
35
|
+
end
|
36
|
+
|
37
|
+
class MongoidTestModel
|
38
|
+
|
39
|
+
include Mongoid::Document
|
40
|
+
include Mongoid::Timestamps
|
41
|
+
|
42
|
+
state_machine :state, initial: :waiting do # log initial state?
|
43
|
+
audit_trail :orm => :mongoid
|
44
|
+
|
45
|
+
event :start do
|
46
|
+
transition [:waiting, :stopped] => :started
|
47
|
+
end
|
48
|
+
|
49
|
+
event :stop do
|
50
|
+
transition :started => :stopped
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class MongoidTestModelDescendant < MongoidTestModel
|
56
|
+
include Mongoid::Timestamps
|
57
|
+
end
|
58
|
+
|
59
|
+
class MongoidTestModelWithMultipleStateMachines
|
60
|
+
|
61
|
+
include Mongoid::Document
|
62
|
+
include Mongoid::Timestamps
|
63
|
+
|
64
|
+
state_machine :first, initial: :beginning do
|
65
|
+
audit_trail
|
66
|
+
|
67
|
+
event :begin_first do
|
68
|
+
transition :beginning => :end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
state_machine :second do
|
73
|
+
audit_trail
|
74
|
+
|
75
|
+
event :begin_second do
|
76
|
+
transition nil => :beginning_second
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# reset integrations so that something like Mongoid is not loaded and conflicting
|
2
|
+
require 'state_machines'
|
3
|
+
StateMachines::Integrations.reset
|
4
|
+
|
5
|
+
require 'spec_helper'
|
6
|
+
require 'state_machines-activerecord'
|
7
|
+
require 'helpers/active_record'
|
8
|
+
|
9
|
+
describe StateMachines::AuditTrail::Backend::ActiveRecord do
|
10
|
+
|
11
|
+
context ':initial option' do
|
12
|
+
it 'default logs' do
|
13
|
+
target = ARModel.new
|
14
|
+
# initial transition is built but not saved
|
15
|
+
expect(target.new_record?).to be_truthy
|
16
|
+
expect(target.ar_model_state_transitions.count).to eq 0
|
17
|
+
target.save!
|
18
|
+
|
19
|
+
# initial transition is saved and should be present
|
20
|
+
expect(target.new_record?).to be_falsey
|
21
|
+
expect(target.ar_model_state_transitions.count).to eq 1
|
22
|
+
state_transition = target.ar_model_state_transitions.first
|
23
|
+
expect(state_transition.from).to be_nil
|
24
|
+
expect(state_transition.to).to eq 'waiting'
|
25
|
+
expect(state_transition.event).to be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'false skips log' do
|
29
|
+
target = ARModelNoInitial.new
|
30
|
+
# initial transition is not-built
|
31
|
+
expect(target.new_record?).to be_truthy
|
32
|
+
expect(target.ar_model_no_initial_state_transitions.count).to eq 0
|
33
|
+
target.save!
|
34
|
+
|
35
|
+
# after save, initial transition is not-saved
|
36
|
+
expect(target.new_record?).to be_falsey
|
37
|
+
expect(target.ar_model_no_initial_state_transitions.count).to eq 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#create_for' do
|
42
|
+
it 'should be Backend::ActiveRecord' do
|
43
|
+
backend = StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModel)
|
44
|
+
expect(backend).to be_instance_of(StateMachines::AuditTrail::Backend::ActiveRecord)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should create a has many association on the state machine owner' do
|
48
|
+
StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModel)
|
49
|
+
expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should handle namespaced models' do
|
53
|
+
StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, SomeNamespace::ARModel)
|
54
|
+
expect(SomeNamespace::ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should handle namespaced state transition model' do
|
58
|
+
StateMachines::AuditTrail::Backend.create_for(SomeNamespace::ARModelStateTransition, ARModel)
|
59
|
+
expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'single state machine' do
|
64
|
+
shared_examples 'audit trail with context' do
|
65
|
+
it 'should populate all fields' do
|
66
|
+
|
67
|
+
# state_transition =target.state_transitions.first
|
68
|
+
# expect(state_transition.from).to be_nil
|
69
|
+
|
70
|
+
expect(target.state_name).to eq :waiting
|
71
|
+
target.start!
|
72
|
+
expect(target.state_name).to eq :started
|
73
|
+
|
74
|
+
last_transition = ARModelWithContextStateTransition.where(:ar_model_with_context_id => target.id).last
|
75
|
+
|
76
|
+
expect(last_transition).not_to be_nil
|
77
|
+
expect(last_transition.event).to eq 'start'
|
78
|
+
expect(last_transition.from).to eq 'waiting'
|
79
|
+
expect(last_transition.to).to eq 'started'
|
80
|
+
expect(last_transition.context).not_to be_nil
|
81
|
+
expect(last_transition.created_at).to be_within(10.seconds).of(Time.now.utc)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'do nothing on failed transition' do
|
85
|
+
expect { target.stop }.not_to change(ARModelWithContextStateTransition, :count)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'on created model' do
|
90
|
+
let!(:target) { ARModelWithContext.create! }
|
91
|
+
include_examples 'audit trail with context'
|
92
|
+
|
93
|
+
it 'should log multiple events' do
|
94
|
+
expect { target.start && target.stop && target.start }.to change(ARModelWithContextStateTransition, :count).by(3)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'on new model' do
|
99
|
+
let!(:target) { ARModelWithContext.new }
|
100
|
+
include_examples 'audit trail with context'
|
101
|
+
|
102
|
+
it 'should log multiple events including the first event from save' do
|
103
|
+
expect { target.start && target.stop && target.start }.to change(ARModelWithContextStateTransition, :count).by(4)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'wants to log a single context' do
|
108
|
+
before(:each) do
|
109
|
+
StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModelWithContext, :context)
|
110
|
+
end
|
111
|
+
|
112
|
+
let!(:target) { ARModelWithContext.create! }
|
113
|
+
|
114
|
+
it 'should populate all fields' do
|
115
|
+
target.start!
|
116
|
+
last_transition = ARModelWithContextStateTransition.where(:ar_model_with_context_id => target.id).last
|
117
|
+
expect(last_transition.context).to eq target.context
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'wants to log multiple context fields' do
|
122
|
+
before(:each) do
|
123
|
+
StateMachines::AuditTrail::Backend.create_for(ARModelWithMultipleContextStateTransition, ARModelWithMultipleContext, [:context, :second_context, :context_with_args])
|
124
|
+
end
|
125
|
+
|
126
|
+
let!(:target) { ARModelWithMultipleContext.create! }
|
127
|
+
|
128
|
+
it 'should populate all fields' do
|
129
|
+
target.start!
|
130
|
+
last_transition = ARModelWithMultipleContextStateTransition.where(:ar_model_with_multiple_context_id => target.id).last
|
131
|
+
expect(last_transition.context).to eq target.context
|
132
|
+
expect(last_transition.second_context).to eq target.second_context
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should log an event with passed arguments' do
|
136
|
+
target.start!('one', 'two', 'three', 'for', id: 1)
|
137
|
+
last_transition = ARModelWithMultipleContextStateTransition.where(:ar_model_with_multiple_context_id => target.id).last
|
138
|
+
expect(last_transition.context_with_args).to eq '1'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
context 'multiple state machines' do
|
146
|
+
let!(:target) { ARModelWithMultipleStateMachines.create! }
|
147
|
+
|
148
|
+
it 'should log a state transition for the affected state machine' do
|
149
|
+
expect { target.begin_first! }.to change(ARModelWithMultipleStateMachinesFirstTransition, :count).by(1)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should not log a state transition for the unaffected state machine' do
|
153
|
+
expect { target.begin_first! }.not_to change(ARModelWithMultipleStateMachinesSecondTransition, :count)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'with an initial state' do
|
158
|
+
let(:target_class) { ARModelWithMultipleStateMachines }
|
159
|
+
let(:state_transition_class) { ARModelWithMultipleStateMachinesFirstTransition }
|
160
|
+
|
161
|
+
it 'should log a state transition for the inital state' do
|
162
|
+
expect { target_class.create! }.to change(state_transition_class, :count).by(1)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should only set the :to state for the initial transition' do
|
166
|
+
target_class.create!
|
167
|
+
initial_transition = state_transition_class.last
|
168
|
+
expect(initial_transition.event).to be_nil
|
169
|
+
expect(initial_transition.from).to be_nil
|
170
|
+
expect(initial_transition.to).to eq 'beginning'
|
171
|
+
expect(initial_transition.created_at).to be_within(10.seconds).of(Time.now.utc)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'without an initial state' do
|
176
|
+
let(:target_class) { ARModelWithMultipleStateMachines }
|
177
|
+
let(:state_transition_class) { ARModelWithMultipleStateMachinesSecondTransition }
|
178
|
+
|
179
|
+
it 'should not log a transition when the object is created' do
|
180
|
+
expect { target_class.create! }.not_to change(state_transition_class, :count)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should log a transition for the first event' do
|
184
|
+
expect { target_class.create.begin_second! }.to change(state_transition_class, :count).by(1)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should not set a value for the :from state on the first transition' do
|
188
|
+
target_class.create.begin_second!
|
189
|
+
first_transition = state_transition_class.last
|
190
|
+
expect(first_transition.event).to eq 'begin_second'
|
191
|
+
expect(first_transition.from).to be_nil
|
192
|
+
expect(first_transition.to).to eq 'beginning_second'
|
193
|
+
expect(first_transition.created_at).to be_within(10.seconds).of(Time.now.utc)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should be fine transitioning before saved on an :action => nil state machine' do
|
197
|
+
expect {
|
198
|
+
machine = target_class.new
|
199
|
+
machine.begin_third
|
200
|
+
machine.save!
|
201
|
+
}.to change(ARModelWithMultipleStateMachinesThirdTransition, :count).by(1)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should queue up transitions to be saved before being saved on an :action => nil state machine' do
|
205
|
+
expect {
|
206
|
+
machine = target_class.new
|
207
|
+
machine.begin_third
|
208
|
+
machine.end_third
|
209
|
+
machine.save!
|
210
|
+
}.to change(ARModelWithMultipleStateMachinesThirdTransition, :count).by(2)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'STI' do
|
215
|
+
it 'resolve class name' do
|
216
|
+
m = ARModelDescendant.create!
|
217
|
+
expect { m.start! }.not_to raise_error
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'resolve class name on own state machine' do
|
221
|
+
m = ARModelDescendantWithOwnStateMachines.create!
|
222
|
+
expect { m.complete! }.not_to raise_error
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|