Rails: Validating URLs

Here's a quick-and-dirty way to validate URLs in your model. Updated: Only allow certain schemes.
require 'uri'

class Film < ActiveRecord::Base

VALID_URI_SCHEMES = ['http', 'https']

validates_presence_of :url
validate :url_must_be_valid

protected

def url_must_be_valid
parsed = URI.parse(url)
if !VALID_URI_SCHEMES.member?(parsed.scheme)
raise URI::InvalidURIError
end
rescue URI::InvalidURIError => e
errors.add(:url, 'is not a valid URL')
end
end

Comments

Matt Good said…
I've used Python's urlparse similarly, though you typically need to also check the parsed result. Lots of things will parse as URLs, but may not include stuff you'd want like a hostname, or scheme.
Good point. I don't want them entering crap like file:///etc/passwd. I'll update it.
Just for posterity, here are the tests I wrote:

require 'test_helper'

class MyModelTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end

test "url_must_be_valid catches bad urls" do
["*://", "ftp://example.com/foo.jpg"].each do |url|
my_model = MyModel.new
my_model.name = "name"
my_model.url = url
assert !my_model.valid?, url
end
end

test "url_must_be_valid accepts good urls" do
my_model = MyModel.new
my_model.name = "name"
my_model.url = "http://example.com/foo.jpg"
assert my_model.valid?, my_model.errors.full_messages
end
end
sensei said…
This comment has been removed by the author.
PaweĊ‚ Pacana said…
Well, I do this that way:

URL_FORMAT = URI::regexp(%w(http https))
validates_format_of :url, :with => URL_FORMAT
Nice ;) Thanks!