Вопрос

I've got a model that refers to a table with a thumbnail column. As for validation, I want the thumbnail to accept values of either empty/nil or a valid url (ideally jpg/gif). Below is my attempt but it doesn't seem to be validating as expected. Could someone please have a look at my model below and tell me where I'm going wrong, thanks

class SearchResult < ActiveRecord::Base

  validate :check_thumbnail

  private
    def check_thumbnail
      if self.thumbnail.nil?
        true
      else
        if /https?:\/\/[\S]+/.match(self.thumbnail).nil?
          false
        else
          false
        end
      end
    end
end
Это было полезно?

Решение

Using official guide for custom validations I would recommend to create url_validator.rb in your app/validators with the following content:

class UrlValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || 'must be a valid URL') unless url_valid?(value)
  end

  def url_valid?(url)
    uri = URI.parse(url) rescue false
    uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
  end
end

Thereafter you may validate thumbnail attribute like this:

class SearchResult < ActiveRecord::Base
  validates :thumbnail, url: true, allow_blank: true
end

Also you may improve url_valid? method to consider extension whitelist via regex (however I would create another validator for this, e.g. image_url_validator.rb to keep basic url validator).

EDIT:

app/validators/image_url_validator.rb

class ImageUrlValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || 'must be a valid URL (only jpg and gif)') unless url_valid?(value)
  end

  def url_valid?(url)
    uri = URI.parse(url) rescue false
    (uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)) && uri.to_s.match(/\.jpg|\.gif/)
  end
end

app/models/search_result.rb

class SearchResult < ActiveRecord::Base
  validates :thumbnail, image_url: true, allow_blank: true
end 

Key benefit of this approach is that you may reuse this custom validations for different attributes in different models.

Другие советы

I would rewrite it as

class SearchResult < ActiveRecord::Base

validate :check_thumbnail

private
  def check_thumbnail
    unless self.thumbnail.blank? || self.thumbnail =~ /https?:\/\/[\S]+/
      self.errors.add(:thumbnail, "is not valid")
    end
  end
end

Use this regex to validate URL, I've used this myself

/\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix

here in your code in the else block, do this way

  else
    URL_REGEX = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix
    if (URL_REGEX).match(self.thumbnail).nil?
      false
    else
      false
    end
  end
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top