I want my rails app to accept dates for a date field in the format dd/mm/yyyy.
In my model I have tried to convert the date to the American standard which I think the Date.parse method that Rails will call on it is expecting:
before_validation :check_due_at_format
def check_due_at_format
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time
end
However, this returns:
TypeError in OrdersController#update
can't dup NilClass
If it is useful to know, the Items form fields are a nested for within Orders, and Orders are set to:
accepts_nested_attributes_for :items, :reject_if => lambda { |a| a[:quantity].blank? && a[:due_at].blank? }, :allow_destroy => :true
So the items are being validated and saved/updated on #order.save/#order.update_attributes
Thank you!
It may be just a case of the due_at value being nil. In your case it's an empty string, but ignored because of the :reject_if option on accepts_nested_attributes_for, and so it remains as nil.
>> Date.strptime(nil, "%d/%m/%Y")
TypeError: can't dup NilClass
Take care of it with some conditional then.
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time unless self.due_at.nil?
I have struck exactly the same problem in Ruby 1.8.7 and the only way that I could solve it was to do the self assignment in two steps!!
xxx = Date.strptime(self.due_at,"%d/%m/%Y").to_time
self.due_at = xxx
I can' believe that this should be necessary. The only thing I can think of is that ruby is assigning fields in the new target date class on a piecemeal basis before it has finished using the due_at source string on the right hand side.
The problem does not seem to exist in Ruby 1.9.3
Related
Using Rails 3.1.3 and Ruby 1.9.3.
I want to give the user a list of possible date/time formats. The user's selection will be stored in the Users table. Date/time values are then formatted using the I18n.localize function. I actually have 10 formats; here by way of example are the first two:
config/locales/datetime.en.yml
en:
time:
format_labels:
mdyslash12: mm/dd/yyyy - hh:mm am (12-hour)
mdyslash24: mm/dd/yyyy - hh:mm (24-hour)
formats:
mdyslash12: ! '%m/%d/%Y %I:%M%p'
mdyslash24: ! '%m/%d/%Y %H:%M'
My question is where to store the list of possible date/time formats. I've identified three possibilities.
1. List options as a CONSTANT in model:
app/models/user.rb
DATETIME_FORMATS = %w[mdyslash12 mdyslash24]
validates :datetime_format, :presence => true,
:inclusion => { :in => DATETIME_FORMATS }
2. Create an application constant and validate against that:
config/initializers/constants.rb
Rails.configuration.datetime_formats = "mdyslash12 mdyslash24"
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => Rails.application.config.datetime_formats.split(" ") }
3. Validate directly against the locale file:
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => (I18n.t 'time.format_labels').stringify_keys.keys }
This option uses a feature that is new to me: I18n.t 'time.format_labels' returns a hash of ALL keys and values from that branch of the locale file. The hash keys are symbols, so to get a string array, I call stringify_keys to convert the symbols to strings, then keys to give me only the keys (no values).
Option #3 is the DRYest in that I don't have to list the possible values in two places. But it doesn't feel quite right to depend on the locale file for the discreet list of possible date/time formats.
What would you recommend? One of these options? Something else?
I'd go with option 1 to start with, since it's simple, clear, and fairly DRY. I might refactor to option 2 if I ended up needing that constant in another model.
Option 3 has the potential to behave differently based on the locale, so I don't like that. If you end up forgetting to specify your format labels in a new locale, your selection list might end up being empty (or if there's a typo in one locale, it might take longer to notice, since the typo would be treated as valid for that locale). Regardless it's probably a good idea to unittest this in all your supported locales.
I want to validate my date (which actually have DATE type) in model. So, i try to write for that simle method and run it via validation.
Teacher
class Teacher < ActiveRecord::Base
attr_accessible :teacher_birthday # DATE type!
belongs_to :user
validates :teacher_birthday, :presence => true,
:unless => :date_is_correct?
########
def date_is_correct?
parsed_data = Date._parse(:teacher_birthday)
input_day = parsed_data[:mday]
input_month = parsed_data[:mon]
input_year = parsed_data[:year]
correct_days = 1..31
correct_months = 1..12
correct_year = 1900..2000
if ( correct_days.member? input_day ) and ( correct_months.member? input_month) and
( correct_year.member? input_year)
true
else
errors.add(:teacher_birthday, 'date is invalid')
false
end
end
When i run rspec a lot of tests fail.
TypeError: can't convert Symbol into String
# ./app/models/teacher.rb:56:in `_parse'
# ./app/models/teacher.rb:56:in `date_is_correct?'
I suppose i do something wrong. Can someone tell me what is wrong?
This isn't necessary at all. If Date.parse(:teacher_birthday) returns a date and doesn't raise an exception, you have a valid date.
Date._parse expects a string-value containing a date and will in your code always try to parse 'teacher_birthday'. You need to get the value of the field first and pass the value to Date._parse. ActiveRecord creates methods with the same name as the field to get the value.
Any of the following will work:
Short way
parsed_data = Date._parse(teacher_birthday)
Identically to the first (the self. is added for you during parsing)
parsed_data = Date._parse(self.teacher_birthday)
Explicit way
parsed_data = Date._parse(self[:teacher_birthday])
A new gem has been created to help validate types in rails and an explanatory blog post exists to answer more of the "why" it was created in the first place.
With this library your code would simple be:
class Post < ActiveRecord::Base
validates_type :teacher_birthday, :date
end
This will throw an exception when anything except a a Date is assigned to :teacher_birthday.
I've got a model, Entity.
class Entity
include Mongoid::Document
field :x
field :y
field :z, type => Hash, :default => {} # new field
end
I added a new field to it, a hash. When I try to use it, I get an error. My code is:
e = Entity.first
if e.z["a"] # if there is a key of this in it?
e.z["a"] = e.z["a"] + 1
else
e.z["a"] = 1
end
But, this error with an undefined method get for hash. If I try to create an initializer for it, to set the values in an existing document, it errors with the same error. What am I doing wrong?
Initializer looks like:
e = Entity.first
e.write_attribute(:z, {})
Thanks
Sorted it.
It seems the answer is to set in Mongoid 1.9.5 the hash to:
field :hash_field, :type => Hash, :default => Hash.new
and it can access and initialize it. Not quite understanding why, but happy to have the answer !
I'm facing a weird behavior in Rails 3 model instantiation.
So, I have a simple model :
class MyModel < ActiveRecord::Base
validates_format_of :val, :with => /^\d+$/, :message => 'Must be an integer value.'
end
Then a simple controller :
def create
#mod = MyModel.new(params[:my_model])
if #mod.save
...
end
end
first, params[:my_model].inspect returns :
{:val => 'coucou :)'}
But after calling #mod = MyModel.new(params[:my_model]) ...
Now, if I call #mod.val.inspect I will get :
0
Why am I not getting the original string ?
At the end the validates succeed because val is indeed an integer.
Is this because val is defined as an integer in the database ?
How do I avoid this behavior and let the validation do his job ?
If val is defined as an integer in your schema then calling #my_model.val will always return an integer because AR does typecasting. That's not new to rails 3, it's always worked that way. If you want the original string value assigned in the controller, try #my_model.val_before_type_cast. Note that validates_format_of performs its validation on this pre-typecast value, so you don't need to specify that there.
EDIT
Sorry I was wrong about the "performs its validation on this pre-typecast value" part. Looking at the code of the validation, it calls .to_s on the post-typecast value which in your case returns "0" and therefore passes validation.
I'd suggest not bothering with this validation to be honest. If 0 is not a valid value for this column then validate that directly, otherwise just rely on the typecasting. If the user enters 123 foo you'll end up with 123 in the database which is usually just fine.
There is also better fitting validator for your case:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#validates_numericality_of
I wrote this retrieval statement to check if an appointment being saved or created dosent conflict with one thats already saved. but its not working, can someone please point me to where I'm going wrong?
#new_appointment = :appointment #which is the params of appointment being sent back from submit.
#appointments = Appointment.all(:conditions => { :date_of_appointment => #new_appointment.date_of_appointment, :trainer_id => #new_appointment.trainer_id}
)
the error is from the :date_of_appointment => #new_appointment.date_of_appointment this will always be false as:
thank you
At face value, there doesn't appear to be anything wrong with your syntax. My guess is that #new_appointment isn't containing the values you're expecting, and thus the database query is returning different values than you expect.
Try dumping out #new_appointment.inspect or check the logfiles to see what SQL the finder is producing, or use
Appointment.send(:construct_finder_sql, :conditions => {
:date_of_appointment => #new_appointment.date_of_appointment,
:trainer_id => #new_appointment.trainer_id
})
to see the SQL that will be generated (construct_finder_sql is a protected ActiveRecord::Base method).
Update based on your edit
#new_appointment = :appointment should be something like #new_appointment = Appointment.new(params[:appointment]). :appointment is just a symbol, it is not automatically related to your params unless you tell it to.