Rails

Searching with Ransack in Ruby on Rails


Reading Time: 6 minutes

Sometimes we need or we want to develop a module quickly and easily that includes filters, sorting, pagination and we don’t want to spend so much time on it, that’s why there are already some implemented and tested tools that we can use and we can save us some headaches and time to achieve the same result or even better.

In this post, I will show you how to create a simple module in which it is possible to search books by different types of fields using Ruby on Rails, Bootstrap, Will paginate, Faker, and Ransack.

So, let’s get started, first we are going to create a new Rails application from the console with the following command:

rails new search-books-with-ransack --database=postgresql

This creates a directory called “search-books-with-ransack” which has an app that is called in the same way. So, first of all, we need to enter to that directory.

cd search-books-with-ransack

I like to use PostgreSQL for my default database because I usually deploy my apps in Heroku and it’s better for production apps.

Scaffolding

For this example, we can use scaffolding to create our books model running the following command in our console

rails generate scaffold book isbn:bigint name author year:integer price:decimal{7-2} status:boolean genre:integer

As you can see rails generate scaffold has already created the model and other files that we will not talk right now. In addition, we can call the following commands in the console.

rails db:create and rails db:migrate

Dependencies

Let’s take a moment to review the gems that we will be using

  • faker – A library for generating fake data, we will use this gem to generate books data.
  • bootstrap – The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
  • ransack – Object-based searching for use in our books controller
  • will_paginate-bootstrap4 – Pagination library for Rails that matches Twitter Bootstrap 4 styling

Let’s add our dependencies in the Gemfile

# Gemfile
  gem 'faker'
  gem 'bootstrap', '~> 4.3.1'
  gem 'ransack'
  gem 'will_paginate-bootstrap4'

and don’t forget to run bundle install to install the gems

Seeds

Now let’s create some books using fake info adding in our db/seeds.rb file the following code

# db/seeds.rb
100.times do
  Book.create(
    isbn: Faker::Code.isbn,
    name: Faker::Book.title,
    author: Faker::Book.author,
    year: Faker::Number.between(1900, 2019),
    genre: Faker::Number.between(1, 5),
    price: Faker::Commerce.price,
    status: Faker::Boolean.boolean
  )
end

additionally, run rails db:seed to populate the example data in the database

Run local server

So far, we can start our server with rails server or rails s and open our favorite web browser and if you go to the URL http://localhost:3000/books. You will see that we already have some books in our database.

So, that’s great but we still have a lot to do, so let’s do it

 

 

Let’s continue adding a simple enum for the genres in our book model and use it later in our filters with a dropdown

# app/models/book.rb
enum genre: { fantasy: 1, romance: 2, horror: 3, fiction: 4, poetry: 5 }

Bootstrap 4

I would like to add some styles with Bootstrap 4, and to be able to use it we must import it into our app/assets/stylesheets/application.scss file like the code listed below.

Make sure the file has .scss extension (or .sass for Sass syntax). If you have just generated a new Rails app, it may come with a .css file instead. If this file exists, it will be served instead of Sass, so rename it:

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

And then we import bootstrap into it

@import "bootstrap";

Ransack

To start using Ransack gem we are going to make a small change in our index method in books_controller

The original code of our index method is as follows

# app/controllers/books_controller.rb
@books = Book.all

To use Ransack you must modify it to use some methods provided by the gem like the code listed below.

# app/controllers/books_controller.rb
@q = Book.ransack(params[:q])
@books = @q.result

Now we can use the ransack helper search_form_for to create our form, replacing the default form_for and start adding the inputs to filter the results.

We can add the helper in the following way, we also are going to add our first input to filter by name using the matcher _cont provided by the gem to use LIKE in our search as well the submit button.

<%= search_form_for @q do |f| %>
<%= f.label :name %>
<%= f.text_field :name_cont, class: 'form-control' %>
<%= f.submit class: 'btn btn-primary' %>
<% end %>

We can also add a link to clean all our search fields and show all the results again like the code listed below

<%= link_to "Clear", request.path, class: "btn btn-default" %>

Since we have our search form working, we can add other fields with different matchers

Equal matcher to find an exact text

<div class="form-group"><%= f.label :year %>
<%= f.text_field :year_eq, class: 'form-control' %></div>

It is also possible to filter the price by selecting a minimum and a maximum using the matcher price_gteq (greater than or equal) and price_lteq (less than or equal).

<div class="form-group"><%= f.label :price, "Price" %>
<%= f.text_field :price_gteq, class: 'form-control' %>
<%= f.text_field :price_lteq, class: 'form-control' %></div>

We can also add a select to allow filtering for each one of the values of the enum genre that we define in our model of books

<div class="form-group"><%= f.label :genre %>
<%= f.select(:genre_eq, options_for_select(Book.genres.map {|k, v| [k.humanize.capitalize, v]}, f.object.genre_eq), { include_blank: true }, {class: "form-control" }) %></div>

Or we can even add a checkbox to filter by a boolean value in the following way

<div class="form-group"><%= f.check_box :status_true, include_hidden: false %>
<%= f.label :status, 'Active' %></div>

There are many matchers that can be used according to the project you are creating, but if you want to check more information, you can consult it directly in the documentation of the Ransack gem.

Ordering

Now we can start to implement the ordering in the following way

Our current code looks like this

<th>Name</th>

and we need to replace it with the following code


<%= sort_link(@q, :name) %>

Additional to this, if you don’t like the order indicators, it is also possible to modify them by modifying the code in the initializer of Ransack

# config/initializers/ransack.rb
Ransack.configure do |c|
  c.custom_arrows = {
    up_arrow: '',
    down_arrow: 'U+02193',
    default_arrow: ''
  }
end

You can also check that the URL changes by adding the selected order, so if you share the link with a partner, in an email or external website, you can get the same result with the order and filters selected.

Pagination

So far so good, and now we can add the pagination gem, but since we are using Bootstrap 4, we will use will_paginate-bootstrap4 that already offers Bootstrap styles for pagination.

Let’s update the next line by adding the pagination in our index method

@books = @q.result

And we replace it with the following code

@books = @q.result.page(params[:page])

And finally, in our view, we will add the pagination code at the end of our list to be able to change the page

<%= will_paginate @books, list_classes: %w(pagination justify-content-end) %>

And this is our final result with filters, ordering, and pagination

If you want to check the project source code, you can visit the following repository.

We are Hiring

In MagmaLabs we are always looking for talented and passionate professionals. If you want to join us don’t hesitate to visit our Careers page.

 

Rails
Streaming Data With Rails > 3.1
Ruby
Warden To Login/Logout Within Your Cucumber Tests
Rails
Lambda In ActiveRecord