rails使用URI.parse验证url会导致意外结果

mar*_*rgo 1 ruby ruby-on-rails

我正在按照此解决方案验证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.nil? && uri.kind_of?(URI::HTTP)
    rescue URI::InvalidURIError
      false
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

测试

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
Run Code Online (Sandbox Code Playgroud)

Rob*_*bel 5

你的问题实际上是一个Ruby问题,在这一行:

uri = URI.parse(uri)
Run Code Online (Sandbox Code Playgroud)

想要发生的是uri重新定义为解析版本uri.什么是实际发生的是,红宝石是看到"哦,你定义一个新的局部变量uri"; 新的局部变量现在优先于uri您使用attr_accessor定义的方法.由于某种原因,Ruby在局部变量赋值期间评估自引用nil.这是阴影的一个例子.

因此,上面的语句会导致URI.parse始终执行值nil,这就是无论您将URI设置为什么,测试都会失败的原因.要修复它,只需使用不同的变量名称:

parsed_uri = URI.parse(uri)
Run Code Online (Sandbox Code Playgroud)

附录:证明我没有做到这一点的简短例子

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
Run Code Online (Sandbox Code Playgroud)

第一个语句失败,因为它只引用了一个不存在的本地var.第二个语句成功,因为Ruby zzas 的赋值中计算变量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
Run Code Online (Sandbox Code Playgroud)

这是你遇到的同样问题; 只需用a puts来检查在线值.它打印nil.