state_machines-audit_trail 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/state_machines/audit_trail/backend.rb +9 -1
- data/lib/state_machines/audit_trail/transition_auditing.rb +1 -1
- data/lib/state_machines/audit_trail/version.rb +1 -1
- data/lib/state_machines/audit_trail_generator.rb +1 -0
- data/spec/helpers/active_record.rb +46 -15
- data/spec/helpers/mongoid.rb +3 -0
- data/spec/lib/state_machines/audit_trail/backend/active_record_spec.rb +57 -18
- data/spec/lib/state_machines/audit_trail_generator_spec.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f76b02dab1bbb82a2ba360cbc86137642fc3874
|
4
|
+
data.tar.gz: a40d8ce4c4f7166f9660669d8a603eb8da9d9e40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9818ab2f5f3146c6444645cc6a92c331c069d842b00ff309bc8c4e8ff61fd09a396e0b5240ef43d6686fcdc8e42cc4dd23e6c8f1814ccd47efa3c2fb20aa3915
|
7
|
+
data.tar.gz: e85f0fee66e5323f67e1ffb4d8bb9cd42313d3e5e33ab846fc43a9b0b8bbcf79205c8cc6f2738d633ae450a4e11bf750de24a9a78d11cbc27ce0bc331f5640f3
|
@@ -9,7 +9,15 @@ class StateMachines::AuditTrail::Backend < Struct.new(:transition_class, :owner_
|
|
9
9
|
# - transition: state machine transition object that state machine passes to after/before transition callbacks
|
10
10
|
#
|
11
11
|
def log(object, transition)
|
12
|
-
|
12
|
+
|
13
|
+
if transition.machine.presence
|
14
|
+
# full transition object
|
15
|
+
namespace = transition.machine.namespace
|
16
|
+
else
|
17
|
+
# initial state open struct
|
18
|
+
namespace = transition.namespace
|
19
|
+
end
|
20
|
+
fields = {namespace: namespace, event: transition.event ? transition.event.to_s : nil, from: transition.from, to: transition.to}
|
13
21
|
[context].flatten(1).each { |field|
|
14
22
|
fields[field] = resolve_context(object, field, transition)
|
15
23
|
} unless self.context.nil?
|
@@ -32,7 +32,7 @@ module StateMachines::AuditTrail::TransitionAuditing
|
|
32
32
|
state_machine.owner_class.after_initialize do |object|
|
33
33
|
current_state = object.send(state_machine.attribute)
|
34
34
|
if !current_state.nil?
|
35
|
-
state_machine.backend.log(object, OpenStruct.new(to: current_state))
|
35
|
+
state_machine.backend.log(object, OpenStruct.new(namespace: state_machine.namespace, to: current_state))
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -12,6 +12,7 @@ class StateMachines::AuditTrailGenerator < ::Rails::Generators::Base
|
|
12
12
|
def create_model
|
13
13
|
args = [transition_class_name,
|
14
14
|
"#{source_model.demodulize.tableize.singularize}:references",
|
15
|
+
'namespace:string',
|
15
16
|
'event:string',
|
16
17
|
'from:string',
|
17
18
|
'to:string',
|
@@ -6,6 +6,11 @@ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':me
|
|
6
6
|
class ARModelStateTransition < ActiveRecord::Base
|
7
7
|
belongs_to :ar_model
|
8
8
|
end
|
9
|
+
|
10
|
+
class ARModelWithNamespaceFooStateTransition < ActiveRecord::Base
|
11
|
+
belongs_to :ar_model_with_namespace
|
12
|
+
end
|
13
|
+
|
9
14
|
class ARModelNoInitialStateTransition < ActiveRecord::Base
|
10
15
|
belongs_to :ar_model_no_initial
|
11
16
|
end
|
@@ -32,7 +37,7 @@ end
|
|
32
37
|
|
33
38
|
class ARModel < ActiveRecord::Base
|
34
39
|
|
35
|
-
state_machine :state, initial: :waiting do
|
40
|
+
state_machine :state, initial: :waiting do
|
36
41
|
audit_trail
|
37
42
|
|
38
43
|
event :start do
|
@@ -47,7 +52,7 @@ end
|
|
47
52
|
|
48
53
|
class ARModelNoInitial < ActiveRecord::Base
|
49
54
|
|
50
|
-
state_machine :state, initial: :waiting do
|
55
|
+
state_machine :state, initial: :waiting do
|
51
56
|
audit_trail initial: false
|
52
57
|
|
53
58
|
event :start do
|
@@ -59,9 +64,25 @@ class ARModelNoInitial < ActiveRecord::Base
|
|
59
64
|
end
|
60
65
|
end
|
61
66
|
end
|
67
|
+
|
68
|
+
class ARModelWithNamespace < ActiveRecord::Base
|
69
|
+
|
70
|
+
state_machine :foo_state, initial: :waiting, namespace: :foo do
|
71
|
+
audit_trail
|
72
|
+
|
73
|
+
event :start do
|
74
|
+
transition [:waiting, :stopped] => :started
|
75
|
+
end
|
76
|
+
|
77
|
+
event :stop do
|
78
|
+
transition :started => :stopped
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
62
83
|
#
|
63
84
|
class ARModelWithContext < ActiveRecord::Base
|
64
|
-
state_machine :state, initial: :waiting do
|
85
|
+
state_machine :state, initial: :waiting do
|
65
86
|
audit_trail context: :context
|
66
87
|
|
67
88
|
event :start do
|
@@ -79,7 +100,7 @@ class ARModelWithContext < ActiveRecord::Base
|
|
79
100
|
end
|
80
101
|
|
81
102
|
class ARModelWithMultipleContext < ActiveRecord::Base
|
82
|
-
state_machine :state, initial: :waiting do
|
103
|
+
state_machine :state, initial: :waiting do
|
83
104
|
audit_trail context: [:context, :second_context, :context_with_args]
|
84
105
|
|
85
106
|
event :start do
|
@@ -150,14 +171,14 @@ class ARModelWithMultipleStateMachines < ActiveRecord::Base
|
|
150
171
|
end
|
151
172
|
end
|
152
173
|
|
153
|
-
module
|
174
|
+
module SomeModule
|
154
175
|
class ARModelStateTransition < ActiveRecord::Base
|
155
|
-
belongs_to :
|
176
|
+
belongs_to :ar_model
|
156
177
|
end
|
157
178
|
|
158
179
|
class ARModel < ActiveRecord::Base
|
159
180
|
|
160
|
-
state_machine :state, initial: :waiting do
|
181
|
+
state_machine :state, initial: :waiting do
|
161
182
|
audit_trail
|
162
183
|
|
163
184
|
event :start do
|
@@ -174,9 +195,13 @@ end
|
|
174
195
|
#
|
175
196
|
# Generate tables
|
176
197
|
#
|
177
|
-
def create_model_table(owner_class, multiple_state_machines = false)
|
198
|
+
def create_model_table(owner_class, multiple_state_machines = false, state_column = nil)
|
178
199
|
ActiveRecord::Base.connection.create_table(owner_class.name.tableize) do |t|
|
179
|
-
|
200
|
+
if state_column.presence
|
201
|
+
t.string state_column
|
202
|
+
else
|
203
|
+
t.string :state unless multiple_state_machines
|
204
|
+
end
|
180
205
|
t.string :type
|
181
206
|
|
182
207
|
if multiple_state_machines
|
@@ -189,10 +214,12 @@ def create_model_table(owner_class, multiple_state_machines = false)
|
|
189
214
|
end
|
190
215
|
end
|
191
216
|
|
192
|
-
|
193
|
-
|
194
|
-
create_model_table(
|
195
|
-
|
217
|
+
|
218
|
+
%w(ARModel ARModelNoInitial ARModelWithContext ARModelWithMultipleContext).each do |name|
|
219
|
+
create_model_table(name.constantize)
|
220
|
+
end
|
221
|
+
|
222
|
+
create_model_table(ARModelWithNamespace, false, :foo_state)
|
196
223
|
create_model_table(ARModelWithMultipleStateMachines, true)
|
197
224
|
|
198
225
|
|
@@ -202,6 +229,7 @@ def create_transition_table(owner_class, state, add_context = false)
|
|
202
229
|
|
203
230
|
# t.references :"#{owner_class.name.pluralize.demodulize.tableize}"
|
204
231
|
t.integer owner_class.name.foreign_key
|
232
|
+
t.string :namespace
|
205
233
|
t.string :event
|
206
234
|
t.string :from
|
207
235
|
t.string :to
|
@@ -213,8 +241,11 @@ def create_transition_table(owner_class, state, add_context = false)
|
|
213
241
|
end
|
214
242
|
end
|
215
243
|
|
216
|
-
|
217
|
-
create_transition_table(
|
244
|
+
%w(ARModel ARModelNoInitial).each do |name|
|
245
|
+
create_transition_table(name.constantize, :state)
|
246
|
+
end
|
247
|
+
|
248
|
+
create_transition_table(ARModelWithNamespace, :foo_state, false)
|
218
249
|
create_transition_table(ARModelWithContext, :state, true)
|
219
250
|
create_transition_table(ARModelWithMultipleContext, :state, true)
|
220
251
|
create_transition_table(ARModelWithMultipleStateMachines, :first)
|
data/spec/helpers/mongoid.rb
CHANGED
@@ -9,6 +9,7 @@ class MongoidTestModelStateTransition
|
|
9
9
|
include Mongoid::Timestamps
|
10
10
|
belongs_to :mongoid_test_model
|
11
11
|
|
12
|
+
field :namespace, type: String
|
12
13
|
field :event, type: String
|
13
14
|
field :from, type: String
|
14
15
|
field :to, type: String
|
@@ -19,6 +20,7 @@ class MongoidTestModelWithMultipleStateMachinesFirstTransition
|
|
19
20
|
include Mongoid::Timestamps
|
20
21
|
belongs_to :mongoid_test_model
|
21
22
|
|
23
|
+
field :namespace, type: String
|
22
24
|
field :event, type: String
|
23
25
|
field :from, type: String
|
24
26
|
field :to, type: String
|
@@ -29,6 +31,7 @@ class MongoidTestModelWithMultipleStateMachinesSecondTransition
|
|
29
31
|
include Mongoid::Timestamps
|
30
32
|
belongs_to :mongoid_test_model
|
31
33
|
|
34
|
+
field :namespace, type: String
|
32
35
|
field :event, type: String
|
33
36
|
field :from, type: String
|
34
37
|
field :to, type: String
|
@@ -9,20 +9,33 @@ require 'helpers/active_record'
|
|
9
9
|
describe StateMachines::AuditTrail::Backend::ActiveRecord do
|
10
10
|
|
11
11
|
context ':initial option' do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
context 'default' do
|
13
|
+
it 'new object' do
|
14
|
+
target = ARModel.new
|
15
|
+
# initial transition is built but not saved
|
16
|
+
expect(target.new_record?).to be_truthy
|
17
|
+
expect(target.ar_model_state_transitions.count).to eq 0
|
18
|
+
target.save!
|
19
|
+
|
20
|
+
# initial transition is saved and should be present
|
21
|
+
expect(target.new_record?).to be_falsey
|
22
|
+
expect(target.ar_model_state_transitions.count).to eq 1
|
23
|
+
state_transition = target.ar_model_state_transitions.first
|
24
|
+
expect(state_transition.from).to be_nil
|
25
|
+
expect(state_transition.to).to eq 'waiting'
|
26
|
+
expect(state_transition.event).to be_nil
|
27
|
+
end
|
18
28
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
it 'create object' do
|
30
|
+
target = ARModel.create!
|
31
|
+
# initial transition is saved and should be present
|
32
|
+
expect(target.new_record?).to be_falsey
|
33
|
+
expect(target.ar_model_state_transitions.count).to eq 1
|
34
|
+
state_transition = target.ar_model_state_transitions.first
|
35
|
+
expect(state_transition.from).to be_nil
|
36
|
+
expect(state_transition.to).to eq 'waiting'
|
37
|
+
expect(state_transition.event).to be_nil
|
38
|
+
end
|
26
39
|
end
|
27
40
|
|
28
41
|
it 'false skips log' do
|
@@ -38,6 +51,32 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
|
|
38
51
|
end
|
39
52
|
end
|
40
53
|
|
54
|
+
context 'namespaced state_machine' do
|
55
|
+
it 'should log namespace' do
|
56
|
+
target = ARModelWithNamespace.create!
|
57
|
+
|
58
|
+
# initial transition is saved and should be present
|
59
|
+
expect(target.new_record?).to be_falsey
|
60
|
+
expect(target.ar_model_with_namespace_foo_state_transitions.count).to eq 1
|
61
|
+
state_transition = target.ar_model_with_namespace_foo_state_transitions.first
|
62
|
+
expect(state_transition.namespace).to eq 'foo'
|
63
|
+
expect(state_transition.from).to be_nil
|
64
|
+
expect(state_transition.to).to eq 'waiting'
|
65
|
+
expect(state_transition.event).to be_nil
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should not log namespace' do
|
69
|
+
target = ARModel.create!
|
70
|
+
expect(target.new_record?).to be_falsey
|
71
|
+
expect(target.ar_model_state_transitions.count).to eq 1
|
72
|
+
state_transition = target.ar_model_state_transitions.first
|
73
|
+
expect(state_transition.namespace).to be_nil
|
74
|
+
expect(state_transition.from).to be_nil
|
75
|
+
expect(state_transition.to).to eq 'waiting'
|
76
|
+
expect(state_transition.event).to be_nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
41
80
|
context '#create_for' do
|
42
81
|
it 'should be Backend::ActiveRecord' do
|
43
82
|
backend = StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModel)
|
@@ -49,13 +88,13 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
|
|
49
88
|
expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
50
89
|
end
|
51
90
|
|
52
|
-
it 'should handle
|
53
|
-
StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition,
|
54
|
-
expect(
|
91
|
+
it 'should handle models within modules' do
|
92
|
+
StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, SomeModule::ARModel)
|
93
|
+
expect(SomeModule::ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
55
94
|
end
|
56
95
|
|
57
|
-
it 'should handle
|
58
|
-
StateMachines::AuditTrail::Backend.create_for(
|
96
|
+
it 'should handle state transition models within modules' do
|
97
|
+
StateMachines::AuditTrail::Backend.create_for(SomeModule::ARModelStateTransition, ARModel)
|
59
98
|
expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
|
60
99
|
end
|
61
100
|
end
|
@@ -10,7 +10,7 @@
|
|
10
10
|
# describe StateMachines::AuditTrailGenerator, type: :generator do
|
11
11
|
#
|
12
12
|
# destination File.expand_path('../../../../tmp', __FILE__)
|
13
|
-
# arguments %w(
|
13
|
+
# arguments %w(SomeModule::Subscription state)
|
14
14
|
#
|
15
15
|
# before(:all) do
|
16
16
|
# prepare_destination
|
@@ -36,7 +36,7 @@
|
|
36
36
|
# end
|
37
37
|
# directory 'some_namespace' do
|
38
38
|
# file 'subscription_state_transition.rb' do
|
39
|
-
# contains 'class
|
39
|
+
# contains 'class SomeModule::SubscriptionStateTransition'
|
40
40
|
# end
|
41
41
|
# end
|
42
42
|
# end
|