active_record-associated_object 0.8.1 → 0.8.3
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4212b6fcecceb21ae133688c2a3332d29b8c0f5760f5a22a8ba3450037a95b20
|
4
|
+
data.tar.gz: 4b96fde3db82d00a3c94153cb9b212f1091bd0040d7d9771d300f2a0a0147d6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 262b75fdf718925e7772b7c1dd4e49ab6b73f6706be97081433712a4f3efef524b1677194ff5376415fe32e770bfa080afd5b347c2ccf78786517dfe5a21a9fb
|
7
|
+
data.tar.gz: 47c40f2a351cd49fe9bc5e0488d37ce79bcb3d30332b3aabbaff681ef7ac7f937d81d01c60d6b7f8393d3fc28f7fa92fb9b4d5eb99ae607d95d0389deea12764
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -60,7 +60,7 @@ We've fixed this so you don't need to care, but this is what's happening.
|
|
60
60
|
> `has_object` only requires a namespace and an initializer that takes a single argument. The above `Post::Publisher` is perfectly valid as an Associated Object — same goes for `class Post::Publisher < Data.define(:post); end`.
|
61
61
|
|
62
62
|
> [!TIP]
|
63
|
-
> You can pass multiple names too: `has_object :publisher, :classified, :fortification`. I recommend `-[i]er`, `-[i]ed` and `-ion` as the general naming conventions for your Associated Objects.
|
63
|
+
> You can pass multiple names too: `has_object :seats, :entitlements, :publisher, :classified, :fortification`. I recommend `-s`, `-[i]er`, `-[i]ed` and `-ion` as the general naming conventions for your Associated Objects.
|
64
64
|
|
65
65
|
> [!TIP]
|
66
66
|
> Plural Associated Object names are also supported: `Account.has_object :seats` will look up `Account::Seats`.
|
@@ -85,6 +85,28 @@ class Post::Publisher < ActiveRecord::AssociatedObject
|
|
85
85
|
end
|
86
86
|
```
|
87
87
|
|
88
|
+
### See Associated Objects in action
|
89
|
+
|
90
|
+
#### RubyVideo.dev
|
91
|
+
|
92
|
+
The team at https://www.rubyvideo.dev has been using Associated Objects to clarify the boundaries of their Active Records and collaborator Associated Objects.
|
93
|
+
|
94
|
+
See the usage in the source here:
|
95
|
+
|
96
|
+
- [`ActiveRecord::AssociatedObject` instances](https://github.com/search?q=repo%3Aadrienpoly%2Frubyvideo%20ActiveRecord%3A%3AAssociatedObject&type=code)
|
97
|
+
- [`has_object` calls](https://github.com/search?q=repo%3Aadrienpoly%2Frubyvideo+has_object&type=code)
|
98
|
+
|
99
|
+
#### Flipper
|
100
|
+
|
101
|
+
The team at [Flipper](https://www.flippercloud.io) used Associated Objects to help keep their new billing structure clean.
|
102
|
+
|
103
|
+
You can see real life examples in these blog posts:
|
104
|
+
|
105
|
+
- [Organizing Rails Code with ActiveRecord Associated Objects](https://garrettdimon.com/journal/posts/organizing-rails-code-with-activerecord-associated-objects)
|
106
|
+
- [Data Modeling Entitlements and Pricing for SaaS Applications](https://garrettdimon.com/journal/posts/data-modeling-saas-entitlements-and-pricing)
|
107
|
+
|
108
|
+
If your team is using Associated Objects, we're more than happy to feature any write ups here.
|
109
|
+
|
88
110
|
### Use the generator to help write Associated Objects
|
89
111
|
|
90
112
|
To set up the `Post::Publisher` from above, you can call `bin/rails generate associated Post::Publisher`.
|
@@ -229,6 +251,68 @@ class Post::PublisherTest < ActiveSupport::TestCase
|
|
229
251
|
end
|
230
252
|
```
|
231
253
|
|
254
|
+
### Polymorphic Associated Objects
|
255
|
+
|
256
|
+
If you want to share logic between associated objects, you can do so via standard Ruby modules:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
# app/models/pricing.rb
|
260
|
+
module Pricing
|
261
|
+
# If you need to share an `extension` across associated objects you can override `Module::included` like this:
|
262
|
+
def self.included(object) = object.extension do
|
263
|
+
# Add common integration methods onto `Account`/`User` when the module is included.
|
264
|
+
# See the `extension` block in the `Extending` section above for an example.
|
265
|
+
end
|
266
|
+
|
267
|
+
def price_set?
|
268
|
+
# Instead of referring to `account` or `user`, use the `record` method to target either.
|
269
|
+
record.price_cents.positive?
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# app/models/account/pricing.rb
|
274
|
+
class Account::Pricing < ActiveRecord::AssociatedObject
|
275
|
+
include ::Pricing
|
276
|
+
end
|
277
|
+
|
278
|
+
# app/models/user/pricing.rb
|
279
|
+
class User::Pricing < ActiveRecord::AssociatedObject
|
280
|
+
include ::Pricing
|
281
|
+
end
|
282
|
+
```
|
283
|
+
|
284
|
+
Now we can call `account.pricing.price_set?` & `user.pricing.price_set?`.
|
285
|
+
|
286
|
+
> [!NOTE]
|
287
|
+
> Polymorphic Associated Objects are definitely a more advanced topic,
|
288
|
+
> so you need to know your Ruby module hierarchy and how to track what `self` changes to fairly well.
|
289
|
+
|
290
|
+
#### Using `ActiveSupport::Concern` as an alternative
|
291
|
+
|
292
|
+
If you prefer the look of Active Support concerns, here's the equivalent to the above Ruby module:
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
# app/models/pricing.rb
|
296
|
+
module Pricing
|
297
|
+
extend ActiveSupport::Concern
|
298
|
+
|
299
|
+
included do
|
300
|
+
extension do
|
301
|
+
# Add common integration methods onto `Account`/`User` when the concern is included.
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def price_set?
|
306
|
+
# Instead of referring to `account` or `user`, use the `record` method to target either.
|
307
|
+
record.price_cents.positive?
|
308
|
+
end
|
309
|
+
end
|
310
|
+
```
|
311
|
+
|
312
|
+
Active Support concerns have some extra features that standard Ruby modules don't, like support for deeply-nested concerns and `class_methods do`.
|
313
|
+
|
314
|
+
In this case, if you're reaching for those, you're probably building something too intricate and potentially brittle.
|
315
|
+
|
232
316
|
### Active Job integration via GlobalID
|
233
317
|
|
234
318
|
Associated Objects include `GlobalID::Identification` and have automatic Active Job serialization support that looks like this:
|
@@ -6,14 +6,14 @@ class ActiveRecord::AssociatedObject::Railtie < Rails::Railtie
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
initializer "
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
9
|
+
initializer "active_job.performs" do
|
10
|
+
require "active_job/performs"
|
11
|
+
ActiveRecord::AssociatedObject.extend ActiveJob::Performs if defined?(ActiveJob::Performs)
|
12
|
+
rescue LoadError
|
13
|
+
# We haven't bundled active_job-performs, so we're continuing without it.
|
14
|
+
end
|
16
15
|
|
16
|
+
initializer "object_association.setup" do
|
17
17
|
ActiveSupport.on_load :active_record do
|
18
18
|
require "active_record/associated_object/object_association"
|
19
19
|
include ActiveRecord::AssociatedObject::ObjectAssociation
|
@@ -8,6 +8,7 @@ class AssociatedGenerator < Rails::Generators::NamedBase
|
|
8
8
|
|
9
9
|
def connect_associated_object
|
10
10
|
record_file = "#{destination_root}/app/models/#{record_path}.rb"
|
11
|
+
|
11
12
|
raise "Record class '#{record_klass}' does not exist" unless File.exist?(record_file)
|
12
13
|
|
13
14
|
inject_into_class record_file, record_klass do
|
@@ -20,8 +21,8 @@ class AssociatedGenerator < Rails::Generators::NamedBase
|
|
20
21
|
# The `:name` argument can handle model names, but associated object class names aren't singularized.
|
21
22
|
# So these record and associated_object methods prevent that.
|
22
23
|
def record_path = record_klass.downcase.underscore
|
23
|
-
def record_klass = name.deconstantize
|
24
|
+
def record_klass = name.camelize.deconstantize
|
24
25
|
|
25
|
-
def associated_object_path = associated_object_class.
|
26
|
-
def associated_object_class = name.demodulize
|
26
|
+
def associated_object_path = associated_object_class.underscore
|
27
|
+
def associated_object_class = name.camelize.demodulize
|
27
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record-associated_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kasper Timm Hansen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
requirements: []
|
72
|
-
rubygems_version: 3.5.
|
72
|
+
rubygems_version: 3.5.18
|
73
73
|
signing_key:
|
74
74
|
specification_version: 4
|
75
75
|
summary: Associate a Ruby PORO with an Active Record class and have it quack like
|