Exploring new ActiveRecord methods & Scopes

It is now week four at FS and we are no longer the newcomers on campus. We finished our first module for the program last Friday, learning about ActiveRecord and it’s importance in making our lives as web developers less painless. Today I will be writing about a few methods in ActiveRecord that can even make our programming careers slightly more enjoyable.

pluck(column_name)

pluck can be used to query single or multiple columns from the underlying table of a model. It accepts a list of column names as argument and returns an array of values of the specified columns with the corresponding data type.

Pluck will simply select the desired attribute(s) and return its values as an array.

Artist.pluck(:name)

can be seen the same as:

Artist.all.map(&:name)

Both queries will return the name attribute values for the instances of the Artist class.

Artist.all
# => [#<Artist:0x007fc7f4e036e8 id: 1, name: "don mcclean">]
Artist.pluck(:name)
# SELECT "artists"."name" FROM "artists"
# => ["don mcclean"]
Artist.all.map(&:name)
# SELECT "artists".* FROM "artists"
# => ["don mcclean"]

Pluck can also be used with different clauses such as where and order.

Song.all
# =>
[#<Song:0x007fc7fb0a1c00 id: 1, name: "American Pie", artist_id: 1>, #<Song:0x007fc7fb0a1ac0 id: 3, name: "And I Love You So", artist_id: 1>, #<Song:0x007fc7fb0a1980 id: 2, name: "Vincent", artist_id: 1>]
Song.where(name: "Vincent").pluck(:id)
# SELECT "songs"."id" FROM "songs" WHERE "songs"."name" = ?
# => [2]
Song.order(:name).pluck(:id)
# SELECT "songs"."id" FROM "songs" ORDER BY "songs"."name" ASC
# => [1, 3, 2]

present? & blank?

An object is blank if it’s false, empty, or a whitespace string. For example, false, '', ' ', nil, [], and {} are all blank.
An object is present if it’s not blank.

#blank? can be closely referred to as the #empty? method we all know but with a few exceptions:

  • The #empty? method cannot be used on an object other than that of the Hash, Array, and String class. The #blank? method can be used on any object.
  • The #empty? method will return a “No method error” when used for a nil object. The #blank? method will return true when the value is equal to nil, false, empty, or whitespace string.

Let’s take a look at some examples of #empty? and #blank?

[].empty?
# => true
"   ".empty?
# => false
nil.empty?
# => NoMethodError
[].blank?
# => true
"   ".blank?
# => true
nil.blank?
# => true

Song.all
# =>
[#<Song:0x007fc7fb0a1c00 id: 1, name: "American Pie", artist_id: 1>, #<Song:0x007fc7fb0a1ac0 id: 3, name: "And I Love You So", artist_id: 1>, #<Song:0x007fc7fb0a1980 id: 2, name: "Vincent", artist_id: 1>]
Song.all.first.empty?
# SELECT "songs".* FROM "songs" ORDER BY "songs"."id" ASC LIMIT ?
# => NoMethodError: undefined method `empty?' for #<Song id: 1, name: "American Pie", artist_id: 1>
Song.all.first.blank?
# SELECT "songs".* FROM "songs" ORDER BY "songs"."id" ASC LIMIT ?
# => false

Great! Now what does #present? do?
The #present? method is the exact opposite of #blank?.

[].present?
# => false
"   ".present?
# => false
nil.present?
# => false

Song.all.first.present?

# SELECT "songs".* FROM "songs" ORDER BY "songs"."id" ASC LIMIT ?
# => true

scope(name, body, &block)

Adds a class method for retrieving and querying objects. The method is intended to return an ActiveRecord::Relation object, which is composable with other scopes. If it returns nil or false, an all scope is returned instead.

Simply put, scopes are almost the same as class methods.

class Shirt < ActiveRecord::Base
  def self.shirt_color(color)
self.where(name: color)
end

# This is the same as:
  scope :shirt_color, -> color { where(name: color) }
end

If we were to call Shirt.shirt_color(color), the return value would be the same for both cases.

Shirt.all
# => [#<Shirt:0x007fbc120afb60 id: 1, name: "black">,
# #<Shirt:0x007fbc120ac168 id: 2, name: "white">]
scope :shirt_color, -> color { where(name: color) }
Shirt.shirt_color('black')
# SELECT "shirts".* FROM "shirts" WHERE "shirts"."name" = ?
# => [#<Shirt:0x007fc0ad8516a8 id: 1, name: "black">]


def
self.shirt_color(color)
self.where(name: color)
end
Shirt.shirt_color('black')
# SELECT "shirts".* FROM "shirts" WHERE "shirts"."name" = ?
# => [#<Shirt:0x007fa924800de8 id: 1, name: "black">]

So what is the difference between using Scope and using a class method?

The difference between a scope and a class method is that a scope will always return a relation (i.e the self.all query) whereas a class method won’t unless we explicitly tell it to. This feature allows the scope method to always be chainable so it will not break even if a nil or false value is passed in as an argument.

Let’s take our previous example and try implementing the present? and pluck methods on our scope and class methods.

class Shirt < ActiveRecord::Base
  scope :shirt_color, -> color { where(name: color) if    color.present? }
  def self.shirt_color(color)
self.where(name: color) if color.present?
end
end

If we were to give our class method an argument of nil, what would happen?

Shirt.shirt_color(nil)
# => nil
Shirt.shirt_color(nil).pluck(:name)
# => NoMethodError: undefined method `pluck' for nil:NilClass

Our class method defaults to a return value of nil since our “if color.present?” statement checks out to be false. The method fails to find a match for nil which in turn makes our #pluck call result in an error.

Now let’s try doing the same with with our scope method instead.

Shirt.shirt_color(nil)
# SELECT "shirts".* FROM "shirts"
# => [#<Shirt:0x007feb8cd06108 id: 1, name: "black">,
# #<Shirt:0x007feb8cd05fc8 id: 2, name: "white">]

Shirt.shirt_color(nil).pluck(:name)
# SELECT "shirts"."name" FROM "shirts"
# => ["black", "white"]

We can see that our scope method returns all the instances of the Shirt class since it couldn’t find a match for the given argument of nil. But because the scope method returned all the instances of the Shirt class, we were still able to continue chaining our #pluck method which ultimately returned to us all the names of the shirt color.

So all in all we only reviewed three of the many methods that ActiveRecord provides us with and we can already see how powerful it can be. There are bound to be many more useful and powerful methods that we are not exposed to yet so let us all continue to explore and experiment!

Sources