Whereable
Translates where-like filter syntax into an Arel-based ActiveRecord scope, so you can safely use SQL syntax in Rails controller parameters. What it lacks in power, it gains in simplicity and ease of use for API consumers.
Installation
Add this line to your application's Gemfile:
gem 'whereable'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install whereable
Usage
Imagine a User model:
class User < ActiveRecord::Base
include Whereable
validates :username, presence: true, uniqueness: true
validates :born_on, presence: true
enum role: { standard: 0, admin: 1 }
end
With this data:
User.create!(username: 'Morpheus', role: :admin, born_on: '1961-07-30')
User.create!(username: 'Neo', role: :standard, born_on: '1964-09-02')
User.create!(username: 'Trinity', role: :standard, born_on: '1967-08-21')
Let's assume you're allowing filtered API access to your Users,
but using the #standard
scope to keep admins hidden. So your controller might include:
User.standard.where(params[:filter])
And your white hat API consumers pass in filter=born_on < '1967-01-01'
, and …
User.standard.where("born_on < '1967-01-01'")
returns Neo as expected, so we're all good.
Meanwhile… Your black hat API consumer passes in filter=true) or (true
, and …
User.standard.where("true) or (true")
returns EVERYONE, because the database query is …
SELECT "users".* FROM "users" WHERE "users"."role" = 0 AND (true) or (true)
This is how the Matrix gets hacked.
Instead add include Whereable
to your model, and change your controller to:
User.standard.whereable(params[:filter])
And then …
User.standard.whereable("born_on < '1967-01-01'")
returns Neo as before, but …
User.standard.whereable("true) or (true")
raises exception …
Whereable::FilterInvalid ('Invalid filter at ) or (true')
Syntax
- Supports and/or with nested parentheses as needed
- Recognizes these operators:
eq ne gte gt lte lt = != <> >= > <= <
, plusIN
andBETWEEN
- Column must be to left of operator, and literal(s) to right
- Comparing columns to each other is not supported
-
BETWEEN
requires two literals separated byAND
- Example:
publish_at between '2020-11-01 12:00 EST' and '2020-11-15 23:59:59 EST'
- Example:
-
IN
requires comma-separated literals in parentheses- Example:
username in (Morpheus, Trinity)
- Example:
- Quotes are optional unless the literal contains spaces or quotes
- Supports double or single quotes, and embedded quotes may be backslash escaped
- Also supports the PostgreSQL double-single embedded quote
- Enum literals must use the name, not the database value:
- 👍
User.whereable('role = admin')
- 👎
User.whereable('role = 1')
- 👍
More
See the Wiki for more.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/MacksMind/whereable.
License
The gem is available as open source under the terms of the MIT License.