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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +14 -3
- data/lib/statesman/adapters/mongoid.rb +9 -2
- data/lib/statesman/adapters/mongoid_queries.rb +165 -0
- data/lib/statesman_mongoid/version.rb +1 -1
- data/lib/statesman_mongoid.rb +2 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 150c388e7dc52a4d1cd5f7dc8aec635bb70d254ecbab0928e151463ac9ca43d1
|
4
|
+
data.tar.gz: 4db6ce07d5bca4cb8bb8f60cd07e0165072d23c977b1b165e7358e150eb580e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 172df6b8fe89efababe619552ed5391765b7e18f38998f7cebf7fa00861a05a770436659b23c0ce4f59435643ac32ae5d1f8eb48bcb3a6d19fb01f68a7e65424
|
7
|
+
data.tar.gz: 15460af6a52050507a9d8443ab696ce89a74a9bca276351ee78decaaed5279f476eac3d0ba3f8e6104cf4b5884100b5a43dccf7d4af59d4282048527e523cf55
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# Statesman Mongoid
|
2
2
|
|
3
|
-
Restored mongoid adapters for
|
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:
|
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/
|
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
|
data/lib/statesman_mongoid.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "statesman_mongoid/version"
|
4
|
-
|
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.
|
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-
|
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
|