Richlewis
Richlewis

Reputation: 15374

Scope or instance variable

From what i have read using a scope in Rails is more efficient as your querying the model directly as opposed to an instance variable which goes via the controller to the model (thats how i see it?)

So i have this query in my controller

@animal_location = User.select(:town).map(&:town).uniq
["Cardiff", "Newport"]

and then this scope in my model

scope :uniq_towns, ->() {
 select("town").group("town")
}
#<ActiveRecord::Relation [#<User id: nil, town: "Cardiff">, #<User id: nil, town: "Newport">]>

In my view to access the town value using @animal_location = User.select(:town).map(&:town).uniq I can access like

<% @animal_location.each do |loc| %>
  <%= loc %>
<% end %>

or if i used the scope and went with @animal_location = User.uniq_towns, in my view i would use

<% @animal_location.each do |loc| %>
  <%= loc.town %>
<% end %>

My first question is would my scope be quicker in this instance and secondly is my scope correct as i am getting User id: nil as part of the hash

Thanks

Upvotes: 1

Views: 1196

Answers (1)

D-side
D-side

Reputation: 9485

It depends on what your source meant by "efficient". Scopes are designed to keep code DRY, in a sense of "Don't Repeat Yourself", and implement an "extract method" refactoring pattern for ActiveRecord (AR) queries. They are easy to maintain.

Why use it? If you use the same query in multiple places consider the situation when you need to change it everywhere. You have to find&replace all occurrences. This way is error-prone and causes more differences in version control that are harder to keep track of.

Extracting this query into a method seems like a logical solution. Class method? No, maybe you shouldn't, but simply put scopes are just that. By using scopes you will:

  • Get a single place to construct a query with a certain meaning (and use it throughout the app)
  • Be pretty sure that the result of a scope is chainable, because the result is a valid AR relation (and you can apply scopes on top of it)
  • Provide your query's meaning a good readable name to keep controller code readable (using scopes in views is valid, but against MVC that Rails follows)

You have:

@animal_location = User.select(:town).map(&:town).uniq

First and foremost, I fail to see why map and group are needed, this works fine too and uses SQL DISTINCT:

@animal_location = User.select(:town).uniq

This looks short enough to be readable. But let's demonstrate scopes here. In your model issue this:

scope :uniq_towns, ->() {
 select("town").uniq
}

...so you can write this in your controler:

@animal_location = User.uniq_towns

There are also other ways to tackle this situation, like before_filter, but it's beyond the scope of the question.

Upvotes: 3

Related Questions