From 624013695c3065a1cd3f07d777830a56ccea0f06 Mon Sep 17 00:00:00 2001 From: Mark Oleson Date: Wed, 18 Dec 2024 21:52:45 -0500 Subject: [PATCH] Configurable polymorphic class name resolution (#79) --- CHANGELOG.MD | 18 +++++++++++++++++- gemfiles/rails_6.1.gemfile.lock | 2 +- gemfiles/rails_7.0.gemfile.lock | 2 +- gemfiles/rails_7.1.gemfile.lock | 2 +- gemfiles/rails_7.2.gemfile.lock | 2 +- lib/subroutine.rb | 12 ++++++++++++ lib/subroutine/association_fields.rb | 2 +- lib/subroutine/version.rb | 4 ++-- 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 778e70c..7725882 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,3 +1,16 @@ +# Changelog + +## Subroutine 4.2.0 + +If you are using polymorphic association fields, you can now customize how Subroutine +resolves those class names to a ruby class by setting a global callable/lambda/proc: + +```ruby +::Subroutine.constantize_polymorphic_class_name = ->(class_name) do + class_name.classify.constantize +end +``` + ## Subroutine 4.1.4 Fields using the time/timestamp/datetime caster will now default back to the old behavior, and use a `precision:` option to opt-in to the new behavior introduced in `v4.1.1`. @@ -16,7 +29,7 @@ to string and re-parsing to a Time object. This fixes issues with losing usec pr ## Subroutine 4.1.0 -A field can no opt out of the natural assignment behavior of ActiveSupport::HashWithIndifferentAccess. Top level param groups are still accessible via indifferent access but if a field sets the `bypass_indifferent_assignment` option to `true` the HashWithIndifferentAccess assignment behavior will be bypassed in favor of direct Hash-like assignment. +A field can now opt out of the natural assignment behavior of ActiveSupport::HashWithIndifferentAccess. Top level param groups are still accessible via indifferent access but if a field sets the `bypass_indifferent_assignment` option to `true` the HashWithIndifferentAccess assignment behavior will be bypassed in favor of direct Hash-like assignment. ```ruby class MyOp < Subroutine::Op @@ -37,6 +50,9 @@ The `Subroutine::Fields` module now contains a class_attribute that allows the a Removed all usage of `ungrouped` params and refactored the storage of grouped params. Params are now stored in either the provided group or the defaults group and accessed via provided_params, params, default_params, and params_with_defaults. Grouped params are accessed the same way but with the group name prefixed eg. `my_private_default_params`. +Polymorphic association fields now resolve class names via `klass.camelize.constantize`, +previously was `klass.classify.constantize`. + ## Subroutine 3.0 Add support for Rails 6.1. Drop support for Rails 6.0 and lower. diff --git a/gemfiles/rails_6.1.gemfile.lock b/gemfiles/rails_6.1.gemfile.lock index 3f5d0a7..1465cf2 100644 --- a/gemfiles/rails_6.1.gemfile.lock +++ b/gemfiles/rails_6.1.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - subroutine (4.1.5) + subroutine (4.2.0) activemodel (>= 6.1) activesupport (>= 6.1) diff --git a/gemfiles/rails_7.0.gemfile.lock b/gemfiles/rails_7.0.gemfile.lock index d35fa11..e36b9a9 100644 --- a/gemfiles/rails_7.0.gemfile.lock +++ b/gemfiles/rails_7.0.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - subroutine (4.1.5) + subroutine (4.2.0) activemodel (>= 6.1) activesupport (>= 6.1) diff --git a/gemfiles/rails_7.1.gemfile.lock b/gemfiles/rails_7.1.gemfile.lock index d7a5b28..263de93 100644 --- a/gemfiles/rails_7.1.gemfile.lock +++ b/gemfiles/rails_7.1.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - subroutine (4.1.5) + subroutine (4.2.0) activemodel (>= 6.1) activesupport (>= 6.1) diff --git a/gemfiles/rails_7.2.gemfile.lock b/gemfiles/rails_7.2.gemfile.lock index 21a5ccc..4aa4df0 100644 --- a/gemfiles/rails_7.2.gemfile.lock +++ b/gemfiles/rails_7.2.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - subroutine (4.1.5) + subroutine (4.2.0) activemodel (>= 6.1) activesupport (>= 6.1) diff --git a/lib/subroutine.rb b/lib/subroutine.rb index 69d241b..b7ef1bc 100644 --- a/lib/subroutine.rb +++ b/lib/subroutine.rb @@ -16,6 +16,18 @@ module Subroutine + # Used by polymorphic association fields to resolve the class name to a ruby class + def self.constantize_polymorphic_class_name(class_name) + return @constantize_polymorphic_class_name.call(class_name) if defined?(@constantize_polymorphic_class_name) + + class_name.camelize.constantize + end + + # When you need to customize how a polymorphic class name is resolved, you can set this callable/lambda/proc + def self.constantize_polymorphic_class_name=(callable) + @constantize_polymorphic_class_name = callable + end + def self.include_defaults_in_params=(bool) @include_defaults_in_params = !!bool end diff --git a/lib/subroutine/association_fields.rb b/lib/subroutine/association_fields.rb index 28d96ba..d3fd110 100644 --- a/lib/subroutine/association_fields.rb +++ b/lib/subroutine/association_fields.rb @@ -177,7 +177,7 @@ def fetch_association_instance(config) get_field(config.foreign_type_method) end - klass = klass.camelize.constantize if klass.is_a?(String) + klass = Subroutine.constantize_polymorphic_class_name(klass) if klass.is_a?(String) return nil unless klass foreign_key = config.foreign_key_method diff --git a/lib/subroutine/version.rb b/lib/subroutine/version.rb index 036e5bb..5844982 100644 --- a/lib/subroutine/version.rb +++ b/lib/subroutine/version.rb @@ -3,8 +3,8 @@ module Subroutine MAJOR = 4 - MINOR = 1 - PATCH = 5 + MINOR = 2 + PATCH = 0 PRE = nil VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join(".")