I have a custom date validator that looks like this:
# app/validators/date_validator.rb
class DateValidator < ActiveModel::EachValidator
DATE_REGEX = /\d{2}\/\d{2}\/\d{4}/
def validate_each(record, attribute, value)
before_type_cast = "#{attribute}_before_type_cast"
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
raw_value ||= value
return if raw_value.blank?
record.errors[attribute] << (options[:message] || "must be in correct date format") if (raw_value =~ DATE_REGEX).nil?
end
end
In my model, I use that validator with the following code:
validates :date_assigned :date => true
My problem is, that when a user enters an invalid date, I'd like to display the invalid date on the form so they can correct it. But currently, when I enter "11111" into the date assigned field on the form, I believe the rails typecasting is converting it to nil (date_assigned is a date value in the DB), so instead of "11111" being displayed, nothing is.
If a user enters "01/01/01" (invalid per the date regex), the view is displaying "0001-01-01" instead of the original (invalid) string of "01/01/01".
Can someone help me figure out how to do this? Thanks
Related
I am trying to POST a json object to the rails app.
One of the field of this object is a a pretty much timestamp. It's saved in a 'timestamp' member in a model.
Rails handles well a lot of datetime formats represented by a string (as example I can send "December 24, 2015 at 9:46:24 PM PST" and it will work).
However, rails will reject an object if I try to send an integer (unix time) timestamp.
Is this standard behavior (or am I missing something)
We can easily do this by defining like a proxy attribute in your model
attr_accessible :integer_timestamp
def integer_timestamp
timestamp.to_time.to_i
end
def integer_timestamp=(value)
self.timestamp = value.blank? ? nil : Time.at(value.to_i)
end
You need to convert your epoch time explicitly.
It can be:
class MyModel < ActiveRecord::Base
def my_attr_name=(timestamp)
dt = begin
timestamp.to_datetime
rescue ArgumentError
DateTime.strptime(timestamp,'%s')
end
write_attribute :my_attr_name, dt
end
end
I have an input field on a form that has the users enter in a dollar amount. I'm using autoNumeric to mask that field so that as a user inputs 1000, it displays 1,000.00 in the text field.
The column in the database (requested_amount) is a decimal with precision => 8 and scale => 2.
I've made a callback based on other SO answers that attempts to strip the comma out of the input before validating and saving to the database.
def strip_commas_from_dollar_fields
self.requested_amount = self.requested_amount.to_s.gsub(',', '')
end
# Callback
before_validation :strip_commas_from_dollar_fields
The params hash that is passed through then I submit the form:
"requested_amount"=>"80,000.00"
However, when I view the number in console, it shows:
=> 80.0
I need the number to be 80000.00 in the database.
Because:
def strip_commas_from_dollar_fields
p self.requested_amount // <- here is 80.0 already
self.requested_amount = self.requested_amount.to_s.gsub(',', '')
end
check it. So, instead of this way try to create wrapper around requested_amount field:
def requested_amount_wrapper=(val)
self.requested_amount = val.to_s.gsub(',', '')
end
def requested_amount_wrapper
self.requested_amount
end
and use this wrapper field in your hash parameters:
"requested_amount_wrapper"=>"80,000.00"
When an ill-formatted or invalid date (e.g. February 29th on non-leap years) is input in a form, any validation appears to access a nil value. The reason for this is that validation in Rails occurs AFTER type casting the original string to a date variable: If this fails (for above reasons), the type casting returns a nil value which is stored in the attribute of the model.
I found out that I can access the original date string via the attribute_before_type_cast functions and was thus able to write the following custom validator:
class DateFieldValidator < ActiveModel::EachValidator
def validate_each( record, attribute, value )
# this may be interesting only if value could not be read
return unless value.nil?
# but not if original value was blank
original_value = record.read_attribute_before_type_cast( attribute )
return if original_value.blank?
# ignore any other value but String
return unless original_value.is_a? String
# _parse must return some useful values
parsed_value = Date._parse( original_value )
unless [ :year, :mon, :mday ].all? { |key| parsed_value.has_key?( key ) }
record.errors.add( attribute, ( options[ :message ] || I18n.t( 'validators.date_field.bad_syntax' )))
return
end
# valid_date? must return true
unless Date.valid_date?( parsed_value[ :year ], parsed_value[ :mon ], parsed_value[ :mday ])
record.errors.add( attribute, ( options[ :message ] || I18n.t( 'validators.date_field.bad_date' )))
return
end
# date is OK
end
end
This works fine but if the date is required (validate is with presence: true) I get two validation errors, one from my validator, the other from Rails, telling the user that the required attribute is missing.
This could be irritating to the user especially as the original bad value is (currently) not shown on the form. Should I display the original value in the form and remove the error message generated by Rails?
P.S. A date picker as helper to insert only valid dates is not an option as I have to consider dates far into the future which would require lots of clicking around in the date picker.
I m in a situation where i need to convert an Object to string so that i can check for Invalid characters/HTML in any filed of that object.
Here is my function for spam check
def seems_spam?(str)
flag = str.match(/<.*>/m) || str.match(/http/) || str.match(/href=/)
Rails.logger.info "** was spam #{flag}"
flag
end
This method use a string and look for wrong data but i don't know how to convert an object to string and pass to this method. I tried this
#request = Request
spam = seems_spam?(#request.to_s)
Please guide
Thanks
You could try #request.inspect
That will show fields that are publicly accessible
Edit: So are you trying to validate each field on the object?
If so, you could get a hash of field and value pairs and pass each one to your method.
#request.instance_values.each do |field, val|
if seems_spam? val
# handle spam
end
If you're asking about implementing a to_s method, Eugene has answered it.
You need to create "to_s" method inside your Object class, where you will cycle through all fields of the object and collecting them into one string.
It will look something like this:
def to_s
attributes.each_with_object("") do |attribute, result|
result << "#{attribute[1].to_s} "
end
end
attribute variable is an array with name of the field and value of the field - [id, 1]
Calling #object.to_s will result with a string like "100 555-2342 machete " which you can check for spam.
I have a MongoMapper model and am trying to convert a comma-delimited string into an Array to be stored.
The main problem is that a string such as tags = "first,second,third" is not getting converted to an array in the database like ["first","second","third"]. Instead it is going in as ["first,second,third"].
There are some other strange things going on as well:
1) In preen_tags I have to include the unless tags.nil? after every line
2) in preen_tags, using the debugger tags returns nil
Here is my model
class Template
include MongoMapper::Document
validate :validate_tags
after_validation :preen_tags
key :template_id, ObjectId
key :title, String
key :description, String
key :tags, Array
timestamps!
def validate_tags
errors.add_to_base "You Must Enter At Least 1 Tag." if tags.blank?
end
def preen_tags
#return if tags.nil? #why doesn't this work??
#only alphanumeric chars allowed, except hyphens and commas
tags = tags[0] if tags.is_a?(Array)
tags = tags.gsub(/[^0-9a-z\-\,]/i, '') unless tags.nil?
#convert spaces to hyphens
tags = tags.gsub(/\s/, '-') unless tags.nil?
tags = tags.split(",") unless tags.nil?
end
end
it's because by default tags is an Array in MongoMapper like you define it. So you can try tags.empty? instead of tags.nil?
In last case tags becomes nil because you try get first element of tags, but there are no one inside. Just nil. You tags becomes nil.
Looks like converting the String to an Array inside the controller before passing it to the Model has solved things.