Ruby は深く触ってないので初学者マンです。
Rails の複数のクラスで共通して使われている Decorator 内で case 式でクラスを判定して返す値を変えていた部分で self.class.name
の文字列で判定するのではなく self.class
で判定すればいいよ。とアドバイスをもらったのですが上手く動作させられなかったのでメモ
case 式だと .class
だと上手く比較できない
class A end def class_type_check a p "a.class is #{a.class}" # => a.class is A" case a.class.name when 'A' then p "A" when 'B' then p "B" end # => "A" case a.class when A then p "A" when B then p "B" end # => end class_type_check_with_case A.new
class_type_check
に渡されるクラスオブジェクトa
の a.class
は A
と表示されます。
クラス名を文字列にした a.class.name
で比較する case
式では当然 when "A"
が該当となります。
しかし、a.class
で比較した case
式では a.class
が A
であるにも関わらず when A
の部分が該当となっていませんでした🤔
a.class
ではなくa
だと上手く動作しました。
class A end def class_type_check a case a when A then p "A" when B then p "B" end # => "A" end class_type_check_with_case A.new
なーぜー? 🤔🤔🤔
a.class
で判定する場合 a.class
はAクラスのインスタンスで A そのものではないからオブジェクトIDとかが違って同一オブジェクトではないとかなのでしょうか???
case 式は ===
で判定している
case は一つの式に対する一致判定による分岐を行います。when 節で指定された値と最初の式を評価した結果とを演算子
===
を用いて 比較して、一致する場合には when 節の本体を評価します。
つまり、case 式0 when 式1, 式2 stmt1 when 式3, 式4 stmt2 else stmt3 endは以下の if 式とほぼ等価です。tmp = 式0 if 式1 === tmp or 式2 === tmp stmt1 elsif 式3 === tmp or 式4 === _tmp stmt2 else stmt3 endref. 制御構造 (Ruby 2.6.0)
. (はてなブログ、引用の間に空白以外の何か挟まないと引用を連続して書けないのか…
case式は
===
演算子を使ったif〜elsif〜end式と等価な処理を行うように実装されている
基本的にこの===
演算子は、サブクラスで再定義されていない限り、単にObject#==
を呼び出すだけ
ref. Rubyのcase式と===演算子について - しばそんノート
Rubyの case 式は if whenの条件 === case の比較する値
と等価で、 ===
JavaScriptとかと違ってより厳密に型とかチェックする演算子ではなく、左辺のオブジェクトで定義されている ===
を呼び出すというものという事っぽい。
上の例を if文に置き換えてみるとこんな感じ
a = A.new p a.class # => A if A === a.class p "A" else p false end # => false
class クラスの ===
で判定する結果が返されるということっぽい。
class クラスの ===
は親クラスの Module クラスで定義されている。
self === obj -> bool
指定されたobj
がself
かそのサブクラスのインスタンスであるとき真を返します。 また、obj
がself
をインクルードしたクラスかそのサブクラスのインスタンスである場合にも 真を返します。上記のいずれでもない場合にfalse
を返します。
言い替えるとobj.kind_of?(self)
がtrue
の場合、true
を返します。
ref. class Module self === obj -> bool
a.class == A
なら true
だけど、 ===
の場合は a.class.kind_of?(A)
という判定になる。
a.class
のA
は Aクラスそのもの(self
)ではないし、a.class
のA
は Aのサブクラスのインスタンスでもないから false
ということっぽい。
なので case a
としている場合は
a = A.new if A === a p "A" else p false end # => "A"
a
は Aクラスのインスタンスだから true
ということになるっぽい。 (ちゃんと理解している訳ではない
case a.class
が当てはまる例
a.class
の A
は Class クラスのオブジェクトなので、case a.class
は Class クラスより親のクラスであれば true
になる 。
class A end a = A.new # XXX === a.class は a.class.kind_of?(XXX) と等価なので a.class.kind_of?(Class) # => true a.class.kind_of?(Module) # => true a.class.kind_of?(Object) # => true
👇
def class_type_check a case n.class when A then p "A" when B then p "B" when String then p "String" when Class then p "Class" when Module then p "Module" end # => "Class" end class_type_check A.new
完 全 理 解 !
ポエム
JavaScriptの不思議な挙動試してみるのとか好きだったし、こういう言語の挙動みたいな部分面白いからまたちゃんとRuby再入門しようかな〜って気持ちになってきた。(不思議な挙動を抑制されるTypeScriptを枷のように感じてるし、今の所まだ Rails の良さを感じられるに至ってい。disるつもりはなくってnot for meって感じなのだ。こんなんだから僕は稼げるエンジニアにはなれないのだろう…
[参考]
- ruby - caseで変数のクラスを比較しようとすると上手く行かない - スタック・オーバーフロー
- class Class (Ruby 2.6.0)
- class Module (Ruby 2.6.0)
- Rubyのcase式と===演算子について - しばそんノート
- 制御構造 (Ruby 2.6.0)
- 作者: Rubyサポーターズ
- 出版社/メーカー: 技術評論社
- 発売日: 2017/05/17
- メディア: 大型本
- この商品を含むブログ (1件) を見る