Skip to content

Commit

Permalink
Merge pull request #37 from aliismayilov/if-unless
Browse files Browse the repository at this point in the history
Allow :if and :unless options
  • Loading branch information
rmm5t authored Feb 24, 2019
2 parents 4f119e9 + c72fdfa commit c64e355
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 16 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down Expand Up @@ -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
Expand Down
30 changes: 14 additions & 16 deletions lib/strip_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

# <b>DEPRECATED:</b> Please use <tt>strip_attributes</tt> (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
Expand All @@ -37,15 +37,15 @@ 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
strip_string(record_or_string, options)
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|
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
97 changes: 97 additions & 0 deletions test/strip_attributes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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("")
Expand Down

0 comments on commit c64e355

Please sign in to comment.