Ruby on Rails チュートリアル

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

古い過去アーカイブ版を表示しています。史料として残してありますが、既に更新が止まっておりセキュリティ上の問題もあるため、学習で使うときは最新版をご利用ください。

第3版 目次

第4章 Rails風味のRuby

この章では、第3章で使用した例を基に、Railsにおいて重要となるRubyのさまざまな要素について探っていくことにしましょう。Rubyは巨大な仕様を持つ言語ですが、幸い、Rails開発者にとって必要な知識は比較的少なくて済みます。また、一般のRuby入門書で扱っている内容とも多少異なっています。この章の目的は、「Rails風味のRuby」というものについての確固たる基盤を、皆さんのこれまでの言語経験に関わらず提供することです。この章には多くの話題が盛り込まれていますが、一度読んだだけで理解する必要はまったくありません。今後もこの章には頻繁に立ち戻って参照します。

4.1 動機

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

前章の終わりでは、Railsのレイアウトを使用してビューでの重複を取り除くために、ほぼ静的なページを単に更新したにとどまりました (リスト4.1)。これは、リスト3.32と同じものです。

リスト4.1: サンプルアプリケーションのWebサイトのレイアウト app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</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のタイトル行の部分に注目しましょう。

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

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

<% provide(:title, "Home") %>
<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="http://www.railstutorial.org/">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
      page_title + " | " + base_title
    end
  end
end

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

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

上のコードを以下に置き換えます。

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

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

リスト4.3: full_titleヘルパーを使ったWebサイトのレイアウト GREEN 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ページのタイトル確認用にテストを更新する RED test/controllers/static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionController::TestCase
  test "should get home" do
    get :home
    assert_response :success
    assert_select "title", "Ruby on Rails Tutorial Sample App"
  end

  test "should get help" do
    get :help
    assert_response :success
    assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get :about
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end
end

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

リスト4.5: RED
$ bundle exec rake test
3 tests, 6 assertions, 1 failures, 0 errors, 0 skips

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

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

この時点で、テストはパスするはずです。

リスト4.7: GREEN
$ bundle exec rake test

注意: これまではrake testを実行した結果の一部 (成功結果や失敗結果など) も載せていましたが、紙幅の関係から、今後は実行結果を省略します。

Rails開発経験者にとっては、リスト4.2のコードはスタイルシートをインクルードするのと大差ない単純なものですが、ここにもRubyの重要な概念が多数含まれています。モジュール、メソッド定義、任意のメソッド引数、コメント、ローカル変数の割り当て、論理値 (boolean)、制御フロー、文字列の結合、そして戻り値です。これらの概念についても、この章ですべて説明します。

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

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

クラウドIDEをご利用の場合は、オススメのirbの設定があります。シンプルなテキストエディタ「nano」を使って、ホームディレクトリに「 .irbrc」ファイルを作ってみましょう (リスト4.8)。

$ nano ~/.irbrc

リスト4.8の設定を使用すると、irbのプロンプトがより簡潔な表示に置き換えられ、irbの邪魔な自動インデント機能がオフになります。

リスト4.8: irbの設定ファイルを追加する ~/.irbrc
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:AUTO_INDENT_MODE] = false

上の設定はあくまでオススメなので、リスト4.8を追加しなくても、以下のように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
>> ""         # 空の文字列
=> ""
>> "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"     # 文字列の画面出力
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 \\."

最後にもう一度申し上げます。ほとんどの場合、シングルクォートとダブルクォートのどちらを使おうと大きな違いはありません。実際、一般のソースコードでは、明確な理由もなく両者が混用されているケースをよく見かけます。以上でRubyの文字列に関する説明は終わりです。あ、言い忘れていたことがありましたね。「Rubyの世界へようこそ!」

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

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

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

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

オブジェクトに渡すメッセージは、一般にはメソッドと呼ばれます。メソッドの実体は、そのオブジェクトに定義された関数です4。文字列は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"

条件文を2つ以上含めたい場合は、elsif (else + if) という文を使います。

>> if s.nil?
>>   "The variable is nil"
>> elsif s.empty?
>>   "The string is empty"
>> elsif s.include?("foo")
>>   "The string includes 'foo'"
>> end
=> "The string includes 'foo'"

論理値は、&&||!演算子によって結合することもできます。それぞれ「and」「or」「not」という予約語で書くこともできます (訳注: Rubyでは、後者のandとorは、前者の&&と||演算子より処理の優先順位が低くなっています)。

>> 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: undefined method `empty?' for nil:NilClass
>> 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での条件式が真のときにだけ実行される式 (後続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の2つしかありません。なお、「!!」(「バンバン (bang bang)」と読みます) という演算子を使うと、そのオブジェクトを2回否定することになるので、どんなオブジェクトも強制的に論理値に変換できます。

>> !!nil
=> false

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

>> !!0
=> true

4.2.4 メソッドの定義

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

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

最後の例を見ると分かるように、メソッドの引数を省略することも可能です (かっこですら省略可能です)。これは、以下のコードでは

def string_message(str = '')

引数にデフォルト値を含めているからです (この例のデフォルト値は空の文字列です)。このように指定すると、str変数に引数を渡すことも渡さないこともできます。引数を渡さない場合は、指定のデフォルト値が自動的に使用されます。

ここで、Rubyの関数には「暗黙の戻り値がある」ことにご注意ください。これは、関数内で最後に評価された式の値が自動的に返されることを意味します (訳注: 関数で戻り値を明示的に指定しなかった場合の動作です)。この場合、引数のstrが空かどうかに応じて、2つのメッセージ文字列のうちのいずれかを返します。もちろん、Rubyでは戻り値を明示的に指定することもできます。以下の関数は上の関数と同じ結果を返します。

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

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

メソッドで引数の変数名にどんな名前を使っても、メソッドの呼び出し側には何の影響も生じないという点にもご注目ください。つまり、最初の例のstrを別の変数名 (the_function_argumentなど) に変更しても、メソッドの呼び出し方は全く同じです。

>> def string_message(the_function_argument = '')
>>   if the_function_argument.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.

4.2.5 title ヘルパー、再び

これで、full_titleヘルパー (リスト4.2) のコードを理解するための準備が整いました5。コメントを使って、各行の振る舞いに注釈を加えてみました (リスト4.9)。

リスト4.9: 注釈付きのtitle_helper. 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
      page_title + " | " + base_title                 # 文字列の結合
    end
  end
end

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

4.3 他のデータ構造

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

4.3.1 配列と範囲演算子

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

Rubyの文字列の理解にだいぶ時間を使ってしまいましたので、次に進むことにします。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]               # Rubyでは角かっこで配列にアクセスする
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # 配列の添字はマイナスにもなれる!
=> 17

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

>> 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.empty?
=> false
>> a.include?(42)
=> true
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
>> a
=> [42, 8, 17]

上のどのメソッドを実行した場合にも、a自身は変更されていないという点にご注目ください。配列の内容を変更したい場合は、そのメソッドに対応する「破壊的」メソッドを使用します。破壊的メソッドの名前には、元のメソッドの末尾に「!」を追加したものを使用するのがRubyの慣習です。

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

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

>> 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]                         # 添字に-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チュートリアルではRuby共通の慣習に従って、短い1行のブロックには波かっこを使用し、長い1行や複数行のブロックにはdo..end記法を使用しています。

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

今度はiの代わりにnumberを使用していることにご注目ください。この変数 (ブロック変数) の名前は固定されていません。

ブロックは見た目よりもずっと奥が深く、ブロックを十分に理解するためには相当なプログラミング経験が必要です。そのためには、ブロックを含むコードをたくさん読みこなすことでブロックの本質を会得する以外に方法はありません8。幸い、人間には個別の事例を一般化する能力というものがあります。ささやかですが、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メソッドは、与えられたブロックを配列や範囲オブジェクトの各要素に対して適用し、その結果を返します。また、後半の2つの例では、mapのブロック内で宣言した引数 (char) に対してメソッドを呼び出しています。こういったケースでは省略記法が一般的で、以下のように書くこともできます。

>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]
>> %w[A B C].map(&:downcase)
=> ["a", "b", "c"]

(メソッド名にシンボルが使われているので奇妙に見えるかもしれません。これについては4.3.3で説明します)。ひとつ面白い話があります。これは実は元々Ruby on Rails独自の記法でした。しかし多くの人がこの記法を好むようになったので、今ではRubyのコア機能として導入されています。

最後のブロックの例として、単体テストにも目を向けてみましょう (リスト4.4)。

test "should get home" do
  get :home
  assert_response :success
  assert_select "title", "Ruby on Rails Tutorial Sample App"
end

ここでは動作をすみずみまで理解する必要はありません (実際、筆者もこのコードをひと目で完璧に把握できるなどとは言いません)。ここで重要なのは、テストコードにdoというキーワードがあることに気付き、そこからテストの本体が「そもそもブロックでできている」ことに気付くことです。すなわち、このtestメソッドは文字列 (説明文) とブロックを引数にとり、テストが実行されるときにブロック内の文が実行される、ということが理解できます。

ところで、1.5.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]       # 配列の冒頭7つの要素を取り出す
=> ["f", "w", "i", "a", "h", "p", "c", "x"]
>> ('a'..'z').to_a.shuffle[0..7].join  # 取り出した要素を結合してひとつの文字列にする
=> "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"}

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

角かっこを使ってハッシュの要素をひとつづつ定義する代わりに、以下のようにキーと値を=>でリテラル表現するほうが簡単です (この記号はハッシュロケットと呼ばれます)。

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

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

ここまではハッシュのキーに文字列を使用していましたが、Railsのハッシュキーでは文字列よりもシンボルの方が広く使用されています。シンボルは文字列と似ていますが、クォートで囲む代わりにコロンが前に置かれている点が異なります。たとえば、:nameはシンボルです。もちろん、シンボルを「余分なものを削ぎ落した軽量な文字列」とみなしても構いません10

>> "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ではシンボルをふんだんに使用しているので、すぐに慣れるでしょう。ただし文字列と違って、利用できない記号がある点にご注意ください

>> :foo-bar
NameError: undefined local variable or method `bar' for main:Object
>> :2foo
SyntaxError

一般的な英文字を使っている限り、シンボルの名前で不自由することはないでしょう。

ハッシュのキーとしてシンボルを採用する場合、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コミュニティでも人気が高まっています。どちらの記法もよく使われているので、両方の見分けがつくことが重要です。ただ最初は少し見分けづらいのも事実です。たとえば:nameは単独でシンボルとして使えますが、name:はその後ろに引数がなければ意味がありません。以下のコードの:name =>name:は、ハッシュとしてのデータ構造は全く同じです。つまり、

{ :name => "Michael Hartl" }

{ name: "Michael Hartl" }

というコードは等価になります (一般的には省略記法が好まれますが、明示的に接頭にコロンをつけてシンボル (:name) であることを強調するという考え方もあります)。

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

リスト4.10: ハッシュの中のハッシュ
>> 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:dangerという 2つの状態を持つ flash という名前のハッシュについて考えてみましょう。

>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", :danger=>"It failed."}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :danger 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を使用することは非常によくあることなので、 p関数というショートカットがあります11

>> 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

ハイフン (-) が入っているためにエラーが発生してしまいます。(4.3.3で、シンボルにハイフンが使えなかったことを思い出してください) このため、以下のような旧式のハッシュロケット記法を使用するしかないのです。

'data-turbolinks-track' => true

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

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

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

従って、

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

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

リスト 4.11: インクルードされた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メソッドを呼び出します14

>> 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
=> :palindrome?

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

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

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

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

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

>> s = Word.new("level")    # 新しいWordを作成し、"level" で初期化する
=> "level"
>> s.palindrome?            # Wordが鏡文字かどうかを調べるメソッド
=> 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.12の (組み込みではない) Wordクラスの継承階層。

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

self == self.reverse

単語が回文であるかどうかを確認できるということです15。なお、Stringクラスの内部では、メソッドや属性を呼び出すときのself.も省略可能です。

self == reverse

といった省略記法でも、うまく動きます。

4.4.3 組込みクラスの変更

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

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

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

>> 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.4で説明します。

4.4.4 コントローラクラス

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

class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

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

>> 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とは切り離して学習する必要があります。

4.4.5 ユーザークラス

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

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

リスト4.13: 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.23.6でも説明したように、@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 のコードを読み込む方法
=> true
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # attribuributes[:name]は存在しないのでnil
=> nil
>> example.name = "Example User"           # 名前を代入する
=> "Example User"
>> example.email = "user@example.com"      # メールアドレスを代入する
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"

上のコードで、requireのパスにある’.’は、Unixの “カレントディレクトリ” (現在のディレクトリ) を表し、’./example_user’というパスは、カレントディレクトリからの相対パスでexample_userファイルを探すようにRubyに指示します。次のコードでは空のexample_userを作成します。次に、対応する属性にそれぞれ手動で値を代入することで、名前とメールアドレスを与えます (リスト4.13attr_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

その他の変更はリポジトリにコミットしましょう。その後、Bitbucketにプッシュし、Herokuにデプロイしましょう。

$ git status
$ git commit -am "Add a full_title helper"
$ git push
$ bundle exec rake test
$ git push heroku

4.5.1 本章のまとめ

  • Rubyは文字列を扱うためのメソッドを多数持っている
  • Rubyの世界では、すべてがオブジェクトである
  • Rubyではdefというキーワードを使ってメソッドを定義する
  • Rubyではclassというキーワードを使ってクラスを定義する
  • Railsのビューでは静的なHTMLと埋め込みRuby(ERb)が使える
  • Rubyの組み込みクラスには配列、範囲、ハッシュなどがある
  • Rubyのブロックは (他の似た機能と比べ) 柔軟な機能で、添え字を使ったデータ構造よりも自然にイテレーションができる
  • シンボルとはラベルである。追加的な構造を持たない (代入などができない) 文字列みたいなもの。
  • Rubyではオブジェクトを継承できる
  • Rubyでは組み込みクラスですら内部を見たり修正したりできる
  • 「“deified”」という単語は回文である

4.6 演習

: 『演習の解答マニュアル (英語)』にはRuby on Railsチュートリアルブックのすべての演習の解答が掲載されており、www.railstutorial.orgでチュートリアルを購入いただいた方には無料で付属します。

  1. リスト4.14のコードにあるすべての疑問符を、それぞれ適切なメソッドに置き換えて、与えられた文字列の文字をシャッフルする関数を作成してください。ヒント: splitメソッド、shuffleメソッド、joinメソッドを組み合わせてみましょう。
  2. リスト4.15を参考にして、上で作成したshuffleメソッドを Stringクラスに追加してください。
  3. person1person2person3という3つのハッシュを作成してください。それぞれのハッシュには:firstキーと:lastキーを与え、さらにそれぞれのキーに名前と名字を値として割り当ててください。次にparamsハッシュを作成し、params[:father]person1params[:mother]person2、そしてparams[:child]person3になるようにしてください。最後に、params[:father][:first]などが正しい値を持っていることを確認してください。
  4. Ruby API のオンラインマニュアルを見つけて、Hashクラスのmergeメソッドについて読んでみてください。では、次の式の値は何ですか?
      { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
    
リスト 4.14: 文字列をシャッフルする関数の骨組み。
>> def string_shuffle(s)
>>   s.?('').?.?
>> end
>> string_shuffle("foobar")
=> "oobfra"
リスト 4.15: shuffleメソッドをStringクラスに追加するための骨組み。
>> class String
>>   def shuffle
>>     self.?('').?.?
>>   end
>> end
>> "foobar".shuffle
=> "borafo"
  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ヘルパーをビューで利用できるようにする方法などがそうです。(この点を深く理解したい方には、「The Rails 4 Way」(Obie Fernandez著) がお勧めです) 。
  6. 文字列の式展開は魅力的です。実際、以前のチュートリアルではこれを多用していました。しかし、 provideメソッドは内部で文字列オブジェクトをSafeBufferオブジェクトに変換してしまうので、注意が必要です。文字列オブジェクトではないため、式展開をビューの中で使うと多くのエスケープ処理が実行されます。たとえば「Help’s on the way」は「Help&amp;#39;s on the way」といった具合にエスケープされます (この問題を指摘いただいたJeremy Fleischmanに感謝します)。
  7. このコードで使用しているsecondメソッドは、実はRuby自身の一部ではなく、Railsが追加したものです。このコードが動作するのは、RailsによるRubyの拡張がRailsコンソールによって自動的に反映されるからです。
  8. なおエキスパート向けには、ブロックがクロージャになっているということを知っていただくと理解しやすいと思います。クロージャとは、データを伴う、その場限りの無名関数です。
  9. 実はRuby 1.9以降では、ハッシュの要素の順序が入力順と同じであることを保証していますが、ハッシュを特定の順序に依存してカウントするのは得策ではありません。
  10. シンボルは文字列に比べてはるかに軽量なので、シンボル同士の比較を高速に行えます。文字列の比較は1文字ずつ行われる必要がありますが、シンボルでは瞬時に全体を比較できます (訳注: シンボルは内部的にはむしろ「固定桁の数値」であり、異なるシンボル同士の数値は重複しないことが保証されます)。これはハッシュのキーとして理想的な性質です。
  11. 実際には些細な違いがあり、pメソッドは画面出力だけでなく返り値もオブジェクトになります。しかし、putsメソッドの場合は引数によらず必ずnilが返り値になります。(指摘してくれたKatarzyna Siwekに感謝します。)
  12. 改行は、行の末尾と次の行の始まりを示します。コードの中では、\nの文字列で表します。
  13. もちろん、人間がいちいち文字数を数えていたら頭がどうにかなってしまいます。だからこそ、多くのテキストエディタにはこれらを支援する機能が備わっています。たとえば、図1.5をもう一度見てみると、コードを80文字以下に抑えるための小さな縦線が右側に見えます。1.2.1で紹介したCloud IDEでは、デフォルトでこのような行が含まれます。TextMateを使用していれば、View > Wrap Column > 78で設定できます。Sublime Textを使用していれば、View > Ruler > 78、またはView > Ruler > 80で設定できます。
  14. このメソッドの動作は、使用しているRubyのバージョンによって異なる可能性があります。この例ではRuby 1.9.3以上のバージョンを前提としています。
  15. Rubyのクラスやselfについてもっと詳しく知りたい場合は、RailsTipsに投稿された “Rubyにおけるクラスとインスタンス変数” (英語) を参照してください。
  16. これらの階層にあるクラスの詳細を知る必要はないと思います。私ですらそれらのクラスの詳細について知らないことがたくさんありますし、それでも私は2005年からRuby on Railsで問題なくプログラミングできています。これは (a) 私がよほど無能であるか、(b) Railsの内部を知りつくさなくても熟練したRails開発者になれる、ということのどちらかでしょう。私のためにも読者の皆様のためにも、後者であることを祈ります。
前の章
第4章 Rails風味のRuby Rails 4.2 (第3版)
次の章