2011-06-06 21:32:03 -04:00
|
|
|
# Ransack
|
|
|
|
|
2014-04-24 03:14:51 -04:00
|
|
|
[![Build Status](https://travis-ci.org/activerecord-hackery/ransack.svg)]
|
|
|
|
(https://travis-ci.org/activerecord-hackery/ransack)
|
|
|
|
[![Gem Version](https://badge.fury.io/rb/ransack.svg)]
|
|
|
|
(http://badge.fury.io/rb/ransack)
|
|
|
|
|
|
|
|
Ransack is a rewrite of [MetaSearch]
|
|
|
|
(https://github.com/activerecord-hackery/meta_search)
|
|
|
|
created by [Ernie Miller](http://twitter.com/erniemiller)
|
|
|
|
and maintained by [Ryan Bigg](http://twitter.com/ryanbigg),
|
2014-08-29 19:31:39 -04:00
|
|
|
[Jon Atack](http://twitter.com/jonatack) and a great group of [contributors]
|
|
|
|
(https://github.com/activerecord-hackery/ransack/graphs/contributors).
|
2014-04-24 03:14:51 -04:00
|
|
|
While it supports many of the same features as MetaSearch, its underlying
|
|
|
|
implementation differs greatly from MetaSearch,
|
2014-08-17 18:49:55 -04:00
|
|
|
and backwards compatibility is not a design goal.
|
2014-04-24 03:14:51 -04:00
|
|
|
|
|
|
|
Ransack enables the creation of both simple and
|
|
|
|
[advanced](http://ransack-demo.herokuapp.com/users/advanced_search)
|
2014-12-12 18:00:50 -05:00
|
|
|
search forms for your Ruby on Rails application (demo source code
|
2014-04-24 03:14:51 -04:00
|
|
|
[here](https://github.com/activerecord-hackery/ransack_demo)).
|
|
|
|
If you're looking for something that simplifies query generation at the model
|
|
|
|
or controller layer, you're probably not looking for Ransack (or MetaSearch,
|
|
|
|
for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel)
|
|
|
|
instead.
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-12-17 18:01:21 -05:00
|
|
|
If you're viewing this at
|
2014-12-17 18:05:07 -05:00
|
|
|
[github.com/activerecord-hackery/ransack](https://github.com/activerecord-hackery/ransack),
|
2014-12-17 18:01:21 -05:00
|
|
|
you're reading the documentation for the master branch with the latest features.
|
|
|
|
[View documentation for the last release (1.5.1).](https://github.com/activerecord-hackery/ransack/tree/v.1.5.1)
|
|
|
|
|
2011-06-06 21:32:03 -04:00
|
|
|
## Getting started
|
|
|
|
|
2014-12-17 18:01:21 -05:00
|
|
|
Ransack is compatible with Rails 3 and 4 (including 4.2) on Ruby 1.9 and later.
|
2015-01-05 13:00:35 -05:00
|
|
|
We try to keep it functioning with Rails master too, although frequent changes
|
|
|
|
in Arel and Active Record make that a moving target. Ransack works
|
2014-12-17 18:01:21 -05:00
|
|
|
out-of-the-box with Active Record and features new support for Mongoid 4.0
|
|
|
|
(without associations, further details below). If you are on Ruby 1.8, you may
|
|
|
|
need to use a slightly earlier version of Ransack, like '< 1.4'.
|
2014-09-23 09:24:50 -04:00
|
|
|
|
|
|
|
In your Gemfile, for the last officially released Ransack gem:
|
2014-07-24 11:38:27 -04:00
|
|
|
|
|
|
|
```ruby
|
|
|
|
gem 'ransack'
|
|
|
|
```
|
|
|
|
|
2014-11-06 17:56:24 -05:00
|
|
|
Or, if you would like to use the latest updates, use the `master` branch:
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
2014-07-25 05:50:47 -04:00
|
|
|
gem 'ransack', github: 'activerecord-hackery/ransack'
|
2013-05-04 01:13:46 -04:00
|
|
|
```
|
2012-04-11 14:20:46 -04:00
|
|
|
|
2014-09-23 08:08:07 -04:00
|
|
|
The other branches (`rails-4`, `rails-4.1`, and `rails-4.2`) were each used for
|
2014-09-23 09:24:50 -04:00
|
|
|
developing and running Ransack with the latest upcoming version of Rails at the
|
2014-10-01 18:05:15 -04:00
|
|
|
time. They are smaller and possibly slightly faster because they do not have to
|
2014-09-23 09:24:50 -04:00
|
|
|
support previous versions of Rails and Active Record. Once support for that
|
|
|
|
Rails version is merged from the branch into Ransack master, the branch is no
|
|
|
|
longer actively maintained -- unless the open source community submits pull
|
|
|
|
requests to maintain them. You are welcome to do so!
|
2013-12-18 14:17:06 -05:00
|
|
|
|
2014-10-01 18:05:15 -04:00
|
|
|
To use one of the branches, for example the `rails-4.1` branch:
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
gem 'ransack', github: 'activerecord-hackery/ransack', branch: 'rails-4.1'
|
|
|
|
```
|
|
|
|
|
2015-01-05 14:41:31 -05:00
|
|
|
If you are using Rails master, be advised that Ransack master does not
|
|
|
|
currently work with the latest breaking changes in Rails master and
|
|
|
|
Arel 7/master of late December 2014. The most recent working commits are:
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
gem 'rails', github: 'rails/rails', ref: '266ff70'
|
|
|
|
gem 'arel', github: 'rails/arel', ref: '008445d'
|
|
|
|
```
|
|
|
|
|
2011-06-06 21:32:03 -04:00
|
|
|
## Usage
|
|
|
|
|
|
|
|
Ransack can be used in one of two modes, simple or advanced.
|
|
|
|
|
|
|
|
### Simple Mode
|
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
This mode works much like MetaSearch, for those of you who are familiar with
|
|
|
|
it, and requires very little setup effort.
|
2011-06-06 21:32:03 -04:00
|
|
|
|
|
|
|
If you're coming from MetaSearch, things to note:
|
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
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
|
2015-01-05 13:00:35 -05:00
|
|
|
`Ransack#result`.
|
2014-11-18 17:48:45 -05:00
|
|
|
|
2014-09-19 17:14:17 -04:00
|
|
|
4. If passed `distinct: true`, `result` will generate a `SELECT DISTINCT` to
|
|
|
|
avoid returning duplicate rows, even if conditions on a join would otherwise
|
2014-12-09 14:52:37 -05:00
|
|
|
result in some. It generates the same SQL as calling `uniq` on the relation.
|
2014-05-11 11:10:08 -04:00
|
|
|
|
|
|
|
Please note that for many databases, a sort on an associated table's columns
|
2014-09-13 15:07:19 -04:00
|
|
|
may result in invalid SQL with `distinct: true` -- in those cases, you're on
|
2014-05-11 11:10:08 -04:00
|
|
|
your own, and will need to modify the result as needed to allow these queries
|
2014-12-09 14:37:48 -05:00
|
|
|
to work.
|
|
|
|
|
2014-12-09 14:52:37 -05:00
|
|
|
If `distinct: true` or `uniq` is causing invalid SQL, another way to remove
|
|
|
|
duplicates is to call `to_a.uniq` on the collection at the end (see the next
|
|
|
|
section below) -- with the caveat that the de-duping is taking place in Ruby
|
|
|
|
instead of in SQL, which is potentially slower and uses more memory, and that
|
|
|
|
it may display awkwardly with pagination if the number of results is greater
|
|
|
|
than the page size.
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-05-11 11:39:38 -04:00
|
|
|
####In your controller
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
|
|
|
def index
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Person.ransack(params[:q])
|
2013-08-06 13:47:59 -04:00
|
|
|
@people = @q.result(distinct: true)
|
2013-05-04 01:13:46 -04:00
|
|
|
end
|
|
|
|
```
|
2014-05-11 11:39:38 -04:00
|
|
|
or without `distinct:true`, for sorting on an associated table's columns (in
|
|
|
|
this example, with preloading each Person's Articles and pagination):
|
2014-05-11 11:10:08 -04:00
|
|
|
|
|
|
|
```ruby
|
|
|
|
def index
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Person.ransack(params[:q])
|
2014-05-11 11:10:08 -04:00
|
|
|
@people = @q.result.includes(:articles).page(params[:page])
|
2014-12-09 14:52:37 -05:00
|
|
|
|
2014-12-09 14:37:48 -05:00
|
|
|
# or use `to_a.uniq` to remove duplicates (can also be done in the view):
|
2014-12-09 14:52:37 -05:00
|
|
|
@people = @q.result.includes(:articles).page(params[:page]).to_a.uniq
|
2014-05-11 11:10:08 -04:00
|
|
|
end
|
|
|
|
```
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-05-11 11:39:38 -04:00
|
|
|
####In your view
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
The two primary Ransack view helpers are `search_form_for` and `sort_link`,
|
2014-08-17 18:36:50 -04:00
|
|
|
which are defined in
|
|
|
|
[Ransack::Helpers::FormHelper](lib/ransack/helpers/form_helper.rb).
|
2014-05-11 11:10:08 -04:00
|
|
|
|
2014-09-24 12:45:37 -04:00
|
|
|
#####Ransack's `search_form_for` helper replaces `form_for` for creating the view search form:
|
2014-05-11 11:31:09 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```erb
|
|
|
|
<%= search_form_for @q do |f| %>
|
2014-09-05 18:52:05 -04:00
|
|
|
|
2014-09-05 18:25:32 -04:00
|
|
|
# Search if the name field contains...
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.label :name_cont %>
|
2014-05-11 11:10:08 -04:00
|
|
|
<%= f.search_field :name_cont %>
|
2014-09-05 18:52:05 -04:00
|
|
|
|
2014-09-05 18:25:32 -04:00
|
|
|
# Search if an associated articles.title starts with...
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.label :articles_title_start %>
|
2014-05-11 11:10:08 -04:00
|
|
|
<%= f.search_field :articles_title_start %>
|
2014-09-05 18:52:05 -04:00
|
|
|
|
2014-09-05 18:25:32 -04:00
|
|
|
# Attributes may be chained. Search multiple attributes for one value...
|
2014-09-05 19:06:01 -04:00
|
|
|
<%= f.label :name_or_description_or_email_or_articles_title_cont %>
|
2014-09-05 19:10:12 -04:00
|
|
|
<%= f.search_field :name_or_description_or_email_or_articles_title_cont %>
|
2014-09-05 18:52:05 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.submit %>
|
|
|
|
<% end %>
|
|
|
|
```
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
`cont` (contains) and `start` (starts with) are just two of the available
|
|
|
|
search predicates. See [Constants]
|
2014-08-17 18:36:50 -04:00
|
|
|
(https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb)
|
|
|
|
for a full list and the [wiki]
|
|
|
|
(https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching)
|
|
|
|
for more information.
|
2012-03-29 10:50:36 -04:00
|
|
|
|
2014-05-11 11:39:38 -04:00
|
|
|
The `search_form_for` answer format can be set like this:
|
2014-04-21 11:23:50 -04:00
|
|
|
```erb
|
|
|
|
<%= search_form_for(@q, format: :pdf) do |f| %>
|
|
|
|
|
|
|
|
<%= search_form_for(@q, format: :json) do |f| %>
|
|
|
|
```
|
|
|
|
|
2014-09-24 12:45:37 -04:00
|
|
|
#####Ransack's `sort_link` helper creates table headers that are sortable links:
|
2014-05-11 11:10:08 -04:00
|
|
|
|
|
|
|
```erb
|
2014-10-05 16:58:08 -04:00
|
|
|
<%= sort_link(@q, :name) %>
|
2014-05-11 11:31:09 -04:00
|
|
|
```
|
|
|
|
Additional options can be passed after the column attribute, like a different
|
|
|
|
column title or a default sort order:
|
|
|
|
|
|
|
|
```erb
|
2014-10-05 17:04:40 -04:00
|
|
|
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
|
2014-05-11 11:10:08 -04:00
|
|
|
```
|
|
|
|
|
2014-11-18 17:48:45 -05:00
|
|
|
With a polymorphic association, you may need to specify the name of the link
|
|
|
|
explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue
|
|
|
|
[#421](https://github.com/activerecord-hackery/ransack/issues/421)):
|
|
|
|
|
|
|
|
```erb
|
|
|
|
<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
|
|
|
|
```
|
|
|
|
|
2014-10-05 16:58:08 -04:00
|
|
|
You can also sort on multiple fields by specifying an ordered array:
|
2014-10-03 20:43:53 -04:00
|
|
|
|
|
|
|
```erb
|
2014-10-05 17:04:40 -04:00
|
|
|
<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
|
2014-10-03 20:43:53 -04:00
|
|
|
```
|
|
|
|
|
2014-10-05 16:58:08 -04:00
|
|
|
In the example above, clicking the link will sort by `last_name` and then
|
|
|
|
`first_name`. Specifying the sort direction on a field in the array tells
|
|
|
|
Ransack to _always_ sort that particular field in the specified direction.
|
|
|
|
|
|
|
|
Multiple `default_order` fields may also be specified with a hash:
|
|
|
|
|
|
|
|
```erb
|
2014-10-14 16:17:30 -04:00
|
|
|
<%= sort_link(@q, :last_name, %i(last_name first_name),
|
2014-10-05 17:04:40 -04:00
|
|
|
default_order: { last_name: 'asc', first_name: 'desc' }) %>
|
2014-10-05 16:58:08 -04:00
|
|
|
```
|
|
|
|
|
|
|
|
This example toggles the sort directions of both fields, by default
|
|
|
|
initially sorting the `last_name` field by ascending order, and the
|
|
|
|
`first_name` field by descending order.
|
2014-10-03 20:43:53 -04:00
|
|
|
|
2014-11-25 17:57:34 -05:00
|
|
|
The sort link may be displayed without the order indicator arrow by passing
|
|
|
|
`hide_indicator: true`:
|
2014-11-24 10:48:29 -05:00
|
|
|
|
|
|
|
```erb
|
|
|
|
<%= sort_link(@q, :name, hide_indicator: true) %>
|
|
|
|
```
|
|
|
|
|
2011-06-06 21:32:03 -04:00
|
|
|
### Advanced Mode
|
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
"Advanced" searches (ab)use Rails' nested attributes functionality in order to
|
|
|
|
generate complex queries with nested AND/OR groupings, etc. This takes a bit
|
|
|
|
more work but can generate some pretty cool search interfaces that put a lot of
|
|
|
|
power in the hands of your users. A notable drawback with these searches is
|
|
|
|
that the increased size of the parameter string will typically force you to use
|
|
|
|
the HTTP POST method instead of GET. :(
|
2011-06-06 21:32:03 -04:00
|
|
|
|
|
|
|
This means you'll need to tweak your routes...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
|
|
|
resources :people do
|
|
|
|
collection do
|
2013-08-06 13:47:59 -04:00
|
|
|
match 'search' => 'people#search', via: [:get, :post], as: :search
|
2013-05-04 01:13:46 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
2011-06-06 21:32:03 -04:00
|
|
|
|
|
|
|
... and add another controller action ...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
|
|
|
def search
|
|
|
|
index
|
|
|
|
render :index
|
|
|
|
end
|
|
|
|
```
|
2012-03-29 10:50:36 -04:00
|
|
|
|
2011-06-06 21:32:03 -04:00
|
|
|
... and update your `search_form_for` line in the view ...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```erb
|
2013-08-06 13:47:59 -04:00
|
|
|
<%= search_form_for @q, url: search_people_path,
|
|
|
|
html: { method: :post } do |f| %>
|
2013-05-04 01:13:46 -04:00
|
|
|
```
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-04-28 10:49:26 -04:00
|
|
|
Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](lib/ransack/helpers/form_builder.rb) to
|
2011-06-11 14:22:41 -04:00
|
|
|
construct much more complex search forms, such as the one on the
|
2013-12-18 14:45:49 -05:00
|
|
|
[demo page](http://ransack-demo.heroku.com) (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-03-26 11:13:55 -04:00
|
|
|
### Ransack #search method
|
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
Ransack will try to to make `#search` available in your models, but in the case
|
2015-01-05 13:00:35 -05:00
|
|
|
that `#search` has already been defined, you can always use the default
|
|
|
|
`#ransack` method. For example, the following would be equivalent:
|
2014-03-26 11:13:55 -04:00
|
|
|
|
2014-04-11 22:44:42 -04:00
|
|
|
```ruby
|
2014-03-26 11:13:55 -04:00
|
|
|
Article.ransack(params[:q])
|
2015-01-05 13:00:35 -05:00
|
|
|
Article.search(params[:q])
|
2014-03-26 11:13:55 -04:00
|
|
|
```
|
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
Users have reported issues of name conflicts with other gems, so `#search` may
|
|
|
|
possibly be deprecated in the next major version of Ransack.
|
|
|
|
|
|
|
|
For now, if Ransack's `#search` method conflicts with the name of another
|
|
|
|
method named `search` in your code or another gem, you may resolve it either by
|
|
|
|
patching the `extended` class_method in `Ransack::Adapters::ActiveRecord::Base`
|
|
|
|
to remove the line `alias :search :ransack unless base.respond_to? :search`, or
|
|
|
|
by placing the following line in your Ransack initializer file at
|
2014-11-09 17:33:40 -05:00
|
|
|
`config/initializers/ransack.rb`:
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
Ransack::Adapters::ActiveRecord::Base.class_eval('remove_method :search')
|
|
|
|
```
|
|
|
|
|
2014-09-01 17:43:58 -04:00
|
|
|
### Associations
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-09-01 17:43:58 -04:00
|
|
|
You can easily use Ransack to search for objects in `has_many` and `belongs_to`
|
|
|
|
associations.
|
2012-10-06 17:14:47 -04:00
|
|
|
|
|
|
|
Given you have these associations ...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
|
|
|
class Employee < ActiveRecord::Base
|
|
|
|
belongs_to :supervisor
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-09-05 19:37:37 -04:00
|
|
|
# has attributes first_name:string and last_name:string
|
2013-05-04 01:13:46 -04:00
|
|
|
end
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
class Department < ActiveRecord::Base
|
|
|
|
has_many :supervisors
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
# has attribute title:string
|
|
|
|
end
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
class Supervisor < ActiveRecord::Base
|
|
|
|
belongs_to :department
|
|
|
|
has_many :employees
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-09-05 19:37:37 -04:00
|
|
|
# has attribute last_name:string
|
2013-05-04 01:13:46 -04:00
|
|
|
end
|
|
|
|
```
|
2012-10-06 17:14:47 -04:00
|
|
|
|
|
|
|
... and a controller ...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```ruby
|
|
|
|
class SupervisorsController < ApplicationController
|
|
|
|
def index
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Supervisor.ransack(params[:q])
|
2014-09-01 17:43:58 -04:00
|
|
|
@supervisors = @q.result.includes(:department, :employees)
|
2013-05-04 01:13:46 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
2012-10-06 17:14:47 -04:00
|
|
|
|
|
|
|
... you might set up your form like this ...
|
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
```erb
|
2014-09-01 17:43:58 -04:00
|
|
|
<%= search_form_for @q do |f| %>
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.label :last_name_cont %>
|
2014-05-11 11:31:09 -04:00
|
|
|
<%= f.search_field :last_name_cont %>
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.label :department_title_cont %>
|
2014-05-11 11:31:09 -04:00
|
|
|
<%= f.search_field :department_title_cont %>
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-09-05 19:30:37 -04:00
|
|
|
<%= f.label :employees_first_name_or_employees_last_name_cont %>
|
|
|
|
<%= f.search_field :employees_first_name_or_employees_last_name_cont %>
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2013-05-04 01:13:46 -04:00
|
|
|
<%= f.submit "search" %>
|
|
|
|
<% end %>
|
2014-05-11 11:31:09 -04:00
|
|
|
...
|
2014-12-10 18:16:58 -05:00
|
|
|
<%= content_tag :table do %>
|
2014-05-11 11:31:09 -04:00
|
|
|
<%= content_tag :th, sort_link(@q, :last_name) %>
|
|
|
|
<%= content_tag :th, sort_link(@q, 'departments.title') %>
|
|
|
|
<%= content_tag :th, sort_link(@q, 'employees.last_name') %>
|
|
|
|
<% end %>
|
2013-05-04 01:13:46 -04:00
|
|
|
```
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-05-09 17:28:43 -04:00
|
|
|
### Using Ransackers to add custom search functions via Arel
|
2014-05-07 18:03:04 -04:00
|
|
|
|
|
|
|
The main premise behind Ransack is to provide access to
|
2014-05-11 11:10:08 -04:00
|
|
|
**Arel predicate methods**. Ransack provides special methods, called
|
|
|
|
_ransackers_, for creating additional search functions via Arel. More
|
|
|
|
information about `ransacker` methods can be found [here in the wiki]
|
2014-05-07 18:03:04 -04:00
|
|
|
(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers).
|
|
|
|
Feel free to contribute working `ransacker` code examples to the wiki!
|
|
|
|
|
2014-08-29 19:48:00 -04:00
|
|
|
### Authorization (whitelisting/blacklisting)
|
2014-06-22 05:15:10 -04:00
|
|
|
|
2014-09-13 16:36:54 -04:00
|
|
|
By default, searching and sorting are authorized on any column of your model
|
|
|
|
and no class methods/scopes are whitelisted.
|
|
|
|
|
2014-08-29 19:48:00 -04:00
|
|
|
Ransack adds four methods to `ActiveRecord::Base` that you can redefine as
|
|
|
|
class methods in your models to apply selective authorization:
|
|
|
|
`ransackable_attributes`, `ransackable_associations`, `ransackable_scopes` and
|
2014-08-29 19:31:39 -04:00
|
|
|
`ransortable_attributes`.
|
2014-08-29 18:00:03 -04:00
|
|
|
|
2014-08-29 18:09:47 -04:00
|
|
|
Here is how these four methods are implemented in Ransack:
|
2014-08-29 18:00:03 -04:00
|
|
|
|
|
|
|
```ruby
|
2014-09-24 12:45:37 -04:00
|
|
|
# Ransackable_attributes, by default, returns all column names
|
|
|
|
# and any defined ransackers as an array of strings.
|
|
|
|
# For overriding with a whitelist array of strings.
|
|
|
|
#
|
|
|
|
def ransackable_attributes(auth_object = nil)
|
|
|
|
column_names + _ransackers.keys
|
|
|
|
end
|
2014-08-29 18:00:03 -04:00
|
|
|
|
2014-09-24 12:45:37 -04:00
|
|
|
# Ransackable_associations, by default, returns the names
|
|
|
|
# of all associations as an array of strings.
|
2014-09-19 17:14:17 -04:00
|
|
|
# For overriding with a whitelist array of strings.
|
2014-09-24 12:45:37 -04:00
|
|
|
#
|
|
|
|
def ransackable_associations(auth_object = nil)
|
|
|
|
reflect_on_all_associations.map { |a| a.name.to_s }
|
|
|
|
end
|
2014-08-29 18:09:47 -04:00
|
|
|
|
2014-09-24 12:45:37 -04:00
|
|
|
# Ransortable_attributes, by default, returns the names
|
|
|
|
# of all attributes available for sorting as an array of strings.
|
|
|
|
# For overriding with a whitelist array of strings.
|
|
|
|
#
|
|
|
|
def ransortable_attributes(auth_object = nil)
|
|
|
|
ransackable_attributes(auth_object)
|
|
|
|
end
|
2014-09-13 16:36:54 -04:00
|
|
|
|
2014-09-24 12:45:37 -04:00
|
|
|
# Ransackable_scopes, by default, returns an empty array
|
|
|
|
# i.e. no class methods/scopes are authorized.
|
|
|
|
# For overriding with a whitelist array of *symbols*.
|
|
|
|
#
|
|
|
|
def ransackable_scopes(auth_object = nil)
|
|
|
|
[]
|
|
|
|
end
|
2014-08-29 18:00:03 -04:00
|
|
|
```
|
|
|
|
|
2014-09-13 16:36:54 -04:00
|
|
|
Any values not returned from these methods will be ignored by Ransack, i.e.
|
|
|
|
they are not authorized.
|
2014-08-29 19:31:39 -04:00
|
|
|
|
2014-08-29 18:09:47 -04:00
|
|
|
All four methods can receive a single optional parameter, `auth_object`. When
|
|
|
|
you call the search or ransack method on your model, you can provide a value
|
2014-08-29 19:48:00 -04:00
|
|
|
for an `auth_object` key in the options hash which can be used by your own
|
2014-08-29 18:32:29 -04:00
|
|
|
overridden methods.
|
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
Here is an example that puts all this together, adapted from
|
2014-08-29 19:48:00 -04:00
|
|
|
[this blog post by Ernie Miller]
|
|
|
|
(http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/).
|
2014-08-31 08:42:07 -04:00
|
|
|
In an `Article` model, add the following `ransackable_attributes` class method
|
|
|
|
(preferably private):
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-29 18:00:03 -04:00
|
|
|
```ruby
|
2014-08-31 08:42:07 -04:00
|
|
|
class Article < ActiveRecord::Base
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2014-08-29 18:00:03 -04:00
|
|
|
def self.ransackable_attributes(auth_object = nil)
|
2014-08-31 08:42:07 -04:00
|
|
|
if auth_object == :admin
|
2014-08-29 18:44:15 -04:00
|
|
|
# whitelist all attributes for admin
|
2014-08-29 18:00:03 -04:00
|
|
|
super
|
|
|
|
else
|
2014-08-29 18:32:29 -04:00
|
|
|
# whitelist only the title and body attributes for other users
|
2014-11-18 17:48:45 -05:00
|
|
|
super & %w(title body)
|
2014-08-29 18:00:03 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
Here is example code for the `articles_controller`:
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
```ruby
|
|
|
|
class ArticlesController < ApplicationController
|
|
|
|
|
|
|
|
def index
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Article.ransack(params[:q], auth_object: set_ransack_auth_object)
|
2014-08-31 08:42:07 -04:00
|
|
|
@articles = @q.result
|
|
|
|
end
|
2014-11-18 17:48:45 -05:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
private
|
|
|
|
|
|
|
|
def set_ransack_auth_object
|
|
|
|
current_user.admin? ? :admin : nil
|
|
|
|
end
|
|
|
|
end
|
2014-08-29 18:00:03 -04:00
|
|
|
```
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
Trying it out in `rails console`:
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
```ruby
|
2014-08-29 18:00:03 -04:00
|
|
|
> Article
|
2014-11-18 17:48:45 -05:00
|
|
|
=> Article(id: integer, person_id: integer, title: string, body: text)
|
2014-08-29 18:00:03 -04:00
|
|
|
|
|
|
|
> Article.ransackable_attributes
|
2014-11-18 17:48:45 -05:00
|
|
|
=> ["title", "body"]
|
2014-08-29 18:00:03 -04:00
|
|
|
|
2014-08-31 08:42:07 -04:00
|
|
|
> Article.ransackable_attributes(:admin)
|
2014-11-18 17:48:45 -05:00
|
|
|
=> ["id", "person_id", "title", "body"]
|
2014-08-29 18:00:03 -04:00
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
> Article.ransack(id_eq: 1).result.to_sql
|
2014-08-29 18:00:03 -04:00
|
|
|
=> SELECT "articles".* FROM "articles" # Note that search param was ignored!
|
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
> Article.ransack({ id_eq: 1 }, { auth_object: nil }).result.to_sql
|
2014-09-02 05:28:17 -04:00
|
|
|
=> SELECT "articles".* FROM "articles" # Search param still ignored!
|
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
> Article.ransack({ id_eq: 1 }, { auth_object: :admin }).result.to_sql
|
2014-08-29 18:00:03 -04:00
|
|
|
=> SELECT "articles".* FROM "articles" WHERE "articles"."id" = 1
|
|
|
|
```
|
2014-09-24 12:45:37 -04:00
|
|
|
|
2014-08-29 19:48:00 -04:00
|
|
|
That's it! Now you know how to whitelist/blacklist various elements in Ransack.
|
2014-08-29 18:00:03 -04:00
|
|
|
|
2014-09-10 18:18:43 -04:00
|
|
|
### Using Scopes/Class Methods
|
2014-06-22 05:15:10 -04:00
|
|
|
|
2014-09-10 18:18:43 -04:00
|
|
|
Continuing on from the preceding section, searching by scopes requires defining
|
2014-09-19 17:22:40 -04:00
|
|
|
a whitelist of `ransackable_scopes` on the model class. The whitelist should be
|
|
|
|
an array of *symbols*. By default, all class methods (e.g. scopes) are ignored.
|
|
|
|
Scopes will be applied for matching `true` values, or for given values if the
|
|
|
|
scope accepts a value:
|
2014-06-22 05:15:10 -04:00
|
|
|
|
2014-08-29 19:31:39 -04:00
|
|
|
```ruby
|
2014-09-10 17:59:12 -04:00
|
|
|
class Employee < ActiveRecord::Base
|
2014-10-01 18:05:15 -04:00
|
|
|
scope :active, ->(boolean = true) { where(active: boolean) }
|
2014-09-10 18:46:25 -04:00
|
|
|
scope :salary_gt, ->(amount) { where('salary > ?', amount) }
|
2014-09-10 17:59:12 -04:00
|
|
|
|
2014-09-10 18:46:25 -04:00
|
|
|
# Scopes are just syntactical sugar for class methods, which may also be used:
|
2014-09-10 17:59:12 -04:00
|
|
|
|
2014-09-10 18:46:25 -04:00
|
|
|
def self.hired_since(date)
|
|
|
|
where('start_date >= ?', date)
|
2014-09-10 17:59:12 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2014-09-10 18:18:43 -04:00
|
|
|
|
|
|
|
def self.ransackable_scopes(auth_object = nil)
|
|
|
|
if auth_object.try(:admin?)
|
2014-09-11 13:02:12 -04:00
|
|
|
# allow admin users access to all three methods
|
2014-09-10 17:59:12 -04:00
|
|
|
%i(active hired_since salary_gt)
|
2014-09-10 18:18:43 -04:00
|
|
|
else
|
2014-09-11 13:02:12 -04:00
|
|
|
# allow other users to search on active and hired_since only
|
2014-09-10 18:18:43 -04:00
|
|
|
%i(active hired_since)
|
2014-09-10 17:59:12 -04:00
|
|
|
end
|
2014-09-10 18:18:43 -04:00
|
|
|
end
|
2014-09-11 13:02:12 -04:00
|
|
|
end
|
2014-09-10 17:59:12 -04:00
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
Employee.ransack({ active: true, hired_since: '2013-01-01' })
|
2014-08-29 19:31:39 -04:00
|
|
|
|
2015-01-05 13:00:35 -05:00
|
|
|
Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user })
|
2014-06-22 05:15:10 -04:00
|
|
|
```
|
|
|
|
|
2014-11-01 16:50:49 -04:00
|
|
|
If the `true` value is being passed via url params or by some other mechanism
|
2014-11-01 20:10:34 -04:00
|
|
|
that will convert it to a string (i.e. `active: 'true'` instead of
|
|
|
|
`active: true`), the true value will *not* be passed to the scope. If you want
|
|
|
|
to pass a `'true'` string to the scope, you should wrap it in an array (i.e.
|
|
|
|
`active: ['true']`).
|
2014-10-30 13:24:43 -04:00
|
|
|
|
2014-10-03 13:06:57 -04:00
|
|
|
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
|
|
|
|
table model, not in the child model. Second, scopes with an array as an
|
|
|
|
argument are not easily usable yet, because the array currently needs to be
|
|
|
|
wrapped in an array to function (see
|
|
|
|
[this issue](https://github.com/activerecord-hackery/ransack/issues/404)),
|
|
|
|
which is not compatible with Ransack form helpers. For this use case, it may be
|
|
|
|
better for now to use [ransackers]
|
2014-11-01 20:10:34 -04:00
|
|
|
(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead,
|
2014-10-30 13:24:43 -04:00
|
|
|
where feasible. Pull requests with solutions and tests are welcome!
|
2014-10-01 18:05:15 -04:00
|
|
|
|
2014-09-05 19:18:03 -04:00
|
|
|
### Grouping queries by OR instead of AND
|
|
|
|
|
|
|
|
The default `AND` grouping can be changed to `OR` by adding `m: 'or'` to the
|
2014-09-05 19:30:37 -04:00
|
|
|
query hash.
|
|
|
|
|
2014-09-07 17:29:41 -04:00
|
|
|
You can easily try it in your controller code by changing `params[:q]` in the
|
|
|
|
`index` action to `params[:q].try(:merge, m: 'or')` as follows:
|
2014-09-07 17:18:07 -04:00
|
|
|
|
|
|
|
```ruby
|
|
|
|
def index
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Artist.ransack(params[:q].try(:merge, m: 'or'))
|
2014-09-07 17:18:07 -04:00
|
|
|
@artists = @q.result
|
|
|
|
end
|
|
|
|
```
|
2014-09-07 17:29:41 -04:00
|
|
|
Normally, if you wanted users to be able to toggle between `AND` and `OR`
|
|
|
|
query grouping, you would probably set up your search form so that `m` was in
|
|
|
|
the URL params hash, but here we assigned `m` manually just to try it out
|
|
|
|
quickly.
|
2014-09-07 17:18:07 -04:00
|
|
|
|
|
|
|
Alternatively, trying it in the Rails console:
|
2014-09-05 19:18:03 -04:00
|
|
|
|
|
|
|
```ruby
|
2015-01-05 13:00:35 -05:00
|
|
|
artists = Artist.ransack(name_cont: 'foo', style_cont: 'bar', m: 'or')
|
2014-09-05 19:18:03 -04:00
|
|
|
=> Ransack::Search<class: Artist, base: Grouping <conditions: [
|
|
|
|
Condition <attributes: ["name"], predicate: cont, values: ["foo"]>,
|
|
|
|
Condition <attributes: ["style"], predicate: cont, values: ["bar"]>
|
|
|
|
], combinator: or>>
|
|
|
|
|
|
|
|
artists.result.to_sql
|
|
|
|
=> "SELECT \"artists\".* FROM \"artists\"
|
|
|
|
WHERE ((\"artists\".\"name\" ILIKE '%foo%'
|
|
|
|
OR \"artists\".\"style\" ILIKE '%bar%'))"
|
|
|
|
```
|
|
|
|
|
2014-09-07 17:05:06 -04:00
|
|
|
The combinator becomes `or` instead of the default `and`, and the SQL query
|
|
|
|
becomes `WHERE...OR` instead of `WHERE...AND`.
|
|
|
|
|
2014-09-05 19:18:03 -04:00
|
|
|
This works with associations as well. Imagine an Artist model that has many
|
|
|
|
Memberships, and many Musicians through Memberships:
|
|
|
|
|
|
|
|
```ruby
|
2015-01-05 13:00:35 -05:00
|
|
|
artists = Artist.ransack(name_cont: 'foo', musicians_email_cont: 'bar', m: 'or')
|
2014-09-05 19:18:03 -04:00
|
|
|
=> Ransack::Search<class: Artist, base: Grouping <conditions: [
|
|
|
|
Condition <attributes: ["name"], predicate: cont, values: ["foo"]>,
|
|
|
|
Condition <attributes: ["musicians_email"], predicate: cont, values: ["bar"]>
|
|
|
|
], combinator: or>>
|
|
|
|
|
|
|
|
artists.result.to_sql
|
|
|
|
=> "SELECT \"artists\".* FROM \"artists\"
|
|
|
|
LEFT OUTER JOIN \"memberships\"
|
|
|
|
ON \"memberships\".\"artist_id\" = \"artists\".\"id\"
|
|
|
|
LEFT OUTER JOIN \"musicians\"
|
|
|
|
ON \"musicians\".\"id\" = \"memberships\".\"musician_id\"
|
|
|
|
WHERE ((\"artists\".\"name\" ILIKE '%foo%'
|
|
|
|
OR \"musicians\".\"email\" ILIKE '%bar%'))"
|
|
|
|
```
|
|
|
|
|
|
|
|
### Using SimpleForm
|
|
|
|
|
2014-10-22 17:53:26 -04:00
|
|
|
If you would like to combine the Ransack and SimpleForm form builders, set the
|
|
|
|
`RANSACK_FORM_BUILDER` environment variable before Rails boots up, e.g. in
|
|
|
|
`config/application.rb` before `require 'rails/all'` as shown below (and add
|
2014-10-23 17:09:35 -04:00
|
|
|
`gem 'simple_form'` in your Gemfile).
|
2014-09-05 19:18:03 -04:00
|
|
|
|
|
|
|
```ruby
|
|
|
|
require File.expand_path('../boot', __FILE__)
|
|
|
|
ENV['RANSACK_FORM_BUILDER'] = '::SimpleForm::FormBuilder'
|
|
|
|
require 'rails/all'
|
|
|
|
```
|
|
|
|
|
2014-05-09 17:28:43 -04:00
|
|
|
### I18n
|
2013-10-07 07:47:52 -04:00
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
Ransack translation files are available in
|
|
|
|
[Ransack::Locale](lib/ransack/locale). You may also be interested in one of the
|
|
|
|
many translations for Ransack available at
|
|
|
|
http://www.localeapp.com/projects/2999.
|
2012-10-06 17:14:47 -04:00
|
|
|
|
2014-10-22 17:41:58 -04:00
|
|
|
Predicate and attribute translations in forms may be specified as follows (see
|
2014-10-22 17:53:26 -04:00
|
|
|
the translation files in [Ransack::Locale](lib/ransack/locale) for more examples):
|
2014-10-22 17:41:58 -04:00
|
|
|
|
2014-10-22 17:53:26 -04:00
|
|
|
locales/en.yml:
|
2014-10-22 17:41:58 -04:00
|
|
|
```yml
|
|
|
|
en:
|
|
|
|
ransack:
|
|
|
|
asc: ascending
|
|
|
|
desc: descending
|
|
|
|
predicates:
|
|
|
|
cont: contains
|
|
|
|
not_cont: not contains
|
|
|
|
start: starts with
|
|
|
|
end: ends with
|
|
|
|
gt: greater than
|
|
|
|
lt: less than
|
|
|
|
attributes:
|
|
|
|
person:
|
|
|
|
name: Full Name
|
|
|
|
article:
|
|
|
|
title: Article Title
|
|
|
|
body: Main Content
|
|
|
|
```
|
|
|
|
|
|
|
|
Attribute names may also be changed globally, or under `activerecord`:
|
|
|
|
|
|
|
|
```yml
|
|
|
|
en:
|
|
|
|
attributes:
|
|
|
|
model_name:
|
|
|
|
model_field1: field name1
|
|
|
|
model_field2: field name2
|
|
|
|
activerecord:
|
|
|
|
attributes:
|
|
|
|
namespace/article:
|
|
|
|
title: AR Namespaced Title
|
|
|
|
namespace_article:
|
|
|
|
title: Old Ransack Namespaced Title
|
|
|
|
```
|
|
|
|
|
2014-11-04 15:18:57 -05:00
|
|
|
## Mongoid
|
2014-11-03 19:45:12 -05:00
|
|
|
|
2014-11-04 15:25:57 -05:00
|
|
|
Ransack now works with Mongoid in the same way as Active Record, except that
|
2014-11-04 15:18:57 -05:00
|
|
|
with Mongoid, associations are not currently supported. A demo app may be found
|
|
|
|
[here](http://ransack-mongodb-demo.herokuapp.com/) and the demo source code is
|
2014-11-04 15:25:57 -05:00
|
|
|
[here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method
|
2015-01-05 13:00:35 -05:00
|
|
|
called on a `ransack` search returns a `Mongoid::Criteria` object:
|
2014-11-03 19:45:12 -05:00
|
|
|
|
|
|
|
```ruby
|
2015-01-05 13:00:35 -05:00
|
|
|
@q = Person.ransack(params[:q])
|
2014-11-04 15:18:57 -05:00
|
|
|
@people = @q.result # => Mongoid::Criteria
|
2014-11-03 19:45:12 -05:00
|
|
|
|
2014-11-04 15:18:57 -05:00
|
|
|
# or you can add more Mongoid queries
|
|
|
|
@people = @q.result.active.order_by(updated_at: -1).limit(10)
|
2014-11-03 19:45:12 -05:00
|
|
|
```
|
|
|
|
|
2014-10-26 18:21:01 -04:00
|
|
|
## Semantic Versioning
|
|
|
|
|
|
|
|
Ransack attempts to follow semantic versioning in the format of `x.y.z`, where:
|
|
|
|
|
2014-10-26 18:25:16 -04:00
|
|
|
`x` stands for a major version (new features that are not backward-compatible).
|
2014-11-04 15:25:57 -05:00
|
|
|
|
2014-10-26 18:25:16 -04:00
|
|
|
`y` stands for a minor version (new features that are backward-compatible).
|
2014-11-04 15:25:57 -05:00
|
|
|
|
2014-10-26 18:25:16 -04:00
|
|
|
`z` stands for a patch (bug fixes).
|
2014-10-26 18:21:01 -04:00
|
|
|
|
2014-10-26 18:25:16 -04:00
|
|
|
In other words: `Major.Minor.Patch`.
|
2014-10-26 18:21:01 -04:00
|
|
|
|
2011-06-06 21:32:03 -04:00
|
|
|
## Contributions
|
|
|
|
|
2012-03-29 10:50:36 -04:00
|
|
|
To support the project:
|
2011-06-06 21:32:03 -04:00
|
|
|
|
2014-05-11 11:10:08 -04:00
|
|
|
* Use Ransack in your apps, and let us know if you encounter anything that's
|
2014-10-14 16:17:30 -04:00
|
|
|
broken or missing. A failing spec to demonstrate the issue is awesome. A pull
|
|
|
|
request with passing tests is even better!
|
|
|
|
* Before filing an issue or pull request, be sure to read and follow the
|
|
|
|
[Contributing Guide](CONTRIBUTING.md).
|
|
|
|
* Please use Stack Overflow or other sites for questions or discussion not
|
|
|
|
directly related to bug reports, pull requests, or documentation improvements.
|
2014-05-11 11:10:08 -04:00
|
|
|
* Spread the word on Twitter, Facebook, and elsewhere if Ransack's been useful
|
|
|
|
to you. The more people who are using the project, the quicker we can find and
|
|
|
|
fix bugs!
|
2011-06-06 21:32:03 -04:00
|
|
|
|
|
|
|
## Copyright
|
|
|
|
|
2014-01-02 04:24:56 -05:00
|
|
|
Copyright © 2011-2014 [Ernie Miller](http://twitter.com/erniemiller)
|