Railsのソースコード読んでみる | Active Support to_param編

f:id:sktktk1230:20180726124729p:plain

普段仕事で使っているRuby on Railsですが、ソースコードを読む機会もなかなかないので、試しにやってみることにしました

読めるようにするまで

以前書いた記事に読めるようにするまでの設定を画像キャプチャ付きで解説しましたので、よろしければこちらをご参照ください
shitake4.hatenablog.com

読んだ箇所

to_param を今日は読んでみようと思います

どんな使い方だっけ?

読んでみる前にまずは使い方を調べてみます
RailsGuidのActive Support コア拡張機能を見てみると

Railsのあらゆるオブジェクトはto_paramメソッドに応答します。
これは、オブジェクトを値として表現するものを返すということです。返された値はクエリ文字列やURLの一部で使用できます。
引用:Active Support コア拡張機能:to_param

ソースコードを読んでみる

1. railsプロジェクトのactivesupportにある機能ですので、activesupportディレクトリのlib配下で def to_param を探してみます

f:id:sktktk1230:20180115104828p:plain

2. 該当箇所が8個ほどあったので、それぞれみてみます

1. activesupport > lib > active_support > core_ext > object > to_query.rb
1. class Object
# frozen_string_literal: true

require "cgi"

class Object
  # Alias of <tt>to_s</tt>.
  def to_param
    to_s
  end

単純にto_s しているだけです
オブジェクトに対して行いたい場合は、オーバーライドして使うということを想定しているのでしょう

2. class NilClass
class NilClass
  # Returns +self+.
  def to_param
    self
  end
end

レシーバ自身を返しています NilClassの戻り値はnilですので、このようなコードでもいいのではないか?と思いました

def to_param
  nil
end

もしかしたらコミットログに戻り値をselfにした理由や経緯があるかもしれないと思ったので、調べてみます
RubyMineの機能で選択範囲のコミットログを見る機能がある為、それを使います f:id:sktktk1230:20180115104852p:plain

残念ながら1コミットしかなかったため、わかりませんでした
f:id:sktktk1230:20180115104905p:plain

3. class TrueClass
class TrueClass
  # Returns +self+.
  def to_param
    self
  end
end
4. class FalseClass
class FalseClass
  # Returns +self+.
  def to_param
    self
  end
end
5. class Array
class Array
  # Calls <tt>to_param</tt> on all its elements and joins the result with
  # slashes. This is used by <tt>url_for</tt> in Action Pack.
  def to_param
    collect(&:to_param).join "/"
  end

レシーバに対して collect を実行しています
collectとはどういうものかというと

collectメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。ブロック引数itemには各要素が入ります。
mapメソッドはcollectメソッドの別名です。
次の例では、16進数を表す文字列を数値に変換した配列を作成しています。
引用: Rubyリファレンス:collect

numbers = ["68", "65", "6C", "6C", "6F"]
p numbers.collect {|item| item.to_i(16) }

=> [104, 101, 108, 108, 111]

引用: Rubyリファレンス:collect

mapも同じ処理を行うメソッドです
(&:to_param) の&:は配列の各要素に対して to_param を実行しています
たとえば、

[1, 2, 3].map(&:to_s)
=> ["1", "2", "3"]

という感じです ※詳しい解説は@kasei-san氏のこちらの記事がわかりやすいかと思います
qiita.com

各要素に to_param した配列に対して .join しています
joinについて調べてみると

joinメソッドは、配列の各要素を文字列に変換し、引数sepを区切り文字として結合した文字列を返します。
引数のデフォルト値は組み込み変数$,の値です。$,の初期値はnilなので、引数を省略すると区切り文字なしで要素を結合した文字列になります。
引用: Rubyリファレンス:join

配列の中身を連結し文字列に変換する処理です
たとえば、

array = ["Ruby", "Mine"]
puts array.join(", ")

の場合は Ruby, Mine と出力されます

区切り文字がなが場合は

array = ["Ruby", "Mine"]
puts array.join

RubyMine というようにそのまま連結し出力します

ということで .join "/" ではto_param された各要素を"/"で連結し、文字列として出力するという処理になります

2. activesupport > lib > active_support > core_ext > string > output_safety.rb
module ActiveSupport #:nodoc:
  class SafeBuffer < String

中略
    def to_param
      to_str
    end

Stringを拡張したSafeBufferクラスのインスタンスに対して to_param するのは to_str と同様になります
SafeBufferクラスがどのような用途で使われるものなのか分からない為、さきほどと同様にコミットログから理由、経緯を調べてみます

選択範囲をSafeBufferクラス内にしコミットログを調べます

class SafeBuffer < String

省略

end

f:id:sktktk1230:20180115104938p:plain

一番古いコミットログを見付け足ので、こちらのリビジョンナンバーをコピーしRailsリポジトリで調べます

f:id:sktktk1230:20180115104951p:plain

GitHubの検索窓にさきほどコピーしたリビジョンナンバーをペーストし検索します
f:id:sktktk1230:20180115105007p:plain

該当のコミットを見てみると
f:id:sktktk1230:20180115105020p:plain

パフォーマンスの改善の為 html_safe を呼び出す場合にはSafeBufferを使うということでした
コメントも読んでみると
f:id:sktktk1230:20180115105032p:plain

Stringに追加していると +<< を実行する時に遅くなっているということでした

読んでみて

コミットログを追ってみるとなぜ実装されているのかが分かるので、コードを読み込むだけでなく、歴史まで追ってみるとより理解が深まるなと思いました