-
Notifications
You must be signed in to change notification settings - Fork 1.7k
How to: Validate the URL for remote uploads
THIS PAGE IS A WORK IN PROGRESS
In Rails 3, you can add a custom validator class to validate that a URL is valid, both in terms of format (using a Regexp matcher) and that it resolves properly. The following example uses the code from https://gist.github.com/948880
First, create a new validator file and save it in lib/uri_validator.rb
require 'net/http'
# Thanks Ilya! http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/
# Original credits: http://blog.inquirylabs.com/2006/04/13/simple-uri-validation/
# HTTP Codes: http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html
class UriValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
raise(ArgumentError, "A regular expression must be supplied as the :format option of the options hash") unless options[:format].nil? or options[:format].is_a?(Regexp)
configuration = { :message => "is invalid or not responding", :format => URI::regexp(%w(http https)) }
configuration.update(options)
if value =~ configuration[:format]
begin # check header response
case Net::HTTP.get_response(URI.parse(value))
when Net::HTTPSuccess then true
else object.errors.add(attribute, configuration[:message]) and false
end
rescue # Recover on DNS failures..
object.errors.add(attribute, configuration[:message]) and false
end
else
object.errors.add(attribute, configuration[:message]) and false
end
end
end
Then add the following line to either the top of your model file (if you will only need it in that one model) or your environment.rb
file if you will need it in multiple models.
require 'uri_validator'
Finally, add the validator to your model.
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
attr_accessor :remote_avatar_url
# ...
validates :remote_avatar_url,
:uri => {
:format => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix,
:unless => remote_avatar_url.blank?
}
end
You can use the FakeWeb gem in your tests to temporarily message the response validation, for instance to ensure that that bit passes if you can't guarantee that the URL you are using in your test objects is valid.
# spec/spec_helper.rb
require 'FakeWeb'
# spec/models/user_spec.rb
describe User do
before do
uri = "http://no.good"
FakeWeb.register_uri(:get, uri, :body => "OK")
@user = User.create(:username => "Bob", :remote_avatar_url => uri)
end
end
Here is an example using factory_bot and the FFaker gem:
# spec/factories.rb
FactoryBot.define do
factory :user do |f|
f.username { Faker::Internet.user_name }
f.remote_avatar_url { Faker::Internet.http_url }
f.after_build { |f| FakeWeb.register_uri(:get, f.remote_avatar_url, :body => "OK") }
end
end