最近ハマったのでメモです。
困ったこと
まずは以下のコードをご覧ください。
class HogeHogeService def self.call(*a, **k) new(*a, **k).tap { |s| s.call } end def initialize(a_code:, b_code:, c_code:) @a_code = a_code, @b_code = b_code, @c_code = c_code end attr_reader :a_code, :b_code, :c_code def call p a_code end end HogeHogeService.call(a_code: 'a', b_code: 'b', c_code: 'c') # => 期待する値: "a"なのに、なぜか["a", "b", "c"]になる
a_codeを参照したとき、"a"だけ出て欲しいのになぜか身に覚えのない配列が表示されて困りました...。
答え
class HogeHogeService def self.call(*a, **k) new(*a, **k).tap { |s| s.call } end def initialize(a_code:, b_code:, c_code:) @a_code = a_code # <=ここにカンマがついていたから! @b_code = b_code # <=ここにカンマがついていたから! @c_code = c_code end attr_reader :a_code, :b_code, :c_code def call p a_code end end # => "a" HogeHogeService.call(a_code: 'a', b_code: 'b', c_code: 'c')
Rubyって、こんな風にカンマをつけて変数を定義すると、配列として解釈してくれるんですね。
多重代入の1種だそうです。
docs.ruby-lang.org
a_code = 'a', 'b', 'c' # ["a", "b", "c"] p a_code
今回のコードでも同じパターンの多重代入が行われていました。
a_code = 'a', b_code = 'b', c_code = 'c' # ...!? b_codeとc_codeの出力どういうことなの... p a_code #=> ["a", "b", "c"] p b_code #=>"b" p c_code #=>"c"
会社の方に教えていただいたのですが、AST(抽象構文木)によると、代入式の左辺にカンマがなく右辺にカンマがある場合、右辺は配列として解釈されるそうです。
RubyのソースのASTを見る方法 - Qiita
全然読めません👼が、@ NODE_ARRAY (line: 1, code_range: (1,9)-(1,40)) って書いてありますね!
❯ ruby -e "a_code = 'a', b_code = 'b', c_code = 'c'" --dump=parsetree ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1, code_range: (1,0)-(1,40)) # +- nd_tbl: :a_code,:b_code,:c_code # +- nd_args: # | (null node) # +- nd_body: # @ NODE_PRELUDE (line: 1, code_range: (1,0)-(1,40)) # +- nd_head: # | (null node) # +- nd_body: # | @ NODE_DASGN_CURR (line: 1, code_range: (1,0)-(1,40)) # | +- nd_vid: :a_code # | +- nd_value: # | @ NODE_ARRAY (line: 1, code_range: (1,9)-(1,40)) # | +- nd_alen: 3 # | +- nd_head: # | | @ NODE_STR (line: 1, code_range: (1,9)-(1,12)) # | | +- nd_lit: "a" # | +- nd_head: # | | @ NODE_DASGN_CURR (line: 1, code_range: (1,14)-(1,26)) # | | +- nd_vid: :b_code # | | +- nd_value: # | | @ NODE_STR (line: 1, code_range: (1,23)-(1,26)) # | | +- nd_lit: "b" # | +- nd_head: # | | @ NODE_DASGN_CURR (line: 1, code_range: (1,28)-(1,40)) # | | +- nd_vid: :c_code # | | +- nd_value: # | | @ NODE_STR (line: 1, code_range: (1,37)-(1,40)) # | | +- nd_lit: "c" # | +- nd_next: # | (null node) # +- nd_compile_option: # +- coverage_enabled: false
上記は読めなかったので、rprという抽象構文木を画像にしてくれるgemを使ってみました。
hoge.rb(ファイル名は任意)に解析したいコードを書いて、同じディレクトリ上で以下を実行します。
sudo gem install rpr
rpr hoge.rb -f dot | dot -Tpng -oast.png
open ast.png
これなら読みやすいですね!ちゃんとarrayとして解釈してることもわかりました☺️