Ruby on rails rails正在使用URI.parse验证url,并引发意外结果
下面我将验证rails应用程序中服务类中的url。测试返回false,但在控制台中,当我使用Ruby的URI类解析给定的url时,不会出现错误。我不认为我的代码中有任何拼写错误-有任何建议我哪里出错了吗Ruby on rails rails正在使用URI.parse验证url,并引发意外结果,ruby-on-rails,ruby,Ruby On Rails,Ruby,下面我将验证rails应用程序中服务类中的url。测试返回false,但在控制台中,当我使用Ruby的URI类解析给定的url时,不会出现错误。我不认为我的代码中有任何拼写错误-有任何建议我哪里出错了吗 class UrlService attr_accessor :uri def initialize(uri) @uri = uri end def valid? begin uri = URI.parse(uri) !uri.host
class UrlService
attr_accessor :uri
def initialize(uri)
@uri = uri
end
def valid?
begin
uri = URI.parse(uri)
!uri.host.nil? && uri.kind_of?(URI::HTTP)
rescue URI::InvalidURIError
false
end
end
end
试验
您的问题实际上是Ruby gotcha,在这一行:
uri = URI.parse(uri)
您希望将uri
重新定义为uri
的解析版本。实际上,Ruby看到“哦,您正在定义一个新的局部变量uri
”;新的局部变量现在优先于使用attr\u访问器定义的方法uri
。出于某种原因,Ruby在局部变量赋值期间将自引用计算为nil
。这是一个例子
因此,上面的语句会导致URI.parse
始终在nil
的值上执行,这就是无论您将该URI设置为什么,测试都会失败的原因。要解决此问题,只需使用不同的变量名:
parsed_uri = URI.parse(uri)
附录:证明我不是在编造的简短例子
这和你遇到的问题是一样的;只需一个
按钮,即可将插入检查值。它打印出nil
您的问题实际上是Ruby gotcha,在这一行:
uri = URI.parse(uri)
irb(main):011:0> class Foo; def bar; "http://www.google.com"; end; end
=> :bar
irb(main):012:0> Foo.new.instance_exec { bar }
=> "http://www.google.com"
irb(main):013:0> Foo.new.instance_exec { bar = (puts bar.inspect) }
nil
=> nil
您希望将uri
重新定义为uri
的解析版本。实际上,Ruby看到“哦,您正在定义一个新的局部变量uri
”;新的局部变量现在优先于使用attr\u访问器定义的方法uri
。出于某种原因,Ruby在局部变量赋值期间将自引用计算为nil
。这是一个例子
因此,上面的语句会导致URI.parse
始终在nil
的值上执行,这就是无论您将该URI设置为什么,测试都会失败的原因。要解决此问题,只需使用不同的变量名:
parsed_uri = URI.parse(uri)
附录:证明我不是在编造的简短例子
这和你遇到的问题是一样的;只需一个按钮,即可将插入检查值。它打印nil
如果预期的用途是验证模型,您可能希望将其作为自定义验证器而不是服务来编写
irb(main):011:0> class Foo; def bar; "http://www.google.com"; end; end
=> :bar
irb(main):012:0> Foo.new.instance_exec { bar }
=> "http://www.google.com"
irb(main):013:0> Foo.new.instance_exec { bar = (puts bar.inspect) }
nil
=> nil
# lib/validators/uri_validator.rb
class UriValidator < ActiveModel::Validator
def validate_each(record, attribute, value)
begin
uri = URI.parse(value)
rescue URI::InvalidURIError
end
if uri && uri.host && uri.kind_of?(URI::HTTP)
record.errors.add attribute,
end
end
end
最直接的测试方法是通过使用验证器的模型类:
require "rails_helper"
RSpec.describe Thing, type: :model do
describe "validations" do
describe "url" do
it "does not allow an invalid URL" do
thing = Thing.new(url: 'foo')
thing.valid
expect(thing.errors).to have_key :url
end
it "allows a valid URL" do
thing = Thing.new(url: 'https://www.google.com/')
thing.valid
expect(thing.errors).to have_key :url
end
end
end
end
如果预期用途是验证模型,您可能希望将其作为自定义验证器而不是服务来编写
# lib/validators/uri_validator.rb
class UriValidator < ActiveModel::Validator
def validate_each(record, attribute, value)
begin
uri = URI.parse(value)
rescue URI::InvalidURIError
end
if uri && uri.host && uri.kind_of?(URI::HTTP)
record.errors.add attribute,
end
end
end
最直接的测试方法是通过使用验证器的模型类:
require "rails_helper"
RSpec.describe Thing, type: :model do
describe "validations" do
describe "url" do
it "does not allow an invalid URL" do
thing = Thing.new(url: 'foo')
thing.valid
expect(thing.errors).to have_key :url
end
it "allows a valid URL" do
thing = Thing.new(url: 'https://www.google.com/')
thing.valid
expect(thing.errors).to have_key :url
end
end
end
end
顺便说一句,如果你打算在模型中使用它,那么把它写成一个而不是一个服务是有意义的。顺便说一句,如果你打算在模型中使用它,那么把它写成一个而不是一个服务是有意义的。我想是这样的。谢谢你的解释。我以为是这样的。谢谢你的解释。谢谢你的评论。我在最初发布的链接中看到了这个选项。我的类不是ActiveRecord或ActiveModel类。谢谢您的评论。我在最初发布的链接中看到了这个选项。我的类不是ActiveRecord或ActiveModel类。