我正在模型中使用此正则表达式来验证用户提交的URL。我不想强迫用户键入http部分,但如果它不存在,我想自己添加它。
validates :url, :format => { :with => /^((http|https):\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+).[a-z]{2,5}(:[0-9]{1,5})?(\/.)?$/ix, :message => " is not valid" }
任何想法我该怎么做?我对验证和正则表达式的经验很少。
如果不存在,请使用前置过滤器添加:
before_validation :smart_add_url_protocol
protected
def smart_add_url_protocol
unless self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
self.url = "http://#{self.url}"
end
end
保留验证信息,这样,如果他们输入错误,他们就可以纠正协议。
不要使用正则表达式,请使用URI.parse
将其拆开,然后查看URL上是否存在方案:
URI.parse
使用URI库还可以很容易地清理可能有人试图将其放入URL的任何无用的废话(例如userinfo)。
接受的答案很好。但是,如果字段(url)是可选的,则可能会引发错误,例如u = URI.parse('/pancakes')
if(!u.scheme)
# prepend http:// and try again
elsif(%w{http https}.include?(u.scheme))
# you're okay
else
# you've been give some other kind of
# URL and might want to complain about it
end
类的undefined method
+。以下应解决该问题:
nil
根据mu的回答,这是我在模型中使用的代码。它在保存:link而不需要模型过滤器时运行。需要Super才能调用默认的保存方法。
def smart_add_url_protocol
if self.url && !url_protocol_present?
self.url = "http://#{self.url}"
end
end
def url_protocol_present?
self.url[/\Ahttp:\/\//] || self.url[/\Ahttps:\/\//]
end
[当人们在def link=(_link)
u=URI.parse(_link)
if (!u.scheme)
link = "http://" + _link
else
link = _link
end
super(link)
end
挂钩中更改型号时,我讨厌它。然后,有一天,由于某种原因,需要使用save(validate:false)将模型持久化,那么某些假定一直在分配的字段上运行的过滤器将无法运行。当然,通常要避免拥有无效数据,但是如果不使用该选项,则无需如此。另一个问题是,每次您从模型询问是否有效,这些修改也会发生。简单地询问模型是否有效可能导致模型被修改的事实只是意料之外的,甚至是不希望的。如果我必须选择一个钩子,那就去before_validation
钩子。但是,这对我来说不会这样做,因为我们为模型提供了预览视图,并且由于永远不会调用该挂钩,因此这会破坏预览视图中的URI。为此,我认为最好将概念分为模块或关注点,并为用户提供一种“猴子补丁”的好方法,以确保更改字段值始终通过添加默认协议的过滤器运行(如果存在)缺少。
before_save
#app/models/helpers/uri_field.rb
module Helpers::URIField
def ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
alias_method "original_#{field}=", "#{field}="
define_method "#{field}=" do |new_uri|
if "#{field}_changed?"
if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
new_uri = "#{default_protocol}://#{new_uri}"
end
self.send("original_#{field}=", new_uri)
end
end
end
end
[如果出于某种原因,您宁愿使用Rails Concern模式,可以很容易地将上述模块转换为关注模块(使用方式与之完全相同,只不过您使用了extend Helpers::URIField
ensure_valid_protocol_in_uri :url
#Should you wish to default to https or support other protocols e.g. ftp, it is
#easy to extend this solution to cover those cases as well
#e.g. with something like this
#ensure_valid_protocol_in_uri :url, "https", "https?|ftp"
:
include Concerns::URIField
P.S。以上方法已通过Rails 3和Mongoid 2进行了测试。PPS如果您发现此方法的重新定义和别名太神奇了,您可以选择不覆盖该方法,而是使用虚拟字段模式,非常类似于密码(虚拟,可批量分配)和encrypted_password(永久保留,不可批量分配)并使用sanitize_url(虚拟的,可批量分配)和url(永久保存,不可大规模分配)。
使用某些上述正则表达式,这是一种方便的方法,用于覆盖模型上的默认url(例如,如果您的ActiveRecord模型具有'url'列)
#app/models/concerns/uri_field.rb
module Concerns::URIField
extend ActiveSupport::Concern
included do
def self.ensure_valid_protocol_in_uri(field, default_protocol = "http", protocols_matcher="https?")
alias_method "original_#{field}=", "#{field}="
define_method "#{field}=" do |new_uri|
if "#{field}_changed?"
if new_uri.present? and not new_uri =~ /^#{protocols_matcher}:\/\//
new_uri = "#{default_protocol}://#{new_uri}"
end
self.send("original_#{field}=", new_uri)
end
end
end
end
end
我不会在验证中尝试这样做,因为它实际上不是验证的一部分。
让验证(可选)进行检查;如果他们搞砸了,那将是一个验证错误,这很好。
如果还没有协议,请考虑使用回调(def url
_url = read_attribute(:url).try(:downcase)
if(_url.present?)
unless _url[/\Ahttp:\/\//] || _url[/\Ahttps:\/\//]
_url = "http://#{_url}"
end
end
_url
end
,after_create
等)在协议之前添加前缀。
((我对其他答案投了赞成票;我认为它们都比我的要好。但这是另一个选择:)