开发者

NoMethodError: undefined method `RuntimeError'

开发者 https://www.devze.com 2023-02-28 07:04 出处:网络
I wrote some code in a Ruby library (incidentally used in Rails) that raised a RuntimeError somewhat like below:

I wrote some code in a Ruby library (incidentally used in Rails) that raised a RuntimeError somewhat like below:

class MyClass
  def initialize(opts = {})
     # do stuff
     thing = opts[:thing]
     raise Ru开发者_如何学编程ntimeError "must have a thing!" unless thing.present? && thing.is_a?(Thing)
     # more stuff
  end
end

and when I ran my fresh new rspec spec over it, which looks somewhat like:

it "should raise an error if we don't pass a thing" do
  lambda {
    my_class = MyClass.new(:thing => nil)
  }.should raise_exception(RuntimeError)
end

I kept getting something weird:

expected RuntimeError, got 
#<NoMethodError: undefined method `RuntimeError' for #<MyClass:0xb5acbf9c>>


You may already have spotted the problem... ah, single-character bugs, doncha love em?

Here it is.

WRONG:

 raise RuntimeError "must have a thing!" unless thing.present? && thing.is_a?(Thing)

RIGHT:

 raise RuntimeError, "must have a thing!" unless thing.present? && thing.is_a?(Thing)

of course, you can also just go ahead and leave out the RuntimeError entirely:

 raise "must have a thing!" unless thing.present? && thing.is_a?(Thing)

because it's the default anyways...


You are missing a comma:

raise RuntimeError, "must have a thing!" unless thing.present? && thing.is_a?(Thing)
                  ^


Just to add a little bit more explanation: in Ruby, there is an ambiguity between variable references and message sends.

foo
Foo

Could either mean "dereference the variable named foo (or Foo)" or "send the message :foo (or :Foo) with an empty argument list to the default receiver".

This ambiguity is resolved as follows:

  1. If foo starts with a lower-case letter, it is assumed to be a message send, unless the parser has seen an assignment to foo before, in which case it is treated as a variable dereference. (Note that the assignment only needs to parsed, not executed; if false then foo = nil end is perfectly fine.)
  2. If Foo starts with an uppercase letter, it is treated as a variable (or rather constant) dereference, unless you pass an argument list (even an empty one), in which case it is treated as a message send.

In this case, RuntimeError is treated as a message send, because it has an argument list: "must have a thing!". This is, of course, because of another of Ruby's peculiarities, namely that it allows you to leave out the parentheses around an argument list, as long it is unambiguous.

IOW: the whole thing is interpreted roughly as

self.RuntimeError("must have a thing!")
0

精彩评论

暂无评论...
验证码 换一张
取 消