LoginSignup
1
0

More than 1 year has passed since last update.

Rails tutorial 第6版 第4章学習メモ

Last updated at Posted at 2021-09-02

第4章

Rails風味のRuby

動機

まずはトピックブランチを作り、そこで変更をコミットしていく。
$ git checkout -b rails-flavored-ruby

組み込みヘルパー

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

  <body>
    <%= yield %>
  </body>
</html>

上記の内下記の行はRailsの組み込み関数stylesheet_link_tagを使ってapplication.cssをすべてのメディアタイプで使えるようにしている。ここには混乱を生じる可能性のあるRubyの概念が4つある。Railsの組み込み関数、カッコを使わないメソッドの呼び出し、シンボル、ハッシュ、これらの概念をすべて説明されるということ。

<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>

カスタムヘルパー

リスト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="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
  sample application.
</p>

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

| Ruby on Rails Tutorial Sample App

これは基本タイトルとしては正しいけど、先頭に余分な|が残ってしまう。
ページタイトルが正しく表示されない問題を解決するにはfull_titleというヘルパーを作成することで、ページタイトルが定義されてない場合は基本タイトルを返し、定義されている場合は基本タイトルに縦棒とページタイトルを追加して返すようにする。

リスト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>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application',
                               'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
#リスト 4.4:Homeページのタイトル確認用にテストを更新する red
test/controllers/static_pages_controller_test.rb
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "Ruby on Rails Tutorial Sample App"
  end

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

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

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

リスト 4.4:Homeページのタイトル確認用にテストを更新する red
test/controllers/static_pages_controller_test.rb
require 'test_helper'

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

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

  test "should get about" do
    get static_pages_about_url
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end
end
リスト4.5:RED
ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rails test
.
.
FAIL["test_should_get_home", #<Minitest::Reporters::Suite:0x000055d8d6be2300 @name="StaticPagesControllerTest">, 1.3027184089996808]
 test_should_get_home#StaticPagesControllerTest (1.30s)
        <Ruby on Rails Tutorial Sample App> expected but was
        <Home | Ruby on Rails Tutorial Sample App>.
.
.

テストがパスするためには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="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
  sample application.
</p>

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

リスト 4.7:green
ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rails test
Finished in 1.31169s
4 tests, 8 assertions, 0 failures, 0 errors, 0 skip

文字列とメソッド

Rubyを学ぶためのツールとして主にRailsコンソールを使っていく。Railsコンソールはirb(IRB:Interactive RuBy)を拡張して作られているのでRubyのすべての機能を使える。
クラウドIDEを利用の場合、おすすめのirb設定があるらしいので「nano」を使ってホームディレクトリに「.irbrc」ファイルを作るらしい。
ここまででわからない言語多数。
irbのカッコ書きのRuByのBが大文字が気になる。あとnanoって何?急に出てきた。ま、やってみるけど。

$ nano ~/.irbrc

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

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

実行後の画面
新しいビットマップ イメージ.jpg

最後に、Crtl-Xと押してnanoエディタから離脱する。ファイルを保存するかどうか聞かれるので、yキーを押して~/.irbrcファイルを保存する。

これで準備が整ったのでrailsコンソールを起動する。

ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rails console
Running via Spring preloader in process 5503
Loading development environment (Rails 6.0.3)
2.6.3 :001 > 

コンソールはデフォルトではdevelopment(開発)環境で起動する、test(テスト)
環境とproduction(本番)環境もありますが、次章以降で説明されるそうだ。

Railsコンソールは素晴らしい学習ツールであり、自由に探索できる。コンソールの中で何かをしようとも何かを壊すことはないので安心してくださいとのこと。もしRailsコンソールがおかしな挙動になったらCtrl-Cを押してコンソールを強制的に抜け出すことが可能。もしくはCtrl-D。

以前しようしたコマンドはCtrl-Pまたは上矢印で再利用可能。

本チュートリアルでは、Rubyのコメントを使うことがある。コメントはナンバー記号#(「ハッシュマーク」や「オクトソープ」とも呼ばれます。)から始まりその行末までがコメントとして扱われる。適切なコメントはそれを読む人間にとって非常に有用。

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

コメントをコンソール内で入力する人は普通はおらんけど、学習のためにコメントを追加してみる。

ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rails console
Running via Spring preloader in process 5503
Loading development environment (Rails 6.0.3)
2.6.3 :001 > 17+42 #  整数の足し算
 => 59 
2.6.3 :002 > 

文字列

文字列(string)は、Webアプリケーションにおいておそらく最も重要なデータ構造。これはWebページというものが究極的にはサーバーからブラウザに送信された文字列にすぎないため。コンソールで文字列について調べてみる。

ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rails console
Running via Spring preloader in process 8135
Loading development environment (Rails 6.0.3)
2.6.3 :001 > "" #空の文字列
 => "" 
2.6.3 :002 > "foo" #空ではない文字列
 => "foo" 

ここで入力したのは文字列リテラルと呼ばれ"ダブルクォートで囲むことで作成される。
+演算子を使って文字列を結合することもできる。

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

文字列を組み立てるほかの方法として式展開(interpolation)というものがある。#{}という特殊な構文を使う。

2.6.3 :004 > first_name = "Michael" #変数の代入
 => "Michael" 
2.6.3 :005 > "#{first_name} Hart" #文字列の式展開
 => "Michael Hart" 

例えば"Michael"という値をfirst_name変数に代入(assign)し、この変数を"#{first_name} Hartl"という形で文字列の中に埋め込むと、文字列の中でその変数が展開される。同様にして苗字と名前を変数に割り当てる。

2.6.3 :006 > first_name = "Michael"
 => "Michael" 
2.6.3 :007 > last_name = "Hartl"
 => "Hartl" 
2.6.3 :008 > first_name + " " + last_name #苗字と名前の間に空白を入れた結合
 => "Michael Hartl" 
2.6.3 :009 > #{first_name} #{last_name}" #式展開を 使って結合(上と全く同じ)
 => nil 
2.6.3 :010 > "#{first_name} #{last_name}" #式展開を使って結合(上と全く同じ)
 => "Michael Hartl" 

↑ちょっと間違えてる、ダブルクォーテーション忘れでnil返ってきた。

出力

文字列を出力したい(画面に表示したい)ときに、Rubyのメソッドで最も一般的に使われるのはputs、「put string」の略。

2.6.3 :011 > puts "foo" #文字列を出力する
foo
 => nil 

putsメソッドでは副作用が重要な役割を果たします。どういうことかと言うと、puts "foo"は文字列「"foo"」を副作用としてスクリーンに表示しますが、戻り値には「文字通りの無」であるnilを返す。nilは「何もない」ことを表すRubyの特別な戻り値。なお、=>nilという結果は、簡素化のために今後省略するけど、ここメモには出力結果をありのままを記録する予定。

上の例からわかるように、putsを使うと改行文字である\nが自動的に末尾に追加される。printメソッドも同様の出力を行いますが、次のように改行文字を追加しない点がことなる。

2.6.3 :012 > print "foo" #文字列の画面出力(putsと同じだが改行がない)
foo => nil 

上の例が示すようにfooが出力された後に改行されないのでそのまま=>nilが続いてします。
意図的に改行を追加したいときは、「バックスラッシュ(\)+n」(\n)という改行文字を使う。この改行文字とprintを組み合わせると、putsと同じ振る舞いを再現することができる。

2.6.3 :001 > print "foo\n" # puts "foo"と等価
foo
 => nil 
シングルクォート内の文字列

これまではすべてダブルクォート文字列を使っていたけど、Rubyではシングルクォートもサポートしている。ほとんどダブルクォートとシングルクォートの違いはなくほとんど同じ。

2.6.3 :002 > 'foo' # シングルクォートの文字列
 => "foo" 
2.6.3 :003 > 'foo' + 'bar'
 => "foobar" 

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

2.6.3 :004 > '#{foo} bar'
 => "\#{foo} bar" 

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

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

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

これが理解できない、頭が固いのかな。

2.6.3 :013 > '\n'
 => "\\n" 
2.6.3 :014 > '\\n'
 => "\\n" 
2.6.3 :015 > '\\\n'
 => "\\\\n" 
2.6.3 :016 > "\\\n"
 => "\\\n" 
2.6.3 :017 > '\n'
 => "\\n" 

いろいろやってみる。???。なぜだ。シングルクォートで\nを囲むと\\nに!なんで?こんなところでつまずいてるの私だけなんだろう。

まぁ進めてみる。

最後にもう一度申し上げられる。ほとんどの場合、シングルクォートとダブルクォートのどちらを使おうと大きな違いはありません。実際、一般のソースコードでは明確な理由もなく混用されてる。そうだ。

演習

  1. city変数に適当な市区町村を、prefecture(訳:県)変数に適当な都道府県を代入してください。

解:rails consoleでやってみるってだけかな。

2.6.3 :003 > city = "大津市"
 => "大津市" 
2.6.3 :004 > prefecture = "滋賀県"
 => "滋賀県" 
  1. 先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。

解:putsを使うって所と式展開ってことは#{を使えって事でしたっけね。スペースも一つ入れることを忘れずに。

2.6.3 :005 > puts = "#{city} #{prefecture}"
 => "大津市 滋賀県" 
  1. 上記の文字列の間にある半角スペースをタブに切り替えてみてください。(ヒント」改行文字と同じで、タブも特殊文字です。)

解:最初タブって何?から始まった。tabキーで入るスペースより広いやつって解釈でやります。
タブを入れる方法がわからないので、google先生に相談。→\tを式展開したら良いのかな。

2.6.3 :018 > puts = city + "\t" + prefecture
 => "大津市\t滋賀県" 
2.6.3 :019 > puts "#{city}" + "\t" + "#{prefecture}"
大津市  滋賀県
 => nil 

ちょっと、間違えたりしてる。

  1. タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?

解:rails consoleで引き続きやってみると。

2.6.3 :020 > puts "#{city}" + '\t' + "#{prefecture}"
大津市\t滋賀県
 => nil 

式展開されず、そのまま表示されてしまう。よし。

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

Rubyではあらゆるものがオブジェクトです。文字列やnilですらオブジェクトです。「オブジェクトは何であるか」という直感を養う必要がある。

逆に、オブジェクトが何をするかを説明するのは簡単です。オブジェクトとはメッセージに応答するもの。文字列のようなオブジェクトは、例えばlengthというメッセージに応答でき、文字列の文字数を返す。そうです。

2.6.3 :023 > city 
 => "大津市" 
2.6.3 :024 > city.length #文字列に"length"というメッセージを送る
 => 3 

大文字でも3が返ってる。

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

2.6.3 :025 > city.empty?
 => false 
2.6.3 :026 > "".empty?
 => true 

empty?メソッドの末尾にある疑問符にご注目下さい。Rubyでは、メソッドがtrueまたはfalseという論理値(boolean)を返すことを、末尾の疑問符で示す習慣がある。論理値は、特に処理の流れを変更するときに有用です。

2.6.3 :027 > s = "sheep"
 => "sheep" 
2.6.3 :028 > if s.empty?
2.6.3 :029?>   "The string is empty"
2.6.3 :030?>   else
2.6.3 :031?>   "The string is nonemty"
2.6.3 :032?>   end
 => "The string is nonemty" 

rails consoleでは条件分岐は1行ずつ改行して入力したらいける。

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

2.6.3 :033 > if s.nil?
2.6.3 :034?>   "The variable is nil"
2.6.3 :035?>   elsif s.empty?
2.6.3 :036?>   "The string is empty"
2.6.3 :037?>   elsif s.include?("sheep")
2.6.3 :038?>   "The string includes 'sheep'"
2.6.3 :039?>   end
 => "The string includes 'sheep'" 

なお、論理値はそれぞれ &&(and)や||(or)、!(not)オペレーターで表すこともできる。

2.6.3 :040 > x = "sheep" #sheepを使う理由は特にない。
 => "sheep" 
2.6.3 :041 > y = ""
 => "" 
2.6.3 :042 > puts "Both strings are empty" if x.empty? && y.empty?
 => nil 
2.6.3 :043 > puts "One of the strings is empty" if x.empty? || y.empty?
One of the strings is empty
 => nil 
2.6.3 :044 > puts "x is not empty" if !x.empty?
x is not empty
 => nil 

おまけ、xを""(空に)してから再度両方empty?を実行してみた結果。

2.6.3 :045 > x = ""
 => "" 
2.6.3 :046 > puts "Both strings are empty" if x.empty? && y.empty?
Both strings are empty

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

2.6.3 :001 > nil.to_s
 => "" 
2.6.3 :002 >

確かに空文字列が出力された。今度はnilに対してさまざまなメソッドを渡せることを確認します(メソッドチェーン(method chaining)と呼ばれる手法)。

2.6.3 :002 > nil.empty?
Traceback (most recent call last):
        1: from (irb):2
NoMethodError (undefined method `empty?' for nil:NilClass)
2.6.3 :003 > nil.to_s.empty?
 => true 

このように、nilオブジェクト自身はempty?メソッドには応答しないけど、nil.to_sとすると応答することがわかる。
皆さんご推察のとおり(推察してないよ。)、実はnilかどうかを調べるメソッドもある。

2.6.3 :004 > "foo".nil?
 => false 
2.6.3 :005 > "".nil?
 => false 
2.6.3 :006 > nil.nil?
 => true 

次のコードは、

2.6.3 :007 > puts "x is not empty" if !x.empty?

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

はい、急に出てきた。unlessキーワード。コレ知らん。

検索・・・「if」分では条件式が真かどうかで処理を分けますが似た制御構造をもつものとして「unless」分があります。「unless」分は条件式が偽の場合の処理を記述するのに使われます。書式は次の通りです。

unless 条件式 then
  条件式が偽の時に実行する処理
end

なるほど、偽の時にする処理ね。

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

Rubyにおいてnilは特別なオブジェクトです。Rubyのオブジェクトのうちオブジェクトそのものの論理値がfalseになるのは、false自身とnilの2つしかありません。なお、「!!」(「バンバン(bangbang)」と読みます)とう演算子を使うと、そのオブジェクトを2回否定することになるので、どんなオブジェクトも強制的に論理値に変換できる。

2.6.3 :006 > !!nil
 => false 

その他あらゆるRubyのオブジェクトは、ゼロですらtrueです。ゼロではないではない。?!?どゆこと?

2.6.3 :007 > !!0
 => true 
演習
  1. "racecar"の文字列の長さはいくつですか?lengthメソッドを使って調べてみてください。

解:.lengthってつけたら良いと思う。

2.6.3 :009 > "racecar".length
 => 7
  1. reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。

解:そのまま"racecar".reverseでいけるかな。

2.6.3 :010 > "racecar".reverse
 => "racecar" 

いけた。

  1. 変数sに"racecar"を代入してください。その後、比較演算子(==)を使って変数ss.reverseの値が同じであるかどうか、調べてみてください。

解:これもそのままかな、やってみる。

2.6.3 :010 > "racecar".reverse
 => "racecar" 
2.6.3 :011 > s = "racecar"
 => "racecar" 
2.6.3 :012 > s = s.reverse
 => "racecar" 
2.6.3 :013 > s
 => "racecar" 
2.6.3 :014 > s == s.reverse
 => true 

1回ミスってる

  1. リスト4.9を実行すると、どんな結果になるでしょうか?変数sに"onomatopoeia"という文字列を代入するとどうなるでしょうか?(ヒント:上矢印(またはCtrl-Pコマンド)を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)

リスト4.9:簡単な回文(前から読んでも後ろから読んでも同じ)かどうかテストする

2.6.3 :015 > s = "onomatopoeia"
 => "onomatopoeia" 
2.6.3 :016 > puts "It's a palindrome!" if s == s.reverse
 => nil 

回文じゃないのでメッセージが表示されない、回文バージョンもしてみる

2.6.3 :018 > s = "racecar"
 => "racecar" 
2.6.3 :019 > puts "It's a palindrome!" if s == s.reverse
It's a palindrome!
 => nil 

4.2.3 メソッドの定義

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

2.6.3 :001 > def string_message(str = '')
2.6.3 :002?>   if str.empty?
2.6.3 :003?>     "It's an empty string!"
2.6.3 :004?>     else
2.6.3 :005?>     "The string is nonempty."
2.6.3 :006?>     end
2.6.3 :007?>   end
 => :string_message 
2.6.3 :008 > puts string_message("foobar")
The string is nonempty.
 => nil 
2.6.3 :009 > puts string_message("")
It's an empty string!
 => nil 
2.6.3 :010 > puts string_message
It's an empty string!
 => nil 

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

2.6.3 :011 > def string_message(str = '')

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

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

2.6.3 :011 > def string_message(str = '')
2.6.3 :012?>   return "It s an empty string!" if str.empty?
2.6.3 :013?>   return "The string is nonempty."
2.6.3 :014?>   end
 => :string_message 
2.6.3 :015 > puts string_message
It s an empty string!
 => nil 
2.6.3 :016 > puts string_message("foobar")
The string is nonempty.
 => nil 

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

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

2.6.3 :024 > def string_message(the_function_argument = '')
2.6.3 :025?>   if the_function_argument.empty?
2.6.3 :026?>     "It's an empty string!"
2.6.3 :027?>     else
2.6.3 :028?>     "The string is nonempty."
2.6.3 :029?>     end
2.6.3 :030?>   end
 => :string_message 
2.6.3 :031 > puts string_message("")
It's an empty string!
 => nil 
2.6.3 :032 > puts string_message("foobar")
The string is nonempty.
 => nil 

演習

  1. リスト4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかチェックするメソッドを定義してみてください。ヒント:リスト4.9の比較方法を参考にしてください。

解:palindrome_tester(s)の中に引数sがあるのでそれが回文かどうかってすれば良いのだしょう。
FILL_IN←これを多分s == s.reverseとする。

リスト4.10:回文に対する簡単なテスト
2.6.3 :033 > def palindrome_tester(s)
2.6.3 :034?>   if FILL_IN
2.6.3 :035?>     puts "It's a palindrome!"
2.6.3 :036?>     else
2.6.3 :037?>     puts "It's not palindrome."
2.6.3 :038?>     end
2.6.3 :039?>   end
 => :palindrome_tester 
  1. 上で定義したメソッドを使って"racecar"と"onomatopoeia"が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文ではない、という結果になれば成功。

解:s = "racecar"としてやってみて、次にs = "onomatopoeia"でしてみる。

2.6.3 :063 > def palidrome_tester(s)
2.6.3 :064?>   if s == s.reverse
2.6.3 :065?>     puts "It's a palindrome!"
2.6.3 :066?>     else
2.6.3 :067?>     puts "It's not apalindrome."
2.6.3 :068?>     end
2.6.3 :069?>   end
 => :palidrome_tester 
2.6.3 :070 > palindrome_tester("racecar")
Traceback (most recent call last):
        1: from (irb):70
NoMethodError (undefined method `palindrome_tester' for main:Object)
Did you mean?  palidrome_tester
2.6.3 :071 > palidrome_tester("racecar")
It's a palindrome!
 => nil 

↑メソッド名を間違えて定義したので間違えたメソッド名でテストしたらOKでした。

onomatopoeiaでもやってみる。

2.6.3 :072 > palidrome_tester("onomatopoeia")
It's not apalindrome.
 => nil 
  1. palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください(つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト4.10の戻り値を受け取り、その結果を返しているという意味になる。

解:nil?メソッドを呼び出し、、、パッと出てこない。メソッドチェーンって続けて条件を入力するって事だった気がする。

てことは、palindrome_tester("racecar").nil?で良い?やってみる。

2.6.3 :073 > palidrome_tester("racecar").nil?
It's a palindrome!
 => true 

出来た!(相変わらず、メソッド名は間違えたままpalidrome_testerで実行してる。)

4.2.4 titleヘルパー、再び

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

リスト4.11:注釈付きの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サイトのレイアウトで使うコンパクトなヘルパーメソッドでは、メソッド定義、変数割り当て、論理評価、制御フロー、文字列の式展開など、Rubyの様々な要素が投入されています。最後に、module ApplicationHelperという要素について解説します。モジュールは、関連したメソッドをまとめる方法の1つで、includeメソッドを使ってモジュールを読み込むことができます。(ミックスイン(mixed in))とも呼びます)。単なるRubyのコードを書くのであれば、モジュールを作成するたびに明示的に読み込んで使うのが普通ですが、Railsでは自動的にヘルパーモジュールを読み込んでくれるので、include行(訳:含む)をわざわざ書く必要がありません。つまり、このfull_titleメソッドは自動的にすべてのビューで利用できるようになっている、ということです。

「へ~。なんで?わからないけど進むことに。」

4.3 他のデータ構造

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

4.3.1 配列と範囲演算子

配列(array)は、特定の順序を持つ要素のリスト。配列を理解することはハッシュ(4.3.3)やRailsのデータモデルを理解するために重要な基盤。(データモデルとはhas_manyなどの関連付けのことなので2.3.3や13.3.1で詳しく説明する。らしい)

splitメソッドを使うと、文字列を自然に変換した配列を得ることができる。

2.6.3 :001 > "foo bar     baz".split # 文字列を3つの要素を持つ配列に分割する

 => ["foo", "bar", "baz"] 

「本当に、空白の数とかは様々だけど配列に変換された。」

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

2.6.3 :003 > "fooxbarxbaz".split('x')
 => ["foo", "bar", "baz"] 

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

2.6.3 :001 > a = [42,8,17]
 => [42, 8, 17] 
2.6.3 :002 > a[0]
 => 42 
2.6.3 :003 > a[1]
 => 8 
2.6.3 :004 > a[2]
 => 17 
2.6.3 :005 > a[-1]
 => 17 

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

2.6.3 :011 > a # 配列「a」の内容を確認する
 => [42, 8, 17] 
2.6.3 :012 > a.first
 => 42 
2.6.3 :013 > a.second
 => 8 
2.6.3 :014 > a.last
 => 17 
2.6.3 :015 > a.last == a[-1] # == を使って比較する
 => true 

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

2.6.3 :016 > x = a.length #配列も文字列と同様lengthメソッドに応答する
 => 3 
2.6.3 :017 > x == 3
 => true 
2.6.3 :018 > x == 1
 => false 
2.6.3 :019 > x != 1
 => true 
2.6.3 :020 > x >= 1
 => true 
2.6.3 :021 > x < 1
 => false 

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

2.6.3 :022 > a
 => [42, 8, 17] 
2.6.3 :023 > a.empty?
 => false 
2.6.3 :024 > a.include?(42)
 => true 
2.6.3 :025 > a.sort
 => [8, 17, 42] 
2.6.3 :026 > a.reverse
 => [17, 8, 42] 
2.6.3 :027 > a.shuffle
 => [8, 42, 17] 
2.6.3 :028 > a
 => [42, 8, 17] 

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

2.6.3 :040 > a
 => [42, 8, 17] 
2.6.3 :041 > a.sort!
 => [8, 17, 42] 
2.6.3 :042 > a
 => [8, 17, 42] 

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

2.6.3 :045 > a.push(6) #6を配列に追加する
 => [8, 17, 42, 6, 6] 
2.6.3 :046 > a << 7 #7を配列に追加する
 => [8, 17, 42, 6, 6, 7] 
2.6.3 :047 > a << "foo" << "bar"
 => [8, 17, 42, 6, 6, 7, "foo", "bar"] 
2.6.3 :048 > 

最後の列では要素の追加をチェーン(chain)できることを示しました。他の多くの言語の配列と異なり、Rubyでは異なる型が配列の中で共存できる。(上の場合は整数と文字列)。

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

2.6.3 :048 > a
 => [8, 17, 42, 6, 6, 7, "foo", "bar"] 
2.6.3 :049 > a.join #単純に連結する
 => "81742667foobar" 
2.6.3 :050 > a.join(', ') # カンマ+スペースを使って連結する
 => "8, 17, 42, 6, 6, 7, foo, bar" 

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

> 0..9
 => 0..9 
2.6.3 :057 > 0..9.to_a # おっと、9に対してto_aを読 んでしまっていますね
Traceback (most recent call last):
        1: from (irb):57
NoMethodError (undefined method `to_a' for 9:Integer)
Did you mean?  to_c
               to_r
               to_f
               to_i
               to_d
               to_s
2.6.3 :058 > (0..9).to_a # 丸カッコを使い、範囲オブジェクトに対してto_aを呼び出しましょう。
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
2.6.3 :059 > 2.6.3 :048 > a

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

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

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

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

2.6.3 :066 > a = (0..9).to_a
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
2.6.3 :067 > a[2..(a.length-1)] #明示的に配列の長さを使って選択
 => [2, 3, 4, 5, 6, 7, 8, 9] 
2.6.3 :068 > a[2..-1] #添字に-1を使って選択
 => [2, 3, 4, 5, 6, 7, 8, 9] 

次のように、文字列に対しても範囲オブジェクトが使える。

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

演習 「やっと」

  1. 文字列「A man,a plan,a canal,Panama」を","で分割して配列にし、変数aに代入してみてください。

解:変数a = spilitを使って(',')これを末尾に付けたら良いと思う。

2.6.3 :072 > a = "A man, a plan, a canal, Panama".split(',')
 => ["A man", " a plan", " a canal", " Panama"] 

あってる?

  1. 今度は、変数aの要素に連結した結果(文字列)を、変数sに代入してみてください。

解:s = a.joinかな。

2.6.3 :073 > s = a.join
 => "A man a plan a canal Panama" 
2.6.3 :074 > s
 => "A man a plan a canal Panama" 

あってる?

  1. 変数sを半角スペースで分割した後、もう一度連結して文字列にしてください(ヒント:メソッドチェーンを使うと1行でもできます)。リスト4.10で使った回文をチェックするメソッドを使って、(現状ではまだ)変数s回文ではないことを確認してください。downcaseメソッドを使って、s.downcase回文であることを確認してください。

解:s = s.split(' ').joinのあとreversを使って回文かどうかを判定したら良いのか。一度railsconsoleを閉じてしまったんでメソッドが無効になってるんで、再度4.10で使った回文をチェックするメソッドを使って書いてあるのでさかのぼってメソッドをひっぱりだすのかな

2.6.3 :021 > def palidrome_tester(s) #またしてもメソッド名間違い
2.6.3 :022?>   if s == s.reverse
2.6.3 :023?>     puts "It's a palindrome!"
2.6.3 :024?>     else
2.6.3 :025?>     puts "It's not apalindrome."
2.6.3 :026?>     end
2.6.3 :027?>   end
 => :palidrome_tester 
2.6.3 :028 > palidrome_tester(s.downcase.reverse)
It's a palindrome!
  1. aからzまでの範囲オブジェクトを作成し7番目の要素を取り出してみて。同様にして、後ろから7番目の要素を取り出してみて。(ヒント:範囲オブジェクトを配列に変換するのを忘れないで!)

解:(a..z).to_aかな、でもって[-7]を付けたら良いかな。 違った('a'..'z').to_aか。

2.6.3 :035 > array = ('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"] 
2.6.3 :036 > array
 => ["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"] 
2.6.3 :037 > array[7]
 => "h" 
2.6.3 :038 > array[-7]
 => "t" 

ふぅ~い。

4.3.2 ブロック

配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます。ブロックは、Rubyの極めて強力な機能であり、かつ分かりにくい機能でもあります。「たしかに、良くわからない」

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

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

ブロックであることを示すには波カッコで囲いますが、次のようにdoとendで囲んで示すこともできる。

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

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

2.6.3 :001 > (1..5).each do |number|
2.6.3 :002 >     puts 2*number
2.6.3 :003?>   puts '--'
2.6.3 :004?>   end
2
--
4
--
6
--
8
--
10
--
 => 1..5 

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

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

2.6.3 :005 > 3.times { puts "Betelgeuse!" } #3.timesではブロックに変数をつかっていない
Betelgeuse!
Betelgeuse!
Betelgeuse!
 => 3 
2.6.3 :006 > (1..5).map { |i| i**2 } # 「**」記法 では冪乗(べき乗)
 => [1, 4, 9, 16, 25] 
2.6.3 :007 > %w[a b c] # %w で文字列の配列を作成
 => ["a", "b", "c"] 
2.6.3 :008 > #w[a b c].map { |char| char.upcase }
 => nil 
2.6.3 :009 > %w[a b c].map { |char| char.upcase }  
 => ["A", "B", "C"] 
2.6.3 :010 > %w[A B C].map { |char| char.downcase }
 => ["a", "b", "c"] 
2.6.3 :011 > 

「上ちょっと間違えた。」

上に示したように、mapメソッドは、渡されたブロックを配列や範囲オブジェクトの各要素に対して適用し、その結果を返す。また、後半の2つの例では、mapのブロック内で宣言した引数(char)に対してメソッドを呼び出しています。こういったケースでは省略記法が一般的で、次のように書くこともできる。(この記法を"symbol-to-proc"と呼ぶ)。

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

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

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

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

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

ところで、1.5.4でランダムなサブドメイン生成するために次のRubyコードを紹介しましたが、このコードを理解するための準備が整ったので、今こそ読み解いてみよう。

2.6.3 :026 > ('a' .. 'z').to_a.shuffle[0..7].join
 => "smybtiaf" 

順を追ってこのコードを組み立ててみると、動作がよくわかる。

2.6.3 :001 > ('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"] 
2.6.3 :002 > ('a'..'z').to_a.shuffle[0..7] # 配 列の冒頭8つの要素を取り出す
 => ["s", "m", "l", "o", "k", "w", "e", "a"] 
2.6.3 :003 > ('a'..'z').to_a.shuffle[0..7].join # 取り出した要素を結合して1つの文字列にする
 => "bmxnlour"

演習

  1. 範囲オブジェクト0..16を使って、各要素の2乗を出力してください。

解:(1..16).each do |i|
puts i **2
end
かな。違うかも。

2.6.3 :004 > (1..16).each do |i|
2.6.3 :005 >     puts i ** 2
2.6.3 :006?>   end
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
 => 1..16 

できてる。

  1. yeller(大声で叫ぶ)というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結したあと、大文字にして結果を返します。例えばyeller(['o','l','d'])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント:'map'とupcase'と'join'メソッドを使ってみましょう。

解:

2.6.3 :016 > def yeller(s) # 最初にyellerを定義して引数に適当な文字を入れる
2.6.3 :017?>   s.map(&:upcase).join 
2.6.3 :018?>   end
 => :yeller 
2.6.3 :019 > yeller(['o','l','d'])
 => "OLD" 

上に同じ
2.6.3 :024 > def yeller(s)
2.6.3 :025?>   s.map{ |i| i.upcase}.join
2.6.3 :026?>   end
 => :yeller 
2.6.3 :027 > yeller(['o','l','d'])
 => "OLD" 
  1. random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント:サブドメインを作るときに使ったRubyコードをメソッド化したものです。

解:

2.6.3 :033 > def random_subdomain 
2.6.3 :034?>   ('a'..'z').to_a.shuffle[0..7].join #a~zで配列を作成し、それをシャッフルして、結合してる。
2.6.3 :035?>   end
 => :random_subdomain 
2.6.3 :036 > random_subdomain
 => "nvfamcxo" 
  1. リスト4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:splitshufflejoinメソッドを組み合わせると、メソッドに渡された文字列(引数)をシャッフルさせることができます。
リスト 4.12:文字列をシャッフルするメソッド(「?」を置き換えてください)
文字列をシャッフルするメソッド (「?」を置き換えてください)
>> def string_shuffle(s)
>>   s.?('').?.?
>> end
>> string_shuffle("foobar")
=> "oobfra"

解:

2.6.3 :046 > def string_shuffle(s) 
2.6.3 :047?>   s.split('').shuffle.join
2.6.3 :048?>   end
 => :string_shuffle 
2.6.3 :049 > string_shuffle("foobar")
 => "booarf" 

4.3.3 ハッシュとシンボル

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

2.6.3 :006 > user = {} #{}は空のハッシュ
 => {} 
2.6.3 :007 > user["first_name"] = "Michael" # キーが"first_name"で値が"Michael"
 => "Michael" 
2.6.3 :008 > user["last_name"] = "Hartl" # キーが"last_name"で値が"Hartl"
 => "Hartl" 
2.6.3 :009 > user["first_name"] # 要素へのアクセスは配列の場合と似ている
 => "Michael" 
2.6.3 :010 > user["last_name"]
 => "Hartl" 
2.6.3 :011 > user #ハッシュのリテラル表記
 => {"first_name"=>"Michael", "last_name"=>"Hartl"} 

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

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

2.6.3 :012 > user = { "first_name" => "Michael","last_name" => "Hartl" }
 => {"first_name"=>"Michael", "last_name"=>"Hartl"} 

「確かに、ほとんど変わらんけイメージしやすい」

ここではRubyにおける慣習として、ハッシュの最初と最後に空白を追加しています。この空白はあってもなくてもよく、コンソールでは無視される。

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

2.6.3 :013 > "name".split('')
 => ["n", "a", "m", "e"] 
2.6.3 :014 > :name.split('')
Traceback (most recent call last):
        1: from (irb):14
NoMethodError (undefined method `split' for :name:Symbol)
2.6.3 :015 > "foobar".reverse
 => "raboof" 
2.6.3 :016 > :foobar.reverse
Traceback (most recent call last):
        1: from (irb):16
NoMethodError (undefined method `reverse' for :foobar:Symbol)

シンボルは、Ruby以外ではごく一部の言語にしか採用されていない特殊なデータ形式です。最初は奇妙に思うかもしれませんが、Railsではシンボルをふんだんにつかっているので、すぐに慣れるでしょう。ただし、文字列と違って、全ての文字が使えるわけではないことに注意して。

2.6.3 :017 > :foo-bar
Traceback (most recent call last):
        2: from (irb):17
        1: from (irb):17:in `rescue in irb_binding'
NameError (undefined local variable or method `bar' for main:Object)
2.6.3 :018 > :2foo
Traceback (most recent call last):
SyntaxError ((irb):18: syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END)

とはいえ、一般的なアルファベットなどを使っている限りにおいては、シンボルで困ることはないでしょう。

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

2.6.3 :019 > user = { :name => "Michael Hartl",:email => "michael@example.com" }
 => {:name=>"Michael Hartl", :email=>"michael@example.com"} 
2.6.3 :020 > user[:name] # :name に対応する値に アクセスする
 => "Michael Hartl" 
2.6.3 :021 > user[:password] # 未定義のキーに対応する値にアクセスする
 => nil 

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

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

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

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

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

この構成は、JavaScriptなど他の言語のハッシュ記法により近いものになっており、Railsコミュニティも人気が高まっています。どちらの記法もよく使われているので、両方の見分けがつくことが重要です。ただ最初は少し見分けずらいのも事実。例えば:nameはシンボルとして独立していますが、引数を伴わないname:では意味が成り立ちません。次のコードの:name =>name:は、ハッシュとしてのデータ構造は全く同じです。つまり、

{ :name => "Michael Hartl" }

上のコードと、

{ name: "Michael Hartl" }

というコードは等価。あえて接頭にコロンをつけてシンボルであることを強調する考え方もあるそうだ。

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

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

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

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

2.6.3 :029 > flash = { success: "It worked!", danger: "It fails." }
 => {:success=>"It worked!", :danger=>"It fails."} 
2.6.3 :030 > flash.each do |key, value|
2.6.3 :031 >     puts "key #{key.inspect} has value #{value.inspect}"
2.6.3 :032?>   end
key :success has value "It worked!"
key :danger has value "It fails."
 => {:success=>"It worked!", :danger=>"It fails."} 

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

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

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

ところで、オブジェクトを表示するためにinspectを使うことは非常によくあることなので、pメソッドというショートカットがある。

2.6.3 :060 > p :name
:name
=> :name

「なんと」

演習

  1. キーが'one''two''three'となっていて、それぞれの値が'uno''dos''tres'となっているハッシュを作ってみてください。そのあと、ハッシュの各要素をみて、それぞれのキーと値を"'#{key}'はスペイン語で'#{value}'"といった形で出力してみてください。

解:

2.6.3 :006 > number = {:one=> "uno",:two=>"dos",:three=>"tres"}
 => {:one=>"uno", :two=>"dos", :three=>"tres"} 
2.6.3 :007 > number[0]
 => nil 
2.6.3 :008 > number[:one]
 => "uno" 
2.6.3 :009 > puts = "'#{number[:one]}'はスペイン 語で'#{number[:one]}'"
 => "'uno'はスペイン語で'uno'" 
2.6.3 :010 > number.each do |key,value|
2.6.3 :011 >     puts "'#{key}'のスペイン語は'#{value}'"
2.6.3 :012?>   end
'one'のスペイン語は'uno'
'two'のスペイン語は'dos'
'three'のスペイン語は'tres'
 => {:one=>"uno", :two=>"dos", :three=>"tres"} 

  1. person1、person2,person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値(名前など)を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。 1)キーparams[:father]の値にperson1を代入、2)キーparams[:father]の値にperson2を代入、3)キーparams[:child]の値にperson3を代入。最後にハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]person1[:first]と一致しているか確かめてみてください。)

解:

2.6.3 :017 > person1= {:first => "tamio",:last => "okuda"}
 => {:first=>"tamio", :last=>"okuda"} 
2.6.3 :018 > person1
 => {:first=>"tamio", :last=>"okuda"} 
2.6.3 :019 > person1[:last]
 => "okuda" 
2.6.3 :020 > person2 = {:first => "kaori", :last => "okui" }
 => {:first=>"kaori", :last=>"okui"} 
2.6.3 :021 > person3 = {:first => "sinzo" ,:last => "abe"}
 => {:first=>"sinzo", :last=>"abe"} 
2.6.3 :022 > params[:father] = paeson1
Traceback (most recent call last):
        1: from (irb):22
NameError (undefined local variable or method `params' for main:Object)
2.6.3 :023 > params[:father] = person1
Traceback (most recent call last):
        2: from (irb):23
        1: from (irb):23:in `rescue in irb_binding'
NameError (undefined local variable or method `params' for main:Object)
2.6.3 :024 > params = {}
 => {} 
2.6.3 :025 > params[:father] = person1
 => {:first=>"tamio", :last=>"okuda"} 
2.6.3 :026 > params[:mother] = person2
 => {:first=>"kaori", :last=>"okui"} 
2.6.3 :027 > params[:child] = person3
 => {:first=>"sinzo", :last=>"abe"} 
2.6.3 :028 > params[:father][:first] == person1[;first]
Traceback (most recent call last):
SyntaxError ((irb):28: syntax error, unexpected ';', expecting ']')
...s[:father][:first] == person1[;first]
...                              ^
2.6.3 :029 > params[:father][:first] == person1[:first]
 => true 
2.6.3 :030 > 
  1. userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。

解:

password_digest = ("a".."z").to_a.shuffle[0..15].join
 => "gtiehnoqdwypfczkx" 
2.6.3 :044 > user = { :name => "mizuman", :email => "mi@hotmailcom", :password_digest => password_digest }
 => {:name=>"mizuman", :email=>"mi@hotmailcom", :password_digest=>"gtiehnoqdwypfczk"} 

「どう?」

  1. Ruby APIを使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか?推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。
{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })

merge(統合)した結果を返すらしいので

"a" => 100, "b" => 300

「にならへんかな。」

2.6.3 :046 > { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
 => {"a"=>100, "b"=>300} 

「なった。」

4.3.4 CSS、再び

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

<%= stylesheet_link_tag 'application', media: 'all',
                                       'data-turbolinks-track': 'reload' %>

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

stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

上のコードでは、このメソッドを呼んでいます。しかし、ここで不思議な点がいくつもあります。第一に、丸カッコがありません。実は、Rubyでは丸カッコは使用してもしなくても構わん。次の2つの行は等価です。

# メソッド呼び出しの丸カッコは省略可能。
stylesheet_link_tag('application', media: 'all',
                                   'data-turbolinks-track': 'reload')
# 上は以下のように書いても同じ
stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

次に、media引数はハッシュのようですが、波カッコがない点が不思議です。実は、ハッシュがメソッド呼び出しの最後の引数である場合は、波カッコを省略できます。次の2つの行は等価です。

# 最後の引数がハッシュの場合、波カッコは省略可能。
stylesheet_link_tag 'application', { media: 'all',
                                     'data-turbolinks-track': 'reload' }
# 上は以下のように書いても同じ
stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

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

stylesheet_link_tag 'application', media: 'all',
           'data-turbokinks-track': 'reload'

上のコードには途中に改行が含まれているにもかかわらずです。実は、Rubyは改行と空白を区別してない。行を分割した理由は、1行を80文字以内に収めてソースコードを読みやすくするためです。
したがって、

stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

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

リスト4.14:読み込まれたCSSによって生成されたHTMLソース。
<link rel="stylesheet" media="all" href="/assets/application.self-
f0d704deea029cf000697e2c0181ec173a1b474645466ed843eb5ee7bb215794.css?body=1"
data-turbolinks-track="reload" />

4.4 Rubyにおけるクラス

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

4.4.1 コンストラクタ

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

2.6.3 :047 > s = "foobar" # ダブルクォートは実は文 字列のコンストラクタ
 => "foobar" 
2.6.3 :048 > s.class
 => String 

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

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

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

この動作はリテラルコンストラクタと等価ですが、動作の内容が明確にしめされています。
配列でも、文字列と同様にインスタンスを生成できます。

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

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

2.6.3 :055 > h = Hash.new
 => {} 
2.6.3 :056 > h[:foo] #存在しないキー (:foo)の値にアクセスしてみる
 => nil 
2.6.3 :057 > h = Hash.new(0) #存在しないキーのデフ ォルト値をnilから0にする
 => {} 
2.6.3 :058 > h[:foo]
 => 0 

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

演習

  1. 1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか?(復習です。

解:
(1..10).to_a.inspect

2.6.3 :059 > (1..10).to_a.inspect
 => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" 

「あってる?」

違う

2.6.3 :001 > a = 1..10
 => 1..10 
2.6.3 :002 > a.class
 => Range 
  1. 今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント:newメソッドに2つの引数を渡す必要があります。

解:

2.6.3 :005 > b = Range.new(1, 10)
 => 1..10 
2.6.3 :006 > b.class
 => Range 

「最初([1..10」)としてしまった」

  1. 比較演算子 ==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。

解:

2.6.3 :007 > a == b
 => true 

4.4.2 クラス継承

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

2.6.3 :001 > s = String.new("foobar")
 => "foobar" 
2.6.3 :002 > s.class #変数sのクラスを調べる
 => String 
2.6.3 :003 > s.class.superclass # Stringクラスの親 クラスを調べる
 => Object 
2.6.3 :004 > s.class.superclass.superclass # Ruby 1.9からBasicObjectが導入
 => BasicObject 
2.6.3 :005 > s.class.superclass.superclass.superclass
 => nil 

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

新しいビットマップ イメージ.jpg
画像 Railsチュートリアルより引用

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

2.6.3 :016 > class Word
2.6.3 :017?>   def palindrome?(string)
2.6.3 :018?>     string == string.reverse
2.6.3 :019?>     end
2.6.3 :020?>   end
 => :palindrome? 

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

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

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

リスト4.15:コンソールでwordクラスを定義する
2.6.3 :001 > class Word < String # WordクラスはStringクラスを継承する
2.6.3 :002?>   # 文字列が回文であればtrueを返す
2.6.3 :003?>   def palindrome?
2.6.3 :004?>     self == self.reverse # selfは文字 列自身を表します
2.6.3 :005?>     end
2.6.3 :006?>   end
 => :palindrome? 

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

2.6.3 :009 > s = Word.new("level") #新しいWordを作 成し、"level"で初期化する
 => "level" 
2.6.3 :010 > s.palindrome? # Wordが回文かどうかをしらべるメソッド
 => true 
2.6.3 :011 > s.length #WordはStringで扱えるすべてのメソッドを継承している
 => 5 

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

2.6.3 :012 > s.class
 => Word 
2.6.3 :013 > s.class.superclass
 => String 
2.6.3 :014 > s.class.superclass.superclass
 => Object 

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

新しいビットマップ イメージ.jpg

Railsチュートリアルより
リスト4.15の(組み込みではない)Wordクラスの継承階層

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

self == self.reverse

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

self == reverse

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

演習
  1. Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください。

解:

2.6.3 :020 > Symbol.class.superclass
 => Module 
2.6.3 :021 > Symbol.class.superclass.superclass
 => Object 
2.6.3 :022 > Hash.class.superclass
 => Module 
2.6.3 :023 > Hash.class.superclass.superclass
 => Object 
2.6.3 :024 > Range.class.superclass
 => Module 
2.6.3 :025 > Range.class.superclass.superclass
 => Object 
2.6.3 :026 > Range.class.superclass.superclass.superclass
 => BasicObject 
2.6.3 :027 > 

  1. リスト4.15にあるself.reverseselfを省略し、reverseと書いてもうまく動くことを確認してみてください。

解:

2.6.3 :001 > class Word < String
2.6.3 :002?>   def palindrome?
2.6.3 :003?>     self == reverse
2.6.3 :004?>     end
2.6.3 :005?>   end
 => :palindrome? 
2.6.3 :010 > s = Word.new("level")
 => "level" 
2.6.3 :011 > s.palindrome?
 => true 
2.6.3 :012 > s.length
 => 5

4.4.3 組み込みクラスの変更

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

2.6.3 :006 > "level".palindrome?
Traceback (most recent call last):
        1: from (irb):6
NoMethodError (undefined method `palindrome?' for "level":String)

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

2.6.3 :024 > class String
2.6.3 :025?>   def palindrome?
2.6.3 :026?>     self == self.reverse
2.6.3 :027?>     end
2.6.3 :028?>   end
 => :palindrome? 
2.6.3 :029 > "deified".palindrome?
 => true 

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

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

2.6.3 :001 > "".blank?
 => true 
2.6.3 :002 > "     ".empty?
 => false 
2.6.3 :003 > "     ".blank?
 => true 
2.6.3 :004 > nil.blank?
 => true 

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

演習

  1. palindrome?メソッドを使って、"racecar"が回文であり、"onomatopoeia"が回文ではないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか?ヒント:downcaseメソッドで小文字にすることを忘れないで。

解:

2.6.3 :009 > "racecar".palindrome?
 => true 
2.6.3 :010 > "onomatopoeia".palindrome?
 => false 
2.6.3 :011 > "Malayalam".downcase.palindrome?
 => true 
  1. リスト4.16を参考に、Stringクラスにshuffleメソッドを追加してみてください。ヒント:リスト4.12も参考になります。
リスト4.16:Stringクラスにshuffleメソッドを定義する(「?」を適切なメソッドを置き換えてください。)
>> class String
>>   def shuffle
>>     self.?('').?.?
>>   end
>> end
>> "foobar".shuffle
=> "borafo"

解:

>> class String
>>   def shuffle
>>     self.?('').?.?
>>   end
>> end
>> "foobar".shuffle
=> "borafo"
  1. リスト4.16のコードにおいて、self.を削除してもうまく動くことを確認してください。

解:

2.6.3 :006 > class String
2.6.3 :007?>   def shuffle
2.6.3 :008?>     split('').shuffle.join
2.6.3 :009?>     end
2.6.3 :010?>   end
 => :shuffle 
2.6.3 :011 > "foobar".shuffle
 => "oaobfr" 

「うまくいくね。」

4.4.4 コントローラクラス

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

class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

ここまでの説明を経て、ついにRailsのコードが説明できるようになります。今回はStaticPagesControllerApplicationControllerを継承して定義されるhomehelpaboutアクションを見ていきます。RailsコンソールはセッションごとにローカルのRails環境を読み込むので、コンソール内で明示的にコントローラを作成したり、そのクラス階層を調べたりすることができます。この仕組みを使って、早速Railsのコードを調べてみましょう。

2.6.3 :012 > controller = StaticPagesController.new
 => #<StaticPagesController:0x000055a06e2d9588 @_action_has_layout=true, @rendered_format=nil, @_routes=nil, @_request=nil, @_response=nil> 
2.6.3 :013 > controller.class
 => StaticPagesController 
2.6.3 :014 > controller.class.superclass
 => ApplicationController 
2.6.3 :015 > controller.class.superclass.superclass
 => ActionController::Base 
2.6.3 :016 > controller.class.superclass.superclass.superclass
 => ActionController::Metal 
2.6.3 :017 > controller.class.superclass.superclass.superclass.superclass
 => AbstractController::Base 
2.6.3 :018 > controller.class.superclass.superclass.superclass.superclass.superclass
 => Object 

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

新しいビットマップ イメージ.jpg

Railsチュートリアルより

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

self == self.reverse

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

self == reverse

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

演習

  1. Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください。

解:

「できないのでとばす」

4.4.5 ユーザークラス

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

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

「アプリケーションのルートディレクトリっていま作ってるsample_appのルートディレクトリって意味だった、さらに上の環境ディレクトリでこのルビーファイルを作るとrails consoleが起動せず、はまる。」

リスト4.17: 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
2.6.3 :001 > require './example_user' # example_userのコードを読み込む方法
 => true 
2.6.3 :002 > example = User.new
 => #<User:0x00007fa47ce207e8 @name=nil, @email=nil>  # attributes[:name]は存在しないのでnil
2.6.3 :003 > example.name = "Example User" #名前を 代入する
 => "Example User" 
2.6.3 :004 > example.email = "user@example.com" # メールアドレスを代入する
 => "user@example.com" 
2.6.3 :005 > example.formatted_email
 => "Example User <user@example.com>" 
2.6.3 :006 > 
2.6.3 :006 > user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
 => #<User:0x00007fa47ce71440 @name="Michael Hartl", @email="mhartl@example.com"> 
2.6.3 :007 > user.formatted_email
 => "Michael Hartl <mhartl@example.com>" 

演習

  1. Userクラスで定義されているname属性を修正して、first_name属性とlast_name属性に分割してみましょう。また、それらの属性を使って"Michael Hartl"といった文字列を返すfull_nameメソッドを定義してみてください。最後に、formatted_emailメソッドの@nameの部分を、full_nameに置き換えてみましょう(もともとの結果と同じになっていれば成功です。)
class User
  attr_accessor :first_name,:last_name,:email

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

  def full_name
    "#{first_name} #{last_name}"
  end

  def formatted_email
    "#{@full_name} <#{@email}>"
  end
end
2.6.3 :003 > user = User.new(first_name: "Michael",last_name:"Hartl", email: "mhartl@example.com")   
 => #<User:0x00007fa47c91b838 @name=nil, @email="mhartl@example.com"> 
2.6.3 :004 > user.formatted_email => " <mhartl@example.com>" 
2.6.3 :005 > 
  1. "Hartl,Michael"といったフォーマット(苗字と名前がカンマ+半角スペースで区切られている文字列)で返すalphabetical_name `メソッドも定義してみましょう。

「苗字と名前、最初に定義したfull_nameは名前、苗字の順だった、逆になっているのに気がづかなかった。というか説明してよ。」

class User
  attr_accessor :first_name,:last_name, :email

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

  def full_name
    "#{first_name} #{last_name}"
  end


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

  def alphabetical_name
    "#{last_name}, #{first_name}"
  end
end
2.6.3 :001 > require './example_user' # example_userのコードを読み込む方法
 => true 
2.6.3 :002 > user = User.new(first_name:"mizu",last_name:"manjyu",email:"mimimi@hotmail.com")
 => #<User:0x00007fa47cec7bb0 @first_name="mizu", @last_name="manjyu", @email="mimimi@hotmail.com"> 
2.6.3 :003 > user.alphabetical_name => "mizu, manjyu" 
2.6.3 :004 > 
  1. full_name.splitalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうかを確認してみましょう。
2.6.3 :001 > require './example_user' # example_userのコードを読み込む方法
 => true 
2.6.3 :002 > user = User.new(first_name:"mizu",last_name:"manjyu",email:"mimimi@hotmail.com")
 => #<User:0x00005628aa9f8c78 @first_name="mizu", @last_name="manjyu", @email="mimimi@hotmail.com"> 
2.6.3 :003 > user.alphabetical_name.split(', ').reverse
 => ["mizu", "manjyu"] 
2.6.3 :004 > user.full_name.split == user.alphabetical_name.split(', ').reverse
 => true 

4.5最後に

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

4.4.5で作成したexample_user.rbファイルは今後使わないので削除してください。

ubuntu:~/environment/sample_app (rails-flavored-ruby) $ rm example_user.rb

「↑で削除した。」

次にその他の変更はリポジトリにコミットして、masterブランチにマージしましょう。

ubuntu:~/environment/sample_app (rails-flavored-ruby) $ git commit -am "Add a full_title helper"
[rails-flavored-ruby 6fb8ed6] Add a full_title helper
 4 files changed, 18 insertions(+), 19 deletions(-)
ubuntu:~/environment/sample_app (rails-flavored-ruby) $ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
ubuntu:~/environment/sample_app (master) $ git merge rails-flavore-ruby
merge: rails-flavore-ruby - not something we can merge
ubuntu:~/environment/sample_app (master) $ git merge rails-flavored-ruby                              
Updating b082446..6fb8ed6
Fast-forward
 .../application_helper.rb        | 12 ++++++-
 .../layouts/application.html.erb |  2 +-
 .../static_pages/home.html.erb   |  5 ++-
 ...atic_pages_controller_test.rb | 18 +++--------
 4 files changed, 18 insertions(+), 19 deletions(-)
ubuntu:~/environment/sample_app (master) $ 

GitHubのリモートリポジトリにpushしたりHerokuにデプロイする前には、テストスイートを動かして既存の降るまいに影響がないかを念のために確認する習慣をつけておきましょう。

ubuntu:~/environment/sample_app (master) $ rails test
Running via Spring preloader in process 10067
Started with run options --seed 28482

  4/4: [======] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.99710s
4 tests, 8 assertions, 0 failures, 0 errors, 0 skips

確認が終わったらGitHubにプッシュし、

ubuntu:~/environment/sample_app (master) $ git pushUsername for 'https://github.com/mizumanjyu/sample_app.git': mizumanjyu
Password for 'https://mizumanjyu@github.com/mizumanjyu/sample_app.git': 
Counting objects: 13, done.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (13/13), 1.15 KiB | 234.00 KiB/s, done.
Total 13 (delta 9), reused 0 (delta 0)
remote: Resolving deltas: 100% (9/9), completed with 9 local objects.
To https://github.com/mizumanjyu/sample_app.git
   b082446..6fb8ed6  master -> master

最後に、Herokuにデプロイして本章は終了です。

ubuntu:~/environment/sample_app (master) $ git push heroku
Username for 'https://git.heroku.com/polar-mountain-55653.git': akimanjyu@hotmail.com
Password for 'https://akimanjyu@hotmail.com@git.heroku.com/polar-mountain-55653.git': 
remote: !       WARNING:
remote: !       Do not authenticate with username and password using git.
remote: !       Run `heroku login` to update your credentials, then retry the git command.
remote: !       See documentation for details: https://devcenter.heroku.com/articles/git#http-git-authentication
fatal: Authentication failed for 'https://git.heroku.com/polar-mountain-55653.git/'

「できてない」

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0