From 4977febb47fb6cd7863a5a5faf77acbc5cac1ca1 Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Wed, 20 Feb 2019 16:05:27 +0100 Subject: [PATCH 1/6] Allow if option --- lib/strip_attributes.rb | 6 +++++- test/strip_attributes_test.rb | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index 86e10c9..3187b7c 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -19,7 +19,7 @@ def strip_attributes!(options = nil) end module StripAttributes - VALID_OPTIONS = [:only, :except, :allow_empty, :collapse_spaces, :replace_newlines, :regex] + VALID_OPTIONS = [:only, :except, :allow_empty, :collapse_spaces, :replace_newlines, :regex, :if] # Unicode invisible and whitespace characters. The POSIX character class # [:space:] corresponds to the Unicode class Z ("separator"). We also @@ -38,6 +38,10 @@ module StripAttributes MULTIBYTE_SUPPORTED = "\u0020" == " " def self.strip(record_or_string, options = nil) + if options && options[:if] && !record_or_string.send(options[:if].to_sym) + return record_or_string + end + if record_or_string.respond_to?(:attributes) strip_record(record_or_string, options) else diff --git a/test/strip_attributes_test.rb b/test/strip_attributes_test.rb index 6fdb8bc..7a566d5 100644 --- a/test/strip_attributes_test.rb +++ b/test/strip_attributes_test.rb @@ -9,6 +9,7 @@ def self.included(base) base.attribute :bang base.attribute :foz base.attribute :fiz + base.attribute :strip_me end end @@ -72,6 +73,15 @@ class StripRegexMockRecord < Tableless strip_attributes regex: /[\^\%&\*]/ end +class IfSymMockRecord < Tableless + include MockAttributes + strip_attributes if: :strip_me? + + def strip_me? + strip_me + end +end + class StripAttributesTest < Minitest::Test def setup @init_params = { @@ -242,6 +252,30 @@ def test_should_strip_unicode assert_equal "foo", record.foo end + def test_should_strip_all_fields + record = IfSymMockRecord.new(@init_params.merge(strip_me: true)) + record.valid? + assert_equal "foo", record.foo + assert_equal "bar", record.bar + assert_equal "biz", record.biz + assert_equal "foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_nil record.baz + assert_nil record.bang + end + + def test_should_strip_no_fields + record = IfSymMockRecord.new(@init_params.merge(strip_me: false)) + record.valid? + assert_equal "\tfoo", record.foo + assert_equal "bar \t ", record.bar + assert_equal "\tbiz ", record.biz + assert_equal " foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_equal "", record.baz + assert_equal " ", record.bang + end + class ClassMethodsTest < Minitest::Test def test_should_strip_whitespace assert_nil StripAttributes.strip("") From 4ee963b55b29b20eb5ceff7bfebe3f477b48d124 Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Wed, 20 Feb 2019 16:09:22 +0100 Subject: [PATCH 2/6] Allow unless option to skip strip_attributes --- lib/strip_attributes.rb | 6 +++++- test/strip_attributes_test.rb | 38 +++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index 3187b7c..cb1e87d 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -19,7 +19,7 @@ def strip_attributes!(options = nil) end module StripAttributes - VALID_OPTIONS = [:only, :except, :allow_empty, :collapse_spaces, :replace_newlines, :regex, :if] + VALID_OPTIONS = [:only, :except, :allow_empty, :collapse_spaces, :replace_newlines, :regex, :if, :unless] # Unicode invisible and whitespace characters. The POSIX character class # [:space:] corresponds to the Unicode class Z ("separator"). We also @@ -42,6 +42,10 @@ def self.strip(record_or_string, options = nil) return record_or_string end + if options && options[:unless] && record_or_string.send(options[:unless].to_sym) + return record_or_string + end + if record_or_string.respond_to?(:attributes) strip_record(record_or_string, options) else diff --git a/test/strip_attributes_test.rb b/test/strip_attributes_test.rb index 7a566d5..ed329f1 100644 --- a/test/strip_attributes_test.rb +++ b/test/strip_attributes_test.rb @@ -10,6 +10,7 @@ def self.included(base) base.attribute :foz base.attribute :fiz base.attribute :strip_me + base.attribute :skip_me end end @@ -82,6 +83,15 @@ def strip_me? end end +class UnlessSymMockRecord < Tableless + include MockAttributes + strip_attributes unless: :skip_me? + + def skip_me? + skip_me + end +end + class StripAttributesTest < Minitest::Test def setup @init_params = { @@ -252,7 +262,7 @@ def test_should_strip_unicode assert_equal "foo", record.foo end - def test_should_strip_all_fields + def test_should_strip_all_fields_if_true record = IfSymMockRecord.new(@init_params.merge(strip_me: true)) record.valid? assert_equal "foo", record.foo @@ -264,7 +274,7 @@ def test_should_strip_all_fields assert_nil record.bang end - def test_should_strip_no_fields + def test_should_strip_no_fields_if_false record = IfSymMockRecord.new(@init_params.merge(strip_me: false)) record.valid? assert_equal "\tfoo", record.foo @@ -276,6 +286,30 @@ def test_should_strip_no_fields assert_equal " ", record.bang end + def test_should_strip_all_fields_unless_false + record = UnlessSymMockRecord.new(@init_params.merge(skip_me: false)) + record.valid? + assert_equal "foo", record.foo + assert_equal "bar", record.bar + assert_equal "biz", record.biz + assert_equal "foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_nil record.baz + assert_nil record.bang + end + + def test_should_strip_no_fields_unless_true + record = UnlessSymMockRecord.new(@init_params.merge(skip_me: true)) + record.valid? + assert_equal "\tfoo", record.foo + assert_equal "bar \t ", record.bar + assert_equal "\tbiz ", record.biz + assert_equal " foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_equal "", record.baz + assert_equal " ", record.bang + end + class ClassMethodsTest < Minitest::Test def test_should_strip_whitespace assert_nil StripAttributes.strip("") From 35581e2f75feb99ea1f92f7b05f745c2fbd60ed0 Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Wed, 20 Feb 2019 16:17:35 +0100 Subject: [PATCH 3/6] Add docs related to :if and :unless options --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index a965af0..3b91f27 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ attributes are stripped of whitespace, but `:only` and `:except` options can be used to limit which attributes are stripped. Both options accept a single attribute (`only: :field`) or arrays of attributes (`except: [:field1, :field2, :field3]`). +It's also possible to skip stripping the attributes altogether per model using the `:if` and `:unless` options. + --- **How You Can Help** @@ -67,6 +69,32 @@ class ConservativePokerPlayer < ActiveRecord::Base end ``` +### Using `if` + +```ruby +# Only records with odd ids will be stripped +class OddPokerPlayer < ActiveRecord::Base + strip_attributes if: :strip_me? + + def strip_me? + id.odd? + end +end +``` + +### Using `unless` + +```ruby +# strip_attributes will be applied randomly +class RandomPokerPlayer < ActiveRecord::Base + strip_attributes unless: :strip_me? + + def strip_me? + [true, false].sample + end +end +``` + ### Using `allow_empty` ```ruby From 6fe664bc9ee0ba969be6f00a0cfa6d068ba98cde Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Thu, 21 Feb 2019 10:39:52 +0100 Subject: [PATCH 4/6] Delegate if/unless options to before_validation --- lib/strip_attributes.rb | 15 ++++++--------- test/strip_attributes_test.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index cb1e87d..a471af4 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -5,7 +5,12 @@ module ActiveModel::Validations::HelperMethods def strip_attributes(options = nil) StripAttributes.validate_options(options) - before_validation do |record| + rails_opts = { + if: options && options[:if], + unless: options && options[:unless] + } + + before_validation(rails_opts) do |record| StripAttributes.strip(record, options) end end @@ -38,14 +43,6 @@ module StripAttributes MULTIBYTE_SUPPORTED = "\u0020" == " " def self.strip(record_or_string, options = nil) - if options && options[:if] && !record_or_string.send(options[:if].to_sym) - return record_or_string - end - - if options && options[:unless] && record_or_string.send(options[:unless].to_sym) - return record_or_string - end - if record_or_string.respond_to?(:attributes) strip_record(record_or_string, options) else diff --git a/test/strip_attributes_test.rb b/test/strip_attributes_test.rb index ed329f1..4811e2a 100644 --- a/test/strip_attributes_test.rb +++ b/test/strip_attributes_test.rb @@ -92,6 +92,11 @@ def skip_me? end end +class IfProcMockRecord < Tableless + include MockAttributes + strip_attributes if: Proc.new { |record| record.strip_me } +end + class StripAttributesTest < Minitest::Test def setup @init_params = { @@ -310,6 +315,30 @@ def test_should_strip_no_fields_unless_true assert_equal " ", record.bang end + def test_should_strip_all_fields_if_true_proc + record = IfProcMockRecord.new(@init_params.merge(strip_me: true)) + record.valid? + assert_equal "foo", record.foo + assert_equal "bar", record.bar + assert_equal "biz", record.biz + assert_equal "foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_nil record.baz + assert_nil record.bang + end + + def test_should_strip_no_fields_if_false_proc + record = IfProcMockRecord.new(@init_params.merge(strip_me: false)) + record.valid? + assert_equal "\tfoo", record.foo + assert_equal "bar \t ", record.bar + assert_equal "\tbiz ", record.biz + assert_equal " foz foz", record.foz + assert_equal "fiz \n fiz", record.fiz + assert_equal "", record.baz + assert_equal " ", record.bang + end + class ClassMethodsTest < Minitest::Test def test_should_strip_whitespace assert_nil StripAttributes.strip("") From 8fd0c2aae917676f33f5ab8742394edeb8366a69 Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Sat, 23 Feb 2019 10:29:45 +0100 Subject: [PATCH 5/6] Remove rails_opts var --- lib/strip_attributes.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index a471af4..d0cef21 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -5,12 +5,7 @@ module ActiveModel::Validations::HelperMethods def strip_attributes(options = nil) StripAttributes.validate_options(options) - rails_opts = { - if: options && options[:if], - unless: options && options[:unless] - } - - before_validation(rails_opts) do |record| + before_validation(options && options.slice(:if, :unless) || {}) do |record| StripAttributes.strip(record, options) end end From c72fdfa007e0043dd2b33efe8346fe7069826f4f Mon Sep 17 00:00:00 2001 From: Ali Ismayilov Date: Sat, 23 Feb 2019 10:39:10 +0100 Subject: [PATCH 6/6] Change options default to be an empty hash --- lib/strip_attributes.rb | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index d0cef21..de36b66 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -2,17 +2,17 @@ module ActiveModel::Validations::HelperMethods # Strips whitespace from model fields and converts blank values to nil. - def strip_attributes(options = nil) + def strip_attributes(options = {}) StripAttributes.validate_options(options) - before_validation(options && options.slice(:if, :unless) || {}) do |record| + before_validation(options.slice(:if, :unless)) do |record| StripAttributes.strip(record, options) end end # DEPRECATED: Please use strip_attributes (non-bang method) # instead. - def strip_attributes!(options = nil) + def strip_attributes!(options = {}) warn "[DEPRECATION] `strip_attributes!` is deprecated. Please use `strip_attributes` (non-bang method) instead." strip_attributes(options) end @@ -37,7 +37,7 @@ module StripAttributes MULTIBYTE_BLANK = /[[:blank:]#{MULTIBYTE_WHITE}]/ MULTIBYTE_SUPPORTED = "\u0020" == " " - def self.strip(record_or_string, options = nil) + def self.strip(record_or_string, options = {}) if record_or_string.respond_to?(:attributes) strip_record(record_or_string, options) else @@ -45,7 +45,7 @@ def self.strip(record_or_string, options = nil) end end - def self.strip_record(record, options = nil) + def self.strip_record(record, options = {}) attributes = narrow(record.attributes, options) attributes.each do |attr, value| @@ -57,13 +57,11 @@ def self.strip_record(record, options = nil) record end - def self.strip_string(value, options = nil) - if options - allow_empty = options[:allow_empty] - collapse_spaces = options[:collapse_spaces] - replace_newlines = options[:replace_newlines] - regex = options[:regex] - end + def self.strip_string(value, options = {}) + allow_empty = options[:allow_empty] + collapse_spaces = options[:collapse_spaces] + replace_newlines = options[:replace_newlines] + regex = options[:regex] if value.respond_to?(:strip) value = (value.blank? && !allow_empty) ? nil : value.strip @@ -97,10 +95,10 @@ def self.strip_string(value, options = nil) # Necessary because Rails has removed the narrowing of attributes using :only # and :except on Base#attributes def self.narrow(attributes, options = {}) - if except = options && options[:except] + if except = options[:except] except = Array(except).collect { |attribute| attribute.to_s } attributes.except(*except) - elsif only = options && options[:only] + elsif only = options[:only] only = Array(only).collect { |attribute| attribute.to_s } attributes.slice(*only) else @@ -109,7 +107,7 @@ def self.narrow(attributes, options = {}) end def self.validate_options(options) - if keys = options && options.keys + if keys = options.keys unless (keys - VALID_OPTIONS).empty? raise ArgumentError, "Options does not specify #{VALID_OPTIONS} (#{options.keys.inspect})" end