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.