我跟随此solution来验证rails应用中服务类中的网址。测试返回false,但在控制台中,当我使用Ruby的URI类解析给定的URL时,不会引发错误。我不认为我的代码中有任何拼写错误 - 我出错的任何建议?
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
测试
require "rails_helper"
RSpec.describe UrlService do
subject {described_class.new(url)}
describe "#valid?" do
context "success" do
let(:url) { "https://www.google.com/" }
it "returns true" do
expect(subject.valid?).to be_truthy
end
end
end
end
答案 0 :(得分:5)
你的问题实际上是一个Ruby问题,在这一行:
uri = URI.parse(uri)
您希望发生的事情是将uri
重新定义为uri
的解析版本。 实际正在发生的事情是Ruby正在看到"哦,你正在定义一个新的局部变量uri
&#34 ;;新的局部变量现在优先于您使用attr_accessor定义的方法uri
。由于某种原因,Ruby在局部变量赋值期间将自引用评估为nil
。这是shadowing的一个例子。
因此,上面的语句会导致URI.parse
始终在值nil
上执行,这就是无论您将URI设置为什么,测试都会失败的原因。要修复它,只需使用不同的变量名称:
parsed_uri = URI.parse(uri)
irb(main):016:0> z
NameError: undefined local variable or method `z' for main:Object
from (irb):16
from /Users/rnubel/.rubies/ruby-2.3.3/bin/irb:11:in `<main>'
irb(main):017:0> z = z
=> nil
第一个语句失败,因为它只引用了一个不存在的局部变量。第二个语句成功,因为Ruby将z
的{{1}}变量评估为z
。
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
即可检查内联值。它会打印puts
。
答案 1 :(得分:-1)
如果预期用途是验证模型,您可能希望将其编写为自定义验证程序而不是服务。
# 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
然后您可以通过以下方式使用它:
validates :some_attribute, presence: true, url: true
测试它的最直接的方法是通过使用验证器的模型类:
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