カラム名をメソッドとして実行できる、とは
Railsコンソールで以下のコードを実行してみる。
@blog = Blog.new #=> #<Blog id: nil, title: nil, content: nil, created_at: nil, updated_at: nil>
@blog.title = "aaa" #=> "aaa"
@blog.title #=> "aaa"
カラム名がそのままメソッドになっている
ことは上記コードを見ればなんとなくわかるだろう。
当たり前のように実行しているが、この概念を本当に理解しているか?
特に2行目の @blog.title = "aaa"
の =
を 代入
と勘違いしてる人が多いのではないか?
カラム名をメソッドとして実行できる概念を理解する
そこで今回はActiveRecordの機能を除き、以下5つのコードを正常に実行できるようなblogモデルにそっくりのクラスを、ライブラリなしで作成していく。
@blog = Blog.new("aaa", "bbb") #=> #<Blog:0x007fe748e000b0 @title="aaa", @content="bbb">
@blog.title #=> "aaa"
@blog.content #=> "bbb"
@blog.title = "aaa" #=> "aaa"
@blog.content = "bbb" #=> "bbb"
まず1行目を見ると以下を把握できる
- Blogというクラスがある
- newメソッドを実行した時点で引数が2つある
-
@title
,@content
というインスタンス変数を用いて、Blogクラスのインスタンスに代入している
従って以下のように実装できる
class Blog
def initialize(title, content)
@title = title
@content = content
end
end
次に2行目と3行目に着目する
-
@blog
の中にはBlogクラスのインスタンスが入っている - titleというインスタンスメソッドがある
- contentというインスタンスメソッドがある
class Blog
def initialize(title, content)
@title = title
@content = content
end
def title
return @title
end
def content
return @content
end
end
Rubyではreturnを省略すると、そのメソッドの最後の行がreturnされる。
ここではtitle, contentというメソッド内は1行しかないので、そのままreturnを省略する。
class Blog
def initialize(title, content)
@title = title
@content = content
end
def title
@title
end
def content
@content
end
end
で、問題の4行目と5行目。
これは @blog.titleへの代入
ではないので勘違いしないように気をつけよう。
@blog.title = "aaa"
上記のコードを以下に書き換えてみよう。
@blog.title=("aaa")
このようにすると @blog.title代入
ではなく title=
という名前の インスタンスメソッドを実行している
ということがわかる。
5行目も同じだ。
最終的に以下のようになる。
class Blog
def initialize(title, content)
@title = title
@content = content
end
def title
@title
end
def title=(title)
@title = title
end
def content=(content)
@content = content
end
end
Railsにおいてはどこに定義されているのか
Railsのblogモデルを見ると以下のようになっている。
class Blog < ApplicationRecord
end
上記コードを見ると def title
や def content=(content)
のような記述は見当たらない。
ではどこに定義されているのか?
それは ActiveRecord::Base
である。
ActiveRecord::Baseが動的に title
や content=
というメソッドを生成している。
class Blog < ApplicationRecord
# def title
# end
# def title=(title)
# end
# def content
# end
# def content=(content)
# end
end