Preface, justification and how it should be done
当有人在“打字”前在“打字<>/代码>上改变模型时,我对此表示仇恨。 几天后,出于某种原因,模型必须保持(有效日期:虚假),但有些过滤器却总是在指定领域运行,因此无法运行。 当然,数据无效通常是你想要避免的,但如果使用这种数据,则不需要这种选择。 还有一个问题是,每当你要求一个模型时,这些修改也是有效的。 简单地询问某一模式是否有效,可能会导致模型得到修改,这一事实只是意想不到的,甚至可能不希望。 如果我不得不选择一个标题I(d)至,然后再选择<0>/code>。 然而,由于我们为我们的模式提供预议意见,而这将打破世界投资倡议在预审中的地位,因为 h永远不会被叫。 因此,我决定最好把这个概念分离到单元或关切中,并为人们适用“钥匙拼凑”,确保改变田地价值时总是通过过滤器进行,如果缺少一个缺省规程,则会增加违约规程。
The module
#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
In your model
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"
As a concern
If for some reason, you d rather use the Rails Concern pattern it is easy to convert the above module to a concern module (it is used in an exactly similar way, except you use include Concerns::URIField
:
#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
P.S. The above approaches were tested with Rails 3 and Mongoid 2.
P.P.S If you find this method redefinition and aliasing too magical you could opt not to override the method, but rather use the virtual field pattern, much like password (virtual, mass assignable) and encrypted_password (gets persisted, non mass assignable) and use a sanitize_url (virtual, mass assignable) and url (gets persisted, non mass assignable).