Skip to content

Commit

Permalink
blank strings will be treated the same as nil for filters other than …
Browse files Browse the repository at this point in the history
…string and symbol
  • Loading branch information
AaronLasseigne committed Jan 10, 2021
1 parent 1568f79 commit 5a5337c
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 7 deletions.
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- drop support for Rails < 5.0
- [#398][] - Predicate methods have been removed.
([how to upgrade](#predicate-methods))
- [#412][] - Filters will now treat blank string values as `nil`
(except `string` and `symbol`). ([how to upgrade](#blank-values-treated-as-nil-for-filters))
- [#392][] - Integer parsing now defaults the base to 10.
([how to upgrade](#integer-parsing-base-now-10))
- The `inputs` method now returns an `ActiveInteraction::Input` instead of a
Expand Down Expand Up @@ -63,6 +65,45 @@ class Example < ActiveInteraction::Base
end
```

## Blank Values Treated As `nil` For Filters

In an effort to improve form support, strings that are `blank?` will
be converted into `nil` for all filters except `string` and `symbol`.
Previously, blank strings would have cased `:invalid_type` errors but
they'll now cause a `:missing` error which should be more form
friendly. If the filter has a default, the blank string will cause
the default to be used.

```ruby
class Example < ActiveInteraction::Base
integer :i
boolean :b, default: false

def execute
[i, b]
end
end

# v3.8
Example.run(i: '', b: '').errors.details
=> {:i=>[{:error=>:invalid_type, :type=>"integer"}], :b=>[{:error=>:invalid_type, :type=>"boolean"}]}

# v4.0
Example.run(i: '', b: '').errors.details
=> {:i=>[{:error=>:missing}]}

# v3.8
Example.run(i: 0, b: '').errors.details
=> {:b=>[{:error=>:invalid_type, :type=>"boolean"}]}

# v4.0
Example.run(i: 0, b: '').errors.details
=> {}

Example.run(i: 0, b: '').result
=> [0, false] # the default is used for `:b`
```

### Integer Parsing Base Now 10

Integers are parsed using `Integer`. By default this meant that when
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ end

Boolean filters convert the strings `"1"`, `"true"`, and `"on"`
(case-insensitive) into `true`. They also convert `"0"`, `"false"`, and `"off"`
into `false`.
into `false`. Blank strings will be treated as `nil`.

``` rb
class BooleanInteraction < ActiveInteraction::Base
Expand Down Expand Up @@ -565,9 +565,10 @@ SymbolInteraction.run!(method: :object_id)
### Dates and times

Filters that work with dates and times behave similarly. By default, they all
convert strings into their expected data types using `.parse`. If you give the
`format` option, they will instead convert strings using `.strptime`. Note that
formats won't work with `DateTime` and `Time` filters if a time zone is set.
convert strings into their expected data types using `.parse`. Blank strings
will be treated as `nil`. If you give the `format` option, they will instead
convert strings using `.strptime`. Note that formats won't work with `DateTime`
and `Time` filters if a time zone is set.

#### Date

Expand Down Expand Up @@ -641,7 +642,8 @@ time :start,
### Numbers

All numeric filters accept numeric input. They will also convert strings using
the appropriate method from `Kernel` (like `.Float`).
the appropriate method from `Kernel` (like `.Float`). Blank strings will be
treated as `nil`.

#### Decimal

Expand Down
3 changes: 2 additions & 1 deletion lib/active_interaction/filters/abstract_date_time_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def matches?(value)

def convert(value)
if value.respond_to?(:to_str)
convert_string(value.to_str)
value = value.to_str
value.blank? ? send(__method__, nil) : convert_string(value)
elsif value.is_a?(GroupedInput)
convert_grouped_input(value)
else
Expand Down
3 changes: 2 additions & 1 deletion lib/active_interaction/filters/abstract_numeric_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def convert(value)
elsif value.respond_to?(:to_int)
safe_converter(value.to_int)
elsif value.respond_to?(:to_str)
safe_converter(value.to_str)
value = value.to_str
value.blank? ? send(__method__, nil) : safe_converter(value)
else
super
end
Expand Down
5 changes: 5 additions & 0 deletions lib/active_interaction/filters/boolean_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ def matches?(value)
end

def convert(value)
if value.respond_to?(:to_str)
value = value.to_str
value = nil if value.blank?
end

case value
when /\A(?:0|false|off)\z/i
false
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/boolean_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ def to_str
end
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(filter.cast(value, nil)).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
filter.cast(value, nil)
end.to raise_error ActiveInteraction::MissingValueError
end
end
end
end

describe '#database_column_type' do
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/date_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,34 @@ def to_str
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end

context 'with a GroupedInput' do
let(:year) { 2012 }
let(:month) { 1 }
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/date_time_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,34 @@ def to_str
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end

context 'with a GroupedInput' do
let(:year) { 2012 }
let(:month) { 1 }
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/decimal_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,34 @@ def to_str
expect(result).to eql BigDecimal(value)
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end
end

describe '#database_column_type' do
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/float_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ def to_str
expect(result).to eql Float(value.to_str)
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end
end

describe '#database_column_type' do
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/integer_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,34 @@ def to_str
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end

it 'supports different bases' do
expect(described_class.new(name, base: 8).cast('071', nil)).to eql 57
expect do
Expand Down
28 changes: 28 additions & 0 deletions spec/active_interaction/filters/time_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,34 @@ def to_str
end
end

context 'with a blank String' do
let(:value) do
Class.new do
def to_str
' '
end
end.new
end

context 'optional' do
include_context 'optional'

it 'returns the default' do
expect(result).to eql options[:default]
end
end

context 'required' do
include_context 'required'

it 'raises an error' do
expect do
result
end.to raise_error ActiveInteraction::MissingValueError
end
end
end

context 'with an Integer' do
let(:value) { rand(1 << 16) }

Expand Down

0 comments on commit 5a5337c

Please sign in to comment.