From ec0b1fef0c224de480421221eba8e18fd96335ff Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 23 Aug 2022 13:56:46 -0500 Subject: [PATCH 1/9] add logic to BooleanParser to allow for optional truthy arguments --- lib/decanter/parser/boolean_parser.rb | 11 ++++- spec/decanter/parser/boolean_parser_spec.rb | 49 ++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/decanter/parser/boolean_parser.rb b/lib/decanter/parser/boolean_parser.rb index 89a0115..b742eaa 100644 --- a/lib/decanter/parser/boolean_parser.rb +++ b/lib/decanter/parser/boolean_parser.rb @@ -7,7 +7,16 @@ class BooleanParser < ValueParser parser do |val, options| raise Decanter::ParseError.new 'Expects a single value' if val.is_a? Array next if (val.nil? || val === '') - [1, '1'].include?(val) || !!/true/i.match(val.to_s) + [1, '1'].include?(val) || !!/true/i.match(val.to_s) || parse_options(val, options) + end + + def self.parse_options(val, options) + truth = options.fetch(:truth, nil)&.to_s&.downcase + accounted_for_values = [nil, "false", "true", "1", "0", ""] + return false if accounted_for_values.include?(truth) + + raise Decanter::ParseError.new 'Expects a single value' if truth.is_a? Array + !!/#{truth}/i.match(val.to_s) end end end diff --git a/spec/decanter/parser/boolean_parser_spec.rb b/spec/decanter/parser/boolean_parser_spec.rb index 6b67f7a..6504bd2 100644 --- a/spec/decanter/parser/boolean_parser_spec.rb +++ b/spec/decanter/parser/boolean_parser_spec.rb @@ -12,7 +12,7 @@ ['boolean', true], ['string', 'true'], ['string', 'True'], - ['string', 'truE'] + ['string', 'truE'], ] falses = [ @@ -20,7 +20,24 @@ ['number', 2], ['string', '2'], ['boolean', false], - ['string', 'tru'] + ['string', 'tru'], + ] + + trues_with_options = [ + ['string', 'yes', 'string', 'yes'], + ['string', 'Yes', 'string', 'yes'], + ['string', 'is true', 'string', 'is true'], + ['string', 'is truE', 'string', 'is True'], + ['number', 3, 'number', 3], + ['number', 3, 'string', '3'], + ['string', '3', 'number', 3], + ] + + falses_with_options = [ + ['string', 'false', 'string', 'false'], + ['string', 'false', 'string', 'False'], + ['string', 'yes', 'string', ''], + ['string', 'yes', 'string', '@yes'] ] let(:name) { :foo } @@ -61,5 +78,33 @@ .to raise_error(Decanter::ParseError) end end + + context 'returns true with options for' do + trues_with_options.each do |cond| + it "#{cond[0]}: #{cond[1]}, option: {#{cond[2]}: #{cond[3]}}" do + expect(parser.parse(name, cond[1], truth: cond[3])).to match({name => true}) + end + end + end + + context 'returns false with options for' do + falses_with_options.each do |cond| + it "#{cond[0]}: #{cond[1]}, option: {#{cond[2]}: #{cond[3]}}" do + expect(parser.parse(name, cond[1], truth: cond[3])).to match({name => false}) + end + end + end + + context 'with empty string and empty options' do + it 'returns nil' do + expect(parser.parse(name, '', truth: '')).to match({name => nil}) + end + end + + context 'with nil and nil options' do + it 'returns nil' do + expect(parser.parse(name, nil, truth: nil)).to match({name => nil}) + end + end end end From 64d7bef9a5171dbd9558cd82e39fe107bb9de195 Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 23 Aug 2022 16:32:19 -0500 Subject: [PATCH 2/9] remove unneeded test cases --- lib/decanter/parser/boolean_parser.rb | 6 +++--- spec/decanter/parser/boolean_parser_spec.rb | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/decanter/parser/boolean_parser.rb b/lib/decanter/parser/boolean_parser.rb index b742eaa..5258de8 100644 --- a/lib/decanter/parser/boolean_parser.rb +++ b/lib/decanter/parser/boolean_parser.rb @@ -11,11 +11,11 @@ class BooleanParser < ValueParser end def self.parse_options(val, options) - truth = options.fetch(:truth, nil)&.to_s&.downcase - accounted_for_values = [nil, "false", "true", "1", "0", ""] + truth = options.fetch(:truth, '').to_s.downcase + raise Decanter::ParseError.new 'Expects a single value' if truth.is_a? Array + accounted_for_values = ['false', 'true', '1', '0', ''] return false if accounted_for_values.include?(truth) - raise Decanter::ParseError.new 'Expects a single value' if truth.is_a? Array !!/#{truth}/i.match(val.to_s) end end diff --git a/spec/decanter/parser/boolean_parser_spec.rb b/spec/decanter/parser/boolean_parser_spec.rb index 6504bd2..386e5c2 100644 --- a/spec/decanter/parser/boolean_parser_spec.rb +++ b/spec/decanter/parser/boolean_parser_spec.rb @@ -12,7 +12,6 @@ ['boolean', true], ['string', 'true'], ['string', 'True'], - ['string', 'truE'], ] falses = [ @@ -20,7 +19,6 @@ ['number', 2], ['string', '2'], ['boolean', false], - ['string', 'tru'], ] trues_with_options = [ @@ -37,7 +35,6 @@ ['string', 'false', 'string', 'false'], ['string', 'false', 'string', 'False'], ['string', 'yes', 'string', ''], - ['string', 'yes', 'string', '@yes'] ] let(:name) { :foo } From e19773fa38df82ede0ff1c85994e6b602bad7a9b Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 23 Aug 2022 16:50:13 -0500 Subject: [PATCH 3/9] refactor single value check, rename arguments --- lib/decanter/parser/boolean_parser.rb | 19 ++++++++++++++----- spec/decanter/parser/boolean_parser_spec.rb | 8 ++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/decanter/parser/boolean_parser.rb b/lib/decanter/parser/boolean_parser.rb index 5258de8..edbaa2f 100644 --- a/lib/decanter/parser/boolean_parser.rb +++ b/lib/decanter/parser/boolean_parser.rb @@ -5,18 +5,27 @@ class BooleanParser < ValueParser allow TrueClass, FalseClass parser do |val, options| - raise Decanter::ParseError.new 'Expects a single value' if val.is_a? Array + single_value_check(val) next if (val.nil? || val === '') [1, '1'].include?(val) || !!/true/i.match(val.to_s) || parse_options(val, options) end def self.parse_options(val, options) - truth = options.fetch(:truth, '').to_s.downcase - raise Decanter::ParseError.new 'Expects a single value' if truth.is_a? Array + raw_true_value = options.fetch(:true_value, nil) + return false if raw_true_value.nil? + + single_value_check(raw_true_value) + true_value = raw_true_value.to_s.downcase + + # want to avoid using values that are already implemented accounted_for_values = ['false', 'true', '1', '0', ''] - return false if accounted_for_values.include?(truth) + return false if accounted_for_values.include?(true_value) + + !!/#{true_value}/i.match(val.to_s) + end - !!/#{truth}/i.match(val.to_s) + def self.single_value_check(v) + raise Decanter::ParseError.new 'Expects a single value' if v.is_a? Array end end end diff --git a/spec/decanter/parser/boolean_parser_spec.rb b/spec/decanter/parser/boolean_parser_spec.rb index 386e5c2..e39dd10 100644 --- a/spec/decanter/parser/boolean_parser_spec.rb +++ b/spec/decanter/parser/boolean_parser_spec.rb @@ -79,7 +79,7 @@ context 'returns true with options for' do trues_with_options.each do |cond| it "#{cond[0]}: #{cond[1]}, option: {#{cond[2]}: #{cond[3]}}" do - expect(parser.parse(name, cond[1], truth: cond[3])).to match({name => true}) + expect(parser.parse(name, cond[1], true_value: cond[3])).to match({name => true}) end end end @@ -87,20 +87,20 @@ context 'returns false with options for' do falses_with_options.each do |cond| it "#{cond[0]}: #{cond[1]}, option: {#{cond[2]}: #{cond[3]}}" do - expect(parser.parse(name, cond[1], truth: cond[3])).to match({name => false}) + expect(parser.parse(name, cond[1], true_value: cond[3])).to match({name => false}) end end end context 'with empty string and empty options' do it 'returns nil' do - expect(parser.parse(name, '', truth: '')).to match({name => nil}) + expect(parser.parse(name, '', true_value: '')).to match({name => nil}) end end context 'with nil and nil options' do it 'returns nil' do - expect(parser.parse(name, nil, truth: nil)).to match({name => nil}) + expect(parser.parse(name, nil, true_value: nil)).to match({name => nil}) end end end From 0322c82059e90e66bd0262d5d56c34132c485861 Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 23 Aug 2022 17:08:37 -0500 Subject: [PATCH 4/9] update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6b3a042..61dffe2 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,12 @@ Note: these parsers are designed to operate on a single value, except for `:arra input :ids, :array, parse_each: :integer ``` +`:boolean` will parse values of `true` and `1` as truthy. If another value is expected to be truthy, use the option `truth_value` to assign a custom truthy case. + +```ruby +input :checkbox, :boolean, truth_value: 'yes' +``` + ### Parser options Parsers can receive options that modify their behavior. These options are passed in as named arguments to `input`: From be56549e11b6aa578c0c0cf35878579dbadfb79c Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 23 Aug 2022 17:10:31 -0500 Subject: [PATCH 5/9] add readme discription to right section --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 61dffe2..9e0ca88 100644 --- a/README.md +++ b/README.md @@ -135,12 +135,6 @@ Note: these parsers are designed to operate on a single value, except for `:arra input :ids, :array, parse_each: :integer ``` -`:boolean` will parse values of `true` and `1` as truthy. If another value is expected to be truthy, use the option `truth_value` to assign a custom truthy case. - -```ruby -input :checkbox, :boolean, truth_value: 'yes' -``` - ### Parser options Parsers can receive options that modify their behavior. These options are passed in as named arguments to `input`: @@ -149,6 +143,12 @@ Parsers can receive options that modify their behavior. These options are passed input :start_date, :date, parse_format: '%Y-%m-%d' ``` +`:boolean` will parse values of `true` and `1` as truthy. If another value is expected to be truthy, use the option `truth_value` to assign a custom truthy case. + +```ruby +input :checkbox, :boolean, truth_value: 'yes' +``` + ### Exceptions By default, `Decanter#decant` will raise an exception when unexpected parameters are passed. To override this behavior, you can change the strict mode option to one of: From c245c0d2d1f845c013f582a9ef89eab63c17298d Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Tue, 30 Aug 2022 15:27:07 -0500 Subject: [PATCH 6/9] added append to true_values array refactor --- lib/decanter/parser/boolean_parser.rb | 27 +++++++++------------ spec/decanter/parser/boolean_parser_spec.rb | 6 ++--- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/decanter/parser/boolean_parser.rb b/lib/decanter/parser/boolean_parser.rb index edbaa2f..1f89e0b 100644 --- a/lib/decanter/parser/boolean_parser.rb +++ b/lib/decanter/parser/boolean_parser.rb @@ -5,27 +5,22 @@ class BooleanParser < ValueParser allow TrueClass, FalseClass parser do |val, options| - single_value_check(val) - next if (val.nil? || val === '') - [1, '1'].include?(val) || !!/true/i.match(val.to_s) || parse_options(val, options) - end - - def self.parse_options(val, options) - raw_true_value = options.fetch(:true_value, nil) - return false if raw_true_value.nil? + normalized_val = normalize(val) + next if normalized_val.nil? - single_value_check(raw_true_value) - true_value = raw_true_value.to_s.downcase + true_values = ['1', 'true'] - # want to avoid using values that are already implemented - accounted_for_values = ['false', 'true', '1', '0', ''] - return false if accounted_for_values.include?(true_value) + option_val = options.fetch(:true_value, nil) + normalized_option = normalize(option_val) - !!/#{true_value}/i.match(val.to_s) + true_values << normalized_option if normalized_option + true_values.find {|tv| !!/#{tv}/i.match(normalized_val)}.present? end - def self.single_value_check(v) - raise Decanter::ParseError.new 'Expects a single value' if v.is_a? Array + def self.normalize(value) + return if (value.nil? || value === '') + raise Decanter::ParseError.new 'Expects a single value' if value.is_a? Array + value.to_s.downcase end end end diff --git a/spec/decanter/parser/boolean_parser_spec.rb b/spec/decanter/parser/boolean_parser_spec.rb index e39dd10..f950c3b 100644 --- a/spec/decanter/parser/boolean_parser_spec.rb +++ b/spec/decanter/parser/boolean_parser_spec.rb @@ -29,12 +29,12 @@ ['number', 3, 'number', 3], ['number', 3, 'string', '3'], ['string', '3', 'number', 3], + ['string', 'false', 'string', 'false'], + ['string', 'false', 'string', 'False'], ] falses_with_options = [ - ['string', 'false', 'string', 'false'], - ['string', 'false', 'string', 'False'], - ['string', 'yes', 'string', ''], + ['string', 'yes', 'string', ''] ] let(:name) { :foo } From 4cd6574955937263802ec247d8cc65b3e7493dc5 Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Wed, 31 Aug 2022 09:20:10 -0500 Subject: [PATCH 7/9] more refactors --- lib/decanter/parser/boolean_parser.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/decanter/parser/boolean_parser.rb b/lib/decanter/parser/boolean_parser.rb index 1f89e0b..db45be4 100644 --- a/lib/decanter/parser/boolean_parser.rb +++ b/lib/decanter/parser/boolean_parser.rb @@ -18,9 +18,10 @@ class BooleanParser < ValueParser end def self.normalize(value) - return if (value.nil? || value === '') raise Decanter::ParseError.new 'Expects a single value' if value.is_a? Array - value.to_s.downcase + return if (value.nil? || value.blank?) + + value.to_s end end end From 590fb7134df0fc22470f81d90c269b02af55cfe5 Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Wed, 31 Aug 2022 09:24:35 -0500 Subject: [PATCH 8/9] update version --- Gemfile.lock | 2 +- lib/decanter/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0e65cf7..117da4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - decanter (4.0.0) + decanter (4.0.1) actionpack (>= 4.2.10) activesupport rails-html-sanitizer (>= 1.0.4) diff --git a/lib/decanter/version.rb b/lib/decanter/version.rb index d29c91e..98f9e75 100644 --- a/lib/decanter/version.rb +++ b/lib/decanter/version.rb @@ -1,3 +1,3 @@ module Decanter - VERSION = '4.0.0'.freeze + VERSION = '4.0.1'.freeze end From 6d6d7ba43182cefefdf4b0320d11c353b6db88e6 Mon Sep 17 00:00:00 2001 From: Owen Roth Date: Wed, 31 Aug 2022 14:34:21 -0500 Subject: [PATCH 9/9] adjusted version number --- Gemfile.lock | 2 +- lib/decanter/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 117da4e..74b5f2d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - decanter (4.0.1) + decanter (4.1.0) actionpack (>= 4.2.10) activesupport rails-html-sanitizer (>= 1.0.4) diff --git a/lib/decanter/version.rb b/lib/decanter/version.rb index 98f9e75..41bd3a3 100644 --- a/lib/decanter/version.rb +++ b/lib/decanter/version.rb @@ -1,3 +1,3 @@ module Decanter - VERSION = '4.0.1'.freeze + VERSION = '4.1.0'.freeze end