How Can an Activerecord::Relation Object Call Class Methods

How can an ActiveRecord::Relation object call class methods

There's not much documentation on the application on class methods to ActiveRecord::Relation objects, but we can understand this behavior by taking a look at how ActiveRecord scopes work.

First, a Rails model scope will return an ActiveRecord::Relation object. From the docs:

Class methods on your model are automatically available on scopes. Assuming the following setup:

class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
scope :featured, -> { where(featured: true) }

def self.latest_article
order('published_at desc').first
end

def self.titles
pluck(:title)
end
end

First, invoking a scope returns an ActiveRecord::Relation object:

Article.published.class
#=> ActiveRecord::Relation

Article.featured.class
#=> ActiveRecord::Relation

Then, you can operate on the ActiveRecord::Relation object using the respective model's class methods:

Article.published.featured.latest_article
Article.featured.titles

It's a bit of a roundabout way of understanding the relationship between class methods and ActiveRecord::Relation, but the gist is this:

  1. By definition, model scopes return ActiveRecord::Relation objects
  2. By definition, scopes have access to class methods
  3. Therefore, ActiveRecord::Relation objects have access to class methods

Calling Class method via Rails ActiveRecord Association

To answer your question directly,

Comment.scope_attributes

Will return a hash of those attributes the current scope on Comment would set. You can test the effect an association has on this like so

I'm not sure I'd use this though - it seems a little odd to have a class method that can only be invoked on scopes of a particular form.

Rails method call on active record relation

You should use active record scope for yo

class DriverInvoice
scope :for_company, -> (company) { where(driver_id: current_affilate_company.drivers.pluck(:id))}
end

@paid_drivers = DriverInvoice.for_company(current_affilate_company)

Then you can use other filters by defining other scopes or using where chain

method on activerecord::relation

How can i make this method return the exact same object it begun with?

by_post method operates on the class (User), not on the relation, thus what you get is actually the "object it begun with" :)

So in order to return a relation from the by_post method, you'd want to load it:

self.by_post(condition)
return self.all if condition.blank? # self.scoped for Rails 3
##something
end

How did the class method call on ActiveRecord::Relation takes a context?

My initial answer was the right one: it can't work...

I guess I finally convinced myself because docs should rule...

Well, I think what's important in the doc is the idea: you can chain scopes with class methods.

But the implementation of the class method given in example is definitely wrong.

Calling a class method through an object instance

This works because scopes of ActiveRecord relations are merged. It's not that find_incomplete is running on individual task instances.

@project.tasks creates an ActiveRecord scope of the tasks for that project instance and then that scope is still in effect when your find_incomplete method is called.

Take a look at the documentation here: http://guides.rubyonrails.org/active_record_querying.html#scopes

Your find_incomplete method works in the same way as the self.published example in the docs.

Think of the underlying SQL query that would run:

@project.tasks would create a where condition like SELECT * FROM projects WHERE project_id = <project_id>

find_all_by_complete then merges in an and condition for complete = 0


I think the other piece of the puzzle that might help is that @project.tasks is not just a simple array array of Task objects, although it will have been converted to such if you type project.tasks in the Rails console. project.tasks is actually an Active Record relation object (or more precisely a proxy)
There are a number of reasons and benefits for this but the main 2 are that it allows chaining and it allows the underlying query to be run on demand only if/when needed.

The relation has a sequence of rules for how method calls are delegated, one of which is to call class methods on the class of the associated objects. (the relation knows that it's a relation to Tasks)

So you are correct when you wrote tasks.find_incomplete is still equal to Task.find_incomplete except that when find_incomplete is called through project.tasks a scope narrowing down to the project_id is already in effect.



Related Topics



Leave a reply



Submit