Ruby on Rails チュートリアルをやってみる:[第4章]Rubyのお勉強編

第4章

チュートリアルrailstutorial.jp

前回marino.hatenadiary.jp


ここではRubyの勉強をしていくようです。
正直ここでは多くのことを詰め込まれるので、軽く目を通して今後出てきたとき、そーいえば説明あったなとここに戻っこれる位の理解ができれば良いと思います。


まずは前回のおさらいをしながら今回の動機についてみていきます。

前回のレイアウトで使われているコードの以下の部分を見ます。

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

この関数はヘルパーと呼ばれます。
そしてタイトルの行の部分の

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

ですが,
もしタイトルをまったく与えていなければ、タイトルが空欄になってしまいます。
実際には

Ruby on Rails Tutorial Sample App |

となってしまい、|が余分です。

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

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>

このヘルパーを定義することで、Homeページにこれまで表示されていた余分な "Home" という単語を表示せず、基本タイトルのみを正しく表示することもできるようになります。

まず、以前のテストコードを更新し、'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

ここでテストを実行し、エラーを確認します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

テストがパスするためにHomeページのビューからprovideの行を削除します。

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>

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



・文字列(string)とメソッド
Ruby を学ぶためのツールとして、主にRailsコンソールを使用することにするそうです。

コンソールを起動するには以下のコマンドを実行します。

$ rails console

1コメント
Rubyのコメントはナンバー記号#から始まり、行の終わりまでコメントとして扱われます。
コンソール内ではコメントを使わないのが普通ですが、学習のためにあえて使ってみましょう。

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


2文字列
今度はコンソールで文字列について調べてみましょう。

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

ここで入力したのは文字列リテラルと呼ばれ、ダブルクォート”で囲むことで作成できます。

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

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

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

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


・出力
文字列を出力するために、Rubyの関数で最も一般的に使われるのはputsです。

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

ここで出て来たnilは”全くない”ことを表すRubyの特別な値です。
同様の出力を行うprintメソッドもありますが、違いとして改行文字を追加しない点が異なります。

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

・シングルクォート内の文字列
Rubyではシングルクォートもサポートしており、ほとんどの場合、ダブルクォートと同じです。

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

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

もうちょっと詳しく触れると以下のような場合のエスケープが必要な文字がある場合はシングルクォートが便利になります。

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


3オブジェクトとメッセージ受け渡し
Rubyではあらゆるものがオブジェクトです。オブジェクトのあらゆる定義は理解が難しく、オブジェクトとは何であるかという直感を時間をかけて養う必要があります。

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

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

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

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

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

>> s = "foobar"
>> if s.empty?
>>   “文字列が空"
>> else
>>   “文字列は空じゃない"
>> end
=> “文字列は空じゃない"

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

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts “両方の文字列が空" if x.empty? && y.empty?
=> nil
>> puts “1つの文字列が空" if x.empty? || y.empty?
“1つの文字列が空"
=> nil
>> puts “Xは空じゃない" if !x.empty?
"Xは空じゃない"
=> 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に対しemptyを行うと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


4メソッドの定義
Railsコンソールでも、前回のhomeアクションや、今回のfull_titleヘルパーと同じ方法でメソッドを定義することができます。普通はコンソールでやるものではないが、今回は学習のためやります。

たとえば、引数を1つ取り、引数が空かどうかに基づいたメッセージを返すstring_messageという関数を定義してみます。

>> def string_message(string)
>>   if string.empty?
>>     “これは空の文字列"
>>   else
>>     “これは空の文字列じゃない"
>>   end
>> end
=> nil
>> puts string_message("")
これは空の文字列
>> puts string_message("foobar")
これは空の文字列じゃない

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

>> def string_message(string)
>>   return “これは空の文字列" if string.empty?
>>   return “これは空の文字列じゃない"
>> end

関数中の最後の表現 “これは空の文字列じゃない" は、returnキーワードがあってもなくても値を返すため、2番目のreturnは無くてもいいです。


5titleヘルパー、再び
ここまでの解説である程度Rubyの知識が身に付いたので、先ほどのfull_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のデータ構造について説明されています。

1配列と範囲演算子

配列 (array) は、特定の順序を持つ要素のリストです。
文字列から配列を得る自然な方法の1つとして、以下のsplitメソッドがあります。

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

この操作によって、3つの文字列からなる配列を得られます。
以下のように他の文字を指定して区切ることもできます。

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

Ruby配列もゼロオリジン(1つ目が0)を採用しています。

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

Rubyでは、角括弧によるアクセス方法以外にも配列の要素にアクセスする方法が提供されています。

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

また、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を使用すると、配列の長さを知らなくても配列の最後の要素を指定することができ、これにより配列を特定の開始位置の要素から最後の要素までを一度に選択することができます。
>> 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"]

2ブロック

配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます。


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

上のコードは、範囲オブジェクトである(1..5)に対してeachメソッドを呼び出します。そのときに、{ |i| puts 2 * i }というブロックを渡します。ブロック変数に対して使用するRubyの構文で|i|のように変数名を縦棒で囲います。

この場合、範囲オブジェクトの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

ブロックは奥が深く、十分に理解するためにはたくさんのコードを読みこなすしかありません。ささやかですが、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"]

以下にランダムなサブドメインを生成するための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"

3ハッシュとシンボル

ハッシュは、本質的には配列と同じですが、インデックスとして整数値以外のものも使用できる点が配列と異なります 。

ハッシュのインデックス (キーと呼ぶのが普通です) は、通常何らかのオブジェクトです。たとえば、以下のように文字列をキーとして使用できます。


>> 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つの重要な違いとして、ハッシュでは要素の「並び順」が保証されないという点があります。



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


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

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


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

ハッシュのキーとしてシンボルを採用する場合、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

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



以下のようにハッシュの値にはほぼ何でも使用することができ、墓のハッシュを使用することすらできます。


>> 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では、このようなハッシュのハッシュ (またはネストされたハッシュ) が大量に使われています。

配列や範囲オブジェクトと同様、ハッシュも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."

最後の例として、便利な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!"



Rubyにおけるクラス

Rubyではあらゆるものがオブジェクトであるということは既に説明しましたが、この節では実際にオブジェクトをいくつか定義してみます。

オブジェクト指向プログラミングの経験がない方にとっては何のことだかわからないと思いますので、いくつかの具体例を示すことにします。



コンストラクタ

実は、これまで示した多くの例の中でも、クラスを使用してオブジェクトのインスタンスを作成してきています。

たとえば、ダブルクォートを使って文字列のインスタンスを作成しましたが、これは文字列のオブジェクトを暗黙で作成するリテラルコンストラクタです。


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

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



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


>> 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のように、インスタンスに対して呼び出すメソッドインスタンスメソッドと呼ばれます。





クラスの継承

クラスについて学ぶとき、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

ここでは、StringクラスのスーパークラスはObjectクラスで、ObjectクラスのスーパークラスはBasicObjectクラスですが、 BasicObjectクラスはスーパークラスを持たないことがわかります。

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



クラスについてもっと深く知るためには、自分自身で作ってみるのが一番です。そこで、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

実はこれは不自然です。

文字列を引数に取るメソッドを作るためだけに、わざわざ新しいクラスを作るのは変です。単語は文字列なので、以下のようにWordクラスは Stringクラスを継承するのが自然です (以下のリストを入力する前に、古いWordクラスの定義を消去するために、Railsコンソールをいったん終了してください)。


>> class Word < String             # WordクラスはStringクラスを継承する。
>>   # 文字列が鏡文字であればtrueを返す。
>>   def palindrome?
>>     self == self.reverse        # selfは文字列自身を表す。
>>   end
>> end
=> nil

上のコードの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



組み込みクラスの変更

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



Rubyでは組み込みの基本クラスの拡張が可能なのです。


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



コントローラクラス

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


class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

ここまで進めてきたのであれば、漠然とでも理解できるはずです。

StaticPagesControllerクラスはApplicationControllerを継承しており、homeメソッド、helpメソッド、aboutメソッドを備えています。 Rails コンソールは、セッションごとにローカルのRails環境を読み込むので、コンソール内で明示的にコントローラを作成したり、そのクラス階層を調べたりすることができます。


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

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



実は、Railsは確かにRubyで書かれていますが、既にRubyとは別物なのです。Railsは独特であり、 Rubyとは切り離して学習する必要があります。このため、とにかくWebアプリケーションを書けるようになりたい方は、最初にRailsを学び、次にRubyを学んでから再びRailsに戻ってくることをお勧めします。





ユーザークラス

最後に完全なクラスを作成して、この章を終わりにします。

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



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

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



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



最後に作成したexample_user.rbファイルは今後使用しないので削除します。


$ rm example_user.rb

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


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





以上で4章は終わりです。

いきなりRubyの勉強をさせられて頭パンパンかも知れないですが、いくら深く理解できたとしても、使ってみないことには身に付きません。

なので、完全にこの章を頭に入れるということではなく、記憶の片隅に置いて、今後使っていく際にこの章を見直しに来れるかが重要だと思います。