Dynamic Attribute Based Finders in Rails 4.2

Objective

To learn about dynamic attribute-based finders in Rails 4.2.

Steps

Step 1

Create a new Rails 4.2.5 project.

$ rails new doer

Step 2

Let's use scaffold to play with some tasks.

$ rails g scaffold task name:string complete:boolean

Step 3

Create the table.

$ rake db:migrate

Step 4

Add some records In seeds.rb:

Task.create(name: 'Get rich quick', complete: false)
Task.create(name: 'Write a book', complete: true)
Task.create(name: 'Buy a puppy', complete: false)
Task.create(name: 'Dance in the rain', complete: true)

Populate the tasks table.

rake db:seed

Step 5

What happens when we use find_all_by_something ?

$ rails c
 tasks = Task.find_all_by_complete(false)
 NoMethodError: undefined method `find_all_by_complete' for #<Class:0x007fe590d96f98>
 Did you mean?  find_or_create_by

It's not available in Rails 4.2 anymore. How do we find all incomplete tasks?

Step 6

We can use find_by(column: value):

$ rails c
Loading development environment (Rails 4.2.5.2)
 > task = Task.find_by(complete: false)
  Task Load (0.3ms)  SELECT  "tasks".* FROM "tasks" WHERE "tasks"."complete" = ? LIMIT 1  [["complete", "f"]]
 => #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56"> 

This returned only one record. We know that there are two incomplete tasks. How can we retrieve all incomplete tasks?

Step 7

We can use where:

incomplete_tasks = Task.where(complete: false)
Task Load (2.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  [["complete", "f"]]
 => #<ActiveRecord::Relation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">]> 

We get an ActiveRecord::Relation object back. We don't have the results from the database.

 > incomplete_tasks.class
 => Task::ActiveRecord_Relation 

Step 8

We can retrieve the records by using to_a:

 > incomplete_tasks.to_a
 => [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">] 

Now we see all the incomplete tasks.

Step 9

Let's get the last incomplete task.

 last_incomplete = Task.where(complete: false).order('created_at DESC')
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  ORDER BY created_at DESC  [["complete", "f"]]
 => #<ActiveRecord::Relation [#<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">, #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">]>

This returns ActiveRecord::Relation object. We can force the query to return results:

 last_incomplete = Task.where(complete: false).order('created_at DESC').first
  Task Load (0.3ms)  SELECT  "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  ORDER BY created_at DESC LIMIT 1  [["complete", "f"]]
 => #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">

Step 10

If you have lot of data, for sorting it is better to use the id instead of the date column for better performance. The primary key already has index.

last_incomplete = Task.where(complete: false).order('id DESC').first

Step 11

We can search for name that has a certain string using LIKE:

tasks = Task.where("name LIKE 'Buy%'").to_a
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE (name LIKE 'Buy%')
  => [#<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 21:50:56", updated_at: "2016-03-04 21:50:56">]

In the controller, it will be:

tasks = Task.where("name LIKE '#{params[:name]}%'").to_a

You can download the code for this article from https://github.com/bparanj/doer.

Summary

In this article, you learned how the dynamic attribute-based finders work in Rails 4.2.5. You also saw how to find records matching a certain string for a column.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.