shiftable 0.4.1 → 0.6.1
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 +4 -4
- data/CHANGELOG.md +37 -5
- data/CODE_OF_CONDUCT.md +0 -0
- data/CONTRIBUTING.md +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +170 -10
- data/lib/shiftable/collection.rb +14 -7
- data/lib/shiftable/mod_signature.rb +70 -23
- data/lib/shiftable/shifting.rb +31 -2
- data/lib/shiftable/shifting_polymorphic_relation.rb +47 -0
- data/lib/shiftable/shifting_record.rb +24 -2
- data/lib/shiftable/shifting_relation.rb +4 -2
- data/lib/shiftable/single.rb +7 -6
- data/lib/shiftable/version.rb +1 -1
- data/lib/shiftable.rb +1 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae22d0c5730e6bde9ad658ee1b8deddb2ca83e8d09cccc560945faf51c796b6a
|
4
|
+
data.tar.gz: b8ef6d21e4273b247a4287a31059eb02bed5bafaf3941562ddfc78f798fd139a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19ab8c6178402cb18788424023d380672a8d4c7597b432703b79f73900950cf353d8fe85702a0fbd09387f3dd61c4276ba69fe0e9e99499bc77b52d8c9bc3b0b
|
7
|
+
data.tar.gz: 66d90707da9952a116619e7421127f2c6bad0f4b74558e24ea0da97d7e5670a17960dfabf92ece49ab9cf10bcc60deb9f556f25fc835df656ba9ad1f0f17aa82
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,45 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
|
3
|
+
### Added
|
4
|
+
|
4
5
|
|
5
6
|
### Changed
|
6
7
|
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
|
11
|
+
|
12
|
+
### Removed
|
13
|
+
|
14
|
+
|
15
|
+
## [0.6.1] - 2021-11-14
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Support for using save with bang (`save!`) to raise error when ActiveRecord save fails.
|
19
|
+
|
20
|
+
## [0.6.0] - 2021-11-12
|
21
|
+
### Added
|
22
|
+
|
23
|
+
- Support for wrappers/hooks around each record shift, and around the entire set (see examples in specs or README)
|
24
|
+
|
25
|
+
## [0.5.1] - 2021-11-12
|
26
|
+
### Fixed
|
27
|
+
|
28
|
+
- Documentation typos in README
|
29
|
+
|
30
|
+
## [0.5.0] - 2021-11-12
|
31
|
+
### Added
|
32
|
+
|
33
|
+
- Support for Polymorphic associations (see examples in specs or README)
|
34
|
+
|
35
|
+
## [0.4.1] - 2021-11-10
|
36
|
+
### Fixed
|
37
|
+
|
38
|
+
- Documentation typos in README
|
39
|
+
|
40
|
+
## [0.4.0] - 2021-10-27
|
41
|
+
### Changed
|
42
|
+
|
7
43
|
- option :preflight_checks renamed to :precheck
|
8
44
|
|
9
45
|
### Added
|
@@ -11,7 +47,6 @@
|
|
11
47
|
- Even more 100% spec coverage
|
12
48
|
|
13
49
|
## [0.3.0] - 2021-10-26
|
14
|
-
|
15
50
|
### Changed
|
16
51
|
|
17
52
|
- Internal rewrite to improve maintainability
|
@@ -22,7 +57,6 @@
|
|
22
57
|
- Even more 100% spec coverage
|
23
58
|
|
24
59
|
## [0.2.0] - 2021-10-24
|
25
|
-
|
26
60
|
### Changed
|
27
61
|
|
28
62
|
- option `before_save` is now `before_shift` as originally documented
|
@@ -34,7 +68,6 @@
|
|
34
68
|
- Documentation
|
35
69
|
|
36
70
|
## [0.1.1] - 2021-10-23
|
37
|
-
|
38
71
|
### Fixed
|
39
72
|
|
40
73
|
- Github Actions build
|
@@ -44,7 +77,6 @@
|
|
44
77
|
- Linting
|
45
78
|
|
46
79
|
## [0.1.0] - 2021-10-23
|
47
|
-
|
48
80
|
### Added
|
49
81
|
|
50
82
|
- Initial release
|
data/CODE_OF_CONDUCT.md
CHANGED
File without changes
|
data/CONTRIBUTING.md
CHANGED
File without changes
|
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -80,7 +80,7 @@ But how can you accomplish this? If you used the `shiftable` gem, won't take but
|
|
80
80
|
class Spaceship < ActiveRecord::Base
|
81
81
|
belongs_to :captain
|
82
82
|
extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
|
83
|
-
before_shift: ->(
|
83
|
+
before_shift: ->(shifting_rel) { shifting_rel.result.ownership_changes += 1 }
|
84
84
|
end
|
85
85
|
```
|
86
86
|
|
@@ -104,18 +104,17 @@ end
|
|
104
104
|
|
105
105
|
This works as you would expect with STI (single table inheritance) classes, i.e. when defined on a subclass, only the records of that class get shifted.
|
106
106
|
|
107
|
-
### Multiple
|
107
|
+
### Multiple associations on a single class
|
108
108
|
|
109
109
|
What if the captain and the spaceship have a boss... the space
|
110
110
|
federation! And in a run-in with their arch-Nemesis the Plinth-inth,
|
111
111
|
all federation spaceships are commandeered! You are ruined!
|
112
112
|
|
113
113
|
```ruby
|
114
|
-
|
115
114
|
class Spaceship < ActiveRecord::Base
|
116
115
|
belongs_to :space_federation
|
117
|
-
extend Shiftable::Collection.new belongs_to: :space_federation,
|
118
|
-
before_shift: lambda { |shifting_rel
|
116
|
+
extend Shiftable::Collection.new belongs_to: :space_federation, has_many: :spaceships,
|
117
|
+
before_shift: lambda { |shifting_rel|
|
119
118
|
shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
|
120
119
|
}
|
121
120
|
end
|
@@ -129,6 +128,112 @@ class SpaceFederation < ActiveRecord::Base
|
|
129
128
|
end
|
130
129
|
```
|
131
130
|
|
131
|
+
### Polymorphism and has_many through
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
class SpaceTreaty < ActiveRecord::Base
|
135
|
+
has_many :space_treaty_signature
|
136
|
+
end
|
137
|
+
|
138
|
+
class SpaceTreatySignature < ActiveRecord::Base
|
139
|
+
belongs_to :space_treaty
|
140
|
+
belongs_to :signatory, polymorphic: true
|
141
|
+
# When two space federations assimilate (i.e. merge) to form a single larger federation,
|
142
|
+
# they become party to (i.e. signatories of) all the treaties that had been signed by either.
|
143
|
+
# In practical terms, this means:
|
144
|
+
#
|
145
|
+
# surviving_federation = SpaceFederation.find(1)
|
146
|
+
# assimilated_federation = SpaceFederation.find(2)
|
147
|
+
# SpaceTreatySignature.where(
|
148
|
+
# signatory_id: assimilated_federation_id,
|
149
|
+
# signatory_type: "SpaceFederation"
|
150
|
+
# ).update_all(
|
151
|
+
# signatory_id: surviving_federation.id
|
152
|
+
# )
|
153
|
+
extend Shiftable::Collection.new(
|
154
|
+
belongs_to: :signatory, has_many: :space_treaty_signature,
|
155
|
+
polymorphic: { type: "SpaceFederation", as: :signatory },
|
156
|
+
method_prefix: "space_federation_",
|
157
|
+
before_shift: lambda { |shifting_rel|
|
158
|
+
# Each item in shifting_rel is an instance of the class where Shiftable::Collection is defined,
|
159
|
+
# in this case: SpaceTreatySignature
|
160
|
+
# And each of them has a signatory which is of type "SpaceFederation",
|
161
|
+
# because a polymorphic collection only targets one type.
|
162
|
+
# shifting_rel.each { |signature| signature.signatory == "SpaceFederation" }
|
163
|
+
}
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
167
|
+
class SpaceFederation < ActiveRecord::Base
|
168
|
+
has_many :space_treaty_signature, as: :signatory
|
169
|
+
has_many :space_treaties, through: :space_treaty_signatures, as: :signatory
|
170
|
+
has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
|
171
|
+
has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
|
172
|
+
def assimilate_from(other_federation)
|
173
|
+
SpaceTreatySignature.space_federation_shift_pcx(shift_to: self, shift_from: other_federation)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Including Planet and SpaceStation, for completeness of the example as the other "types" of polymorphic signatories
|
178
|
+
class Planet < ActiveRecord::Base
|
179
|
+
has_many :space_treaty_signature, as: :signatory
|
180
|
+
has_many :space_treaties, through: :space_treaty_signatures
|
181
|
+
has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
|
182
|
+
has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
|
183
|
+
end
|
184
|
+
|
185
|
+
class SpaceStation < ActiveRecord::Base
|
186
|
+
has_many :space_treaty_signature, as: :signatory
|
187
|
+
has_many :space_treaties, through: :space_treaty_signatures
|
188
|
+
has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
|
189
|
+
has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
### Wrapping a shift
|
194
|
+
|
195
|
+
For example, in a transaction. Let's update the nemesis foundation example from above with a transaction shift_each_wrapper,
|
196
|
+
which we'll pull from the [`activerecord-transactionable`](https://github.com/pboling/activerecord-transactionable) gem, which provides best practice framing around transactions.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
class Spaceship < ActiveRecord::Base
|
200
|
+
belongs_to :space_federation
|
201
|
+
extend Shiftable::Collection.new(
|
202
|
+
belongs_to: :space_federation,
|
203
|
+
has_many: :spaceships,
|
204
|
+
before_shift: lambda { |shifting_rel|
|
205
|
+
shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
|
206
|
+
},
|
207
|
+
wrapper: {
|
208
|
+
each: lambda { |record, &block|
|
209
|
+
tresult = record.transaction_wrapper(outside_rescued_errors: ActiveRecord::RecordNotUnique) do
|
210
|
+
puts "melon #{record.name} honey"
|
211
|
+
block.call # does the actual saving!
|
212
|
+
end
|
213
|
+
# NOTE: The value returned by the wrapper will also be returned by the call to `shift_cx`.
|
214
|
+
# You could return the whole tresult object here, instead of just true/false!
|
215
|
+
tresult.success?
|
216
|
+
},
|
217
|
+
all: lambda { |rel, &block|
|
218
|
+
tresult = Spaceship.transaction_wrapper do
|
219
|
+
puts "can you eat #{rel.count} shoes"
|
220
|
+
block.call
|
221
|
+
end
|
222
|
+
tresult.success?
|
223
|
+
}
|
224
|
+
}
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
class SpaceFederation < ActiveRecord::Base
|
229
|
+
has_many :spaceships
|
230
|
+
|
231
|
+
def all_spaceships_commandeered_by(nemesis_federation)
|
232
|
+
Spaceship.shift_cx(shift_to: nemesis_federation, shift_from: self)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
132
237
|
### Complete example
|
133
238
|
|
134
239
|
Putting it all together...
|
@@ -145,23 +250,78 @@ end
|
|
145
250
|
class Spaceship < ActiveRecord::Base
|
146
251
|
belongs_to :captain
|
147
252
|
extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
|
148
|
-
before_shift: ->(
|
253
|
+
before_shift: ->(shifting_rel) { shifting_rel.result.ownership_changes += 1 }
|
149
254
|
|
150
255
|
belongs_to :space_federation
|
151
|
-
extend Shiftable::Collection.new
|
152
|
-
|
153
|
-
|
154
|
-
|
256
|
+
extend Shiftable::Collection.new(
|
257
|
+
belongs_to: :space_federation,
|
258
|
+
has_many: :spaceships,
|
259
|
+
before_shift: lambda { |shifting_rel|
|
260
|
+
shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
|
261
|
+
},
|
262
|
+
wrapper: {
|
263
|
+
each: lambda { |record, &block|
|
264
|
+
tresult = record.transaction_wrapper(outside_rescued_errors: ActiveRecord::RecordNotUnique) do
|
265
|
+
puts "melon #{record.name} honey"
|
266
|
+
block.call # does the actual saving!
|
267
|
+
end
|
268
|
+
tresult.success?
|
269
|
+
},
|
270
|
+
all: lambda { |rel, &block|
|
271
|
+
tresult = Spaceship.transaction_wrapper do
|
272
|
+
puts "can you eat #{rel.count} shoes"
|
273
|
+
block.call
|
274
|
+
end
|
275
|
+
tresult.success?
|
276
|
+
}
|
277
|
+
}
|
278
|
+
)
|
155
279
|
end
|
156
280
|
|
157
281
|
class SpaceFederation < ActiveRecord::Base
|
158
282
|
has_many :captains
|
159
283
|
has_many :spaceships
|
284
|
+
has_many :space_treaty_signature, as: :signatory
|
285
|
+
has_many :space_treaties, through: :space_treaty_signatures, as: :signatory
|
286
|
+
has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
|
287
|
+
has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
|
288
|
+
|
289
|
+
def assimilate_from(other_federation)
|
290
|
+
SpaceTreatySignature.space_federation_shift_cx(shift_to: self, shift_from: other_federation)
|
291
|
+
end
|
160
292
|
|
161
293
|
def all_spaceships_commandeered_by(nemesis_federation)
|
162
294
|
Spaceship.shift_cx(shift_to: nemesis_federation, shift_from: self)
|
163
295
|
end
|
164
296
|
end
|
297
|
+
class SpaceTreaty < ActiveRecord::Base
|
298
|
+
has_many :space_treaty_signatures
|
299
|
+
end
|
300
|
+
|
301
|
+
class SpaceTreatySignature < ActiveRecord::Base
|
302
|
+
belongs_to :space_treaty
|
303
|
+
belongs_to :signatory, polymorphic: true
|
304
|
+
extend Shiftable::Collection.new(
|
305
|
+
belongs_to: :signatory, has_many: :space_treaty_signatures,
|
306
|
+
polymorphic: { type: "SpaceFederation", as: :signatory },
|
307
|
+
method_prefix: "space_federation_"
|
308
|
+
)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Including Planet and SpaceStation, for completeness of the example as the other "types" of polymorphic signatories
|
312
|
+
class Planet < ActiveRecord::Base
|
313
|
+
has_many :space_treaty_signatures, as: :signatory
|
314
|
+
has_many :space_treaties, through: :space_treaty_signatures
|
315
|
+
has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
|
316
|
+
has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
|
317
|
+
end
|
318
|
+
|
319
|
+
class SpaceStation < ActiveRecord::Base
|
320
|
+
has_many :space_treaty_signature, as: :signatory
|
321
|
+
has_many :space_treaties, through: :space_treaty_signatures
|
322
|
+
has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
|
323
|
+
has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
|
324
|
+
end
|
165
325
|
```
|
166
326
|
|
167
327
|
... stay tuned!
|
data/lib/shiftable/collection.rb
CHANGED
@@ -16,7 +16,7 @@ module Shiftable
|
|
16
16
|
class Collection < Module
|
17
17
|
# associations: belongs_to, has_many
|
18
18
|
# options: method_prefix, before_shift
|
19
|
-
def initialize(belongs_to:, has_many:, method_prefix: nil, before_shift: nil)
|
19
|
+
def initialize(belongs_to:, has_many:, polymorphic: nil, method_prefix: nil, before_shift: nil, wrapper: nil)
|
20
20
|
# Ruby's Module initializer doesn't take any arguments
|
21
21
|
super()
|
22
22
|
|
@@ -35,12 +35,18 @@ module Shiftable
|
|
35
35
|
has_many: has_many.to_s.to_sym
|
36
36
|
},
|
37
37
|
options: {
|
38
|
+
polymorphic: polymorphic,
|
38
39
|
method_prefix: method_prefix,
|
39
40
|
# will prevent the save if it returns false
|
40
41
|
# allows for any custom logic to be run, such as setting attributes, prior to the shift (save).
|
41
|
-
before_shift: before_shift
|
42
|
+
before_shift: before_shift,
|
43
|
+
# wrapper: {
|
44
|
+
# all: ->() { klass.transaction_wrapper { yield } },
|
45
|
+
# each: ->() { klass.transaction_wrapper { yield } },
|
46
|
+
# }
|
47
|
+
wrapper: wrapper
|
42
48
|
},
|
43
|
-
type: :cx
|
49
|
+
type: polymorphic ? :pcx : :cx
|
44
50
|
)
|
45
51
|
end
|
46
52
|
|
@@ -62,12 +68,13 @@ module Shiftable
|
|
62
68
|
module ShiftCollectionModulizer
|
63
69
|
def to_mod(signature)
|
64
70
|
prefix = signature.method_prefix
|
71
|
+
type = signature.type
|
65
72
|
Module.new do
|
66
|
-
define_method(:"#{prefix}
|
67
|
-
signature.
|
73
|
+
define_method(:"#{prefix}shift_#{type}_column") do
|
74
|
+
signature.send("shift_#{type}_column")
|
68
75
|
end
|
69
|
-
define_method(:"#{prefix}
|
70
|
-
signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
|
76
|
+
define_method(:"#{prefix}shift_#{type}") do |shift_to:, shift_from:, bang: false|
|
77
|
+
signature.shift_data!(shift_to: shift_to, shift_from: shift_from, bang: bang)
|
71
78
|
end
|
72
79
|
end
|
73
80
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module Shiftable
|
4
4
|
class ModSignature
|
5
|
-
|
6
|
-
|
5
|
+
VALID_ASSOCIATIONS = { sg: %i[belongs_to has_one], cx: %i[belongs_to has_many], pcx: %i[belongs_to has_many] }.freeze
|
6
|
+
VALID_TYPES = VALID_ASSOCIATIONS.keys.dup.freeze
|
7
7
|
DEFAULT_BEFORE_SHIFT = ->(*_) { true }
|
8
8
|
attr_reader :associations, :options, :type, :base
|
9
9
|
|
@@ -24,6 +24,18 @@ module Shiftable
|
|
24
24
|
raise ArgumentError, "exactly two distinct associations must be provided" if invalid_number_of_associations?
|
25
25
|
end
|
26
26
|
|
27
|
+
def wrapper
|
28
|
+
options[:wrapper] || {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def polymorphic_type
|
32
|
+
options.dig(:polymorphic, :type)
|
33
|
+
end
|
34
|
+
|
35
|
+
def polymorphic_as
|
36
|
+
options.dig(:polymorphic, :as)
|
37
|
+
end
|
38
|
+
|
27
39
|
def invalid_type?
|
28
40
|
!VALID_TYPES.include?(type)
|
29
41
|
end
|
@@ -48,59 +60,87 @@ module Shiftable
|
|
48
60
|
end
|
49
61
|
|
50
62
|
def validate_relationships
|
51
|
-
|
52
|
-
raise ArgumentError, "Unable to find belongs_to: :#{belongs_to} in #{base}" unless
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
bt_reflection = base.reflect_on_association(belongs_to)
|
64
|
+
raise ArgumentError, "Unable to find belongs_to: :#{belongs_to} in #{base}" unless bt_reflection
|
65
|
+
# We can't validate any further if the reflection is polymorphic
|
66
|
+
return true if bt_reflection.polymorphic?
|
67
|
+
|
68
|
+
klass = bt_reflection.klass
|
69
|
+
has_reflection = klass.reflect_on_association(has_rel)
|
70
|
+
raise ArgumentError, "Unable to find #{has_rel_name}: :#{has_rel} in #{klass}" unless has_reflection
|
57
71
|
end
|
58
72
|
|
59
73
|
module CxMethods
|
60
|
-
def
|
74
|
+
def has_rel
|
61
75
|
associations[:has_many]
|
62
76
|
end
|
63
77
|
|
64
|
-
|
65
|
-
|
66
|
-
def shift_data!(shift_to:, shift_from:)
|
78
|
+
def shift_data!(shift_to:, shift_from:, bang: false)
|
67
79
|
validate_relationships
|
68
80
|
shifting_rel = ShiftingRelation.new(
|
69
81
|
to: shift_to,
|
70
82
|
from: shift_from,
|
71
83
|
column: shift_column,
|
72
|
-
base: base
|
84
|
+
base: base,
|
85
|
+
wrapper: wrapper,
|
86
|
+
bang: bang
|
73
87
|
)
|
74
|
-
shifting_rel.shift do
|
75
|
-
before_shift&.call(shifting_rel
|
88
|
+
shifting_rel.shift do
|
89
|
+
before_shift&.call(shifting_rel)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
module PcxMethods
|
95
|
+
# This method could be defined for parity, but it is never used.
|
96
|
+
# def has_rel
|
97
|
+
# associations[:has_many]
|
98
|
+
# end
|
99
|
+
|
100
|
+
def shift_data!(shift_to:, shift_from:, bang: false)
|
101
|
+
validate_relationships
|
102
|
+
shifting_rel = ShiftingPolymorphicRelation.new(
|
103
|
+
to: shift_to,
|
104
|
+
from: shift_from,
|
105
|
+
column: {
|
106
|
+
type: polymorphic_type,
|
107
|
+
as: polymorphic_as,
|
108
|
+
id_column: shift_pcx_column
|
109
|
+
},
|
110
|
+
base: base,
|
111
|
+
wrapper: wrapper,
|
112
|
+
bang: bang
|
113
|
+
)
|
114
|
+
shifting_rel.shift do
|
115
|
+
before_shift&.call(shifting_rel)
|
76
116
|
end
|
77
117
|
end
|
78
118
|
end
|
79
119
|
|
80
120
|
module SgMethods
|
81
|
-
def
|
121
|
+
def has_rel
|
82
122
|
associations[:has_one]
|
83
123
|
end
|
84
124
|
|
85
|
-
alias has_rel has_one
|
86
|
-
|
87
125
|
# Do not move record if a record already exists (we are shifting a "has_one" association, after all)
|
88
126
|
def precheck
|
89
127
|
options[:precheck]
|
90
128
|
end
|
91
129
|
|
92
|
-
def shift_data!(shift_to:, shift_from:)
|
130
|
+
def shift_data!(shift_to:, shift_from:, bang: false)
|
93
131
|
validate_relationships
|
94
132
|
shifting = ShiftingRecord.new(
|
95
133
|
to: shift_to,
|
96
134
|
from: shift_from,
|
97
135
|
column: shift_column,
|
98
|
-
base: base
|
136
|
+
base: base,
|
137
|
+
wrapper: wrapper,
|
138
|
+
bang: bang
|
99
139
|
) do
|
100
|
-
!precheck || !shift_to.send(
|
140
|
+
!precheck || !shift_to.send(has_rel)
|
101
141
|
end
|
102
|
-
shifting.shift do
|
103
|
-
before_shift&.call(shifting
|
142
|
+
shifting.shift do
|
143
|
+
before_shift&.call(shifting)
|
104
144
|
end
|
105
145
|
end
|
106
146
|
end
|
@@ -122,9 +162,16 @@ module Shiftable
|
|
122
162
|
options[:before_shift] || DEFAULT_BEFORE_SHIFT
|
123
163
|
end
|
124
164
|
|
165
|
+
def shift_pcx_column
|
166
|
+
"#{polymorphic_as}_id"
|
167
|
+
end
|
168
|
+
|
125
169
|
def shift_column
|
126
170
|
reflection = base.reflect_on_association(belongs_to).klass.reflect_on_association(has_rel)
|
127
171
|
reflection.foreign_key
|
128
172
|
end
|
173
|
+
|
174
|
+
alias shift_sg_column shift_column
|
175
|
+
alias shift_cx_column shift_column
|
129
176
|
end
|
130
177
|
end
|
data/lib/shiftable/shifting.rb
CHANGED
@@ -3,9 +3,13 @@
|
|
3
3
|
module Shiftable
|
4
4
|
# Gets data to be shifted
|
5
5
|
class Shifting
|
6
|
-
|
6
|
+
# Public API
|
7
|
+
attr_accessor :result, :bang, :shift_all_wrapper, :shift_each_wrapper
|
8
|
+
attr_reader :to, :from
|
9
|
+
# Internal API
|
10
|
+
attr_reader :column, :base, :run_save
|
7
11
|
|
8
|
-
def initialize(to:, from:, column:, base:)
|
12
|
+
def initialize(to:, from:, column:, base:, wrapper:, bang:)
|
9
13
|
@to = to
|
10
14
|
@from = from
|
11
15
|
@column = column
|
@@ -14,6 +18,9 @@ module Shiftable
|
|
14
18
|
do_query = block_given? ? yield : true
|
15
19
|
@result = do_query ? query : nil
|
16
20
|
@run_save = true
|
21
|
+
@shift_all_wrapper = wrapper[:all]
|
22
|
+
@shift_each_wrapper = wrapper[:each]
|
23
|
+
@bang = bang
|
17
24
|
end
|
18
25
|
|
19
26
|
# def found?
|
@@ -31,6 +38,28 @@ module Shiftable
|
|
31
38
|
raise ArgumentError, "shift_from must have an id (primary key) value, but is: #{from&.id}" unless from&.id
|
32
39
|
end
|
33
40
|
|
41
|
+
def run_save!
|
42
|
+
if shift_all_wrapper
|
43
|
+
shift_all_wrapper.call(self) do
|
44
|
+
do_saves
|
45
|
+
end
|
46
|
+
else
|
47
|
+
do_saves
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def do_saves
|
52
|
+
if shift_each_wrapper
|
53
|
+
each do |rec|
|
54
|
+
shift_each_wrapper.call(rec) do
|
55
|
+
bang ? rec.save! : rec.save
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
bang ? each(&:save!) : each(&:save)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
34
63
|
# def query
|
35
64
|
# raise "query must be defined in a subclass"
|
36
65
|
# end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shiftable
|
4
|
+
# Gets data to be shifted
|
5
|
+
class ShiftingPolymorphicRelation < Shifting
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def polymorphic_id_column
|
9
|
+
column[:id_column]
|
10
|
+
end
|
11
|
+
|
12
|
+
def polymorphic_type_column
|
13
|
+
"#{column[:as]}_type"
|
14
|
+
end
|
15
|
+
|
16
|
+
def found?
|
17
|
+
result.any?
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
result.each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return result (once it is shifted)
|
25
|
+
def shift
|
26
|
+
return false unless found?
|
27
|
+
|
28
|
+
each do |record|
|
29
|
+
record.send("#{polymorphic_id_column}=", to.id)
|
30
|
+
end
|
31
|
+
@run_save = yield if block_given?
|
32
|
+
return result unless run_save
|
33
|
+
|
34
|
+
run_save!
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def query
|
41
|
+
base.where(
|
42
|
+
polymorphic_type_column => column[:type],
|
43
|
+
polymorphic_id_column => from.id
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -12,12 +12,34 @@ module Shiftable
|
|
12
12
|
return false unless found?
|
13
13
|
|
14
14
|
result.send("#{column}=", to.id)
|
15
|
-
@run_save = yield
|
16
|
-
|
15
|
+
@run_save = yield if block_given?
|
16
|
+
return nil unless run_save
|
17
|
+
|
18
|
+
run_save!
|
17
19
|
end
|
18
20
|
|
19
21
|
private
|
20
22
|
|
23
|
+
def run_save!
|
24
|
+
if shift_all_wrapper
|
25
|
+
shift_all_wrapper.call(self) do
|
26
|
+
do_save
|
27
|
+
end
|
28
|
+
else
|
29
|
+
do_save
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def do_save
|
34
|
+
if shift_each_wrapper
|
35
|
+
shift_each_wrapper.call(result) do
|
36
|
+
bang ? result.save! : result.save
|
37
|
+
end
|
38
|
+
else
|
39
|
+
bang ? result.save! : result.save
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
21
43
|
def query
|
22
44
|
base.find_by(column => from.id)
|
23
45
|
end
|
@@ -20,8 +20,10 @@ module Shiftable
|
|
20
20
|
each do |record|
|
21
21
|
record.send("#{column}=", to.id)
|
22
22
|
end
|
23
|
-
@run_save = yield
|
24
|
-
|
23
|
+
@run_save = yield if block_given?
|
24
|
+
return result unless run_save
|
25
|
+
|
26
|
+
run_save!
|
25
27
|
result
|
26
28
|
end
|
27
29
|
|
data/lib/shiftable/single.rb
CHANGED
@@ -14,14 +14,14 @@
|
|
14
14
|
# belongs_to: :captain,
|
15
15
|
# has_one: :spaceship,
|
16
16
|
# precheck: true,
|
17
|
-
# before_shift: ->(
|
17
|
+
# before_shift: ->(shifting_rel) { shifting_rel.result..ownership_changes += 1 }
|
18
18
|
# )
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
module Shiftable
|
22
22
|
# Inheriting from Module is a powerful pattern. If you like it checkout the debug_logging gem!
|
23
23
|
class Single < Module
|
24
|
-
def initialize(belongs_to:, has_one:, method_prefix: nil, precheck: true, before_shift: nil)
|
24
|
+
def initialize(belongs_to:, has_one:, method_prefix: nil, precheck: true, before_shift: nil, wrapper: nil)
|
25
25
|
# Ruby's Module initializer doesn't take any arguments
|
26
26
|
super()
|
27
27
|
|
@@ -44,7 +44,8 @@ module Shiftable
|
|
44
44
|
method_prefix: method_prefix,
|
45
45
|
# will prevent the save if it returns false
|
46
46
|
# allows for any custom logic to be run, such as setting attributes, prior to the shift (save).
|
47
|
-
before_shift: before_shift
|
47
|
+
before_shift: before_shift,
|
48
|
+
wrapper: wrapper
|
48
49
|
},
|
49
50
|
type: :sg
|
50
51
|
)
|
@@ -70,10 +71,10 @@ module Shiftable
|
|
70
71
|
prefix = signature.method_prefix
|
71
72
|
Module.new do
|
72
73
|
define_method(:"#{prefix}shift_column") do
|
73
|
-
signature.
|
74
|
+
signature.send("shift_#{signature.type}_column")
|
74
75
|
end
|
75
|
-
define_method(:"#{prefix}shift_single") do |shift_to:, shift_from
|
76
|
-
signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
|
76
|
+
define_method(:"#{prefix}shift_single") do |shift_to:, shift_from:, bang: false|
|
77
|
+
signature.shift_data!(shift_to: shift_to, shift_from: shift_from, bang: bang)
|
77
78
|
end
|
78
79
|
end
|
79
80
|
end
|
data/lib/shiftable/version.rb
CHANGED
data/lib/shiftable.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative "shiftable/version"
|
4
4
|
require_relative "shiftable/shifting"
|
5
5
|
require_relative "shiftable/shifting_record"
|
6
|
+
require_relative "shiftable/shifting_polymorphic_relation"
|
6
7
|
require_relative "shiftable/shifting_relation"
|
7
8
|
require_relative "shiftable/mod_signature"
|
8
9
|
require_relative "shiftable/collection"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shiftable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Boling
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord-transactionable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: byebug
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +150,20 @@ dependencies:
|
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '1.0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: silent_stream
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '1'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '1'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
168
|
name: sqlite3
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -181,6 +209,7 @@ files:
|
|
181
209
|
- lib/shiftable/collection.rb
|
182
210
|
- lib/shiftable/mod_signature.rb
|
183
211
|
- lib/shiftable/shifting.rb
|
212
|
+
- lib/shiftable/shifting_polymorphic_relation.rb
|
184
213
|
- lib/shiftable/shifting_record.rb
|
185
214
|
- lib/shiftable/shifting_relation.rb
|
186
215
|
- lib/shiftable/single.rb
|