<h1>
Railsチュートリアル 第4回報告
</h1>
2019/12/19 yuukari
<h2>トピックブランチを作成しよう</h2>
作業を行うときはmaster以外のブランチで作業する癖をつけること!
```
$ git checkout -b rails-flavored-ruby
```
<h2>
組み込みヘルパー
</h2>
> サンプルアプリケーションのレイアウト
> **app/views/layouts/application.html.erb**
```
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
```
```
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track' => true %>
```
・Railsの組込み関数<font color="red">stylesheet_link_tag</font>で<font color
="red">application.css</font>をすべてのメディアタイプをインクルードしている(全てのファイルで適用させている)。
<h2>関数を自作する</h2>
新しく自作した関数を<font color="red">カスタムヘルパー</font>という。ここでは<font color="red">full_title</font>ヘルパーというものを作成した。
> 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
```
> full_titleヘルパーを使ったWebサイトのレイアウト
> **app/views/layouts/application.html.erb**
```
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
```
> **app/views/static_pages/home.html.erb**
```
<% provide(:title, "Home") %>
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="http://www.railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</p>
```
<h2>コメント表示</h2>
Rubyのコメントは「#」から始まりその行の終わりまでがコメント扱いされる。
```
# ページごとの完全なタイトルを返す ←この行がコメント
def full_title(page_title = '')
.
.
.
end
```
<h2>
文字列
</h2>
文字列は「"(ダブルクォート)」で文字を囲むことで作成できる。
```
$ rails console
>> "" # 空の文字列
=> ""
>> "foo" # 空ではない文字列
=> "foo"
```
+演算子を使って文字列を結合することも出来る。
```
>> "foo" + "bar" # 文字列の結合
=> "foobar"
```
<h2>式展開</h2>
変数を<font color="red">#{}</font>で囲むことで、その変数に文字の値を代入することが出来る。
```
>> first_name = "Michael"
=> "Michael"
>> "#{first_name} Hartl"
=> "Michael Hartl"
```
<h2>
出力
</h2>
Rubyでの文字列の出力としては<font color="red">puts(プットエス)</font>が一般的。
```
>> puts "foo"
foo
=> nil
```
nilはRuby特有の返し値で「何もない」の意。
<h2>ダブルクォーテーションとシングルクォーテーション</h2>
文字列をシングルクォーテーションで囲んだ時、ダブルクォーテーションで囲んだ時とは違い式展開は行われない。
```
$ "#{foo}bar"
=> "foobar"
$ '#{foo}bar'
=> "\#{foo}bar"
```
また、ダブルクォーテーションで囲んだ文字列内で「#」等の文字を使用したい場合、「\」or「¥」を文字の前に付ける必要がある(<font color="red">エスケープシーケンス</font>)。
```
>> '\n'
=> "\\n"
```
<h2>オブジェクト</h2>
Rubyは文字列やnilなどあらゆるものがオブジェクトとされており、メッセージに反応するものである。
* length:文字列の数を問うメッセージ
```
>> "foobar".length
=> 6 #foobarは6文字なので「6」という値が返ってくる
```
オブジェクトに渡すメッセージを<font color="red">メソッド</font>と呼び、その実体はオブジェクトに定義された関数である。
* empty?:文字列が空かどうかを問うメッセージ
```
>> "foobar".empty?
=> false
>> "".empty?
=> true
```
Rubyではメソッド末尾に「?」を置くことでそのメソッドが論理値を返すことを示している。(empty「?」など)
* elsif(else+if):条件文を2つ含める時に使う
```
>> if s.nil?
>> "The variable is nil"
>> elsif s.empty?
>> "The string is empty"
>> elsif s.include?("foo")
>> "The string includes 'foo'"
>> end
=> "The string includes 'foo'"
```
論理値は、「&(and)」,「||(or)」,「!(not)」演算子で結合可能。
```
>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Both strings are empty" if x.empty? && y.empty?
=> nil
>> puts "One of the strings is empty" if x.empty? || y.empty?
"One of the strings is empty"
=> nil
>> puts "x is not empty" if !x.empty?
"x is not empty"
=> nil
```
* to_s:オブジェクトを文字列に変換する
```
>> nil.to_s
=> ""
```
nilもオブジェクトであるため、空の文字列に変換される。
* メソッドチェーン
```
>> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
>> nil.to_s.empty?
=> true
#nil自体は文字列ではないためempty?に反応しないが、to_sで空の文字列に変換した後であればtrueを返す
```
このようにメソッドの後にメソッドを繋げることを<font color="red">メソッドチェーン</font>という。
<h2>配列</h2>
配列とは特定の順序を持つ要素のリストである。<font color="red">split</font>メソッドを使うことで文字列を自然に変換した配列を生成できる。
```
>> "foo bar baz".split
=> ["foo", "bar", "baz"]
```
他の多くの言語と同じく、Rubyの要素のインデックスも「0」から始まる。そしてRubyの配列には「-(マイナス)」を指定してアクセスすることも出来る(この場合「-1」がリストの一番最後の要素に相当し、そこから左側に進んでいく)。
```
>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]
=> 17
```
配列は多くのメソッドに応答する。
```
>> a
=> [42, 8, 17]
>> a.empty?
=> false
>> a.include?(42)
=> true
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
>> a
=> [42, 8, 17]
```
上記の中ではaそのものの中身の順番は変わっていない。もし中身を変えたい場合はメソッドの末尾に「!」を付け加え、<font color="red">破壊的メソッド</font>にする。
```
>> a
=> [42, 8, 17]
>> a.sort! #破壊的メソッド
=> [8, 17, 42]
>> a
=> [8, 17, 42]
```
<font color="red">push</font>メソッドを使用すれば配列の中身に要素を追加できる。
```
>> a.push(6)
=> [42, 8, 17, 6]
>> a << 7
=> [42, 8, 17, 6, 7]
>> a << "foo" << "bar"
=> [42, 8, 17, 6, 7, "foo", "bar"]
```
<font color="red">join</font>メソッドは、splitメソッドと異なり配列をすべて連結することが出来る。
```
>> a
=> [42, 8, 17, 7, "foo", "bar"]
>> a.join
=> "428177foobar"
>> a.join(', ')
=> "42, 8, 17, 7, foo, bar"
```
<h2>
範囲
</h2>
範囲を示す時は<font color="red">「○○..××」</font>を使って表す。
```
$ (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
$ ('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"]
```
上では0から9の範囲を指定して配列化、下ではaからzの範囲を指定して配列化を行っている。
範囲の最後に「-1」を指定すれば配列の長さを知らなくても自動で最後の要素まで指定してくれるから便利。
```
>> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..(a.length-1)]
=> [2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..-1]
=> [2, 3, 4, 5, 6, 7, 8, 9]
```
<h2>ブロック</h2>
ブロックは、簡単に言えば引数の塊である。
```
>> (1..5).each { |i| puts 2 * i } #{}内がブロック
2
4
6
8
10
=> 1..5
```
||で囲まれている変数でブロックを操作する。
ブロックは、1行で済む短いもの場合は{}で囲み、2行以上のものはdo~endでまとめる。
* mapメソッド
与えられたブロックを配列や範囲オブジェクトの各要素に対して適用し、結果を返す。
```
>> 3.times { puts "Betelgeuse!" }
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }
=> [1, 4, 9, 16, 25]
>> %w[a b c]
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]
>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]
```
<h2>ハッシュとシンボル</h2>
ハッシュは<font color="red">キー(値を入れる入れ物)</font>と<font color="red">バリュー(値そのもの)</font>という2つの要素を持っている。
```
>> user = {}
=> {}
>> user["first_name"] = "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"
=> "Hartl"
>> user["first_name"]
=> "Michael"
>> user
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}
```
ハッシュでは、配列と違って<font color="red">要素の並び順は全く意識されない。</font>
シンボルとは、<font color="red">文字列のように見える整数</font>である。
```
"name" #文字列
:name #シンボル
```
文字列とはこのように表記の仕方が違う。文字列よりも比較や検索時の速度において有利なので、ハッシュのキーを指定するときに使う。
```
>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]
=> "Michael Hartl"
>> user[:password]
=> nil
```
<h2>
クラス継承
</h2>
クラス階層を調べる際、<font color="red">superclass</font>メソッドを使用すると親クラスを辿ることが出来る。
```
>> s = String.new("foobar")
=> "foobar"
>> s.class
=> String
>> s.class.superclass
=> Object
>> s.class.superclass.superclass
=> BasicObject
>> s.class.superclass.superclass.superclass
=> nil
```
このようにすれば、元々存在するクラスを自作のクラスに継承させることが出来る。
```
>> class Word < String 。
>> def palindrome?
>> self == self.reverse
>> end
>> end
=> nil
```
<h3>演習(1)</h3>
1. city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入してください。
A.
```
$ prefecture="Tokyo"
=>"Tokyo"
$ city="Hachioji"
=>"Hachioji"
```
2. 先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。
A.
```
$ puts "#{prefecure} prefecture #{city} city"
Tokyo prefecture Hachioji city
```
3. 上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)
A.
```
$ puts "#{prefecture}\tprefecture\t#{city}\tcity"
Tokyo prefecture Hachioji city
```
4. タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
A. 「\t」がタブではなく「\t」のまま出力された
<h3>演習(2)</h3>
1. "racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
A.
```
$ "racecar".length
=>7
```
2. reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
A.
```
$ "racecar".reverse
=>racecar
```
3. 変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
A.
```
$ s="racecar"
s==s.reverse
=>true
```
4. リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)
A. sに"racecar"が入った状態では「It's a palindrome!」と表示され、"onomatopoeia"と入った状態では何も表示されず「nil」が帰ってきた
<h3>演習(3)</h3>
1. リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。
A.
```
$ def palindrome_tester(s)
$ if s==s.reverse
$ puts "It's a palindrome!"
$ else
$ puts "It's not a palindrome."
$ end
$ end
```
2. 上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。
A.
```
$ puts palindrome_tester("racecar")
It's a palindrome! //回文である
=> nil
$ puts palindrome_tester("onomatopoeia")
It's not a palindrome. //回文でない
=> nil
```
3. palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。
A.
```
$ puts palindrome_tester("racecar").nil?
It's a palindrome!
true
=> nil //戻り値nil
```
<h3>演習(4)</h3>
1. 文字列「A man, a plan, a canal, Panama」を ", " で分割して配列にし、変数aに代入してみてください。
A.
```
$ a="A man, a plan, a canal, Panama".split(', ')
=> ["A man", "a plan", "a canal", "Panama"]
```
2. 今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。
A.
```
$ s=a.join
=> "A mana plana canalPanama"
```
3. 変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。
A.
```
$ s.split(' ').join
=> "AmanaplanacanalPanama"
$ puts palindrome_tester(s)
It's not a palindrome.
=> nil
$ s.downcase
=> "a mana plana canalpanama"
```
4. aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)
A.
```
$ e=('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"]
$ e[7]
=> "h"
$ e[-7]
=> "t"
```
<h3>演習(5)</h3>
1. 範囲オブジェクト0..16を使って、各要素の2乗を出力してください。
A.
```
$ (0..16).map{|d| d**2}
=> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256]
```
2. yeller (大声で叫ぶ) というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。
A.
```
$ def yeller(s)
$ s.map(&:upcase).join
$ end
=> :yeller
$ yeller(['o','l','d'])
=> "OLD"
```
3. random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。
A.
```
$ def random_subdomain
$ ('a'..'z').to_a.shuffle[0..7].join
$ end
```
4. リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。
A.
```
$ def string_shuffle(s)
$ s.split('').shuffle.join
$ end
```
<h3>演習(6)</h3>
1. キーが'one'、'two'、'three'となっていて、それぞれの値が'uno'、'dos'、'tres'となっているハッシュを作ってみてください。その後、ハッシュの各要素をみて、それぞれのキーと値を"'#{key}'のスペイン語は'#{value}'"といった形で出力してみてください。
A.
```
$ number={:one=>"uno", :two=>"dos", :three=>"tres"}
$ number.each do |key,value|
$ puts "'#{key}'のスペイン語は'#{value}'"
$ end
```
2. person1、person2、person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値 (名前など) を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。1.) キーparams[:father]の値にperson1を代入、2). キーparams[:mother]の値にperson2を代入、3). キーparams[:child]の値にperson3を代入。最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)
A.
```
$ person1={:first=>"Takeshi", :last=>"Kato"}
=> {:first=>"Takeshi", :last=>"Kato"}
$ person2={:first=>"Masako", :last=>"Kato"}
=> {:first=>"Masako", :last=>"Kato"}
$ person3={:first=>"Kenta", :last=>"Kato"}
=> {:first=>"Kenta", :last=>"Kato"}
$ params={}
=> {}
$ params[:father]=person1
=> {:first=>"Takeshi", :last=>"Kato"}
$ params[:mother]=person2
=> {:first=>"Masako", :last=>"Kato"}
$ params[:child]=person3
=> {:first=>"Kenta", :last=>"Kato"}
$ params[:father][:first]==person1[:first]
=> true
```
3. userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。
A.
```
$ user={:name=>"Jinno Shuma", :email=>"c01171576b@edu,teu,ac,jp", :pasword_digest=>('a'..'z').to_a.shuffle[0..15].join}
```
4. Ruby API (訳注: もしくはるりまサーチ) を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。
A.
推測:selfとotherで同じキーが使われており、ブロック付きではないのでotherの値が上書きされ、a=100,b=300となる
結果:
```
$ {"a"=>100, "b"=>200}.merge({"b"=>300})
=> {"a"=>100, "b"=>300}
```
<h3>演習(7)</h3>
1. 1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか? (復習です)
A.
` $ (1..10).to_a`
2. 今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント: newメソッドに2つの引数を渡す必要があります
A.
```
$ Range.new(1,10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
3. 比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。
A.
```
$ Range.new(1,10).to_a==(1..10).to_a
=> true
```
<h3>演習(8)</h3>
1. Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください。
A. Range < Class < Module < Object < BasicObject
2. リスト 4.15にあるself.reverseのselfを省略し、reverseと書いてもうまく動くことを確認してみてください。
A.
```
$ class Word < String
$ def palindrome?
$ self==reverse
$ end
$ end
$ s.palindrome?
=> true
```
<h3>演習(9)</h3>
1. palindrome?メソッドを使って、“racecar”が回文であり、“onomatopoeia”が回文でないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか? ヒント: downcaseメソッドで小文字にすることを忘れないで。
A.
```
$ "racecar".palindrome?
=> true
$ "onomatopoeia".palindrome?
=> false
$ "Malayalam".downcase.palindrome?
=> true
```
2. リスト 4.16を参考に、Stringクラスにshuffleメソッドを追加してみてください。ヒント: リスト 4.12も参考になります。
A.
```
$ class String
$ def shuffle
$ self.split('').shuffle.join
$ end
$ end
```
3. リスト 4.16のコードにおいて、self.を削除してもうまく動くことを確認してください。
A.
```
$ class String
$ def shuffle
$ split('').shuffle.join
$ end
$ end
=> :shuffle
$ "foobar".shuffle
=> "ofabro"
```
<h3>演習(10)</h3>
1. 第2章で作ったToyアプリケーションのディレクトリでRailsコンソールを開き、User.newと実行することでuserオブジェクトが生成できることを確認してみましょう。
A.
```
$ User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```
2. 生成したuserオブジェクトのクラスの継承階層を調べてみてください。
A. User < Class < Module < Object < BasicObject
<h3>演習(11)</h3>
1. Userクラスで定義されているname属性を修正して、first_name属性とlast_name属性に分割してみましょう。また、それらの属性を使って "Michael Hartl" といった文字列を返すfull_nameメソッドを定義してみてください。最後に、formatted_emailメソッドのnameの部分を、full_nameに置き換えてみましょう (元々の結果と同じになっていれば成功です)
A.
```
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. "Hartl, Michael" といったフォーマット (苗字と名前がカンマ+半角スペースで区切られている文字列) で返すalphabetical_nameメソッドを定義してみましょう。
A.
```
def alphabetical_name
"#{@last_name}, #{@first_name}"
end
```
3. full_name.splitとalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうか確認してみましょう。
A.
```
$ user.full_name.split==user.alphabetical_name.split(', ').reverse
=> true
```
{"metaMigratedAt":"2023-06-15T02:29:52.546Z","metaMigratedFrom":"Content","title":"\nRailsチュートリアル 第4回報告\n","breaks":true,"contributors":"[{\"id\":\"be22ebc6-b157-44e4-a310-abf54e48ef32\",\"add\":0,\"del\":64},{\"id\":\"9a073587-93a1-4741-b293-9b3c057fbae5\",\"add\":24775,\"del\":8790}]"}