statesman 1.1.0 → 1.2.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 +4 -4
- data/.rubocop.yml +7 -1
- data/.travis.yml +19 -4
- data/CHANGELOG.md +30 -0
- data/README.md +11 -35
- data/lib/generators/statesman/add_constraints_to_most_recent_generator.rb +28 -0
- data/lib/generators/statesman/add_most_recent_generator.rb +25 -0
- data/lib/generators/statesman/generator_helpers.rb +6 -2
- data/lib/generators/statesman/templates/add_constraints_to_most_recent_migration.rb.erb +13 -0
- data/lib/generators/statesman/templates/add_most_recent_migration.rb.erb +9 -0
- data/lib/generators/statesman/templates/create_migration.rb.erb +4 -3
- data/lib/generators/statesman/templates/update_migration.rb.erb +5 -4
- data/lib/statesman.rb +1 -0
- data/lib/statesman/adapters/active_record.rb +23 -8
- data/lib/statesman/adapters/active_record_queries.rb +62 -15
- data/lib/statesman/railtie.rb +9 -0
- data/lib/statesman/version.rb +1 -1
- data/lib/tasks/statesman.rake +49 -0
- data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions.rb +13 -0
- data/spec/fixtures/add_most_recent_to_bacon_transitions.rb +9 -0
- data/spec/generators/statesman/active_record_transition_generator_spec.rb +0 -2
- data/spec/generators/statesman/add_constraints_to_most_recent_generator_spec.rb +38 -0
- data/spec/generators/statesman/add_most_recent_generator_spec.rb +35 -0
- data/spec/generators/statesman/migration_generator_spec.rb +10 -1
- data/spec/generators/statesman/mongoid_transition_generator_spec.rb +0 -2
- data/spec/spec_helper.rb +22 -7
- data/spec/statesman/adapters/active_record_queries_spec.rb +110 -28
- data/spec/statesman/adapters/active_record_spec.rb +61 -31
- data/spec/statesman/adapters/mongoid_spec.rb +8 -17
- data/spec/statesman/adapters/shared_examples.rb +10 -17
- data/spec/statesman/callback_spec.rb +2 -6
- data/spec/statesman/config_spec.rb +2 -5
- data/spec/statesman/guard_spec.rb +3 -9
- data/spec/statesman/machine_spec.rb +91 -129
- data/spec/support/active_record.rb +35 -4
- data/spec/support/generators_shared_examples.rb +1 -4
- data/statesman.gemspec +5 -3
- metadata +52 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7adec92ffe517ecf4227b1190dcf1289abce8fe
|
4
|
+
data.tar.gz: 2f5876f6d294df4f2d52272c473d5c4521884d1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e9be2c9b771cbd4f2d19b7de27854f047659c33deb6907af3b38bb8130feda521f9faf22ffe7560dc01fed63390ab569aae6b8fe3469e1d4159efba430ba428
|
7
|
+
data.tar.gz: e4e509269a3fd7adcfc3cfa15be27cef5d5e36e8e04fea8acf3c1fb6cef213ed3d91174c0efee5985eb691ec15ab59d105579769e651b4e1ca1d6546eb2cdc99
|
data/.rubocop.yml
CHANGED
@@ -4,8 +4,11 @@ AllCops:
|
|
4
4
|
Include:
|
5
5
|
- Rakefile
|
6
6
|
- statesman.gemfile
|
7
|
+
- lib/tasks/*.rake
|
7
8
|
Exclude:
|
8
|
-
- vendor
|
9
|
+
- vendor/**/*
|
10
|
+
- .*/**
|
11
|
+
- spec/fixtures/**/*
|
9
12
|
|
10
13
|
StringLiterals:
|
11
14
|
Enabled: false
|
@@ -36,3 +39,6 @@ GuardClause:
|
|
36
39
|
|
37
40
|
SingleSpaceBeforeFirstArg:
|
38
41
|
Enabled: false
|
42
|
+
|
43
|
+
Style/DotPosition:
|
44
|
+
EnforcedStyle: 'trailing'
|
data/.travis.yml
CHANGED
@@ -1,11 +1,26 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
1
3
|
rvm:
|
2
4
|
- 2.1
|
3
5
|
- 2.0.0
|
4
6
|
- 1.9.3
|
7
|
+
|
8
|
+
sudo: false
|
9
|
+
|
5
10
|
services: mongodb
|
11
|
+
|
12
|
+
before_script:
|
13
|
+
- mysql -e 'CREATE DATABASE statesman_test;'
|
14
|
+
- psql -c 'CREATE DATABASE statesman_test;' -U postgres
|
15
|
+
|
6
16
|
script:
|
7
|
-
|
8
|
-
|
17
|
+
- bundle exec rubocop
|
18
|
+
- bundle exec rake
|
19
|
+
|
9
20
|
env:
|
10
|
-
- "RAILS_VERSION=
|
11
|
-
- "RAILS_VERSION=4.
|
21
|
+
- "RAILS_VERSION=3.2.21"
|
22
|
+
- "RAILS_VERSION=4.0.12"
|
23
|
+
- "RAILS_VERSION=4.1.8"
|
24
|
+
- "RAILS_VERSION=4.2.0"
|
25
|
+
- "RAILS_VERSION=4.2.0 DATABASE_URL=mysql2://root@localhost/statesman_test"
|
26
|
+
- "RAILS_VERSION=4.2.0 DATABASE_URL=postgres://postgres@localhost/statesman_test"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
## v1.2.0 18 March 2015
|
2
|
+
|
3
|
+
*Changes*
|
4
|
+
|
5
|
+
- Add a `most_recent` column to transition tables to greatly speed up queries (ActiveRecord adapter only).
|
6
|
+
- All queries are backwards-compatible, so everything still works without the new column.
|
7
|
+
- The upgrade path is:
|
8
|
+
- Generate and run a migration for adding the column, by running `rails generate statesman:add_most_recent <ParentModel> <TransitionModel>`.
|
9
|
+
- Backfill the `most_recent` column on old records by running `rake statesman:backfill_most_recent[ParentModel] `.
|
10
|
+
- Add constraints and indexes to the transition table that make use of the new field, by running `rails g statesman:add_constraints_to_most_recent <ParentModel> <TransitionModel>`.
|
11
|
+
- The upgrade path has been designed to be zero-downtime, even on large tables. As a result, please note that queries will only use the `most_recent` field after the constraints have been added.
|
12
|
+
- `ActiveRecordQueries.{not_,}in_state` now accepts an array of states.
|
13
|
+
|
14
|
+
|
15
|
+
## v1.1.0 9 December 2014
|
16
|
+
*Fixes*
|
17
|
+
|
18
|
+
- Support for Rails 4.2.0.rc2:
|
19
|
+
- Remove use of serialized_attributes when using 4.2+. (patch by [@greysteil](https://github.com/greysteil))
|
20
|
+
- Use reflect_on_association rather than directly using the reflections hash. (patch by [@timrogers](https://github.com/timrogers))
|
21
|
+
- Fix `ActiveRecordQueries.in_state` when `Model.initial_state` is defined as a symbol. (patch by [@isaacseymour](https://github.com/isaacseymour))
|
22
|
+
|
23
|
+
*Changes*
|
24
|
+
|
25
|
+
- Transition metadata now defaults to `{}` rather than `nil`. (patch by [@greysteil](https://github.com/greysteil))
|
26
|
+
|
27
|
+
## v1.0.0 21 November 2014
|
28
|
+
|
29
|
+
No changes from v1.0.0.beta2
|
30
|
+
|
1
31
|
## v1.0.0.beta2 10 October 2014
|
2
32
|
*Breaking changes*
|
3
33
|
|
data/README.md
CHANGED
@@ -60,7 +60,7 @@ class Order < ActiveRecord::Base
|
|
60
60
|
has_many :order_transitions
|
61
61
|
|
62
62
|
def state_machine
|
63
|
-
OrderStateMachine.new(self, transition_class: OrderTransition)
|
63
|
+
@state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition)
|
64
64
|
end
|
65
65
|
|
66
66
|
private
|
@@ -222,6 +222,13 @@ It is also possible to use the PostgreSQL JSON column if you are using Rails 4.
|
|
222
222
|
* Remove `include Statesman::Adapters::ActiveRecordTransition` statement from your
|
223
223
|
transition model
|
224
224
|
|
225
|
+
#### Creating transitions without using `#transition_to` with ActiveRecord
|
226
|
+
|
227
|
+
By default, Statesman will include a `most_recent` column on the transitions
|
228
|
+
table, and update its value each time `#transition_to` is called. If you create
|
229
|
+
transitions manually (for example to backfill for a new state) you will need to
|
230
|
+
set the `most_recent` attribute manually.
|
231
|
+
|
225
232
|
|
226
233
|
## Configuration
|
227
234
|
|
@@ -289,6 +296,9 @@ model object and transition object are passed as arguments to the callback.
|
|
289
296
|
This callback can have side-effects as it will only be run once immediately
|
290
297
|
after the transition.
|
291
298
|
|
299
|
+
If you specify `after_commit: true`, the callback will be executed once the
|
300
|
+
transition has been committed to the database.
|
301
|
+
|
292
302
|
#### `Machine.new`
|
293
303
|
```ruby
|
294
304
|
my_machine = Machine.new(my_model, transition_class: MyTransitionModel)
|
@@ -423,40 +433,6 @@ describe "some callback" do
|
|
423
433
|
end
|
424
434
|
```
|
425
435
|
|
426
|
-
#### Creating models in certain states
|
427
|
-
|
428
|
-
Sometimes you'll want to test a guard/transition from one state to another, where the state you want to go from is not the initial state of the model. In this instance you'll need to construct a model instance in the state required. However, if you have strict guards, this can be a pain. One way to get around this in tests is to directly create the transitions in the database, hence avoiding the guards.
|
429
|
-
|
430
|
-
We use [FactoryGirl](https://github.com/thoughtbot/factory_girl) for creating our test objects. Given an `Order` model that is backed by Statesman, we can easily set it up to be in a particular state:
|
431
|
-
|
432
|
-
```ruby
|
433
|
-
factory :order do
|
434
|
-
property "value"
|
435
|
-
...
|
436
|
-
|
437
|
-
trait :shipped do
|
438
|
-
after(:create) do |order|
|
439
|
-
FactoryGirl.create(:order_transition, :shipped, order: order)
|
440
|
-
end
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
factory :order_transition do
|
445
|
-
order
|
446
|
-
...
|
447
|
-
|
448
|
-
trait :shipped do
|
449
|
-
to_state "shipped"
|
450
|
-
end
|
451
|
-
end
|
452
|
-
```
|
453
|
-
|
454
|
-
This means you can easily create an `Order` in the `shipped` state:
|
455
|
-
|
456
|
-
```ruby
|
457
|
-
let(:shipped_order) { FactoryGirl.create(:order, :shipped) }
|
458
|
-
```
|
459
|
-
|
460
436
|
---
|
461
437
|
|
462
438
|
GoCardless ♥ open source. If you do too, come [join us](https://gocardless.com/jobs#software-engineer).
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "generators/statesman/generator_helpers"
|
3
|
+
|
4
|
+
module Statesman
|
5
|
+
class AddConstraintsToMostRecentGenerator < Rails::Generators::Base
|
6
|
+
include Statesman::GeneratorHelpers
|
7
|
+
|
8
|
+
desc "Adds uniqueness and not-null constraints to the most recent column " \
|
9
|
+
"for a statesman transition"
|
10
|
+
|
11
|
+
argument :parent, type: :string, desc: "Your parent model name"
|
12
|
+
argument :klass, type: :string, desc: "Your transition model name"
|
13
|
+
|
14
|
+
source_root File.expand_path('../templates', __FILE__)
|
15
|
+
|
16
|
+
def create_model_file
|
17
|
+
template("add_constraints_to_most_recent_migration.rb.erb",
|
18
|
+
migration_file_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def migration_file_name
|
24
|
+
"db/migrate/#{next_migration_number}_"\
|
25
|
+
"add_constraints_to_most_recent_for_#{table_name}.rb"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "generators/statesman/generator_helpers"
|
3
|
+
|
4
|
+
module Statesman
|
5
|
+
class AddMostRecentGenerator < Rails::Generators::Base
|
6
|
+
include Statesman::GeneratorHelpers
|
7
|
+
|
8
|
+
desc "Adds most_recent to a statesman transition model"
|
9
|
+
|
10
|
+
argument :parent, type: :string, desc: "Your parent model name"
|
11
|
+
argument :klass, type: :string, desc: "Your transition model name"
|
12
|
+
|
13
|
+
source_root File.expand_path('../templates', __FILE__)
|
14
|
+
|
15
|
+
def create_model_file
|
16
|
+
template("add_most_recent_migration.rb.erb", migration_file_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def migration_file_name
|
22
|
+
"db/migrate/#{next_migration_number}_add_most_recent_to_#{table_name}.rb"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -28,9 +28,13 @@ module Statesman
|
|
28
28
|
klass.demodulize.underscore.pluralize
|
29
29
|
end
|
30
30
|
|
31
|
+
def index_name(index_id)
|
32
|
+
"index_#{table_name}_#{index_id}"
|
33
|
+
end
|
34
|
+
|
31
35
|
def mysql?
|
32
|
-
ActiveRecord::Base.configurations[Rails.env]
|
33
|
-
|
36
|
+
ActiveRecord::Base.configurations[Rails.env].
|
37
|
+
try(:[], "adapter").try(:match, /mysql/)
|
34
38
|
end
|
35
39
|
end
|
36
40
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class AddConstraintsToMostRecentFor<%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
disable_ddl_transaction!
|
3
|
+
|
4
|
+
def up
|
5
|
+
add_index :<%= table_name %>, [:<%= parent_id %>, :most_recent], unique: true, where: "most_recent", name: "index_<%= table_name %>_parent_most_recent", algorithm: :concurrently
|
6
|
+
change_column_null :<%= table_name %>, :most_recent, false
|
7
|
+
end
|
8
|
+
|
9
|
+
def down
|
10
|
+
remove_index :<%= table_name %>, name: "index_<%= table_name %>_parent_most_recent"
|
11
|
+
change_column_null :<%= table_name %>, :most_recent, true
|
12
|
+
end
|
13
|
+
end
|
@@ -5,10 +5,11 @@ class Create<%= migration_class_name %> < ActiveRecord::Migration
|
|
5
5
|
t.text :metadata<%= ", default: \"{}\"" unless mysql? %>
|
6
6
|
t.integer :sort_key, null: false
|
7
7
|
t.integer :<%= parent_id %>, null: false
|
8
|
-
t.
|
8
|
+
t.boolean :most_recent, null: false
|
9
|
+
t.timestamps null: false
|
9
10
|
end
|
10
11
|
|
11
|
-
add_index :<%= table_name %>, :<%= parent_id %>
|
12
|
-
add_index :<%= table_name %>, [
|
12
|
+
add_index :<%= table_name %>, [:<%= parent_id %>, :sort_key], unique: true, name: "<%= index_name :parent_sort %>"
|
13
|
+
add_index :<%= table_name %>, [:<%= parent_id %>, :most_recent], unique: true, where: "most_recent", name: "<%= index_name :parent_most_recent %>"
|
13
14
|
end
|
14
15
|
end
|
@@ -4,10 +4,11 @@ class AddStatesmanTo<%= migration_class_name %> < ActiveRecord::Migration
|
|
4
4
|
add_column :<%= table_name %>, :metadata, :text<%= ", default: \"{}\"" unless mysql? %>
|
5
5
|
add_column :<%= table_name %>, :sort_key, :integer, null: false
|
6
6
|
add_column :<%= table_name %>, :<%= parent_id %>, :integer, null: false
|
7
|
-
add_column :<%= table_name %>, :
|
8
|
-
add_column :<%= table_name %>, :
|
7
|
+
add_column :<%= table_name %>, :most_recent, null: false
|
8
|
+
add_column :<%= table_name %>, :created_at, :datetime, null: false
|
9
|
+
add_column :<%= table_name %>, :updated_at, :datetime, null: false
|
9
10
|
|
10
|
-
add_index :<%= table_name %>, :<%= parent_id %>
|
11
|
-
add_index :<%= table_name %>, [
|
11
|
+
add_index :<%= table_name %>, [:<%= parent_id %>, :sort_key], unique: true, name: "<%= index_name :parent_sort %>"
|
12
|
+
add_index :<%= table_name %>, [:<%= parent_id %>, :most_recent], unique: true, where: "most_recent", name: "<%= index_name :parent_most_recent %>"
|
12
13
|
end
|
13
14
|
end
|
data/lib/statesman.rb
CHANGED
@@ -27,8 +27,8 @@ module Statesman
|
|
27
27
|
to = to.to_s
|
28
28
|
create_transition(from, to, metadata)
|
29
29
|
rescue ::ActiveRecord::RecordNotUnique => e
|
30
|
-
if e.message.include?(
|
31
|
-
e.message.include?(
|
30
|
+
if e.message.include?(@transition_class.table_name) &&
|
31
|
+
e.message.include?('sort_key') || e.message.include?('most_recent')
|
32
32
|
raise TransitionConflictError, e.message
|
33
33
|
else raise
|
34
34
|
end
|
@@ -53,11 +53,16 @@ module Statesman
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def create_transition(from, to, metadata)
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
transition_attributes = { to_state: to,
|
57
|
+
sort_key: next_sort_key,
|
58
|
+
metadata: metadata }
|
59
|
+
|
60
|
+
transition_attributes.merge!(most_recent: true) if most_recent_column?
|
61
|
+
|
62
|
+
transition = transitions_for_parent.build(transition_attributes)
|
59
63
|
|
60
64
|
::ActiveRecord::Base.transaction do
|
65
|
+
unset_old_most_recent
|
61
66
|
@observer.execute(:before, from, to, transition)
|
62
67
|
transition.save!
|
63
68
|
@last_transition = transition
|
@@ -72,14 +77,24 @@ module Statesman
|
|
72
77
|
@parent_model.send(@transition_class.table_name)
|
73
78
|
end
|
74
79
|
|
80
|
+
def unset_old_most_recent
|
81
|
+
return unless most_recent_column?
|
82
|
+
transitions_for_parent.update_all(most_recent: false)
|
83
|
+
end
|
84
|
+
|
85
|
+
def most_recent_column?
|
86
|
+
transition_class.columns_hash.include?("most_recent")
|
87
|
+
end
|
88
|
+
|
75
89
|
def next_sort_key
|
76
90
|
(last && last.sort_key + 10) || 0
|
77
91
|
end
|
78
92
|
|
79
93
|
def serialized?(transition_class)
|
80
|
-
if ::ActiveRecord.gem_version
|
81
|
-
|
82
|
-
|
94
|
+
if ::ActiveRecord.respond_to?(:gem_version) &&
|
95
|
+
::ActiveRecord.gem_version >= Gem::Version.new('4.2.0.a')
|
96
|
+
transition_class.columns_hash["metadata"].
|
97
|
+
cast_type.is_a?(::ActiveRecord::Type::Serialized)
|
83
98
|
else
|
84
99
|
transition_class.serialized_attributes.include?("metadata")
|
85
100
|
end
|
@@ -7,25 +7,51 @@ module Statesman
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
def in_state(*states)
|
10
|
-
states = states.map(&:to_s)
|
10
|
+
states = states.flatten.map(&:to_s)
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
if use_most_recent_column?
|
13
|
+
in_state_with_most_recent(states)
|
14
|
+
else
|
15
|
+
in_state_without_most_recent(states)
|
16
|
+
end
|
16
17
|
end
|
17
18
|
|
18
19
|
def not_in_state(*states)
|
19
|
-
states = states.map(&:to_s)
|
20
|
+
states = states.flatten.map(&:to_s)
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
if use_most_recent_column?
|
23
|
+
not_in_state_with_most_recent(states)
|
24
|
+
else
|
25
|
+
not_in_state_without_most_recent(states)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
28
30
|
|
31
|
+
def in_state_with_most_recent(states)
|
32
|
+
joins(most_recent_transition_join).
|
33
|
+
where(states_where('last_transition', states), states)
|
34
|
+
end
|
35
|
+
|
36
|
+
def not_in_state_with_most_recent(states)
|
37
|
+
joins(most_recent_transition_join).
|
38
|
+
where("NOT (#{states_where('last_transition', states)})", states)
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_state_without_most_recent(states)
|
42
|
+
joins(transition1_join).
|
43
|
+
joins(transition2_join).
|
44
|
+
where(states_where('transition1', states), states).
|
45
|
+
where("transition2.id" => nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
def not_in_state_without_most_recent(states)
|
49
|
+
joins(transition1_join).
|
50
|
+
joins(transition2_join).
|
51
|
+
where("NOT (#{states_where('transition1', states)})", states).
|
52
|
+
where("transition2.id" => nil)
|
53
|
+
end
|
54
|
+
|
29
55
|
def transition_class
|
30
56
|
raise NotImplementedError, "A transition_class method should be " \
|
31
57
|
"defined on the model"
|
@@ -55,15 +81,36 @@ module Statesman
|
|
55
81
|
AND transition2.sort_key > transition1.sort_key"
|
56
82
|
end
|
57
83
|
|
58
|
-
def
|
84
|
+
def most_recent_transition_join
|
85
|
+
"LEFT OUTER JOIN #{transition_name} AS last_transition
|
86
|
+
ON #{table_name}.id = last_transition.#{model_foreign_key}
|
87
|
+
AND last_transition.most_recent = #{db_true}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def states_where(temporary_table_name, states)
|
59
91
|
if initial_state.to_s.in?(states.map(&:to_s))
|
60
|
-
|
61
|
-
|
92
|
+
"#{temporary_table_name}.to_state IN (?) OR " \
|
93
|
+
"#{temporary_table_name}.to_state IS NULL"
|
62
94
|
else
|
63
|
-
|
64
|
-
|
95
|
+
"#{temporary_table_name}.to_state IN (?) AND " \
|
96
|
+
"#{temporary_table_name}.to_state IS NOT NULL"
|
65
97
|
end
|
66
98
|
end
|
99
|
+
|
100
|
+
def db_true
|
101
|
+
::ActiveRecord::Base.connection.quote(true)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Only use the most_recent column if it has a unique index guaranteeing
|
105
|
+
# it has good data
|
106
|
+
def use_most_recent_column?
|
107
|
+
::ActiveRecord::Base.connection.index_exists?(
|
108
|
+
transition_name,
|
109
|
+
[model_foreign_key, :most_recent],
|
110
|
+
unique: true,
|
111
|
+
name: "index_#{transition_name}_parent_most_recent"
|
112
|
+
)
|
113
|
+
end
|
67
114
|
end
|
68
115
|
end
|
69
116
|
end
|