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 diff --git a/lib/strip_attributes.rb b/lib/strip_attributes.rb index 86e10c9..de36b66 100644 --- a/lib/strip_attributes.rb +++ b/lib/strip_attributes.rb @@ -2,24 +2,24 @@ 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 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 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, :unless] # Unicode invisible and whitespace characters. The POSIX character class # [:space:] corresponds to the Unicode class Z ("separator"). We also @@ -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 diff --git a/test/strip_attributes_test.rb b/test/strip_attributes_test.rb index 6fdb8bc..4811e2a 100644 --- a/test/strip_attributes_test.rb +++ b/test/strip_attributes_test.rb @@ -9,6 +9,8 @@ def self.included(base) base.attribute :bang base.attribute :foz base.attribute :fiz + base.attribute :strip_me + base.attribute :skip_me end end @@ -72,6 +74,29 @@ 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 UnlessSymMockRecord < Tableless + include MockAttributes + strip_attributes unless: :skip_me? + + def skip_me? + 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 = { @@ -242,6 +267,78 @@ def test_should_strip_unicode assert_equal "foo", record.foo end + def test_should_strip_all_fields_if_true + 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_if_false + 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 + + 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 + + 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("")