I'm newbie and and wondering if its possible to validate the presence of an array name not nil. Actually on my model I have
validates :name, presence: true
this prevents that from a web form is not possible to send the name blank, but as as soon name[] is an string_array and nil is an string, when I try to send [nil,nil] from curl it succeeds.
I found this: rails validation of presence not failing on nil and this ActiveRecord validation for nil and read the api http://edgeguides.rubyonrails.org/active_record_validations.html but I didn't found the clue.
Does anyone can help?
Thanks in advance.
Edit: with validates :name, presence: true, allow_nil: false doens't work. If I send and invalid name it succeeds. Example:
curl -X POST -d 'patient[name[]]=["Mathew","de Dios]"&patient[email]=mat#gmail.com&patient[password]=123456&patient[password_confirmation]=123456&patient[sex]="female"&patient[doctor]=9' http://localhost:3000/api/patients.json
{**"success":true**,"data":{"active":null,"age":null,"created_at":"2013-08-15T11:19:03Z","dao":null,"day_active":null,"doctor_id":9,"email":"mat#gmail.com","geo_ini":null,"id":2124,"migraine_triggers":null,**"name":[null]**,"password_digest":"$2a$10$say8LiNmnazWL/EWKBKtL.fa5pJLKe4mo8Sn.HD6w2jeUrc5vmTe2","phone":null,"remember_token":"iX4Ohuj_Z6c2mDQZ_5e2vw","rich":null,"sex":"\"female\"","updated_at":"2013-08-15T11:19:03Z"},"status":"created"}
In the case that name is an array and that you want to check for nil elements, you can write a custom validation method. Here is an example :
validate :check_name_array_for_nil
def check_name_array_for_nil
self.name.split(",").each do |x|
if x.nil?
errors.add(:name, "nil in name array")
end
end
end
EDIT:
On second thought,this requires you to be storing the name as a string separated by commas.
I'm not sure but with a strict validation maybe?
In the rails guide
class Person < ActiveRecord::Base
validates :name, presence: { strict: true }
end
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
More info about the strict validation.
Related
I'm learning Rails and in going through the official guides, I came across some code which I could not really understand the meaning of.
Case 1 -
class Person < ApplicationRecord
validates :name, presence: true
end
It looks to me that validates is a method that takes a symbol called :name as an argument. But then, what is presence? Is it also a method? But if it is, what is the significance of the : right after presence. I understand that the value true is being set for presence, which serves as kind of a validation, requiring the presence of (in other words). But I'm not quite clear on the syntax.
It might also be possible that presence: true is just a hash, where :presence (the symbol) is the key, and true is the value.
Case 2 -
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true, message: 'must be abided'
end
Again, validates is the method that takes a symbol :terms_of_service as an argument. But what about the rest? Is it a hash with 2 key-value pairs, somewhat like {acceptance: true, message: 'must be abided'}?
And if it is indeed a hash, why is it tacked on to the validates method in each case? Why can't it be
validates :terms_of_service
acceptance: true, message: 'must be abided'
Thanks for the help!
That is the syntax for passing a hash to the method. What that is doing is the same thing as validates(:terms_of_service, {acceptance: true, message: 'must be abided'}). It's a common way of passing extra options to a method.
In Ruby there's a strong tradition for passing in options as a Hash as the last argument, strong enough that this tradition became new feature borrowed from Python: Keyword arguments.
In classic Ruby the method would be defined as this:
def validates(*args)
options = args.last.is_a?(Hash) ? args.pop : { }
# args is a list of fields
end
In Ruby 2.3 you can do this:
def validates(*args, **options)
# options is automatically any hash-style arguments if present
end
In Ruby 2.0+ you can also do this:
def validates(*args, acceptance: false, message: nil)
end
Where that defines options as first-class variables.
It's a common Ruby pattern, so it's good to understand what's going on here. Try writing your own methods that take options and you'll see how it plays out.
I'm trying to find out of it's possible to perform a method call that alters the information going into the database for some attributes, during validations. The desired workflow is: user submits a url, I validate it, if it matches the regex, then embedly is called. The embedly function gets the information for the title and image_url. I would like to perform validations on the title and image_url as well, but these don't exist until I've called the embedly method.
Is there a way to:
1. validate the link_url
2. call the embedly method
3. validate the resulting title and image_url attributes?
Any help is appreciated:
class ListLink < ActiveRecord::Base
belongs_to :list
default_scope -> {order('created_at DESC')}
#the REGEX urls are matched against
VALID_URL_REGEX = /\A(http:\/\/|https:\/\/|www|)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\z/i
validates :link_url, presence: true,
format:{with: VALID_URL_REGEX, message: "Please enter a valid url."}
validates :list_id, presence: true
#if is a valid url, ping embedly for more information on it
before_save :embedly
#is it possible to call just these 2 validations after the :embedly method?
validates :title, presence: true, length:{minimum: 4, maximum: 200}
validates :image_url, presence: true
private
def embedly
embedly_api = Embedly::API.new :key => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
:user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my#email.com)'
#duplicate the url for use in the embedly API
url = link_url.dup
obj = embedly_api.extract :url => url
#extract and save a title and image element to the database
self.title = obj[0].title
self.image_url = obj[0]["images"][0]["url"]
end
end
You could write a before_validation callback which checks your link_url and if it's valid (i.e. if it matches the URL), performs your embedly stuff. Then, during the regular validation, you can still add the error message to an invalid link_url. This could look something like this:
class ListLink < ActiveRecord::Base
before_validation :embedly_if_valid
# the REGEX urls are matched against
VALID_URL_REGEX = /\A(http:\/\/|https:\/\/|www|)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\z/i
validates :link_url, presence: true,
format:{with: VALID_URL_REGEX, message: "Please enter a valid url."}
validates :list_id, presence: true
validates :title, presence: true, length:{minimum: 4, maximum: 200}
validates :image_url, presence: true
private
def embedly_if_valid
embedly if self.link_url =~ VALID_URL_REGEX
end
def embedly
embedly_api = Embedly::API.new :key => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
:user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my#email.com)'
#duplicate the url for use in the embedly API
url = link_url.dup
obj = embedly_api.extract :url => url
#extract and save a title and image element to the database
self.title = obj[0].title
self.image_url = obj[0]["images"][0]["url"]
end
end
Note that you will probably get validation errors for the title and image_url fields if the link_url is not valid. You could probably code around this if it is not desired by setting the link_url to nil in a before_validation hook unless it is valid.
Instead of using the before_save callback, I suggest doing this in a custom setter for link_url, something along the lines of...
def link_url=(url)
if url =~ VALID_URL_REGEX
super
embedly
end
end
or, if you're not calling link_url = ... do this in a before_validation callback.
You won't be able to do it the way you mention here. You seem to have different validations depending on the state of the object.
Instead you could run the embedly before save and then validate everything. If you need some validation in order to use the embedly method, you could use a form object (example #3) to separate the different steps of your flow.
You could also use before_validation to run embedly, however it makes it that you need to duplicate the validation on the fields required by embedly (you need to test it in your method before validation, and then on the model in case you need to re-run the method later). You could get passed it with a custom validation method called explicitely, but I'm not a big fan of this.
I would re-think the design a little. Calling an external service like Embedly is best done as a background job as you never know how long it's going to take, and it could block other requests for your application.
You would have to allow the ListLink to be created without a title and image_url, though. Your background worker would then set these attributes when it runs.
You would still want to validate them at this point, of course, but this can be done using conditional validations. Something like this:
validates :title, presence: true, length:{minimum: 4, maximum: 200}, unless: :new_record?
I have a Rails 3.2.18 app where I'm trying to do some conditional validation on a model.
In the call model there are two fields :location_id (which is an association to a list of pre-defined locations) and :location_other (which is a text field where someone could type in a string or in this case an address).
What I want to be able to do is use validations when creating a call to where either the :location_id or :location_other is validated to be present.
I've read through the Rails validations guide and am a little confused. Was hoping someone could shed some light on how to do this easily with a conditional.
I believe this is what you're looking for:
class Call < ActiveRecord::Base
validate :location_id_or_other
def location_id_or_other
if location_id.blank? && location_other.blank?
errors.add(:location_other, 'needs to be present if location_id is not present')
end
end
end
location_id_or_other is a custom validation method that checks if location_id and location_other are blank. If they both are, then it adds a validation error. If the presence of location_id and location_other is an exclusive or, i.e. only one of the two can be present, not either, and not both, then you can make the following change to the if block in the method.
if location_id.blank? == location_other.blank?
errors.add(:location_other, "must be present if location_id isn't, but can't be present if location_id is")
end
Alternate Solution
class Call < ActiveRecord::Base
validates :location_id, presence: true, unless: :location_other
validates :location_other, presence: true, unless: :location_id
end
This solution (only) works if the presence of location_id and location_other is an exclusive or.
Check out the Rails Validation Guide for more information.
I'm having an issue stripping dollar signs and commas out of a currency before validations are run. For some reason the value is being set to 0 when saved.
class Property < ActiveRecord::Base
before_validation :format_currency
validates :street_name_1, presence: true
validates :city, presence: true
validates :state, presence: true
validates :postal_code, presence: true
validates :rate, presence: true, numericality: true
private
def format_currency
self.rate = rate.to_s.gsub(/[^0-9\.]/, '').to_i # Strip out non-numeric characters
end
end
When I do '$3,500'.to_s.gsub(/[^0-9\.]/, '').to_i in the rails console it correctly returns "3500". rate is being passed in to the model properly from the form as well.
For the life of me I can't figure out why it's not properly setting the value on save. Any help would be appreciated.
EDIT: I needed to override the default setter and do my gsub when the rate is set due to the database column being integer.
def rate=(rate)
write_attribute(:rate, rate.to_s.gsub(/[^0-9\.]/, '').to_i)
end
Since you are storing the rate column as an integer, and you are using Rails, I propose that your remove the format_currency method entirely. Rails has a method to deal with currencies already: number_to_currency.
When you want to output the price, simple use the helper:
number_to_currency #property.rate
This gives you greater flexibility too, since you can localise it and pass it a number of other options. Have a look at the API.
Edit, as per OP editing the question:
If you want to save something in that format, why don't you apply the dollar sine in the model?
before_save :set_rate
def set_rate
self.rate = "$#{rate}"
end
I'm learning rails with the book Agile Web development with Rails 4e. It uses the following so far as our product model (adapted from a scaffold):
class Product < ActiveRecord::Base
attr_accessible :description, :image_url, :price, :title
validates :description, :title, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)$}i,
message: 'Must be a valid URL for a gif, png, or jpg..'
}
end
I'm wondering why it tests first for the presence of :image_url, but then in the tertiary validation to make sure the image url is valid, it allows for blank responses which contradicts the first validation. I don't understand why this is supposed to work as is.
As an additional question, if the image_url is empty, how can I test if it is empty in my code? (e.g. in the product view to display a default image.)
Model validations are tested in isolation. A model is valid if and only if it passes validation for each validates statement independently.
It's probably bad-form, and evidently confusing for that allow_blank: true to be in the 4th validation, but that only applies to that single statement. The model must pass all validations to be considered valid, so the 1st statement merely imposes a tighter restriction than the 4th.
A final point, note that presence tests for non-nilness, whereas blank is defined as nil or the empty string. It is therefore possible to be both present and blank; e.g. image_url = ''. However, it remains the case that validations are tested separately in isolation.
I think maybe you are confused about the validation code? I'm a noob, and this is probably not entirely accurate: the validates keyword doesn't test for presence, it starts a block that you use to specify your validations.
As is, your code will validate the :image_url according to your specifications if it exists. If you took out allow_blank: true, then a nonexistent or blank :image_url would fail the validations.
I'm new to Rails as well and am using the same book. My understanding is that in order to stop the validation returning two errors immediately against validation (i.e. one if the field is blank and one if it doesn't have a correct file extension) it must allow_blank for the file format.
The best way I can explain it is to suggest removing the allow_blank: true code and trying to submit the description form again.
You should then see that you get both the validation errors for the field being blank and the file format being wrong.
The allow_blank therefore tells the validation to only error on the file format once the field is no longer blank.
Confused me as well which is why I ended up here!