Calculating non-Rolling Last Week in Rails - ruby-on-rails

I have a model called Article and what I want to do is show all of the Articles where the created_at date was for last week. I understand how to a rolling week (ie the last 7 days to now), such as this question.
scope :last_week, lambda { :conditions => { :created_at => 1.week.ago..DateTime.now.end_of_day } }
However, what I want to do is to find the Articles that were Sunday - Saturday of last week, regardless of what day the current day is.

Similar to John's answer, but I think this fits your situation a little better:
scope :last_week, lambda { { conditions: { created_at: last_week_range } } }
def self.last_week_range
1.week.ago.beginning_of_week(:sunday)..1.week.ago.end_of_week(:sunday)
end

Date.parse gives an awesome results being run on the weekday name:
require 'date'
# ⇒ true
Date.today
# ⇒ <Date: 2013-04-02 ((2456385j,0s,0n),+0s,2299161j)>
Date.parse('Sunday')
# ⇒ <Date: 2013-03-31 ((2456383j,0s,0n),+0s,2299161j)>
Date.parse('Wednesday')
# ⇒ <Date: 2013-04-03 ((2456386j,0s,0n),+0s,2299161j)>
So all you need is:
def last_weekend
d = Date.parse('Saturday')
d -= 7 if d > Date.today
Range.new d, d+1
end
Hope this helps.

Rails provides a helper method to get the beginning and end of a week for a given date. The method defaults to monday so you have to pass in sunday to override it.
scope :last_week, lambda { :conditions => { :created_at => Date.today.at_beginning_of_week(:sunday)..Date.today.at_end_of_week(:sunday) } }
I have usen Date.today as an example but you could have the scope have a date argument so you could apply any date to the scope.
Edit:
scope :last_week, lambda{ |date| {:conditions => ((date - 1.week).at_beginning_of_week(:sunday))..((date-1.week).at_end_of_week(:sunday)) }}
then just call the scope as
last_week(some_date)

Related

Rspec test record is within range of dates

I'm having this scope function inside my model
# models/Post.rb
def self.filtered (params)
unless params[:year].blank? && params[:month].blank?
year = params[:year].to_i
month = params[:month].to_i
return where(created_at: DateTime.new(year, month, 1).beginning_of_day..DateTime.new(year, month, -1).end_of_day)
end
self
end
# controllers/posts_controller.rb
#posts = Post.filtered(params)
Which basically returns all archived posts of specific year and month
SELECT `posts`.* FROM `posts`
WHERE (`posts`.`created_at` BETWEEN '2017-10-01 00:00:00' AND '2017-10-31 23:59:59')
I'm trying to write a test for this method to make sure that a post is was created in the requested year and month, how can I do this?
# spec/models/post_spec.rb
describe '.filtered' do
let!(:older) { FactoryGirl.create(:post, created_at: 1.month.ago) } # this post should not appear in the list
let!(:newer) { FactoryGirl.create(:post, created_at: Time.zone.now) } # this post should appear in the list
it 'is within specific year and month' do
expect(Post.filtered({year: Date.today.strftime("%Y"), month: Date.today.strftime("%m")}).map { |post| post.created_at }).to be ???
end
end
Use the include matcher to verify a record is included in the result set.
expect(Post.filtered({year: Date.today.strftime("%Y"), month: Date.today.strftime("%m")}).to include(newer)
Use #contain_exactly to match elements when order should be disregarded.
# spec/models/post_spec.rb
describe '.filtered' do
let!(:older) { FactoryGirl.create(:post, created_at: 1.month.ago) } # this post should not appear in the list
let!(:newer) { FactoryGirl.create(:post, created_at: Time.zone.now) } # this post should appear in the list
it 'is within specific year and month' do
expect(Post.filtered({year: Date.today.strftime("%Y"), month: Date.today.strftime("%m")}).map { |post| article.created_at }).to contain_exactly(newer)
end
end
By the way, instead of creating a class method like what you did here, you might want to consider a scope so it can be chained with other scopes.

How to get all documents and hide this which are expired if they are defined?

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

Using a variable column name in a rails helper method where( ) query

I'm working with a User model that includes booleans for 6 days
[sun20, mon21, tue22, wed23, thur24, fri25]
with each user having option to confirm which of the 6 days they are participating in.
I'm trying to define a simple helper method:
def day_confirmed(day)
User.where(day: true).count
end
where I could pass in a day and find out how many users are confirmed for that day, like so:
day_confirmed('mon21')
My problem is that when I use day in the where(), rails assumes that I'm searching for a column named day instead of outputting the value that I'm passing in to my method.
Surely I'm forgetting something, right?
This syntax:
User.where( day: true )
Is equivalent to:
User.where( :day => true )
That's because using : in a hash instead of =>, e.g. { foo: bar }, is the same as using a symbol for the key, e.g. { :foo => bar }. Perhaps this will help:
foo = "I am a key"
hsh = { foo: "bar" }
# => { :foo => "bar" }
hsh.keys
# => [ :foo ]
hsh = { foo => "bar" }
# => { "I am a key" => "bar" }
hsh.keys
# => [ "I am a key" ]
So, if you want to use the value of the variable day rather than the symbol :day as the key, try this instead:
User.where( day => true )
If these are your column names, [sun20, mon21, tue22, wed23, thur24, fri25]
And you are calling day_confirmed('mon21') and trying to find the column 'mon21' where it is true, you can use .to_sym on the date variable
def day_confirmed(day)
User.where(day.to_sym => true).count
end
the .to_sym will get the value of date, and covert it to :mon21

named_scope for date.today Rails 2.3.9

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.

named_scope conditions and timezone mismatch

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"

Resources