3

When I use scope which I prepared in model, rails returns incorrect data.

My Model:

class CurrencyRate < ActiveRecord::Base
    scope :eur_today, -> {where(currency: "eur").where(date: Time.now.in_time_zone.to_date).first}
end

Inforamation from rails console:

2.3.0 :011 > CurrencyRate.eur_today

CurrencyRate Load (0.2ms)  SELECT  "currency_rates".* FROM 
"currency_rates" WHERE "currency_rates"."currency" = ? AND 
"currency_rates"."date" = ?  ORDER BY "currency_rates"."id" ASC LIMIT 1  
[["currency", "eur"], ["date", "2017-08-09"]]
CurrencyRate Load (0.2ms)  SELECT "currency_rates".* FROM 
"currency_rates"

 => #<ActiveRecord::Relation [#<CurrencyRate id: 2, currency: "eur", 
 sale: 4.248546249999998, purchase: 4.265333125, date: "2017-08-08", 
 created_at: "2017-08-08 20:52:08", updated_at: "2017-08-08 20:54:10", 
 sale_percentage_diff: nil, purchase_percentage_diff: nil>]>

When I use the same query like in scope, returned data is correct:

2.3.0 :012 > CurrencyRate.where(currency: "eur").where(date: Time.now.in_time_zone.to_date).first 

CurrencyRate Load (0.2ms)  SELECT  "currency_rates".* FROM 
"currency_rates" WHERE "currency_rates"."currency" = ? AND 
"currency_rates"."date" = ?  ORDER BY "currency_rates"."id" ASC LIMIT 1  
[["currency", "eur"], ["date", "2017-08-09"]]

=> nil 

Why scope doesn't work correct?

2
  • 1
    Your scope is not a real scope because it returns the first found CurrencyRate record. Remove that .first in your scope. You can see in your IRB that there are 2 generated SQL queries, one with the limit 1 (because of your .first) and another just doing "select all from current_rates" Commented Aug 9, 2017 at 20:42
  • I understand it now. Thanks. Commented Aug 9, 2017 at 21:11

1 Answer 1

3

Because scopes must return an ActiveRecord::Relation. Get rid of the first call in your scope and use that outside the scope. Why? Because scopes have to be chainable.

Sign up to request clarification or add additional context in comments.

3 Comments

or just make it a class instance method e.g. def self.eur_today since the "scope" name seems to suggest the intention is to return the rate of the Euro as of today. Although I would probably make this def self.currency_value(currency,as_of=Time.now.in_time_zone.to_date) so that it can be reused better
Yep, that's an alternative
I tried both ways. I understand now how scopes work. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.