In model/item.rb I have custom validation method
validate :double_dates
after_save :double_check
private
def double_dates
if Item.where(:user_id => self.user_id, :asin => self.asin, :domain => self.domain).where("DATE(created_at) = ?", Date.today).length >= 1
errors.add(:created_at, "no double dates")
end
Validation method does work in "rails c". But, when I run two task at the same time (rails runner "ApplicationController.rotate" I have Item.save in rotate method) - validation stops working.
irb(main):044:0> i1.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):045:0> i2.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):046:0> i1.created_at == i2.created_at
=> false
irb(main):047:0> i1.created_at.to_time.to_i == i2.created_at.to_time.to_i
=> true
irb(main):048:0> i1.created_at.class
=> ActiveSupport::TimeWithZone
irb(main):049:0> i2.created_at.class
=> ActiveSupport::TimeWithZone
irb(main):050:0> i1.created_at.usec => 311714
irb(main):051:0> i2.created_at.usec => 312779
Any hints or advice would be appreciated.
I had a similar problem once, and guess you could actually solve this problem adding indexes to the table. Add an index to the created_at column in Items.
Here you've got a full explanation about indexing in rails.
Hope it helps!
Thanks to #FabKremer I was able to solve it :)
by adding #i.created_at = Time.now.change(usec: 0) before Item.save :)
Nevertheless I'm accepting his answer, cuz it'll work for most cases.
Related
Everything works fine on local.
This doesn't work on Heroku:
class Ticket
def self.how_many_today
todays_tickets = Ticket.all.to_a.select!{ |t| t.created_at.to_date == Date.today }
todays_tickets == nil ? 0 : todays_tickets.count
end
# This method is scheduled with cron
def self.reset_todays_nr
#todays_nr = nil
end
def self.set_todays_nr
if #todays_nr.nil?
#todays_nr = how_many_today + 1
else
#todays_nr += 1
end
end
end
Namely, playing on heroku run console reveals this inconsistency:
irb(main):023:0* set_todays_nr
=> 1
irb(main):024:0> set_todays_nr
=> 2
irb(main):025:0> set_todays_nr
=> 3
irb(main):026:0> Ticket.all.to_a.select!{ |t| t.created_at.to_date == Date.today }
=> nil
irb(main):028:0> Ticket.first.created_at
=> Sat, 20 Dec 2014 16:19:31 UTC +00:00
irb(main):029:0> Ticket.first.created_at.to_date
=> Sat, 20 Dec 2014
irb(main):030:0> Date.today
=> Sat, 20 Dec 2014
irb(main):031:0> Date.today.to_date
=> Sat, 20 Dec 2014
irb(main):032:0> Date.today == Ticket.first.created_at.to_date
=> true
irb(main):033:0> Date.today.to_date == Ticket.first.created_at.to_date
=> true
irb(main):034:0>
irb(main):035:0* Ticket.all.to_a.select!{ |t| t.created_at.to_date == Date.today }
=> nil
irb(main):036:0> Ticket.all.map(&:created_at)
=> [Sat, 20 Dec 2014 16:19:31 UTC +00:00, Sat, 20 Dec 2014 16:21:12 UTC +00:00]
irb(main):037:0> _[0].to_date == Date.today
=> true
It looks like the condition for select! is properly parsed, manual check shows there are some elements to that condition, but select! does not return any array. Once again, this does work locally.
Database has been migrated and fixtures loaded just as on local.
Although self.reset_todays_nr is scheduled with cron which might cause problems, this method is not triggered in this case, so it's rather irrelevant for the problem, but I posted it here just in case this problem is more advanced than I suppose.
Could anyone help me out here, please?
That is weird indeed. Particularly because I ran some commands in my Rails console just now and array.select!{} shouldn't return nil unless the array was empty to begin with.
[1].select!{ |t| false } #=> []
[].select!{ |t| false } #=> nil
So recheck what the output of Ticket.all.to_a is.
Also, your select condition can be set simply as:
var = Ticket.select{ |t| t.created_at.to_date == Date.today }
That will select all tickets itself and then filter.
But it would be preferable to filter and count in the query rather than load up everything in memory and then do further operations for comparisons. Check this out:
Ticket.where("DATE(created_at) = DATE(?)", Date.today).count
Or change the DATE(?) part with your SQL's "get today's date" function.
Alternatively, you could:
now = DateTime.now
range = (today.beginning_of_day)..(today.end_of_day)
Ticket.where(created_at: range).count
Keep the possible discrepancy of time-zone in mind i.e. the created_at column might have a different time-zone than generated by DateTime.now. You'll have to check.
#Humza, thank you very much! It isn't the precise solution, but helped me to solve the case. Thanks a lot for Ticket.where("DATE(created_at) = DATE(?)", Date.today).count - I was thinking exactly the same, i.e. not to load the entire array and only then evaluate it, so thanks for a way of doing it. If you look at my code, you see that in set_todays_nr I purposedly place how_many_today in a condition so as to run the search at most as often as cron is scheduled to reset #todays_nr. After changing it, bug became more visible: because of the flow of the app, the new how_many_today was returning 1 - the ticket is created before this method is called. Though the mystery of strange heroku behaviour remains unsolved, I didn't sleuth further as changing the method to the below form solved the problem. Now it looks like this:
def self.how_many_today
Ticket.where("DATE(created_at) = DATE(?)", Date.today).count
end
# This method is scheduled with cron; check config/schedule.rb
def self.reset_todays_nr
#todays_nr = nil
end
def self.set_todays_nr
if #todays_nr.nil?
#todays_nr = how_many_today
else
#todays_nr += 1
end
end
irb(main):044:0> i1.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):045:0> i2.created_at
=> Thu, 24 Apr 2014 02:41:15 UTC +00:00
irb(main):046:0> i1.created_at == i2.created_at
=> false
irb(main):047:0> i1.created_at.to_time.to_i == i2.created_at.to_time.to_i
=> true
Seems not to work validates_uniqueness_of :created_at
because
irb(main):046:0> i1.created_at == i2.created_at
=> false
How to validate created_at? Don't want to save with the same date.
+++ UPDATE +++
irb(main):048:0> i1.created_at.class
=> ActiveSupport::TimeWithZone
irb(main):049:0> i2.created_at.class
=> ActiveSupport::TimeWithZone
Since they might have different precision milliseconds.
Refer to the post: Testing ActiveSupport::TimeWithZone objects for equality
Chances are the millisecond values would be unequal.
puts i1.created_at.usec
puts i2.created_at.usec
I think, if you are getting concurrent requests, there are chances that you may have multiple entries in the table which are created at same time and will have same time stamps.
As you said, if you don't want to save with the same date, you can put a lock while saving the entries, removing the possibility of creating two entries at same time. In that case validates_uniqueness_of :created_at should also work.
Just in case
class Item < ActiveRecord::Base
attr_accessible :asin, :domain, :formatted_price, :user_id, :created_at
validate :double_dates
private
def double_dates
if Item.where(:user_id => self.user_id, :asin => self.asin, :domain => self.domain).where("DATE(created_at) = ?", Date.today).length >= 1
errors.add(:created_at, "no double dates")
end
end
end
Hi I have application running on Ruby 1.8.7 (i am presently not in a position to update this to 1.9.2) and Rails 3.0.9 running mongoid 2.2.2.
I have a couple of named scopes which I have defined as follows
scope :for_pickups, where(:state => "accepted")
scope :confirmed_shipments, where(:state => "confirmed", :created_at.lt => Date.today - 1.day).desc(:created_at).limit(10)
scope :undelivered_shipments, Proc.new{ |start_date_utc, end_date_utc|
start_date_utc = (Date.today - 7.days).to_time.utc if !start_date_utc.present?
end_date_utc = Date.today.to_time.utc if !end_date_utc.present?
any_of({:created_at.gte => start_date_utc, :created_at.lte => end_date_utc},
{:pickup_date.gte => start_date_utc, :pickup_date.lte => end_date_utc},
{:desired_arrival_date.gte => start_date_utc, :desired_arrival_date.lte => end_date_utc}).
where(:state.in => ["accepted"], :state.nin => ["delivered"]).without(:carrier_digest, :carrier_label_image_base64, :carrier_label_html_base64)
}
scope :search, Proc.new{|search_params|
regexp = Regexp.new(/.*#{search_params.strip}.*/i, true)
where(:golfer_name => regexp)
}
This seems to work well and return the expected values when I use them on their own but when I use scope chaining it gets messed up.
For instance when I run Model.undelivered_shipments.search("sid") i get an array which has :ids as the first parameter
[:ids, #[{:created_at=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}, {:pickup_date=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}, {:desired_arrival_date=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}], :_id=>"sid", :state=>{"$nin"=>["delivered"], "$in"=>["accepted"]}},
options: {:fields=>{:carrier_digest=>0, :carrier_label_html_base64=>0, :carrier_label_image_base64=>0}},
class: Shipment,
embedded: false>
]
Not sure why it returns this and why mongoid criteria defines :_id => 'sid' However when i run the search before the undelivered_shipments method it generates the criteria correctly.
It works if run it with other scopes like for_pickups.search etc but not with the undelivered shipments. I understand I am missing something but I've been through the docs and I can't find anything that really helps. I would really appreciate any help on this
Thanks in advance.
I have the following model and methods:
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(:purchase_date => Date.today)
end
def self.yesterday
where(:purchase_date => Date.yesterday)
end
Why on my form if I give my date_select field :purchase_date 1/4/2012(yesterday method) it also counts as today(today method) and if I give it 1/5/2012 (today) it is nil?
P.S. I am using Rails 3.0.10 with PostgreSQL.
Update
This is my console returns:
$ rails console --s
Loading development environment in sandbox (Rails 3.0.10)
Any modifications you make will be rolled back on exit
irb(main):001:0> Date.today
=> Wed, 04 Jan 2012
irb(main):002:0> Time.now
=> 2012-01-04 21:28:07 -0500
irb(main):003:0> Time.zone.now
=> Thu, 05 Jan 2012 02:28:18 UTC +00:00
irb(main):004:0> Date.yesterday
=> Wed, 04 Jan 2012
irb(main):005:0>
Now yesterday is screwed up, makes no sense....
This is happening because calculations.rb is calling the "current" method of the Date class for the configured timezone (Defaults to UTC).
If you open the rails console you can see the date at which "Date.yesterday" is calculating on by doing:
Time.zone.today
This will probably show you tomorrow's date. So Date.yesterday for what rails sees as today, is today. ;)
You can work with the Date library more directly by doing:
Date.today.advance(:days => -1)
This will give you yesterday's date like you expect whereas active_support is returning:
Date.current.advance(:days => -1)
If you use the Time class, you'll have access to UTC (or "zulu") time zone rather than using the environment's time zone in Date class.
class UserPrice < ActiveRecord::Base
attr_accessible :price, :purchase_date,
def self.today
where(purchase_date: today_utc_date)
end
def self.yesterday
where(purchase_date: today_utc_date.yesterday)
end
private
def today_utc_date
#today ||= Time.zone.today
end
end
Also, if you need to process an outside date, for example params[:purchase_date]:
Time.parse(params[:purchase_date]).utc.to_date
I have written a task that will run periodically to recalculate and update the information in a particular column of an Active Record model. Frequently, the newly calculated object (it's a json-encoded hash, if that matters) will be the same as the existing objects. In such cases, I suspect that it may be more efficient to check for this identity and abort the save rather than saving. Thus, I am considering using self.save! if changed? rather than merely self.save!. My questions are:
Does Active Record do this automatically, so that these two commands are actually equivalent?
If, as I suspect, it does not, am I likely to save CPU time by adding the if changed? condition?
(I apologize if I've misused some terminology here. I'm new to the game.)
No db query is made, here is a simple example using Rails 3.1.1 in console:
> s = Shop.first
=> #<Shop id: 1, name: "Ben", address: "paris", latitude:
> 48.856614, longitude: 2.3522219, gmaps: true, created_at: "2011-10-13 13:17:24", updated_at: "2011-10-13 13:17:24"> ruby-1.9.2-p290 :003 >
> s.name = "Ben" => "Ben"
> s.save
=> true
> s.updated_at
=> Thu, 13 Oct 2011 13:17:24 UTC +00:00
> s.update_attribute(:name, "Ben")
=> true
> s.updated_at
=> Thu, 13 Oct 2011 13:17:24 UTC +00:00
> s.update_attributes({:name => "Ben"})
=> true
> s.updated_at
=> Thu, 13 Oct 2011 13:17:24 UTC +00:00
> s.name = "Ben"
=> "Ben"
> s.save!
> s.updated_at
=> Thu, 13 Oct 2011 13:17:24 UTC +00:00
Edit:
rails profiler 's = Shop.first; s.name = "Ben"; s.save;' 's = Shop.first; s.name =
Loaded suite script/rails
Started
ProfilerTest#test_s_shop_first_s_name_ben_s_save (78 ms warmup)
process_time: 5 ms
ProfilerTest#test_s_shop_first_s_name_ben_s_save_if_s_changed (1 ms warmup)
process_time: 2 ms
Finished in 2.597531 seconds.
So it's faster with if changed?.
I know this is a very old post at this point... but it is also worth noting that callbacks like before_save would always be called even if the database will not be updated by the save!. So, calling save! instead of save! if changed? could take considerably longer if the callbacks are doing anything of consequence (including additional database queries).