statesman_mongoid 0.1.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12b141e7bcaf520970181af415dcdfe5256b148573fd01e3a4639d6585811f97
4
- data.tar.gz: 536f5ef0cd30da45fa1abb42ff78fcb80f1be5362fa57257c515580d32676c9a
3
+ metadata.gz: 150c388e7dc52a4d1cd5f7dc8aec635bb70d254ecbab0928e151463ac9ca43d1
4
+ data.tar.gz: 4db6ce07d5bca4cb8bb8f60cd07e0165072d23c977b1b165e7358e150eb580e5
5
5
  SHA512:
6
- metadata.gz: 836739d3e0bc37bebcb07a509bf4e5609715545b3aa564954999d6da5823a0f2442e0f60ef1756e1ce47163a6d1481e284c1591608b6c8f9376f30293fda4805
7
- data.tar.gz: 90d396cfbec77f2e354970e214a7f845e5dac8bbeac546ac2262a41cead4abe11da8d9485e3aa9dcdfc576d538c60b66fbc5059cdddcc8cc9da88a2bac18caed
6
+ metadata.gz: 172df6b8fe89efababe619552ed5391765b7e18f38998f7cebf7fa00861a05a770436659b23c0ce4f59435643ac32ae5d1f8eb48bcb3a6d19fb01f68a7e65424
7
+ data.tar.gz: 15460af6a52050507a9d8443ab696ce89a74a9bca276351ee78decaaed5279f476eac3d0ba3f8e6104cf4b5884100b5a43dccf7d4af59d4282048527e523cf55
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- statesman_mongoid (0.1.1)
4
+ statesman_mongoid (0.3.0)
5
5
  statesman (~> 10.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Statesman Mongoid
2
2
 
3
- Restored mongoid adapters for the Statesman gem.
3
+ Restored mongoid adapters for [Statesman](https://github.com/gocardless/statesman).
4
+
5
+ TODO: Restore generators and test units
6
+
7
+ NOTE: This is an early salvage of the dropped mongoid support with a refitted queries adapter, a couple of fixes and some manual testing, use at your own risk and feel free to contribute.
4
8
 
5
9
  ## Installation
6
10
 
@@ -20,7 +24,14 @@ Or install it yourself as:
20
24
 
21
25
  ## Usage
22
26
 
23
- TODO: Write usage instructions here
27
+ TODO: Expand usage instructions
28
+
29
+ Follow instructions in the Statesman's doc and replace invoked classes with these ones when approriate:
30
+ ``` ruby
31
+ Statesman::Adapters::Mongoid
32
+ Statesman::Adapters::MongoidTransition
33
+ Statesman::Adapters::MongoidQueries
34
+ ```
24
35
 
25
36
  ## Development
26
37
 
@@ -30,7 +41,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
30
41
 
31
42
  ## Contributing
32
43
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/statesman_mongoid.
44
+ Bug reports and pull requests are welcome on GitHub at https://github.com/oz-tal/statesman_mongoid.
34
45
 
35
46
  ## License
36
47
 
@@ -33,18 +33,25 @@ module Statesman
33
33
  @last_transition = nil
34
34
  end
35
35
 
36
- def history(*)
36
+ def history(force_reload: false)
37
+ reset if force_reload
37
38
  transitions_for_parent.asc(:sort_key)
38
39
  end
39
40
 
40
41
  def last(force_reload: false)
41
42
  if force_reload
42
- @last_transition = history.last
43
+ @last_transition = history(force_reload: true).last
43
44
  else
44
45
  @last_transition ||= history.last
45
46
  end
46
47
  end
47
48
 
49
+ def reset
50
+ # Aggressive, but the query cache can't be targeted at a more granular level
51
+ ::Mongoid::QueryCache.clear_cache
52
+ end
53
+
54
+
48
55
  private
49
56
 
50
57
  def transition_class_hash_fields
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on the somewhat compatible ActiveRecordQueries at commit 455a21dd74bb7d3b7555de0dd66e2ece9461c22d
4
+
5
+ module Statesman
6
+ module Adapters
7
+ module MongoidQueries
8
+ def self.check_missing_methods!(base)
9
+ missing_methods = %i[transition_class initial_state].
10
+ reject { |m| base.respond_to?(m) }
11
+ return if missing_methods.none?
12
+
13
+ raise NotImplementedError,
14
+ "#{missing_methods.join(', ')} method(s) should be defined on " \
15
+ "the model. Alternatively, use the new form of `include " \
16
+ "Statesman::Adapters::MongoidQueries[" \
17
+ "transition_class: MyTransition, " \
18
+ "initial_state: :some_state]`"
19
+ end
20
+
21
+ def self.included(base)
22
+ check_missing_methods!(base)
23
+
24
+ base.include(
25
+ ClassMethods.new(
26
+ transition_class: base.transition_class,
27
+ initial_state: base.initial_state,
28
+ most_recent_transition_alias: base.try(:most_recent_transition_alias),
29
+ transition_name: base.try(:transition_name),
30
+ ),
31
+ )
32
+ end
33
+
34
+ def self.[](**args)
35
+ ClassMethods.new(**args)
36
+ end
37
+
38
+ class ClassMethods < Module
39
+ def initialize(**args)
40
+ @args = args
41
+ end
42
+
43
+ def included(base)
44
+ ensure_inheritance(base)
45
+
46
+ query_builder = QueryBuilder.new(base, **@args)
47
+
48
+ define_in_state(base, query_builder)
49
+ define_not_in_state(base, query_builder)
50
+
51
+ define_method(:reload) do |*a|
52
+ instance = super(*a)
53
+ if instance.respond_to?(:state_machine, true)
54
+ instance.state_machine.reset
55
+ end
56
+ instance
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def ensure_inheritance(base)
63
+ klass = self
64
+ existing_inherited = base.method(:inherited)
65
+ base.define_singleton_method(:inherited) do |subclass|
66
+ existing_inherited.call(subclass)
67
+ subclass.send(:include, klass)
68
+ end
69
+ end
70
+
71
+ def define_in_state(base, query_builder)
72
+ base.define_singleton_method(:in_state) do |*states|
73
+ query_builder.states_where(states.flatten)
74
+ end
75
+ end
76
+
77
+ def define_not_in_state(base, query_builder)
78
+ base.define_singleton_method(:not_in_state) do |*states|
79
+ query_builder.states_where_not(states.flatten)
80
+ end
81
+ end
82
+ end
83
+
84
+ class QueryBuilder
85
+ def initialize(model, transition_class:, initial_state:,
86
+ most_recent_transition_alias: nil,
87
+ transition_name: nil)
88
+ @model = model
89
+ @transition_class = transition_class
90
+ @initial_state = initial_state
91
+ @most_recent_transition_alias = most_recent_transition_alias
92
+ @transition_name = transition_name
93
+ end
94
+
95
+ def states_where(states)
96
+ ids = aggregate_ids_for_most_recent(states, inclusive_match: true)
97
+ model.where(_id: { '$in' => ids })
98
+ end
99
+
100
+ def states_where_not(states)
101
+ ids = aggregate_ids_for_most_recent(states, inclusive_match: false)
102
+ model.where(_id: { '$in' => ids })
103
+ end
104
+
105
+ def aggregate_ids_for_most_recent(states, inclusive_match: true)
106
+ aggregation = [
107
+ # Sort most recent
108
+ { '$sort': { sort_key: -1 } },
109
+ # Group by foreign key & get most recent states
110
+ {
111
+ '$group': {
112
+ _id: "$#{model_foreign_key}",
113
+ to_state: { '$first': '$to_state' },
114
+ model_foreign_key => { '$first': "$#{model_foreign_key}" },
115
+ },
116
+ },
117
+ # Include/exclude states by provided states
118
+ { '$match': { to_state: { (inclusive_match ? '$in' : '$nin') => states } } },
119
+ # Trim response to only the foreign key
120
+ { '$project': { _id: 0, to_state: 0 } },
121
+ ]
122
+
123
+ # Hit the database and return a flat array of ids
124
+ transition_class.collection.aggregate(aggregation).pluck(model_foreign_key)
125
+ end
126
+
127
+
128
+ private
129
+
130
+ attr_reader :model, :transition_class, :initial_state
131
+
132
+ def transition_name
133
+ @transition_name || transition_class.collection.name.to_sym
134
+ end
135
+
136
+ def transition_reflection
137
+ model.reflect_on_all_associations(:has_many).each do |value|
138
+ return value if value.klass == transition_class
139
+ end
140
+
141
+ raise MissingTransitionAssociation,
142
+ "Could not find has_many association between #{self.class} " \
143
+ "and #{transition_class}."
144
+ end
145
+
146
+ def model_primary_key
147
+ transition_reflection.primary_key
148
+ end
149
+
150
+ def model_foreign_key
151
+ transition_reflection.foreign_key
152
+ end
153
+
154
+ def model_table
155
+ transition_reflection.name
156
+ end
157
+
158
+ def most_recent_transition_alias
159
+ @most_recent_transition_alias ||
160
+ "most_recent_#{transition_name.to_s.singularize}"
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StatesmanMongoid
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "statesman_mongoid/version"
4
- require_relative "statesman/adapters/mongoid"
5
- require_relative "statesman/adapters/mongoid_transition"
3
+ # require_relative "statesman_mongoid/version"
4
+ Dir[__dir__ + '/statesman/adapters/*'].each &method(:require)
6
5
 
7
6
  module StatesmanMongoid
8
7
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman_mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - oz-tal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-03 00:00:00.000000000 Z
11
+ date: 2023-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: statesman
@@ -37,6 +37,7 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - lib/statesman/adapters/mongoid.rb
40
+ - lib/statesman/adapters/mongoid_queries.rb
40
41
  - lib/statesman/adapters/mongoid_transition.rb
41
42
  - lib/statesman_mongoid.rb
42
43
  - lib/statesman_mongoid/version.rb