Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f547cd2
handle ruby 3.x keyword arguments delegation
rcugut Jan 6, 2022
46cdaa4
unlock activerecord < 7 dependency
olegantonyan Jan 21, 2022
c01f473
fix non-integer relation
olegantonyan Feb 2, 2022
5c56db4
Merge pull request #1 from olegantonyan/ruby-3-and-rails-7-support
rcugut May 20, 2022
592c057
Ruby 3 and Rails 7 support
rcugut May 20, 2022
03d89f9
Merge pull request #53 from rcugut/ruby-3-support
peerkleio Jun 9, 2023
a74a8ea
Update Actions for Rails v7 and Ruby 3
peerkleio Jun 9, 2023
1ee36f1
Update gitignore to ignore lockfiles
peerkleio Jun 9, 2023
957826d
Fix incorrect format for CI with multiple Rubys
peerkleio Jun 9, 2023
1172e9c
Add Ruby 3 CI exclustions
peerkleio Jun 9, 2023
ef12e69
Bump to v3.2.0
peerkleio Jun 9, 2023
3fc20e8
Merge pull request #58 from clio/ruby-3
peerkleio Jun 9, 2023
9200eb4
Fix gem publish action
peerkleio Jun 9, 2023
302a807
Merge pull request #59 from clio/update-publish-action
peerkleio Jun 9, 2023
498a584
Another pipeline fix for publishing
peerkleio Jun 9, 2023
40e2b27
Merge pull request #60 from clio/update-publish-action
peerkleio Jun 9, 2023
f77fc1e
write attribute with polymorphic integer type
wendy-clio Dec 12, 2023
2164bf3
comments' refactor
wendy-clio Dec 13, 2023
148a2cc
Merge pull request #61 from clio/fix_has_many_has_one_assign_logic
wendy-clio Dec 15, 2023
c9ad377
should still able to set other reflection successfully
wendy-clio Dec 19, 2023
246e6c9
Merge pull request #62 from clio/fix_set_own_attributes_extension
wendy-clio Dec 20, 2023
6f9bfc3
bump the version to 3.2.2
wendy-clio Dec 21, 2023
ebb1d76
Merge pull request #63 from clio/bump_version_to_3.2.2
wendy-clio Dec 21, 2023
6253f67
upgrade rails to 7.2.x version; clean up unsupported rails and ruby v…
wendy-clio Oct 29, 2024
f86162e
Merge pull request #64 from clio/upgrade_rails_to_7_2
wendy-clio Jan 13, 2025
ced3ea0
Rails 8 support (#67)
parsahonarmand99 Jul 25, 2025
c5f3319
feat: support Rails 8.1 in PolymorphicArrayValueExtension
raymasiclat Apr 30, 2026
986cd98
test: add multi-type polymorphic-array spec
raymasiclat Apr 30, 2026
ebe8c81
Merge pull request #68 from clio/raymasiclat/rails-8-1-support
raymasiclat Apr 30, 2026
073022e
Track upstream v3.5.0; keep polymorphic_name guard for plain polymorp…
arman-radicle Jul 23, 2026
3abc8c9
Rebaseline onto upstream clio v3.5.0 (supersedes #1/#2/#3); keep poly…
arman-radicle Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,25 @@ jobs:
fail-fast: false
matrix:
gemfile:
- Gemfile.rails-6.1-stable
- Gemfile.rails-7.0-stable
- Gemfile.rails-7.2-stable
ruby-version: ['3.1', '3.0']
- Gemfile.rails-8.0-stable
- Gemfile.rails-8.1-stable
ruby-version: ['3.0', '3.1', '3.2', '3.3']
exclude:
# Rails 7.2 doesn't work with Ruby 3.0 (requires Ruby 3.1+)
- gemfile: Gemfile.rails-7.2-stable
ruby-version: "3.0"
ruby-version: '3.0'
# Rails 8.0 doesn't work with Ruby 3.0 or 3.1 (requires Ruby 3.2+)
- gemfile: Gemfile.rails-8.0-stable
ruby-version: '3.0'
- gemfile: Gemfile.rails-8.0-stable
ruby-version: '3.1'
# Rails 8.1 requires Ruby 3.2+
- gemfile: Gemfile.rails-8.1-stable
ruby-version: '3.0'
- gemfile: Gemfile.rails-8.1-stable
ruby-version: '3.1'
env:
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}
steps:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v3.5.0 (2026-04-30)

### Added

- Add Rails 8.1 compatibility. Rails 8.1 changed `PredicateBuilder::PolymorphicArrayValue#initialize` from `(associated_table, values)` to `(reflection, values)`, dropping the `@associated_table` ivar. `type_to_ids_mapping` now reads `@reflection` when available and falls back to `@associated_table.send(:reflection)` for older Rails versions.

## v3.2.1 (2023-12-14)

### Fixed
Expand All @@ -26,3 +32,11 @@

- Remove unsupported rails versions(5.0, 5.2, 6.0) and ruby version(2.7)

## v3.4.0 (2024-XX-XX)
Comment thread
arman-radicle marked this conversation as resolved.

### Added

- Add Rails 8.0 compatibility (requires Ruby 3.2+)

### Removed
Comment thread
arman-radicle marked this conversation as resolved.
- Remove unsupported rails versions 6.x
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,37 @@ Lastly, you will need to be careful of any place where you are doing raw SQL que

## Setup

You'll need to have git, Ruby, and MySQL. Then get up and running with a few commands:
You'll need to have git and Ruby. Then get up and running with a few commands:

```bash
$ git clone ...
$ bundle install
$ vim spec/support/database.yml # Update username and password
$ bin/setup
$ bin/setup # Uses SQLite3 for testing (no additional setup required)
$ bundle exec rspec
```

## Database Compatibility

This gem works with any database supported by ActiveRecord (SQLite3, MySQL, PostgreSQL, etc.).
The gem extends ActiveRecord's polymorphic associations and doesn't use database-specific features.

Development and testing uses SQLite3 for simplicity.

## Contributing

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request

# FCM Annotations
Comment thread
arman-radicle marked this conversation as resolved.

This fork of `clio/polymorphic_integer_type` should stay in sync with upstream. As of the Rails 8 upgrade (FCM-8671) it is based on upstream **v3.5.0** with a single intentional difference.

**The only change from the fork** is a guard in `lib/polymorphic_integer_type/belongs_to_polymorphic_association_extension.rb`. Upstream unconditionally rewrites a polymorphic association's stored `_type` to the target's `base_class`, which clobbers a model's custom `polymorphic_name` on plain `polymorphic: true` associations. We scope that rewrite to this gem's integer-type associations only, so plain polymorphic associations respect `polymorphic_name` (the `rma` app relies on this, for example: `Enrollment.polymorphic_name => 'Patient::Segment'`).

Regression coverage is in `spec/fcm_polymorphic_name_guard_spec.rb`, along with the `Country` / `legacy_source` test fixtures.

**When re-syncing with upstream, re-apply the guard after merging.**
51 changes: 25 additions & 26 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,53 +1,52 @@
require "bundler/gem_tasks"
require "yaml"
require "active_record"
# frozen_string_literal: true

require 'bundler/gem_tasks'
require 'yaml'
require 'active_record'
Comment thread
arman-radicle marked this conversation as resolved.

namespace :test do
task :all do
Dir.glob("./gemfiles/Gemfile*").each do |gemfile|
next if gemfile.end_with?(".lock")
Dir.glob('./gemfiles/Gemfile*').each do |gemfile|
next if gemfile.end_with?('.lock')

puts "Running specs for #{Pathname.new(gemfile).basename}"
system("BUNDLE_GEMFILE=#{gemfile} bundle install > /dev/null && BUNDLE_GEMFILE=#{gemfile} bundle exec rspec")
puts ""
puts ''
end
end
end

namespace :db do
database_config = YAML.load(File.open("./spec/support/database.yml"))
admin_database_config = database_config.merge(database: "mysql")
migration_path = File.expand_path("./spec/support/migrations")
database_config = YAML.load(File.open('./spec/support/database.yml'))
migration_path = File.expand_path('./spec/support/migrations')

desc "Create the database"
desc 'Create the database'
task :create do
ActiveRecord::Base.establish_connection(admin_database_config)
puts "Database created."
# SQLite3 creates the database file automatically, just ensure directory exists
db_file = database_config.fetch(:database)
FileUtils.mkdir_p(File.dirname(db_file)) unless File.dirname(db_file) == '.'
puts 'Database ready (SQLite3).'
end

desc "Migrate the database"
desc 'Migrate the database'
task :migrate do
ActiveRecord::Base.establish_connection(database_config)
if defined?(ActiveRecord::MigrationContext)
migration_context = ActiveRecord::MigrationContext.new(migration_path, ActiveRecord::SchemaMigration)
migration_context.migrate
else
ActiveRecord::Migrator.migrate(migration_path)
end
Rake::Task["db:schema"].invoke
puts "Database migrated."
ActiveRecord::MigrationContext.new(migration_path).migrate
Rake::Task['db:schema'].invoke
puts 'Database migrated.'
end

desc "Drop the database"
desc 'Drop the database'
task :drop do
ActiveRecord::Base.establish_connection(admin_database_config)
# For SQLite3, just delete the file
db_file = database_config.fetch(:database)
File.delete(db_file) if File.exist?(db_file)
puts "Database deleted."
puts 'Database deleted.'
end

desc "Reset the database"
desc 'Reset the database'
task reset: [:drop, :create, :migrate]
desc 'Create a db/schema.rb file that is portable against any DB supported by AR'
desc 'Create a db/schema.rb file that is portable against any DB supported by AR'

task :schema do
# Noop to make ActiveRecord happy
Expand Down
8 changes: 0 additions & 8 deletions gemfiles/Gemfile.rails-6.1-stable

This file was deleted.

7 changes: 7 additions & 0 deletions gemfiles/Gemfile.rails-8.0-stable
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

source "https://rubygems.org"

gemspec path: ".."

gem "activerecord", github: "rails/rails", branch: "8-0-stable"
7 changes: 7 additions & 0 deletions gemfiles/Gemfile.rails-8.1-stable
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

source "https://rubygems.org"

gemspec path: ".."

gem "activerecord", github: "rails/rails", branch: "8-1-stable"
1 change: 0 additions & 1 deletion lib/polymorphic_integer_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require "polymorphic_integer_type/extensions"
require "polymorphic_integer_type/mapping"
require "polymorphic_integer_type/module_generator"
require "polymorphic_integer_type/belongs_to_valid_options_extension"
require "polymorphic_integer_type/belongs_to_polymorphic_association_extension"
require "polymorphic_integer_type/activerecord_5_0_0/polymorphic_array_value_extension"
require "polymorphic_integer_type/polymorphic_foreign_association_extension"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ module PolymorphicArrayValueExtension
# end

def type_to_ids_mapping
if ACTIVE_RECORD_VERSION < Gem::Version.new("6.1")
association = @associated_table.send(:association)
# Rails 8.1 changed `PredicateBuilder::PolymorphicArrayValue#initialize`
# from `(associated_table, values)` to `(reflection, values)`, dropping
# the `@associated_table` ivar. Read whichever ivar is available so this
# gem works across Rails 6.1–8.1.
association = if instance_variable_defined?(:@reflection) && @reflection
Comment thread
arman-radicle marked this conversation as resolved.
@reflection
else
association = @associated_table.send(:reflection)
@associated_table.send(:reflection)
end

name = association.name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
module PolymorphicIntegerType
module BelongsToPolymorphicAssociationExtension
private
module ActiveRecord
module Associations
class BelongsToPolymorphicAssociation < BelongsToAssociation
private

if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("6.1")
def replace_keys(record)
super

if reflection.options[:integer_type] || reflection.options[:polymorphic].is_a?(Hash)
owner[reflection.foreign_type] = record.class.base_class unless record.nil?
end
end
else
def replace_keys(record, force: false)
super
return if record.nil?

if reflection.options[:integer_type] || reflection.options[:polymorphic].is_a?(Hash)
owner[reflection.foreign_type] = record.class.base_class unless record.nil?
end
# FCM fork delta -- RE-APPLY after any upstream sync!
#
# This gem *reopens* (does not prepend) `BelongsToPolymorphicAssociation`,
# so the `super` above is BelongsToAssociation#replace_keys, which sets
# the foreign key but NOT the polymorphic type, so this method is
# responsible for the `_type`.
#
# Upstream sets it unconditionally to the target's `base_class`, which
# clobbers a model's custom `polymorphic_name` on plain `polymorphic: true`
# associations.
#
# The rma app relies on custom `polymorphic_name` (e.g.
# Enrollment => 'Patient::Segment'), so use `base_class` ONLY for this
# gem's integer-type associations (detected via the generated
# `<foreign_type>_mapping` class method) and fall back to Rails'
# `polymorphic_name` for plain polymorphic associations.
#
# Regression is tested in spec/fcm_polymorphic_name_guard_spec.rb.
owner[reflection.foreign_type] =
if owner.class.respond_to?("#{reflection.foreign_type}_mapping")
record.class.base_class
else
record.class.polymorphic_name
end
Comment on lines +28 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice, much simpler than the multi-file change that we had before to fix the STI inheritance issue! Checking respond_to?("#{reflection.foreign_type}_mapping") seems like a good signal that the association is using this gem based on this line 👍

end
end
end
end

ActiveRecord::Associations::BelongsToPolymorphicAssociation.prepend(PolymorphicIntegerType::BelongsToPolymorphicAssociationExtension)
11 changes: 0 additions & 11 deletions lib/polymorphic_integer_type/belongs_to_valid_options_extension.rb

This file was deleted.

14 changes: 5 additions & 9 deletions lib/polymorphic_integer_type/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ module ClassMethods

def belongs_to(name, scope = nil, **options)
options = scope if scope.kind_of? Hash
integer_type = options.delete :integer_type
super
if options[:polymorphic] && (options[:integer_type] || options[:polymorphic].is_a?(Hash))
if options[:polymorphic] && (integer_type || options[:polymorphic].is_a?(Hash))
mapping =
case options[:integer_type]
case integer_type
when true then PolymorphicIntegerType::Mapping[name]
when nil then options[:polymorphic]
else
raise ArgumentError, "Unknown integer_type value: #{options[:integer_type].inspect}"
raise ArgumentError, "Unknown integer_type value: #{integer_type.inspect}"
end.dup

foreign_type = reflections[name.to_s].foreign_type
Expand Down Expand Up @@ -117,12 +118,7 @@ def remove_integer_type_and_set_attributes_and_extension(integer_type_values, re
if is_polymorphic_integer
reflection.foreign_integer_type = foreign_integer_type
reflection.integer_type = integer_type

if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("6.1")
ActiveRecord::Associations::Association.prepend(PolymorphicIntegerType::PolymorphicForeignAssociationExtension)
else
ActiveRecord::Associations::ForeignAssociation.prepend(PolymorphicIntegerType::PolymorphicForeignAssociationExtension)
end
ActiveRecord::Associations::ForeignAssociation.prepend(PolymorphicIntegerType::PolymorphicForeignAssociationExtension)
end
end

Expand Down
4 changes: 3 additions & 1 deletion lib/polymorphic_integer_type/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module PolymorphicIntegerType
VERSION = "3.3.0"
VERSION = '3.5.0'
end
35 changes: 19 additions & 16 deletions polymorphic_integer_type.gemspec
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
# frozen_string_literal: true

lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'polymorphic_integer_type/version'

Gem::Specification.new do |spec|
spec.name = "polymorphic_integer_type"
spec.name = 'polymorphic_integer_type'
spec.version = PolymorphicIntegerType::VERSION
spec.authors = ["Kyle d'Oliveira"]
spec.email = ["kyle@goclio.com"]
spec.description = %q{Allows the *_type field in the DB to be an integer rather than a string}
spec.summary = %q{Use integers rather than strings for the _type field}
spec.homepage = ""
spec.license = "MIT"
spec.authors = ['Kyle d\'Oliveira']
spec.email = ['kyle@goclio.com']
spec.description = 'Allows the *_type field in the DB to be an integer rather than a string'
spec.summary = 'Use integers rather than strings for the _type field'
spec.homepage = ''
spec.license = 'MIT'

spec.required_ruby_version = '>= 3.0'

spec.files = `git ls-files -- . ':!.github/'`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.require_paths = ['lib']

spec.add_dependency "activerecord", "< 8"
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "sqlite3"
spec.add_development_dependency "pry-byebug"
spec.add_dependency 'activerecord', '< 9.0'
spec.add_development_dependency 'bundler'
spec.add_development_dependency 'pry-byebug'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec'
spec.add_development_dependency 'sqlite3'
end
Loading