Rubyの基礎文法などを見ているとちょくちょく登場するattr_accessorというメソッド。
Ruby on Railsから入った自分は、最初見たときには全く使用用途が分かりませんでした。
そこからいろいろ調べてみるも
- セッターとゲッターの両方を担うメソッド
- インスタンス変数の値を参照、変更する
などなど正直理解不能です…
わかっている方には当た有前すぎて何言ってんだこいつ状態かもしれませんが、もし自分と同じところでつまずいた方がいらっしゃれば参考にしていただけると幸いです。
attr_accessorの使用用途
まずは問題のattr_accessorメソッドですが一言でいうとインスタンスに外部から参照、更新可能な属性を持たせるメソッドです。
一つ一つ解説していきます。
インスタンスの値には直接アクセスできない
Railsを使っていると忘れがちなのですが、大前提としてインスタンスの値には直接アクセスすることはできません。
例として以下にHogeクラスを用意しました。インスタンス変数としてnameとageの2つがあります。
class Hoge
def initialize(name, age)
@name = name
@age = age
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
hoge = Hoge.new("hogehoge",12)
hoge.hello
p hoge.name
この状態でHogeのインスタンスであるhogeの変数であるnameを出力しようとしてみると…
"Hello! I'm hogehoge "
"I'm 12 old"
Traceback (most recent call last):
attr_accessor.rb:33:in `<main>': undefined method `name' for #<Hoge:0x0000000004e68eb8 @name="hogehoge", @age=12> (NoMethodError)
とNoMethodErrorが発生します。
それもそのはず。Hogeクラスにはnameというメソッドが存在していないためです。
こういったインスタンスの値にアクセスできない問題を解決するためにセッターとゲッターというメソッドが出てきます。
ゲッター
先ほどのようにインスタンスの値を参照したいときに使われるのがゲッターです。ゲッターの設定方法には2種類あり、メソッドを定義する方法とアクセスメソッドを使う方法です。
メソッドを定義する方法は実にシンプルで
class Hoge
def initialize(name, age)
@name = name
@age = age
end
# インスタンス変数と同じ名前にすることで値の参照を実現させる
def name
name = @name
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
と実行した際にインスタンス変数を引っ張ってくるメソッドを定義します。
ただこれだと少しかっこ悪いのでアクセスメソッドを使った方がまとまりが良いです。
class Hoge
attr_reader :name
def initialize(name, age)
@name = name
@age = age
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
attr_readerメソッドを使いことで上のようにインスタンス変数の値を参照できます。この際定義する値はインスタンス変数で定義したものと同じでなければいけません。
これが値を参照するためのゲッターという役割です。
セッター
それでは値の参照ができるようになったので、Railsと同じような感覚でインスタンスの値に別の値を代入してみると…
class Hoge
attr_reader :name
def initialize(name, age)
@name = name
@age = age
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
hoge = Hoge.new("hogehoge",12)
hoge.hello
p hoge.name
hoge.name = "fugafuga"
--------------------------------------------------------------------------------
"Hello! I'm hogehoge "
"I'm 12 old"
"hogehoge"
Traceback (most recent call last):
attr_accessor.rb:37:in `<main>': undefined method `name=' for #<Hoge:0x0000000004fbb040 @name="hogehoge", @age=12> (NoMethodError)
Did you mean? name
とまたもや怒られてしまいます。
これはゲッターでは値を参照することができても、更新する機能は含まれていないので新しい値を代入できないためです。
この問題を解決するために使われるのがセッターと呼ばれるものです。
セッターもメソッドをそのまま定義する方法とアクセサメソッドを使う方法の2種類があります。
def name=(name)
@name = name
end
メソッドの後に**=**を付けるとセッターになります。
内容はシンプルで受け取った引数をインスタンス変数に反映するだけです。
アクセサメソッドを使う方法。
class Hoge
attr_reader :name
attr_writer :name
def initialize(name, age)
@name = name
@age = age
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
アクセサメソッドはattr_writerを使います。
hoge = Hoge.new("hogehoge",12)
hoge.hello
p hoge.name
hoge.name = "fugafuga"
hoge.hello
------------------------------------------------------
"Hello! I'm hogehoge "
"I'm 12 old"
"hogehoge"
"Hello! I'm fugafuga "
"I'm 12 old"
と確かにnameの値が更新されています。
attr_accessor
いよいよattr_accessorの解説ですが上記のゲッターとセッターの両方を担うのがこのメソッドの役割ということになります。
つまりattr_reader, attr_writerの2種類を書かなくても
class Hoge
# この一行でゲッターとセッターの役割を果たしている
attr_accessor :name
def initialize(name, age)
@name = name
@age = age
end
def hello
p "Hello! I'm #{@name} "
p "I'm #{@age} old"
end
end
hoge = Hoge.new("hogehoge",12)
hoge.hello
p hoge.name
hoge.name = "fugafuga"
hoge.hello
----------------------------------------------------------
Hello! I'm hogehoge "
"I'm 12 old"
"hogehoge"
"Hello! I'm fugafuga "
"I'm 12 old"
とかなりシンプルに書くことができます。
ここまで読んでいて不思議に思ったかもしれませんが、Railsでは通常このアクセサメソッドは出てきません。
ではなぜメソッドのようにテーブルの値を参照、更新できているのかというと、ActiveRecord::Baseで自動機的に設定されているからです。
よければそのことについても記事を書いているのでご覧下さい
>> rails db:migrateとモデルという存在について
ここまでがattr_accessorの解説になります。
まとめ
メソッド | 機能 | 役割 |
---|---|---|
attr_reader | ゲッター | インスタンス変数の値を参照できるようになる |
attr_writer | セッター | インスタンス変数の値を更新できるようになる |
attr_accessor | ゲッター・セッター | インスタンス変数の値を参照、更新できるようになる |
Ruby on Railsは非常に便利なフレームワークですが、時々Rubyの基礎を勉強し直さないといけないなと強く思いました…
皆様も是非一度この辺りの内容を自分でコードを書いてみることをおすすめします!
それではー