Ruby on Rails チュートリアル

プロダクト開発の0→1を学ぼう

第2版 目次

第4章Rails風味のRuby

この章では、第3章で使用した例を基に、Railsにおいて重要となるRubyのさまざまな要素について探っていくことにしましょう。Rubyは巨大な仕様を持つ言語ですが、幸い、Rails開発者にとって必要な知識は比較的少なくて済みます。さらに、Railsのために必要なRubyの知識は、通常のRubyを学ぶ過程とは異なります。動的なWebアプリを作ることができればそれでよいというのであれば、まずRailsを学ぶようにし、Rubyについては当分の間、必要が生じた場合にのみ学習することをお勧めします。そうではなく、真のRailsエキスパートになりたいのであれば、Rubyをさらに深いレベルまで理解する必要があります。本書は、そのための開発技術の基礎を築く助けになるでしょう。1.1.1でも示したように、Railsチュートリアル を終えた後には「 Beginning Ruby」、「The Well-Grounded Rubyist」、「The Ruby Way」などの純粋なRubyについての本を読むことをお勧めします。

この章には多くの話題が盛り込まれていますが、一度読んだだけで理解する必要はまったくありません。今後もこの章には頻繁に立ち戻って参照します。

4.1動機

前章でお見せしたとおり、Rubyの基礎知識がまったくない状態であったにもかかわらずRailsアプリケーションの骨組みを作り上げ、さらにテストまで行うことができました。このときは、本書が提供するテストコードと、テストスイートがパスするまでエラーメッセージの修正を繰り返すという方法だけを頼りに作業を進めました。しかしこのような初歩的な作業をいつまでも続けるわけにはいきませんので、今の私たちのRubyに関する知識と経験の限界に真正面から挑み、これを乗り越えるためにこの章を割り当てることにします。

前章の終わりでは、Railsのレイアウトを使用してビューでの重複を取り除くために、リスト4.1に示したように、ほぼ静的なページを単に更新したにとどまりました。これは、リスト3.26をほんのわずか修正しただけのものです。

リスト4.1 サンプルアプリケーションのWebサイトのレイアウト。
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>
    <%= stylesheet_link_tag "application", media: "all",
                                           "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

リスト4.1の以下の行に注目してください。

<%= stylesheet_link_tag "application", media: "all",
                                       "data-turbolinks-track" => true %>

ここでは、Railsの組み込み関数stylesheet_link_tag (詳細はRails APIを参照) を使用して、application.cssをすべてのメディアタイプにインクルードしています (メディアタイプには、コンピュータの画面や印刷画面なども含まれます)。Rails開発経験者にとってこの行は実にシンプルですが、しかしここには少なくとも混乱を生じる可能性のあるRubyの概念が4つあります。Railsの組み込み関数、括弧を使わない関数呼び出し、シンボル、そしてハッシュです。これらの概念についてはこの章ですべて説明します。

Railsのビューでは膨大な組み込み関数を使用することができますが、それに加えて新しい関数を作成することもできます。この関数はヘルパーと呼ばれます。カスタムヘルパーを作成する方法を学ぶために、まずリスト4.1のタイトル行の部分に注目しましょう。

Ruby on Rails Tutorial Sample App | <%= yield(:title) %>

上の行は、ページタイトルの定義に依存しています。この定義は、以下のようにビューでprovideを使用して行われています。

<% provide(:title, 'Home') %>
<h1>Sample App</h1>
<p>
    This is the home page for the
    <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
</p>

このとき、もしタイトルをまったく与えていなければ、タイトルが空欄になってしまいます。これを防ぐには、すべてのページで使用する基本タイトルを定め、特定のページでは異なるタイトルに変更できるようなオプションを与えるのが常套手段です。これは現在のレイアウトでも、ある点を除いて達成されています。もしビューの1つからprovide呼び出しを削除すると、そのページ固有のタイトルの代わりに以下のタイトルが表示されます。

Ruby on Rails Tutorial Sample App | 

基本タイトルとしてはこれで正しいのですが、末尾に余分な縦棒|が残ってしまっています。

ページタイトルが正しく表示されない問題を解決するために、 full_titleというヘルパーを作成することにします。full_titleヘルパーは、ページタイトルが定義されていない場合は基本タイトル “Ruby on Rails Tutorial Sample App”を返し、定義されている場合は基本タイトルに縦棒と追加ページタイトルを追加して返します (リスト4.2)1

リスト4.2 full_titleヘルパーを定義する。
app/helpers/application_helper.rb
module ApplicationHelper

  # ページごとの完全なタイトルを返します。
  def full_title(page_title)
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      "#{base_title} | #{page_title}"
    end
  end
end

ヘルパーを作成したので、これを使用してレイアウトをシンプルにすることができます。

<title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>

上の行を以下で置き換えます。

<title><%= full_title(yield(:title)) %></title>

置き換えの結果をリスト4.3に示します。

リスト4.3 サンプルアプリケーションのWebサイトのレイアウト。
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= stylesheet_link_tag "application", media: "all",
                                           "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

このヘルパーを定義することで、Homeページにこれまで表示されていた余分な "Home" という単語を表示せず、基本タイトルのみを正しく表示することもできるようになります。これを行うには、まずリスト4.4に示すように以前のテストコードを更新し、'Home'の文字が表示されていないことを確認するテストを追加します。

リスト4.4 Homeページのタイトル確認用にテストを更新する。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end

    it "should have the base title" do
      visit '/static_pages/home'
      expect(page).to have_title("Ruby on Rails Tutorial Sample App")
    end

    it "should not have a custom page title" do
      visit '/static_pages/home'
      expect(page).not_to have_title('| Home')
    end
  end
  .
  .
  .
end

ここで、単に既存のテストコードを修正するだけではなく、新しいテストコードを追加した理由について考えてみてください (ヒント: 答えは3.3.1にあります)。

ここでテストスイートを実行して、テストが失敗することを確認します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

テストがパスするためには、リスト4.5のようにHomeページのビューからprovideの行を削除する必要があります。

リスト4.5 ページタイトルをカスタマイズせずに表示するHomeページ。
app/views/static_pages/home.html.erb
<h1>Sample App</h1>
<p>
    This is the home page for the
    <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
</p>

これでテストにパスするはずです。

$ bundle exec rspec spec/requests/static_pages_spec.rb

Rails開発経験者にとっては、リスト4.2のコードはスタイルシートをインクルードするのと大差ない単純なものですが、ここにもRuby未経験者を混乱させる可能性のある概念が多数含まれています。モジュール、コメント、ローカル変数割り当て、論理値 (boolean)、制御フロー、文字列の挿入、戻り値です。これらの概念についても、この章ですべて説明します。

4.2文字列(string)とメソッド

Ruby を学ぶためのツールとして、主にRailsコンソールを使用することにします。これは2.3.3 でも登場した、Railsアプリケーションを対話的に操作するためのコマンドラインツールです。コンソールはインタラクティブRuby (irb) 上に構築されているため、Rubyの機能をすべて使うことができます (4.4.4でも説明しますが、コンソールからRails環境にアクセスすることもできます)。以下のコマンドをコマンドラインで実行し、Railsコンソールを起動しましょう。

$ rails console
Loading development environment
>>

デフォルトでは、コンソールは開発 (development) 環境という、Railsによって定義された3種類の環境のうちの1つで起動します (他の2つはテスト (test) 環境本番 (production) 環境です)。この区別はこの章においては重要ではありませんが、7.1.1でこれらの環境について詳細に説明します。

Railsコンソールは素晴しい学習ツールであり、その中を自由に探索できます。コンソールの中で何をしようとも、何かを壊すことは (まず) ありえないので、ご安心ください。Railsコンソールでは、スタックから抜けるにはCtrl-C を押し、完全にコンソールを終了するにはCtrl-Dを押します。以後この章を進めるにあたり、有用なリソースであるRuby APIを参照しながら学習することをぜひお勧めします。Ruby APIには高濃縮の情報が詰まっています (少々濃厚すぎるとも言えます)。たとえば、Rubyの文字列の詳細を知りたい場合は、Ruby APIエントリのStringクラスを参照すればよいのです。

4.2.1コメント

Rubyのコメント はナンバー記号# ("ハッシュマーク"や、"オクトソープ"とも呼ばれます) から始まり、行の終わりまでコメントとして扱われます。Rubyはコメントの内容を実行することはありませんが、適切なコメントはそれを読む人間にとって (コードの作者にとっても) 非常に有用です。以下のコードの場合、

  # ページごとの完全なタイトルを返します。
  def full_title(page_title)
    .
    .
    .
  end

最初の行が、その後に定義されている関数の目的を説明しているコメントです。

コンソール内ではコメントを使わないのが普通ですが、ここでは学習のためにあえて以下のようにコメントを追加してみましょう。

$ rails console
>> 17 + 42   # 整数の加算
=> 59

この章のコードを (ファイルに保存するのでなく) Railsコンソールに入力したりコピペしたりするときであれば、コメントを省略してもかまいません。コメントをRailsコンソールに入力しても、コメントは常に無視されるので問題ありません。

4.2.2文字列

文字列 (string)は、おそらくどのWeb アプリケーションにおいても最も重要なデータ構造です。これは、Web ページというものが究極的にはサーバーからブラウザに送信された文字列にすぎないためです。それでは、コンソールで文字列について調べてみましょう。今回はrails consoleの代わりに、短縮版のrails cでコンソールを起動します。

$ rails c
>> ""         # 空の文字列
=> ""
>> "foo"      # 空でない文字列
=> "foo"

ここで入力したものは文字列リテラルと呼ばれ (面白いことにリテラル文字列とも呼ばれます)、ダブルクォート" で囲むことで作成できます。このコンソールは、入力したそれぞれの行を評価した結果を表示しており、文字列リテラルの場合には文字列自身が表示されます。

+ 演算子を使用して、文字列を結合することもできます。

>> "foo" + "bar"    # 文字列の結合
=> "foobar"

評価の結果は、"foo""bar" を足した"foobar"になりました2

文字列を組み立てる他の方法として式展開 (interpolation)というものがあり、#{}という特殊な構文を使用します3

>> first_name = "Michael"    #変数割り当て
=> "Michael"
>> "#{first_name} Hartl"     # 文字列の式展開
=> "Michael Hartl"

ここでは、"Michael" という値をfirst_name変数に割り当て、この変数が "#{first_name} Hartl" という文字列の中で式展開されます。苗字と名前の両方を変数に割り当てることもできます。

>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # スペースをはさんで結合する
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # 式展開による同等のコード
=> "Michael Hartl"

最後の2つの結果は同等であることに注目してください。なお、著者は後者の式展開が好みです。空白を" "という形式で加えるのはぎこちなく思えます。

出力

文字列を出力するために、Rubyの関数で最も一般に使われるのはputsです (putの三人称単数現在形ではなく “put string”なので、“put ess” と発音します)。

>> puts "foo"     # put string
foo
=> nil

puts メソッドは副作用が重要な役割を果たします。どういうことかと言うと、puts "foo"は文字列 foo を副作用としてスクリーンに表示しますが、返り値には「無」を返します (正確にはnilというリテラルを返します。nil は "まったく無い"ことを表すRubyの特別な値です)。(=> nil という結果は、今後簡素化のために省略することがあります。)

putsを使用して出力すると、改行文字である\nが自動的に出力の末尾に追加されます。printメソッドも同様の出力を行いますが、以下のように、改行文字を追加しない点が異なります。

>> print "foo"    # 文字列の出力 (putsと同じだが改行しない)
foo=> nil
>> print "foo\n"  # puts "foo" と同等
foo
=> nil

シングルクォート内の文字列

これまでの例ではすべてダブルクォート文字列を使用していましたが、Rubyではシングルクォートもサポートしています。ほとんどの場合、ダブルクォートとシングルクォートのどちらを使用しても実質的に同じです。

>> 'foo'          # シングルクォートで囲んだ文字列
=> "foo"
>> 'foo' + 'bar'
=> "foobar"

ただし、1つ重要な違いがあります。Rubyはシングルクォート文字列の中では式展開を行いません。

>> '#{foo} bar'     # シングルクォートで囲まれた文字列では式展開されない
=> "\#{foo} bar"

逆に言えば、ダブルクォート文字列を用いた文字列で#のような特殊な文字を使用する場合は、この文字をバックスラッシュでエスケープする必要があります。

ダブルクォート文字列でもシングルクォート文字列と同じことができ、ダブルクォート文字列では式展開もできるのであれば、シングルクォート文字列にはどのような使い道があるのでしょうか。シングルクォートは、入力した文字をエスケープせずに「そのまま」保持するときに便利です。たとえば、いわゆる “バックスラッシュ” 文字は、改行文字\nと同様多くのシステム上で特殊な文字として扱われます。シングルクォートで文字列を囲めば、簡単にバックスラッシュ文字のような特殊文字をそのまま変数に含めることができます。

>> '\n'       # 'バックスラッシュ n' をそのまま扱う
=> "\\n"

以前の例で扱った#文字と同様、Rubyではバックスラッシュをエスケープするためにバックスラッシュがもう1つ必要です。ダブルクォート文字列の中では、バックスラッシュ文字そのものは2つのバックスラッシュによって表されます。このようなささいな例の場合はそれほど問題になりませんが、以下のようにエスケープの必要な文字が大量にある場合には、シングルクォートは非常に便利です。

>> 'Newlines (\n) and tabs (\t) both use the backslash character \.'
=> "Newlines (\\n) and tabs (\\t) both use the backslash character \\."

4.2.3オブジェクトとメッセージ受け渡し

Rubyでは、あらゆるものがオブジェクトです。文字列やnilですらオブジェクトです。このことの技術的な意味は4.4.2で説明しますが、オブジェクトについてのこのような定義は、本で読んだだけではわかりません。さまざまなオブジェクトの例に触れることで、オブジェクトとは何であるかという直感を時間をかけて養う必要があります。

逆に、オブジェクトが何をするかを説明するのは簡単です。オブジェクトとは (いついかなる場合にも) メッセージに応答するものです。文字列のようなオブジェクトは、たとえば lengthというメッセージに応答できますが、これは文字列の文字数を返します。

>> "foobar".length        # 文字列に "length" というメッセージを渡す
=> 6

オブジェクトに渡されるメッセージは、一般にはメソッドと呼ばれます。メソッドの実体は、そのオブジェクトに定義された関数です4。Rubyの文字列は、以下のようにempty?メソッドにも応答することができます。

>> "foobar".empty?
=> false
>> "".empty?
=> true

empty?メソッドの末尾にある疑問符に注目してください。これは Rubyの慣習で、true もしくは falseという論理値 (boolean)を返すことを示します。論理値は特に制御フローにおいて有用です。

>> s = "foobar"
>> if s.empty?
>>   "The string is empty"
>> else
>>   "The string is nonempty"
>> end
=> "The string is nonempty"

論理値は、&& (“and”)、|| (“or”)、そして! ("not") 演算子によって結合することもできます。

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Both strings are empty" if x.empty? && y.empty?
=> nil
>> puts "One of the strings is empty" if x.empty? || y.empty?
"One of the strings is empty"
=> nil
>> puts "x is not empty" if !x.empty?
"x is not empty"
=> nil

Rubyでは、あらゆるものがオブジェクトです。従って、nilもオブジェクトであり、これも多くのメソッドに応答できます。ほぼすべてのオブジェクトを文字列に変換するto_sメソッドを使用して、nilがメソッドに応答する例をお目にかけましょう。

>> nil.to_s
=> ""

確かに空文字列が出力されました。今度はnil に対してメッセージを連鎖 (chain)して渡せることを確認します。

>> nil.empty?
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
>> nil.to_s.empty?      # メッセージの連鎖
=> true

上に示したように、nil オブジェクトはempty? メソッドに応答しませんが、nil.to_sには応答します。

ご想像のとおり、nilかどうかを調べるメソッドもあります:

>> "foo".nil?
=> false
>> "".nil?
=> false
>> nil.nil?
=> true

ところで、以下のコードは

puts "x is not empty" if !x.empty?

ifキーワードの別の使い方を示しています。Rubyではこのように、後続するifでの条件式が真のときにだけ実行される式を書くことができ、コードが非常に簡潔になります。なお、unlessキーワードも同様に使用できます。

>> string = "foobar"
>> puts "The string '#{string}' is nonempty." unless string.empty?
The string 'foobar' is nonempty.
=> nil

Rubyにおいてnilは特別なオブジェクトです。Rubyのオブジェクトのうち、オブジェクトそのものの論理値がfalseになるのは、(falseというオブジェクト自身を除いて) nilだけです。

>> if nil
>>   true
>> else
>>   false        # nilはfalse
>> end
=> false

その他のあらゆるRuby のオブジェクトは、ゼロですらtrueです。

>> if 0
>>   true        # 0 (を含むあらゆるオブジェクト、nilとfalseは除く) はtrue
>> else
>>   false
>> end
=> true

4.2.4メソッドの定義

Railsコンソールでも、リスト3.6homeアクションや、リスト4.2full_titleヘルパーと同じ方法でメソッドを定義することができます (メソッドの定義はファイルで行うのが普通なので、コンソールで行うのは少々面倒ですが、デモンストーレション目的であれば十分です)。たとえば、引数を1つ取り、引数が空かどうかに基づいたメッセージを返すstring_messageという関数を定義してみましょう。

>> def string_message(string)
>>   if string.empty?
>>     "It's an empty string!"
>>   else
>>     "The string is nonempty."
>>   end
>> end
=> nil
>> puts string_message("")
It's an empty string!
>> puts string_message("foobar")
The string is nonempty.

ここで、Rubyの関数は 暗黙の戻り値を持つということに注意してください。これは、最後に評価された式の値が自動的に返されることを意味します。この場合、引数のstringが空かどうかに基づいた2つのメッセージ文字列のうちのいずれかが返されます。Rubyでは、戻り値を明示的に指定することもできます。以下の関数は上の関数と同じ結果を返します。

>> def string_message(string)
>>   return "It's an empty string!" if string.empty?
>>   return "The string is nonempty."
>> end

上の説明で気付いた方もいると思いますが、2番目のreturn は実はなくてもかまいません。関数中の最後の表現 "The string is nonempty." は、returnキーワードがあってもなくても値を返すためです。ここでは、両方にreturnを使用する方が見た目の対称性が保たれるので好ましいと言えます。

4.2.5 title ヘルパー、再び

ここまでの解説でRubyの知識がある程度身についたので5、リスト4.2full_titleヘルパーを理解できるようになったはずです。

module ApplicationHelper

  # ページごとの完全なタイトルを返します。# コメント行
  def full_title(page_title)                          # メソッド定義
    base_title = "Ruby on Rails Tutorial Sample App"  # 変数に値を割り当てる
    if page_title.empty?                              # 論理値テスト
      base_title                                      # 暗黙の返り値
    else
      "#{base_title} | #{page_title}"                 # 文字列の式展開
    end
  end
end

Webサイトのレイアウトで使用するコンパクトなヘルパーメソッドは、関数定義、変数割り当て、論理評価、制御フロー、そして文字列の式展開などのRubyの要素が一体となってできあがっています。最後にmodule ApplicationHelperという要素について解説します。このモジュールは、関連したメソッドをまとめる方法の1つで、Rubyのクラスにincludeを使うことでミックスイン (mixed in)することができます。通常のRubyを書くときには、モジュールを書いてはこれを明示的にインクルードするという作業を頻繁に行いますが、このヘルパーモジュールの場合はRailsが自動的にインクルードしてくれます。つまり、このfull_titleメソッドは自動的にすべてのビューで利用できます。

4.3他のデータ構造

Webアプリケーションは突き詰めればただの文字列に過ぎませんが、実際にはこれらの文字列を作るために文字列以外のデータ構造も必要となります。この節では、Railsアプリケーションを書くために重要となる、いくつかのRubyのデータ構造について説明します。

4.3.1配列と範囲演算子

配列 (array) は、特定の順序を持つ要素のリストです。本書Railsチュートリアルではこれまで配列について解説していませんでしたが、配列を理解することは、ハッシュ (4.3.3) やRailsのデータモデルを理解するための重要な基盤となります (データモデルとはhas_manyなどの関連付けのことであり、詳細については2.3.310.1.3で説明します)。

Rubyの文字列の理解にだいぶ時間を使ってしまいましたので、次に進むことにします。文字列から配列を得る自然な方法の1つとして、以下のsplitメソッドがあります。

>>  "foo bar     baz".split     # 文字列を3つの要素を持つ配列に分割する
=> ["foo", "bar", "baz"]

この操作によって、3つの文字列からなる配列が得られます。デフォルトでは、splitは文字列を空白で区切って配列にしますが、以下のように他の文字を指定して区切ることもできます。

>> "fooxbarxbazx".split('x')
=> ["foo", "bar", "baz"]

多くのコンピュータ言語の慣習と同様、 Rubyの配列もゼロオリジンを採用しています。これは、配列の最初の要素のインデックスは0、2番目は1...と続くことを意味します。

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # 配列へのアクセスには [ ] を使用する
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # 負の値を持つインデックスも使用できる
=> 17

上で示したとおり、配列の要素にアクセスするには角括弧を使用します。Rubyでは、角括弧によるアクセス方法以外にも配列の要素にアクセスする方法が提供されています6

>> a                  # 'a' の内容に注目
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]    # ==演算子による比較
=> true

最後の行では、等しいことを確認する比較演算子==を使ってみました。この演算子や != (“等しくない”) などの演算子は、他の多くの言語と共通です。

>> x = a.length       # 配列も文字列と同様 'length' メソッドに応答する
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

配列は、上記コードの最初の行のlengthメソッド以外にも、さまざまなメソッドに応答します。

>> a
=> [42, 8, 17]
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
>> a
=> [42, 8, 17]

上のどのメソッドを実行した場合にも、a自身は変更されていないという点に注目してください。配列の内容を変更したい場合は、そのメソッドに対応する “破壊的” メソッドを使用します。

>> a
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]

また、pushメソッド (または同等の<<演算子) を使用して配列に追加することもできます。

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a.push(6)                  # 配列に6を追加
=> [42, 8, 17, 6]
>> a << 7                     # 配列に7を追加
=> [42, 8, 17, 6, 7]
>> a << "foo" << "bar"        # 配列に連続プッシュ
=> [42, 8, 17, 6, 7, "foo", "bar"]

最後の例では、要素の追加を連鎖 (chain) できることを示しました。また、Rubyの配列は、他の多くの言語の配列とは異なり、さまざまな型を共存させることができます (この場合は整数と文字列)。

文字列を配列に変換するのにsplitを使用しました。joinメソッドはこれと逆の動作です。

>> a
=> [42, 8, 17, 7, "foo", "bar"]
>> a.join                       # 区切り文字なしで連結
=> "428177foobar"
>> a.join(', ')                 # カンマとスペースで区切って連結
=> "42, 8, 17, 7, foo, bar"

範囲 (range)は、配列と密接に関係しています。to_aメソッドを使用して配列に変換すると理解しやすいと思います。

>> 0..9
=> 0..9
>> 0..9.to_a              # 配列でない数字の9にto_aを実行してしまった
NoMethodError: undefined method `to_a' for 9:Fixnum
>> (0..9).to_a            # 範囲にto_aを実行するには丸括弧で囲む
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

0..9 は範囲として有効ですが、上の2番目の表記ではメソッドを呼ぶ際に括弧を追加する必要があることを示しています。

範囲は、配列の要素を取り出すのに便利です。

>> a = %w[foo bar baz quux]         # %wを使用して文字列の配列に変換
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]

インデックスに-1という値を指定できるのは極めて便利です。-1を使用すると、配列の長さを知らなくても配列の最後の要素を指定することができ、これにより配列を特定の開始位置の要素から最後の要素までを一度に選択することができます。

>> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>> a[2..(a.length-1)]               # 配列の長さを明示的に使用している。
=> [2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..-1]                         # index -1の使用例。
=> [2, 3, 4, 5, 6, 7, 8, 9]

以下のように、文字に対しても範囲を使用できます。

>> ('a'..'e').to_a
=> ["a", "b", "c", "d", "e"]

4.3.2ブロック

配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます。ブロックとは、Rubyの極めて強力な機能であり、かつ混乱を呼びやすい機能でもあります。

>> (1..5).each { |i| puts 2 * i }
2
4
6
8
10
=> 1..5

上のコードは、範囲オブジェクトである(1..5)に対してeachメソッドを呼び出します。そのときに、{ |i| puts 2 * i }というブロックを渡します。|i|では変数名が縦棒に囲まれていますが、これはブロック変数に対して使用するRubyの構文で、ブロックを操作するときに使用する変数を指定します。この場合、範囲オブジェクトのeachメソッドは、iという1つのローカル変数を使用してブロックを操作できます。そして、範囲に含まれるそれぞれの値をこの変数に次々に代入してブロックを実行します。

ブロックは波括弧 { } で囲んで示すことができますが、以下のようにdoとendで囲んで示すこともできます。

>> (1..5).each do |i|
?>   puts 2 * i
>> end
2
4
6
8
10
=> 1..5

ブロックには複数の行を記述できます (実際ほとんどの場合複数です)。Rails チュートリアルでは、波括弧を短い1行のブロックに使用し、do..end記法を長い1行や複数行のブロックに使用するという、Ruby共通の慣習に従っています。

>> (1..5).each do |number|
?>   puts 2 * number
>>   puts '--'
>> end
2
--
4
--
6
--
8
--
10
--
=> 1..5

ここではnumberiと差し替えたことで、他の変数名を使用してもよいということを強調しています。

ブロックは見た目に反して奥が深く、ブロックを十分に理解するためには相当なプログラミング経験が必要です。そのためには、ブロックを含むコードをたくさん読みこなすことでブロックの本質を会得する以外に方法はありません7。幸いなことに、人間には個別の事例を一般化する能力というものがあります。ささやかですが、mapメソッドなどを使用したブロックの使用例を参考のためにいくつか挙げてみます。

>> 3.times { puts "Betelgeuse!" }   # 3.timesではブロックに変数を使用していない
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # **は累乗を表す
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # %wは文字列配列への変換であることを思い出そう
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]
>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]

上に示したように、map メソッドは、配列や範囲オブジェクトの各要素に対して、与えられたブロックを適用した結果を返します。

ところで、1.4.4でランダムなサブドメインを生成するために以下のRubyコードを紹介しましたが、今なら理解できる状態になったはずです。

('a'..'z').to_a.shuffle[0..7].join

このコードを段階的に組み立ててみると、動作がよくわかります。

>> ('a'..'z').to_a                     # アルファベットの配列
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
>> ('a'..'z').to_a.shuffle             # シャッフルする
=> ["c", "g", "l", "k", "h", "z", "s", "i", "n", "d", "y", "u", "t", "j", "q",
"b", "r", "o", "f", "e", "w", "v", "m", "a", "x", "p"]
>> ('a'..'z').to_a.shuffle[0..7]       # 最初の8つの要素を取り出す
=> ["f", "w", "i", "a", "h", "p", "c", "x"]
>> ('a'..'z').to_a.shuffle[0..7].join  # つなげて1つの文字列にする
=> "mznpybuj"

4.3.3ハッシュとシンボル

ハッシュは、本質的には配列と同じですが、インデックスとして整数値以外のものも使用できる点が配列と異なります (この理由から、いくつかの言語 (特にPerl) ではハッシュを連想配列と呼ぶこともあります)。ハッシュのインデックス (キーと呼ぶのが普通です) は、通常何らかのオブジェクトです。たとえば、以下のように文字列をキーとして使用できます。

>> user = {}                          # {}は空のハッシュ
=> {}
>> user["first_name"] = "Michael"     # キー: "first_name"、値: "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # キー: "last_name"、値: "Hartl"
=> "Hartl"
>> user["first_name"]                 # 配列と同じ方法で要素にアクセスできる
=> "Michael"
>> user                               # ハッシュのリテラルな表記
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

ハッシュは、キーと値のペアを波括弧で囲んで表記します。キーと値のペアを持たない括弧の組 ({}) は空のハッシュです。ここで重要なのは、ハッシュの波括弧は、ブロックの波括弧とはまったく別物であるという点です (これは確かに紛らわしいものです) 。ハッシュは配列と似ていますが、1つの重要な違いとして、ハッシュでは要素の「並び順」が保証されないという点があります8

ハッシュの要素を1つずつ角括弧を使って定義するよりも、キーと値をハッシュロケットと呼ばれる=> によってリテラル表現するほうが簡単です。

>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

ここではRubyにおける慣習として、ハッシュの最初と最後に空白を追加しています。この空白はあってもなくてもよく、コンソールでは無視されます (なぜスペースを置くようになったのかはわかりません。おそらく初期の有力な Rubyプログラマが好んだ結果、慣習となったのでしょう)。

ここまではハッシュのキーとして文字列を使用していましたが、Railsでは文字列よりもシンボルを使用する方が普通です。シンボルは文字列と似ていますが、クォートで囲む代わりにコロンが前に置かれている点が異なります。たとえば、:nameはシンボルです。もちろん、余計なことを一切考えずに、シンボルを単なる文字列とみなしても構いません9

>> "name".split('')
=> ["n", "a", "m", "e"]
>> :name.split('')
NoMethodError: undefined method `split' for :name:Symbol
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol

シンボルは、Ruby以外ではごく一部の言語にしか採用されていない特殊なデータ形式です。最初は奇妙に思うかもしれませんが、Railsではシンボルをふんだんに使用しているので、すぐに慣れるでしょう。

ハッシュのキーとしてシンボルを採用する場合、user のハッシュは以下のように定義できます。

>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]              # :name に一致する値にアクセスします。
=> "Michael Hartl"
>> user[:password]          # 未定義のキーの値にアクセスします。
=> nil

最後の例を見ると、未定義のハッシュ値は単純にnilであることがわかります。

ハッシュではシンボルをキーとして使うことが一般的なので、Ruby 1.9ではこのような特殊な場合のための新しい記法をサポートしています。

>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h2 = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h1 == h2
=> true

2つ目の記法は、シンボルとハッシュロケットの組み合わせを、以下のようにキーの名前の (前ではなく) 後にコロンを置き、その後に値が続くように置き換えたものです。

{ name: "Michael Hartl", email: "michael@example.com" }

この構成は、JavaScriptなど他の言語のハッシュ記法により近いものになっており、Railsコミュニティでも人気が高まっています。どちらの記法もよく使われているので、両方の見分けがつくことが重要です。

リスト4.6に示したように、ハッシュの値にはほぼ何でも使用することができ、他のハッシュを使用することすらできます。

リスト4.6 ハッシュをネストする。
>> params = {}        # 'params' というハッシュを定義する ('parameters' の略)。
=> {}
>> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" }
=> {:name=>"Michael Hartl", :email=>"mhartl@example.com"}
>> params
=> {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}}
>>  params[:user][:email]
=> "mhartl@example.com"

Railsでは、このようなハッシュのハッシュ (またはネストされたハッシュ) が大量に使われています。実際の使用例は7.3で説明します。

配列や範囲オブジェクトと同様、ハッシュもeachメソッドに応答します。たとえば、:success:errorという 2つの状態を持つ flash という名前のハッシュについて考えてみましょう。

>> flash = { success: "It worked!", error: "It failed." }
=> {:success=>"It worked!", :error=>"It failed."}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :error has value "It failed."

ここで、配列のeachメソッドでは、ブロックの変数は1つだけですが、ハッシュのeachメソッドでは、ブロックの変数はキーの2つになっていることに注意してください。従って、 ハッシュに対してeachメソッドを実行すると、ハッシュの1つの「キーと値のペア」ごとに処理を繰り返します。

最後の例として、便利なinspectメソッドを紹介します。これは要求されたオブジェクトを表現する文字列を返します。

>> puts (1..5).to_a            # 配列を文字列として出力
1
2
3
4
5
>> puts (1..5).to_a.inspect    # 配列のリテラルを出力
[1, 2, 3, 4, 5]
>> puts :name, :name.inspect
name
:name
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"

ところで、オブジェクトを表示するためにinspectとputsを使用することは非常によくあることなので、 両者を合わせたp関数というショートカットがあります。

>> p :name             # 'puts :name.inspect' と同等
:name

4.3.4 CSS、再び

それでは、もう一度リスト4.1に戻り、レイアウトに CSS (cascading style sheet) を追加する以下の行を見てみましょう。

<%= stylesheet_link_tag "application", media: "all",
                                       "data-turbolinks-track" => true %>

今なら、このコードを理解できるようになったはずです。4.1でも簡単に説明したとおり、Railsではスタイルシートを追加するための特別な関数を使用しています。

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

上のコードは、その特別な関数を呼んでいます。しかし、ここで不思議な点がいくつもあります。まず、丸括弧がないのが不思議です。実は、Ruby では丸括弧は使用してもしなくても構いません。以下の2つの行は同等です。

# 関数呼び出しの丸括弧は省略可能。
stylesheet_link_tag("application", media: "all",
                                   "data-turbolinks-track" => true)
stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

次に、:media引数はハッシュのようですが、波括弧がない点が不思議です。実は、ハッシュが関数呼び出しの最後の引数である場合は、波括弧を省略できます。以下の2つの行は同等です。

# 最後の引数がハッシュの場合、波括弧は省略可能。
stylesheet_link_tag "application", { media: "all",
                                     "data-turbolinks-track" => true }
stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

次に、data-turbolinks-trackにおけるキーと値のペアの表記が、旧式のハッシュロケット (=>) スタイルになっている点が不思議です。実は、以下のような新しいハッシュ記法を使用すると、

data-turbolinks-track: true

:data-turbolinks-trackというシンボルを作成しようとしますが、シンボルではハイフンを使用できないため、この記法は無効です。このため、以下のような旧式のハッシュロケット記法を使用するしかないのです。

"data-turbolinks-track" => true

最後に、Rubyが以下のようなコードを正常に実行できているのが不思議です。

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

上のコードには途中に改行が含まれているにもかかわらずです。実は、Rubyは改行の有無に左右されません10。著者が上のコードに改行を追加した理由は、コードの読みやすさを損なわないために1行あたり80文字までに制限していたためです11

従って、

stylesheet_link_tag "application", media: "all",
                                   "data-turbolinks-track" => true

上のコードではstylesheet_link_tag関数を2つの引数で呼んでいます。最初の引数である文字列は、スタイルシートへのパスを示しています。次の引数であるハッシュには2つの要素があり、最初の要素はメディアタイプを示し、次の要素はturbolinks機能をオンにしています (Turbolinksの詳細については、本書の次のドラフト版で説明します)。コードが<%= %>で囲まれていることによって、 このコードの実行結果はERbのテンプレートに挿入されます。ブラウザ上でこのページのソースを表示すると、必要なスタイルシートが含まれていることを確認できます (リスト4.7)。(CSSファイル名の後に、?body=1のような行が余分に表示されていることがあります。これらはRailsによって挿入されているもので、サーバー上で変更があった場合にブラウザがCSSを再読み込みするのに使用します。)

リスト4.7 インクルードされたCSSによって生成されたHTMLソース。
<link data-turbolinks-track="true" href="/assets/application.css" media="all"
rel="stylesheet" />

実際にhttp://localhost:3000/assets/application.cssのCSSファイルにアクセスしてみると、わずかなコメントがある以外は空になっています。CSSの変更は第5章で行います。

4.4 Ruby におけるクラス

Rubyではあらゆるものがオブジェクトであるということは既に説明しましたが、この節では実際にオブジェクトをいくつか定義してみましょう。Rubyは、多くのオブジェクト指向言語と同様、メソッドをまとめるのにクラスを使用しています。これらのクラスからインスタンスが生成されることでオブジェクトが作成されます。オブジェクト指向プログラミングの経験がない方にとっては何のことだかわからないと思いますので、いくつかの具体例を示すことにします。

4.4.1コンストラクタ

実は、これまで示した多くの例の中でも、クラスを使用してオブジェクトのインスタンスを作成してきたのですが、オブジェクトを作成するところを明示的に説明していませんでした。たとえば、ダブルクォートを使って文字列のインスタンスを作成しましたが、これは文字列のオブジェクトを暗黙で作成するリテラルコンストラクタです。

>> s = "foobar"       # ダブルクォートを使用した、文字列のリテラルコンストラクタ
=> "foobar"
>> s.class
=> String

上のコードでは、文字列がclassメソッドに応答しており、その文字列が所属するクラスを単に返していることがわかります。

暗黙のリテラルコンストラクタを使う代わりに、明示的に同等の名前付きコンストラクタを使うことができます。名前付きコンストラクタは、クラス名に対してnewメソッドを呼び出します12

>> s = String.new("foobar")   # 文字列の名前付きコンストラクタ
=> "foobar"
>> s.class
=> String
>> s == "foobar"
=> true

この動作はリテラルコンストラクタと同等ですが、動作の内容が明確に示されています。

配列でも、文字列と同様にインスタンスを生成できます。

>> a = Array.new([1, 3, 2])
=> [1, 3, 2]

ただし、ハッシュの場合は若干異なります。配列のコンストラクタであるArray.new は配列の初期値を引数に取りますが、 Hash.new はハッシュのデフォルト 値を引数に取ります。これは、キーが存在しない場合のデフォルト値です。

>> h = Hash.new
=> {}
>> h[:foo]            # 存在しないキー :fooの値にわざとアクセスしてみる
=> nil
>> h = Hash.new(0)    # キーが存在しない場合のデフォルト値をnilから0に変更する
=> {}
>> h[:foo]
=> 0

メソッドがクラス自身 (この場合はnew) に対して呼び出されるとき、このメソッドをクラスメソッドと呼びます。クラスのnewメソッドを呼び出した結果は、そのクラスのオブジェクトであり、これはクラスのインスタンスとも呼ばれます。lengthのように、インスタンスに対して呼び出すメソッドはインスタンスメソッドと呼ばれます。

4.4.2クラス継承

クラスについて学ぶとき、superclassメソッドを使ってクラス階層を調べてみるとよくわかります。

>> s = String.new("foobar")
=> "foobar"
>> s.class                        # sのクラスを表示
=> String
>> s.class.superclass             # Stringクラスのスーパークラスを表示
=> Object
>> s.class.superclass.superclass  # Ruby 1.9では新たに BasicObject を使用
=> BasicObject
>> s.class.superclass.superclass.superclass
=> nil

継承階層を図4.1に示します。ここでは、StringクラスのスーパークラスはObjectクラスで、ObjectクラスのスーパークラスはBasicObjectクラスですが、 BasicObjectクラスはスーパークラスを持たないことがわかります。この図式は、すべての Ruby のオブジェクトにおいて成り立ちます。クラス階層をたどっていくと、 Rubyにおけるすべてのクラスは最終的にスーパークラスを持たないBasicObjectクラスを継承しています。これが、"Rubyではあらゆるものがオブジェクトである" ということの技術的な意味です。

string_inheritance_ruby_1_9
図4.1 Stringクラスの継承階層

クラスについてもっと深く知るためには、自分自身で作ってみるのが一番です。そこで、Wordクラスを作成し、その中に、ある単語を前からと後ろからのどちらから読んでも同じ (つまり回文になっている) ならばtrueを返すpalindrome?メソッドを作成してみましょう。

>> class Word
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> nil

このクラスとメソッドは以下のように使うことができます。

>> w = Word.new              # Wordオブジェクトを作成する
=> #<Word:0x22d0b20>
>> w.palindrome?("foobar")
=> false
>> w.palindrome?("level")
=> true

もし上の例が少し不自然に思えるならば、勘が鋭いといえます。というのも、これはわざと不自然に書いたからです。文字列を引数に取るメソッドを作るためだけに、わざわざ新しいクラスを作るのは変です。単語文字列なので、リスト4.8のようにWordクラスは Stringクラスを継承するのが自然です (以下のリストを入力する前に、古いWordクラスの定義を消去するために、Railsコンソールをいったん終了してください)。

リスト4.8 コンソールでWordクラスを定義する。
>> class Word < String             # WordクラスはStringクラスを継承する。
>>   # 文字列が鏡文字であればtrueを返す。
>>   def palindrome?
>>     self == self.reverse        # selfは文字列自身を表す。
>>   end
>> end
=> nil

3.1.2でも簡単に説明しましたが、上のコードのWord < Stringは継承のためのRubyの記法です。こうすることで、新しいpalindrome?メソッドだけではなく、Stringクラスで使用できるすべてのメソッドをWordクラスに対しても使用できるようになります。

>> s = Word.new("level")    # 新しいWordを作成し、"level" で初期化する
=> "level"
>> s.palindrome?            # Wordが鏡文字かどうかmethod.
=> true
>> s.length                 # Wordがstringのすべてのメソッドも継承している
=> 5

WordクラスはStringクラスを継承しているので、コンソールを使用してクラス階層を明示的に確認できます。

>> s.class
=> Word
>> s.class.superclass
=> String
>> s.class.superclass.superclass
=> Object

図4.2にこのクラス階層を示します。

word_inheritance_ruby_1_9
図4.2リスト4.8の (組み込みではない) Wordクラスの継承階層

リスト4.8では、単語の文字を逆順にしたものが元の単語と同じであるかどうかのチェックを、Wordクラスの中から自分自身が持つ単語にアクセスすることで行なっていることに注目してください。Rubyでは、selfキーワードを使用してこれを指定することができます。Wordクラスの中では、selfはオブジェクト自身を指します。これはつまり、以下のコードを使用して、

self == self.reverse

単語が回文であるかどうかを確認できるということです13

4.4.3組込みクラスの変更

継承は強力な概念ですが、もし仮に継承を使用せずにpalindrome?メソッドをStringクラス自身に追加する (つまりStringクラスを拡張する) という、より自然な方法を使用することが可能だとしたら、わざわざWordクラスを作らなくてもpalindrome?をリテラル文字列に対して直接実行できるようになるはずです。そんなことが可能なのでしょうか (なお、現在のコードはそのようになっていないため、以下のようにエラーになります)。

>> "level".palindrome?
NoMethodError: undefined method `palindrome?' for "level":String

驚いたことに、Rubyでは組み込みの基本クラスの拡張が可能なのです。Ruby のクラスはオープンで変更可能であり、クラス設計者でない開発者でもこれらのクラスにメソッドを自由に追加することが許されています14

>> class String
>>   # 文字列が回文であればtrueを返す
>>   def palindrome?
>>     self == self.reverse
>>   end
>> end
=> nil
>> "deified".palindrome?
=> true

(Rubyで組み込みクラスにメソッドを追加できるということは実にクールですが、"deified" (=神格化された) という単語が回文になっていることも、それに劣らずクールではないでしょうか。)

組み込みクラスの変更は極めて強力なテクニックですが、大いなる力には大いなる責任が伴います (訳注: 「スパイダーマン」の名台詞)。従って、真に正当な理由がない限り、組み込みクラスにメソッドを追加することは無作法であると考えられています。Railsの場合、組み込みクラスの変更を正当化できる理由がいくつもあります。たとえば、Web アプリケーションでは、変数が絶対に空白にならないようにしたくなることがよくあります (ユーザー名などはスペースやその他の空白文字になって欲しくないものです) ので、Railsはblank?メソッドをRuby に追加しています。Railsの拡張は自動的にRailsコンソールにも取り込まれるので、以下のようにコンソールで拡張の結果を確認できます (注意: 以下のコードは純粋な irb では動作しません)。

>> "".blank?
=> true
>> "      ".empty?
=> false
>> "      ".blank?
=> true
>> nil.blank?
=> true

スペースが集まってできた文字列は空 (empty) とは認識されませんが、空白 (blank) であると認識されていることがわかります。ここで、nilは空白と認識されることに注意してください。nilは文字列ではないので、Railsが実はblank?メソッドをStringクラスではなく、そのさらに上の基底クラスに追加していることが推測できます。その基底クラスとは、(この章の最初で説明した) Object自身です。RailsによってRubyの組み込みクラスに追加が行われている例については、8.2.1で説明します。

4.4.4コントローラクラス

これまでクラスや継承について説明してきましたが、これらの話は前の章にもあったような気がします。それもそのはずで、StaticPagesコントローラで継承やクラスについて触れたことがありました (リスト3.16)。

class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

本書をここまで進めてきた今であれば、上のコードの意味は (たとえ漠然とであっても) 理解できるはずです。StaticPagesControllerクラスはApplicationControllerを継承しており、homeメソッド、helpメソッド、aboutメソッドを備えています。 Rails コンソールは、セッションごとにローカルのRails環境を読み込むので、コンソール内で明示的にコントローラを作成したり、そのクラス階層を調べたりすることができます15

>> controller = StaticPagesController.new
=> #<StaticPagesController:0x22855d0>
>> controller.class
=> StaticPagesController
>> controller.class.superclass
=> ApplicationController
>> controller.class.superclass.superclass
=> ActionController::Base
>> controller.class.superclass.superclass.superclass
=> ActionController::Metal
>> controller.class.superclass.superclass.superclass.superclass
=> AbstractController::Base
>> controller.class.superclass.superclass.superclass.superclass.superclass
=> Object

継承の関係を図4.3に示します。

static_pages_controller_inheritance
図4.3 StaticPagesコントローラの継承階層。

Railsコンソールでは、その中からコントローラのアクション (実はメソッド) を呼ぶこともできます。

>> controller.home
=> nil

ここでは、homeアクションの中身は空なのでnilが返されます。

ここで重要な点があります。Railsのアクションには戻り値がありません。少なくとも、返される値は重要ではありません。第3章で示したとおり、homeアクションはWebページを表示するためのものであり、値を返すためのものではありませんでした。しかも、第3章では一度もStaticPagesController.newを実行しませんでした。どうしてこれでうまくいっているのでしょうか。

実は、Railsは確かにRubyで書かれていますが、既にRubyとは別物なのです。Railsのクラスは、普通のRubyオブジェクトと同様に振る舞うものもありますが、多くのクラスにはRailsの魔法の粉が振りかけられています。Railsは独特であり、 Rubyとは切り離して学習する必要があります。このため、とにかくWebアプリケーションを書けるようになりたい方は、最初にRailsを学び、次にRubyを学んでから再びRailsに戻ってくることをお勧めします。

4.4.5ユーザークラス

最後に完全なクラスを作成して、この章を終わりにしましょう。そこで、第6章で使用するUserクラスを最初から作成することにします。

これまではコンソール上でクラスを定義しましたが、このような面倒な作業はもう行いたくありません。これからは、アプリケーションのルートディレクトリにexample_user.rbファイルを作成し、そこにリスト4.9のように書くことにします。

リスト4.9 example_userで使用するコード。
example_user.rb
class User
  attr_accessor :name, :email

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@name} <#{@email}>"
  end
end

上のコードはこれまでよりもやや複雑になっていますので、順に見ていくことにします。以下の最初の行は、

  attr_accessor :name, :email

ユーザー名とメールアドレスに対応するアトリビュートアクセサをそれぞれ作成します。このコードは、2.2.2でも説明したように、@nameおよび@emailインスタンス変数について、取り出し(get) と割り当て(set) を行う "ゲッター" と "セッター" というメソッドをそれぞれ作成します。Railsでは、インスタンス変数を作成するだけでビューで自動的に使えるようになるという点に主な利用価値がありますが、一般的には、インスタンス変数はRubyのそのクラス内のどこでも利用できるようにしたい変数として使われます (これについては後で詳しく説明します)。インスタンス変数は常に@記号で始まり、未定義の状態では値がnilになります。

最初の行にあるinitializeは、Rubyの特殊なメソッドです。これは User.newを実行すると自動的に呼び出されるメソッドです。この場合のinitializeメソッドは、以下のようにattributesという引数を1つ取ります。

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

上のコードで、attributes変数は空のハッシュをデフォルトの値として持つため、名前やメールアドレスのないユーザーを作ることができます (4.3.3を思い出してください。存在しないキーに対してハッシュはnilを返すので、:nameキーがなければattributes[:name]nil になり、同じことがattributes[:email]にも言えます)。

最後に、formatted_emailメソッドを定義しましょう (4.2.2)。このメソッドは、文字列の式展開を利用して、@name@emailに割り当てられた値をユーザーのメールアドレスとして構成します。

  def formatted_email
    "#{@name} <#{@email}>"
  end

@ 記号によって示されているとおり、@name@emailは両方ともインスタンス変数なので、自動的にformatted_emailメソッドで使えるようになります。

Railsコンソールを起動し、example_userのコードをrequireして、自作したクラスを試しに使ってみましょう。

>> require './example_user'     # example_user のコードを読み込む方法
=> ["User"]
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # attributes[:name]がnilなのでnilが返される
=> nil
>> example.name = "Example User"           # nil でない名前を代入
=> "Example User"
>> example.email = "user@example.com"      # nil でないメールアドレスを代入
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"

上のコードで、requireのパスにある’.’は、Unixの “カレントディレクトリ” (現在のディレクトリ) を表し、’./example_user’というパスは、カレントディレクトリからの相対パスでexample_userファイルを探すようにRubyに指示します。次のコードでは空のexample_userを作成します。次に、対応する属性にそれぞれ手動で値を代入することで、名前とメールアドレスを与えます (リスト4.9attr_accessorを使用しているので、アトリビュートアクセサを使用して代入できます)。以下のコードは、

example.name = "Example User"

@name変数に"Example User"という値を設定します。同様にemail属性にも値を設定します。これらの値はformatted_emailメソッドで使用されます。

4.3.4では、最後のハッシュ引数の波括弧を省略できることを説明しました。それと同じ要領でinitializeメソッドにハッシュを渡すことで、属性が定義済みの他のユーザーを作成することができます。

>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #<User:0x225167c @email="mhartl@example.com",
@name="Michael Hartl">
>> user.formatted_email
=> "Michael Hartl <mhartl@example.com>"

第7章 では、 ハッシュ引数を使用してオブジェクトを初期化します。これは一般にマスアサインメント (mass assignment) と呼ばれる技法で、Railsアプリケーションで多用されています。

4.5最後に

以上で、Ruby言語の概要の説明を終わります。第5章では、この章で学んだ内容をサンプルアプリケーションの開発に活かしていきます。

4.4.5で作成したexample_user.rbファイルは今後使用することはありませんので、削除してください。

$ rm example_user.rb

その他の変更はリポジトリにコミットしましょう。

$ git add .
$ git commit -m "Add a full_title helper"

4.6演習

  1. リスト4.10のコードにある2つの疑問符を、それぞれ適切なメソッドに置き換えて、与えられた文字列の文字をシャッフルする関数を作成してください。ヒント: splitメソッド、shuffleメソッド、joinメソッドを組み合わせてみましょう。
  2. リスト4.11を参考にして、上で作成したshuffleメソッドを Stringクラスに追加してください。
  3. person1person2person3という3つのハッシュを作成してください。それぞれのハッシュには:firstキーと:lastキーを与え、さらにそれぞれのキーに名前と名字を値として割り当ててください。次にparamsハッシュを作成し、params[:father]person1params[:mother]person2、そしてparams[:child]person3になるようにしてください。最後に、params[:father][:first]などが正しい値を持っていることを確認してください。
  4. Ruby API のオンラインマニュアルを見つけて、Hashクラスのmergeメソッドについて読んでみてください。
  5. (自由課題) Ruby Koans16 (訳注: TDDでRubyを学べる無料のコンテンツ) をやってみて、Rubyの感覚を掴んでみてください。
リスト4.10 文字列をシャッフルする関数の骨組み。
>> def string_shuffle(s)
>>   s.split('').?.?
>> end
=> nil
>> string_shuffle("foobar")
リスト4.11 shuffleメソッドをStringクラスに追加するための骨組み。
>> class String
>>   def shuffle
>>     self.split('').?.?
>>   end
>> end
=> nil
>> "foobar".shuffle
  1. あるヘルパーが特定のコントローラでのみ使用するものであれば、それに対応するヘルパーファイルに置く必要があります。たとえばStaticPagesコントローラ用ヘルパーは、通常app/helpers/static_pages_helper.rbになります。今回の場合、full_titleヘルパーはサイトのすべてのページで使用することを前提にしていますが、Railsにはこのような場合のための特別なヘルパーファイルapp/helpers/application_helper.rbがあります。
  2. “foo”と“bar”という言葉の起源の詳細については、Jargon Fileの “foo” の項を参照してください。特に“foobar”は“FUBAR” (=めちゃくちゃ) とは何の関係もないということについて。
  3. PerlやPHPに精通した開発者であれば、"foo $bar"のようなドル記号による自動的な挿入を連想して比較することでしょう。
  4. この章の全体にわたって、関数という言葉とメソッドという言葉が混在していることを前もってお詫びいたします。Rubyでは関数とメソッドには何の違いもありません。すべてのメソッドは関数であり、すべての関数はメソッドでもあります。それもこれも、あらゆるものがオブジェクトであるからです。
  5. とはいうものの、まだ理解のおよばない点が1つ残っています。Railsがこれらすべてを結びつけている方法です。URLがどのようにしてアクションにマップされているのか、full_titleヘルパーをどのようにしてビューで使用できるようにしているのかなどは、この時点では明かされていません。これは実に興味深いテーマであり、意欲のある方はぜひ自分で調べてみてください。ただし、Railsがどのように動いているかを正確に知らなくても、Railsを使用するうえでは何の問題もありません (この点を深く理解したい方には、「The Rails 4 Way」(Obie Fernandez著) がお勧めです) 。
  6. このコードで使用しているsecondメソッドは、実はRuby自身の一部ではなく、Railsが追加したものです。このコードが動作するのは、RailsによるRubyの拡張がRailsコンソールによって自動的に反映されるからです。
  7. なおエキスパート向けには、ブロックがクロージャになっているということを知っていただくと理解しやすいと思います。クロージャとは、データを伴う、その場限りの無名関数です。
  8. 実はRuby 1.9では、ハッシュの要素の順序が入力順と同じであることを保証していますが、ハッシュを特定の順序に依存してカウントするのは得策ではありません。
  9. 余計なものを削ぎ落した結果、シンボル同士の比較を容易に行えます。文字列は1文字ずつ比較する必要がありますが、シンボルは一度に全体を比較できます。これはハッシュのキーとして理想的な性質です。
  10. 改行は、行の末尾と次の行の始まりを示します。コードの中では、\nの文字列で表します。
  11. もちろん、人間がいちいち文字数を数えていたら頭がどうにかなってしまいます。だからこそ、多くのテキストエディタにはこれらを支援する機能が備わっています。たとえば、図1.1をもう一度見てみると、コードを80文字以下に抑えるための小さな縦線が右側に見えます (実際には少し余裕を持たせて78列にしてあります) 。TextMateを使用していれば、View > Wrap Column > 78で設定できます。Sublime Textを使用していれば、View > Ruler > 78、またはView > Ruler > 80で設定できます。
  12. このメソッドの動作は、使用しているRubyのバージョンによって異なる可能性があります。この例ではRuby 1.9.3を前提としています。
  13. Rubyのクラスやselfキーワードについての詳細は、RailsTipsの “Ruby におけるクラスとインスタンス変数” (英語) を参照してください。
  14. JavaScriptに精通している方のために補足すると、この機能は組み込みクラスのプロトタイプオブジェクトを使用してクラスを拡張することと似ています (読者のErik Eldridgeによる指摘に感謝します)。 
  15. これらの階層にあるクラスの詳細を知る必要はないと思います。私ですらそれらのクラスの詳細について知らないことがたくさんありますし、それでも私は2005年からRuby on Railsで問題なくプログラミングできています。これは (a) 私がよほど無能であるか、(b) Railsの内部を知りつくさなくても熟練したRails開発者になれる、ということのどちらかでしょう。私のためにも読者の皆様のためにも、後者であることを祈ります。
  16. http://rubykoans.com/ 
第4章 Rails風味のRuby Rails 4.0 (第2版)