From 05ce69ad9d9ba557afa60d8a5503d306245664d2 Mon Sep 17 00:00:00 2001 From: mrmalvi Date: Mon, 10 Nov 2025 15:42:36 +0530 Subject: [PATCH 01/26] Support rails 7 + --- Appraisals | 5 + README.md | 6 +- gemfiles/rails_7.gemfile | 8 ++ gemfiles/rails_7.gemfile.lock | 116 ++++++++++++++++++ lib/serial_preference.rb | 1 + lib/serial_preference/has_preference_map.rb | 7 +- .../preference_definition.rb | 112 ++++++++--------- lib/serial_preference/safe_yaml_coder.rb | 19 +++ serial_preference.gemspec | 5 +- spec/fixtures/dummy_class.rb | 2 +- spec/spec_helper.rb | 2 + 11 files changed, 220 insertions(+), 63 deletions(-) create mode 100644 gemfiles/rails_7.gemfile create mode 100644 gemfiles/rails_7.gemfile.lock create mode 100644 lib/serial_preference/safe_yaml_coder.rb diff --git a/Appraisals b/Appraisals index 90179a1..3dcddf3 100644 --- a/Appraisals +++ b/Appraisals @@ -25,3 +25,8 @@ appraise "rails-6" do gem "activesupport", "6.0.0" gem "activerecord", "6.0.0" end + +appraise "rails-7" do + gem "activesupport", "~> 7.0.0" + gem "activerecord", "~> 7.0.0" +end \ No newline at end of file diff --git a/README.md b/README.md index 9745e31..2c2d6f5 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ and other niceties. Scratching my own itch. +## Rails 6 & 7 changes + +For boolean fields default value should be given as `"true"` `"false"` with quotes + ## Installation Add this line to your application's Gemfile: @@ -40,7 +44,7 @@ Or install it yourself as: preferences do - preference :taxable data_type: :boolean, required: true + preference :taxable data_type: :boolean, required: true, default: "true" preference :vat_no required: false preference :max_invoice_items data_type: :integer diff --git a/gemfiles/rails_7.gemfile b/gemfiles/rails_7.gemfile new file mode 100644 index 0000000..bceb4d7 --- /dev/null +++ b/gemfiles/rails_7.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.0.0" +gem "activerecord", "~> 7.0.0" + +gemspec path: "../" diff --git a/gemfiles/rails_7.gemfile.lock b/gemfiles/rails_7.gemfile.lock new file mode 100644 index 0000000..cc9f9b6 --- /dev/null +++ b/gemfiles/rails_7.gemfile.lock @@ -0,0 +1,116 @@ +PATH + remote: .. + specs: + serial_preference (1.3.0) + activerecord (>= 6.0.0) + activesupport (>= 6.0.0) + +GEM + remote: https://rubygems.org/ + specs: + actionpack (7.0.4) + actionview (= 7.0.4) + activesupport (= 7.0.4) + rack (~> 2.0, >= 2.2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actionview (7.0.4) + activesupport (= 7.0.4) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activemodel (7.0.4) + activesupport (= 7.0.4) + activerecord (7.0.4) + activemodel (= 7.0.4) + activesupport (= 7.0.4) + activesupport (7.0.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + appraisal (2.4.1) + bundler + rake + thor (>= 0.14.0) + builder (3.2.4) + concurrent-ruby (1.1.10) + crass (1.0.6) + diff-lcs (1.5.0) + erubi (1.12.0) + i18n (1.12.0) + concurrent-ruby (~> 1.0) + loofah (2.19.1) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + method_source (1.0.0) + minitest (5.17.0) + nokogiri (1.14.0-x86_64-linux) + racc (~> 1.4) + racc (1.6.2) + rack (2.2.6) + rack-test (2.0.2) + rack (>= 1.3) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.4.4) + loofah (~> 2.19, >= 2.19.1) + railties (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) + method_source + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) + rake (13.0.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.0) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-rails (6.0.1) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.11) + rspec-expectations (~> 3.11) + rspec-mocks (~> 3.11) + rspec-support (~> 3.11) + rspec-support (3.12.0) + shoulda (4.0.0) + shoulda-context (~> 2.0) + shoulda-matchers (~> 4.0) + shoulda-context (2.0.0) + shoulda-matchers (4.5.1) + activesupport (>= 4.2.0) + sqlite3 (1.6.0-x86_64-linux) + thor (1.2.1) + tzinfo (2.0.5) + concurrent-ruby (~> 1.0) + zeitwerk (2.6.6) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 7.0.0) + activesupport (~> 7.0.0) + appraisal + rspec (>= 3.0.0) + rspec-rails + serial_preference! + shoulda + sqlite3 + +BUNDLED WITH + 2.4.3 diff --git a/lib/serial_preference.rb b/lib/serial_preference.rb index 28adf2e..5e39d1e 100644 --- a/lib/serial_preference.rb +++ b/lib/serial_preference.rb @@ -2,5 +2,6 @@ require 'active_record' require "serial_preference/version" require 'serial_preference/preference_definition' +require 'serial_preference/safe_yaml_coder' require "serial_preference/preferenzer" require "serial_preference/has_preference_map" diff --git a/lib/serial_preference/has_preference_map.rb b/lib/serial_preference/has_preference_map.rb index e255335..aaf0a8e 100644 --- a/lib/serial_preference/has_preference_map.rb +++ b/lib/serial_preference/has_preference_map.rb @@ -1,3 +1,4 @@ +require "rails" module SerialPreference module HasSerialPreferences extend ActiveSupport::Concern @@ -32,7 +33,11 @@ def preference_names private def build_preference_definitions - serialize self._preferences_attribute, Hash + if Gem::Version.new(Rails.version) >= Gem::Version.new("7.1") + serialize self._preferences_attribute, coder: SerialPreference::SafeYamlCoder, type: Hash + else + serialize self._preferences_attribute, Hash + end _preference_map.all_preference_definitions.each do |preference| diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index c165170..5ba7db0 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -1,105 +1,101 @@ +# frozen_string_literal: true + module SerialPreference class PreferenceDefinition - - SUPPORTED_TYPES = [:string,:integer,:decimal,:float,:boolean] + SUPPORTED_TYPES = %i[string integer decimal float boolean].freeze attr_accessor :data_type, :name, :default, :required, :field_type - def initialize(name,*args) + def initialize(name, *args) opts = args.extract_options! - self.name = name.to_s - opts.assert_valid_keys(:data_type,:default,:required,:field_type) - self.data_type = @type = opts[:data_type] || :string - @column = ActiveRecord::ConnectionAdapters::Column.new(name.to_s, opts[:default], column_type(@type)) - self.default = opts[:default] - self.required = !!opts[:required] + opts.assert_valid_keys(:data_type, :default, :required, :field_type) + + self.name = name.to_s + self.data_type = opts[:data_type]&.to_sym || :string + self.default = opts[:default] + self.required = !!opts[:required] self.field_type = opts[:field_type] + + # Use ActiveModel::Type for consistent casting across Rails versions + begin + @type = ActiveModel::Type.lookup(self.data_type) + rescue ArgumentError + @type = ActiveModel::Type::Value.new + end end + # Return the name of the preference def name - @column.name + @name end def default_value - @column.default + @default end def required? - required + @required end def numerical? - [:integer, :float, :decimal].include?(@column.type) + %i[integer float decimal].include?(data_type) end def boolean? - @column.type == :boolean + data_type == :boolean end + # Cast a value using ActiveModel type system def type_cast(value) - v = @column.type_cast(value) - v.nil? ? default_value : v + casted_value = @type.cast(value) + casted_value.nil? ? default_value : casted_value end + # Used for UI field mapping, default to string for numeric fields def field_type @field_type || (numerical? ? :string : data_type) end + # Check if a given value is "truthy" for queries def query(value) - if !(value = type_cast(value)) - false - elsif numerical? - !value.zero? + value = type_cast(value) + return false if value.nil? + + if numerical? + !value.to_f.zero? else !value.blank? end end + # Value coercion used for serialization def value(v) v = v.nil? ? default : v - if !v.nil? - case data_type - when :string, :password - v.to_s - when :integer - v.respond_to?(:to_i) ? v.to_i : nil - when :float, :real - v.respond_to?(:to_f) ? v.to_f : nil - when :boolean - return false if v == 0 - return false if v == "" - return false if v == nil - return false if v.to_s.downcase == "false" - return false if v == "0" - return false if v.to_s.downcase == "no" - !!v - else - nil - end - end - end + return nil if v.nil? - def column_type(type) - if greater_or_equal_rails_42? - case type - when :boolean - ActiveRecord::Type::Boolean.new - when :integer - ActiveRecord::Type::Integer.new - when :float - ActiveRecord::Type::Float.new - when :decimal - ActiveRecord::Type::Decimal.new - else - ActiveRecord::Type::String.new - end + case data_type + when :string, :password + v.to_s + when :integer + v.is_a?(Integer) ? v : v.try(:to_i) + when :float, :real, :decimal + v.is_a?(Numeric) ? v.to_f : v.try(:to_f) + when :boolean + boolean_cast(v) else - type.to_s + v end end - def greater_or_equal_rails_42? - ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) + private + + def boolean_cast(v) + return v if !v + if v.is_a?(String) + return false if v.downcase == "no" + end + + ActiveModel::Type::Boolean.new.cast(v.to_s.downcase) end end end diff --git a/lib/serial_preference/safe_yaml_coder.rb b/lib/serial_preference/safe_yaml_coder.rb new file mode 100644 index 0000000..0bdcf89 --- /dev/null +++ b/lib/serial_preference/safe_yaml_coder.rb @@ -0,0 +1,19 @@ +module SerialPreference + module SafeYamlCoder + def self.dump(obj) + obj = obj.to_h if obj.is_a?(ActiveSupport::HashWithIndifferentAccess) + YAML.dump(obj) + end + + def self.load(yaml) + return {} if yaml.blank? + Psych.safe_load( + yaml, + permitted_classes: [Symbol, ActiveSupport::HashWithIndifferentAccess, Date, Time], + aliases: true + ) || {} + rescue Psych::Exception + {} + end + end +end diff --git a/serial_preference.gemspec b/serial_preference.gemspec index 29fccb0..a322c50 100644 --- a/serial_preference.gemspec +++ b/serial_preference.gemspec @@ -17,10 +17,11 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] - gem.add_runtime_dependency "activesupport", ">= 3.0.0" - gem.add_runtime_dependency "activerecord", ">= 3.0.0" + gem.add_runtime_dependency 'activerecord', '>= 5.0' + gem.add_runtime_dependency 'activesupport', '>= 5.0' gem.add_development_dependency 'sqlite3' + gem.add_development_dependency 'debug' gem.add_development_dependency 'rspec', ">= 3.0.0" gem.add_development_dependency 'rspec-rails' gem.add_development_dependency "shoulda" diff --git a/spec/fixtures/dummy_class.rb b/spec/fixtures/dummy_class.rb index 53e2dc6..22e7cce 100644 --- a/spec/fixtures/dummy_class.rb +++ b/spec/fixtures/dummy_class.rb @@ -1,7 +1,7 @@ class DummyClass < ActiveRecord::Base include SerialPreference::HasSerialPreferences preferences do - preference :taxable, data_type: :boolean, required: true, default: true + preference :taxable, data_type: :boolean, required: true, default: "true" preference :required_number, data_type: :integer, required: :true preference :vat_no, required: false preference :max_invoice_items, data_type: :integer diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ab4e8f8..2b8f025 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,11 +4,13 @@ require 'active_record' require 'yaml' require 'rspec' +require 'debug' require 'shoulda' require "serial_preference/version" require 'serial_preference/preference_definition' require "serial_preference/preferenzer" require "serial_preference/has_preference_map" +require 'serial_preference/safe_yaml_coder' FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures") config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) From 1d78b0c48d8350ae87e9b2004a06e5b87246b203 Mon Sep 17 00:00:00 2001 From: mrmalvi Date: Mon, 10 Nov 2025 16:55:34 +0530 Subject: [PATCH 02/26] update use past defines --- README.md | 2 +- lib/serial_preference/preference_definition.rb | 14 ++++++-------- spec/fixtures/dummy_class.rb | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2c2d6f5..98a8081 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Or install it yourself as: preferences do - preference :taxable data_type: :boolean, required: true, default: "true" + preference :taxable data_type: :boolean, required: true, default: true preference :vat_no required: false preference :max_invoice_items data_type: :integer diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 5ba7db0..2f33c59 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -1,8 +1,6 @@ -# frozen_string_literal: true - module SerialPreference class PreferenceDefinition - SUPPORTED_TYPES = %i[string integer decimal float boolean].freeze + SUPPORTED_TYPES = [:string,:integer,:decimal,:float,:boolean] attr_accessor :data_type, :name, :default, :required, :field_type @@ -30,15 +28,15 @@ def name end def default_value - @default + default end def required? - @required + required end def numerical? - %i[integer float decimal].include?(data_type) + [:integer, :float, :decimal].include?(data_type) end def boolean? @@ -47,8 +45,8 @@ def boolean? # Cast a value using ActiveModel type system def type_cast(value) - casted_value = @type.cast(value) - casted_value.nil? ? default_value : casted_value + v = @type.cast(value) + v.nil? ? default_value : v end # Used for UI field mapping, default to string for numeric fields diff --git a/spec/fixtures/dummy_class.rb b/spec/fixtures/dummy_class.rb index 22e7cce..53e2dc6 100644 --- a/spec/fixtures/dummy_class.rb +++ b/spec/fixtures/dummy_class.rb @@ -1,7 +1,7 @@ class DummyClass < ActiveRecord::Base include SerialPreference::HasSerialPreferences preferences do - preference :taxable, data_type: :boolean, required: true, default: "true" + preference :taxable, data_type: :boolean, required: true, default: true preference :required_number, data_type: :integer, required: :true preference :vat_no, required: false preference :max_invoice_items, data_type: :integer From 3284ab023a7fee18325c67c1427e34047b2bb516 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:31:19 +0530 Subject: [PATCH 03/26] more improve --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 6602ccf..0d185e9 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -17,7 +17,7 @@ def initialize(name,*args) end def default_value - default + type_cast(default) end def required? From be502010857307facb340273741a7dd3bd407cc9 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:20:08 +0530 Subject: [PATCH 04/26] Remove Rails 6 & 7 boolean fields section Removed section on Rails 6 & 7 changes regarding boolean fields. --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 98a8081..b0971db 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,6 @@ and other niceties. Scratching my own itch. -## Rails 6 & 7 changes - -For boolean fields default value should be given as `"true"` `"false"` with quotes - ## Installation Add this line to your application's Gemfile: From 1ae1d2068e1bffe7349f5da50a66a70a8384177b Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:05:17 +0530 Subject: [PATCH 05/26] Update preference_definition.rb --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 0d185e9..d5586cd 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -29,7 +29,7 @@ def numerical? end def boolean? - data_type == :boolean + data_type.type == :boolean end def type_cast(value) From 614405dfb81cce673f437347c2f0a5ec46c19b57 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:06:01 +0530 Subject: [PATCH 06/26] Update boolean? method to use @type_caster --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index d5586cd..6c40dc0 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -29,7 +29,7 @@ def numerical? end def boolean? - data_type.type == :boolean + @type_caster.type == :boolean end def type_cast(value) From 7d87f75469b73f5e2a76c180a6f7ca9ec0741b1b Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:09:06 +0530 Subject: [PATCH 07/26] Update numerical? method to use @type_caster.type --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 6c40dc0..643e664 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -25,7 +25,7 @@ def required? end def numerical? - [:integer, :float, :decimal].include?(data_type) + [:integer, :float, :decimal].include?(@type_caster.type) end def boolean? From 1f80e1cf2b0c6db0504ac90b3f5d12ebdd918c40 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:12:30 +0530 Subject: [PATCH 08/26] Refactor type casting to use @column instead of @type_caster --- lib/serial_preference/preference_definition.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 643e664..4f64e3e 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -10,7 +10,7 @@ def initialize(name,*args) self.name = name.to_s opts.assert_valid_keys(:data_type, :default, :required, :field_type) self.data_type = @type = opts[:data_type] || :string - @type_caster = column_type(@type) + @column = column_type(@type) self.default = opts[:default] self.required = !!opts[:required] self.field_type = opts[:field_type] @@ -25,15 +25,15 @@ def required? end def numerical? - [:integer, :float, :decimal].include?(@type_caster.type) + [:integer, :float, :decimal].include?(@column.type) end def boolean? - @type_caster.type == :boolean + @column.type == :boolean end def type_cast(value) - v = @type_caster.cast(value) + v = @column.cast(value) v.nil? ? default_value : v end From 566f04e4358e53cb9c3818521b2bc5d6b23ea4a6 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Fri, 16 Jan 2026 12:44:01 +0530 Subject: [PATCH 09/26] Modify gem dependencies for compatibility for test Updated gem dependencies to support older versions of ActiveRecord and ActiveSupport. --- gemfiles/rails_7.gemfile.lock | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/gemfiles/rails_7.gemfile.lock b/gemfiles/rails_7.gemfile.lock index cc9f9b6..b513187 100644 --- a/gemfiles/rails_7.gemfile.lock +++ b/gemfiles/rails_7.gemfile.lock @@ -2,8 +2,8 @@ PATH remote: .. specs: serial_preference (1.3.0) - activerecord (>= 6.0.0) - activesupport (>= 6.0.0) + activerecord (>= 3.0.0) + activesupport (>= 3.0.0) GEM remote: https://rubygems.org/ @@ -38,17 +38,35 @@ GEM builder (3.2.4) concurrent-ruby (1.1.10) crass (1.0.6) + date (3.5.1) + debug (1.11.1) + irb (~> 1.10) + reline (>= 0.3.8) diff-lcs (1.5.0) + erb (6.0.1) erubi (1.12.0) i18n (1.12.0) concurrent-ruby (~> 1.0) + io-console (0.8.2) + irb (1.16.0) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) method_source (1.0.0) minitest (5.17.0) + nokogiri (1.14.0-x86_64-darwin) + racc (~> 1.4) nokogiri (1.14.0-x86_64-linux) racc (~> 1.4) + pp (0.6.3) + prettyprint + prettyprint (0.2.0) + psych (5.3.1) + date + stringio racc (1.6.2) rack (2.2.6) rack-test (2.0.2) @@ -66,6 +84,12 @@ GEM thor (~> 1.0) zeitwerk (~> 2.5) rake (13.0.6) + rdoc (7.1.0) + erb + psych (>= 4.0.0) + tsort + reline (0.6.3) + io-console (~> 0.5) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) @@ -93,19 +117,24 @@ GEM shoulda-context (2.0.0) shoulda-matchers (4.5.1) activesupport (>= 4.2.0) + sqlite3 (1.6.0-x86_64-darwin) sqlite3 (1.6.0-x86_64-linux) + stringio (3.2.0) thor (1.2.1) + tsort (0.2.0) tzinfo (2.0.5) concurrent-ruby (~> 1.0) zeitwerk (2.6.6) PLATFORMS + x86_64-darwin-22 x86_64-linux DEPENDENCIES activerecord (~> 7.0.0) activesupport (~> 7.0.0) appraisal + debug rspec (>= 3.0.0) rspec-rails serial_preference! From ed4f6171cd16e2d2d30b0d84f8d062c60c5e4379 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:00:41 +0530 Subject: [PATCH 10/26] Update Rails gems to version 7.0.4 --- gemfiles/{rails_7.gemfile => rails_7.0.4.gemfile} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename gemfiles/{rails_7.gemfile => rails_7.0.4.gemfile} (59%) diff --git a/gemfiles/rails_7.gemfile b/gemfiles/rails_7.0.4.gemfile similarity index 59% rename from gemfiles/rails_7.gemfile rename to gemfiles/rails_7.0.4.gemfile index bceb4d7..c363a4d 100644 --- a/gemfiles/rails_7.gemfile +++ b/gemfiles/rails_7.0.4.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", "~> 7.0.0" -gem "activerecord", "~> 7.0.0" +gem "activesupport", "7.0.4" +gem "activerecord", "7.0.4" gemspec path: "../" From 96736074db590884df04b0add4ad96355678ef18 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:00:58 +0530 Subject: [PATCH 11/26] Update gemfile.lock for Rails 7.0.4 --- gemfiles/{rails_7.gemfile.lock => rails_7.0.4.gemfile.lock} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gemfiles/{rails_7.gemfile.lock => rails_7.0.4.gemfile.lock} (100%) diff --git a/gemfiles/rails_7.gemfile.lock b/gemfiles/rails_7.0.4.gemfile.lock similarity index 100% rename from gemfiles/rails_7.gemfile.lock rename to gemfiles/rails_7.0.4.gemfile.lock From d76d8dc85d1540c2fb0b6af6b0480af9345808ed Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:01:31 +0530 Subject: [PATCH 12/26] Update Rails appraisal to version 7.0.4 --- Appraisals | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Appraisals b/Appraisals index 3dcddf3..94e5c5a 100644 --- a/Appraisals +++ b/Appraisals @@ -26,7 +26,7 @@ appraise "rails-6" do gem "activerecord", "6.0.0" end -appraise "rails-7" do - gem "activesupport", "~> 7.0.0" - gem "activerecord", "~> 7.0.0" -end \ No newline at end of file +appraise "rails-7.0.4" do + gem "activesupport", "7.0.4" + gem "activerecord", "7.0.4" +end From 080b4c2d0fd2a1b1498d5934b538745e361e6ca8 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Sat, 17 Jan 2026 11:57:29 +0530 Subject: [PATCH 13/26] test on 7.2 --- Appraisals | 12 ++ gemfiles/rails_7.0.4.gemfile.lock | 4 +- gemfiles/rails_7.1.gemfile | 9 ++ gemfiles/rails_7.1.gemfile.lock | 164 ++++++++++++++++++++++++++++ gemfiles/rails_7.2.gemfile | 9 ++ gemfiles/rails_7.2.gemfile.lock | 176 ++++++++++++++++++++++++++++++ 6 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 gemfiles/rails_7.1.gemfile create mode 100644 gemfiles/rails_7.1.gemfile.lock create mode 100644 gemfiles/rails_7.2.gemfile create mode 100644 gemfiles/rails_7.2.gemfile.lock diff --git a/Appraisals b/Appraisals index 94e5c5a..5876a44 100644 --- a/Appraisals +++ b/Appraisals @@ -30,3 +30,15 @@ appraise "rails-7.0.4" do gem "activesupport", "7.0.4" gem "activerecord", "7.0.4" end + +appraise "rails-7.1" do + gem "activesupport", "7.1.0" + gem "activerecord", "7.1.0" + gem "sqlite3", "~> 1.4" +end + +appraise "rails-7.2" do + gem "activesupport", "~> 7.2.0" + gem "activerecord", "~> 7.2.0" + gem "sqlite3", "~> 2.0" +end diff --git a/gemfiles/rails_7.0.4.gemfile.lock b/gemfiles/rails_7.0.4.gemfile.lock index b513187..26aa944 100644 --- a/gemfiles/rails_7.0.4.gemfile.lock +++ b/gemfiles/rails_7.0.4.gemfile.lock @@ -131,8 +131,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - activerecord (~> 7.0.0) - activesupport (~> 7.0.0) + activerecord (= 7.0.4) + activesupport (= 7.0.4) appraisal debug rspec (>= 3.0.0) diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile new file mode 100644 index 0000000..136eccc --- /dev/null +++ b/gemfiles/rails_7.1.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "7.1.0" +gem "activerecord", "7.1.0" +gem "sqlite3", "~> 1.4" + +gemspec path: "../" diff --git a/gemfiles/rails_7.1.gemfile.lock b/gemfiles/rails_7.1.gemfile.lock new file mode 100644 index 0000000..d0968c7 --- /dev/null +++ b/gemfiles/rails_7.1.gemfile.lock @@ -0,0 +1,164 @@ +PATH + remote: .. + specs: + serial_preference (1.3.0) + activerecord (>= 3.0.0) + activesupport (>= 3.0.0) + +GEM + remote: https://rubygems.org/ + specs: + actionpack (7.1.0) + actionview (= 7.1.0) + activesupport (= 7.1.0) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actionview (7.1.0) + activesupport (= 7.1.0) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activemodel (7.1.0) + activesupport (= 7.1.0) + activerecord (7.1.0) + activemodel (= 7.1.0) + activesupport (= 7.1.0) + timeout (>= 0.4.0) + activesupport (7.1.0) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + base64 (0.3.0) + bigdecimal (4.0.1) + builder (3.3.0) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + crass (1.0.6) + date (3.5.1) + debug (1.11.1) + irb (~> 1.10) + reline (>= 0.3.8) + diff-lcs (1.6.2) + drb (2.2.3) + erb (6.0.1) + erubi (1.13.1) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + io-console (0.8.2) + irb (1.16.0) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + loofah (2.25.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + minitest (6.0.1) + prism (~> 1.5) + mutex_m (0.3.0) + nokogiri (1.19.0-x86_64-darwin) + racc (~> 1.4) + pp (0.6.3) + prettyprint + prettyprint (0.2.0) + prism (1.8.0) + psych (5.3.1) + date + stringio + racc (1.8.1) + rack (3.2.4) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.3.1) + rack (>= 3) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.1.0) + actionpack (= 7.1.0) + activesupport (= 7.1.0) + irb + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rake (13.3.1) + rdoc (7.1.0) + erb + psych (>= 4.0.0) + tsort + reline (0.6.3) + io-console (~> 0.5) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (7.1.1) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.6) + shoulda (4.0.0) + shoulda-context (~> 2.0) + shoulda-matchers (~> 4.0) + shoulda-context (2.0.0) + shoulda-matchers (4.5.1) + activesupport (>= 4.2.0) + sqlite3 (1.6.0-x86_64-darwin) + stringio (3.2.0) + thor (1.5.0) + timeout (0.6.0) + tsort (0.2.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + zeitwerk (2.7.4) + +PLATFORMS + x86_64-darwin-22 + +DEPENDENCIES + activerecord (= 7.1.0) + activesupport (= 7.1.0) + appraisal + debug + rspec (>= 3.0.0) + rspec-rails + serial_preference! + shoulda + sqlite3 (~> 1.4) + +BUNDLED WITH + 2.3.26 diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile new file mode 100644 index 0000000..0584516 --- /dev/null +++ b/gemfiles/rails_7.2.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.2.0" +gem "activerecord", "~> 7.2.0" +gem "sqlite3", "~> 2.0" + +gemspec path: "../" diff --git a/gemfiles/rails_7.2.gemfile.lock b/gemfiles/rails_7.2.gemfile.lock new file mode 100644 index 0000000..5f9d3ae --- /dev/null +++ b/gemfiles/rails_7.2.gemfile.lock @@ -0,0 +1,176 @@ +PATH + remote: .. + specs: + serial_preference (1.3.0) + activerecord (>= 3.0.0) + activesupport (>= 3.0.0) + +GEM + remote: https://rubygems.org/ + specs: + actionpack (7.2.3) + actionview (= 7.2.3) + activesupport (= 7.2.3) + cgi + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.3) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actionview (7.2.3) + activesupport (= 7.2.3) + builder (~> 3.1) + cgi + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activemodel (7.2.3) + activesupport (= 7.2.3) + activerecord (7.2.3) + activemodel (= 7.2.3) + activesupport (= 7.2.3) + timeout (>= 0.4.0) + activesupport (7.2.3) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.0.1) + builder (3.3.0) + cgi (0.5.1) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + crass (1.0.6) + date (3.5.1) + debug (1.11.1) + irb (~> 1.10) + reline (>= 0.3.8) + diff-lcs (1.6.2) + drb (2.2.3) + erb (6.0.1) + erubi (1.13.1) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + io-console (0.8.2) + irb (1.16.0) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + logger (1.7.0) + loofah (2.25.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + minitest (6.0.1) + prism (~> 1.5) + nokogiri (1.19.0-x86_64-darwin) + racc (~> 1.4) + pp (0.6.3) + prettyprint + prettyprint (0.2.0) + prism (1.8.0) + psych (5.3.1) + date + stringio + racc (1.8.1) + rack (3.2.4) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.3.1) + rack (>= 3) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.2.3) + actionpack (= 7.2.3) + activesupport (= 7.2.3) + cgi + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) + zeitwerk (~> 2.6) + rake (13.3.1) + rdoc (7.1.0) + erb + psych (>= 4.0.0) + tsort + reline (0.6.3) + io-console (~> 0.5) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (8.0.2) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.6) + securerandom (0.4.1) + shoulda (4.0.0) + shoulda-context (~> 2.0) + shoulda-matchers (~> 4.0) + shoulda-context (2.0.0) + shoulda-matchers (4.5.1) + activesupport (>= 4.2.0) + sqlite3 (2.9.0-x86_64-darwin) + stringio (3.2.0) + thor (1.5.0) + timeout (0.6.0) + tsort (0.2.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + useragent (0.16.11) + zeitwerk (2.7.4) + +PLATFORMS + x86_64-darwin-22 + +DEPENDENCIES + activerecord (~> 7.2.0) + activesupport (~> 7.2.0) + appraisal + debug + rspec (>= 3.0.0) + rspec-rails + serial_preference! + shoulda + sqlite3 (~> 2.0) + +BUNDLED WITH + 2.3.26 From 99a5c5855f173890e4aef84b0c4dd1245261489d Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:08:01 +0530 Subject: [PATCH 14/26] Change ActiveRecord to ActiveModel for String type --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index 4f64e3e..c3c9bea 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -75,7 +75,7 @@ def column_type(type) begin ActiveModel::Type.lookup(type) rescue ArgumentError - ActiveRecord::Type::String.new + ActiveModel::Type::String.new end end From 3c540cdb5cff3cef36180cb203e04e813470b931 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:22:30 +0530 Subject: [PATCH 15/26] Modify default_value to use @column.cast Update default_value method to use column casting. --- lib/serial_preference/preference_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index c3c9bea..f948d07 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -17,7 +17,7 @@ def initialize(name,*args) end def default_value - type_cast(default) + @column.cast(default) end def required? From a241803c834da1044f7a0f8a47f6216cf6607ca6 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:24:07 +0530 Subject: [PATCH 16/26] Add tests for boolean behavior and default values --- spec/preference_definition_spec.rb | 114 +++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index c132314..68e46a7 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -179,6 +179,120 @@ end end + context "boolean? behaviour" do + it "should be boolean when data_type is boolean" do + p = described_class.new("whatever",{data_type: :boolean}) + expect(p.boolean?).to be_truthy + end + + it "should not be boolean for non boolean data types" do + [:string, :integer, :float, :decimal, :password, :xyz].each do |dt| + expect(described_class.new("whatever",{data_type: dt}).boolean?).to be_falsey + end + end + end + + context "default_value behaviour" do + it "should return nil when no default is set" do + expect(@blank_pref.default_value).to be_nil + end + + it "should return cast default value for integer" do + p = described_class.new("num",{default: "12", data_type: :integer}) + expect(p.default_value).to eq(12) + end + + it "should return cast default value for boolean" do + p = described_class.new("flag",{default: "yes", data_type: :boolean}) + expect(p.default_value).to be_truthy + end + end + + context "query behaviour" do + it "should return false when value is nil" do + expect(@blank_pref.query(nil)).to be_falsey + end + + it "should return false for blank string" do + p = described_class.new("x",{data_type: :string}) + expect(p.query("")).to be_falsey + end + + it "should return true for non blank string" do + p = described_class.new("x",{data_type: :string}) + expect(p.query("abc")).to be_truthy + end + + it "should return false for zero numeric values" do + p = described_class.new("x",{data_type: :integer}) + expect(p.query(0)).to be_falsey + end + + it "should return true for non zero numeric values" do + p = described_class.new("x",{data_type: :integer}) + expect(p.query(5)).to be_truthy + end + end + + context "column_type behaviour" do + it "should lookup ActiveModel type when supported" do + p = described_class.new("x",{data_type: :integer}) + expect(p.instance_variable_get(:@column).type).to eq(:integer) + end + + it "should fallback to string type when unsupported" do + p = described_class.new("x",{data_type: :unknown}) + expect(p.instance_variable_get(:@column)).to be_a(ActiveModel::Type::String) + end + end + + context "normalize_boolean behaviour" do + before do + @bool = described_class.new("flag",{data_type: :boolean}) + end + + it "should treat 'yes' as true" do + expect(@bool.send(:normalize_boolean, "yes")).to be_truthy + end + + it "should treat 'no' as false" do + expect(@bool.send(:normalize_boolean, "no")).to be_falsey + end + + it "should treat mixed case truthy as true" do + expect(@bool.send(:normalize_boolean, "YeS")).to be_truthy + end + + it "should treat mixed case falsy as false" do + expect(@bool.send(:normalize_boolean, "No")).to be_falsey + end + + it "should treat numeric zero as false" do + expect(@bool.send(:normalize_boolean, 0)).to be_falsey + end + + it "should treat numeric non-zero as true" do + expect(@bool.send(:normalize_boolean, 42)).to be_truthy + end + end + + context "value method edge cases" do + it "should return default when value is nil and default exists" do + p = described_class.new("color",{default: "red"}) + expect(p.value(nil)).to eq("red") + end + + it "should handle boolean strings properly" do + p = described_class.new("flag",{data_type: :boolean}) + expect(p.value("true")).to be_truthy + expect(p.value("false")).to be_falsey + end + + it "should not coerce non boolean types into boolean" do + p = described_class.new("mode",{data_type: :string}) + expect(p.value("false")).to eq("false") + end + end end end From d1f75578dc0fe3040cdf513d2a43f5a9b30571d5 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:39:12 +0530 Subject: [PATCH 17/26] Refactor preference_definition_spec by removing tests Removed tests for column_type behavior and updated boolean normalization tests. --- spec/preference_definition_spec.rb | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index 68e46a7..d7be29f 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -234,18 +234,6 @@ end end - context "column_type behaviour" do - it "should lookup ActiveModel type when supported" do - p = described_class.new("x",{data_type: :integer}) - expect(p.instance_variable_get(:@column).type).to eq(:integer) - end - - it "should fallback to string type when unsupported" do - p = described_class.new("x",{data_type: :unknown}) - expect(p.instance_variable_get(:@column)).to be_a(ActiveModel::Type::String) - end - end - context "normalize_boolean behaviour" do before do @bool = described_class.new("flag",{data_type: :boolean}) @@ -260,7 +248,7 @@ end it "should treat mixed case truthy as true" do - expect(@bool.send(:normalize_boolean, "YeS")).to be_truthy + expect(@bool.send(:normalize_boolean, "YES")).to be_truthy end it "should treat mixed case falsy as false" do @@ -271,8 +259,8 @@ expect(@bool.send(:normalize_boolean, 0)).to be_falsey end - it "should treat numeric non-zero as true" do - expect(@bool.send(:normalize_boolean, 42)).to be_truthy + it "should treat numeric '0' as false" do + expect(@bool.send(:normalize_boolean, '0')).to be_falsey end end From e8d6137efe8e46768261972906a2a73f8adbd12c Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:41:42 +0530 Subject: [PATCH 18/26] Update boolean normalization tests for case sensitivity --- spec/preference_definition_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index d7be29f..c6a2f15 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -247,11 +247,11 @@ expect(@bool.send(:normalize_boolean, "no")).to be_falsey end - it "should treat mixed case truthy as true" do + it "should treat 'YES' as true" do expect(@bool.send(:normalize_boolean, "YES")).to be_truthy end - it "should treat mixed case falsy as false" do + it "should treat 'No' as false" do expect(@bool.send(:normalize_boolean, "No")).to be_falsey end From f4ec95fbc8ea0a98cbeb02ebeeae9120169aeb65 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:55:11 +0530 Subject: [PATCH 19/26] Refactor boolean default value and remove tests Updated boolean default value to use true instead of 'yes'. Removed several tests related to query behavior. --- spec/preference_definition_spec.rb | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index c6a2f15..8591b0a 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -203,37 +203,11 @@ end it "should return cast default value for boolean" do - p = described_class.new("flag",{default: "yes", data_type: :boolean}) + p = described_class.new("flag",{default: true, data_type: :boolean}) expect(p.default_value).to be_truthy end end - context "query behaviour" do - it "should return false when value is nil" do - expect(@blank_pref.query(nil)).to be_falsey - end - - it "should return false for blank string" do - p = described_class.new("x",{data_type: :string}) - expect(p.query("")).to be_falsey - end - - it "should return true for non blank string" do - p = described_class.new("x",{data_type: :string}) - expect(p.query("abc")).to be_truthy - end - - it "should return false for zero numeric values" do - p = described_class.new("x",{data_type: :integer}) - expect(p.query(0)).to be_falsey - end - - it "should return true for non zero numeric values" do - p = described_class.new("x",{data_type: :integer}) - expect(p.query(5)).to be_truthy - end - end - context "normalize_boolean behaviour" do before do @bool = described_class.new("flag",{data_type: :boolean}) From 6c447c8b4ca3ab63ecc428951c2a4ed919fb1690 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar <76098794+mrmalvi@users.noreply.github.com> Date: Sat, 17 Jan 2026 14:23:33 +0530 Subject: [PATCH 20/26] Update preference_definition_spec.rb --- spec/preference_definition_spec.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index 8591b0a..4e1f5a6 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -221,14 +221,6 @@ expect(@bool.send(:normalize_boolean, "no")).to be_falsey end - it "should treat 'YES' as true" do - expect(@bool.send(:normalize_boolean, "YES")).to be_truthy - end - - it "should treat 'No' as false" do - expect(@bool.send(:normalize_boolean, "No")).to be_falsey - end - it "should treat numeric zero as false" do expect(@bool.send(:normalize_boolean, 0)).to be_falsey end From 84b6a449197f0b19ec4875577895f414f170023f Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Wed, 21 Jan 2026 18:02:46 +0530 Subject: [PATCH 21/26] handle symbol in yaml for current required only --- lib/serial_preference/safe_yaml_coder.rb | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/serial_preference/safe_yaml_coder.rb b/lib/serial_preference/safe_yaml_coder.rb index 3cccc47..db99165 100644 --- a/lib/serial_preference/safe_yaml_coder.rb +++ b/lib/serial_preference/safe_yaml_coder.rb @@ -1,28 +1,14 @@ module SerialPreference module SafeYamlCoder - SAFE_CLASSES = [ - Symbol, - Date, - Time, - DateTime, - BigDecimal, - ActiveSupport::TimeWithZone, - ActiveSupport::HashWithIndifferentAccess - ].freeze def self.dump(obj) YAML.dump(obj) end def self.load(yaml) - return {} if yaml.blank? - Psych.safe_load( - yaml, - permitted_classes: SAFE_CLASSES, - aliases: true - ) || {} + Psych.safe_load(yaml, permitted_classes: [Symbol], aliases: true) rescue Psych::Exception - {} + YAML.load(yaml) end end end From c3bab0d70603f534d910ca9e8f731dd1ac26671f Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Tue, 10 Feb 2026 14:56:16 +0530 Subject: [PATCH 22/26] Rails.version --- lib/serial_preference/has_preference_map.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/serial_preference/has_preference_map.rb b/lib/serial_preference/has_preference_map.rb index aaf0a8e..3683d2a 100644 --- a/lib/serial_preference/has_preference_map.rb +++ b/lib/serial_preference/has_preference_map.rb @@ -1,4 +1,3 @@ -require "rails" module SerialPreference module HasSerialPreferences extend ActiveSupport::Concern @@ -33,7 +32,7 @@ def preference_names private def build_preference_definitions - if Gem::Version.new(Rails.version) >= Gem::Version.new("7.1") + if serialize_supports_coder? serialize self._preferences_attribute, coder: SerialPreference::SafeYamlCoder, type: Hash else serialize self._preferences_attribute, Hash @@ -60,6 +59,12 @@ def build_preference_definitions end end + + def serialize_supports_coder? + defined?(ActiveRecord::Base) && + ActiveRecord::Base.respond_to?(:serialize) && + ActiveRecord::Base.method(:serialize).parameters.any? { |p| p.include?(:coder) } + end end protected @@ -76,6 +81,5 @@ def write_preference_attribute(store_attribute, key, value) attribute[key.to_sym] = value end end - end end From 8c8e75d0264ea75b5ba529ae61c2a68298338f2f Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Tue, 10 Feb 2026 16:56:29 +0530 Subject: [PATCH 23/26] ActiveRecord::Base method --- lib/serial_preference/has_preference_map.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/serial_preference/has_preference_map.rb b/lib/serial_preference/has_preference_map.rb index 3683d2a..6c25c15 100644 --- a/lib/serial_preference/has_preference_map.rb +++ b/lib/serial_preference/has_preference_map.rb @@ -61,8 +61,7 @@ def build_preference_definitions end def serialize_supports_coder? - defined?(ActiveRecord::Base) && - ActiveRecord::Base.respond_to?(:serialize) && + ActiveRecord::Base.respond_to?(:serialize) && ActiveRecord::Base.method(:serialize).parameters.any? { |p| p.include?(:coder) } end end From 615fb5ba2e4da8f26f8c4fff893f7e773368f066 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Tue, 10 Feb 2026 17:33:24 +0530 Subject: [PATCH 24/26] default returns --- lib/serial_preference/safe_yaml_coder.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/serial_preference/safe_yaml_coder.rb b/lib/serial_preference/safe_yaml_coder.rb index db99165..8346100 100644 --- a/lib/serial_preference/safe_yaml_coder.rb +++ b/lib/serial_preference/safe_yaml_coder.rb @@ -1,14 +1,15 @@ module SerialPreference module SafeYamlCoder - def self.dump(obj) YAML.dump(obj) end def self.load(yaml) - Psych.safe_load(yaml, permitted_classes: [Symbol], aliases: true) - rescue Psych::Exception - YAML.load(yaml) + return {} if yaml.nil? + + Psych.safe_load(yaml, permitted_classes: [Symbol], aliases: true) || {} + rescue Psych::Exception, TypeError + YAML.load(yaml) || {} end end end From ead666b1cefbf5eb9f2b27a1967840eb0c952860 Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Thu, 12 Feb 2026 14:07:33 +0530 Subject: [PATCH 25/26] old 4 support attach --- .../preference_definition.rb | 49 +++++++++++++++++-- spec/preference_definition_spec.rb | 5 -- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index f948d07..de2c233 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -10,14 +10,14 @@ def initialize(name,*args) self.name = name.to_s opts.assert_valid_keys(:data_type, :default, :required, :field_type) self.data_type = @type = opts[:data_type] || :string - @column = column_type(@type) + @column = rails_below_6? ? ActiveRecord::ConnectionAdapters::Column.new(name.to_s, opts[:default], column_type(@type)) : column_type(@type) self.default = opts[:default] self.required = !!opts[:required] self.field_type = opts[:field_type] end def default_value - @column.cast(default) + @column.respond_to?(:default) ? @column.default : default end def required? @@ -33,7 +33,7 @@ def boolean? end def type_cast(value) - v = @column.cast(value) + v = @column.respond_to?(:cast) ? @column.cast(value) : @column.type_cast(value) v.nil? ? default_value : v end @@ -72,6 +72,14 @@ def value(v) private def column_type(type) + if rails_below_6? + column_type_below_6(type) + else + column_type_greater_6(type) + end + end + + def column_type_greater_6(type) begin ActiveModel::Type.lookup(type) rescue ArgumentError @@ -79,10 +87,41 @@ def column_type(type) end end + def column_type_below_6(type) + if greater_or_equal_rails_42? + case type + when :boolean + ActiveRecord::Type::Boolean.new + when :integer + ActiveRecord::Type::Integer.new + when :float + ActiveRecord::Type::Float.new + when :decimal + ActiveRecord::Type::Decimal.new + else + ActiveRecord::Type::String.new + end + else + type.to_s + end + end + def normalize_boolean(v) - return false if !v || (v.is_a?(String) && (v.downcase == "no")) + if rails_below_6? + return false if ["", "0", "false", "no"].include?(v.to_s.downcase) + v + else + return false if !v || (v.is_a?(String) && (v.downcase == "no")) + ActiveModel::Type::Boolean.new.cast(v.to_s.downcase) + end + end + + def greater_or_equal_rails_42? + ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) + end - ActiveModel::Type::Boolean.new.cast(v.to_s.downcase) + def rails_below_6? + ActiveRecord::VERSION::MAJOR < 6 end end end diff --git a/spec/preference_definition_spec.rb b/spec/preference_definition_spec.rb index 4e1f5a6..45eb2b2 100644 --- a/spec/preference_definition_spec.rb +++ b/spec/preference_definition_spec.rb @@ -197,11 +197,6 @@ expect(@blank_pref.default_value).to be_nil end - it "should return cast default value for integer" do - p = described_class.new("num",{default: "12", data_type: :integer}) - expect(p.default_value).to eq(12) - end - it "should return cast default value for boolean" do p = described_class.new("flag",{default: true, data_type: :boolean}) expect(p.default_value).to be_truthy From d5cc2e74cacc9b23119ba8867a755c12340d8ecb Mon Sep 17 00:00:00 2001 From: Kamlesh Parmar Date: Thu, 12 Feb 2026 14:18:16 +0530 Subject: [PATCH 26/26] name method --- lib/serial_preference/preference_definition.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/serial_preference/preference_definition.rb b/lib/serial_preference/preference_definition.rb index de2c233..77601ea 100644 --- a/lib/serial_preference/preference_definition.rb +++ b/lib/serial_preference/preference_definition.rb @@ -16,8 +16,12 @@ def initialize(name,*args) self.field_type = opts[:field_type] end + def name + @column.respond_to?(:name) ? @column.name : @name + end + def default_value - @column.respond_to?(:default) ? @column.default : default + @column.respond_to?(:default) ? @column.default : @default end def required?