no implicit conversion of ActiveSupport::TimeWithZone into Integer - ruby-on-rails

I want to set default time to 2 hours from now. So I wrote this peace of code:
<%= f.datetime_select :starts_at, :default => { :hour => 2.hours.from_now, :minute => 0 }, :order => [:day, :month, :year] %>
I've got an error:
no implicit conversion of ActiveSupport::TimeWithZone into Integer

2.hours.from_now yields a ActiveSupport::TimeWithZone object but hour is expecting an Integer.
:hour => 2.hours.from_now.hour
I tried this and it works fine for me. Do you have a validation on your model which is causing it to fail?
Also this will not give you exactly 2 hours since you are setting the minute variable.

Have you tried with?
:hour => 2.hours.from_now.hour
I edited because you need the absolute hour.

Related

select_date is showing number in dropdown not month name

Hi I am using select_date function to have dropdown in my view for month name only. But it is giving me number instead of month name in the dropdown. Can anybody help on this?
my code:
<%= select_date(Date.today, :order => [:month, :year], :discard_day => true, :use_short_month => true) %>
Thanks in advance
Sumanta
I do not see any issue with syntax, I tried the same and got the expected result - Month in dropdown. As suggested please restart your server.

How to parse time field into hours, minutes and seconds?

How would I parse my start_at column to be three different fields for hours, minutes and seconds instead of just one?
Note: I want to keep it as one column and not make three different ones.
Here is my code:
Table:
class CreateTimers < ActiveRecord::Migration
def change
create_table :timers do |t|
t.time :start_at
t.timestamps
end
end
end
Form:
<%= form_for(#timer) do |f| %>
<div class="form-inputs">
<%= f.text_field :start_at %>
</div>
<div class="form-actions">
<%= f.submit 'Start', :class => 'btn-primary span3' %>
</div>
<% end %>
If you just want to split time to three separate inputs you can use the time_select helper.
Otherwise use the strftime method; Check http://strfti.me for more help.
You don't say what format your time values are in. Because users love to be different, if you give them a free-form text field, odds are really good you'll get data in varying formats, so you'll have to be flexible.
You can try using DateTime's parse method, which provides some flexibility for the formats.
DateTime.parse('2001-02-03T04:05:06+07:00')
#=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
DateTime.parse('20010203T040506+0700')
#=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
DateTime.parse('3rd Feb 2001 04:05:06 PM')
#=> #<DateTime: 2001-02-03T16:05:06+00:00 ...>
There's also the Chronic gem, which allows even more flexibility in how the values can be entered.
Once you have a DateTime or Time value, you can use that object's methods to get at the hour, minute and second fields:
now = Time.now # => 2012-10-03 07:50:26 -0700
now.hour # => 7
now.min # => 50
now.sec # => 26
or:
require 'date'
now = DateTime.now # => #<DateTime: 2012-10-03T07:52:53-07:00 ((2456204j,53573s,622304000n),-25200s,2299161j)>
now.hour # => 7
now.min # => 52
now.sec # => 53
Check out strftime at http://www.ruby-doc.org/core-1.9.3/Time.html
For e.g.
start_at - 2011-04-26 04:14:56 UTC
For hours:
start_at.strftime("%H") = "04"
For minutes:
start_at.strftime("%M") = "14"
For seconds:
start_at.strftime("%S") = "26"
start_at.strftime("%H %M %S")
More detailed documentation is at: http://www.ruby-doc.org/core-1.9.3/Time.html

multiparameter error with datetime_select

I have the following code in my form.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15, :include_blank => false) %> if either one is blank.
When one of the fields is left blank, I get:
1 error(s) on assignment of multiparameter attributes
The params that are being passed are:
{"utf8"=>"✓",
"authenticity_token"=>"kQpfsj5RxnDtxkvBdwPEFnX1fY6euKnMQeDRAkvJvIE=",
"event"=>{"description"=>"",
"venue"=>"",
"street"=>"",
"city"=>"",
"country_id"=>"",
"date_time(1i)"=>"",
"date_time(2i)"=>"",
"date_time(3i)"=>"",
"date_time(4i)"=>"00",
"date_time(5i)"=>"00",
"ticket_url"=>""},
"x"=>"94",
"y"=>"12"}
Anyone know why this is occurring?
There seems to be a "dirty" fix for this at this link, but perhaps there is a better solution in Rails 3?
Christian. This is a bug in Rails that checks the database to infer the type needed for the multiparameter attributes. My guess is that your "date_time" attribute is not associated with a time column in your database.
I recently tackled this problem where I wanted a non-database attribute to accepted multiparameter attributes, this was the best solution I could come up with:
I found myself wanting to set an attr_accessor to handle passing a date to my model in a form_for tag with the f.datetime_select helper. So this is what I had:
Model:
attr_accessor :my_time
View:
<%= f.datetime_select :my_time %>
Unfortunately when I submit my form I get this:
1 error(s) on assignment of multiparameter attributes
Well it turns out that this is actually a Rails bug a ticket for which has been submitted. In the meantime how do we make this work? The only solution I could find that was remotely attractive was to make use of composed_of as a replacement for attr_accessor. so...
Model:
composed_of :my_time,
:class_name => 'Time',
:mapping => %w(Time to_s),
:constructor => Proc.new{ |item| item },
:converter => Proc.new{ |item| item }
I know almost nothing about the composed_of method so you should probably do your own reading on it, but what I do know is that it creates both a reader and writer for the given instance variable, and more importantly, the setter accepts multiparameter attributes. How I chose the options:
class_name: the name of our expected class. In this case, Time
mapping: the first argument is the class and the second argument seems to work with any method that an instance of the class responds to. I chose to_s
constructor: Not really sure how this is supposed to work. Seems to be called when #my_time is nil.
converter: Not really sure how this is supposed to work. Seems to be called when from my_time=, but doesn't seem to be applied with mass assignment.
One problem I ran into with this solution was that times were getting set in UTC instead of the environment's time zone. So unfortunately we cannot use my_time directly, but instead need to convert it to the proper time zone:
Time.zone.parse(my_time.to_s(:number))
What Does ActiveRecord::MultiparameterAssignmentErrors Mean?
def initialize(attributes={})
date_hack(attributes, "deliver_date")
super(attributes)
end
def date_hack(attributes, property)
keys, values = [], []
attributes.each_key {|k| keys << k if k =~ /#{property}/ }.sort
keys.each { |k| values << attributes[k]; attributes.delete(k); }
attributes[property] = values.join("-")
end
I had the same problem using a date dropdown that wasn't backed by a database attribute. I wrote a little Rack middleware to cope with the problem:
class DateParamsParser
def initialize(app)
#app = app
end
def call(env)
if %w{POST PUT}.include? env['REQUEST_METHOD']
params = Rack::Utils.parse_query(env["rack.input"].read, "&")
# selects only relevant params like 'date1(1i)'
filtered_params = params.select{ |key, value| key =~ /\(\di\)/ }
# delete date params
filtered_params.each { |key, value| params.delete(key) }
# returns something like {'date1' => [2012, 5, 14], 'date2' => [2002, 3, 28]}
date_array_params = filtered_params.sort.reduce({}) do |array_params, keyvalue|
date_key = keyvalue.first.match(/(.+)\(/)[1] + ']'
array_params[date_key] ||= []
array_params[date_key] << keyvalue.last
array_params
end
# Creates params with date strings like {'date1' => '2012-5-14', 'date2' => '2002-3-28'}
date_params = Hash[date_array_params.map{ |key, date_array| [key, date_array.join('-')] }]
params.merge! date_params
env["rack.input"] = StringIO.new(Rack::Utils.build_query(params))
env["rack.input"].rewind
end
#app.call(env)
end
end
And in application.rb I put
config.middleware.insert_before ActionDispatch::ParamsParser, "DateParamsParser"
Note that I only build a date string here. So if you also require time you'll need to build the date_params differently.
I faced the same problem with the model below
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
end
The corresponding form with the field for the expiration date:
<div class="field">
<%= f.label :expiration_date %>
<%= f.date_select(:expiration_date, start_year: Time.now.year + 3, :end_year => Time.now.year - 3, discard_day: true) %>
</div>
as mentioned by #gabeodess the problem is checking the database to infer the type accordingly the solution I did for it was adding the following code to the model to put the type of the needed attribute in this case :expiration_date so the model is modified to be the following
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
columns_hash["expiration_date"] = ActiveRecord::ConnectionAdapters::Column.new("expiration_date", nil, "date")
end
Hope this is useful
Remove :include_blank => false from your code.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15 %>
Thanks....
I was facing the same problem.
I just added attr_accessible for that attribute and it works fine.
Hope it helps.

Rails date_select helper and validation

I have a date field in a model backed form in my Rails App:
<%= f.date_select :birthday,
{:start_year => Time.now.year,
:end_year => 1900,
:use_short_month => true,
:order => [:month, :day, :year],
:prompt => {:month => 'Month', :day => 'Day', :year => 'Year'}},
{:class => 'year',
:id => 'user_birthday'}
%>
It is being validated in the model code using:
validates_presence_of :birthday, :message => 'is a required field'
Unfortunately, if the user enters a partial value such as just the year, the form still submits without an error. Instead a funky date value gets written to the db. How do I make all three fields be mandatory?
I'd like to write a custom validation for this, but I don't know how to properly access the indvidual pieces of the birthday element. How can I do this?
Thanks!
Moe
I think you would have to create the validation in the controller itself.
The date parts are being passed to birthday(1i), birthday(2i) and birthday(3i). The problem here is that they are assigned immediately when passing the attributes and thus before any validations occur.
You could also overwrite the attributes= method to create your own validation there, but I would not suggest you to do that.
Keep in mind that if you do validations, it might be good to validate against any incorrect date as well. (for instance 31st of February, which when passed will yield 2nd of March and not an error).
I think the main issue here is that ActiveRecord is actually replacing the empty values with 1 before creating the Date, which also means that if the visitor pass only the year, the date will be created on the 1st of January that year. I guess that is an expected behaviour to allow use of only one of year/month/day select and still create a useful date.
Related to this post, this is the best solution I've found. However I should add :day, :month, :year as attr_accessible, thing I don't understand why.. (because of validation? please let me know..)
User.rb
MONTHS = ["January", 1], ["February", 2], ...
DAYS = ["01", 1], ["02", 2], ["03", 3], ...
START_YEAR = Time.now.year - 100
END_YEAR = Time.now.year
YEAR_RANGE = START_YEAR..END_YEAR
attr_accessible :day, :month, :year
attr_accessor :day, :month, :year
before_save :prepare_birthday
validate :validate_birthday
private
def prepare_birthday
begin
unless year.blank? # in order to avoid Year like 0000
self.birthday = Date.new(self.year.to_i, self.month.to_i, self.day.to_i)
end
rescue ArgumentError
false
end
end
def validate_birthday
errors.add(:birthday, "Birthday is invalid") unless prepare_birthday
end
user registration form
<%= f.select :month, options_for_select(User::MONTHS), :include_blank => "Month" %>
<%= f.select :day, options_for_select(User::DAYS), :include_blank => "Day" %>
<%= f.select :year, options_for_select(User::YEAR_RANGE), :include_blank =>"Year" %>
You could override the validate_on_create method, like the following:
class MyModel < ActiveRecord::Base
def validate_on_create
Date.parse(birthday)
rescue
errors.add_to_base("Wrong date format")
end
end
After following Benoitr's suggestions I came up with something similar using virtual attributes. On the View side there are 3 separate select's (year,mon,day) inside of a 'fields_for'. The data is submitted to the controller's mass assignment (no modifications in controller, see asciicasts #16) and then passed to a getter/setter (i.e. virtual attribute) in the model. I'm using Rails 3.0.3, and simpleForm for the view code.
In the View:
<%= f.label "Design Date", :class=>"float_left" %>
<%= f.input :design_month, :label => false, :collection => 1..12 %>
<%= f.input :design_day, :label => false, :collection => 1..31 %>
<%= f.input :design_year, :label => false, :collection => 1900..2020 %>
In the Model:
validate :design_date_validator
def design_year
design_date.year
end
def design_month
design_date.month
end
def design_day
design_date.day
end
def design_year=(year)
if year.to_s.blank?
#design_date_errors = true
else
self.design_date = Date.new(year.to_i,design_date.month,design_date.day)
end
end
def design_month=(month)
if month.to_s.blank?
#design_date_errors = true
else
self.design_date = Date.new(design_date.year,month.to_i,design_date.day)
end
end
def design_day=(day)
if day.to_s.blank?
#design_date_errors = true
else
self.design_date = Date.new(design_date.year,design_date.month,day.to_i)
end
end
#validator
def design_date_validator
if #design_date_errors
errors.add(:base, "Design Date Is invalid")
end
end
'design_date_attr' is the virtual attribute which sets the value of design_date in the database. The getter passes back an hash similar to what gets submitted in the form. The setter checks for blanks and creates a new date object and sets it and also sets the error variable. The custom validator :design_date_validator checks for the error instance variable and sets the errors variable. I used ':base' because the variable name was not human readable and using base removes that value from the error string.
A few things to refactor might be the error checking instance variable, but it seems to work at least. If anyone knows a better way to update the Date objects I'd love to hear it.

date_select tag when upgrading from 2.1.2 to 2.3.8

We are currently in the process of upgrading our rails version and I have come across some weird error in one of my date_select tags.
Here is the culprit:
<%= date_select :consultant_assignment, :start_date, :order => [:day, :month, :year],
:start_year => 5.years.ago.year, :end_year => 5.years.from_now.year, :use_short_month => true %>
This throws out an error like this:
NoMethodError in Profiles#show
Showing app/views/people/_upcoming_assignments.html.erb where line #46 raised:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.size
Extracted source (around line #46):
43: <label for="consultant_assignment_start_date"><%= _('Start') %>:</label>
44: <% debugger %>
45:
46: <%= date_select :consultant_assignment, :start_date, :order => [:day, :month, :year], :start_year => 5.years.ago.year, :end_year => 5.years.from_now.year, :use_short_month => true %>
47:
48:
49:
I have tried to trace this problem and it seems that the error is generated when I put in the :month option in the :order array. So when I do :order => [:day, :year] the page loads perfectly.
I have searched for the exact problem but being more of a front end deisgner, I'm not sure how to procede with this. Is this an error by my code? Or is it a deprecated method?
Here is the application trace:
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:746:in `_unmemoized_month_names'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.3.8/lib/active_support/memoizable.rb:72:in `month_names'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:781:in `month_name'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:708:in `select_month'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:705:in `upto'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:705:in `select_month'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:898:in `send'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:898:in `build_selects_from_types'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:896:in `each'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:896:in `build_selects_from_types'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:646:in `select_date'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/date_helper.rb:920:in `to_date_select_tag_without_error_wrapping'
I have googled about unmemoized month names but I found nada.
Okay, I've tried taking out the whole :order clause just to make the darn thing work. This time it doesn't throw an error but the tag doesn't appear! I tried checking in irb and apparently my date_select tag is not returning any select tags, but just a bunch of hidden tags!
I tried this with the default rails example:
date_select("post", "written_on")
irb returns this:
=> "\n\n\n"
as you can see, no select tag anywhere. I tried datetime_select and I see a select tag in there. So what's the deal with 2.3.8's date_select?
use these format
date_select(object_name, method, options = {}, html_options = {})
put start_year , end_year , order_field etc within the options tag.
I think this might helps you.
The selects are prepared for multi-parameter assignment to an Active Record object.
Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that all month choices are valid.
It seems it is a problem with i18n as rails can't see the month names declared for some reason(it SHOULD see it since the default lang is en). Actually for some reason, it doesn't see the default language. Anyway our quick fix for now was to add a MONTH_NAMES constant in constants.rb and add that to the options in date_select.

Resources