#はじめに
たまに下記のようなコードを見かけることがあります。
attr_accessor(*Hoge::ATTRIBUTES)
同僚のSさんと話している時に、この書き方でなぜ動くんだろうという話になり、調べてみたら面白かったのでまとめておこうと思います。
#先に結論から
・メソッド呼び出しをする時に引数に*
をつけると配列を展開することができる
・attr_accessor
は複数個のSymbolを受け取ることができる
・attr_accessor(*Hoge::ATTRIBUTES)
とした時に、配列が展開されて複数個のSymbolが渡される
#よく見る使われ方
よく見るのは、あるクラスで属性の定数配列を定義して複数のクラスで使い回すというものだと思います。
・・。
文章だと何を言ってるか分かりませんね・・
実際に見た方が早いと思うので、早速コードを見てみましょう。
class Hoge
ATTRIBUTES = %i[
name
age
].freeze
attr_accessor(*Hoge::ATTRIBUTES)
end
class Huga
attr_accessor(*Hoge::ATTRIBUTES)
end
この例の場合は、Hogeで属性の定数配列を定義してあげて、Hugaでも使いまわしています。
これにより、重複したコードを減らすことができます。便利!!
#仕組みについて
ここまでで、なんとなく使い方は分かっていただけたと思います。
ですが、なぜこれで動くかは謎のままです・・
実際にどのような仕組みになっているかを確かめるためにいろいろ試してみました。
##引数で渡す時に*
をつけないでみる
class Hoge
ATTRIBUTES = %i[
name
age
].freeze
attr_accessor(*Hoge::ATTRIBUTES)
end
class Huga
# Hogeの前の`*`を消してます
attr_accessor(Hoge::ATTRIBUTES) # == attr_accessor([:name, :age])
end
上記を実行してみると以下のようなエラーが表示されました。
TypeError: [:name, :age] is not a symbol nor a string
エラーメッセージを見ると、symbol
かstring
しか受け付けないよと言われてるみたいです。
リファレンスをみると、
[PARAM] name: String または Symbol を 1 つ以上指定します。
とあり、配列は渡すことができないためエラーになっていたようです。
ここで、重要なのはString または Symbol を 1 つ以上指定の部分です。
##そもそも*
をつけたときの挙動って?
なんとなく、引数に*をつけるかつけないかで挙動が変わるというところまでは分かってきました。具体的にどう変わるのかを調べていると、こちらの記事を見つけ、*をつけることで配列を展開して渡すことができるということが分かりました。
実際に試してみます。
pry(main)> p([1,2])
[1, 2]
pry(main)> p(*[1,2])
1
2
確かに*
をつけることで、配列が展開されて渡されてるようです。
##attr_accessor(*Hoge::ATTRIBUTES)の正体
ここまでで、既にお気づきの方もいるかもしれませんが、
attr_accessor(*Hoge::ATTRIBUTES)
の部分は以下のようになります。
class Hoge
ATTRIBUTES = %i[
name
age
].freeze
attr_accessor(*Hoge::ATTRIBUTES) # == attr_accessor(:name, :age)
end
class Huga
attr_accessor(*Hoge::ATTRIBUTES) # == attr_accessor(:name, :age)
end
つまり、*
をつけることで配列が展開され、attr_accessor
には複数個のSymbolが渡されていました。
先ほども触れましたが、リファレンスには
String または Symbol を 1 つ以上指定
とあり、複数個のSymbolを指定することが可能なので、問題なく設定できていたようです。
#終わりに
Rubyは知れば知るほど新しい発見があって、面白い言語だなーと改めて思いました!
#参考記事