statesman 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +3 -26
- data/.rubocop_todo.yml +7 -8
- data/CHANGELOG.md +4 -0
- data/Gemfile +5 -4
- data/Guardfile +3 -3
- data/README.md +1 -1
- data/lib/generators/statesman/active_record_transition_generator.rb +1 -1
- data/lib/generators/statesman/generator_helpers.rb +1 -1
- data/lib/generators/statesman/migration_generator.rb +1 -1
- data/lib/generators/statesman/mongoid_transition_generator.rb +1 -1
- data/lib/statesman.rb +7 -7
- data/lib/statesman/adapters/active_record.rb +18 -12
- data/lib/statesman/adapters/memory.rb +4 -1
- data/lib/statesman/adapters/mongoid.rb +2 -2
- data/lib/statesman/machine.rb +2 -6
- data/lib/statesman/version.rb +1 -1
- data/lib/tasks/statesman.rake +9 -3
- data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_with_partial_index.rb +6 -1
- data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_without_partial_index.rb +5 -1
- data/spec/generators/statesman/active_record_transition_generator_spec.rb +9 -9
- data/spec/generators/statesman/migration_generator_spec.rb +4 -4
- data/spec/generators/statesman/mongoid_transition_generator_spec.rb +4 -4
- data/spec/statesman/adapters/active_record_queries_spec.rb +1 -1
- data/spec/statesman/adapters/active_record_spec.rb +43 -19
- data/spec/statesman/adapters/mongoid_spec.rb +30 -0
- data/spec/statesman/adapters/shared_examples.rb +0 -1
- data/spec/statesman/machine_spec.rb +4 -4
- data/spec/support/active_record.rb +7 -7
- data/spec/support/generators_shared_examples.rb +5 -5
- data/spec/support/mongoid.rb +2 -2
- data/statesman.gemspec +6 -5
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4ea9661229864328ca481ff132837554323967d265616d38c295c5fa94ca9103
|
4
|
+
data.tar.gz: 8974f12e8e70d925f0b35e3753a9977d1ff94a1a76bf05c0a967535037ce6a24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec7c81ee5c313377acaf35c1196ccf74036769bb945c636d93f56d346deda319534ea9dbeff87bbfe84a2f3d71d7a1a4ae3d435d80c2fa46dc43d2ff3304661a
|
7
|
+
data.tar.gz: 78ed181ce45c7517f8617f61e26c20780f0f2c5b3a04236e4766761b985d42172774d05a54d05bcc2d72bc379eb2f6ed3fe68c122ce8989f19d3d5ba16d286af
|
data/.rubocop.yml
CHANGED
@@ -1,30 +1,7 @@
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
2
|
+
|
2
3
|
inherit_gem:
|
3
|
-
|
4
|
+
gc_ruboconfig: rubocop.yml
|
4
5
|
|
5
6
|
AllCops:
|
6
|
-
|
7
|
-
Exclude:
|
8
|
-
- vendor/**/*
|
9
|
-
- .*/**
|
10
|
-
- spec/fixtures/**/*
|
11
|
-
|
12
|
-
StringLiterals:
|
13
|
-
Enabled: false
|
14
|
-
|
15
|
-
MethodLength:
|
16
|
-
CountComments: false
|
17
|
-
Max: 15
|
18
|
-
|
19
|
-
AbcSize:
|
20
|
-
Max: 18
|
21
|
-
|
22
|
-
LineLength:
|
23
|
-
Max: 80
|
24
|
-
|
25
|
-
# Allow class and message or instance raises
|
26
|
-
Style/RaiseArgs:
|
27
|
-
Enabled: false
|
28
|
-
|
29
|
-
Lint/RescueWithoutErrorClass:
|
30
|
-
Enabled: false
|
7
|
+
TargetRubyVersion: 2.2
|
data/.rubocop_todo.yml
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2017-
|
3
|
+
# on 2017-12-20 11:33:28 +0000 using RuboCop version 0.52.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
10
|
-
|
11
|
-
|
12
|
-
Max: 42
|
9
|
+
# Offense count: 5
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 18
|
13
12
|
|
14
|
-
# Offense count:
|
13
|
+
# Offense count: 4
|
15
14
|
# Configuration parameters: CountComments.
|
16
|
-
Metrics/
|
17
|
-
Max:
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Max: 14
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## v3.3.0, 5 January 2018
|
2
|
+
|
3
|
+
- Touch `updated_at` on transitions when unsetting `most_recent` flag (patch by [@NGMarmaduke](https://github.com/NGMarmaduke))
|
4
|
+
- Fix `force_reload` for ActiveRecord models with loaded transitions (patch by [@jacobpgn](https://github.com/))
|
1
5
|
|
2
6
|
## v3.2.0, 27 November 2017
|
3
7
|
|
data/Gemfile
CHANGED
@@ -3,13 +3,14 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
gem "rails", "~> #{ENV['RAILS_VERSION']}" if ENV["RAILS_VERSION"]
|
6
|
-
gem "ruboconfig", git: "https://github.com/gocardless/ruboconfig", tag: "v1.0.1"
|
7
6
|
|
8
7
|
group :development do
|
9
8
|
gem "mongoid", ">= 3.1" unless ENV["EXCLUDE_MONGOID"]
|
10
9
|
|
11
10
|
# test/unit is no longer bundled with Ruby 2.2, but required by Rails
|
12
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.0")
|
13
|
-
|
14
|
-
|
11
|
+
gem "test-unit", "~> 3.0" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.0")
|
12
|
+
end
|
13
|
+
|
14
|
+
group :development, :test do
|
15
|
+
gem "gc_ruboconfig", "~> 2.1.0"
|
15
16
|
end
|
data/Guardfile
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
|
-
guard :rspec, all_on_start: true, cmd:
|
4
|
+
guard :rspec, all_on_start: true, cmd: "bundle exec rspec --color" do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
6
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
-
watch(
|
7
|
+
watch("spec/spec_helper.rb") { "spec" }
|
8
8
|
end
|
9
9
|
|
10
|
-
guard :rubocop, all_on_start: true, cli: [
|
10
|
+
guard :rubocop, all_on_start: true, cli: ["--format", "clang"] do
|
11
11
|
watch(/.+\.rb$/)
|
12
12
|
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
13
13
|
watch(%r{(?:.+/)?\rubocop-todo\.yml$}) { |m| File.dirname(m[0]) }
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ module Statesman
|
|
11
11
|
argument :parent, type: :string, desc: "Your parent model name"
|
12
12
|
argument :klass, type: :string, desc: "Your transition model name"
|
13
13
|
|
14
|
-
source_root File.expand_path(
|
14
|
+
source_root File.expand_path("../templates", __FILE__)
|
15
15
|
|
16
16
|
def create_model_file
|
17
17
|
template("create_migration.rb.erb", migration_file_name)
|
@@ -11,7 +11,7 @@ module Statesman
|
|
11
11
|
argument :parent, type: :string, desc: "Your parent model name"
|
12
12
|
argument :klass, type: :string, desc: "Your transition model name"
|
13
13
|
|
14
|
-
source_root File.expand_path(
|
14
|
+
source_root File.expand_path("../templates", __FILE__)
|
15
15
|
|
16
16
|
def create_model_file
|
17
17
|
template("update_migration.rb.erb", file_name)
|
@@ -10,7 +10,7 @@ module Statesman
|
|
10
10
|
argument :parent, type: :string, desc: "Your parent model name"
|
11
11
|
argument :klass, type: :string, desc: "Your transition model name"
|
12
12
|
|
13
|
-
source_root File.expand_path(
|
13
|
+
source_root File.expand_path("../templates", __FILE__)
|
14
14
|
|
15
15
|
def create_model_file
|
16
16
|
template("mongoid_transition_model.rb.erb", model_file_name)
|
data/lib/statesman.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Statesman
|
2
|
-
autoload :Config,
|
3
|
-
autoload :Machine,
|
4
|
-
autoload :Callback,
|
5
|
-
autoload :Guard,
|
6
|
-
autoload :Utils,
|
7
|
-
autoload :Version,
|
2
|
+
autoload :Config, "statesman/config"
|
3
|
+
autoload :Machine, "statesman/machine"
|
4
|
+
autoload :Callback, "statesman/callback"
|
5
|
+
autoload :Guard, "statesman/guard"
|
6
|
+
autoload :Utils, "statesman/utils"
|
7
|
+
autoload :Version, "statesman/version"
|
8
8
|
module Adapters
|
9
9
|
autoload :Memory, "statesman/adapters/memory"
|
10
10
|
autoload :ActiveRecord, "statesman/adapters/active_record"
|
@@ -16,7 +16,7 @@ module Statesman
|
|
16
16
|
autoload :MongoidTransition,
|
17
17
|
"statesman/adapters/mongoid_transition"
|
18
18
|
end
|
19
|
-
require
|
19
|
+
require "statesman/railtie" if defined?(::Rails::Railtie)
|
20
20
|
|
21
21
|
# Example:
|
22
22
|
# Statesman.configure do
|
@@ -13,17 +13,17 @@ module Statesman
|
|
13
13
|
if ::ActiveRecord::Base.connection.respond_to?(:supports_partial_index?)
|
14
14
|
::ActiveRecord::Base.connection.supports_partial_index?
|
15
15
|
else
|
16
|
-
::ActiveRecord::Base.connection.adapter_name ==
|
16
|
+
::ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
def initialize(transition_class, parent_model, observer, options = {})
|
21
21
|
serialized = serialized?(transition_class)
|
22
|
-
column_type = transition_class.columns_hash[
|
22
|
+
column_type = transition_class.columns_hash["metadata"].sql_type
|
23
23
|
if !serialized && !JSON_COLUMN_TYPES.include?(column_type)
|
24
|
-
raise UnserializedMetadataError
|
24
|
+
raise UnserializedMetadataError, transition_class.name
|
25
25
|
elsif serialized && JSON_COLUMN_TYPES.include?(column_type)
|
26
|
-
raise IncompatibleSerializationError
|
26
|
+
raise IncompatibleSerializationError, transition_class.name
|
27
27
|
end
|
28
28
|
@transition_class = transition_class
|
29
29
|
@parent_model = parent_model
|
@@ -41,8 +41,8 @@ module Statesman
|
|
41
41
|
@last_transition = nil
|
42
42
|
end
|
43
43
|
|
44
|
-
def history
|
45
|
-
if transitions_for_parent.loaded?
|
44
|
+
def history(force_reload: false)
|
45
|
+
if transitions_for_parent.loaded? && !force_reload
|
46
46
|
# Workaround for Rails bug which causes infinite loop when sorting
|
47
47
|
# already loaded result set. Introduced in rails/rails@b097ebe
|
48
48
|
transitions_for_parent.to_a.sort_by(&:sort_key)
|
@@ -53,7 +53,7 @@ module Statesman
|
|
53
53
|
|
54
54
|
def last(force_reload: false)
|
55
55
|
if force_reload
|
56
|
-
@last_transition = history.last
|
56
|
+
@last_transition = history(force_reload: true).last
|
57
57
|
else
|
58
58
|
@last_transition ||= history.last
|
59
59
|
end
|
@@ -87,6 +87,12 @@ module Statesman
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def unset_old_most_recent
|
90
|
+
most_recent = transitions_for_parent.where(most_recent: true)
|
91
|
+
updated_at = if ::ActiveRecord::Base.default_timezone == :utc
|
92
|
+
Time.now.utc
|
93
|
+
else
|
94
|
+
Time.now
|
95
|
+
end
|
90
96
|
# Check whether the `most_recent` column allows null values. If it
|
91
97
|
# doesn't, set old records to `false`, otherwise, set them to `NULL`.
|
92
98
|
#
|
@@ -94,10 +100,10 @@ module Statesman
|
|
94
100
|
# support partial indexes. By doing the conditioning on the column,
|
95
101
|
# rather than Rails' opinion of whether the database supports partial
|
96
102
|
# indexes, we're robust to DBs later adding support for partial indexes.
|
97
|
-
if transition_class.columns_hash[
|
98
|
-
|
103
|
+
if transition_class.columns_hash["most_recent"].null == false
|
104
|
+
most_recent.update_all(most_recent: false, updated_at: updated_at)
|
99
105
|
else
|
100
|
-
|
106
|
+
most_recent.update_all(most_recent: nil, updated_at: updated_at)
|
101
107
|
end
|
102
108
|
end
|
103
109
|
|
@@ -107,7 +113,7 @@ module Statesman
|
|
107
113
|
|
108
114
|
def serialized?(transition_class)
|
109
115
|
if ::ActiveRecord.respond_to?(:gem_version) &&
|
110
|
-
::ActiveRecord.gem_version >= Gem::Version.new(
|
116
|
+
::ActiveRecord.gem_version >= Gem::Version.new("4.2.0.a")
|
111
117
|
transition_class.type_for_attribute("metadata").
|
112
118
|
is_a?(::ActiveRecord::Type::Serialized)
|
113
119
|
else
|
@@ -117,7 +123,7 @@ module Statesman
|
|
117
123
|
|
118
124
|
def transition_conflict_error?(e)
|
119
125
|
e.message.include?(@transition_class.table_name) &&
|
120
|
-
(e.message.include?(
|
126
|
+
(e.message.include?("sort_key") || e.message.include?("most_recent"))
|
121
127
|
end
|
122
128
|
end
|
123
129
|
end
|
@@ -4,7 +4,6 @@ module Statesman
|
|
4
4
|
module Adapters
|
5
5
|
class Memory
|
6
6
|
attr_reader :transition_class
|
7
|
-
attr_reader :history
|
8
7
|
attr_reader :parent_model
|
9
8
|
|
10
9
|
# We only accept mode as a parameter to maintain a consistent interface
|
@@ -32,6 +31,10 @@ module Statesman
|
|
32
31
|
@history.sort_by(&:sort_key).last
|
33
32
|
end
|
34
33
|
|
34
|
+
def history(*)
|
35
|
+
@history
|
36
|
+
end
|
37
|
+
|
35
38
|
private
|
36
39
|
|
37
40
|
def next_sort_key
|
@@ -10,7 +10,7 @@ module Statesman
|
|
10
10
|
@transition_class = transition_class
|
11
11
|
@parent_model = parent_model
|
12
12
|
@observer = observer
|
13
|
-
unless transition_class_hash_fields.include?(
|
13
|
+
unless transition_class_hash_fields.include?("statesman_metadata")
|
14
14
|
raise UnserializedMetadataError, metadata_field_error_message
|
15
15
|
end
|
16
16
|
end
|
@@ -32,7 +32,7 @@ module Statesman
|
|
32
32
|
@last_transition = nil
|
33
33
|
end
|
34
34
|
|
35
|
-
def history
|
35
|
+
def history(*)
|
36
36
|
transitions_for_parent.asc(:sort_key)
|
37
37
|
end
|
38
38
|
|
data/lib/statesman/machine.rb
CHANGED
@@ -138,12 +138,8 @@ module Statesman
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def validate_callback_type_and_class(callback_type, callback_class)
|
141
|
-
if callback_type.nil?
|
142
|
-
|
143
|
-
end
|
144
|
-
if callback_class.nil?
|
145
|
-
raise ArgumentError.new("missing keyword: callback_class")
|
146
|
-
end
|
141
|
+
raise ArgumentError, "missing keyword: callback_type" if callback_type.nil?
|
142
|
+
raise ArgumentError, "missing keyword: callback_class" if callback_class.nil?
|
147
143
|
end
|
148
144
|
|
149
145
|
def validate_state(state)
|
data/lib/statesman/version.rb
CHANGED
data/lib/tasks/statesman.rake
CHANGED
@@ -8,6 +8,11 @@ namespace :statesman do
|
|
8
8
|
parent_class = parent_model_name.constantize
|
9
9
|
transition_class = parent_class.transition_class
|
10
10
|
parent_fk = "#{parent_model_name.demodulize.underscore}_id"
|
11
|
+
updated_at = if ActiveRecord::Base.default_timezone == :utc
|
12
|
+
Time.now.utc
|
13
|
+
else
|
14
|
+
Time.now
|
15
|
+
end
|
11
16
|
|
12
17
|
total_models = parent_class.count
|
13
18
|
done_models = 0
|
@@ -18,10 +23,10 @@ namespace :statesman do
|
|
18
23
|
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
19
24
|
# Set all transitions' most_recent to FALSE
|
20
25
|
transition_class.where(parent_fk => models.map(&:id)).
|
21
|
-
update_all(most_recent: false)
|
26
|
+
update_all(most_recent: false, updated_at: updated_at)
|
22
27
|
else
|
23
28
|
transition_class.where(parent_fk => models.map(&:id)).
|
24
|
-
update_all(most_recent: nil)
|
29
|
+
update_all(most_recent: nil, updated_at: updated_at)
|
25
30
|
end
|
26
31
|
|
27
32
|
# Set current transition's most_recent to TRUE
|
@@ -44,7 +49,8 @@ namespace :statesman do
|
|
44
49
|
latest_ids = transition_class.find_by_sql(latest_ids_query).
|
45
50
|
to_a.collect(&:id)
|
46
51
|
|
47
|
-
transition_class.where(id: latest_ids).
|
52
|
+
transition_class.where(id: latest_ids).
|
53
|
+
update_all(most_recent: true, updated_at: updated_at)
|
48
54
|
end
|
49
55
|
|
50
56
|
done_models += batch_size
|
data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_with_partial_index.rb
CHANGED
@@ -2,7 +2,12 @@ class AddConstraintsToMostRecentForBaconTransitions < ActiveRecord::Migration
|
|
2
2
|
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def up
|
5
|
-
add_index :bacon_transitions,
|
5
|
+
add_index :bacon_transitions,
|
6
|
+
%i[bacon_id most_recent],
|
7
|
+
unique: true,
|
8
|
+
where: "most_recent",
|
9
|
+
name: "index_bacon_transitions_parent_most_recent",
|
10
|
+
algorithm: :concurrently
|
6
11
|
change_column_null :bacon_transitions, :most_recent, false
|
7
12
|
end
|
8
13
|
|
data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_without_partial_index.rb
CHANGED
@@ -2,7 +2,11 @@ class AddConstraintsToMostRecentForBaconTransitions < ActiveRecord::Migration
|
|
2
2
|
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def up
|
5
|
-
add_index :bacon_transitions,
|
5
|
+
add_index :bacon_transitions,
|
6
|
+
%i[bacon_id most_recent],
|
7
|
+
unique: true,
|
8
|
+
name: "index_bacon_transitions_parent_most_recent",
|
9
|
+
algorithm: :concurrently
|
6
10
|
end
|
7
11
|
|
8
12
|
def down
|
@@ -4,35 +4,35 @@ require "generators/statesman/active_record_transition_generator"
|
|
4
4
|
|
5
5
|
describe Statesman::ActiveRecordTransitionGenerator, type: :generator do
|
6
6
|
it_behaves_like "a generator" do
|
7
|
-
let(:migration_name) {
|
7
|
+
let(:migration_name) { "db/migrate/create_bacon_transitions.rb" }
|
8
8
|
end
|
9
9
|
|
10
|
-
describe
|
10
|
+
describe "creates a migration" do
|
11
11
|
subject { file("db/migrate/#{time}_create_bacon_transitions.rb") }
|
12
12
|
|
13
13
|
before { allow(Time).to receive(:now).and_return(mock_time) }
|
14
14
|
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
15
15
|
|
16
|
-
let(:mock_time) { double(
|
17
|
-
let(:time) {
|
16
|
+
let(:mock_time) { double("Time", utc: double("UTCTime", strftime: time)) }
|
17
|
+
let(:time) { "5678309" }
|
18
18
|
|
19
19
|
it "includes a foreign key" do
|
20
20
|
expect(subject).to contain("add_foreign_key :bacon_transitions, :bacons")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe
|
24
|
+
describe "properly adds class names" do
|
25
25
|
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
26
|
-
subject { file(
|
26
|
+
subject { file("app/models/yummy/bacon_transition.rb") }
|
27
27
|
|
28
28
|
it { is_expected.to contain(/:bacon_transition/) }
|
29
29
|
it { is_expected.not_to contain(%r{:yummy/bacon}) }
|
30
30
|
it { is_expected.to contain(/class_name: 'Yummy::Bacon'/) }
|
31
31
|
end
|
32
32
|
|
33
|
-
describe
|
33
|
+
describe "properly formats without class names" do
|
34
34
|
before { run_generator %w[Bacon BaconTransition] }
|
35
|
-
subject { file(
|
35
|
+
subject { file("app/models/bacon_transition.rb") }
|
36
36
|
|
37
37
|
it { is_expected.not_to contain(/class_name:/) }
|
38
38
|
it { is_expected.to contain(/class BaconTransition/) }
|
@@ -40,7 +40,7 @@ describe Statesman::ActiveRecordTransitionGenerator, type: :generator do
|
|
40
40
|
|
41
41
|
describe "it doesn't create any double-spacing" do
|
42
42
|
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
43
|
-
subject { file(
|
43
|
+
subject { file("app/models/yummy/bacon_transition.rb") }
|
44
44
|
|
45
45
|
it { is_expected.to_not contain(/\n\n\n/) }
|
46
46
|
end
|
@@ -4,14 +4,14 @@ require "generators/statesman/migration_generator"
|
|
4
4
|
|
5
5
|
describe Statesman::MigrationGenerator, type: :generator do
|
6
6
|
it_behaves_like "a generator" do
|
7
|
-
let(:migration_name) {
|
7
|
+
let(:migration_name) { "db/migrate/add_statesman_to_bacon_transitions.rb" }
|
8
8
|
end
|
9
9
|
|
10
|
-
describe
|
11
|
-
let(:migration_number) {
|
10
|
+
describe "the model contains the correct words" do
|
11
|
+
let(:migration_number) { "5678309" }
|
12
12
|
|
13
13
|
let(:mock_time) do
|
14
|
-
double(
|
14
|
+
double("Time", utc: double("UTCTime", strftime: migration_number))
|
15
15
|
end
|
16
16
|
|
17
17
|
subject do
|
@@ -3,17 +3,17 @@ require "support/generators_shared_examples"
|
|
3
3
|
require "generators/statesman/mongoid_transition_generator"
|
4
4
|
|
5
5
|
describe Statesman::MongoidTransitionGenerator, type: :generator do
|
6
|
-
describe
|
6
|
+
describe "the model contains the correct words" do
|
7
7
|
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
8
|
-
subject { file(
|
8
|
+
subject { file("app/models/yummy/bacon_transition.rb") }
|
9
9
|
|
10
10
|
it { is_expected.not_to contain(%r{:yummy/bacon}) }
|
11
11
|
it { is_expected.to contain(/class_name: 'Yummy::Bacon'/) }
|
12
12
|
end
|
13
13
|
|
14
|
-
describe
|
14
|
+
describe "the model contains the correct words" do
|
15
15
|
before { run_generator %w[Bacon BaconTransition] }
|
16
|
-
subject { file(
|
16
|
+
subject { file("app/models/bacon_transition.rb") }
|
17
17
|
|
18
18
|
it { is_expected.not_to contain(/class_name:/) }
|
19
19
|
it { is_expected.not_to contain(/CreateYummy::Bacon/) }
|
@@ -132,7 +132,7 @@ describe Statesman::Adapters::ActiveRecordQueries, active_record: true do
|
|
132
132
|
# this spec.
|
133
133
|
MyActiveRecordModel.send(:has_many,
|
134
134
|
:custom_name,
|
135
|
-
class_name:
|
135
|
+
class_name: "OtherActiveRecordModelTransition")
|
136
136
|
|
137
137
|
MyActiveRecordModel.class_eval do
|
138
138
|
def self.transition_class
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "timecop"
|
2
3
|
require "statesman/adapters/shared_examples"
|
3
4
|
require "statesman/exceptions"
|
4
5
|
|
@@ -17,11 +18,11 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
17
18
|
context "with unserialized metadata and non json column type" do
|
18
19
|
before do
|
19
20
|
metadata_column = double
|
20
|
-
allow(metadata_column).to receive_messages(sql_type:
|
21
|
+
allow(metadata_column).to receive_messages(sql_type: "")
|
21
22
|
allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
|
22
|
-
{
|
23
|
+
{ "metadata" => metadata_column })
|
23
24
|
if ::ActiveRecord.respond_to?(:gem_version) &&
|
24
|
-
::ActiveRecord.gem_version >= Gem::Version.new(
|
25
|
+
::ActiveRecord.gem_version >= Gem::Version.new("4.2.0.a")
|
25
26
|
expect(MyActiveRecordModelTransition).
|
26
27
|
to receive(:type_for_attribute).with("metadata").
|
27
28
|
and_return(ActiveRecord::Type::Value.new)
|
@@ -42,20 +43,20 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
42
43
|
context "with serialized metadata and json column type" do
|
43
44
|
before do
|
44
45
|
metadata_column = double
|
45
|
-
allow(metadata_column).to receive_messages(sql_type:
|
46
|
+
allow(metadata_column).to receive_messages(sql_type: "json")
|
46
47
|
allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
|
47
|
-
{
|
48
|
+
{ "metadata" => metadata_column })
|
48
49
|
if ::ActiveRecord.respond_to?(:gem_version) &&
|
49
|
-
::ActiveRecord.gem_version >= Gem::Version.new(
|
50
|
+
::ActiveRecord.gem_version >= Gem::Version.new("4.2.0.a")
|
50
51
|
serialized_type = ::ActiveRecord::Type::Serialized.new(
|
51
|
-
|
52
|
+
"", ::ActiveRecord::Coders::JSON
|
52
53
|
)
|
53
54
|
expect(MyActiveRecordModelTransition).
|
54
55
|
to receive(:type_for_attribute).with("metadata").
|
55
56
|
and_return(serialized_type)
|
56
57
|
else
|
57
58
|
expect(MyActiveRecordModelTransition).
|
58
|
-
to receive_messages(serialized_attributes: {
|
59
|
+
to receive_messages(serialized_attributes: { "metadata" => "" })
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
@@ -70,20 +71,20 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
70
71
|
context "with serialized metadata and jsonb column type" do
|
71
72
|
before do
|
72
73
|
metadata_column = double
|
73
|
-
allow(metadata_column).to receive_messages(sql_type:
|
74
|
+
allow(metadata_column).to receive_messages(sql_type: "jsonb")
|
74
75
|
allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
|
75
|
-
{
|
76
|
+
{ "metadata" => metadata_column })
|
76
77
|
if ::ActiveRecord.respond_to?(:gem_version) &&
|
77
|
-
::ActiveRecord.gem_version >= Gem::Version.new(
|
78
|
+
::ActiveRecord.gem_version >= Gem::Version.new("4.2.0.a")
|
78
79
|
serialized_type = ::ActiveRecord::Type::Serialized.new(
|
79
|
-
|
80
|
+
"", ::ActiveRecord::Coders::JSON
|
80
81
|
)
|
81
82
|
expect(MyActiveRecordModelTransition).
|
82
83
|
to receive(:type_for_attribute).with("metadata").
|
83
84
|
and_return(serialized_type)
|
84
85
|
else
|
85
86
|
expect(MyActiveRecordModelTransition).
|
86
|
-
to receive_messages(serialized_attributes: {
|
87
|
+
to receive_messages(serialized_attributes: { "metadata" => "" })
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
@@ -125,7 +126,7 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
125
126
|
context "ActiveRecord::RecordNotUnique unrelated to this transition" do
|
126
127
|
let(:error) do
|
127
128
|
if ::ActiveRecord.respond_to?(:gem_version) &&
|
128
|
-
::ActiveRecord.gem_version >= Gem::Version.new(
|
129
|
+
::ActiveRecord.gem_version >= Gem::Version.new("4.0.0")
|
129
130
|
ActiveRecord::RecordNotUnique.new("unrelated")
|
130
131
|
else
|
131
132
|
ActiveRecord::RecordNotUnique.new("unrelated", nil)
|
@@ -157,6 +158,11 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
157
158
|
from(true).to be_falsey
|
158
159
|
end
|
159
160
|
|
161
|
+
it "touches the previous transition's updated_at timestamp" do
|
162
|
+
expect { Timecop.freeze(Time.now + 5.seconds) { create } }.
|
163
|
+
to(change { previous_transition.reload.updated_at })
|
164
|
+
end
|
165
|
+
|
160
166
|
context "and a query on the parent model's state is made" do
|
161
167
|
context "in a before action" do
|
162
168
|
it "still has the old state" do
|
@@ -228,17 +234,35 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
|
|
228
234
|
described_class.new(MyActiveRecordModelTransition, model, observer)
|
229
235
|
end
|
230
236
|
|
231
|
-
before { alternate_adapter.create(:y, :z, []) }
|
232
|
-
|
233
237
|
it "still returns the cached value" do
|
238
|
+
alternate_adapter.create(:y, :z, [])
|
239
|
+
|
234
240
|
expect_any_instance_of(MyActiveRecordModel).
|
235
241
|
to receive(:my_active_record_model_transitions).never
|
236
242
|
expect(adapter.last.to_state).to eq("y")
|
237
243
|
end
|
238
244
|
|
239
|
-
context "when
|
240
|
-
|
241
|
-
|
245
|
+
context "when explicitly not using the cache" do
|
246
|
+
context "when the transitions are in memory" do
|
247
|
+
before do
|
248
|
+
model.my_active_record_model_transitions.load
|
249
|
+
alternate_adapter.create(:y, :z, [])
|
250
|
+
end
|
251
|
+
|
252
|
+
it "reloads the value" do
|
253
|
+
expect(adapter.last(force_reload: true).to_state).to eq("z")
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when the transitions are not in memory" do
|
258
|
+
before do
|
259
|
+
model.my_active_record_model_transitions.reset
|
260
|
+
alternate_adapter.create(:y, :z, [])
|
261
|
+
end
|
262
|
+
|
263
|
+
it "reloads the value" do
|
264
|
+
expect(adapter.last(force_reload: true).to_state).to eq("z")
|
265
|
+
end
|
242
266
|
end
|
243
267
|
end
|
244
268
|
end
|
@@ -48,5 +48,35 @@ describe Statesman::Adapters::Mongoid, mongo: true do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
context "when a new transition has been created elsewhere" do
|
53
|
+
let(:alternate_adapter) do
|
54
|
+
described_class.new(MyMongoidModelTransition, model, observer)
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when explicitly not using the cache" do
|
58
|
+
context "when the transitions are in memory" do
|
59
|
+
before do
|
60
|
+
model.my_mongoid_model_transitions.entries
|
61
|
+
alternate_adapter.create(:y, :z)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "reloads the value" do
|
65
|
+
expect(adapter.last(force_reload: true).to_state).to eq("z")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when the transitions are not in memory" do
|
70
|
+
before do
|
71
|
+
model.my_mongoid_model_transitions.reset
|
72
|
+
alternate_adapter.create(:y, :z)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "reloads the value" do
|
76
|
+
expect(adapter.last(force_reload: true).to_state).to eq("z")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
51
81
|
end
|
52
82
|
end
|
@@ -11,7 +11,6 @@ require "spec_helper"
|
|
11
11
|
# history: Returns the full transition history
|
12
12
|
# last: Returns the latest transition history item
|
13
13
|
#
|
14
|
-
# rubocop:disable Metrics/LineLength
|
15
14
|
# NOTE This line cannot reasonably be shortened.
|
16
15
|
shared_examples_for "an adapter" do |adapter_class, transition_class, options = {}|
|
17
16
|
# rubocop:enable Metrics/LineLength
|
@@ -450,7 +450,7 @@ describe Statesman::Machine do
|
|
450
450
|
|
451
451
|
context "with one possible state" do
|
452
452
|
before { instance.transition_to!(:y) }
|
453
|
-
it { is_expected.to eq([
|
453
|
+
it { is_expected.to eq(["z"]) }
|
454
454
|
|
455
455
|
context "guarded using metadata" do
|
456
456
|
before do
|
@@ -459,7 +459,7 @@ describe Statesman::Machine do
|
|
459
459
|
end
|
460
460
|
end
|
461
461
|
|
462
|
-
it { is_expected.to eq([
|
462
|
+
it { is_expected.to eq(["z"]) }
|
463
463
|
end
|
464
464
|
|
465
465
|
context "excluded by guard using metadata" do
|
@@ -666,12 +666,12 @@ describe Statesman::Machine do
|
|
666
666
|
context "when a non statesman exception is raised" do
|
667
667
|
before do
|
668
668
|
allow(instance).to receive(:transition_to!).
|
669
|
-
and_raise(RuntimeError,
|
669
|
+
and_raise(RuntimeError, "user defined exception")
|
670
670
|
end
|
671
671
|
|
672
672
|
it "should not rescue the exception" do
|
673
673
|
expect { instance.transition_to(:some_state, metadata) }.
|
674
|
-
to raise_error(RuntimeError,
|
674
|
+
to raise_error(RuntimeError, "user defined exception")
|
675
675
|
end
|
676
676
|
end
|
677
677
|
end
|
@@ -59,10 +59,10 @@ class CreateMyActiveRecordModelTransitionMigration < MIGRATION_CLASS
|
|
59
59
|
t.integer :sort_key
|
60
60
|
|
61
61
|
# MySQL doesn't allow default values on text fields
|
62
|
-
if ActiveRecord::Base.connection.adapter_name ==
|
62
|
+
if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
63
63
|
t.text :metadata
|
64
64
|
else
|
65
|
-
t.text :metadata, default:
|
65
|
+
t.text :metadata, default: "{}"
|
66
66
|
end
|
67
67
|
|
68
68
|
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
@@ -137,10 +137,10 @@ class CreateOtherActiveRecordModelTransitionMigration < MIGRATION_CLASS
|
|
137
137
|
t.integer :sort_key
|
138
138
|
|
139
139
|
# MySQL doesn't allow default values on text fields
|
140
|
-
if ActiveRecord::Base.connection.adapter_name ==
|
140
|
+
if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
141
141
|
t.text :metadata
|
142
142
|
else
|
143
|
-
t.text :metadata, default:
|
143
|
+
t.text :metadata, default: "{}"
|
144
144
|
end
|
145
145
|
|
146
146
|
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
@@ -234,10 +234,10 @@ class CreateNamespacedARModelTransitionMigration < MIGRATION_CLASS
|
|
234
234
|
t.integer :sort_key
|
235
235
|
|
236
236
|
# MySQL doesn't allow default values on text fields
|
237
|
-
if ActiveRecord::Base.connection.adapter_name ==
|
237
|
+
if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
238
238
|
t.text :metadata
|
239
239
|
else
|
240
|
-
t.text :metadata, default:
|
240
|
+
t.text :metadata, default: "{}"
|
241
241
|
end
|
242
242
|
|
243
243
|
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
@@ -250,7 +250,7 @@ class CreateNamespacedARModelTransitionMigration < MIGRATION_CLASS
|
|
250
250
|
end
|
251
251
|
|
252
252
|
add_index :my_namespace_my_active_record_model_transitions, :sort_key,
|
253
|
-
unique: true, name:
|
253
|
+
unique: true, name: "my_namespaced_key"
|
254
254
|
|
255
255
|
if Statesman::Adapters::ActiveRecord.database_supports_partial_indexes?
|
256
256
|
add_index :my_namespace_my_active_record_model_transitions,
|
@@ -2,22 +2,22 @@ require "rails/version"
|
|
2
2
|
require "rspec/rails"
|
3
3
|
require "ammeter/init"
|
4
4
|
|
5
|
-
TMP_GENERATOR_PATH = File.expand_path(
|
5
|
+
TMP_GENERATOR_PATH = File.expand_path("../generator-tmp", __FILE__)
|
6
6
|
|
7
|
-
shared_examples
|
7
|
+
shared_examples "a generator" do
|
8
8
|
destination TMP_GENERATOR_PATH
|
9
9
|
before { prepare_destination }
|
10
10
|
let(:gen) { generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
11
11
|
|
12
|
-
it
|
12
|
+
it "invokes create_model_file method" do
|
13
13
|
expect(gen).to receive(:create_model_file)
|
14
14
|
gen.invoke_all
|
15
15
|
end
|
16
16
|
|
17
|
-
describe
|
17
|
+
describe "it runs the generator and check things out" do
|
18
18
|
before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
|
19
19
|
|
20
|
-
describe
|
20
|
+
describe "it generates a correctly named file" do
|
21
21
|
subject { file(migration_name) }
|
22
22
|
it { is_expected.to be_a_migration }
|
23
23
|
end
|
data/spec/support/mongoid.rb
CHANGED
data/statesman.gemspec
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
lib = File.expand_path(
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
3
|
+
require "statesman/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "statesman"
|
7
7
|
spec.version = Statesman::VERSION
|
8
8
|
spec.authors = ["GoCardless"]
|
9
9
|
spec.email = ["developers@gocardless.com"]
|
10
|
-
spec.description =
|
10
|
+
spec.description = "A statesman-like state machine library"
|
11
11
|
spec.summary = spec.description
|
12
12
|
spec.homepage = "https://github.com/gocardless/statesman"
|
13
13
|
spec.license = "MIT"
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.required_ruby_version =
|
20
|
+
spec.required_ruby_version = ">= 2.2"
|
21
21
|
|
22
22
|
spec.add_development_dependency "ammeter", "~> 1.1"
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency "rspec-its", "~> 1.1"
|
30
30
|
spec.add_development_dependency "rspec-rails", "~> 3.1"
|
31
31
|
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
32
|
-
spec.add_development_dependency "rubocop", "~> 0.
|
32
|
+
spec.add_development_dependency "rubocop", "~> 0.52.0"
|
33
33
|
spec.add_development_dependency "sqlite3", "~> 1.3"
|
34
|
+
spec.add_development_dependency "timecop", "~> 0.9.1"
|
34
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statesman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GoCardless
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ammeter
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.
|
159
|
+
version: 0.52.0
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.
|
166
|
+
version: 0.52.0
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: sqlite3
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '1.3'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: timecop
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 0.9.1
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.9.1
|
181
195
|
description: A statesman-like state machine library
|
182
196
|
email:
|
183
197
|
- developers@gocardless.com
|
@@ -265,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
265
279
|
version: '0'
|
266
280
|
requirements: []
|
267
281
|
rubyforge_project:
|
268
|
-
rubygems_version: 2.
|
282
|
+
rubygems_version: 2.7.3
|
269
283
|
signing_key:
|
270
284
|
specification_version: 4
|
271
285
|
summary: A statesman-like state machine library
|