LoginSignup
2

More than 3 years have passed since last update.

Rails Tutorial 第6版 学習まとめ 第4章

Posted at

概要

この記事は私の知識をより確実なものにするためにRailsチュートリアル解説記事を書くことで理解を深め
勉強の一環としています。稀にとんでもない内容や間違えた内容が書いてあるかもしれませんので
ご了承ください。
できればそれとなく教えてくれますと幸いです・・・

出典
Railsチュートリアル第6版

この章でやること

Rubyの文法や構造について今後のために学習する。

動機

Rubyについてほぼ知らなくてもRailsを使えばある程度はコードが書けてしまうという便利さを知った一方
Rubyについて知らないと開発の効率が悪いうえに、コードの理解力にも限界がある。
ここでRubyそのものについても学習する。

いつも通りトピックブランチを作って作業する。

$ git checkout -b rails-flavored-ruby
Switched to a new branch 'rails-flavored-ruby'

組み込みヘルパー

前章で作成したapplication.html.erbの内部に書かれていた

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

ここで使われている文法なども勉強していく。
stylesheet_link_tagというものがRails組み込みヘルパーにあたる。

カスタムヘルパー

Railsでは組み込み関数以外にも独自に定義し田関数が使える。
今回はサイトタイトルにprovideメソッドで何も値を与えなかった場合にデフォルトタイトルを返す
関数を作成してみる。

レイアウトファイルapplication.html.erbで使いたいヘルパーなので
applicationヘルパーに定義する

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

文字列とメソッド

Rails consoleを使って学習を進めていく。

自分の場合エラーを吐かれて当初起動できなかったので
参考程度に事例と対策を載せておく。

起動時出てきたエラー

FATAL: Listen error: unable to monitor directories for changes.
Visit https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers for info on how to fix this

参考サイト
https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers

rails consoleが起動できない時の対処法(「FATAL: Listen error」エラー編)

起動がうまくいけば
このようなコンソールが起動する。

Running via Spring preloader in process 6472
Loading development environment (Rails 6.0.3)
>> 

ターミナルと同じく上矢印キーで以前たたいたコマンドをもってこれるので使わない手はない

文字列

重要だと個人的に感じるものをとりあげてみる。

・文字列は""(ダブルクォート)で囲むことで作成可能

・#{}で囲むことで文字列の中で変数を展開することができる。

・文字列は''(シングルクォート)で囲むことでも作成可能

・シングルクォートで囲った文字列では式展開ができない

・#のような特殊な文字をそのまま表示したい場合にはシングルクォートが便利

演習

1.

>> city = "新宿区"
=> "新宿区"
>> prefecture = "東京都"
=> "東京都"

2.

>> puts prefecture + " " + city
東京都 新宿区
=> nil

3.

>> puts prefecture + "\t" + city                                                                     
東京都  新宿区
=> nil

4.

>> puts prefecture + '\t' + city                                                                      
東京都\t新宿区
=> nil

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

ここも重要な部分を要約して取り上げてみる。

・Rubyはどんなものもオブジェクト
  └文字列・ただの数値・nullもオブジェクト扱い

・Rubyのメソッドは?を末尾につけたものは論理値を返すという慣習がある

・Rubyはelse ifじゃなくてelsif! ←しょっちゅう間違えて無駄なエラーを吐くから注意()

・Rubyでは後続ifやunless文を使える。 →コードが見やすく簡潔になりめちゃ便利

・文字列やnilなどあらゆるオブジェクトは先頭に!を二つつけると2回否定する、つまり論理値に変換できる。

>> !"一回否定すると反転する"
=> false
>> !!"二回否定するともどって結果論理値にできる"
=> true

一番わかりやすいのはclassメソッドでそのオブジェクトのクラスを表示すること。
オブジェクトに.classとすることでそのオブジェクトのクラスがわかる。

>> nil.class
=> NilClass
>> "".class
=> String
>> 12.class
=> Integer

このようになんであろうが必ずクラスを持つ

演習

1.

>> "racecar".length
=> 7

2.

>> "racecar".reverse
=> "racecar"

3.

>> s= "racecar"
=> "racecar"
>> s == s.reverse
=> true

4.

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

メソッドの定義

ここもやっていることは至極単純なので
要点だけまとめる。

・メソッドにはデフォルト値を含めることができる。(デフォルト値がある場合は引数の省略可)

・Rubyはメソッドの引数のカッコ()を省略して記述できる。 ←これわかってないと以降の章ののコードが意味不明に…

・Rubyはメソッドに暗黙の戻り値がある。(returnを置かなくてもメソッド内の最後の式を暗黙で返す。)

>> def show_message
>> "Nice to meet you!"
>> end
=> :show_message
>> show_message
=> "Nice to meet you!"

少々わかりづらいかもしれないが、show_messageメソッドを実行すると"Nice to meet you!"と勝手に返されている
return文で戻り値を指定しなくても暗黙の戻り値としてそのメソッド内の一番最後の式の評価内容を返すということがわかる。

演習

1.

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

>> palindrome_tester("racecar")
It's a palindrome!
=> nil
>> palindrome_tester("onomatopoeia")
It's not a palindrome!
=> nil

3.

>> palindrome_tester("racecar").nil?
It's a palindrome!
=> true

titleヘルパー、再び

full_titleメソッドはApplicationHelperに書かれている。
module ApplicationHelperというのはメソッドをひとまとめにする機能で
include モジュール名とすることでそのモジュールを使えるもの

Railsの便利機能の一つでヘルパーモジュールは勝手に読み込むのでわざわざincludeしなくても
使える。

ApplicationHelperはすべてのビューの共通ヘルパーモジュールのためどのビューでも使える。
超便利。

他のデータ構造

配列と範囲演算子

ここも要点をまとめる。

・配列に対して、sort,reverse,shuffleなど様々なメソッドを使えるが
それらのメソッドは値を返すだけで、配列の中身は変わらない
中身をそのまま書き換えるにはメソッドの末尾に!を追加して「破壊的メソッド」を使う。

・pushや<<を使うことで配列の末尾に要素を追加できる。

・0..4と指定すると0,1,2,3,4を返す。(範囲オブジェクト)

・ちょっと複雑だが配列の添え字に範囲オブジェクトと-1を指定することで

>> a[2..-1]
=> [2, 3, 4, 5, 6, 7, 8, 9]

このような動作を実現できる
このときの範囲オブジェクトは2 ~ 配列の一番最後まで という指定になる。

演習

1.

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

2.

>> s = a.join
=> "A mana plana canalPanama"

3.

>> palindrome_tester(s.split.join.downcase)
It's a palindrome!
=> nil

4.

>> 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"]
>> array[7]
=> "h"
>> array[-7]
=> "t"

ブロック

Rubyの機能でめちゃ便利だけど難しいのがブロック
(1..5).each { |i| puts 2 * i }を日本語でやさしく読み解いてみると
1~5の各数字でそれぞれに2×その数を行う。 …余計わかりずらい()
ようは範囲オブジェクト1..5を1個ずつ取り出して変数iに入れて2*iをした結果をputsしている。
説明が難しい...

これをブロックを使わずに実装すると

>> puts 2 * 1
2
=> nil
>> puts 2 * 2
4
=> nil
>> puts 2 * 3
6
=> nil
>> puts 2 * 4
8
=> nil
>> puts 2 * 5
10
=> nil

ブロックを使うことでコード量を減らせる+応用できる。つまり超便利で重要。以上…

times → 指定された回数ブロック内の処理を繰り返す。

each → 配列や範囲オブジェクトなどの各要素にブロック内の処理を適用する。

map → 配列や範囲オブジェクトなどの各要素にブロック内の処理を適用し、その結果を返す。

これを理解してブロックを使えばチュートリアル中のブロックはある程度理解できる…と思う。

チュートリアルで扱っているminitestのテストコードの記述もブロックになっている。

test "it is a test" do 

end

この記述だとdo endで囲まれた部分がブロックとなっている。

演習

1.

>> (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.これは問題文の指示通りにmap使うとめちゃくちゃ遠回り

map使わない方法

>> def yeller(strings)
>> strings.join.upcase
>> end
=> :yeller
>> yeller(['o','l','d'])
=> "OLD"

map使う方法

>> def yeller(strings)
>> s = strings.map { |string| string.upcase}
>> s.join
>> end
=> :yeller
>> yeller(['o','l','d'])
=> "OLD"

3.

>> def random_subdomain
>> ("a".."z").to_a.shuffle[0..7].join
>> end
=> :random_subdomain
>> random_subdomain
=> "gimfnslj"

4.

>> def string_shuffle(s)
>> s.split('').shuffle.join
>> end
=> :string_shuffle
>> string_shuffle("foobar")
=> "bforao"

ハッシュとシンボル

ここでの説明は省くが一つだけ注意

ハッシュの省略記法
params = { name: "Michael", email: "rails.turorial@gmail.com"}
これはあくまで:name:emailというシンボルをキーに指定していることを忘れてはいけない。
自分がしっかり理解していなかったためだが
後の章で
params[:user][:email]なんて表記があったときなんでシンボル? なんてことにならないよう…

演習

1.

>> numbers = { "one" => "uno", "two" => "dos", "three" => "tres"}
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}
>> numbers.each { |key,value| puts "'#{key}'のスペイン語は'#{value}'"}
'one'のスペイン語は'uno'
'two'のスペイン語は'dos'
'three'のスペイン語は'tres'
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}

2.

>> person1 = { first: "Shinzo", last:"Abe" }
=> {:first=>"Shinzo", :last=>"Abe"}
>> person2 = { first: "yoshihide", last:"suga" }                                                      
=> {:first=>"yoshihide", :last=>"suga"}
>> person3 = { first: "Taro", last:"Aso" }                                                            
=> {:first=>"Taro", :last=>"Aso"}
>> params = { father: person1, mother: person2, child: person3}
=> {:father=>{:first=>"Shinzo", :last=>"Abe"}, :mother=>{:first=>"yoshihide", :last=>"suga"}, :child=>{:first=>"Taro", :last=>"Aso"}}
>> params[:father][:first]
=> "Shinzo"

3.

>> user = { name:"take", email:"take.webengineer@gmail.com", password_digest: ("a".."z").to_a.shuffle[0..15].join}
=> {:name=>"take", :email=>"take.webengineer@gmail.com", :password_digest=>"mkadrptqhysfnoec"}

4.存在するキーの場合は上書き、存在しない場合は新規で追加するHashメソッド
キー"b"は存在するので"b"の値が上書きされる
つまりこうなる

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

CSS、再び

ここではレイアウトファイルに書いてあった意味不明な行を解読してみる

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

この行は
・メソッド呼び出し時のカッコは省略してよい
・メソッド呼び出し時の最後の引数のハッシュの{}は省略してよい
という決まりを使って簡略化している。
簡略化しないで書くと

stylesheet_link_tag ('application', {media: 'all',
                                   'data-turbolinks-track': 'reload'})

となる。

Rubyにおけるクラス

コンストラクタ

通常、何かのクラスのインスタンスはnewを使って生成する。
user = User.new
これは名前付きコンストラクタと言い、クラス名にnewメソッドを呼び出す。
しかし一部のクラスは違った書き方でもインスタンスを生成できる

"こんにちは"
a = [1,2,3]
0..5
これらはリテラルコンストラクタと言う
これらはそれぞれStringクラス、Arrayクラス、Rangeクラスのインスタンスである。

演習

1...演算子 → 0..10など

2.

>> Range.new(1,10)
=> 1..10

3.

>> (0..10) == Range.new(0,10)                                                                         
=> true

クラス継承

チュートリアルに詳しく書いてあるので省略

演習

1.すべてObjectクラスを継承している。

>> Hash.superclass
=> Object
>> Range.superclass
=> Object
>> Symbol.superclass
=> Object

2.

>> class Word < String
>> def palindrome?
>> self == reverse
>> end
>> end
=> :palindrome?
>> s = Word.new("しんぶんし")
=> "しんぶんし"
>> s.palindrome?
=> true

組み込みクラスの変更

Rubyに存在する組み込みクラスは
拡張することが可能。ただ相当な理由でもない限りは組み込みクラスを拡張するべきではない
Railsはよくつかわれる便利なメソッドを組み込みクラスに追加している。

演習

1,2,3を同時にやってみる。

>> class String
>> def palindrome?
>> downcase == downcase.reverse
>> end
>> def shuffle
>> split('').shuffle.join
>> end
>> end
=> :shuffle
>> "racecar".palindrome?
=> true
>> "racecar".shuffle
=> "reaarcc"

コントローラクラス

image.png
Railsチュートリアル第6版より
https://railstutorial.jp/chapters/rails_flavored_ruby?version=6.0#fig-static_pages_controller_inheritance

第3章で作成したStaticPagesコントローラの継承構造はこんな感じ

演習

1.

takemo-admin:~/environment/toy_app (master) $ cd ../sample_app/
takemo-admin:~/environment/sample_app (rails-flavored-ruby) $ cd ../toy_app/
takemo-admin:~/environment/toy_app (master) $ rails c
Running via Spring preloader in process 13820
Loading development environment (Rails 6.0.3)
>> user = User.new
   (1.4ms)  SELECT sqlite_version(*)
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>

2.

>> user.class.superclass
=> ApplicationRecord(abstract)
>> user.class.superclass.superclass
=> ActiveRecord::Base
>> user.class.superclass.superclass.superclass
=> Object

ユーザークラス

ここも個人的に覚えておきたいところをまとめておく。

・attr_accessor:属性に対するアクセサ(ゲッター、セッター)を定義してくれる。
これを定義することでインスタンスに値を代入したり、インスタンスから値を受け取ったりできる。

・@付き変数:インスタンス変数。コントローラで定義するとビューで使える他、同一クラス内で共通して使える。

・initializeメソッド:newメソッドでインスタンスを生成した際に自動で呼ばれる。
インスタンス変数の初期化などで用いる。

・requireでファイルを読み込む,includeでモジュールを読み込む。

演習

1.

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 + "\s" + @last_name
  end
  def formatted_email
    "#{full_name} <#{@email}>"
  end
end

2.

  def alphabetical_name
    @last_name + ",\s" + @first_name
  end

3.

>> example.full_name.split == example.alphabetical_name.split(', ').reverse
=> true

前の章へ

次の章へ

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
What you can do with signing up
2