When to Use Association Extensions VS Named Scopes

When to use association extensions vs named scopes?

Association extensions are very useful for creating custom methods for creating, updating, etc (not necessarily finding).

Because you have access to the proxy_owner, proxy_reflection, proxy_target, you have a nice hook into the relationship.

Check out the Association Extension section of the Rails docs:

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Rails 4: how to use named scope with has_many associations

I believe it should read like this in Rails 4:

scope :live, -> { where(is_deleted: 0, sent_to_api: 1) }

The rails 4 docs and all examples in it show you passing in a callable object to the scope to ensure it gets called each time. If it doesn't work like this try implementing it as a class method and see how that works out for you.

http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html

How do you scope ActiveRecord associations in Rails 3?

I suggest you take a look at "Named scopes are dead"

The author explains there how powerful Arel is :)

I hope it'll help.

EDIT #1 March 2014

As some comments state, the difference is now a matter of personal taste.

However, I still personally recommend to avoid exposing Arel's scope to an upper layer (being a controller or anything else that access the models directly), and doing so would require:

  1. Create a scope, and expose it thru a method in your model. That method would be the one you expose to the controller;
  2. If you never expose your models to your controllers (so you have some kind of service layer on top of them), then you're fine. The anti-corruption layer is your service and it can access your model's scope without worrying too much about how scopes are implemented.

When to and when not to use lambda for named scopes?

In Rails 4

Always use lambda. The second syntax is incorrect in Rails 4 and will throw an error (undefined method 'call' for ActiveRecord::Relation)

# activerecord/lib/active_record/scoping/named.rb
scope = all.scoping { body.call(*args) }

In Rails 3

scope method behaves same way in both cases - it created a new class method called credits. Difference is that when given a lambda, it evaluates this lambda every time this new method is called to get the scope, while when given relation, it just uses what has been passed.

# activerecord/lib/active_record/named_scope.rb
options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options

In this case, lambda always return exactly same relation, so no difference will be noted.

Lambda notation is used usually to pass arguments to the scope:

scope :before, lambda {|date| where.created_at < date}

Which then can be used like:

Model.before(1.day.ago)

This is naturally impossible to write without the lambda.

Rails Scoped has many through has many with scope

You need to actually define a second association:

class Project < ApplicationRecord
has_many :issues
has_many :open_issues,
-> { where(status: 'open') },
class_name: 'Issue'

has_many :assignees, through: :open_issues
end

A has_many through: association just takes the name of another association that it joins through. You cannot define an association that goes through an association extension which you are incorrectly referring to as a scope.

A scope is really just a class method which can be called on any relation (since it proxies to the class) while association extensions can only be called on association proxies.

If you want to actually create a scoped association, you need to pass a callable such as a lambda.

has_many :open_issues,
-> { where(status: 'open') },
class_name: 'Issue'

This really just applies a set of filters directly to the association itself.

something.xs.ys.for(z).bs

Is not actually compatible with how associations actually work in Rails. Associations are not callable on relations or association proxy objects - only on records themselves.

Use scope of a belongs_to association to scope in the first model - Ruby on Rails

If you are just looking to merge in the scope then

class Child < ApplicationRecord
belongs_to :parent
scope :with_great_parent, -> (min_grade) {joins(:parent).merge(Parent.great(min_grade))}
end

should handle this for you. The SQL generated will be similar to

SELECT * 
FROM children
INNER JOIN parents ON children.parent_id = parents.id
WHERE
parents.grade > --Whatever value you pass as min_grade

See ActiveRecord::SpawnMethods#merge for more information

Injecting a scope through an association extension in Rails 2.3.17

I ended up not finding a way to work around this. In the end I had to avoid the bug in the specific situation in which it occurred. Fortunately it was only in a couple of places in the app.

Rails named scope not working on association

Rails scopes are just class methods. Internally Active Record converts a scope into a class method. So, when you define this scope:

  scope :payment_methods_for_seller, ->(seller) { joins(:seller_buyer_payment_methods).where(:seller => seller) }

You can consider this payment_methods_for_seller method as a class method of Buyer class. That's why you get this error:

NoMethodError: undefined method `payment_methods_for_seller' for Buyer:0x000001028e6d88

when you called the class method on an object of the Buyer class:

a_buyer.payment_methods_for_seller a_seller

You can't call a scope/class method on an object of the class. You can call it on the class itself:

Buyer.payment_methods_for_seller

The 2nd example works because in that case you defined the payment_methods_for_seller method as an instance method of the Buyer class.

Hope this clears your confusion.

Instead of using scopes you can get the relevant records through the seller_buyer_methods association:

a_buyer.seller_buyer_payment_methods.where( :seller => a_seller )

Here is a nice blog post on Active Record scopes vs class methods which will give you some more interesting information on this topic.



Related Topics



Leave a reply



Submit