From 46de17eba86808be2b01d1bbb60124570d020a97 Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Sun, 7 Jun 2020 22:15:18 -0400 Subject: [PATCH 1/2] Add ransack! method which raises error if passed unknown condition --- CHANGELOG.md | 4 ++ README.md | 37 +++++++++++++++++++ lib/ransack/adapters/active_record/base.rb | 4 ++ lib/ransack/search.rb | 3 +- .../adapters/active_record/base_spec.rb | 4 ++ spec/ransack/search_spec.rb | 30 ++++++++++++++- 6 files changed, 79 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a487e427..44d8caba6 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 ec4c1a992..7055173d4 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 fa8ed32ee..9ad348a75 100644 --- a/lib/ransack/adapters/active_record/base.rb +++ b/lib/ransack/adapters/active_record/base.rb @@ -18,6 +18,10 @@ def ransack(params = {}, options = {}) 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 9b4384443..35ca06539 100644 --- a/lib/ransack/search.rb +++ b/lib/ransack/search.rb @@ -30,6 +30,7 @@ def initialize(object, params = {}, options = {}) ) @scope_args = {} @sorts ||= [] + @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true build(params.with_indifferent_access) end @@ -45,7 +46,7 @@ def build(params) 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 180d5e6a1..3ede567d1 100644 --- a/spec/ransack/adapters/active_record/base_spec.rb +++ b/spec/ransack/adapters/active_record/base_spec.rb @@ -122,6 +122,10 @@ module ActiveRecord 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 a824f1373..0b134b2c5 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 From f085e8affdeb01bb1db3639e7a7a57b8ad0ccd27 Mon Sep 17 00:00:00 2001 From: Aaron Lipman Date: Sun, 13 Dec 2020 12:02:57 -0500 Subject: [PATCH 2/2] move "If you're coming from MetaSearch" section to bottom of readme It's been eight years since Ransack succeeded MetaSearch. As most developers have likely migrated from MetaSearch to Ransack by now, anyone reading the readme is likely starting fresh with Ransack - so it's perhaps less useful to highlight differences from MetaSearch in the readme's introduction. Move the "Updating From MetaSearch" section to the bottom of the readme, and make sure any important details (like setting the default search param) are covered appropriately within the readme. --- README.md | 61 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 7055173d4..af3234f39 100644 --- a/README.md +++ b/README.md @@ -41,27 +41,14 @@ gem 'ransack', github: 'activerecord-hackery/ransack' ## Usage -Ransack can be used in one of two modes, simple or advanced. +Ransack can be used in one of two modes, simple or advanced. For +searching/filtering not requiring complex boolean logic, Ransack's simple +mode should meet your needs. -### Simple Mode - -This mode works much like MetaSearch, for those of you who are familiar with -it, and requires very little setup effort. +If you're coming from MetaSearch (Ransack's predecessor), refer to the +[Updating From MetaSearch](#updating-from-metasearch) section -If you're coming from MetaSearch, things to note: - - 1. The default param key for search params is now `:q`, instead of `:search`. - This is primarily to shorten query strings, though advanced queries (below) - will still run afoul of URL length limits in most browsers and require a - switch to HTTP POST requests. This key is [configurable](https://github.com/activerecord-hackery/ransack/wiki/Configuration). - - 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search - object is passed to it. - - 3. Common ActiveRecord::Relation methods are no longer delegated by the - search object. Instead, you will get your search results (an - ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to - `Ransack#result`. +### Simple Mode #### In your controller @@ -84,6 +71,20 @@ def index end ``` +##### Default search parameter + +Ransack uses a default `:q` param key for search params. This may be changed by +setting the `search_key` option in a Ransack initializer file (typically +`config/initializers/ransack.rb`): + +``` +Ransack.configure do |c| + # Change default search parameter key name. + # Default key name is :q + c.search_key = :query +end +``` + #### In your view The two primary Ransack view helpers are `search_form_for` and `sort_link`, @@ -903,6 +904,28 @@ en: title: Old Ransack Namespaced Title ``` +### Updating From MetaSearch + +Ransack works much like MetaSearch, for those of you who are familiar with +it, and requires very little setup effort. + +If you're coming from MetaSearch, things to note: + + 1. The default param key for search params is now `:q`, instead of `:search`. + This is primarily to shorten query strings, though advanced queries (below) + will still run afoul of URL length limits in most browsers and require a + switch to HTTP POST requests. This key is + [configurable](default-search-parameter) via setting the `search_key` option + in your Ransack intitializer file. + + 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search + object is passed to it. + + 3. Common ActiveRecord::Relation methods are no longer delegated by the + search object. Instead, you will get your search results (an + ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to + `Ransack#result`. + ## Mongoid Mongoid support has been moved to its own gem at [ransack-mongoid](https://github.com/activerecord-hackery/ransack-mongoid).