【Ruby】putsの返り値はnilらしいと聞いて色々みてみた。
きっかけ
Railsチュートリアルを眺めててたら
4.2.4 メソッドの定義の演習3で
palindrome_tester("racecar")に対してnil?メソッドを呼び出し、 戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。 (以下略)
def palindrome_tester(s) if (条件。問題なので省略) puts "It's a palindrome!" else puts "It's not a palindrome." end end
と書いてあり、なんでだろ?と思ったので調べてみた。
また、 注記13にもこんなことが書いてある。
実際には些細な違いがあり、pメソッドは画面出力だけでなく戻り値も オブジェクトになります。 しかし、putsメソッドの場合は引数によらず必ずnilが戻り値になります。 (以下略)
リファレンスを見てみる
module function Kernel.#putsより
puts(*arg) -> nil 引数と改行を順番に 標準出力 $stdout に出力します。 引数がなければ改行のみを出力します。 引数が配列の場合、その要素と改行を順に出力します。 配列や文字列以外のオブジェクトが引数として与えられた場合には、 当該オブジェクトを最初に to_ary により配列へ、 次に to_s メソッドにより文字列へ変換を試みます。 末尾が改行で終っている引数や配列の要素に対しては puts 自身 は改行を出力しません。
なるほど標準出力されるのか〜
元々標準出力に向けてアウトプットするためのメソッドであり、返り値を得るようにはできてないですよってことなのだろうか。
似ているメソッドとして、module function Kernel.#pも見てみる。
p(*arg) -> object | Array 引数を人間に読みやすい形に整形して改行と順番に標準出力 $stdout に出力します。 主にデバッグに使用します。 引数の inspect メソッドの返り値と改行を順番に出力します。つまり以下のコードと同じです。 print arg[0].inspect, "\n", arg[1].inspect, "\n", ... 整形に用いられるObject#inspectは普通に文字列に変換すると 区別がつかなくなるようなクラス間の差異も表現できるように工夫されています。 p に引数を与えずに呼び出した場合は特に何もしません。
こっちは出力した情報を色々抜き出したりできるよう、返り値がオブジェクトになっているっぽい。
試してみる
Rails上で比較してみた。
- メソッドではメッセージ作成のみ。viewで
<%= %>
# 例えばapplication_helper.rb def thanks_message(user_name = "ユーザー") "#{user_name}さん、ありがとうございます" end # 例えばthanks.html.erb <div class="thanks_message" <%= thanks_message(@user.name) %> <div>
<結果>
当たり前だけど(ユーザー名)さん、ありがとうございます
と表示される。
- メソッド内でメッセージをputsして、それを
<% %>
viewに埋め込む
# 例えばapplication_helper.rb def thanks_message(user_name = "ユーザー") puts "#{user_name}さん、ありがとうございます" end # 例えばthanks.html.erb <div class="thanks_message" <% thanks_message(@user.name) %> <div>
<結果>
viewには何も表示されない!
一方、rails s
してるconsoleの方を見てみるとこっちに出力されてた。
色々試したが整理するとこんな感じ↓
メソッド内の出力メソッド↓/viewの表記→ | <% %> | <%= %> |
---|---|---|
なし | 非表示 | 表示 |
puts | 非表示 | 非表示 |
p | 非表示 | 表示 |
(pでも表示できるけど無駄感ハンパない。。)
余談
そもそも<%= %>
は何を出力してくれるのか
結果を出力
なるほどわからん。
# <%# WRONG %> # Hi, Mr. <% puts "Frodo" %>
はい、ごめんなさい。。
- erb(Eruby)のソース github.com
ここらへんだろうか https://github.com/jeremyevans/erubi/blob/master/lib/erubi.rb#L129-L133
when '=' rspace = nil if tailch && !tailch.empty? add_text(lspace) if lspace add_expression(indicator, code) add_text(rspace) if rspace
# Add the given ruby expression result to the template, # escaping it based on the indicator given and escape flag. def add_expression(indicator, code) if ((indicator == '=') ^ @escape) add_expression_result(code) else add_expression_result_escaped(code) end end # Add the result of Ruby expression to the template def add_expression_result(code) @src << " #{@bufvar} << (" << code << ').to_s;' // to_sで文字列に変換 end # Add the escaped result of Ruby expression to the template def add_expression_result_escaped(code) @src << " #{@bufvar} << #{@escapefunc}((" << code << '));' end
結局の所to_s
で文字列に変換してるということらしい。
to_sはObject classのメソッドなので、何でも文字列として返す(はず)。
例えば対象がarrayでも
[1, 2, 3].to_s =>"[1, 2, 3]" # 返り値はこんな文字列になる
まとめ
- putsの返り値はnil, pの返り値はarrayオブジェクト
- viewに出力するときはオブジェクトを用意して素直に
<%= %>
で書こう