英文:
Why does ruby class comparison fail in this case statement?
问题
在给定的rspec示例中,为什么when Question
不是真值?
require "rails_helper"
class Question
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass
when Question # this doesn't match 🤔???
tada = "🎁"
expect(tada).to eq("🎁")
else
# why?????
fail "into confusion"
end
end
end
而且,不,这不是一项课程作业,哈哈。我在工作中遇到了这个问题,现在我不知道什么是真的了。
另外,使用字符串比较可以工作:
require "rails_helper"
class Question
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass.to_s
when Question.to_s # this doesn't match 🤔???
tada = "🎁"
expect(tada).to eq("🎁")
else
# why?????
fail "into confusion"
end
end
end
另外,使用rspec是测试此意外行为的便捷方式。此问题与rspec无关。
英文:
Given the following rspec example, why is when Question
not truthy?
require "rails_helper"
class Question
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass
when Question # this doesn't match 🤔???
tada = "🎉"
expect(tada).to eq("🎉")
else
# why?????
fail "into confusion"
end
end
end
And no this isn't a course assignment lol. I ran into this at work and now I don't know what's true anymore.
side note, using string comparison works:
require "rails_helper"
class Question
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass.to_s
when Question.to_s # this doesn't match 🤔???
tada = "🎉"
expect(tada).to eq("🎉")
else
# why?????
fail "into confusion"
end
end
end
another aside: using rspec was a convenient way to test this unexpected behavior. this issue isn't related to rspec.
答案1
得分: 4
看起来你混淆了 类 和 实例。
通常,当你在 case
语句内的 when
子句中使用一个类时,你是在测试变量是否是该类的一个 实例:
obj = Question.new # 注意这里的 'new'
case obj
when Question
# obj 是 Question 的一个实例,因此会匹配。
puts "匹配 Question"
when SomethingElse
# SomethingElse 的实例会匹配这里。
puts "匹配 Something else"
end
# => 输出 "匹配 Question"
在你的示例中,你不是在测试 Question
的一个实例,而是在测试 Question
本身,它是一个类,因此也是 Class
的一个实例:
klass = Question # 没有 'new',只是 Question 本身。
case klass
when Question
# klass 不是 Question 的一个实例,因此不会匹配...
puts "匹配 Question"
when Class
# klass 实际上是 Question,它是 Class 的一个实例,
# 所以会匹配。
puts "匹配 Class"
end
# => 输出 "匹配 Class"。
这是因为 Module.===
的定义(Class
是 Module
的子类):
mod === obj → true or false
Case Equality—Returns
true
if obj is an instance ofmod
or an instance of one ofmod
’s descendants. Of limited use for modules, but can be used incase
statements to classify objects by class.
对于字符串(String
),===
是 ==
的别名,因此 when
子句会基于相等性进行匹配。
英文:
It looks like you are mixing up classes and instances.
Normally when you use a class in a when
clause inside a case
statement, you are testing whether the variable is an instance of that class:
obj = Question.new # Note the 'new' here
case obj
when Question
# obj is an instance of Question, so this will match.
puts "matches Question"
when SomethingElse
# instances of SomethingElse would match here.
puts "matches Something else"
end
# => Outputs "matches Question"
In your example, you aren’t testing an instance of Question
, but rather Question
itself, which is a class and therefore an instance of Class
:
klass = Question # No 'new', just Question itself.
case klass
when Question
# klass isn't an instance of Question, so this doesn't match...
puts "matches Question"
when Class
# klass actually is Question, which is an instance of Class,
# so this does match.
puts "matches Class"
end
# => Outputs "matches Class".
This works because of how Module.===
is defined (Class
is a subclass of Module
):
> mod === obj → true or false
>
> Case Equality—Returns true
if obj is an instance of mod
or an instance of one of mod
’s descendants. Of limited use for modules, but can be used in case
statements to classify objects by class.
With String
s, ===
is an alias of ==
, so when
clauses will match on equality.
答案2
得分: -1
更新:我最初发布的解决方案之所以有效,是因为进行了字符串比较,但是实际上是在实例上执行 ===
,而不是在类上执行。我去掉了.to_s
,将===
转换为 self.===
,现在下面的示例解决方案对我来说按照我期望的方式工作。
已更正的原始代码:
我刚刚了解到,如果我想比较两个类,它们都需要实现 self.===
。下面是一个有效的示例:
require "rails_helper"
class Question
def self.===(other)
other == self
end
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass
when Question
tada = "🎉"
expect(tada).to eq("🎉")
else
fail "fail? it'll never fail :)"
end
end
end
这是帮助我找到答案的文章链接:
https://thoughtbot.com/blog/case-equality-operator-in-ruby
英文:
Update: the original solution I posted worked because of the string comparison, but was implementing ===
on the instance and not on the class. I removed the .to_s
and converted ===
into self.===
and now the example solution below works for me they way I expect.
Corrected Original:
I just learned that if I want to compare two classes, they both will need to implement self.===
. Here's a working example:
require "rails_helper"
class Question
def self.===(other)
other == self
end
end
describe Question do
it "confuses me" do
klass = Question
# this passes
expect(klass).to eq(Question)
# this doesn't fail (thankfully)
fail "loudly" if klass != Question
case klass
when Question
tada = "🎉"
expect(tada).to eq("🎉")
else
fail "fail? it'll never fail :) "
end
end
end
And here's the article that helped me find that answer:
https://thoughtbot.com/blog/case-equality-operator-in-ruby
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论