Hosang Jeon
Hosang Jeon

Reputation: 1423

Calling a ActiveRecord class method for ActiveRecord_Relation as a receiver

I want to create a class method for a class inherits ActiveRecord:Base. What the method need to do is add where clauses based on the options and it works well.

class Article < ActiveRecord::Base

  def self.list_by_params(params={})
    articles = self
    articles = articles.where(author_id: params[:author_id]) unless params[:author_id].blank?
    articles = articles.where(category_id: params[:category_id]) unless params[:category_id].blank?
    articles = articles.where("created_at > ?", params[:created_at].to_date) unless params[:created_at].blank?
    articles
  end

end

This code works fine in case of the call such as:

articles = Article.list_by_params({author_id: 700})
#=> Works fine as I expected.

articles = Article.joins(:authors).list_by_params({author_id: 700})
#=> Works fine as I expected.

However, the problem is that, if I want to call the list_by_params without filtering params, then it lose its former relations. For example:

articles = Article.joins(:authors).list_by_params({})
#=> articles is just a `Article` (not an ActiveRecord_Relation) class itself without joining :authors.

Is there any chance that I made a mistake?

Thanks in advance.

Upvotes: 0

Views: 232

Answers (2)

Hosang Jeon
Hosang Jeon

Reputation: 1423

For the self explanation, I've solved the problems by using where(nil).

Actually, Model.scoped returned anonymous scope but the method has been deprecated since Rails version 4. Now, where(nil) can replace the functionality.

class Article < ActiveRecord::Base

  def self.list_by_params(params={})
    articles = where(nil)  # <-- HERE IS THE PART THAT I CHANGED.
    articles = articles.where(author_id: params[:author_id]) unless params[:author_id].blank?
    articles = articles.where(category_id: params[:category_id]) unless params[:category_id].blank?
    articles = articles.where("created_at > ?", params[:created_at].to_date) unless params[:created_at].blank?
    articles
  end

end

Upvotes: 0

Albin
Albin

Reputation: 3032

What you are looking for is a scope.

I would do something like this

scope :for_author,  lambda { |author| where(author_id: author) unless author.blank? }
scope :in_category, lambda { |category| where(category_id: category) unless category.blank? }
scope :created_after,  lambda { |date| where('created_at > ?', date.to_date) unless date.blank? }

scope :list_by_params, lambda do |params|
  for_author(params[:author_id])
  .in_category(params[:category_id])
  .created_after(params[:created_at])
end

Now you can reuse the components of your query. Everything has a names and it gets easier to read the code.

Upvotes: 1

Related Questions