statesman 3.5.0 → 7.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +49 -250
  3. data/.rubocop.yml +1 -1
  4. data/.rubocop_todo.yml +26 -6
  5. data/CHANGELOG.md +106 -0
  6. data/Gemfile +10 -4
  7. data/Guardfile +2 -0
  8. data/README.md +78 -48
  9. data/Rakefile +2 -4
  10. data/lib/generators/statesman/active_record_transition_generator.rb +2 -0
  11. data/lib/generators/statesman/generator_helpers.rb +2 -0
  12. data/lib/generators/statesman/migration_generator.rb +2 -0
  13. data/lib/statesman.rb +14 -4
  14. data/lib/statesman/adapters/active_record.rb +259 -37
  15. data/lib/statesman/adapters/active_record_queries.rb +100 -36
  16. data/lib/statesman/adapters/active_record_transition.rb +2 -0
  17. data/lib/statesman/adapters/memory.rb +2 -0
  18. data/lib/statesman/adapters/memory_transition.rb +2 -0
  19. data/lib/statesman/callback.rb +2 -0
  20. data/lib/statesman/config.rb +28 -0
  21. data/lib/statesman/exceptions.rb +34 -2
  22. data/lib/statesman/guard.rb +3 -4
  23. data/lib/statesman/machine.rb +29 -7
  24. data/lib/statesman/railtie.rb +2 -0
  25. data/lib/statesman/utils.rb +2 -0
  26. data/lib/statesman/version.rb +3 -1
  27. data/lib/tasks/statesman.rake +3 -1
  28. data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_with_partial_index.rb +2 -0
  29. data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions_without_partial_index.rb +2 -0
  30. data/spec/fixtures/add_most_recent_to_bacon_transitions.rb +2 -0
  31. data/spec/generators/statesman/active_record_transition_generator_spec.rb +2 -0
  32. data/spec/generators/statesman/migration_generator_spec.rb +2 -0
  33. data/spec/spec_helper.rb +3 -30
  34. data/spec/statesman/adapters/active_record_queries_spec.rb +167 -91
  35. data/spec/statesman/adapters/active_record_spec.rb +15 -1
  36. data/spec/statesman/adapters/active_record_transition_spec.rb +2 -0
  37. data/spec/statesman/adapters/memory_spec.rb +2 -0
  38. data/spec/statesman/adapters/memory_transition_spec.rb +2 -0
  39. data/spec/statesman/adapters/shared_examples.rb +2 -0
  40. data/spec/statesman/callback_spec.rb +2 -0
  41. data/spec/statesman/config_spec.rb +2 -0
  42. data/spec/statesman/exceptions_spec.rb +88 -0
  43. data/spec/statesman/guard_spec.rb +2 -0
  44. data/spec/statesman/machine_spec.rb +79 -4
  45. data/spec/statesman/utils_spec.rb +2 -0
  46. data/spec/support/active_record.rb +9 -12
  47. data/spec/support/generators_shared_examples.rb +2 -0
  48. data/statesman.gemspec +19 -7
  49. metadata +40 -32
  50. data/lib/generators/statesman/mongoid_transition_generator.rb +0 -25
  51. data/lib/generators/statesman/templates/mongoid_transition_model.rb.erb +0 -14
  52. data/lib/statesman/adapters/mongoid.rb +0 -66
  53. data/lib/statesman/adapters/mongoid_transition.rb +0 -10
  54. data/spec/generators/statesman/mongoid_transition_generator_spec.rb +0 -23
  55. data/spec/statesman/adapters/mongoid_spec.rb +0 -86
  56. data/spec/support/mongoid.rb +0 -28
@@ -1,25 +0,0 @@
1
- require "rails/generators"
2
- require "generators/statesman/generator_helpers"
3
-
4
- module Statesman
5
- class MongoidTransitionGenerator < Rails::Generators::Base
6
- include Statesman::GeneratorHelpers
7
-
8
- desc "Create a Mongoid-based transition model with the required attributes"
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", __dir__)
14
-
15
- def create_model_file
16
- template("mongoid_transition_model.rb.erb", model_file_name)
17
- end
18
-
19
- private
20
-
21
- def collection_name
22
- klass.underscore.pluralize
23
- end
24
- end
25
- end
@@ -1,14 +0,0 @@
1
- class <%= klass %>
2
- include Mongoid::Document
3
- include Mongoid::Timestamps
4
-
5
- field :to_state, type: String
6
- field :sort_key, type: Integer
7
- field :statesman_metadata, type: Hash
8
-
9
- include Statesman::Adapters::MongoidTransition
10
-
11
- index({ sort_key: 1 })
12
-
13
- belongs_to :<%= parent %><%= class_name_option %>, index: true
14
- end
@@ -1,66 +0,0 @@
1
- require_relative "../exceptions"
2
-
3
- module Statesman
4
- module Adapters
5
- class Mongoid
6
- attr_reader :transition_class
7
- attr_reader :parent_model
8
-
9
- def initialize(transition_class, parent_model, observer, _opts = {})
10
- @transition_class = transition_class
11
- @parent_model = parent_model
12
- @observer = observer
13
- unless transition_class_hash_fields.include?("statesman_metadata")
14
- raise UnserializedMetadataError, metadata_field_error_message
15
- end
16
- end
17
-
18
- def create(from, to, metadata = {})
19
- from = from.to_s
20
- to = to.to_s
21
- transition = transitions_for_parent.build(to_state: to,
22
- sort_key: next_sort_key,
23
- statesman_metadata: metadata)
24
-
25
- @observer.execute(:before, from, to, transition)
26
- transition.save!
27
- @last_transition = transition
28
- @observer.execute(:after, from, to, transition)
29
- @observer.execute(:after_commit, from, to, transition)
30
- transition
31
- ensure
32
- @last_transition = nil
33
- end
34
-
35
- def history(*)
36
- transitions_for_parent.asc(:sort_key)
37
- end
38
-
39
- def last(force_reload: false)
40
- if force_reload
41
- @last_transition = history.last
42
- else
43
- @last_transition ||= history.last
44
- end
45
- end
46
-
47
- private
48
-
49
- def transition_class_hash_fields
50
- transition_class.fields.select { |_, v| v.type == Hash }.keys
51
- end
52
-
53
- def metadata_field_error_message
54
- "#{transition_class.name}#statesman_metadata is not of type 'Hash'"
55
- end
56
-
57
- def transitions_for_parent
58
- @parent_model.send(@transition_class.collection_name)
59
- end
60
-
61
- def next_sort_key
62
- (last && last.sort_key + 10) || 10
63
- end
64
- end
65
- end
66
- end
@@ -1,10 +0,0 @@
1
- module Statesman
2
- module Adapters
3
- module MongoidTransition
4
- def self.included(base)
5
- base.send(:alias_method, :metadata, :statesman_metadata)
6
- base.send(:alias_method, :metadata=, :statesman_metadata=)
7
- end
8
- end
9
- end
10
- end
@@ -1,23 +0,0 @@
1
- require "spec_helper"
2
- require "support/generators_shared_examples"
3
- require "generators/statesman/mongoid_transition_generator"
4
-
5
- describe Statesman::MongoidTransitionGenerator, type: :generator do
6
- describe "the model contains the correct words" do
7
- subject { file("app/models/yummy/bacon_transition.rb") }
8
-
9
- before { run_generator %w[Yummy::Bacon Yummy::BaconTransition] }
10
-
11
- it { is_expected.to_not contain(%r{:yummy/bacon}) }
12
- it { is_expected.to contain(/class_name: 'Yummy::Bacon'/) }
13
- end
14
-
15
- describe "the model contains the correct words" do
16
- subject { file("app/models/bacon_transition.rb") }
17
-
18
- before { run_generator %w[Bacon BaconTransition] }
19
-
20
- it { is_expected.to_not contain(/class_name:/) }
21
- it { is_expected.to_not contain(/CreateYummy::Bacon/) }
22
- end
23
- end
@@ -1,86 +0,0 @@
1
- require "spec_helper"
2
- require "statesman/adapters/shared_examples"
3
- require "statesman/exceptions"
4
- require "support/mongoid"
5
- require "mongoid"
6
-
7
- describe Statesman::Adapters::Mongoid, mongo: true do
8
- after { Mongoid.purge! }
9
-
10
- let(:observer) { double(Statesman::Machine, execute: nil) }
11
- let(:model) { MyMongoidModel.create(current_state: :pending) }
12
-
13
- it_behaves_like "an adapter", described_class, MyMongoidModelTransition
14
-
15
- describe "#initialize" do
16
- context "with unserialized metadata" do
17
- before do
18
- allow_any_instance_of(described_class).
19
- to receive_messages(transition_class_hash_fields: [])
20
- end
21
-
22
- it "raises an exception if metadata is not serialized" do
23
- expect do
24
- described_class.new(MyMongoidModelTransition, MyMongoidModel,
25
- observer)
26
- end.to raise_exception(Statesman::UnserializedMetadataError)
27
- end
28
- end
29
- end
30
-
31
- describe "#last" do
32
- let(:adapter) do
33
- described_class.new(MyMongoidModelTransition, model, observer)
34
- end
35
-
36
- context "with a previously looked up transition" do
37
- before { adapter.create(:x, :y) }
38
-
39
- before { adapter.last }
40
-
41
- it "caches the transition" do
42
- expect_any_instance_of(MyMongoidModel).
43
- to_not receive(:my_mongoid_model_transitions)
44
- adapter.last
45
- end
46
-
47
- context "and a new transition" do
48
- before { adapter.create(:y, :z) }
49
-
50
- it "retrieves the new transition from the database" do
51
- expect(adapter.last.to_state).to eq("z")
52
- end
53
- end
54
- end
55
-
56
- context "when a new transition has been created elsewhere" do
57
- let(:alternate_adapter) do
58
- described_class.new(MyMongoidModelTransition, model, observer)
59
- end
60
-
61
- context "when explicitly not using the cache" do
62
- context "when the transitions are in memory" do
63
- before do
64
- model.my_mongoid_model_transitions.entries
65
- alternate_adapter.create(:y, :z)
66
- end
67
-
68
- it "reloads the value" do
69
- expect(adapter.last(force_reload: true).to_state).to eq("z")
70
- end
71
- end
72
-
73
- context "when the transitions are not in memory" do
74
- before do
75
- model.my_mongoid_model_transitions.reset
76
- alternate_adapter.create(:y, :z)
77
- end
78
-
79
- it "reloads the value" do
80
- expect(adapter.last(force_reload: true).to_state).to eq("z")
81
- end
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,28 +0,0 @@
1
- require "mongoid"
2
-
3
- Mongoid.configure do |config|
4
- config.connect_to("statesman_test")
5
- end
6
-
7
- class MyMongoidModel
8
- include Mongoid::Document
9
-
10
- field :current_state, type: String
11
-
12
- has_many :my_mongoid_model_transitions
13
- end
14
-
15
- class MyMongoidModelTransition
16
- include Mongoid::Document
17
- include Mongoid::Timestamps
18
-
19
- field :to_state, type: String
20
- field :sort_key, type: Integer
21
- field :statesman_metadata, type: Hash
22
-
23
- index(sort_key: 1)
24
-
25
- belongs_to :my_mongoid_model, index: true
26
-
27
- include Statesman::Adapters::MongoidTransition
28
- end