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

f:id:sktktk1230:20180726124729p:plain

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

読めるようにするまで

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

読んだ箇所

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

どんな使い方だっけ?

読んでみる前にまずは使い方を調べてみます
RailsGuidesの日本語ドキュメントを見てみると

このメソッドは、エスケープされていないkeyを受け取ると、そのキーをto_paramが返す値に対応させるクエリ文字列の一部を生成します
引用:Active Support コア拡張機能:present

使い方はこんな感じのようです

current_user.to_query('user') # => "user=357-john-smith"

引数=レシーバを変換した値 という文字列を生成します

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

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

f:id:sktktk1230:20180118143800p:plain

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

activesupport > lib > active_support > core_ext > object > to_query.rb
1. class Object
class Object

省略

  # Converts an object into a string suitable for use as a URL query string,
  # using the given <tt>key</tt> as the param name.
  def to_query(key)
    "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
  end
end

まず CGI.escape() を見てみます

与えられた文字列を URL エンコードした文字列を新しく作成し返します。
[PARAM] string:
URL エンコードしたい文字列を指定します。

引用:Rubyリファレンスマニュアル:CGI.escape

文字列の引数を1つ取り、それに対してURLエンコードする処理です

CGI.escape() の引数部分 key.to_param で何をやっているのかは以前書いたこちらを参照ください

shitake4.hatenablog.com

続いて、 CGI.escape(to_param.to_s)を見ます

CGI.escape はさきほどと同様です
to_param.to_s はレシーバを to_param し、その戻り値を to_sしています

to_s はレシーバを文字列に変換するメソッドです
次のクラスに実装されています

ここまでをまとめると
key=valueという文字列を生成するメソッドになります

key部分を生成する際には to_query の引数に対して to_paramし、それをHTMLエスケープして作ります
value部分を生成する際には レシーバを to_paramし、それをHTMLエスケープするということです

2. class Array
class Array

省略

  # Converts an array into a string suitable for use as a URL query string,
  # using the given +key+ as the param name.
  #
  #   ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
  def to_query(key)
    prefix = "#{key}[]"

    if empty?
      nil.to_query(prefix)
    else
      collect { |value| value.to_query(prefix) }.join "&"
    end
  end
end

変数prefixの値の例を見てみます
仮にkeyがrubyMineだとすると

prefix = "rubyMine[]"

という文字列になります

次にif empty? を見てみます
empty?はRubyの標準メソッドです
使い方を見てみると

empty?メソッドは、配列が空であればtrue、1つ以上の要素があればfalseを返します。
引用:Rubyリファレンス:empty?

if empty? はレシーバである配列の中身が空であるかを判定しています

次の処理nil.to_query(prefix)を見てみます
どんな挙動かというと
f:id:sktktk1230:20180118144405p:plain

最後に collect { |value| value.to_query(prefix) }.join "&" を見ます
collect { |value| value.to_query(prefix) } では

レシーバに対して 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

レシーバの中身1つずつに対して、value.to_query(prefix) を実行しています
そして戻り値を配列にしています
実行例としてはこちらになります
f:id:sktktk1230:20180118144355p:plain

.join "&" では配列中身を&で連結し文字列生成しています

joinの挙動はこちらです

joinメソッドは、配列の各要素を文字列に変換し、引数sepを区切り文字として結合した文字列を返します。
引用:Rubyリファレンス:join

読んでみて

HTTPGetメソッドでパラメータを生成する際の使用するメソッドはWebサービスを作る上で欠かせないものだったりするので、 他のフレームワークや言語で作る際にはここで読んだ内容が活かせるなと思いました