##前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者
##基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。
第4章です。この章は冗長な内容だった記憶が…。つべこべ言わずやっていきます。
本日のBGMはこちら。
matryoshka "zatracenie[full album]"
####【4.1.2 カスタムヘルパー メモ】
・新しく作ったメソッド=カスタムヘルパー
・全てのページで使うヘルパー:app/helpers/application_helper.rb に設置
・特定のコントローラだけが使うヘルパー:app/helpers/(該当のコントローラ名).rb に設置
####【4.2.2 文字列 メモと演習】
・putsメソッドは戻り値にnilを返す。改行文字である\nが出力の末尾に追加される。
・printメメソッドは改行文字を追加しない。
・シングルクォート内では式展開できない。入力した文字をエスケープせずに、そのまま保持するときに便利
1. city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入してください。
→ city = "sapporo" prefecture = "hokkaido"(今コンサドーレ札幌の試合観ながらやってるので)
2. 先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。
→ 下記
>> puts prefecture + " " + city
hokkaido sapporo
=> nil
3. 上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)
→ 下記
>> puts prefecture + "\t" + city
hokkaido sapporo
=> nil
4. タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
→ 下記
>> puts prefecture + '\t' + city
hokkaido\tsapporo
=> nil
####【4.2.3 オブジェクトとメッセージ受け渡し 演習】
1. "racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
→ 下記
>> s = "racecar"
=> "racecar"
>> s.length
=> 7
2. reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
→ 下記
>> s.reverse
=> "racecar"
3. 変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
→ もう入れとったわ。下記
>> s == s.reverse
=> true
4. リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)
→ 下記
>> puts "It's a palindrome!" if s == s.reverse
It's a palindrome!
=> nil
>> s = "onomatopoeia"
=> "onomatopoeia"
>> puts "It's a palindrome!" if s == s.reverse
=> nil
####【4.2.4 メソッドの定義 演習】
1. リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。
→ 下記
>> def palindrome_tester(s)
>> if s == s.reverse
>> puts "It's a palindrome!"
>> else
>> puts "It's not a palindrome."
>> end
>> end
=> :palindrome_tester
2. 上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。
→ 下記
>> palindrome_tester("racecar")
It's a palindrome!
=> nil
>> palindrome_tester("onomatopoeia")
It's not a palindrome.
=> nil
3. palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。
→ 下記
>> palindrome_tester("racecar").nil?
It's a palindrome!
=> true
####【4.3.1 配列と範囲演算子 演習】
1. 文字列「A man, a plan, a canal, Panama」を ", " で分割して配列にし、変数aに代入してみてください。
→ 下記
>> a = "A man, a plan, a canal, Panama".split(',')
=> ["A man", " a plan", " a canal", " Panama"]
2. 今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。
→ 下記
>> s = a.join
=> "A man a plan a canal Panama"
3. 変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。
→ ひとまとめにするとこうなる(一回コンソール落としてたからメソッドの再定義めんどくさかったやん!)
>> palindrome_tester(s.split(' ').join.downcase)
It's a palindrome!
=> nil
4. aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)
→ 最初は0から始まるから6ですね。
>> A = ("a".."z").to_a
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
>> A[6]
=> "g"
>> A[-7]
=> "t"
####【4.3.2 ブロック 演習】
1. 範囲オブジェクト0..16を使って、各要素の2乗を出力してください。
→ 下記
>> (0..16).each { |i| puts i**2 }
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 0..16
2. yeller (大声で叫ぶ) というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。
→ 下記。これもっと賢いやり方ないかな。引数に配列じゃなくて文字列入れるだけでいいようにするとか。
>> def yeller(s)
>> s.map(&:upcase).join
>> end
=> :yeller
>> yeller(['o','l','d'])
=> "OLD"
3. random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。
→ 下記
>> def random_subdomain
>> ("a".."z").to_a.shuffle[0..7].join
>> end
=> :random_subdomain
>> random_subdomain
=> "unwdemsp"
4. リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。
→ 下記
>> def string_shuffle(s)
>> s.split('').shuffle.join
>> end
=> :string_shuffle
>> string_shuffle("foobar")
=> "arbfoo"
####【4.3.3 ハッシュとシンボル 演習】
1. キーが'one'、'two'、'three'となっていて、それぞれの値が'uno'、'dos'、'tres'となっているハッシュを作ってみてください。その後、ハッシュの各要素をみて、それぞれのキーと値を"'#{key}'のスペイン語は'#{value}'"といった形で出力してみてください。
→ 下記
>> n = { one: 'uno', two: 'dos', three: 'tres' }
=> {:one=>"uno", :two=>"dos", :three=>"tres"}
>> n.each do |key, value|
?> puts "#{key}のスペイン語は#{value}"
>> end
oneのスペイン語はuno
twoのスペイン語はdos
threeのスペイン語はtres
=> {:one=>"uno", :two=>"dos", :three=>"tres"}
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]と一致しているか確かめてみてください)
→ 下記。それぞれの名前とキーの意味が分かるアナタはガンバサポーター。(2020シーズン第14節のスタメンより)
>> person1 = { first: "Yuki", last: "Yamamoto" }
=> {:first=>"Yuki", :last=>"Yamamoto"}
>> person2 = { first: "Yosuke", last: "Ideguchi" }
=> {:first=>"Yosuke", :last=>"Ideguchi"}
>> person3 = { first: "Shu", last: "Kurata" }
=> {:first=>"Shu", :last=>"Kurata"}
>> params = { anchor: person1, rih: person2, lih: person3 }
=> {:anchor=>{:first=>"Yuki", :last=>"Yamamoto"}, :rih=>{:first=>"Yosuke", :last=>"Ideguchi"}, :lih=>{:first=>"Shu", :last=>"Kurata"}}
>> params[:anchor][:first] == person1[:first]
=> true
3. userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。
→ 下記
>> user = { name: "tk", email: "tk@mail.com", password_digest: ("a".."z").to_a.shuffle[0..15].join }
=> {:name=>"tk", :email=>"tk@mail.com", :password_digest=>"socxlgerjatyinbw"}
4. Ruby API (訳注: もしくはるりまサーチ) を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。
{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })
→ 正直、るりまサーチで調べてもよく分かんないんですよね。日本語が日本語じゃない。なので普通にグーグル先生に頼ると、mergeメソッドは複数のハッシュを結合させるメソッドとのこと。そして、merge「前」のハッシュに、merge「後」のハッシュを結合するのですが、重複するハッシュがある場合は、「後」のハッシュが上書きされます。ということは、上の結果はbが300になるはず。実際の結果は下記。合ってますね。
>> { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
=> {"a"=>100, "b"=>300}
####【4.4.1 コンストラクタ 演習】
1. 1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか? (復習です)
→ 下記
>> r = 1..10
=> 1..10
2. 今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント: newメソッドに2つの引数を渡す必要があります。
→ 下記
>> r2 = Range.new(1,10)
=> 1..10
3. 比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。
→ 下記
>> r == r2
=> true
####【4.4.2 クラス継承 演習】
1. Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください。
→ まずはRangeから。
>> r = Range.new(1,3)
=> 1..3
>> r.class
=> Range
>> r.class.superclass
=> Object
>> r.class.superclass.superclass
=> BasicObject
>> r.class.superclass.superclass.superclass
=> nil
次にHash
>> h = {}
=> {}
>> h.class
=> Hash
>> h.class.superclass
=> Object
>> h.class.superclass.superclass
=> BasicObject
>> h.class.superclass.superclass.superclass
=> nil
最後にSymbol
>> s = :symbol
=> :symbol
>> s.class
=> Symbol
>> s.class.superclass
=> Object
>> s.class.superclass.superclass
=> BasicObject
>> s.class.superclass.superclass.superclass
=> nil
2. リスト 4.15にあるself.reverseのselfを省略し、reverseと書いてもうまく動くことを確認してみてください。
→ 下記
>> class Word < String
>> def palindrome?
>> self == reverse
>> end
>> end
=> :palindrome?
>> s = Word.new("level")
=> "level"
>> s.palindrome?
=> true
####【4.4.3 組み込みクラスの変更 演習】
1. palindrome?メソッドを使って、“racecar”が回文であり、“onomatopoeia”が回文でないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか? ヒント: downcaseメソッドで小文字にすることを忘れないで。
→ 4.4.2の演習の流れでいきましょう。
s = Word.new("racecar")
=> "racecar"
>> s.palindrome?
=> true
>> s = Word.new("onomatopoeia")
=> "onomatopoeia"
>> s.palindrome?
=> false
>> s.downcase.palindrome?
=> true
2. リスト 4.16を参考に、Stringクラスにshuffleメソッドを追加してみてください。ヒント: リスト 4.12も参考になります。
3. 比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。
→ まとめて下記
>> def shuffle
>> self.split('').shuffle.join
>> end
>> end
=> :shuffle
>> "foobar".shuffle
=> "fobaro"
>> class String
>> def shuffle
>> split('').shuffle.join
>> end
>> end
=> :shuffle
>> "foobar".shuffle
=> "boroaf"
####【4.4.4 コントローラクラス 演習】
1. 第2章で作ったToyアプリケーションのディレクトリでRailsコンソールを開き、User.newと実行することでuserオブジェクトが生成できることを確認してみましょう。
→ toy_app消してますやん…ということで、git cloneを実行。また一つ賢くなりました。usernameとpasswordを聞かれましたが、Githubのアカウント名とログインパスワードを入力すれば実行できました。
$ git clone 該当のリモートリポジトリのhttps
そして本題に入ろうとしたら、各種エラーが…。budle install --without productionして、rails db:migrateして解決。無事userオブジェクトを作成できました。
>> user = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
2. 生成したuserオブジェクトのクラスの継承階層を調べてみてください。
→ 下記
>> user.class
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime)
>> user.class.superclass
=> ApplicationRecord(abstract)
>> user.class.superclass.superclass
=> ActiveRecord::Base
>> user.class.superclass.superclass.superclass
=> Object
>> user.class.superclass.superclass.superclass.superclass
=> BasicObject
>> user.class.superclass.superclass.superclass.superclass.superclass
=> nil
####【4.4.5 ユーザークラス 演習】
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
"#{self.full_name} <#{@email}>"
end
end
コンソール上で、
>> require './example_user'
=> true
user = User.new(first_name: "t" ,last_name: "k", email: "tk@mail.com")
=> #<User:0x00000000030715e0 @first_name="t", @last_name="k", @email="tk@mail.com">
>> user.formatted_email=> "t k <tk@mail.com>"
2. "Hartl, Michael" といったフォーマット (苗字と名前がカンマ+半角スペースで区切られている文字列) で返すalphabetical_nameメソッドを定義してみましょう。
→ 下記
def full_name
"#{@first_name} #{@last_name}"
end
3. full_name.splitとalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうか確認してみましょう。
→ 上の演習2を記入してるのでコンソールに読み込み直し。上の入力値も短すぎるので入れ直します。(ヤットさんのaが抜けてる、、、)
>> require './example_user'=> true
>> user = User.new(first_name: "Ysuhito", last_name: "Endo", email: "ye@ye.com")
=> #<User:0x0000000003059968 @first_name="Ysuhito", @last_name="Endo", @email="ye@ye.com">
>> user.full_name.split=> ["Ysuhito", "Endo"]
>> user.alphabetical_name.split(', ').reverse
=> ["Ysuhito", "Endo"]
>> user.full_name.split == user.alphabetical_name.split(', ').reverse
=> true
###第4章まとめ
・全体で使うのか、特定のコントローラで使うのか、それらによってヘルパーの定義ファイルも使い分ける。
・オブジェクト指向を感覚に刷り込んでいけ。すべてがオブジェクトだ。
・クラスは継承できる。必ず大元のクラスがある。継承しているから色々な機能が使える。
・クラスに定義するのがクラスメソッド、インスタンスで定義するのがインスタンスメソッド。
・( )とか{ }省略できる。引数の最後のブロックの{ }など。
・:nameはシンボル。ハッシュ記法は ~~: "mojiretu" の書き方が一般的。
・ぼちぼち分からない単語があったので、用語集にまとめています。
この章は作業多かったな〜。でもコードリーディングには省略記法とかの知識が大事かも。次の5章から、再びアプリ開発に戻ります!
⇨ 第5章へ!
⇦ 第3章はこちら
学習にあたっての前提・著者ステータスはこちら
####なんとなくイメージを掴む用語集
・組み込み関数
プログラミング言語などの仕様にあらかじめ用意され、標準で使用できる関数のこと。これに対し、プログラマがコード上で定義・実装した関数を「ユーザー定義関数」(user-defined function)という。
・API(Application Programming Interface)
あるコンピュータプログラム(ソフトウェア)の機能や管理するデータなどを、外部の他のプログラムから呼び出して利用するための手順やデータ形式などを定めた規約のこと。APIの利用には、ソフトウェア開発の効率化・セキュリティの向上・最新情報を簡単に取得可能 といったメリットがある。
・エスケープ(エスケープ文字・処理)
意味を持つ文字列をただの文字列にしたり、その逆で意味を与えたりすること、その働きを持つ文字(\など)のこと。
・リテラル (literal)
ソースコードに書いた文字とか数字のこと。
・ネスト(nest)
あるものの中に、それと同じ形や種類の(一回り小さい)ものが入っている状態や構造のこと。
・アクセサー(accessir)
オブジェクト指向プログラミングで、オブジェクト内部のメンバ変数(属性、プロパティ)に外部からアクセスするために用意されたメソッド。メンバ変数をオブジェクト内部に隠蔽し、外部から直接参照させないようにするために用意される。
・メンバ変数
インスタンス変数のこと。