shiftable 0.4.0 → 0.6.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: 62926c718edd257728210a0870ea5eca44aeb35b33a929d87d2efb8a5f902910
4
- data.tar.gz: b4c35fcc2a3eff45d992b5260e6d4f2f13d870782f40d06285172eb2f1207f9d
3
+ metadata.gz: 03d919c64ab0be03ba40c5474cfa7971dda6158ab01df20940bb644c32482b19
4
+ data.tar.gz: 3246e28e7578ea7b56ae905c23c675e121a8c71556d127b8ee4a0fbeedcce15b
5
5
  SHA512:
6
- metadata.gz: 8c21a1f0c51b4a3cb715730fe07bb29d562a84d8dd79b558a3ca06c6a7bf20032928225b41aa1e749b4a7a85afe4fdb33cbf9d068b7539ef622665272f1f8115
7
- data.tar.gz: d9bc2d54fe6fb3f15f781b33ddf83c6abfa91f7b66e426274bc9ef07595e61a9c6b4f42a2ffe27e6b8cce5453dbcc0221ac07261bddb762a3a9eba3e79d7cef1
6
+ metadata.gz: b3721c0d437b1ab45deb99f71f7a199fe4c37aae31f86f8443eb2effd1bd07c9d930aa64c4a4b824c7371863c9cb219251e59dc673d9ef40a73551e75c7efc67
7
+ data.tar.gz: c0d40a890f89fda5137575d9052131605f33e6a861e6b9e55b6681d89cfeba4f9ac5547cb09c4ab68cb69bafa50a9c6ebc75412152cf8c3bf9e76f90ce80f823
data/CHANGELOG.md CHANGED
@@ -1,7 +1,38 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.4.0] - 2021-10-27
3
+ ### Added
4
+
5
+
6
+ ### Changed
7
+
8
+
9
+ ### Fixed
10
+
11
+
12
+ ### Removed
13
+
14
+
15
+ ## [0.6.0] - 2021-11-12
16
+ ### Added
17
+
18
+ - Support for wrappers/hooks around each record shift, and around the entire set (see examples in specs or README)
4
19
 
20
+ ## [0.5.1] - 2021-11-12
21
+ ### Fixed
22
+
23
+ - Documentation typos in README
24
+
25
+ ## [0.5.0] - 2021-11-12
26
+ ### Added
27
+
28
+ - Support for Polymorphic associations (see examples in specs or README)
29
+
30
+ ## [0.4.1] - 2021-11-10
31
+ ### Fixed
32
+
33
+ - Documentation typos in README
34
+
35
+ ## [0.4.0] - 2021-10-27
5
36
  ### Changed
6
37
 
7
38
  - option :preflight_checks renamed to :precheck
@@ -11,7 +42,6 @@
11
42
  - Even more 100% spec coverage
12
43
 
13
44
  ## [0.3.0] - 2021-10-26
14
-
15
45
  ### Changed
16
46
 
17
47
  - Internal rewrite to improve maintainability
@@ -22,7 +52,6 @@
22
52
  - Even more 100% spec coverage
23
53
 
24
54
  ## [0.2.0] - 2021-10-24
25
-
26
55
  ### Changed
27
56
 
28
57
  - option `before_save` is now `before_shift` as originally documented
@@ -34,7 +63,6 @@
34
63
  - Documentation
35
64
 
36
65
  ## [0.1.1] - 2021-10-23
37
-
38
66
  ### Fixed
39
67
 
40
68
  - Github Actions build
@@ -44,7 +72,6 @@
44
72
  - Linting
45
73
 
46
74
  ## [0.1.0] - 2021-10-23
47
-
48
75
  ### Added
49
76
 
50
77
  - Initial release
data/CODE_OF_CONDUCT.md CHANGED
File without changes
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ ## Contributing
2
+
3
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/pboling/activerecord-transactionable][source]
4
+ . This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
5
+ the [code of conduct][conduct].
6
+
7
+ To submit a patch, please fork the project and create a patch with tests. Once you're happy with it send a pull request!
8
+
9
+ ## Contributors
10
+
11
+ [![Contributors](https://contrib.rocks/image?repo=pboling/activerecord-transactionable)][contributors]
12
+
13
+ Made with [contributors-img][contrib-rocks].
14
+
15
+ [comment]: <> (Following links are used by README, CONTRIBUTING)
16
+
17
+ [conduct]: https://github.com/pboling/activerecord-transactionable/blob/master/CODE_OF_CONDUCT.md
18
+
19
+ [contrib-rocks]: https://contrib.rocks
20
+
21
+ [contributors]: https://github.com/pboling/activerecord-transactionable/graphs/contributors
22
+
23
+ [comment]: <> (Following links are used by README, CONTRIBUTING, Homepage)
24
+
25
+ [source]: https://github.com/pboling/activerecord-transactionable/
data/LICENSE.txt CHANGED
File without changes
data/README.md CHANGED
@@ -5,31 +5,23 @@ Do your Spaceships belong to Captains, but sometimes a Captain will retire, and
5
5
  We've all been there. This gem provides structure around the process of "shifting" your records from one associated
6
6
  record to a new record.
7
7
 
8
- | Project | Shiftable |
9
- |------------------------ | ----------------------- |
10
- | gem name | [shiftable](https://rubygems.org/gems/shiftable) |
11
- | license | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) |
12
- | download rank | [![Downloads Today](https://img.shields.io/gem/rd/shiftable.svg)](https://github.com/pboling/shiftable) |
13
- | version | [![Version](https://img.shields.io/gem/v/shiftable.svg)](https://rubygems.org/gems/shiftable) |
14
- | dependencies | [![Depfu](https://badges.depfu.com/badges/0412727b7e3b740b950a683eebc708e2/count.svg)](https://depfu.com/github/pboling/shiftable?project_id=32594) |
15
- | unit tests | [![unit tests](https://github.com/pboling/shiftable/actions/workflows/test.yml/badge.svg)](https://github.com/pboling/shiftable/actions) |
16
- | lint status | [![lint status](https://github.com/pboling/shiftable/actions/workflows/style.yml/badge.svg)](https://github.com/pboling/shiftable/actions) |
17
- | unsupported status | [![unsupported status](https://github.com/pboling/shiftable/actions/workflows/unsupported.yml/badge.svg)](https://github.com/pboling/shiftable/actions) |
18
- | test coverage | [![Test Coverage](https://api.codeclimate.com/v1/badges/a53aa8b7c413b950d519/test_coverage)](https://codeclimate.com/github/pboling/shiftable/test_coverage) |
19
- | maintainability | [![Maintainability](https://api.codeclimate.com/v1/badges/a53aa8b7c413b950d519/maintainability)](https://codeclimate.com/github/pboling/shiftable/maintainability) |
20
- | code triage | [![Open Source Helpers](https://www.codetriage.com/pboling/shiftable/badges/users.svg)](https://www.codetriage.com/pboling/shiftable) |
21
- | homepage | [on Github.com][homepage], [on Railsbling.com][blogpage] |
22
- | documentation | [on RDoc.info][documentation] |
23
- | Spread ~♡ⓛⓞⓥⓔ♡~ | [🌏][aboutme], [👼][angelme], [💻][coderme], [![Tweet @ Peter][followme-img]][tweetme], [🌹][politicme] |
8
+ | Project | Shiftable |
9
+ |--------------------------- | ----------------------- |
10
+ | name, license, docs | [![RubyGems.org](https://img.shields.io/badge/name-shiftable-brightgreen.svg?style=flat)][rubygems] [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)][license-ref] [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation] |
11
+ | version & downloads | [![Version](https://img.shields.io/gem/v/shiftable.svg)][rubygems] [![Total Downloads](https://img.shields.io/gem/dt/shiftable.svg)][rubygems] [![Downloads Today](https://img.shields.io/gem/rd/shiftable.svg)][rubygems] [![Homepage](https://img.shields.io/badge/source-github-brightgreen.svg?style=flat)][source] |
12
+ | dependencies & linting | [![Depfu](https://badges.depfu.com/badges/0412727b7e3b740b950a683eebc708e2/count.svg)][depfu] [![lint status](https://github.com/pboling/shiftable/actions/workflows/style.yml/badge.svg)][actions] |
13
+ | unit tests | [![supported rubies](https://github.com/pboling/shiftable/actions/workflows/supported.yml/badge.svg)][actions] [![unsupported status](https://github.com/pboling/shiftable/actions/workflows/unsupported.yml/badge.svg)][actions] |
14
+ | coverage & maintainability | [![Test Coverage](https://api.codeclimate.com/v1/badges/a53aa8b7c413b950d519/test_coverage)][climate_coverage] [![codecov](https://codecov.io/gh/pboling/shiftable/branch/main/graph/badge.svg?token=J1542PYN2Z)][codecov_coverage] [![Maintainability](https://api.codeclimate.com/v1/badges/a53aa8b7c413b950d519/maintainability)][climate_maintainability] [![Security Policy](https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat)][security] |
15
+ | resources | [![Discussion](https://img.shields.io/badge/discussions-github-brightgreen.svg?style=flat)][gh_discussions] [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github) [![Join the chat at https://gitter.im/pboling/shiftable](https://badges.gitter.im/Join%20Chat.svg)][chat] [![Blog](https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat)][blogpage] |
16
+ | Spread ~♡ⓛⓞⓥⓔ♡~ | [![Open Source Helpers](https://www.codetriage.com/pboling/shiftable/badges/users.svg)][code_triage] [![Liberapay Patrons](https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay)][liberapay_donate] [![Sponsor Me](https://img.shields.io/badge/sponsor-pboling.svg?style=social&logo=github)][gh_sponsors] [🌏][aboutme] [👼][angelme] [💻][coderme] [🌹][politicme] [![Tweet @ Peter][followme-img]][tweetme] |
24
17
 
25
18
  ## Compatibility
26
19
 
27
20
  Targeted ruby compatibility is non-EOL versions of Ruby, currently 2.6, 2.7, and 3.0, but may work on older Rubies back
28
21
  to 2.0, though it is limited to 2.5 in the gemspec. Feel free to fork if you need something older! Targeted ActiveRecord
29
22
  (Rails not required) compatibility follows the same scheme
30
- as [Rails Security Issue maintenance policy](https://guides.rubyonrails.org/maintenance_policy.html#security-issues),
31
- currently 6.1, 6.0, 5.2, but it is highly likely that this code will work in any version of ActiveRecord/Rails that runs
32
- on Ruby 2+.
23
+ as [Rails Security Issue maintenance policy][maintenancee_policy], currently 6.1, 6.0, 5.2, but it is highly likely that
24
+ this code will work in any version of ActiveRecord/Rails that runs on Ruby 2+.
33
25
 
34
26
  ## Installation
35
27
 
@@ -88,7 +80,7 @@ But how can you accomplish this? If you used the `shiftable` gem, won't take but
88
80
  class Spaceship < ActiveRecord::Base
89
81
  belongs_to :captain
90
82
  extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
91
- before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
83
+ before_shift: ->(shifting_rel) { shifting_rel.result.ownership_changes += 1 }
92
84
  end
93
85
  ```
94
86
 
@@ -112,22 +104,127 @@ end
112
104
 
113
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.
114
106
 
115
- ### Multiple association on a single class
107
+ ### Multiple associations on a single class
116
108
 
117
109
  What if the captain and the spaceship have a boss... the space
118
110
  federation! And in a run-in with their arch-Nemesis the Plinth-inth,
119
111
  all federation spaceships are commandeered! You are ruined!
120
112
 
121
113
  ```ruby
122
-
123
114
  class Spaceship < ActiveRecord::Base
124
115
  belongs_to :space_federation
125
- extend Shiftable::Collection.new belongs_to: :space_federation, has_one: :spaceship,
126
- before_shift: lambda do |shifting_rel:, shift_to:, shift_from:|
127
- shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
116
+ extend Shiftable::Collection.new belongs_to: :space_federation, has_many: :spaceships,
117
+ before_shift: lambda { |shifting_rel|
118
+ shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
119
+ }
120
+ end
121
+
122
+ class SpaceFederation < ActiveRecord::Base
123
+ has_many :spaceships
124
+
125
+ def all_spaceships_commandeered_by(nemesis_federation)
126
+ Spaceship.shift_cx(shift_to: nemesis_federation, shift_from: self)
127
+ end
128
+ end
129
+ ```
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)
128
174
  end
129
175
  end
130
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
+
131
228
  class SpaceFederation < ActiveRecord::Base
132
229
  has_many :spaceships
133
230
 
@@ -153,27 +250,97 @@ end
153
250
  class Spaceship < ActiveRecord::Base
154
251
  belongs_to :captain
155
252
  extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
156
- before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
253
+ before_shift: ->(shifting_rel) { shifting_rel.result.ownership_changes += 1 }
157
254
 
158
255
  belongs_to :space_federation
159
- extend Shiftable::Collection.new belongs_to: :space_federation, has_one: :spaceship,
160
- before_shift: lambda do |shifting_rel:, shift_to:, shift_from:|
161
- shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
162
- end
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
+ )
163
279
  end
164
280
 
165
281
  class SpaceFederation < ActiveRecord::Base
166
282
  has_many :captains
167
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
168
292
 
169
293
  def all_spaceships_commandeered_by(nemesis_federation)
170
294
  Spaceship.shift_cx(shift_to: nemesis_federation, shift_from: self)
171
295
  end
172
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
173
325
  ```
174
326
 
175
327
  ... stay tuned!
176
328
 
329
+ ## More Information
330
+
331
+ * RubyDoc
332
+ Documentation: [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation]
333
+ * GitHub
334
+ Discussions: [![Discussion](https://img.shields.io/badge/discussions-github-brightgreen.svg?style=flat)][gh_discussions]
335
+ * Live Chat on
336
+ Gitter: [![Join the chat at https://gitter.im/pboling/activerecord-transactionable](https://badges.gitter.im/Join%20Chat.svg)][chat]
337
+ * Maintainer's Blog: [![Blog](https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat)][blogpage]
338
+
339
+ ## Code of Conduct
340
+
341
+ Everyone interacting in the Shiftable project's codebases, issue trackers, chat rooms and mailing lists is expected to
342
+ follow the [code of conduct][conduct].
343
+
177
344
  ## Development
178
345
 
179
346
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can
@@ -185,14 +352,13 @@ push git commits and the created tag, and push the `.gem` file to [rubygems.org]
185
352
 
186
353
  ## Contributing
187
354
 
188
- Bug reports and pull requests are welcome on GitHub at https://github.com/pboling/shiftable. This project is intended to
189
- be a safe, welcoming space for collaboration, and contributors are expected to adhere to
190
- the [code of conduct](https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md).
355
+ See [CONTRIBUTING.md][contributing]
191
356
 
192
- ## Code of Conduct
357
+ ## Contributors
193
358
 
194
- Everyone interacting in the Shiftable project's codebases, issue trackers, chat rooms and mailing lists is expected to
195
- follow the [code of conduct](https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md).
359
+ [![Contributors](https://contrib.rocks/image?repo=pboling/shiftable)]("https://github.com/pboling/shiftable/graphs/contributors")
360
+
361
+ Made with [contributors-img](https://contrib.rocks).
196
362
 
197
363
  ## Versioning
198
364
 
@@ -207,18 +373,36 @@ the [Pessimistic Version Constraint][pvc] with two digits of precision.
207
373
  For example:
208
374
 
209
375
  ```ruby
210
- spec.add_dependency "shiftable", "~> 0.2"
376
+ spec.add_dependency "shiftable", "~> 0.4"
211
377
  ```
212
378
 
379
+ ## Contact
380
+
381
+ Author and maintainer is Peter Boling ([@pboling][gh_sponsors]).
382
+
383
+ Comments are welcome in the [GitHub Discussions][gh_discussions] board.
384
+
385
+ For security-related issues see [SECURITY][security].
386
+
213
387
  ## License
214
388
 
215
389
  The gem is available as open source under the terms of
216
- the [MIT License](https://opensource.org/licenses/MIT) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
217
- .
390
+ the [MIT License][license] [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)][license-ref].
391
+ See [LICENSE][license] for the official [Copyright Notice][copyright-notice-explainer].
218
392
 
219
393
  * Copyright (c) 2021 [Peter H. Boling][peterboling] of [Rails Bling][railsbling]
220
394
 
221
- [license]: LICENSE
395
+ [copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
396
+
397
+ [gh_discussions]: https://github.com/pboling/shiftable/discussions
398
+
399
+ [conduct]: https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md
400
+
401
+ [security]: https://github.com/pboling/shiftable/blob/master/SECURITY.md
402
+
403
+ [license]: https://github.com/pboling/shiftable/blob/master/LICENSE.txt
404
+
405
+ [license-ref]: https://opensource.org/licenses/MIT
222
406
 
223
407
  [semver]: http://semver.org/
224
408
 
@@ -240,8 +424,32 @@ the [MIT License](https://opensource.org/licenses/MIT) [![License: MIT](https://
240
424
 
241
425
  [politicme]: https://nationalprogressiveparty.org
242
426
 
243
- [documentation]: http://rdoc.info/github/pboling/shiftable/frames
427
+ [documentation]: https://rubydoc.info/github/pboling/shiftable/main
428
+
429
+ [source]: https://github.com/pboling/shiftable/
430
+
431
+ [actions]: https://github.com/pboling/shiftable/actions
432
+
433
+ [issues]: https://github.com/pboling/shiftable/issues
434
+
435
+ [climate_maintainability]: https://codeclimate.com/github/pboling/shiftable/maintainability
244
436
 
245
- [homepage]: https://github.com/pboling/shiftable/
437
+ [climate_coverage]: https://codeclimate.com/github/pboling/shiftable/test_coverage
438
+
439
+ [codecov_coverage]: https://codecov.io/gh/pboling/shiftable
440
+
441
+ [code_triage]: https://www.codetriage.com/pboling/shiftable
442
+
443
+ [depfu]: https://depfu.com/github/pboling/shiftable?project_id=32594
246
444
 
247
445
  [blogpage]: http://www.railsbling.com/tags/shiftable/
446
+
447
+ [rubygems]: https://rubygems.org/gems/shiftable
448
+
449
+ [chat]: https://gitter.im/pboling/shiftable?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
450
+
451
+ [maintenancee_policy]: https://guides.rubyonrails.org/maintenance_policy.html#security-issues
452
+
453
+ [liberapay_donate]: https://liberapay.com/pboling/donate
454
+
455
+ [gh_sponsors]: https://github.com/sponsors/pboling
@@ -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,11 +68,12 @@ 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}shift_cx_column") do
67
- signature.shift_column
73
+ define_method(:"#{prefix}shift_#{type}_column") do
74
+ signature.send("shift_#{type}_column")
68
75
  end
69
- define_method(:"#{prefix}shift_cx") do |shift_to:, shift_from:|
76
+ define_method(:"#{prefix}shift_#{type}") do |shift_to:, shift_from:|
70
77
  signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
71
78
  end
72
79
  end
@@ -2,11 +2,8 @@
2
2
 
3
3
  module Shiftable
4
4
  class ModSignature
5
- VALID_TYPES = %i[sg cx].freeze
6
- VALID_ASSOCIATIONS = {
7
- sg: %i[belongs_to has_one],
8
- cx: %i[belongs_to has_many]
9
- }.freeze
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
10
7
  DEFAULT_BEFORE_SHIFT = ->(*_) { true }
11
8
  attr_reader :associations, :options, :type, :base
12
9
 
@@ -27,6 +24,18 @@ module Shiftable
27
24
  raise ArgumentError, "exactly two distinct associations must be provided" if invalid_number_of_associations?
28
25
  end
29
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
+
30
39
  def invalid_type?
31
40
  !VALID_TYPES.include?(type)
32
41
  end
@@ -51,42 +60,66 @@ module Shiftable
51
60
  end
52
61
 
53
62
  def validate_relationships
54
- bt = base.reflect_on_association(belongs_to)
55
- raise ArgumentError, "Unable to find belongs_to: :#{belongs_to} in #{base}" unless bt
56
-
57
- klass = bt.klass
58
- hr = klass.reflect_on_association(has_rel)
59
- raise ArgumentError, "Unable to find #{has_rel_name}: :#{has_rel} in #{klass}" unless hr
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
60
71
  end
61
72
 
62
73
  module CxMethods
63
- def has_many
74
+ def has_rel
64
75
  associations[:has_many]
65
76
  end
66
77
 
67
- alias has_rel has_many
68
-
69
78
  def shift_data!(shift_to:, shift_from:)
70
79
  validate_relationships
71
80
  shifting_rel = ShiftingRelation.new(
72
81
  to: shift_to,
73
82
  from: shift_from,
74
83
  column: shift_column,
75
- base: base
84
+ base: base,
85
+ wrapper: wrapper
86
+ )
87
+ shifting_rel.shift do
88
+ before_shift&.call(shifting_rel)
89
+ end
90
+ end
91
+ end
92
+
93
+ module PcxMethods
94
+ # This method could be defined for parity, but it is never used.
95
+ # def has_rel
96
+ # associations[:has_many]
97
+ # end
98
+
99
+ def shift_data!(shift_to:, shift_from:)
100
+ validate_relationships
101
+ shifting_rel = ShiftingPolymorphicRelation.new(
102
+ to: shift_to,
103
+ from: shift_from,
104
+ column: {
105
+ type: polymorphic_type,
106
+ as: polymorphic_as,
107
+ id_column: shift_pcx_column
108
+ },
109
+ base: base,
110
+ wrapper: wrapper
76
111
  )
77
- shifting_rel.shift do |result|
78
- before_shift&.call(shifting_rel: result, shift_to: shift_to, shift_from: shift_from)
112
+ shifting_rel.shift do
113
+ before_shift&.call(shifting_rel)
79
114
  end
80
115
  end
81
116
  end
82
117
 
83
118
  module SgMethods
84
- def has_one
119
+ def has_rel
85
120
  associations[:has_one]
86
121
  end
87
122
 
88
- alias has_rel has_one
89
-
90
123
  # Do not move record if a record already exists (we are shifting a "has_one" association, after all)
91
124
  def precheck
92
125
  options[:precheck]
@@ -98,12 +131,13 @@ module Shiftable
98
131
  to: shift_to,
99
132
  from: shift_from,
100
133
  column: shift_column,
101
- base: base
134
+ base: base,
135
+ wrapper: wrapper
102
136
  ) do
103
- !precheck || !shift_to.send(has_one)
137
+ !precheck || !shift_to.send(has_rel)
104
138
  end
105
- shifting.shift do |result|
106
- before_shift&.call(shifting: result, shift_to: shift_to, shift_from: shift_from)
139
+ shifting.shift do
140
+ before_shift&.call(shifting)
107
141
  end
108
142
  end
109
143
  end
@@ -125,9 +159,16 @@ module Shiftable
125
159
  options[:before_shift] || DEFAULT_BEFORE_SHIFT
126
160
  end
127
161
 
162
+ def shift_pcx_column
163
+ "#{polymorphic_as}_id"
164
+ end
165
+
128
166
  def shift_column
129
167
  reflection = base.reflect_on_association(belongs_to).klass.reflect_on_association(has_rel)
130
168
  reflection.foreign_key
131
169
  end
170
+
171
+ alias shift_sg_column shift_column
172
+ alias shift_cx_column shift_column
132
173
  end
133
174
  end
@@ -3,9 +3,9 @@
3
3
  module Shiftable
4
4
  # Gets data to be shifted
5
5
  class Shifting
6
- attr_reader :to, :from, :column, :base, :result, :run_save
6
+ attr_reader :to, :from, :column, :base, :result, :run_save, :shift_all_wrapper, :shift_each_wrapper
7
7
 
8
- def initialize(to:, from:, column:, base:)
8
+ def initialize(to:, from:, column:, base:, wrapper:)
9
9
  @to = to
10
10
  @from = from
11
11
  @column = column
@@ -14,6 +14,8 @@ module Shiftable
14
14
  do_query = block_given? ? yield : true
15
15
  @result = do_query ? query : nil
16
16
  @run_save = true
17
+ @shift_all_wrapper = wrapper[:all]
18
+ @shift_each_wrapper = wrapper[:each]
17
19
  end
18
20
 
19
21
  # def found?
@@ -31,6 +33,28 @@ module Shiftable
31
33
  raise ArgumentError, "shift_from must have an id (primary key) value, but is: #{from&.id}" unless from&.id
32
34
  end
33
35
 
36
+ def run_save!
37
+ if shift_all_wrapper
38
+ shift_all_wrapper.call(self) do
39
+ do_saves
40
+ end
41
+ else
42
+ do_saves
43
+ end
44
+ end
45
+
46
+ def do_saves
47
+ if shift_each_wrapper
48
+ each do |rec|
49
+ shift_each_wrapper.call(rec) do
50
+ rec.save
51
+ end
52
+ end
53
+ else
54
+ each(&:save)
55
+ end
56
+ end
57
+
34
58
  # def query
35
59
  # raise "query must be defined in a subclass"
36
60
  # 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 result if block_given?
16
- result.save if run_save
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
+ result.save
37
+ end
38
+ else
39
+ 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 result if block_given?
24
- each(&:save) if run_save
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
 
@@ -14,14 +14,14 @@
14
14
  # belongs_to: :captain,
15
15
  # has_one: :spaceship,
16
16
  # precheck: true,
17
- # before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
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,7 +71,7 @@ module Shiftable
70
71
  prefix = signature.method_prefix
71
72
  Module.new do
72
73
  define_method(:"#{prefix}shift_column") do
73
- signature.shift_column
74
+ signature.send("shift_#{signature.type}_column")
74
75
  end
75
76
  define_method(:"#{prefix}shift_single") do |shift_to:, shift_from:|
76
77
  signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shiftable
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
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.0
4
+ version: 0.6.0
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-10-27 00:00:00.000000000 Z
11
+ date: 2021-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1'
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
@@ -109,105 +123,7 @@ dependencies:
109
123
  - !ruby/object:Gem::Version
110
124
  version: '3.10'
111
125
  - !ruby/object:Gem::Dependency
112
- name: rspec-block_is_expected
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '1.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '1.0'
125
- - !ruby/object:Gem::Dependency
126
- name: rubocop
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '1.2'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '1.2'
139
- - !ruby/object:Gem::Dependency
140
- name: rubocop-faker
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '1.1'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '1.1'
153
- - !ruby/object:Gem::Dependency
154
- name: rubocop-md
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '1.0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '1.0'
167
- - !ruby/object:Gem::Dependency
168
- name: rubocop-minitest
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - "~>"
172
- - !ruby/object:Gem::Version
173
- version: '0.15'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: '0.15'
181
- - !ruby/object:Gem::Dependency
182
- name: rubocop-packaging
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - "~>"
186
- - !ruby/object:Gem::Version
187
- version: '0.5'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - "~>"
193
- - !ruby/object:Gem::Version
194
- version: '0.5'
195
- - !ruby/object:Gem::Dependency
196
- name: rubocop-performance
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - "~>"
200
- - !ruby/object:Gem::Version
201
- version: '1.11'
202
- type: :development
203
- prerelease: false
204
- version_requirements: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - "~>"
207
- - !ruby/object:Gem::Version
208
- version: '1.11'
209
- - !ruby/object:Gem::Dependency
210
- name: rubocop-rake
126
+ name: rspec-benchmark
211
127
  requirement: !ruby/object:Gem::Requirement
212
128
  requirements:
213
129
  - - "~>"
@@ -221,21 +137,7 @@ dependencies:
221
137
  - !ruby/object:Gem::Version
222
138
  version: '0.6'
223
139
  - !ruby/object:Gem::Dependency
224
- name: rubocop-rspec
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - "~>"
228
- - !ruby/object:Gem::Version
229
- version: '2.5'
230
- type: :development
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - "~>"
235
- - !ruby/object:Gem::Version
236
- version: '2.5'
237
- - !ruby/object:Gem::Dependency
238
- name: rubocop-rspec-focused
140
+ name: rspec-block_is_expected
239
141
  requirement: !ruby/object:Gem::Requirement
240
142
  requirements:
241
143
  - - "~>"
@@ -249,47 +151,19 @@ dependencies:
249
151
  - !ruby/object:Gem::Version
250
152
  version: '1.0'
251
153
  - !ruby/object:Gem::Dependency
252
- name: rubocop-thread_safety
253
- requirement: !ruby/object:Gem::Requirement
254
- requirements:
255
- - - "~>"
256
- - !ruby/object:Gem::Version
257
- version: '0.4'
258
- type: :development
259
- prerelease: false
260
- version_requirements: !ruby/object:Gem::Requirement
261
- requirements:
262
- - - "~>"
263
- - !ruby/object:Gem::Version
264
- version: '0.4'
265
- - !ruby/object:Gem::Dependency
266
- name: simplecov
267
- requirement: !ruby/object:Gem::Requirement
268
- requirements:
269
- - - "~>"
270
- - !ruby/object:Gem::Version
271
- version: '0.21'
272
- type: :development
273
- prerelease: false
274
- version_requirements: !ruby/object:Gem::Requirement
275
- requirements:
276
- - - "~>"
277
- - !ruby/object:Gem::Version
278
- version: '0.21'
279
- - !ruby/object:Gem::Dependency
280
- name: simplecov-cobertura
154
+ name: silent_stream
281
155
  requirement: !ruby/object:Gem::Requirement
282
156
  requirements:
283
157
  - - "~>"
284
158
  - !ruby/object:Gem::Version
285
- version: '1.4'
159
+ version: '1'
286
160
  type: :development
287
161
  prerelease: false
288
162
  version_requirements: !ruby/object:Gem::Requirement
289
163
  requirements:
290
164
  - - "~>"
291
165
  - !ruby/object:Gem::Version
292
- version: '1.4'
166
+ version: '1'
293
167
  - !ruby/object:Gem::Dependency
294
168
  name: sqlite3
295
169
  requirement: !ruby/object:Gem::Requirement
@@ -328,12 +202,14 @@ extra_rdoc_files: []
328
202
  files:
329
203
  - CHANGELOG.md
330
204
  - CODE_OF_CONDUCT.md
205
+ - CONTRIBUTING.md
331
206
  - LICENSE.txt
332
207
  - README.md
333
208
  - lib/shiftable.rb
334
209
  - lib/shiftable/collection.rb
335
210
  - lib/shiftable/mod_signature.rb
336
211
  - lib/shiftable/shifting.rb
212
+ - lib/shiftable/shifting_polymorphic_relation.rb
337
213
  - lib/shiftable/shifting_record.rb
338
214
  - lib/shiftable/shifting_relation.rb
339
215
  - lib/shiftable/single.rb
@@ -353,14 +229,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
353
229
  requirements:
354
230
  - - ">="
355
231
  - !ruby/object:Gem::Version
356
- version: '2.5'
232
+ version: 2.5.0
357
233
  required_rubygems_version: !ruby/object:Gem::Requirement
358
234
  requirements:
359
235
  - - ">="
360
236
  - !ruby/object:Gem::Version
361
237
  version: '0'
362
238
  requirements: []
363
- rubygems_version: 3.2.22
239
+ rubygems_version: 3.0.3.1
364
240
  signing_key:
365
241
  specification_version: 4
366
242
  summary: Easily shift record associations