Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config option for turning off custom scope argument sanitization #742

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

*Garett Arrowood*

* Add ability to turn off sanitization of custom scope arguments.
PR [#742](https://github.com/activerecord-hackery/ransack/pull/742).

*Garett Arrowood*

### Fixed

* Use class attributes properly so that inheritance is respected.
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,21 @@ Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user })
In Rails 3 and 4, if the `true` value is being passed via url params or some
other mechanism that will convert it to a string, the true value may not be
passed to the ransackable scope unless you wrap it in an array
(i.e. `activated: ['true']`). This is currently resolved in Rails 5 :smiley:
(i.e. `activated: ['true']`). Ransack will take care of changing 'true' into a
boolean. This is currently resolved in Rails 5 :smiley:

However, perhaps you have `user_id: [1]` and you do not want Ransack to convert
1 into a boolean. (Values sanitized to booleans can be found in the
[constants.rb](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb#L28)).
To turn this off, and handle type conversions yourself, set
`sanitize_custom_scope_booleans` to false in an initializer file like
config/initializers/ransack.rb:

```ruby
Ransack.configure do |c|
c.sanitize_custom_scope_booleans = false
end
```

Scopes are a recent addition to Ransack and currently have a few caveats:
First, a scope involving child associations needs to be defined in the parent
Expand Down
19 changes: 18 additions & 1 deletion lib/ransack/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ module Configuration
:ignore_unknown_conditions => true,
:hide_sort_order_indicators => false,
:up_arrow => '▼'.freeze,
:down_arrow => '▲'.freeze
:down_arrow => '▲'.freeze,
:sanitize_scope_args => true
}

def configure
Expand Down Expand Up @@ -98,6 +99,22 @@ def custom_arrows=(opts = {})
self.options[:down_arrow] = opts[:down_arrow].freeze if opts[:down_arrow]
end

# Ransack sanitizes many values in your custom scopes into booleans.
# [1, '1', 't', 'T', 'true', 'TRUE'] all evaluate to true.
# [0, '0', 'f', 'F', 'false', 'FALSE'] all evaluate to false.
#
# This default may be globally overridden in an initializer file like
# `config/initializers/ransack.rb` as follows:
#
# Ransack.configure do |config|
# # Accept my custom scope values as what they are.
# config.sanitize_custom_scope_booleans = false
# end
#
def sanitize_custom_scope_booleans=(boolean)
self.options[:sanitize_scope_args] = boolean
end

# By default, Ransack displays sort order indicator arrows in sort links.
# The default may be globally overridden in an initializer file like
# `config/initializers/ransack.rb` as follows:
Expand Down
10 changes: 8 additions & 2 deletions lib/ransack/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,18 @@ def inspect
private

def add_scope(key, args)
sanitized_args = if Ransack.options[:sanitize_scope_args]
sanitized_scope_args(args)
else
args
end

if @context.scope_arity(key) == 1
@scope_args[key] = args.is_a?(Array) ? args[0] : args
else
@scope_args[key] = args.is_a?(Array) ? sanitized_scope_args(args) : args
@scope_args[key] = args.is_a?(Array) ? sanitized_args : args
end
@context.chain_scope(key, sanitized_scope_args(args))
@context.chain_scope(key, sanitized_args)
end

def sanitized_scope_args(args)
Expand Down
34 changes: 20 additions & 14 deletions spec/ransack/adapters/active_record/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,31 @@ module ActiveRecord
expect(s.result.to_sql).to (include 'age > 18')
end

# TODO: Implement a way to pass true/false values like 0 or 1 to
# scopes (e.g. with `in` / `not_in` predicates), without Ransack
# converting them to true/false boolean values instead.

# it 'passes true values to scopes', focus: true do
# s = Person.ransack('over_age' => 1)
# expect(s.result.to_sql).to (include 'age > 1')
# end

# it 'passes false values to scopes', focus: true do
# s = Person.ransack('over_age' => 0)
# expect(s.result.to_sql).to (include 'age > 0')
# end

it 'chains scopes' do
s = Person.ransack('over_age' => 18, 'active' => true)
expect(s.result.to_sql).to (include 'age > 18')
expect(s.result.to_sql).to (include 'active = 1')
end

context "with sanitize_custom_scope_booleans set to false" do
before(:all) do
Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
end

after(:all) do
Ransack.configure { |c| c.sanitize_custom_scope_booleans = true }
end

it 'passes true values to scopes' do
s = Person.ransack('over_age' => 1)
expect(s.result.to_sql).to (include 'age > 1')
end

it 'passes false values to scopes' do
s = Person.ransack('over_age' => 0)
expect(s.result.to_sql).to (include 'age > 0')
end
end
end

it 'does not raise exception for string :params argument' do
Expand Down