Update time attribute in Rails, SUPER easy, i'm being dense - ruby-on-rails

I have the following RentalRequest object:
#<RentalRequest id: 3, pickup_start: "2015-03-16 21:00:00", pickup_end: "2015-03-16 23:00:00">
I'd like to update pickup_start and pickup_end to be on 2015-03-17 instead. When I run:
RentalRequest.find(3).update_attribute(pickup_end: "2015-03-17 23:00:00")
ArgumentError: wrong number of arguments (1 for 2)
Not sure what other argument I need to provide.
If it helps, here's the type and output of the attribute by itself
RentalRequest.find(3).pickup_end
=> Mon, 16 Mar 2015 23:00:00 UTC +00:00
RentalRequest.find(3).pickup_end.class
=> ActiveSupport::TimeWithZone

Look at the docs for update_attribute, see how it is expecting name, value as the arguments?
You're passing name: value which is a hash, a single argument of a hash. That's why you're getting the error about wrong number of arguments, the method is expecting 2 but you're providing only 1.
You can either change your code to call: update_attribute( :pickup_end, "2015-03-etc..") or you can use the update_attributes method instead, which takes a hash. I'd recommend the latter, because then you can update both attributes at once, in the same SQL statement, and in the same DB transaction:
update_attributes pickup_end: "2015...etc", pickup_start: "2015...etc"

Related

Can't grab a day from datetime in Rails view

Basically, what work in rails console doesn't seem to work in my view - I have a workshop model with column "start_date" of datetime type.
I have created an object of this kind and when I enter rails console
Workshop.find(<id>).start_date.day
returns specific day as expected (e.g. '21' in case of datetime 'Fri, 21 Apr 2017 17:00:00 UTC +00:00').
However, in my view I am not able to use 'day' method. I'm using Slim gem and this code in my show view for workshop:
p= workshop.start_date.day
results in this error: undefined method `day' for nil:NilClass
But code
p= workshop.start_date
displays the whole date properly and code
p= workshop.start_date.class
displays that the class of the object is: ActiveSupport::TimeWithZone
What is happening, why do Rails say that the class is nil, how to make it work?
Please use the following in views,
p= workshop.start_date.day if workshop.start_date
I guess some record has the start_date attribute nil and that is the reason the views throw error.

rails hash of date functions

I have some reocure's model, that has frequencies and should create several records on it's own creation.
I'm trying to convert the integer frequency (1,7,30) to a function of (days, weeks, months) Respectively, so I can add this to the new records.
I tried doing it by using a hash of date functions, in order to use it like date+i.months():
date_hash={1=>days(), 7=>weeks(), 30=>months()}
but I'm getting an error.
It should be used inside of a loop:
some_number.times do |i|
Record.create({...., :date => start_date+i.(date_hash[frequency]),....})
end
I'm getting this error:
undefined method `days' for # <MyController:0x111111>
Thanks for your assist.
You have a lot of not-Ruby here. Ruby doesn't provide first-class functions like you seem to expect (date() would assign the value of that key to the result of the invocation of some function named date available in the local scope), and you can't call a variable method name like that. In Ruby, method calls on objects (receivers) are messages sent to the receiver, with optional arguments. The typical way you would call something like this is by using Object#send with the desired method name:
methods = {1 => :days, 7 => :weeks, 30 => :months}
x.send(methods[1]) # Calls x#days
However, the intent of your code is rather unclear, and there is likely a much better-factored way of doing what you want.

ActiveRecord parse string to datetime?

If I pass a String into Datetime column while creating new AR object, it will be automatically parse:
1.9.2p290 :011 > Movie.new(:release_date=>"21-Nov-1990")
=> #<Movie id: nil, release_date: "1990-11-21 00:00:00", created_at: nil, updated_at: nil>
How does Rails, or ActiveRecord, do this magic? Which method does it use?
Rails adds a to_date method to String. Its source is simple:
# File activesupport/lib/active_support/core_ext/string/conversions.rb, line 42
def to_date
return nil if self.blank?
::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end
Date._parse is native to Ruby (the same method is called by Date.parse) and it's where the real work is done.
It first uses a regular expression to remove extraneous symbols from the string, then passes it to other methods like _parse_eu, _parse_iso, _parse_dot and so on. Each of these uses its own regular expressions and other methods to see if it's a date that it understands and extract the meaningful information from it. Once one of them "works" (i.e. returns true), the rest are skipped. Finally, back in _parse, the extracted information is used to build a date and time, doing a little more work to figure out things like checking for the day of the week and whether a year value of "12" should mean 1912 or 2012.
The docs call this a heuristic method, which could be taken to mean it throws a bunch of possibilities at the wall to see what sticks. It's pretty poorly-documented but works remarkably well.
There's also to_datetime if you need the time.
You probably want to use Date.strptime(str).
As other comments and the documentation suggests, String#to_datetime "Converts a string to a DateTime value.":
"1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
"01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
"2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
"12/13/2012".to_datetime # => ArgumentError: invalid date

How can I use US-style dates in Rails using Ruby 1.9?

I'm in the U.S., and we usually format dates as "month/day/year". I'm trying to make sure that my Rails app, using Ruby 1.9, assumes this format everywhere, and works the way it did under Ruby 1.8.
I know that lots of people have this issue, so I'd like to create a definitive guide here.
Specifically:
'04/01/2011' is April 1, 2011, not Jan 4, 2011.
'4/1/2011' is also April 1, 2011 - the leading zeros should not be necessary.
How can I do this?
Here's what I have so far.
Controlling Date#to_s behavior
I have this line in application.rb:
# Format our dates like "12/25/2011'
Date::DATE_FORMATS[:default] = '%m/%d/%Y'
This ensures that if I do the following:
d = Date.new(2011,4,1)
d.to_s
... I get "04/01/2011", not "2011-04-01".
Controlling String#to_date behavior
ActiveSupport's String#to_date method currently looks like this (source):
def to_date
return nil if self.blank?
::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end
(In case you don't follow that, the second line creates a new date, passing in year, month and day, in that order. The way it gets the year, month and day values is by using Date._parse, which parses a string and somehow decides what those values are, then returns a hash. .values_at pulls the values out of that hash in the order Date.new wants them.)
Since I know that I will normally pass in strings like "04/01/2011" or "4/1/2011", I can fix this by monkeypatching it like this:
class String
# Keep a pointer to ActiveSupport's String#to_date
alias_method :old_to_date, :to_date
# Redefine it as follows
def to_date
return nil if self.blank?
begin
# Start by assuming the values are in this order, separated by /
month, day, year = self.split('/').map(&:to_i)
::Date.new(year, month, day)
rescue
# If this fails - like for "April 4, 2011" - fall back to original behavior
begin
old_to_date
rescue NoMethodError => e
# Stupid, unhelpful error from the bowels of Ruby date-parsing code
if e.message == "undefined method `<' for nil:NilClass"
raise InvalidDateError.new("#{self} is not a valid date")
else
raise e
end
end
end
end
end
class InvalidDateError < StandardError; end;
This solution makes my tests pass, but is it crazy? Am I just missing a configuration option somewhere, or is there some other, easier solution?
Are there any other date-parsing cases I'm not covering?
Gem: ruby-american_date
This gem was created since I asked this question. I'm now using it and have been pleased.
https://github.com/jeremyevans/ruby-american_date
Date.strptime is probably what you're looking for in ruby 1.9.
You're probably stuck monkeypatching it onto string.to_date for now, but strptime is the best solution for parsing dates from strings in ruby 1.9.
Also, the formats are symmetric with strftime as far as I know.
you can use rails-i18n gem or just copy the en-US.yml and set your default locale "en-US" in config/application.rb
For parsing US-style dates, you could use:
Date.strptime(date_string, '%m/%d/%Y')
In console:
> Date.strptime('04/01/2011', '%m/%d/%Y')
=> Fri, 01 Apr 2011
> Date.strptime('4/1/2011', '%m/%d/%Y')
=> Fri, 01 Apr 2011
Use REE? :D
Seriously though. If this is a small app you have complete control over or you are standardizing on that date format, monkey patching for a project is totally reasonable. You just need to make sure all your inputs come in with the correct format, be it via API or website.
Instead of using to_s for Date instances, get in the habit of using strftime. It takes a format string that gives you complete control over the date format.
Edit:
strptime gives you full control over the parsing by specifying a format string as well. You can use the same format string in both methods.
Another option is Chronic - http://chronic.rubyforge.org/
You just need to set the endian preference to force only MM/DD/YYYY date format:
Chronic::DEFAULT_OPTIONS[ :endian_precedence ] = [ :middle ]
However the default for Chronic is the out-of-order US date format anyway!

How convert a string into a ActiveSupport::TimeWithZone?

I need to update a field (called updated_at). The field in MySQL is of type datetime, and the class is ActiveSupport::TimeWithZone. But the dates are strings like "10/17/2008". I used "10/17/2008".to_date (And I intend .to_time and .to_datetime), and even if in console the ActiveRecord class save successfully, the field in the database still is the current date.
OK.. let's take them one at the time.
First, it is not recommended to set a field name updated_at, since this is a "magic" field that is automatically populated by Rails.
If you want to disable this functionality, you may:
class Foo < ActiveRecord::Base
self.record_timestamps = false
end
in your class, but this will also disable created_at fields.
The best option is to add a new field (e.g. my_updated_at) as date in the database, and then Rails will automatically handle conversions, meaning that the next snippet will work:
Foo.new({:my_updated_at => "10/17/2008"})
Second, the answer on how to parse a string to ActiveSupport::TimeWithZone is:
ActiveSupport::TimeZone['UTC'].parse("10/17/2008")
but I don't think this will help you (of course, change UTC with your current date/time).
Simply
date_as_string = "2008-10-17"
ActiveSupport::TimeZone['UTC'].parse(date_as_string)
# => Fri, 17 Oct 2008 00:00:00 UTC +00:00
And just to confirm that it worked..
ActiveSupport::TimeZone['UTC'].parse(date_as_string).class
# => ActiveSupport::TimeWithZone

Resources