shiftable 0.2.0 → 0.5.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: 769e7ab8e27a7ea4a5ea0e9ea2b7efeda58865f11a15f6504e0e00f6db8f9ce9
4
- data.tar.gz: 3caa7d81b477be16f016358e6412a300782688632ea640685bdda70e63df6231
3
+ metadata.gz: 678fdd1fc65199227c201ecd4346e4c0019c78747d949ac9b4dcba8b38158c24
4
+ data.tar.gz: 3f527ed0b2e70a2e677b006320b3e667d0512ccfcacc5bb76232129b9f37c6d0
5
5
  SHA512:
6
- metadata.gz: 3055e1d7bb53da73830429a6d942c992aea32991412b3c97a281f5559a151a6ed9cf8f11cad868c83a7c329886f97c75db3cd69d4eb7bef5c8cd7114a9b3e894
7
- data.tar.gz: e060753ad1c7190ca7047fa12040f6799f43d425762414e0201d424f63291f425c7354b7377fb3cc6fbee662ebc328b54fc63debbcc084fba08b2fcadfbac95c
6
+ metadata.gz: c281d086eabfbce50dcd9f3098374656a8c7536b0ce1d5e21d5262c6e205172f90930aa7120a747904d6599313c235adba0bc5487011fa7b5df0e8c90af9157b
7
+ data.tar.gz: 217eb1fb303dbc45ccb962bddbff8c4dc246b20351b1f1f2312b9e4ea8c4db7a90360c089b040f0276e544a38193d01feb1728bd6f3949dfe849509a12d38a64
data/CHANGELOG.md CHANGED
@@ -1,7 +1,47 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.2.0] - 2021-10-24
3
+ ### Added
4
+
5
+
6
+ ### Changed
7
+
8
+
9
+ ### Fixed
10
+
11
+
12
+ ### Removed
13
+
14
+
15
+ ## [0.5.0] - 2021-11-12
16
+ ### Added
17
+
18
+ - Support for Polymorphic associations (see examples in specs or README)
19
+
20
+ ## [0.4.1] - 2021-11-10
21
+ ### Fixed
22
+
23
+ - Documentation typos in README
24
+
25
+ ## [0.4.0] - 2021-10-27
26
+ ### Changed
27
+
28
+ - option :preflight_checks renamed to :precheck
29
+
30
+ ### Added
31
+
32
+ - Even more 100% spec coverage
33
+
34
+ ## [0.3.0] - 2021-10-26
35
+ ### Changed
36
+
37
+ - Internal rewrite to improve maintainability
38
+ - Extreme edge cases involving incorrect configuration will raise slightly different errors.
39
+
40
+ ### Added
41
+
42
+ - Even more 100% spec coverage
4
43
 
44
+ ## [0.2.0] - 2021-10-24
5
45
  ### Changed
6
46
 
7
47
  - option `before_save` is now `before_shift` as originally documented
@@ -13,7 +53,6 @@
13
53
  - Documentation
14
54
 
15
55
  ## [0.1.1] - 2021-10-23
16
-
17
56
  ### Fixed
18
57
 
19
58
  - Github Actions build
@@ -23,7 +62,6 @@
23
62
  - Linting
24
63
 
25
64
  ## [0.1.0] - 2021-10-23
26
-
27
65
  ### Added
28
66
 
29
67
  - Initial release
@@ -31,4 +69,11 @@
31
69
  - 100% test coverage
32
70
 
33
71
  [0.1.0]: https://github.com/pboling/shiftable/releases/tag/v0.1.0
34
- [0.1.1]: https://github.com/pboling/shiftable/releases/tag/v0.1.1
72
+
73
+ [0.1.1]: https://github.com/pboling/shiftable/releases/tag/v0.1.1
74
+
75
+ [0.2.0]: https://github.com/pboling/shiftable/releases/tag/v0.2.0
76
+
77
+ [0.3.0]: https://github.com/pboling/shiftable/releases/tag/v0.3.0
78
+
79
+ [0.4.0]: https://github.com/pboling/shiftable/releases/tag/v0.4.0
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
 
@@ -87,7 +79,7 @@ But how can you accomplish this? If you used the `shiftable` gem, won't take but
87
79
 
88
80
  class Spaceship < ActiveRecord::Base
89
81
  belongs_to :captain
90
- extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, preflight_checks: true,
82
+ extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
91
83
  before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
92
84
  end
93
85
  ```
@@ -100,8 +92,10 @@ class Spaceship < ActiveRecord::Base
100
92
  belongs_to :captain
101
93
 
102
94
  class << self
103
- include Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, preflight_checks: true,
104
- before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
95
+ include Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
96
+ before_shift: lambda { |shifting:, shift_to:, shift_from:|
97
+ shifting.ownership_changes += 1
98
+ }
105
99
  end
106
100
  end
107
101
  ```
@@ -110,7 +104,7 @@ end
110
104
 
111
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.
112
106
 
113
- ### Multiple association on a single class
107
+ ### Multiple associations on a single class
114
108
 
115
109
  What if the captain and the spaceship have a boss... the space
116
110
  federation! And in a run-in with their arch-Nemesis the Plinth-inth,
@@ -120,10 +114,10 @@ all federation spaceships are commandeered! You are ruined!
120
114
 
121
115
  class Spaceship < ActiveRecord::Base
122
116
  belongs_to :space_federation
123
- extend Shiftable::Collection.new belongs_to: :space_federation, has_one: :spaceship,
124
- before_shift: lambda do |shifting_rel:, shift_to:, shift_from:|
125
- shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
126
- end
117
+ extend Shiftable::Collection.new belongs_to: :space_federation, has_many: :spaceships,
118
+ before_shift: lambda { |shifting_rel:, shift_to:, shift_from:|
119
+ shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
120
+ }
127
121
  end
128
122
 
129
123
  class SpaceFederation < ActiveRecord::Base
@@ -135,6 +129,68 @@ class SpaceFederation < ActiveRecord::Base
135
129
  end
136
130
  ```
137
131
 
132
+ ### Polymorphism and has_many through
133
+
134
+ ```ruby
135
+ class SpaceTreaty < ActiveRecord::Base
136
+ has_many :space_treaty_signature
137
+ end
138
+
139
+ class SpaceTreatySignature < ActiveRecord::Base
140
+ belongs_to :space_treaty
141
+ belongs_to :signatory, polymorphic: true
142
+ # When two space federations assimilate (i.e. merge) to form a single larger federation,
143
+ # they become party to (i.e. signatories of) all the treaties that had been signed by either.
144
+ # In practical terms, this means:
145
+ #
146
+ # surviving_federation = SpaceFederation.find(1)
147
+ # assimilated_federation = SpaceFederation.find(2)
148
+ # SpaceTreatySignature.where(
149
+ # signatory_id: assimilated_federation_id,
150
+ # signatory_type: "SpaceFederation"
151
+ # ).update_all(
152
+ # signatory_id: surviving_federation.id
153
+ # )
154
+ extend Shiftable::Collection.new(
155
+ belongs_to: :signatory, has_many: :space_treaty_signature,
156
+ polymorphic_type: "SpaceFederation",
157
+ method_prefix: "space_federation_",
158
+ before_shift: lambda { |shifting_rel:, shift_to:, shift_from:|
159
+ # Each item in shifting_rel is an instance of the class where Shiftable::Collection is defined,
160
+ # in this case: SpaceTreatySignature
161
+ # And each of them has a signatory which is of type "SpaceFederation",
162
+ # because a polymorphic collection only targets one type.
163
+ # shifting_rel.each { |signature| signature.signatory == "SpaceFederation" }
164
+ }
165
+ )
166
+ end
167
+
168
+ class SpaceFederation < ActiveRecord::Base
169
+ has_many :space_treaty_signature, as: :signatory
170
+ has_many :space_treaties, through: :space_treaty_signatures, as: :signatory
171
+ has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
172
+ has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
173
+ def assimilate_from(other_federation)
174
+ SpaceTreatySignature.space_federation_shift_cx(shift_to: self, shift_from: other_federation)
175
+ end
176
+ end
177
+
178
+ # Including Planet and SpaceStation, for completeness of the example as the other "types" of polymorphic signatories
179
+ class Planet < ActiveRecord::Base
180
+ has_many :space_treaty_signature, as: :signatory
181
+ has_many :space_treaties, through: :space_treaty_signatures
182
+ has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
183
+ has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
184
+ end
185
+
186
+ class SpaceStation < ActiveRecord::Base
187
+ has_many :space_treaty_signature, as: :signatory
188
+ has_many :space_treaties, through: :space_treaty_signatures
189
+ has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
190
+ has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
191
+ end
192
+ ```#<--rubocop/md-->#<--rubocop/md-->`
193
+
138
194
  ### Complete example
139
195
 
140
196
  Putting it all together...
@@ -150,28 +206,79 @@ end
150
206
 
151
207
  class Spaceship < ActiveRecord::Base
152
208
  belongs_to :captain
153
- extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, preflight_checks: true,
209
+ extend Shiftable::Single.new belongs_to: :captain, has_one: :spaceship, precheck: true,
154
210
  before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
155
211
 
156
212
  belongs_to :space_federation
157
- extend Shiftable::Collection.new belongs_to: :space_federation, has_one: :spaceship,
158
- before_shift: lambda do |shifting_rel:, shift_to:, shift_from:|
159
- shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
160
- end
213
+ extend Shiftable::Collection.new belongs_to: :space_federation, has_many: :spaceships,
214
+ before_shift: lambda { |shifting_rel:, shift_to:, shift_from:|
215
+ shifting_rel.each { |spaceship| spaceship.federation_changes += 1 }
216
+ }
161
217
  end
162
218
 
163
219
  class SpaceFederation < ActiveRecord::Base
164
220
  has_many :captains
165
221
  has_many :spaceships
222
+ has_many :space_treaty_signature, as: :signatory
223
+ has_many :space_treaties, through: :space_treaty_signatures, as: :signatory
224
+ has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
225
+ has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
226
+
227
+ def assimilate_from(other_federation)
228
+ SpaceTreatySignature.space_federation_shift_cx(shift_to: self, shift_from: other_federation)
229
+ end
166
230
 
167
231
  def all_spaceships_commandeered_by(nemesis_federation)
168
232
  Spaceship.shift_cx(shift_to: nemesis_federation, shift_from: self)
169
233
  end
170
234
  end
235
+ class SpaceTreaty < ActiveRecord::Base
236
+ has_many :space_treaty_signatures
237
+ end
238
+
239
+ class SpaceTreatySignature < ActiveRecord::Base
240
+ belongs_to :space_treaty
241
+ belongs_to :signatory, polymorphic: true
242
+ extend Shiftable::Collection.new(
243
+ belongs_to: :signatory, has_many: :space_treaty_signatures,
244
+ polymorphic_type: "SpaceFederation",
245
+ method_prefix: "space_federation_"
246
+ )
247
+ end
248
+
249
+ # Including Planet and SpaceStation, for completeness of the example as the other "types" of polymorphic signatories
250
+ class Planet < ActiveRecord::Base
251
+ has_many :space_treaty_signatures, as: :signatory
252
+ has_many :space_treaties, through: :space_treaty_signatures
253
+ has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
254
+ has_many :treaty_stations, class_name: "SpaceStation", through: :space_treaty_signatures, as: :signatory
255
+ end
256
+
257
+ class SpaceStation < ActiveRecord::Base
258
+ has_many :space_treaty_signature, as: :signatory
259
+ has_many :space_treaties, through: :space_treaty_signatures
260
+ has_many :treaty_federations, class_name: "SpaceFederation", through: :space_treaty_signatures, as: :signatory
261
+ has_many :treaty_planets, class_name: "Planet", through: :space_treaty_signatures, as: :signatory
262
+ end
171
263
  ```
172
264
 
173
265
  ... stay tuned!
174
266
 
267
+ ## More Information
268
+
269
+ * RubyDoc
270
+ Documentation: [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation]
271
+ * GitHub
272
+ Discussions: [![Discussion](https://img.shields.io/badge/discussions-github-brightgreen.svg?style=flat)][gh_discussions]
273
+ * Live Chat on
274
+ Gitter: [![Join the chat at https://gitter.im/pboling/activerecord-transactionable](https://badges.gitter.im/Join%20Chat.svg)][chat]
275
+ * Maintainer's Blog: [![Blog](https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat)][blogpage]
276
+
277
+ ## Code of Conduct
278
+
279
+ Everyone interacting in the Shiftable project's codebases, issue trackers, chat rooms and mailing lists is expected to
280
+ follow the [code of conduct][conduct].
281
+
175
282
  ## Development
176
283
 
177
284
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can
@@ -183,14 +290,13 @@ push git commits and the created tag, and push the `.gem` file to [rubygems.org]
183
290
 
184
291
  ## Contributing
185
292
 
186
- Bug reports and pull requests are welcome on GitHub at https://github.com/pboling/shiftable. This project is intended to
187
- be a safe, welcoming space for collaboration, and contributors are expected to adhere to
188
- the [code of conduct](https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md).
293
+ See [CONTRIBUTING.md][contributing]
189
294
 
190
- ## Code of Conduct
295
+ ## Contributors
191
296
 
192
- Everyone interacting in the Shiftable project's codebases, issue trackers, chat rooms and mailing lists is expected to
193
- follow the [code of conduct](https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md).
297
+ [![Contributors](https://contrib.rocks/image?repo=pboling/shiftable)]("https://github.com/pboling/shiftable/graphs/contributors")
298
+
299
+ Made with [contributors-img](https://contrib.rocks).
194
300
 
195
301
  ## Versioning
196
302
 
@@ -205,18 +311,36 @@ the [Pessimistic Version Constraint][pvc] with two digits of precision.
205
311
  For example:
206
312
 
207
313
  ```ruby
208
- spec.add_dependency "shiftable", "~> 0.2"
314
+ spec.add_dependency "shiftable", "~> 0.4"
209
315
  ```
210
316
 
317
+ ## Contact
318
+
319
+ Author and maintainer is Peter Boling ([@pboling][gh_sponsors]).
320
+
321
+ Comments are welcome in the [GitHub Discussions][gh_discussions] board.
322
+
323
+ For security-related issues see [SECURITY][security].
324
+
211
325
  ## License
212
326
 
213
327
  The gem is available as open source under the terms of
214
- the [MIT License](https://opensource.org/licenses/MIT) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
215
- .
328
+ the [MIT License][license] [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)][license-ref].
329
+ See [LICENSE][license] for the official [Copyright Notice][copyright-notice-explainer].
216
330
 
217
331
  * Copyright (c) 2021 [Peter H. Boling][peterboling] of [Rails Bling][railsbling]
218
332
 
219
- [license]: LICENSE
333
+ [copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
334
+
335
+ [gh_discussions]: https://github.com/pboling/shiftable/discussions
336
+
337
+ [conduct]: https://github.com/pboling/shiftable/blob/master/CODE_OF_CONDUCT.md
338
+
339
+ [security]: https://github.com/pboling/shiftable/blob/master/SECURITY.md
340
+
341
+ [license]: https://github.com/pboling/shiftable/blob/master/LICENSE.txt
342
+
343
+ [license-ref]: https://opensource.org/licenses/MIT
220
344
 
221
345
  [semver]: http://semver.org/
222
346
 
@@ -238,8 +362,32 @@ the [MIT License](https://opensource.org/licenses/MIT) [![License: MIT](https://
238
362
 
239
363
  [politicme]: https://nationalprogressiveparty.org
240
364
 
241
- [documentation]: http://rdoc.info/github/pboling/shiftable/frames
365
+ [documentation]: https://rubydoc.info/github/pboling/shiftable/main
366
+
367
+ [source]: https://github.com/pboling/shiftable/
368
+
369
+ [actions]: https://github.com/pboling/shiftable/actions
370
+
371
+ [issues]: https://github.com/pboling/shiftable/issues
372
+
373
+ [climate_maintainability]: https://codeclimate.com/github/pboling/shiftable/maintainability
374
+
375
+ [climate_coverage]: https://codeclimate.com/github/pboling/shiftable/test_coverage
376
+
377
+ [codecov_coverage]: https://codecov.io/gh/pboling/shiftable
242
378
 
243
- [homepage]: https://github.com/pboling/shiftable/
379
+ [code_triage]: https://www.codetriage.com/pboling/shiftable
380
+
381
+ [depfu]: https://depfu.com/github/pboling/shiftable?project_id=32594
244
382
 
245
383
  [blogpage]: http://www.railsbling.com/tags/shiftable/
384
+
385
+ [rubygems]: https://rubygems.org/gems/shiftable
386
+
387
+ [chat]: https://gitter.im/pboling/shiftable?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
388
+
389
+ [maintenancee_policy]: https://guides.rubyonrails.org/maintenance_policy.html#security-issues
390
+
391
+ [liberapay_donate]: https://liberapay.com/pboling/donate
392
+
393
+ [gh_sponsors]: https://github.com/sponsors/pboling
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
3
  # Usage:
5
4
  #
6
5
  # class BlasterRounds < ActiveRecord::Base
@@ -15,72 +14,62 @@
15
14
  module Shiftable
16
15
  # Inheriting from Module is a powerful pattern. If you like it checkout the debug_logging gem!
17
16
  class Collection < Module
18
- def initialize(belongs_to:, has_many:, method_prefix: nil, preflight_checks: true, before_shift: nil)
17
+ # associations: belongs_to, has_many
18
+ # options: method_prefix, before_shift
19
+ def initialize(belongs_to:, has_many:, polymorphic: nil, method_prefix: nil, before_shift: nil)
20
+ # Ruby's Module initializer doesn't take any arguments
19
21
  super()
20
- raise ArgumentError, "belongs_to must be a symbol" unless belongs_to.is_a?(Symbol)
21
- raise ArgumentError, "has_many must be a symbol" unless has_many.is_a?(Symbol)
22
-
23
- # For the following, imagine you are a Spaceship Captain, the Spaceship belongs_to you, and it has only one Captain.
24
- # But you have to sell it to your nemesis!
25
- #
26
- # The name of the belongs_to association, defined on the shifting model, e.g. Spaceship
27
- # Normally a camel-cased, symbolized, version of the class name.
28
- # In the case where Spaceship belongs_to: :captain, this is :captain.
29
- @belongs_to = belongs_to
30
-
31
- # The name of the has_one association, defined on the shift_to/shift_from model, e.g. Captain.
32
- # Normally a camel-cased, symbolized, version of the class name.
33
- # In the case where Captain has_one: :spaceship, this is :spaceship.
34
- @has_many = has_many
35
22
 
36
- @method_prefix = method_prefix
37
-
38
- # will prevent the save if it returns false
39
- # allows for any custom logic to be run, such as setting shift_from attributes, prior to the shift is saved.
40
- @before_shift = before_shift
23
+ @signature = ModSignature.new(
24
+ # For the following, imagine you are a Space Federation, each Spaceship in the fleet belongs_to you,
25
+ # i.e. the federation has_many spaceships.
26
+ # But you lose the war, and your nemesis commandeers all your ships!
27
+ associations: {
28
+ # The name of the belongs_to association, defined on the shifting model, e.g. Spaceship
29
+ # Normally a camel-cased, symbolized, version of the class name.
30
+ # In the case where Spaceship belongs_to: :space_federation, this is :space_federation.
31
+ belongs_to: belongs_to.to_s.to_sym,
32
+ # The name of the has_many association, defined on the shift_to/shift_from model, e.g. SpaceFederation.
33
+ # Normally a camel-cased, symbolized, version of the class name.
34
+ # In the case where SpaceFederation has_many: :spaceships, this is :spaceships.
35
+ has_many: has_many.to_s.to_sym
36
+ },
37
+ options: {
38
+ polymorphic: polymorphic,
39
+ method_prefix: method_prefix,
40
+ # will prevent the save if it returns false
41
+ # allows for any custom logic to be run, such as setting attributes, prior to the shift (save).
42
+ before_shift: before_shift
43
+ },
44
+ type: polymorphic ? :pcx : :cx
45
+ )
41
46
  end
42
47
 
48
+ # NOTE: Possible difference in how inheritance works when using extend vs include
49
+ # with Shiftable::Collection.new
43
50
  def extended(base)
44
- shift_cx_modulizer = ShiftCollectionModulizer.to_mod(@belongs_to, @has_many, @method_prefix,
45
- @before_shift)
51
+ shift_cx_modulizer = ShiftCollectionModulizer.to_mod(@signature.add_base(base))
46
52
  base.singleton_class.send(:prepend, shift_cx_modulizer)
47
53
  end
48
54
 
55
+ # NOTE: Possible difference in how inheritance works when using extend vs include
56
+ # with Shiftable::Collection.new
49
57
  def included(base)
50
- shift_cx_modulizer = ShiftCollectionModulizer.to_mod(@belongs_to, @has_many, @method_prefix,
51
- @before_shift)
58
+ shift_cx_modulizer = ShiftCollectionModulizer.to_mod(@signature.add_base(base))
52
59
  base.send(:prepend, shift_cx_modulizer)
53
60
  end
54
61
 
55
62
  # Creates anonymous Ruby Modules, containing dynamically built methods
56
63
  module ShiftCollectionModulizer
57
- def to_mod(belongs_to, has_many, mepr, before_shift)
64
+ def to_mod(signature)
65
+ prefix = signature.method_prefix
66
+ type = signature.type
58
67
  Module.new do
59
- define_method(:"#{mepr}shift_cx_column") do
60
- reflection = reflect_on_association(belongs_to).klass.reflect_on_association(has_many)
61
- reflection.foreign_key
68
+ define_method(:"#{prefix}shift_#{type}_column") do
69
+ signature.send("shift_#{type}_column")
62
70
  end
63
- define_method(:"#{mepr}shift_cx_relation") do |id|
64
- return nil unless id
65
-
66
- where(send("#{mepr}shift_cx_column") => id)
67
- end
68
- define_method(:"#{mepr}shift_cx_safe_relation") do |shift_to:, shift_from:|
69
- return false if shift_from&.id.nil?
70
- return false if shift_to&.id.nil?
71
-
72
- send("#{mepr}shift_cx_relation", shift_from.id)
73
- end
74
- define_method(:"#{mepr}shift_cx") do |shift_to:, shift_from:|
75
- shifting_rel = send("#{mepr}shift_cx_safe_relation", shift_to: shift_to, shift_from: shift_from)
76
- return false unless shifting_rel && shifting_rel.any?
77
-
78
- shifting_rel.each do |shifting|
79
- shifting.send("#{send("#{mepr}shift_cx_column")}=", shift_to.id)
80
- end
81
- before_shift&.call(shifting_rel: shifting_rel, shift_to: shift_to, shift_from: shift_from)
82
- shifting_rel.each(&:save)
83
- shifting_rel
71
+ define_method(:"#{prefix}shift_#{type}") do |shift_to:, shift_from:|
72
+ signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
84
73
  end
85
74
  end
86
75
  end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shiftable
4
+ class ModSignature
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
+ DEFAULT_BEFORE_SHIFT = ->(*_) { true }
8
+ attr_reader :associations, :options, :type, :base
9
+
10
+ # Imagine you are a Spaceship Captain, the Spaceship belongs_to you, and it has only one Captain.
11
+ # But you have to sell it to your nemesis!
12
+ def initialize(associations:, type:, options: {})
13
+ @associations = associations
14
+ @options = options
15
+ @type = type
16
+ # See: https://maximomussini.com/posts/practical-applications-of-the-singleton-class/
17
+ singleton_class.send(:prepend, Object.const_get("Shiftable::ModSignature::#{type.capitalize}Methods", false))
18
+ validate
19
+ end
20
+
21
+ def validate
22
+ raise ArgumentError, "type must be one of: #{VALID_TYPES}, provided: #{type}" if invalid_type?
23
+ raise ArgumentError, "associations must be symbols" if invalid_association_key_type?
24
+ raise ArgumentError, "exactly two distinct associations must be provided" if invalid_number_of_associations?
25
+ end
26
+
27
+ def polymorphic_type
28
+ options.dig(:polymorphic, :type)
29
+ end
30
+
31
+ def polymorphic_as
32
+ options.dig(:polymorphic, :as)
33
+ end
34
+
35
+ def invalid_type?
36
+ !VALID_TYPES.include?(type)
37
+ end
38
+
39
+ def invalid_association_key_type?
40
+ associations.keys.detect { |key| !key.is_a?(Symbol) }
41
+ end
42
+
43
+ def invalid_number_of_associations?
44
+ associations.keys.uniq.length != 2
45
+ end
46
+
47
+ # @note Chainable
48
+ # @return self
49
+ def add_base(base)
50
+ @base = base
51
+ self
52
+ end
53
+
54
+ def has_rel_name
55
+ VALID_ASSOCIATIONS[type][1]
56
+ end
57
+
58
+ def validate_relationships
59
+ bt_reflection = base.reflect_on_association(belongs_to)
60
+ raise ArgumentError, "Unable to find belongs_to: :#{belongs_to} in #{base}" unless bt_reflection
61
+ # We can't validate any further if the reflection is polymorphic
62
+ return true if bt_reflection.polymorphic?
63
+
64
+ klass = bt_reflection.klass
65
+ has_reflection = klass.reflect_on_association(has_rel)
66
+ raise ArgumentError, "Unable to find #{has_rel_name}: :#{has_rel} in #{klass}" unless has_reflection
67
+ end
68
+
69
+ module CxMethods
70
+ def has_rel
71
+ associations[:has_many]
72
+ end
73
+
74
+ def shift_data!(shift_to:, shift_from:)
75
+ validate_relationships
76
+ shifting_rel = ShiftingRelation.new(
77
+ to: shift_to,
78
+ from: shift_from,
79
+ column: shift_column,
80
+ base: base
81
+ )
82
+ shifting_rel.shift do |result|
83
+ before_shift&.call(shifting_rel: result, shift_to: shift_to, shift_from: shift_from)
84
+ end
85
+ end
86
+ end
87
+
88
+ module PcxMethods
89
+ # This method could be defined for parity, but it is never used.
90
+ # def has_rel
91
+ # associations[:has_many]
92
+ # end
93
+
94
+ def shift_data!(shift_to:, shift_from:)
95
+ validate_relationships
96
+ shifting_rel = ShiftingPolymorphicRelation.new(
97
+ to: shift_to,
98
+ from: shift_from,
99
+ column: {
100
+ type: polymorphic_type,
101
+ as: polymorphic_as,
102
+ id_column: shift_pcx_column
103
+ },
104
+ base: base
105
+ )
106
+ shifting_rel.shift do |result|
107
+ before_shift&.call(shifting_rel: result, shift_to: shift_to, shift_from: shift_from)
108
+ end
109
+ end
110
+ end
111
+
112
+ module SgMethods
113
+ def has_rel
114
+ associations[:has_one]
115
+ end
116
+
117
+ # Do not move record if a record already exists (we are shifting a "has_one" association, after all)
118
+ def precheck
119
+ options[:precheck]
120
+ end
121
+
122
+ def shift_data!(shift_to:, shift_from:)
123
+ validate_relationships
124
+ shifting = ShiftingRecord.new(
125
+ to: shift_to,
126
+ from: shift_from,
127
+ column: shift_column,
128
+ base: base
129
+ ) do
130
+ !precheck || !shift_to.send(has_rel)
131
+ end
132
+ shifting.shift do |result|
133
+ before_shift&.call(shifting: result, shift_to: shift_to, shift_from: shift_from)
134
+ end
135
+ end
136
+ end
137
+
138
+ # The name of the belongs_to association, defined on the shifting model, e.g. Spaceship
139
+ # Normally a camel-cased, symbolized, version of the class name.
140
+ # In the case where Spaceship belongs_to: :captain, this is :captain.
141
+ def belongs_to
142
+ associations[:belongs_to]
143
+ end
144
+
145
+ def method_prefix
146
+ options[:method_prefix]
147
+ end
148
+
149
+ # will prevent the save if it returns false
150
+ # allows for any custom logic to be run, such as setting shift_from attributes, prior to the shift is saved.
151
+ def before_shift
152
+ options[:before_shift] || DEFAULT_BEFORE_SHIFT
153
+ end
154
+
155
+ def shift_pcx_column
156
+ "#{polymorphic_as}_id"
157
+ end
158
+
159
+ def shift_column
160
+ reflection = base.reflect_on_association(belongs_to).klass.reflect_on_association(has_rel)
161
+ reflection.foreign_key
162
+ end
163
+
164
+ alias shift_sg_column shift_column
165
+ alias shift_cx_column shift_column
166
+ end
167
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shiftable
4
+ # Gets data to be shifted
5
+ class Shifting
6
+ attr_reader :to, :from, :column, :base, :result, :run_save
7
+
8
+ def initialize(to:, from:, column:, base:)
9
+ @to = to
10
+ @from = from
11
+ @column = column
12
+ @base = base
13
+ validate
14
+ do_query = block_given? ? yield : true
15
+ @result = do_query ? query : nil
16
+ @run_save = true
17
+ end
18
+
19
+ # def found?
20
+ # raise "found? must be defined in a subclass"
21
+ # end
22
+
23
+ # def shift
24
+ # raise "shift must be defined in a subclass"
25
+ # end
26
+
27
+ private
28
+
29
+ def validate
30
+ raise ArgumentError, "shift_to must have an id (primary key) value, but is: #{to&.id}" unless to&.id
31
+ raise ArgumentError, "shift_from must have an id (primary key) value, but is: #{from&.id}" unless from&.id
32
+ end
33
+
34
+ # def query
35
+ # raise "query must be defined in a subclass"
36
+ # end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
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 result if block_given?
32
+ each(&:save) if run_save
33
+ result
34
+ end
35
+
36
+ private
37
+
38
+ def query
39
+ base.where(
40
+ polymorphic_type_column => column[:type],
41
+ polymorphic_id_column => from.id
42
+ )
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shiftable
4
+ # Gets data to be shifted
5
+ class ShiftingRecord < Shifting
6
+ def found?
7
+ !!result
8
+ end
9
+
10
+ # @return true, false
11
+ def shift
12
+ return false unless found?
13
+
14
+ result.send("#{column}=", to.id)
15
+ @run_save = yield result if block_given?
16
+ result.save if run_save
17
+ end
18
+
19
+ private
20
+
21
+ def query
22
+ base.find_by(column => from.id)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shiftable
4
+ # Gets data to be shifted
5
+ class ShiftingRelation < Shifting
6
+ include Enumerable
7
+
8
+ def found?
9
+ result.any?
10
+ end
11
+
12
+ def each(&block)
13
+ result.each(&block)
14
+ end
15
+
16
+ # @return result (once it is shifted)
17
+ def shift
18
+ return false unless found?
19
+
20
+ each do |record|
21
+ record.send("#{column}=", to.id)
22
+ end
23
+ @run_save = yield result if block_given?
24
+ each(&:save) if run_save
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ def query
31
+ base.where(column => from.id)
32
+ end
33
+ end
34
+ end
@@ -13,7 +13,7 @@
13
13
  # extend Shiftable::Single.new(
14
14
  # belongs_to: :captain,
15
15
  # has_one: :spaceship,
16
- # preflight_checks: true,
16
+ # precheck: true,
17
17
  # before_shift: ->(shifting:, shift_to:, shift_from:) { shifting.ownership_changes += 1 }
18
18
  # )
19
19
  # end
@@ -21,77 +21,59 @@
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, preflight_checks: true, before_shift: nil)
24
+ def initialize(belongs_to:, has_one:, method_prefix: nil, precheck: true, before_shift: nil)
25
+ # Ruby's Module initializer doesn't take any arguments
25
26
  super()
26
- raise ArgumentError, "belongs_to must be a symbol" unless belongs_to.is_a?(Symbol)
27
- raise ArgumentError, "has_one must be a symbol" unless has_one.is_a?(Symbol)
28
27
 
29
- # For the following, imagine you are a Spaceship Captain, the Spaceship belongs_to you, and it has only one Captain.
30
- # But you have to sell it to your nemesis!
31
- #
32
- # The name of the belongs_to association, defined on the shifting model, e.g. Spaceship
33
- # Normally a camel-cased, symbolized, version of the class name.
34
- # In the case where Spaceship belongs_to: :captain, this is :captain.
35
- @belongs_to = belongs_to
36
-
37
- # The name of the has_one association, defined on the shift_to/shift_from model, e.g. Captain.
38
- # Normally a camel-cased, symbolized, version of the class name.
39
- # In the case where Captain has_one: :spaceship, this is :spaceship.
40
- @has_one = has_one
41
-
42
- @method_prefix = method_prefix
43
-
44
- # Do not move record if a record already exists (we are shifting a "has_one" association, after all)
45
- @preflight_checks = preflight_checks
46
-
47
- # will prevent the save if it returns false
48
- # allows for any custom logic to be run, such as setting shift_from attributes, prior to the shift is saved.
49
- @before_shift = before_shift
28
+ @signature = ModSignature.new(
29
+ # For the following, imagine you are a Spaceship Captain, the Spaceship belongs_to you, and it has only one Captain.
30
+ # But you have to sell it to your nemesis!
31
+ associations: {
32
+ # The name of the belongs_to association, defined on the shifting model, e.g. Spaceship
33
+ # Normally a camel-cased, symbolized, version of the class name.
34
+ # In the case where Spaceship belongs_to: :captain, this is :captain.
35
+ belongs_to: belongs_to.to_s.to_sym,
36
+ # The name of the has_one association, defined on the shift_to/shift_from model, e.g. Captain.
37
+ # Normally a camel-cased, symbolized, version of the class name.
38
+ # In the case where Captain has_one: :spaceship, this is :spaceship.
39
+ has_one: has_one.to_s.to_sym
40
+ },
41
+ options: {
42
+ # Do not move record if a record already exists (we are shifting a "has_one" association, after all)
43
+ precheck: precheck,
44
+ method_prefix: method_prefix,
45
+ # will prevent the save if it returns false
46
+ # allows for any custom logic to be run, such as setting attributes, prior to the shift (save).
47
+ before_shift: before_shift
48
+ },
49
+ type: :sg
50
+ )
50
51
  end
51
52
 
53
+ # NOTE: Possible difference in how inheritance works when using extend vs include
54
+ # with Shiftable::Single.new
52
55
  def extended(base)
53
- shift_single_modulizer = ShiftSingleModulizer.to_mod(@belongs_to, @has_one, @method_prefix, @preflight_checks,
54
- @before_shift)
56
+ shift_single_modulizer = ShiftSingleModulizer.to_mod(@signature.add_base(base))
55
57
  base.singleton_class.send(:prepend, shift_single_modulizer)
56
58
  end
57
59
 
60
+ # NOTE: Possible difference in how inheritance works when using extend vs include
61
+ # with Shiftable::Single.new
58
62
  def included(base)
59
- shift_single_modulizer = ShiftSingleModulizer.to_mod(@belongs_to, @has_one, @method_prefix, @preflight_checks,
60
- @before_shift)
63
+ shift_single_modulizer = ShiftSingleModulizer.to_mod(@signature.add_base(base))
61
64
  base.send(:prepend, shift_single_modulizer)
62
65
  end
63
66
 
64
67
  # Creates anonymous Ruby Modules, containing dynamically built methods
65
68
  module ShiftSingleModulizer
66
- def to_mod(belongs_to, has_one, mepr, preflight_checks, before_shift)
69
+ def to_mod(signature)
70
+ prefix = signature.method_prefix
67
71
  Module.new do
68
- define_method(:"#{mepr}shift_column") do
69
- reflection = reflect_on_association(belongs_to).klass.reflect_on_association(has_one)
70
- reflection.foreign_key
71
- end
72
- define_method(:"#{mepr}shift_find") do |id|
73
- return nil unless id
74
-
75
- find_by(send("#{mepr}shift_column") => id)
72
+ define_method(:"#{prefix}shift_column") do
73
+ signature.send("shift_#{signature.type}_column")
76
74
  end
77
- define_method(:"#{mepr}shift_safe_find") do |shift_to:, shift_from:|
78
- return false if shift_from&.id.nil?
79
- return false if shift_to&.id.nil?
80
-
81
- if preflight_checks
82
- already_exists = shift_to.send(has_one)
83
- return false if already_exists
84
- end
85
-
86
- send("#{mepr}shift_find", shift_from.id)
87
- end
88
- define_method(:"#{mepr}shift_single") do |shift_to:, shift_from:|
89
- shifting = send("#{mepr}shift_safe_find", shift_to: shift_to, shift_from: shift_from)
90
- return false unless shifting
91
-
92
- shifting.send("#{send("#{mepr}shift_column")}=", shift_to.id)
93
- shifting.save if before_shift.nil? || before_shift.call(shifting: shifting, shift_to: shift_to,
94
- shift_from: shift_from)
75
+ define_method(:"#{prefix}shift_single") do |shift_to:, shift_from:|
76
+ signature.shift_data!(shift_to: shift_to, shift_from: shift_from)
95
77
  end
96
78
  end
97
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shiftable
4
- VERSION = "0.2.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/shiftable.rb CHANGED
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "shiftable/version"
4
- require_relative "shiftable/single"
4
+ require_relative "shiftable/shifting"
5
+ require_relative "shiftable/shifting_record"
6
+ require_relative "shiftable/shifting_polymorphic_relation"
7
+ require_relative "shiftable/shifting_relation"
8
+ require_relative "shiftable/mod_signature"
5
9
  require_relative "shiftable/collection"
10
+ require_relative "shiftable/single"
6
11
 
7
12
  module Shiftable
8
13
  class Error < StandardError; end
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.2.0
4
+ version: 0.5.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-24 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,14 @@ 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
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -109,105 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.10'
111
111
  - !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
112
+ name: rspec-benchmark
211
113
  requirement: !ruby/object:Gem::Requirement
212
114
  requirements:
213
115
  - - "~>"
@@ -221,21 +123,7 @@ dependencies:
221
123
  - !ruby/object:Gem::Version
222
124
  version: '0.6'
223
125
  - !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
126
+ name: rspec-block_is_expected
239
127
  requirement: !ruby/object:Gem::Requirement
240
128
  requirements:
241
129
  - - "~>"
@@ -248,34 +136,6 @@ dependencies:
248
136
  - - "~>"
249
137
  - !ruby/object:Gem::Version
250
138
  version: '1.0'
251
- - !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
139
  - !ruby/object:Gem::Dependency
280
140
  name: sqlite3
281
141
  requirement: !ruby/object:Gem::Requirement
@@ -314,10 +174,16 @@ extra_rdoc_files: []
314
174
  files:
315
175
  - CHANGELOG.md
316
176
  - CODE_OF_CONDUCT.md
177
+ - CONTRIBUTING.md
317
178
  - LICENSE.txt
318
179
  - README.md
319
180
  - lib/shiftable.rb
320
181
  - lib/shiftable/collection.rb
182
+ - lib/shiftable/mod_signature.rb
183
+ - lib/shiftable/shifting.rb
184
+ - lib/shiftable/shifting_polymorphic_relation.rb
185
+ - lib/shiftable/shifting_record.rb
186
+ - lib/shiftable/shifting_relation.rb
321
187
  - lib/shiftable/single.rb
322
188
  - lib/shiftable/version.rb
323
189
  homepage: https://railsbling.com/tags/shiftable
@@ -335,14 +201,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
335
201
  requirements:
336
202
  - - ">="
337
203
  - !ruby/object:Gem::Version
338
- version: '2.5'
204
+ version: 2.5.0
339
205
  required_rubygems_version: !ruby/object:Gem::Requirement
340
206
  requirements:
341
207
  - - ">="
342
208
  - !ruby/object:Gem::Version
343
209
  version: '0'
344
210
  requirements: []
345
- rubygems_version: 3.2.22
211
+ rubygems_version: 3.0.3.1
346
212
  signing_key:
347
213
  specification_version: 4
348
214
  summary: Easily shift record associations