poly 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 56f2e1989d5977256fd93964e07d62d3a26b696bc469baf3ddf477141eb7f383
4
+ data.tar.gz: 33239362e770c93e2871754f9697763c893960af1e0238aac59ddfd94a8b7399
5
+ SHA512:
6
+ metadata.gz: 190707ca692b512d914adcd3b5cfbdfab79a14730ce14043a6c361642ebb6b193941ae75909e24a6405b9f96c23b746b61d9b66daa77255cb0a9241cc3b3be5a
7
+ data.tar.gz: 6ae262801aaab5a09416080fb36df87a9ee65918698ab08bc12e1e2b6c612c9d9bee288a396842bc89c6b165e6515d8b50f84009f5670b5111a2c1ff2be1735e
data/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-02-18
11
+
12
+ ### Added
13
+
14
+ - `Poly::Migration` — helpers for declaring polymorphic resource/role/owner columns and indexes
15
+ consistently across `create_table`, `change_table`, and `add_column`-style migrations.
16
+ Helpers: `poly_resource`, `poly_role`, `poly_owner`, `poly_resource_index`, `poly_owner_index`.
17
+ - `Poly::Owners` — stamps `owner_type`/`owner_id` (or custom columns) before validation.
18
+ Supports proc/method/object owner resolution, `allow_nil`, and `immutable` options.
19
+ Validates that the named association is a polymorphic `belongs_to` and that the owner
20
+ is a persisted `ActiveRecord::Base` instance.
21
+ - `poly_role immutable: true` option — raises on update if the role has already been set,
22
+ preventing role changes after create.
23
+ - `poly_resource_index` and `poly_owner_index` migration helpers for consistent index naming
24
+ and uniqueness declarations on polymorphic column pairs.
25
+
26
+ ### Changed
27
+
28
+ - `Poly::Label` renamed to `Poly::Role`; the role column (e.g. `commentable_role`) replaces
29
+ the former label column (`commentable_label`).
30
+ - `poly_role` now enforces lowercase alphanumeric/underscore format (`/\A[a-z0-9_]+\z/`)
31
+ and normalises values (strip + downcase) before validation and in `for_role` queries.
32
+ - Ruby requirement raised to `>= 3.2.0`.
33
+
34
+ ### Removed
35
+
36
+ - `Poly::Label` — fully replaced by `Poly::Role`. Update column names and any
37
+ `for_label` / `poly_label` references to `for_role` / `poly_role` accordingly.
38
+
39
+ ## [0.2.0] - 2024
40
+
41
+ ### Added
42
+
43
+ - `Poly::Role` (originally shipped as `Poly::Label`) — role validation, normalization,
44
+ and `for_role` scope for polymorphic associations.
45
+
46
+ ## [0.1.0] - 2024
47
+
48
+ ### Added
49
+
50
+ - `Poly::Joins` — type-safe polymorphic `INNER JOIN` generation via `define_polymorphic_joins!`.
51
+ Creates methods like `joins_commentable(ClassName)` that validate the reverse
52
+ `has_many`/`has_one` association before building the join SQL.
53
+
54
+ [Unreleased]: https://github.com/leewhittaker/poly/compare/v1.0.0...HEAD
55
+ [1.0.0]: https://github.com/leewhittaker/poly/compare/v0.2.0...v1.0.0
56
+ [0.2.0]: https://github.com/leewhittaker/poly/compare/v0.1.0...v0.2.0
57
+ [0.1.0]: https://github.com/leewhittaker/poly/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Lee Whittaker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # Poly
2
+
3
+ Type-safe joins, role identity, and owner identity for polymorphic `belongs_to` associations in Rails.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'poly'
11
+ ```
12
+
13
+ Then run `bundle install`.
14
+
15
+ ## Requirements
16
+
17
+ - Ruby >= 3.2
18
+ - ActiveRecord >= 7.1
19
+
20
+ ## Usage
21
+
22
+ ### Poly::Joins
23
+
24
+ Generates type-safe `INNER JOIN` methods for polymorphic associations. Include the module in a model that has a polymorphic `belongs_to`, and it will define a `joins_<association>` class method for each one.
25
+
26
+ ```ruby
27
+ class Comment < ApplicationRecord
28
+ belongs_to :commentable, polymorphic: true
29
+
30
+ include Poly::Joins
31
+ end
32
+
33
+ class Post < ApplicationRecord
34
+ has_many :comments, as: :commentable
35
+ end
36
+
37
+ class User < ApplicationRecord
38
+ has_many :comments, as: :commentable
39
+ end
40
+ ```
41
+
42
+ Now you can join through the polymorphic association by passing the target class:
43
+
44
+ ```ruby
45
+ # Join comments to the posts table
46
+ Comment.joins_commentable(Post)
47
+ # => SELECT "comments".* FROM "comments"
48
+ # INNER JOIN "posts"
49
+ # ON "comments"."commentable_id" = "posts"."id"
50
+ # AND "comments"."commentable_type" = 'Post'
51
+
52
+ # Chainable with other scopes
53
+ Comment.joins_commentable(Post).where(posts: { title: 'Hello' })
54
+
55
+ # Join to a different target type
56
+ Comment.joins_commentable(User).where(users: { name: 'Lee' })
57
+ ```
58
+
59
+ **Safety:** The target class must declare the reverse association (`has_many` or `has_one` with `as: :commentable`). If it doesn't, a `PolymorphicJoinError` is raised:
60
+
61
+ ```ruby
62
+ Comment.joins_commentable(Unrelated)
63
+ # => PolymorphicJoinError: Unrelated must declare has_one/has_many as: :commentable
64
+ ```
65
+
66
+ ### Poly::Role
67
+
68
+ Adds a validated role column to a polymorphic association. This is useful when a single polymorphic relationship needs to distinguish between different roles or categories.
69
+
70
+ Your table needs a `<association>_role` string column:
71
+
72
+ ```ruby
73
+ create_table :taggings do |t|
74
+ t.references :taggable, polymorphic: true, null: false
75
+ t.string :taggable_role, null: false
76
+ t.timestamps
77
+ end
78
+
79
+ # Index: composite on (taggable_type, taggable_id, taggable_role) if uniqueness is required
80
+ add_index :taggings, [:taggable_type, :taggable_id, :taggable_role], unique: true
81
+ ```
82
+
83
+ Then include the module and declare the role-enabled association:
84
+
85
+ ```ruby
86
+ class Tagging < ApplicationRecord
87
+ belongs_to :taggable, polymorphic: true
88
+
89
+ include Poly::Role
90
+
91
+ poly_role :taggable
92
+ # optionally:
93
+ # poly_role :taggable, max_length: 128
94
+ # poly_role :taggable, immutable: true
95
+ end
96
+ ```
97
+
98
+ This gives you:
99
+
100
+ - **Normalization** — roles are stripped and downcased before validation and before `for_role` queries
101
+ - **Validation** — roles must match `/\A[a-z0-9_]+\z/` and be at most 64 characters (configurable via `max_length:`)
102
+ - **Scope** — `for_role` queries by role, normalizing the input automatically
103
+ - **Immutability** — `immutable: true` adds an `on: :update` validation that prevents role changes after create
104
+
105
+ ```ruby
106
+ tagging = Tagging.new(taggable: post, taggable_role: ' Primary ')
107
+ tagging.valid?
108
+ tagging.taggable_role # => "primary"
109
+
110
+ Tagging.for_role(' PRIMARY ')
111
+ # => normalizes to 'primary' before querying
112
+ ```
113
+
114
+ ### Poly::Owners
115
+
116
+ Stamps `owner_type`/`owner_id` columns before validation. Useful for recording data ownership at write time without coupling the model to tenancy or policy logic.
117
+
118
+ Your table needs `owner_type` and `owner_id` columns (in addition to your polymorphic resource columns):
119
+
120
+ ```ruby
121
+ create_table :coins do |t|
122
+ t.references :ledger, null: false
123
+ t.references :resource, polymorphic: true, null: false
124
+ t.string :resource_role, null: false
125
+ t.string :owner_type
126
+ t.integer :owner_id
127
+ t.timestamps
128
+ end
129
+
130
+ # Index: always composite — never index owner_type and owner_id separately
131
+ add_index :coins, [:owner_type, :owner_id]
132
+ ```
133
+
134
+ Then declare how the owner should be resolved:
135
+
136
+ ```ruby
137
+ class Coin < ApplicationRecord
138
+ belongs_to :ledger
139
+ belongs_to :resource, polymorphic: true
140
+
141
+ include Poly::Owners
142
+
143
+ poly_owner :resource, owner: -> { ledger&.account }
144
+ # optionally:
145
+ # poly_owner :resource, owner: -> { ledger&.account }, allow_nil: false
146
+ # poly_owner :resource, owner: -> { ledger&.account }, immutable: true
147
+ end
148
+ ```
149
+
150
+ **`owner` resolution** — can be a `Proc` (evaluated in instance context), a `Symbol`/`String` (method name called on the record), or a direct `ActiveRecord::Base` instance. The owner must be persisted; an `ArgumentError` is raised otherwise.
151
+
152
+ **Options:**
153
+
154
+ | Option | Default | Description |
155
+ |---|---|---|
156
+ | `type_column:` | `:owner_type` | Column to store the owner class name |
157
+ | `id_column:` | `:owner_id` | Column to store the owner id |
158
+ | `allow_nil:` | `true` | When `false`, raises if the owner resolves to `nil` |
159
+ | `immutable:` | `false` | When `true`, prevents owner changes after create via `on: :update` validation |
160
+
161
+ ### Poly::Migration
162
+
163
+ Adds migration helpers so polymorphic resource/role/owner columns are declared consistently.
164
+
165
+ Use it in your migration base class:
166
+
167
+ ```ruby
168
+ class ApplicationMigration < ActiveRecord::Migration[7.1]
169
+ include Poly::Migration
170
+ end
171
+ ```
172
+
173
+ Supported styles:
174
+
175
+ - `create_table` / `change_table` via a table builder (`t`)
176
+ - direct existing-table operations via `add_column` style (pass table name)
177
+
178
+ #### Create Table / Change Table
179
+
180
+ ```ruby
181
+ class CreateCoins < ApplicationMigration
182
+ def change
183
+ create_table :coins do |t|
184
+ poly_resource t, :resource, null: false
185
+ poly_role t, :resource, null: false
186
+ poly_owner t, null: false
187
+ t.timestamps
188
+ end
189
+
190
+ poly_resource_index :coins, :resource
191
+ poly_owner_index :coins
192
+ end
193
+ end
194
+ ```
195
+
196
+ #### Existing Table (add_column style)
197
+
198
+ ```ruby
199
+ class AddPolyColumnsToCoins < ApplicationMigration
200
+ def change
201
+ poly_resource :coins, :resource, null: false
202
+ poly_role :coins, :resource, null: false
203
+ poly_owner :coins, null: false
204
+
205
+ poly_resource_index :coins, :resource
206
+ poly_owner_index :coins
207
+ end
208
+ end
209
+ ```
210
+
211
+ #### Helper Reference
212
+
213
+ | Helper | Purpose |
214
+ |---|---|
215
+ | `poly_resource(table_or_builder, name, null: true, id_type: :string)` | Adds `<name>_type` and `<name>_id` |
216
+ | `poly_role(table_or_builder, name, null: true)` | Adds `<name>_role` |
217
+ | `poly_owner(table_or_builder, type_column: :owner_type, id_column: :owner_id, id_type: :string, null: true)` | Adds owner type/id columns |
218
+ | `poly_resource_index(table, name, unique: false)` | Adds index on `<name>_type`, `<name>_id` |
219
+ | `poly_owner_index(table, type_column: :owner_type, id_column: :owner_id, unique: false)` | Adds index on owner columns |
220
+
221
+ `id_type` defaults to `:string` so owner/resource IDs can store bigint, UUID, ULID, or other identifier formats consistently.
222
+
223
+ ## Development
224
+
225
+ ```bash
226
+ bundle install # Install dependencies
227
+ bundle exec rspec # Run tests
228
+ bundle exec rubocop # Lint
229
+ COVERAGE=true bundle exec rspec # Run tests with coverage report
230
+ ```
231
+
232
+ ## License
233
+
234
+ Released under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
data/lib/poly/joins.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poly::Joins
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ define_polymorphic_joins!
8
+ end
9
+
10
+ class_methods do
11
+ def define_polymorphic_joins!
12
+ reflect_on_all_associations(:belongs_to).each do |assoc|
13
+ next unless assoc.options[:polymorphic]
14
+
15
+ reflection = assoc
16
+ assoc_name = reflection.name
17
+ method_name = :"joins_#{assoc_name}"
18
+
19
+ next if singleton_class.method_defined?(method_name)
20
+
21
+ define_singleton_method(method_name) do |klass|
22
+ raise PolymorphicJoinError, 'Expected an ActiveRecord model' unless klass <= ActiveRecord::Base
23
+
24
+ base_klass = klass.base_class
25
+
26
+ unless join_allowed?(klass, as: assoc_name)
27
+ raise PolymorphicJoinError,
28
+ "Polymorphic join requires #{base_klass} to declare: " \
29
+ "has_many :#{name.underscore.pluralize}, as: :#{assoc_name}"
30
+ end
31
+
32
+ source = arel_table
33
+ target = base_klass.arel_table
34
+
35
+ joins(
36
+ source.join(target)
37
+ .on(source["#{assoc_name}_id"].eq(target[:id])
38
+ .and(source["#{assoc_name}_type"].eq(base_klass.name)))
39
+ .join_sources
40
+ )
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def join_allowed?(klass, as:)
48
+ klass.reflect_on_all_associations.any? do |assoc|
49
+ next false unless assoc.options[:as] == as
50
+ next false unless %i[has_many has_one].include? assoc.macro
51
+
52
+ assoc.klass == self
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class PolymorphicJoinError < StandardError; end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poly::Migration
4
+ # Table-builder helper (create_table/change_table):
5
+ # poly_resource t, :resource
6
+ # Direct helper (add_column style):
7
+ # poly_resource :coins, :resource
8
+ def poly_resource(table_or_builder, name, null: true, id_type: :string)
9
+ if table_builder?(table_or_builder)
10
+ table_or_builder.references name, polymorphic: true, null: null, type: id_type
11
+ else
12
+ add_column table_or_builder, :"#{name}_type", :string, null: null
13
+ add_column table_or_builder, :"#{name}_id", id_type, null: null
14
+ end
15
+ end
16
+
17
+ # Table-builder helper (create_table/change_table):
18
+ # poly_role t, :resource
19
+ # Direct helper (add_column style):
20
+ # poly_role :coins, :resource
21
+ def poly_role(table_or_builder, name, null: true)
22
+ if table_builder?(table_or_builder)
23
+ table_or_builder.string :"#{name}_role", null: null
24
+ else
25
+ add_column table_or_builder, :"#{name}_role", :string, null: null
26
+ end
27
+ end
28
+
29
+ # Table-builder helper (create_table/change_table):
30
+ # poly_owner t
31
+ # Direct helper (add_column style):
32
+ # poly_owner :coins
33
+ def poly_owner(table_or_builder, type_column: :owner_type, id_column: :owner_id, id_type: :string, null: true)
34
+ if table_builder?(table_or_builder)
35
+ table_or_builder.string type_column, null: null
36
+ table_or_builder.public_send(id_type, id_column, null: null)
37
+ else
38
+ add_column table_or_builder, type_column, :string, null: null
39
+ add_column table_or_builder, id_column, id_type, null: null
40
+ end
41
+ end
42
+
43
+ def poly_resource_index(table, name, unique: false)
44
+ add_index table, [:"#{name}_type", :"#{name}_id"], unique: unique
45
+ end
46
+
47
+ def poly_owner_index(table, type_column: :owner_type, id_column: :owner_id, unique: false)
48
+ add_index table, [type_column, id_column], unique: unique
49
+ end
50
+
51
+ private
52
+
53
+ def table_builder?(value)
54
+ value.respond_to?(:references)
55
+ end
56
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poly::Owners
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ # Declares owner columns populated before validation.
8
+ # poly_owner :resource, owner: -> { ledger.account }
9
+ # poly_owner :resource, owner: :account
10
+ def poly_owner(assoc_name, owner:, type_column: :owner_type, id_column: :owner_id,
11
+ allow_nil: true, immutable: false)
12
+ raise ArgumentError, 'owner is required' if owner.nil?
13
+
14
+ assoc = reflect_on_association(assoc_name.to_sym)
15
+ unless assoc&.macro == :belongs_to && assoc.options[:polymorphic]
16
+ raise ArgumentError, "#{name} must declare belongs_to :#{assoc_name}, polymorphic: true"
17
+ end
18
+
19
+ before_validation { Poly::Owners.apply_owner(self, owner, type_column, id_column, allow_nil: allow_nil) }
20
+ poly_owner_immutability!(type_column, id_column) if immutable
21
+ end
22
+
23
+ private
24
+
25
+ def poly_owner_immutability!(type_column, id_column)
26
+ validate(on: :update) do
27
+ if will_save_change_to_attribute?(type_column) || will_save_change_to_attribute?(id_column)
28
+ errors.add(:base, 'owner cannot be changed once set')
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.apply_owner(record, owner, type_column, id_column, allow_nil: true)
35
+ resolved = resolve_owner(record, owner)
36
+ if resolved.nil?
37
+ assign_nil_owner(record, type_column, id_column, allow_nil: allow_nil)
38
+ elsif resolved.is_a?(ActiveRecord::Base)
39
+ raise ArgumentError, 'owner must be persisted' unless resolved.persisted?
40
+
41
+ record.public_send(:"#{type_column}=", resolved.class.base_class.name)
42
+ record.public_send(:"#{id_column}=", resolved.id)
43
+ else
44
+ raise ArgumentError, "owner must resolve to an ActiveRecord::Base, got #{resolved.class}"
45
+ end
46
+ end
47
+
48
+ def self.assign_nil_owner(record, type_column, id_column, allow_nil:)
49
+ raise ArgumentError, 'owner resolved to nil' unless allow_nil
50
+
51
+ record.public_send(:"#{type_column}=", nil)
52
+ record.public_send(:"#{id_column}=", nil)
53
+ end
54
+
55
+ def self.resolve_owner(record, owner)
56
+ case owner
57
+ when Proc
58
+ record.instance_exec(&owner)
59
+ when Symbol, String
60
+ record.public_send(owner)
61
+ else
62
+ owner
63
+ end
64
+ end
65
+ end
data/lib/poly/role.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poly::Role
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ # Declares a role column on a polymorphic belongs_to.
8
+ # poly_role :schedulable -> expects schedulable_role column
9
+ # poly_role :resource -> expects resource_role column
10
+ def poly_role(assoc_name, max_length: 64, immutable: false)
11
+ role_col = :"#{assoc_name}_role"
12
+
13
+ validates role_col,
14
+ presence: true,
15
+ format: { with: /\A[a-z0-9_]+\z/ },
16
+ length: { maximum: max_length }
17
+
18
+ before_validation { public_send(:"#{role_col}=", public_send(role_col).to_s.strip.downcase.presence) }
19
+
20
+ scope :for_role, ->(role) { where(role_col => role.to_s.strip.downcase) }
21
+ poly_role_immutability!(role_col) if immutable
22
+ end
23
+
24
+ private
25
+
26
+ def poly_role_immutability!(role_col)
27
+ validate(on: :update) do
28
+ errors.add(role_col, 'cannot be changed once set') if will_save_change_to_attribute?(role_col)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poly
4
+ VERSION = '1.0.0'
5
+ end
data/lib/poly.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'poly/version'
5
+ require 'poly/joins'
6
+ require 'poly/role'
7
+ require 'poly/owners'
8
+ require 'poly/migration'
9
+
10
+ module Poly
11
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: poly
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lee Whittaker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.1'
41
+ description: Type-safe joins and role identity for polymorphic belongs_to associations.
42
+ email:
43
+ - lee@whittakertech.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - lib/poly.rb
53
+ - lib/poly/joins.rb
54
+ - lib/poly/migration.rb
55
+ - lib/poly/owners.rb
56
+ - lib/poly/role.rb
57
+ - lib/poly/version.rb
58
+ homepage: https://github.com/leewhittaker/poly
59
+ licenses:
60
+ - MIT
61
+ metadata:
62
+ homepage_uri: https://github.com/leewhittaker/poly
63
+ source_code_uri: https://github.com/leewhittaker/poly
64
+ changelog_uri: https://github.com/leewhittaker/poly/blob/main/CHANGELOG.md
65
+ rubygems_mfa_required: 'true'
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.2.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.4.19
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Polymorphic association utilities for ActiveRecord
85
+ test_files: []