Working with a Rails 2.3.9 app and wondering how to write my named_scope such that I only get workouts from the current date. I am setting the timezone in in the application controller with a before_filter. The below doesn't throw an error, just doesn't filter:
workout.rb
named_scope :today_only, :conditions => [ "workouts.created_at <= ? AND workouts.created_at >= ?", Time.zone.now, 1.days.ago ]
application_controller.rb
before_filter :set_user_time_zone
You're not seeing the responses you want because Ruby is evaluating your call to Time.now when it evaluates your class definition, not when you're calling the scope. You need to pass a lambda to your named_scope call to get it to evaluate on every request:
# ensure that Time.now is evaluated on every call
named_scope :today_only, lambda {{ :conditions => ["workouts.created_at BETWEEN ? AND ?", Time.zone.now.at_beginning_of_day, Time.zone.now.end_of_day ] }}
Also, I think your Time boundaries may be incorrect. Are you looking for workouts that were created in the past 24 hours (relative to Time.now), or only workouts that were created "today?" Your example works for the former, the example above does the latter.
Related
I'm working through the 'Ruby On Rails 3 Essential Training' and have received a problem when using name scopes. When finding records and using queries withing the Rails console everything went smoothly until I tried to use a name scope in my subject.rb file. This is my code in the subject.rb file.
Class Subject < ActiveRecord::Base
scope :visible, where(:visible => true)
end
I saved the .rb file and restarted my Rails console but when I run from my rails console:
subjects = Subject.visible
I get: ArgumentError: The scope body needs to be callable.
Does anyone know why I'm getting this error.
The scope's body needs to be wrapped in something callable like a Proc or Lambda:
scope :visible, -> {
where(:visible => true)
}
The reason for this is that it ensures the contents of the block is evaluated each time the scope is used.
I got the same error , while before my solution I had a space between where and ( like below
scope :registered , -> { where ( place_id: :place_id , is_registered: :true ) }
after i removed the space between where and ( like below i made my page working
scope :registered , -> { where( place_id: :place_id , is_registered: :true ) }
Yes, indeed, this is rails 4 way of calling scopes. You'd need to change it, if you're upgrading to Rails 4 from Rails 3.
What you are using: scope :visible, where(:visible => true) goes for eager loading, and has been deprecated in Rails 4.
scope :visible, where(:visible => true)
This line of code gets evaluated when the particular class is loaded, and not at the time, this very scope is called.
There are a few cases when this thing does matter, like:
scope :future, where('published_at > ?', Time.now)
scope :future, -> { where('published_at > ?', Time.now) }
In first case, ? will be replaced with the very time the class would have been loaded, but the second & correct case, that time will be used at which the scope would have been called on the class.
I want to hide past events if they are defined and get all other. How to show all documents even if :once_at is nil and if :once_at is defined then hide these ones which are expired?
My recent approach, shows only events with defined :once_at, (I tryed with :once_at => nil, but without results):
default_scope where(:once_at.gte => Date.today)
or (also not working)
default_scope excludes(:once_at.lte => Date.today)
When do you think Date.today is evaluated? If you say this:
default_scope where(:once_at.gte => Date.today)
Date.today will be evaluated when the class is being loaded. This is almost never what you want to happen, you usually want Date.today to be evaluated when the default scope is used and the usual way to make that happen is to use a proc or lambda for the scope:
default_scope -> { where(:once_at.gte => Date.today) }
The next problem is what to do about documents that don't have a :once_at or those with an explicit nil in :once_at. nil won't be greater than today so you'd best check your conditions separately with an :$or query:
default_scope -> do
where(
:$or => [
{ :once_at => nil },
{ :once_at.gte => Date.today }
]
)
end
Here is the setup:
end_date = DateTime.parse('2010-01-01 12:00:00-04')
and sometimes it's initialized:
end_date = 1.days.ago
The question... do these named_scope(s) generate the same EXACT SQL?
named_scope :before, lambda { |end_date|
{ :conditions =>
["overdraft_transaction_payments.created_at < ?", end_date] }
}
and
named_scope :before, lambda { |end_date|
{ :conditions =>
["overdraft_transaction_payments.created_at < ?", end_date.utc] }
}
In the first example I use end_date and in the second I use end_date.utc.
(It may be important to note.... The DB server's OS is set to CDT, and the DB uses UTC internally. The rails server's OS is set to CDT and the application instance is set to EDT. I realize that this is probably not the optimum way to configure these systems, however, the issue at hand is the ActiveRecord output.)
For what it is worth my intuition says that the first example is going to generate a time string for the local TZ where the second is going to generate a UTC-0.
PS: Is there a mini test case I can use to validate my intuition?
I believe under the hood the date is going to be converted with a call to ".to_s(:db)" and you can see in an IRB console session what that's going to return:
>> DateTime.parse('2010-01-01 12:00:00-04').to_s(:db)
=> "2010-01-01 12:00:00"
>> DateTime.parse('2010-01-01 12:00:00-04').utc.to_s(:db)
=> "2010-01-01 16:00:00"
>> 1.days.ago.to_s(:db)
=> "2010-08-12 18:01:09"
>> 1.days.ago.utc.to_s(:db)
=> "2010-08-12 18:01:13"
I thought the following two were equivalent:
named_scope :admin, lambda { |company_id| {:conditions => ['company_id = ?', company_id]} }
named_scope :admin, lambda do |company_id|
{:conditions => ['company_id = ?', company_id]}
end
but Ruby is complaining:
ArgumentError: tried to create Proc object without a block
Any ideas?
it's a parser problem. try this
named_scope :admin, (lambda do |company_id|
{:conditions => ['company_id = ?', company_id]}
end)
I think the problem may be related to the difference in precedence between {...} and do...end
There's some SO discussion here
I think assigning a lambda to a variable (which would be a Proc) could be done with a do
... end:
my_proc = lambda do
puts "did it"
end
my_proc.call #=> did it
If you're on ruby 1.9 or later 1, you can use the lambda literal (arrow syntax), which has high enough precedence to prevent the method call from "stealing" the block from the lambda.
named_scope :admin, ->(company_id) do
{:conditions => ['company_id = ?', company_id]}
end
1 The first stable Ruby 1.9.1 release was 2009-01-30.
It's something related to precedence as I can tell
1.upto 3 do # No parentheses, block delimited with do/end
|x| puts x
end
1.upto 3 {|x| puts x } # Syntax Error: trying to pass a block to 3!
following example:
named_scope :search, lambda {|my_args| {...}} do
def access_my_args
p "#{my_args}"
end
end
# Call:
Model.search(args).access_my_args
As you can see I want to access the arguments from the lambda in the named_scope extension. Is there a way to do this?
A more specific example:
class User < ActiveRecord::Base
named_scope :by_name, lambda {|name_from_scope| {:conditions => {:name => name_from_scope}}} do
def change_name
each { |i| i.update_attribute(:name, "#{name_from_scope}xyz") }
end
end
end
(I know that there is a find_by_name scope and so on...). I want to use the name_from_scope argument, that is passed in the scope in the scope extension.
named_scope :test_scope, lambda {|id| {:conditions => {:id => id}} } do
def test_scope_method
each {|i| puts #proxy_options.to_yaml}
end
end
I don't believe you can get to the arguments directly without extending activerecord.
#proxy_options will give you the compiled options in the block. So, in your example, you won't have access to name_from_scope but you will have access to #proxy_options[:conditions][:name].
Is this what you're trying to do?
named_scope :search, lambda {|*my_args|
OtherClass.announce_search_for_model(my_args, self.class)
{ :conditions => ['created_at < ?', my_args[:created_at]], :limit => my_args[:limit] }
}
args = {:created_at > 'NOW()', :limit => 5}
Model.search(args)
If you're wanting to observe what's passed onto the named_scope then I would do that in the lambda.
Named_scope results will always be a result as if you'd used Model.find. This is a functionality of rails so you need to override rails functionality with a Module if you want something different. I wouldn't recommend doing that because named_scope extensions are there for simplifying finders, not observing parameters.