From 42cce5d6b1eb17cdbb91b5c3a7d8cdcba3d5fd4b Mon Sep 17 00:00:00 2001 From: Klemen Date: Thu, 11 Sep 2025 10:52:14 +0200 Subject: [PATCH 1/2] Strip attributes via before_save, not just before_validation --- lib/strip_attributes.rb | 7 +++++++ test/strip_attributes_test.rb | 7 +++++++ test/test_helper.rb | 21 +++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index 013a1de..18ccbc1 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -8,6 +8,13 @@ def strip_attributes(options = {}) before_validation(options.slice(:if, :unless)) do |record| StripAttributes.strip(record, options) end + + # Also add before_save to handle save(validate: false) scenarios + if respond_to?(:before_save) + before_save(options.slice(:if, :unless)) do |record| + StripAttributes.strip(record, options) + end + end end end diff --git a/test/strip_attributes_test.rb b/test/strip_attributes_test.rb index aaa786c..c73310e 100644 --- a/test/strip_attributes_test.rb +++ b/test/strip_attributes_test.rb @@ -373,6 +373,13 @@ def test_should_strip_no_fields_if_false_proc assert_equal " ", record.bang end + def test_should_strip_on_save_without_validation + record = StripAllMockRecord.new(foo: " ") + record.save(validate: false) + + assert_nil record.foo, "Expected empty string ' ' to be converted to nil" + end + class ClassMethodsTest < Minitest::Test def test_should_strip_whitespace assert_nil StripAttributes.strip("") diff --git a/test/test_helper.rb b/test/test_helper.rb index 1386552..913e72d 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -15,6 +15,27 @@ class Tableless include ActiveAttr::Serialization include ActiveModel::Validations::Callbacks + + # Define before_save callback support for testing + define_model_callbacks :save + + # Mock save method that respects validate: false option + def save(options = {}) + if options[:validate] == false + # When validate: false, skip validation but still run save callbacks + run_callbacks :save + else + # Normal save: run validation callbacks then save callbacks + run_callbacks :validation + run_callbacks :save + end + true + end + + # Support for before_save callback registration + def self.before_save(*args, &block) + set_callback(:save, :before, *args, &block) + end end # Avoid annoying deprecation warning From 37eb28b94f0f7bbb2be2aeac6016493b8daca637 Mon Sep 17 00:00:00 2001 From: Klemen Date: Fri, 12 Sep 2025 23:13:30 +0200 Subject: [PATCH 2/2] make sure that values are not normalized twice upon save operation --- lib/strip_attributes.rb | 11 +++++++++-- test/test_helper.rb | 8 ++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index 18ccbc1..10aea00 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -7,12 +7,19 @@ def strip_attributes(options = {}) before_validation(options.slice(:if, :unless)) do |record| StripAttributes.strip(record, options) + # Mark that stripping has been done to avoid double processing + record.instance_variable_set(:@attributes_stripped, true) end # Also add before_save to handle save(validate: false) scenarios + # Only run if validation was skipped (attributes not already stripped) if respond_to?(:before_save) - before_save(options.slice(:if, :unless)) do |record| - StripAttributes.strip(record, options) + before_save do |record| + unless record.instance_variable_get(:@attributes_stripped) + StripAttributes.strip(record, options) + end + # Reset the flag after save for next operation + record.instance_variable_set(:@attributes_stripped, false) end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 913e72d..eddac93 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,7 +16,8 @@ class Tableless include ActiveModel::Validations::Callbacks - # Define before_save callback support for testing + # Simple mock of save callbacks for testing + extend ActiveModel::Callbacks define_model_callbacks :save # Mock save method that respects validate: false option @@ -31,11 +32,6 @@ def save(options = {}) end true end - - # Support for before_save callback registration - def self.before_save(*args, &block) - set_callback(:save, :before, *args, &block) - end end # Avoid annoying deprecation warning