Ruby の初心者向け記事の定番間違いの一つに,attr_accessor
のことを「アクセッサー」と呼ぶ,というものがあります。
単に言葉の表現が間違っているだけの場合と,そもそも正しく理解していない場合とがあるように思われます。
Ruby の場合,アクセッサーというのは,
- インスタンス変数の値を読み出すためのメソッド(ゲッターと呼ぶ)
- インスタンス変数に値をセットするメソッド(セッターと呼ぶ)
の総称です。
以下の典型的な例を見ましょう
class Person
def name
@name
end
def name=(name)
@name = name
end
end
a_person = Person.new
a_person.name = "Kaoru"
puts a_person.name # => "Kaoru"
二つのメソッドを定義していますが,name
がゲッターで,name=
がセッターです。この二つを合わせてアクセッサーと呼ぶわけです。
慣習として,
- ゲッターのメソッド名はインスタンス変数名から
@
を除いたもの - セッターのメソッド名はインスタンス変数名から
@
を除いたものの末尾に=
を付加したもの
とします(ルールではなく慣習です)。
名前の末尾が =
であるようなメソッドは「代入メソッド」と呼ばれ,Ruby の構文上,ちょっぴり特別扱いされます。
上の例で
a_person.name = "Kaoru"
と書いたように,メソッド呼び出しの際に =
の前にスペースを入れても構わないのです。
上のコードは代入式の一種です1。
それだけではありません。代入メソッドを使った自己代入式も書けます。
以下のコードを見ましょう(クラス定義は先ほどのとおりだとします)。
a_person = Person.new
# (1)
a_person.name ||= "Kaoru"
puts a_person.name # => "Kaoru"
# (2)
a_person.name ||= "Haruka"
puts a_person.name # => "Kaoru"
(1) では,インスタンス変数 @name
にまだ値がセットされていない(読み出すと nil
になる)状態で自己代入演算子 ||=
を使っているので,@name
の値が "Kaoru"
になります。
しかし,(2) では,@name
に文字列オブジェクトが代入された状態で実行されるので代入は起こりません。そのため,a_person.name
の返り値は "Haruka"
ではなく "Kaoru"
になっています。
前ふりが長くなりました。ここから本題に入りましょう。
先ほどのクラス定義を見ると,ゲッター,セッターの定義で(単純とはいえ)それぞれ 3 行も費やしています。
インスタンス変数が 5 個あって,それぞれゲッターもセッターも定義するとなると,30 行も必要になります。これは馬鹿馬鹿しいですね。
そこで,このような単純なゲッター,セッターを定義するなら2
class Person
attr_accessor :name
end
と書けば済むようになっています。
この attr_accessor
のことを「アクセッサー」と書いている記事をよく目にします。
しかし,attr_accessor
はアクセッサーを定義してくれるものであって,これ自体がアクセッサーなのではありません。
おそらく名前の一部に「accessor」と付いているのに引きずられて「アクセッサー」と思い込んでしまうのでしょう。
ところで,attr_accessor
というのは一体何者でしょうか?
他言語から来た方は,Ruby のクラス定義構文の一部分と思うかもしれませんが,そんな文法はありません。
attr_accessor
はただのメソッドです。
参考:Module#attr_accessor (Ruby 3.0.0 リファレンスマニュアル)
つまり,
attr_accessor :name
は,:name
というシンボルを引数として,attr_accessor
というメソッドを呼び出しているだけなのです。
こうすることで,クラスにアクセッサーが定義されます。
attr_accessor
はメソッドを定義するメソッドなわけです。
少し補足すると
- クラスもまたオブジェクトである
- クラス定義式(
class Person
〜end
)の中では,そのクラスがself
となる - つまり,クラス定義式内でレシーバーをあらわに書かないメソッド呼び出しを行うと,当該のクラスがレシーバーとなる
ということです。
最後の点を確認するコードを以下に示します。
class Hoge
p name # => "Hoge"
end
このコードで,name
はローカル変数ではありません。この箇所以前に name
への代入式が存在しないので,ローカル変数ではありえないのです。
とすると,メソッド呼び出しのはずです。
このメソッド呼び出しにはレシーバーが書かれていません。
ここはクラス定義式の中ですから,name
のレシーバーは Hoge
クラスです。
Hoge
は Class
というクラスのインスタンスですから,name
はクラス名・モジュール名を返すメソッドです。だから "Hoge"
が表示されます。
参考:Module#inspect (Ruby 3.0.0 リファレンスマニュアル)
以上が理解できると,
class Person
attr_accessor :name
end
は
class Person
end
Person.attr_accessor(:name)
と書くのと同じであることが分かるでしょう。