state_machines-audit_trail 1.0.0 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b8a0bfd2dd9a83185bc24c1c55d3804b2fba5a8e
4
- data.tar.gz: 03e55491819582dbf56a32b494756564d0da7ffd
2
+ SHA256:
3
+ metadata.gz: fa9c1aad624d9dad005c50ab44eb5522908b1790490f6e8b1eacc1b05261a7e3
4
+ data.tar.gz: 9bcb723941ab88d56534181ad6be0c147de7262ee408c2a5607a77bd146b94e2
5
5
  SHA512:
6
- metadata.gz: d1c764d8ed988ecb2a766939127e76ce902241320bd53fdce5641830bb3eb52c666179dae873d23fc2f989a5fd65013722af497716a59d65e06c5d5d5a659e6b
7
- data.tar.gz: 60d5d11f419b337f253141fb36a17a51dcb2852cb549e89b64a59f4c8f06ed8a30fe7785038138a7a11884212ca1cd0b52cbac094cd9133699eb3165a5dd39f4
6
+ metadata.gz: ffaacbb29036d844d7a25d72da512906bee8d7a646e63b42b68f3f99bfcd7ff572e33177b84cf85ef079cb284dc2667611ce22b40835d524f065fdfb76f7926f
7
+ data.tar.gz: 15cf000aea32b80c3f0cf675fe9cad34574ce127d4328c3faea9f70b62e7d35550b67ef62ed9209000bf46c84e949ccec27821ba2d90861cfb075c43ae616a76
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  *.gem
2
- /.bundle
2
+ .bundle
3
3
  Gemfile.lock
4
+ *.gemfile.lock
4
5
  pkg/*
5
6
  .DS_Store
6
7
  *.rbc
@@ -0,0 +1 @@
1
+ state_machines-audit_trail
@@ -0,0 +1 @@
1
+ 2.3.1
@@ -1,17 +1,43 @@
1
1
  language: ruby
2
- sudo: false
3
2
  cache: bundler
4
3
 
5
4
  script: "bundle exec rake"
6
5
 
7
6
  services: mongodb
8
7
  rvm:
9
- - 2.1
10
- - 2.0.0
11
8
  - 2.2
9
+ - 2.3
10
+ - 2.4
11
+ - 2.5
12
+ - 2.6
13
+ - 2.7
14
+ - ruby-head
15
+ - jruby-head
12
16
  - jruby
13
17
  - rbx-2
18
+ gemfile:
19
+ - gemfiles/rails_5.0.gemfile
20
+ - gemfiles/rails_5.1.gemfile
21
+ - gemfiles/rails_5.2.gemfile
22
+ - gemfiles/rails_6.0.gemfile
23
+ - gemfiles/rails_edge.gemfile
14
24
  matrix:
25
+ exclude:
26
+ - rvm: 2.2
27
+ gemfile: gemfiles/rails_6.0.gemfile
28
+ - rvm: 2.3
29
+ gemfile: gemfiles/rails_6.0.gemfile
30
+ - rvm: 2.4
31
+ gemfile: gemfiles/rails_6.0.gemfile
32
+ - rvm: 2.2
33
+ gemfile: gemfiles/rails_edge.gemfile
34
+ - rvm: 2.3
35
+ gemfile: gemfiles/rails_edge.gemfile
36
+ - rvm: 2.4
37
+ gemfile: gemfiles/rails_edge.gemfile
15
38
  allow_failures:
16
39
  - rvm: rbx-2
17
- - rvm: jruby
40
+ - rvm: ruby-head
41
+ - rvm: jruby-head
42
+ - gemfile: gemfiles/rails_edge.gemfile
43
+
@@ -0,0 +1,16 @@
1
+ appraise 'rails_5.0' do
2
+ gem 'activerecord', '~> 5.0'
3
+ gem 'activemodel', '~> 5.0'
4
+ end
5
+
6
+ appraise 'rails_5.1' do
7
+ gem 'activerecord', '~> 5.1'
8
+ gem 'activemodel', '~> 5.1'
9
+ end
10
+
11
+ appraise 'rails_edge' do
12
+ gem 'rails', github: 'rails/rails', branch: 'master'
13
+ gem 'activerecord', github: 'rails/rails', branch: 'master'
14
+ gem 'activemodel', github: 'rails/rails', branch: 'master'
15
+ gem 'arel', github: 'rails/arel', branch: 'master'
16
+ end
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  [![Build Status](https://travis-ci.org/state-machines/state_machines-audit_trail.svg?branch=master)](https://travis-ci.org/state-machines/state_machines-audit_trail)
2
- [![Code Climate](https://codeclimate.com/github/state-machines/state_machines-audit_trail.png)](https://codeclimate.com/github/state-machines/state_machines-audit_trail)
2
+ [![Code Climate](https://codeclimate.com/github/state-machines/state_machines-audit_trail.svg)](https://codeclimate.com/github/state-machines/state_machines-audit_trail)
3
3
 
4
4
  # state_machines-audit_trail
5
5
  Log transitions on a [state_machines gem](https://github.com/state-machines/state_machines) to support auditing and business process analytics.
@@ -12,7 +12,7 @@ for any state machine. Having an audit trail gives you a complete history of the
12
12
  to investigate incidents or perform analytics, like: _"How long does it take on average to go from state a to state b?"_,
13
13
  or _"What percentage of cases goes from state a to b via state c?"_
14
14
 
15
- For more information read [Why developers should be force-fed state machines](http://www.shopify.com/technology/3383012-why-developers-should-be-force-fed-state-machines).
15
+ For more information read [Why developers should be force-fed state machines](https://engineering.shopify.com/blogs/engineering/17488160-why-developers-should-be-force-fed-state-machines).
16
16
 
17
17
  ## ORM support
18
18
 
@@ -56,7 +56,7 @@ will generate the `SubscriptionStateTransition` model and an accompanying migrat
56
56
 
57
57
  ```ruby
58
58
  class Subscription < ActiveRecord::Base
59
- state_machines :state, initial: :start do
59
+ state_machine :state, initial: :start do
60
60
  audit_trail
61
61
  ...
62
62
  ```
@@ -64,6 +64,10 @@ class Subscription < ActiveRecord::Base
64
64
  ### That's it!
65
65
  `audit_trail` will register an `after_transition` callback that is used to log all transitions including the initial state if there is one.
66
66
 
67
+ ## Upgrading from state_machine-audit_trail
68
+
69
+ See the wiki, https://github.com/state-machines/state_machines-audit_trail/wiki/Converting-from-former-state_machine-audit_trail-to-state_machines-audit_trail
70
+
67
71
  ## Configuration options
68
72
 
69
73
  ### `:initial` - turn off initial state logging
@@ -97,7 +101,7 @@ In order to utilize this feature, you need to:
97
101
  #### Example 1 - Store a single attribute value
98
102
  Store `Subscription` `field1` in `Transition` field `field1`:
99
103
  ```ruby
100
- audit_trail :context: :field1
104
+ audit_trail context: :field1
101
105
  ```
102
106
 
103
107
  #### Example 2 - Store multiple attribute values
@@ -106,7 +110,25 @@ Store `Subscription` `field1` and `field2` in `Transition` fields `field1` and `
106
110
  audit_trail context: [:field1, :field2]
107
111
  ```
108
112
 
109
- #### Example 3 - Store simple method results
113
+ #### Example 3 - Store multiple values from a single context object
114
+ Store `Subscription` `user` in `Transition` fields `user_id` and `user_name`:
115
+ ```ruby
116
+ class Subscription < ActiveRecord::Base
117
+ state_machine :state, initial: :start do
118
+ audit_trail context: :user
119
+ ...
120
+ end
121
+ end
122
+
123
+ class SubscriptionStateTransition < ActiveRecord::Base
124
+ def user=(u)
125
+ self.user_id = u.id
126
+ self.user_name = u.name
127
+ end
128
+ end
129
+ ```
130
+
131
+ #### Example 4 - Store simple method results
110
132
  Store simple method results.
111
133
 
112
134
  Sometimes it can be useful to store dynamically computed information, such as those from a `Subscription` method `#plan_time_remaining`
@@ -114,7 +136,7 @@ Sometimes it can be useful to store dynamically computed information, such as th
114
136
 
115
137
  ```ruby
116
138
  class Subscription < ActiveRecord::Base
117
- state_machines :state, initial: :start do
139
+ state_machine :state, initial: :start do
118
140
  audit_trail :context: :plan_time_remaining
119
141
  ...
120
142
 
@@ -123,12 +145,12 @@ class Subscription < ActiveRecord::Base
123
145
  ...
124
146
  ```
125
147
 
126
- #### Example 4 - Store advanced method results
148
+ #### Example 5 - Store advanced method results
127
149
  Store method results that interrogate the transition for information such as `event` arguments:
128
150
 
129
151
  ```ruby
130
152
  class Subscription < ActiveRecord::Base
131
- state_machines :state, initial: :start do
153
+ state_machine :state, initial: :start do
132
154
  audit_trail :context: :user_name
133
155
  ...
134
156
 
data/Rakefile CHANGED
@@ -6,4 +6,10 @@ RSpec::Core::RakeTask.new(:spec) do |task|
6
6
  task.rspec_opts = ['--color']
7
7
  end
8
8
 
9
- task :default => [:spec]
9
+ if ENV['APPRAISAL_INITIALIZED'] || ENV['TRAVIS']
10
+ task :default => :spec
11
+ else
12
+ require 'appraisal'
13
+ Appraisal::Task.new
14
+ task :default => :appraisal
15
+ end
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.0.0"
6
+ gem "activemodel", "~> 5.0.0"
7
+ gem "sqlite3", "~> 1.3.6"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.1.0"
6
+ gem "activemodel", "~> 5.1.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2.0"
6
+ gem "activemodel", "~> 5.2.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.0.0"
6
+ gem "activemodel", "~> 6.0.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", github: "rails/rails", branch: "master"
6
+ gem "activerecord", github: "rails/rails", branch: "master"
7
+ gem "activemodel", github: "rails/rails", branch: "master"
8
+ gem "arel", github: "rails/arel", branch: "master"
9
+
10
+ gemspec path: "../"
@@ -1,2 +1,3 @@
1
+ # Comment here to test travis build on gem PR with no meaningful changes
1
2
  # To keep Rails happy
2
3
  require 'state_machines/audit_trail'
@@ -1,4 +1,4 @@
1
- class StateMachines::AuditTrail::Backend < Struct.new(:transition_class, :owner_class, :context)
1
+ class StateMachines::AuditTrail::Backend < Struct.new(:transition_class, :owner_class, :options)
2
2
 
3
3
  autoload :Mongoid, 'state_machines/audit_trail/backend/mongoid'
4
4
  autoload :ActiveRecord, 'state_machines/audit_trail/backend/active_record'
@@ -9,10 +9,18 @@ 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
- fields = {event: transition.event ? transition.event.to_s : nil, from: transition.from, to: transition.to}
13
- [context].flatten(1).each { |field|
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}
21
+ [*options[:context]].each { |field|
14
22
  fields[field] = resolve_context(object, field, transition)
15
- } unless self.context.nil?
23
+ }
16
24
 
17
25
  # begin
18
26
  persist(object, fields)
@@ -30,16 +38,21 @@ class StateMachines::AuditTrail::Backend < Struct.new(:transition_class, :owner_
30
38
  # To add a new ORM, implement something similar to lib/state_machines/audit_trail/backend/active_record.rb
31
39
  # and return from here the appropriate object based on which ORM the transition_class is using
32
40
  #
33
- def self.create_for(transition_class, owner_class, context = nil)
41
+ def self.create_for(transition_class, owner_class, options = {})
34
42
  if Object.const_defined?('ActiveRecord') && transition_class.ancestors.include?(::ActiveRecord::Base)
35
- return StateMachines::AuditTrail::Backend::ActiveRecord.new(transition_class, owner_class, context)
43
+ return StateMachines::AuditTrail::Backend::ActiveRecord.new(transition_class, owner_class, options)
36
44
  elsif Object.const_defined?('Mongoid') && transition_class.ancestors.include?(::Mongoid::Document)
37
- return StateMachines::AuditTrail::Backend::Mongoid.new(transition_class, owner_class, context)
45
+ return StateMachines::AuditTrail::Backend::Mongoid.new(transition_class, owner_class, options)
38
46
  else
39
47
  raise 'Not implemented. Only support for ActiveRecord and Mongoid is implemented. Pull requests welcome.'
40
48
  end
41
49
  end
42
50
 
51
+ # Exists in case ORM layer has a different way of answering this question, but works for most.
52
+ def new_record?(object)
53
+ object.new_record?
54
+ end
55
+
43
56
  protected
44
57
 
45
58
  def persist(object, fields)
@@ -47,7 +60,15 @@ class StateMachines::AuditTrail::Backend < Struct.new(:transition_class, :owner_
47
60
  end
48
61
 
49
62
  def resolve_context(object, context, transition)
50
- if object.method(context).arity != 0
63
+ # ---------------
64
+ # TODO: remove this check after we set a minimum version of Rails/ActiveRecord to 5.1+. At that time, the argument will be removed and the arity check will be enough. - rosskevin
65
+ # Don't send params to Rails 5+ associations because it triggers a ton of deprecation messages.
66
+ # @see https://github.com/state-machines/state_machines-audit_trail/issues/6
67
+ # check if activerecord && the context is an association
68
+ skip_args = object.is_a?(::ActiveRecord::Base) && object.class.reflections.keys.include?(context.to_s)
69
+ # ---------------
70
+
71
+ if object.method(context).arity != 0 && !skip_args
51
72
  object.send(context, transition)
52
73
  else
53
74
  object.send(context)
@@ -1,13 +1,11 @@
1
1
  require 'state_machines-activerecord'
2
2
 
3
3
  class StateMachines::AuditTrail::Backend::ActiveRecord < StateMachines::AuditTrail::Backend
4
- attr_accessor :context
5
-
6
- def initialize(transition_class, owner_class, context = nil)
4
+ def initialize(transition_class, owner_class, options = {})
5
+ super
7
6
  @association = transition_class.to_s.tableize.split('/').last.to_sym
8
- super transition_class, owner_class
9
- self.context = context # FIXME: actually not sure why we need to do this, but tests fail otherwise. Something with super's Struct?
10
- owner_class.has_many(@association, class_name: transition_class.to_s) unless owner_class.reflect_on_association(@association)
7
+ assoc_options = {class_name: transition_class.to_s}.merge(options.slice(:as))
8
+ owner_class.has_many(@association, **assoc_options) unless owner_class.reflect_on_association(@association)
11
9
  end
12
10
 
13
11
  def persist(object, fields)
@@ -12,6 +12,7 @@ module StateMachines::AuditTrail::TransitionAuditing
12
12
  #
13
13
  # options:
14
14
  # - :class - custom state transition class
15
+ # - :owner_class - the class which is to own the persisted transition objects
15
16
  # - :context - methods to call/store in field of same name in the state transition class
16
17
  # - :initial - if false, won't log null => initial state transition upon instantiation
17
18
  #
@@ -21,18 +22,21 @@ module StateMachines::AuditTrail::TransitionAuditing
21
22
  raise ":class option[#{options[:class]}] must be a class (not a string)." unless options[:class].is_a? Class
22
23
  end
23
24
  transition_class = options[:class] || default_transition_class
25
+ owner_class = options[:owner_class] || self.owner_class
24
26
 
25
27
  # backend implements #log to store transition information
26
- @backend = StateMachines::AuditTrail::Backend.create_for(transition_class, self.owner_class, options[:context])
28
+ @backend = StateMachines::AuditTrail::Backend.create_for(transition_class, owner_class, options.slice(:context, :as))
27
29
 
28
30
  # Initial state logging can be turned off. Very useful for a model with multiple state_machines using a single TransitionState object for logging
29
31
  unless options[:initial] == false
30
32
  unless state_machine.action == nil
31
33
  # Log the initial transition from null => initial (upon object instantiation)
32
34
  state_machine.owner_class.after_initialize do |object|
33
- current_state = object.send(state_machine.attribute)
34
- if !current_state.nil?
35
- state_machine.backend.log(object, OpenStruct.new(to: current_state))
35
+ if state_machine.backend.new_record? object
36
+ current_state = object.send(state_machine.attribute)
37
+ if !current_state.nil?
38
+ state_machine.backend.log(object, OpenStruct.new(namespace: state_machine.namespace, to: current_state))
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -1,5 +1,5 @@
1
1
  module StateMachines
2
2
  module AuditTrail
3
- VERSION = '1.0.0'
3
+ VERSION = '2.0.2'
4
4
  end
5
5
  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 # log initial state?
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 # log initial state?
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 # log initial state?
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 # log initial state?
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,46 @@ class ARModelWithMultipleStateMachines < ActiveRecord::Base
150
171
  end
151
172
  end
152
173
 
153
- module SomeNamespace
174
+ class ARResourceStateTransition < ActiveRecord::Base
175
+ belongs_to :resource, polymorphic: true
176
+ end
177
+
178
+ class ARFirstModelWithPolymorphicStateTransition < ActiveRecord::Base
179
+ state_machine :state, :initial => :pending do
180
+ audit_trail class: ARResourceStateTransition, as: :ar_resource
181
+
182
+ event :start do
183
+ transition :pending => :in_progress
184
+ end
185
+
186
+ event :finish do
187
+ transition :in_progress => :complete
188
+ end
189
+ end
190
+ end
191
+
192
+ class ARSecondModelWithPolymorphicStateTransition < ActiveRecord::Base
193
+ state_machine :state, :initial => :pending do
194
+ audit_trail class: ARResourceStateTransition, as: :ar_resource
195
+
196
+ event :start do
197
+ transition :pending => :in_progress
198
+ end
199
+
200
+ event :finish do
201
+ transition :in_progress => :complete
202
+ end
203
+ end
204
+ end
205
+
206
+ module SomeModule
154
207
  class ARModelStateTransition < ActiveRecord::Base
155
- belongs_to :test_model
208
+ belongs_to :ar_model
156
209
  end
157
210
 
158
211
  class ARModel < ActiveRecord::Base
159
212
 
160
- state_machine :state, initial: :waiting do # log initial state?
213
+ state_machine :state, initial: :waiting do
161
214
  audit_trail
162
215
 
163
216
  event :start do
@@ -174,9 +227,13 @@ end
174
227
  #
175
228
  # Generate tables
176
229
  #
177
- def create_model_table(owner_class, multiple_state_machines = false)
230
+ def create_model_table(owner_class, multiple_state_machines = false, state_column = nil)
178
231
  ActiveRecord::Base.connection.create_table(owner_class.name.tableize) do |t|
179
- t.string :state unless multiple_state_machines
232
+ if state_column.presence
233
+ t.string state_column
234
+ else
235
+ t.string :state unless multiple_state_machines
236
+ end
180
237
  t.string :type
181
238
 
182
239
  if multiple_state_machines
@@ -189,19 +246,21 @@ def create_model_table(owner_class, multiple_state_machines = false)
189
246
  end
190
247
  end
191
248
 
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
249
 
250
+ %w(ARModel ARModelNoInitial ARModelWithContext ARModelWithMultipleContext ARFirstModelWithPolymorphicStateTransition ARSecondModelWithPolymorphicStateTransition).each do |name|
251
+ create_model_table(name.constantize)
252
+ end
253
+
254
+ create_model_table(ARModelWithNamespace, false, :foo_state)
255
+ create_model_table(ARModelWithMultipleStateMachines, true)
198
256
 
199
- def create_transition_table(owner_class, state, add_context = false)
200
- class_name = "#{owner_class.name}#{state.to_s.camelize}Transition"
257
+ def create_transition_table(owner_class_name, state, add_context: false, polymorphic: false)
258
+ class_name = "#{owner_class_name}#{state.to_s.camelize}Transition"
201
259
  ActiveRecord::Base.connection.create_table(class_name.tableize) do |t|
202
260
 
203
- # t.references :"#{owner_class.name.pluralize.demodulize.tableize}"
204
- t.integer owner_class.name.foreign_key
261
+ t.references "#{owner_class_name.demodulize.underscore}", index: false, polymorphic: polymorphic
262
+ # t.integer owner_class_name.foreign_key
263
+ t.string :namespace
205
264
  t.string :event
206
265
  t.string :from
207
266
  t.string :to
@@ -213,10 +272,14 @@ def create_transition_table(owner_class, state, add_context = false)
213
272
  end
214
273
  end
215
274
 
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)
275
+ %w(ARModel ARModelNoInitial).each do |name|
276
+ create_transition_table(name, :state)
277
+ end
278
+
279
+ create_transition_table("ARModelWithNamespace", :foo_state, add_context: false)
280
+ create_transition_table("ARModelWithContext", :state, add_context: true)
281
+ create_transition_table("ARModelWithMultipleContext", :state, add_context: true)
282
+ create_transition_table("ARModelWithMultipleStateMachines", :first)
283
+ create_transition_table("ARModelWithMultipleStateMachines", :second)
284
+ create_transition_table("ARModelWithMultipleStateMachines", :third)
285
+ create_transition_table("ARResource", :state, polymorphic: true)
@@ -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
@@ -1,5 +1,5 @@
1
1
  test:
2
- sessions:
2
+ clients:
3
3
  default:
4
4
  database: sm_audit_trail
5
5
  hosts:
@@ -9,20 +9,35 @@ require 'helpers/active_record'
9
9
  describe StateMachines::AuditTrail::Backend::ActiveRecord do
10
10
 
11
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!
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
+ assert_transition state_transition, nil, nil, 'waiting'
25
+ end
18
26
 
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
27
+ it 'create object' do
28
+ target = ARModel.create!
29
+ # initial transition is saved and should be present
30
+ expect(target.new_record?).to be_falsey
31
+ expect(target.ar_model_state_transitions.count).to eq 1
32
+ state_transition = target.ar_model_state_transitions.first
33
+ assert_transition state_transition, nil, nil, 'waiting'
34
+
35
+ # ensure we don't have a second initial state transition logged (issue #4)
36
+ target = target.reload()
37
+ expect(target.ar_model_state_transitions.count).to eq 1
38
+ state_transition = target.ar_model_state_transitions.first
39
+ assert_transition state_transition, nil, nil, 'waiting'
40
+ end
26
41
  end
27
42
 
28
43
  it 'false skips log' do
@@ -38,6 +53,32 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
38
53
  end
39
54
  end
40
55
 
56
+ context 'namespaced state_machine' do
57
+ it 'should log namespace' do
58
+ target = ARModelWithNamespace.create!
59
+
60
+ # initial transition is saved and should be present
61
+ expect(target.new_record?).to be_falsey
62
+ expect(target.ar_model_with_namespace_foo_state_transitions.count).to eq 1
63
+ state_transition = target.ar_model_with_namespace_foo_state_transitions.first
64
+ expect(state_transition.namespace).to eq 'foo'
65
+ expect(state_transition.from).to be_nil
66
+ expect(state_transition.to).to eq 'waiting'
67
+ expect(state_transition.event).to be_nil
68
+ end
69
+
70
+ it 'should not log namespace' do
71
+ target = ARModel.create!
72
+ expect(target.new_record?).to be_falsey
73
+ expect(target.ar_model_state_transitions.count).to eq 1
74
+ state_transition = target.ar_model_state_transitions.first
75
+ expect(state_transition.namespace).to be_nil
76
+ expect(state_transition.from).to be_nil
77
+ expect(state_transition.to).to eq 'waiting'
78
+ expect(state_transition.event).to be_nil
79
+ end
80
+ end
81
+
41
82
  context '#create_for' do
42
83
  it 'should be Backend::ActiveRecord' do
43
84
  backend = StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModel)
@@ -49,13 +90,13 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
49
90
  expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
50
91
  end
51
92
 
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
93
+ it 'should handle models within modules' do
94
+ StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, SomeModule::ARModel)
95
+ expect(SomeModule::ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
55
96
  end
56
97
 
57
- it 'should handle namespaced state transition model' do
58
- StateMachines::AuditTrail::Backend.create_for(SomeNamespace::ARModelStateTransition, ARModel)
98
+ it 'should handle state transition models within modules' do
99
+ StateMachines::AuditTrail::Backend.create_for(SomeModule::ARModelStateTransition, ARModel)
59
100
  expect(ARModel.reflect_on_association(:ar_model_state_transitions).collection?).to be_truthy
60
101
  end
61
102
  end
@@ -106,7 +147,7 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
106
147
 
107
148
  context 'wants to log a single context' do
108
149
  before(:each) do
109
- StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModelWithContext, :context)
150
+ StateMachines::AuditTrail::Backend.create_for(ARModelWithContextStateTransition, ARModelWithContext, context: :context)
110
151
  end
111
152
 
112
153
  let!(:target) { ARModelWithContext.create! }
@@ -120,7 +161,7 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
120
161
 
121
162
  context 'wants to log multiple context fields' do
122
163
  before(:each) do
123
- StateMachines::AuditTrail::Backend.create_for(ARModelWithMultipleContextStateTransition, ARModelWithMultipleContext, [:context, :second_context, :context_with_args])
164
+ StateMachines::AuditTrail::Backend.create_for(ARModelWithMultipleContextStateTransition, ARModelWithMultipleContext, context: [:context, :second_context, :context_with_args])
124
165
  end
125
166
 
126
167
  let!(:target) { ARModelWithMultipleContext.create! }
@@ -222,4 +263,26 @@ describe StateMachines::AuditTrail::Backend::ActiveRecord do
222
263
  expect { m.complete! }.not_to raise_error
223
264
  end
224
265
  end
266
+
267
+ context 'polymorphic' do
268
+ it 'creates polymorphic state transitions' do
269
+ m1 = ARFirstModelWithPolymorphicStateTransition.create!
270
+ m2 = ARSecondModelWithPolymorphicStateTransition.create!
271
+ m2.start!
272
+ m2.finish!
273
+
274
+ expect(m1.ar_resource_state_transitions.count).to eq(1)
275
+ expect(m2.ar_resource_state_transitions.count).to eq(3)
276
+ expect(ARResourceStateTransition.count).to eq(4)
277
+ end
278
+ end
279
+
280
+ private
281
+
282
+ def assert_transition(state_transition, event, from, to)
283
+ # expect(state_transition.namespace).to eq namespace
284
+ expect(state_transition.event).to eq event
285
+ expect(state_transition.from).to eq from
286
+ expect(state_transition.to).to eq to
287
+ end
225
288
  end
@@ -1,98 +1,98 @@
1
- # reset integrations so that something like ActiveRecord is not loaded and conflicting
2
- require 'state_machines'
3
- StateMachines::Integrations.reset
4
-
5
- require 'spec_helper'
6
- require 'state_machines-mongoid'
7
- require 'helpers/mongoid'
8
-
9
- describe StateMachines::AuditTrail::Backend::Mongoid do
10
-
11
- context '#create_for' do
12
- it 'should create a Mongoid backend' do
13
- backend = StateMachines::AuditTrail::Backend.create_for(MongoidTestModelStateTransition, MongoidTestModel)
14
- expect(backend).to be_instance_of(StateMachines::AuditTrail::Backend::Mongoid)
15
- end
16
- end
17
-
18
- context 'single state machine' do
19
- let!(:target) { MongoidTestModel.create! }
20
-
21
- it 'should populate all fields' do
22
- target.start!
23
- last_transition = MongoidTestModelStateTransition.where(:mongoid_test_model_id => target.id).last
24
-
25
- expect(last_transition.event).to eq 'start'
26
- expect(last_transition.from).to eq 'waiting'
27
- expect(last_transition.to).to eq 'started'
28
- expect(last_transition.created_at).to be_within(10.seconds).of(DateTime.now)
29
- end
30
-
31
- it 'should log multiple events' do
32
- expect { target.start && target.stop && target.start }.to change(MongoidTestModelStateTransition, :count).by(3)
33
- end
34
-
35
- it 'do nothing on failed transition' do
36
- expect { target.stop }.not_to change(MongoidTestModelStateTransition, :count)
37
- end
38
- end
39
-
40
- context 'multiple state machines' do
41
- let!(:target) { MongoidTestModelWithMultipleStateMachines.create! }
42
-
43
- it 'should log a state transition for the affected state machine' do
44
- expect { target.begin_first! }.to change(MongoidTestModelWithMultipleStateMachinesFirstTransition, :count).by(1)
45
- end
46
-
47
- it 'should not log a state transition for the unaffected state machine' do
48
- expect { target.begin_first! }.not_to change(MongoidTestModelWithMultipleStateMachinesSecondTransition, :count)
49
- end
50
- end
51
-
52
- context 'on an object with a state machine having an initial state' do
53
- let(:target_class) { MongoidTestModelWithMultipleStateMachines }
54
- let(:state_transition_class) { MongoidTestModelWithMultipleStateMachinesFirstTransition }
55
-
56
- it 'should log a state transition for the inital state' do
57
- expect { target_class.create! }.to change(state_transition_class, :count).by(1)
58
- end
59
-
60
- it 'should only set the :to state for the initial transition' do
61
- target_class.create!
62
- initial_transition = state_transition_class.last
63
- expect(initial_transition.event).to be_nil
64
- expect(initial_transition.from).to be_nil
65
- expect(initial_transition.to).to eq 'beginning'
66
- expect(initial_transition.created_at).to be_within(10.seconds).of(DateTime.now)
67
- end
68
- end
69
-
70
- context 'on an object with a state machine not having an initial state' do
71
- let(:target_class) { MongoidTestModelWithMultipleStateMachines }
72
- let(:state_transition_class) { MongoidTestModelWithMultipleStateMachinesSecondTransition }
73
-
74
- it 'should not log a transition when the object is created' do
75
- expect { target_class.create! }.not_to change(state_transition_class, :count)
76
- end
77
-
78
- it 'should log a transition for the first event' do
79
- expect { target_class.create.begin_second! }.to change(state_transition_class, :count).by(1)
80
- end
81
-
82
- it 'should not set a value for the :from state on the first transition' do
83
- target_class.create.begin_second!
84
- first_transition = state_transition_class.last
85
- expect(first_transition.event).to eq 'begin_second'
86
- expect(first_transition.from).to be_nil
87
- expect(first_transition.to).to eq 'beginning_second'
88
- expect(first_transition.created_at).to be_within(10.seconds).of(DateTime.now)
89
- end
90
- end
91
-
92
- context 'on a class using STI' do
93
- it 'should properly grab the class name from STI models' do
94
- m = MongoidTestModelDescendant.create!
95
- expect { m.start! }.not_to raise_error
96
- end
97
- end
98
- end
1
+ # # reset integrations so that something like ActiveRecord is not loaded and conflicting
2
+ # require 'state_machines'
3
+ # StateMachines::Integrations.reset
4
+ #
5
+ # require 'spec_helper'
6
+ # require 'state_machines-mongoid'
7
+ # require 'helpers/mongoid'
8
+ #
9
+ # describe StateMachines::AuditTrail::Backend::Mongoid do
10
+ #
11
+ # context '#create_for' do
12
+ # it 'should create a Mongoid backend' do
13
+ # backend = StateMachines::AuditTrail::Backend.create_for(MongoidTestModelStateTransition, MongoidTestModel)
14
+ # expect(backend).to be_instance_of(StateMachines::AuditTrail::Backend::Mongoid)
15
+ # end
16
+ # end
17
+ #
18
+ # context 'single state machine' do
19
+ # let!(:target) { MongoidTestModel.create! }
20
+ #
21
+ # it 'should populate all fields' do
22
+ # target.start!
23
+ # last_transition = MongoidTestModelStateTransition.where(:mongoid_test_model_id => target.id).last
24
+ #
25
+ # expect(last_transition.event).to eq 'start'
26
+ # expect(last_transition.from).to eq 'waiting'
27
+ # expect(last_transition.to).to eq 'started'
28
+ # expect(last_transition.created_at).to be_within(10.seconds).of(DateTime.now)
29
+ # end
30
+ #
31
+ # it 'should log multiple events' do
32
+ # expect { target.start && target.stop && target.start }.to change(MongoidTestModelStateTransition, :count).by(3)
33
+ # end
34
+ #
35
+ # it 'do nothing on failed transition' do
36
+ # expect { target.stop }.not_to change(MongoidTestModelStateTransition, :count)
37
+ # end
38
+ # end
39
+ #
40
+ # context 'multiple state machines' do
41
+ # let!(:target) { MongoidTestModelWithMultipleStateMachines.create! }
42
+ #
43
+ # it 'should log a state transition for the affected state machine' do
44
+ # expect { target.begin_first! }.to change(MongoidTestModelWithMultipleStateMachinesFirstTransition, :count).by(1)
45
+ # end
46
+ #
47
+ # it 'should not log a state transition for the unaffected state machine' do
48
+ # expect { target.begin_first! }.not_to change(MongoidTestModelWithMultipleStateMachinesSecondTransition, :count)
49
+ # end
50
+ # end
51
+ #
52
+ # context 'on an object with a state machine having an initial state' do
53
+ # let(:target_class) { MongoidTestModelWithMultipleStateMachines }
54
+ # let(:state_transition_class) { MongoidTestModelWithMultipleStateMachinesFirstTransition }
55
+ #
56
+ # it 'should log a state transition for the inital state' do
57
+ # expect { target_class.create! }.to change(state_transition_class, :count).by(1)
58
+ # end
59
+ #
60
+ # it 'should only set the :to state for the initial transition' do
61
+ # target_class.create!
62
+ # initial_transition = state_transition_class.last
63
+ # expect(initial_transition.event).to be_nil
64
+ # expect(initial_transition.from).to be_nil
65
+ # expect(initial_transition.to).to eq 'beginning'
66
+ # expect(initial_transition.created_at).to be_within(10.seconds).of(DateTime.now)
67
+ # end
68
+ # end
69
+ #
70
+ # context 'on an object with a state machine not having an initial state' do
71
+ # let(:target_class) { MongoidTestModelWithMultipleStateMachines }
72
+ # let(:state_transition_class) { MongoidTestModelWithMultipleStateMachinesSecondTransition }
73
+ #
74
+ # it 'should not log a transition when the object is created' do
75
+ # expect { target_class.create! }.not_to change(state_transition_class, :count)
76
+ # end
77
+ #
78
+ # it 'should log a transition for the first event' do
79
+ # expect { target_class.create.begin_second! }.to change(state_transition_class, :count).by(1)
80
+ # end
81
+ #
82
+ # it 'should not set a value for the :from state on the first transition' do
83
+ # target_class.create.begin_second!
84
+ # first_transition = state_transition_class.last
85
+ # expect(first_transition.event).to eq 'begin_second'
86
+ # expect(first_transition.from).to be_nil
87
+ # expect(first_transition.to).to eq 'beginning_second'
88
+ # expect(first_transition.created_at).to be_within(10.seconds).of(DateTime.now)
89
+ # end
90
+ # end
91
+ #
92
+ # context 'on a class using STI' do
93
+ # it 'should properly grab the class name from STI models' do
94
+ # m = MongoidTestModelDescendant.create!
95
+ # expect { m.start! }.not_to raise_error
96
+ # end
97
+ # end
98
+ # 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(SomeNamespace::Subscription state)
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 SomeNamespace::SubscriptionStateTransition'
39
+ # contains 'class SomeModule::SubscriptionStateTransition'
40
40
  # end
41
41
  # end
42
42
  # end
@@ -22,12 +22,17 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency('state_machines-mongoid')
23
23
  s.add_development_dependency('rake')
24
24
  s.add_development_dependency('rspec', '>= 3.0.0')
25
- s.add_development_dependency('activerecord', '>= 4.0.0')
26
- s.add_development_dependency('sqlite3')
27
- s.add_development_dependency('mongoid', '>= 4.0.0')
28
- s.add_development_dependency('bson_ext')
25
+ s.add_development_dependency('activerecord', '>= 5.0.0')
26
+ if(defined?(JRUBY_VERSION))
27
+ s.add_development_dependency('activerecord-jdbcsqlite3-adapter')
28
+ else
29
+ s.add_development_dependency('sqlite3')
30
+ end
31
+ s.add_development_dependency('mongoid', '>= 6.0.0.beta')
32
+ s.add_development_dependency('bson')
29
33
  s.add_development_dependency('generator_spec')
30
- s.add_development_dependency('rails', '>= 4.0.0')
34
+ s.add_development_dependency('rails', '>= 5.0.0')
35
+ s.add_development_dependency('appraisal', '~> 2.2.0')
31
36
 
32
37
  s.files = `git ls-files`.split($/).reject { |f| f =~ /^samples\// }
33
38
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machines-audit_trail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Ross
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-03-30 00:00:00.000000000 Z
13
+ date: 2020-06-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: state_machines
@@ -88,14 +88,14 @@ dependencies:
88
88
  requirements:
89
89
  - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: 4.0.0
91
+ version: 5.0.0
92
92
  type: :development
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
- version: 4.0.0
98
+ version: 5.0.0
99
99
  - !ruby/object:Gem::Dependency
100
100
  name: sqlite3
101
101
  requirement: !ruby/object:Gem::Requirement
@@ -116,16 +116,16 @@ dependencies:
116
116
  requirements:
117
117
  - - ">="
118
118
  - !ruby/object:Gem::Version
119
- version: 4.0.0
119
+ version: 6.0.0.beta
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
- version: 4.0.0
126
+ version: 6.0.0.beta
127
127
  - !ruby/object:Gem::Dependency
128
- name: bson_ext
128
+ name: bson
129
129
  requirement: !ruby/object:Gem::Requirement
130
130
  requirements:
131
131
  - - ">="
@@ -158,14 +158,28 @@ dependencies:
158
158
  requirements:
159
159
  - - ">="
160
160
  - !ruby/object:Gem::Version
161
- version: 4.0.0
161
+ version: 5.0.0
162
162
  type: :development
163
163
  prerelease: false
164
164
  version_requirements: !ruby/object:Gem::Requirement
165
165
  requirements:
166
166
  - - ">="
167
167
  - !ruby/object:Gem::Version
168
- version: 4.0.0
168
+ version: 5.0.0
169
+ - !ruby/object:Gem::Dependency
170
+ name: appraisal
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: 2.2.0
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - "~>"
181
+ - !ruby/object:Gem::Version
182
+ version: 2.2.0
169
183
  description: Log transitions on a state_machines to support auditing and business
170
184
  process analytics.
171
185
  email:
@@ -178,11 +192,19 @@ extra_rdoc_files: []
178
192
  files:
179
193
  - ".gitignore"
180
194
  - ".rspec"
195
+ - ".ruby-gemset"
196
+ - ".ruby-version"
181
197
  - ".travis.yml"
198
+ - Appraisals
182
199
  - Gemfile
183
200
  - LICENSE
184
201
  - README.md
185
202
  - Rakefile
203
+ - gemfiles/rails_5.0.gemfile
204
+ - gemfiles/rails_5.1.gemfile
205
+ - gemfiles/rails_5.2.gemfile
206
+ - gemfiles/rails_6.0.gemfile
207
+ - gemfiles/rails_edge.gemfile
186
208
  - lib/state_machines-audit_trail.rb
187
209
  - lib/state_machines/audit_trail.rb
188
210
  - lib/state_machines/audit_trail/backend.rb
@@ -220,8 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
242
  - !ruby/object:Gem::Version
221
243
  version: '0'
222
244
  requirements: []
223
- rubyforge_project:
224
- rubygems_version: 2.4.5
245
+ rubygems_version: 3.0.3
225
246
  signing_key:
226
247
  specification_version: 4
227
248
  summary: Log transitions on a state_machines to support auditing and business process