statesman_mongoid 0.1.1 → 0.3.0

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
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