####第4章
##Rails風味のRuby
###動機###
まずはトピックブランチを作り、そこで変更をコミットしていく。
$ git checkout -b rails-flavored-ruby
###組み込みヘルパー###
######リスト4.1:サンプルアプリケーションのレイアウト######
<!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ヘルパーを定義する######
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######
<!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######
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######
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######
<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の設定アイルを追加する######
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:AUTO_INDENT_MODE] = false
最後に、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
に!なんで?こんなところでつまずいてるの私だけなんだろう。
まぁ進めてみる。
最後にもう一度申し上げられる。ほとんどの場合、シングルクォートとダブルクォートのどちらを使おうと大きな違いはありません。実際、一般のソースコードでは明確な理由もなく混用されてる。そうだ。
###演習
-
city
変数に適当な市区町村を、prefecture
(訳:県)変数に適当な都道府県を代入してください。
解:rails consoleでやってみるってだけかな。
2.6.3 :003 > city = "大津市"
=> "大津市"
2.6.3 :004 > prefecture = "滋賀県"
=> "滋賀県"
- 先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力には
puts
を使ってください。
解:puts
を使うって所と式展開ってことは#{を使えって事でしたっけね。スペースも一つ入れることを忘れずに。
2.6.3 :005 > puts = "#{city} #{prefecture}"
=> "大津市 滋賀県"
- 上記の文字列の間にある半角スペースをタブに切り替えてみてください。(ヒント」改行文字と同じで、タブも特殊文字です。)
解:最初タブって何?から始まった。tabキーで入るスペースより広いやつって解釈でやります。
タブを入れる方法がわからないので、google先生に相談。→\t
を式展開したら良いのかな。
2.6.3 :018 > puts = city + "\t" + prefecture
=> "大津市\t滋賀県"
2.6.3 :019 > puts "#{city}" + "\t" + "#{prefecture}"
大津市 滋賀県
=> nil
ちょっと、間違えたりしてる。
- タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
解: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
######演習
- "racecar"の文字列の長さはいくつですか?
length
メソッドを使って調べてみてください。
解:.lengthってつけたら良いと思う。
2.6.3 :009 > "racecar".length
=> 7
-
reverse
メソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
解:そのまま"racecar".reverseでいけるかな。
2.6.3 :010 > "racecar".reverse
=> "racecar"
いけた。
- 変数
s
に"racecar"を代入してください。その後、比較演算子(==
)を使って変数s
とs.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回ミスってる
- リスト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
###演習###
- リスト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
- 上で定義したメソッドを使って"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
-
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.######
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"]
###演習 「やっと」
- 文字列「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"]
あってる?
- 今度は、変数
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"
あってる?
- 変数
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!
-
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"
演習
- 範囲オブジェクト
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
できてる。
-
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"
-
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"
- リスト4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:
split
、shuffle
、join
メソッドを組み合わせると、メソッドに渡された文字列(引数)をシャッフルさせることができます。
リスト 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
メソッドというショートカットがある。
:name
=> :name
「なんと」
###演習
- キーが
'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"}
-
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 >
-
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"}
「どう?」
- 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から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
- 今度は
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」)としてしまった」
- 比較演算子
==
を使って、上記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ではあらゆるものがオブジェクトである"ということの技術な意味です。
クラスについての理解を深めるためには、自分でクラスを作成してみるのが一番です。そこで、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にこのクラスに階層に示します。
Railsチュートリアルより
リスト4.15の(組み込みではない)Wordクラスの継承階層
リスト 4.15では、単語の文字を逆順にしたものが元の単語と同じであるかどうかのチェックをWord
クラスの中から自分自身が持つ単語にアクセスすることで行っていることにご注目ください。Rubyでは、self
キーワードを使ってこれを指定することができます。Word
クラスの中では、self
はオブジェクト自身を指します。これはつまり、次のコードを使って、
self == self.reverse
単語が回文であるかどうかを確認できるということです。なお、Stringクラスの内部では、メソッドや属性を呼び出すときのself
も省略可能です。
self == reverse
といった省略記法でも、うまく動きます。
######演習######
- 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 >
- リスト4.15にある
self.reverse
のself
を省略し、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で説明します。
###演習
-
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
- リスト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"
- リスト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のコードが説明できるようになります。今回はStaticPagesController
はApplicationController
を継承して定義されるhome
やhelp
、about
アクションを見ていきます。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に示します。
Railsチュートリアルより
リスト4.15では、単語の文字を逆順にしたものが元の単語と同じであるかどうかのチェックをWord
クラスの中から自分自身が持つ単語にアクセスすることで行っていることにご注目ください。Rubyでは、self
キーワードを使ってこれを指定することができます。Word
クラスの中では、self
はオブジェクト自身を指します。これはつまり、次のコードを使って、
self == self.reverse
単語が回文であるかどうかを確認できるということです。なお、Stringクラスの内部では、メソッド属性を呼び出すときのself.
も省略可能です。
self == reverse
といった省略記法でも、うまく動きます。
###演習
- 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>"
###演習
- 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 >
- "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 >
-
full_name.split
とalphabetical_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/'
「できてない」