普段仕事で使っているRuby on Railsですが、ソースコードを読む機会もなかなかないので、試しにやってみることにしました
読めるようにするまで
以前書いた記事で読めるようにするまでの設定を画像キャプチャ付きで解説しましたので、よろしければこちらをご参照下さい
shitake4.hatenablog.com
読んだ箇所
alias_attribute
を今日は読んでみようと思います
どんな使い方だっけ?
読んでみる前にまずは使い方を調べてみます
RAILS GUIDESを見てみると
3.2 属性
3.2.1 alias_attribute
モデルの属性には、リーダー (reader)、ライター (writer)、述語 (predicate) があります。上に対応する3つのメソッドを持つ、モデルの属性の別名 (alias) を一度に作成することができます。他の別名作成メソッドと同様、1つ目の引数には新しい名前、2つ目の引数には元の名前を指定します (変数に代入するときと同じ順序、と覚えておく手もあります)。
class User < ActiveRecord::Base # emailカラムを"login"という名前でも参照したい # そうすることで認証のコードがわかりやすくなる alias_attribute :login, :email end引用:RAILS GUIDES
ソースコードを読んでみる
1. railsプロジェクトのactivesupportにある機能ですので、activesupportディレクトリのlib配下で def alias_attribute
を探してみます
2. 該当箇所が1箇所あったので、みてみます
1. activesupport > lib > active_support > core_ext > moudle > aliasing.rb
# frozen_string_literal: true class Module # Allows you to make aliases for attributes, which includes # getter, setter, and a predicate. # # class Content < ActiveRecord::Base # # has a title attribute # end # # class Email < Content # alias_attribute :subject, :title # end # # e = Email.find(1) # e.title # => "Superstars" # e.subject # => "Superstars" # e.subject? # => true # e.subject = "Megastars" # e.title # => "Megastars" def alias_attribute(new_name, old_name) # The following reader methods use an explicit `self` receiver in order to # support aliases that start with an uppercase letter. Otherwise, they would # be resolved as constants instead. module_eval <<-STR, __FILE__, __LINE__ + 1 def #{new_name}; self.#{old_name}; end # def subject; self.title; end def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end STR end end
module_eval
メソッドに対して ヒアドキュメントで文字列(def〜STRの上の行まで)、__FILE__
、 __LINE__ + 1
を渡しています
※ヒアドキュメントとは?
Rubyのヒアドキュメント 4パターンのまとめ -- ぺけみさお
第2引数、第3引数に記述されている __FILE__, __LINE__ +1
がわからなかったので、調べてみました
__FILE__
現在のソースファイル名
フルパスとは限らないため、フルパスが必要な場合は File.expand_path(__FILE__) とする必要があります。
__LINE__
現在のソースファイル中の行番号
フルパスとは限らないため、フルパスが必要な場合は File.expand_path(__FILE__) とする必要があります。
引用:#疑似変数
疑似変数と呼ばれる特殊な変数のようです。
実は nil,true,false
も疑似変数でそれぞれのクラス(NilClass, TrueClass, FalseClass)の唯一のインスタンスが格納されているようです
module_evalを調べてみると
モジュールのコンテキストで文字列 expr またはモジュール自身をブロックパラメータとするブロックを 評価してその結果を返します。
モジュールのコンテキストで評価するとは、実行中そのモジュールが self になるということです。 つまり、そのモジュールの定義式の中にあるかのように実行されます。
ただし、ローカル変数は module_eval/class_eval の外側のスコープと共有します。
文字列が与えられた場合には、定数とクラス変数のスコープは自身のモジュール定義式内と同じスコープになります。 ブロックが与えられた場合には、定数とクラス変数のスコープはブロックの外側のスコープになります。
[PARAM] expr:
評価される文字列。
[PARAM] fname:
文字列を指定します。ファイル fname に文字列 expr が書かれているかのように実行されます。 スタックトレースの表示などを差し替えることができます。
[PARAM] lineno:
文字列を指定します。行番号 lineno から文字列 expr が書かれているかのように実行されます。 スタックトレースの表示などを差し替えることができます。
例:
class C end a = 1 C.class_eval %Q{ def m # メソッドを動的に定義できる。 return :m, #{a} end } p C.new.m #=> [:m, 1]
つまり、moduleに動的にでセッターメソッド、ゲッターメソッドと真偽値を返すメソッドを定義しているということになります
読んでみて
__FILE__
や __LINE__
はコード読んでいるとちょくちょく出てきてたので、そこは気にせず、読んでましたが、ちゃんと調べてみると新たな発見があったので、
ソースコードをしっかりと読み込むことは大切だと感じました