diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a487e4..44d8cab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +* Add `ActiveRecord::Base.ransack!` which raises error if passed unknown condition + + *Aaron Lipman* + ## 2.4.0 - 2020-11-27 * diff --git a/README.md b/README.md index ec4c1a9..7055173 100644 --- a/README.md +++ b/README.md @@ -670,6 +670,43 @@ Trying it out in `rails console`: That's it! Now you know how to whitelist/blacklist various elements in Ransack. +### Handling unknown predicates or attributes + +By default, Ransack will ignore any unknown predicates or attributes: + +```ruby +Article.ransack(unknown_attr_eq: 'Ernie').result.to_sql +=> SELECT "articles".* FROM "articles" +``` + +Ransack may be configured to raise an error if passed an unknown predicate or +attributes, by setting the `ignore_unknown_conditions` option to `false` in your +Ransack initializer file at `config/initializers/ransack.rb`: + +```ruby +Ransack.configure do |c| + # Raise errors if a query contains an unknown predicate or attribute. + # Default is true (do not raise error on unknown conditions). + c.ignore_unknown_conditions = false +end +``` + +```ruby +Article.ransack(unknown_attr_eq: 'Ernie') +# ArgumentError (Invalid search term unknown_attr_eq) +``` + +As an alternative to setting a global configuration option, the `.ransack!` +class method also raises an error if passed an unknown condition: + +```ruby +Article.ransack!(unknown_attr_eq: 'Ernie') +# ArgumentError: Invalid search term unknown_attr_eq +``` + +This is equivilent to the `ignore_unknown_conditions` configuration option, +except it may be applied on a case-by-case basis. + ### Using Scopes/Class Methods Continuing on from the preceding section, searching by scopes requires defining diff --git a/lib/ransack/adapters/active_record/base.rb b/lib/ransack/adapters/active_record/base.rb index fa8ed32..9ad348a 100644 --- a/lib/ransack/adapters/active_record/base.rb +++ b/lib/ransack/adapters/active_record/base.rb @@ -18,6 +18,10 @@ module Ransack Search.new(self, params, options) end + def ransack!(params = {}, options = {}) + ransack(params, options.merge(ignore_unknown_conditions: false)) + end + def ransacker(name, opts = {}, &block) self._ransackers = _ransackers.merge name.to_s => Ransacker .new(self, name, opts, &block) diff --git a/lib/ransack/search.rb b/lib/ransack/search.rb index 9b43844..35ca065 100644 --- a/lib/ransack/search.rb +++ b/lib/ransack/search.rb @@ -30,6 +30,7 @@ module Ransack ) @scope_args = {} @sorts ||= [] + @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true build(params.with_indifferent_access) end @@ -45,7 +46,7 @@ module Ransack base.send("#{key}=", value) elsif @context.ransackable_scope?(key, @context.object) add_scope(key, value) - elsif !Ransack.options[:ignore_unknown_conditions] + elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions raise ArgumentError, "Invalid search term #{key}" end end diff --git a/spec/ransack/adapters/active_record/base_spec.rb b/spec/ransack/adapters/active_record/base_spec.rb index 180d5e6..3ede567 100644 --- a/spec/ransack/adapters/active_record/base_spec.rb +++ b/spec/ransack/adapters/active_record/base_spec.rb @@ -122,6 +122,10 @@ module Ransack expect { Person.ransack('') }.to_not raise_error end + it 'raises exception if ransack! called with unknown condition' do + expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error + end + it 'does not modify the parameters' do params = { name_eq: '' } expect { Person.ransack(params) }.not_to change { params } diff --git a/spec/ransack/search_spec.rb b/spec/ransack/search_spec.rb index a824f13..0b134b2 100644 --- a/spec/ransack/search_spec.rb +++ b/spec/ransack/search_spec.rb @@ -232,7 +232,7 @@ module Ransack context 'with an invalid condition' do subject { Search.new(Person, unknown_attr_eq: 'Ernie') } - context 'when ignore_unknown_conditions is false' do + context 'when ignore_unknown_conditions configuration option is false' do before do Ransack.configure { |c| c.ignore_unknown_conditions = false } end @@ -240,13 +240,39 @@ module Ransack specify { expect { subject }.to raise_error ArgumentError } end - context 'when ignore_unknown_conditions is true' do + context 'when ignore_unknown_conditions configuration option is true' do before do Ransack.configure { |c| c.ignore_unknown_conditions = true } end specify { expect { subject }.not_to raise_error } end + + subject(:with_ignore_unknown_conditions_false) { + Search.new(Person, + { unknown_attr_eq: 'Ernie' }, + { ignore_unknown_conditions: false } + ) + } + + subject(:with_ignore_unknown_conditions_true) { + Search.new(Person, + { unknown_attr_eq: 'Ernie' }, + { ignore_unknown_conditions: true } + ) + } + + context 'when ignore_unknown_conditions search parameter is absent' do + specify { expect { subject }.not_to raise_error } + end + + context 'when ignore_unknown_conditions search parameter is false' do + specify { expect { with_ignore_unknown_conditions_false }.to raise_error ArgumentError } + end + + context 'when ignore_unknown_conditions search parameter is true' do + specify { expect { with_ignore_unknown_conditions_true }.not_to raise_error } + end end it 'does not modify the parameters' do