ActiveRecord validate url if it is present

10,609

Solution 1

Separate your two validators.

validates :url, presence: true
validates :url, format: { with: URI.regexp }, if: Proc.new { |a| a.url.present? }

(almost) 2 year anniversary edit

As vrybas and Barry state, the Proc is unnecessary. You can write your validators like this:

validates :url, presence: true
validates :url, format: { with: URI.regexp }, if: 'url.present?'

Solution 2

Separate the validators as in Yanis's answer, but you don't need a Proc for this.

You can use the common validation options to bypass the format validation if the value is nil by setting the allow_nil parameter.

Alternatively, setting the allow_blank parameter would also work if the value is the empty string '', which may be more useful if you're setting url from an form input.

The complete validator could look like this:

validates :url, presence: true
validates :url, format: { with: URI.regexp }, allow_blank: true
Share:
10,609
craig
Author by

craig

Certified, Epic-Clarity consultant. Open-source projects: PsCrystal - PowerShell wrapper of the Crystal Reports SDK PsEnterprise - PowerShell wrapper of BusinessObjects Enterprise SDK AppleScript email merge - Email merge using Outlook or Mail/Address Book source-highlight-crystal - GNU source-highlight language for Crystal Reports' formula language crystal-reports-tmbundle - A TextMate bundle for the Crystal Report's forumula language

Updated on June 22, 2022

Comments

  • craig
    craig almost 2 years

    I would like to ensure that my class's url property has a value and if it does, it is valid:

    class Entity < ActiveRecord::Base
    
      validates :name, presence: true
      validates :url, presence: true, :format => {:with => URI.regexp}
    
    end
    

    In the rails console:

    > e = Entity.new(name: 'foo')
    => #<Entity id: nil, name: "foo", url: nil, created_at: nil, updated_at: nil> 
    

    Which results in two errors for the url attribute:

    > e.valid?
    => false
    
    > e.errors
    => #<ActiveModel::Errors:0x007fed9e324e28 @base=#<Entity id: nil, name: "foo", url: nil, created_at: nil, updated_at: nil>, @messages={:url=>["can't be blank", "is invalid"]}> 
    

    Ideally, a nil url would produce a single error (i.e. can't be blank).

    As such, I've change the validates rule:

    validates :url, presence: true, :with => Proc.new { URI.regexp if :url? }
    

    I can't get the syntax to work, however. What am I missing?

  • craig
    craig almost 10 years
    Is there a way to add the error that results from an invalid URL to the objects errors[] array? Currently, entity.url = 'foo bar' raises an error (URI::InvalidURIError: bad URI(is not URI?): foo foo, rather than adding it to the collection.
  • vrybas
    vrybas about 8 years
    Without using Proc: validates :url, format: { with: URI.regexp }, if: 'url.present?'
  • XanderStrike
    XanderStrike over 7 years
    URI.regexp is not a good validation of URLs. Consider that test cases like a:// and extra text http://google.com extra text pass this validation.
  • Alexander Popov
    Alexander Popov almost 4 years
    Also without a proc: if: :url