LoginSignup
1
0

More than 1 year has passed since last update.

Railsチュートリアル(第6版) 第4章 Rails風味のRuby

Last updated at Posted at 2021-05-22

第4章

第4章では、文法や構造といった部分に着目し勉強することが大半となる。
基本となるところなので、内容を押さえておきたい章である。
この章に来るまでは、railsの便利機能によって進めてきたが、今回はrubyに注目することとなる。

カスタムヘルパー

app/views/layouts/application.htmlえrb:
<%= stylesheet_link_tag 'application', media: 'all',
                                       'data-turbolinks-track': 'reload' %>

Railsの組み込み関数stylesheet_link_tagを使って、application.cssをすべてのメディアタイプで使えるようにしてる。

一見シンプルに見える箇所だが、Rubyの4つの概念がある。
・Railsの組み込み関数
・カッコを使わないメソッド呼び出し
・シンボル
・ハッシュ

Q:カスタムヘルパーとは?
A:新しく作ったメソッドの事をカスタムヘルパーと言う

前章では

<%= yield(:title) %> | Ruby on Rails Tutorial Sample App

というものを追加したが、もしタイトルを与えられていなかったら?
| Ruby on Rails Tutorial Sample App
こんな感じで、左に「|」余分な縦棒が残ったまま表示される。
これを解決しようって話

そこで使うのが、ヘルパーというもの。

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

このヘルパーを定義したので、下記のコードを書き換える。

変更前

app/views/layouts/application.html.erb
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>

変更後

app/views/layouts/application.html.erb
<title><%= full_title(yield(:title)) %></title>

すごくシンプルになった。

コードを変更したので、Homeという文字が表示されてないことを確認するテストに変更

test/controllers/static_pages_controller_test.rb
  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "#{@base_title}"
  end

テストした結果

$ rails test
5 runs, 9 assertions, 1 failures, 0 errors, 0 skips

テストが失敗したので、Homeページビューから、<% provide(:title, "Home") %>の行を削除して、再度テストすると

$ rails test
5 runs, 9 assertions, 0 failures, 0 errors, 0 skips

テストが成功した。

文字列とメソッド

・文字列
 ・文字列はダブルクォート " で囲むことで作成可能
 ・#{}とすることで式展開ができる。
 ・文字列はシングルクォート'で囲んでも作成可能
 ・シングルクォート'で囲むと式展開が出来ない
 ・特殊文字を使うなら、シングルクォート'を使うこと

コンソールの話
rails consoleでは、出力するときputsを使う。
(put stringの略なので、発音は「put ess」とのことだが、普通にputsと発音してもOK)

putsprintの違い
putsは改行をしてくれる
printは改行してくれない。そのため、意図的に改行するなら、「バックスラッシュ(\)+ n」(\n)という改行文字を使う。

演算子で文字列結合

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

・式展開

>> first_name = "Michael"    # 変数の代入
=> "Michael"
>> "#{first_name} Hartl"     # 文字列の式展開
=> "Michael Hartl"

・出力

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

・シングルクォート内の文字列

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

演習

 > city = "渋谷区"
 => "渋谷区" 
 > prefecture = "東京都"
 => "東京都" 
 > puts prefecture + " " + city
東京都 渋谷区
 > puts prefecture + "\t" + city
東京都  渋谷区
 > puts prefecture + `\t` + city
東京都\t渋谷区

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

ここで要点だけまとめると

・Rubyはどんなものもオブジェクト
 →nilも当然オブジェクト

・Rubyメソッドの末尾に?を付けると論理値(boolean)で返す

・Rubyは、elsif(else + if)で書く。(C#をやっていたから、これを見た時「なんでこんなことになってしまって」と思った)

・論理値は、&&||!で表すこともできる。

・メソッドチェインというものがあり、メソッドを繋げていくもの。
例は下に記載

>> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
>> nil.to_s.empty?      # メソッドチェーンの例
=> true

to_sとすることで文字列となり、中身をempty?でチェックし、nilは空なのでtrueを返している。

因みにnilかどうかを返すメソッドもある。

>> "foo".nil?
=> false
>> "".nil?
=> false
>> nil.nil?
=> true

 ・後続ifやunlessというものがある。(コードを短くしたい時に使用)

後続ifの例↓

puts "x is not empty" if !x.empty?

unlessの例↓

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

 ・「!!」(バンバン)という演算子を使うと2回否定することになる

>> !!nil
=> false

また、Rubyはあらゆるものがオブジェクトなので、ゼロもtrueになる

>> !!0
=> true

演習

>> "racecar".length
=> 7

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

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

>> puts "It's a palindrome!" if s == s.reverse                                                                                                                 
It's a palindrome!
=> nil

メソッドの定義

ざっくり要点だけ

・メソッドにデフォルト値を含められる。

・メソッドはデフォルト値があれば、引数を省略することができる。

・Rubyのメソッドには暗黙の戻り値がある。当然明示的に戻り値を指定できる。
 どういうことかというと

>> def string_message(str = '')
>>   return "It's an empty string!" if str.empty?
>>   return "The string is nonempty."
>> end

最後に評価された式が、自動的に返されるという特性(暗黙の戻り値)があるので、2行目のreturnを消しても"The string is nonempty."という答えは返ってくる。なので、下のように記述しても問題ない。

>> def string_message(str = '')
>>   return "It's an empty string!" if str.empty?
>>   "The string is nonempty."
>> end

他には、メソッドで引数の変数名はどんな名前でも、メソッドの呼び出し側には何も影響がでない。(最初の例のstrという変数名をthe_function_argumentに変更したが、呼び出し側は問題ない)

>> def string_message(the_function_argument = '')
>>   if the_function_argument.empty?
>>     "It's an empty string!"
>>   else
>>     "The string is nonempty."
>>   end
>> end
=> :string_message
>> puts string_message("")
It's an empty string!
>> puts string_message("foobar")
The string is nonempty.

演習

1
>> def palindrome_tester(s)
>>   if s == s.reverse
>>     puts "It's a palindrome!"
>>   else
>>     puts "It's not a palindrome."
>>   end
>> end

2
>> palindrome_tester("racecar")
It's a palindrome!

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

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

titleヘルパー

・Webサイトのレイアウトで使うヘルパーメソッドには、メソッド定義、変数割り当て、論理評価、制御フロー、文字列の式展開等の様々な要素が含まれている。

・モジュールとは、メソッドをまとめる方法の一つで、includeメソッドを用いることで、モジュールを読み込むことが出来る。Railsでは自動的にヘルパーモジュールを読み込んでくれるため、includeを記載しなくてもOK。故に、full_titleメソッドは自動的にすべてのビューで利用できる。

データ構造

配列と範囲演算子
配列について解説していく。
・配列(array)とは、特定の順序を持つ要素リスト
 例えば、["foo", "bar", "baz"]のようなもの。

配列を要素毎に分割する.split

>>  "foo bar     baz".split     # 文字列を3つの要素を持つ配列に分割する
=> ["foo", "bar", "baz"]

.splitは通常空白で分割するが、文字指定も可能

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

・Rubyでは他の言語と同様ゼロオリジンを使っている。
(初めてプログラミング触れた時、「ゼロ開始とは、違和感があるようなないような」って思ってた)
 ゼロオリジンとは、一番最初の配列の要素を0から始める事

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # Rubyでは角カッコで配列にアクセスする
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # 配列の添字はマイナスにもなれる!
=> 17

配列の要素に対して、数字以外のアクセス方法としては、下のような方法がある。

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

・様々な演算子たちの紹介

>> x = a.length       # 配列も文字列と同様lengthメソッドに応答する
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

・様々なメソッドたち紹介

>> 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
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]

・配列に要素を追加、pushメソッド、または<<演算子の使用

>> a.push(6)                  # 6を配列に追加する
=> [42, 8, 17, 6]
>> a << 7                     # 7を配列に追加する
=> [42, 8, 17, 6, 7]
>> a << "foo" << "bar"        # 配列に連続して追加する
=> [42, 8, 17, 6, 7, "foo", "bar"]

splitメソッドと逆の、joinメソッド

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

・範囲(range)の使用、to_aメソッドを使ってやると分かりやすいど

>> 0..9
=> 0..9
>> 0..9.to_a              # おっと、9に対してto_aを呼んでしまっていますね
NoMethodError: undefined method `to_a' for 9:Fixnum
>> (0..9).to_a            # 丸カッコを使い、範囲オブジェクトに対してto_aを呼びましょう
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

・インデックスに-1を使うと便利なやつ。配列の最後の要素を指定できる。

=> [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]                         # 添字に-1を使って選択
=> [2, 3, 4, 5, 6, 7, 8, 9]

・文字列にも範囲オブジェクトは使えるぞ

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

ブロック

範囲と配列には、ブロックを伴なって様々なメソッドに応答する。ブロックはRubyのすごく便利な機能

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

解説
(1..5)に対し、eachメソッドを呼び出している。メソッドに渡されているこの{ |i| puts 2 * i }というのがブロックにあたる。
ブロック内にある変数名が縦棒2本で囲まれた|i|となる。これは、ローカル変数といって、そのブロック内で使えるもの。
他にもdoendで囲んだブロックの作り方もある。

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

使用する時はdo...end記法が大半なので、こちらを覚えればOK

mapメソッドを使ったブロックの例

>> 3.times { puts "Betelgeuse!" }   # 3.timesではブロックに変数を使っていない
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # 「**」記法は冪乗 (べき乗)
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # %w で文字列の配列を作成
=> ["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"]

・最後にブロックの例として、単体テストに目を向ける

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

今まで書いてきたテストにもブロックが使われていることに気づく。即ち、このtestメソッドはブロックを引数に取って、テストが実行される時にブロック内のコードを実行している。

演習

> (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 
> def yeller(str)
> str.join.upcase
> end
=> :yeller
> yeller(['o','l','d'])
=> "OLD"
> def random_subdomain
> ("a".."z").to_a.shuffle[0..7].join
> end
=> :random_subdomain
> random_subdomain
=> "aehaetn"
>> def string_shuffle(s)
>> s.split('').shuffle.join
>> end
=> :string_shuffle
>> string_shuffle("foobar")
=> "bofroa"

ハッシュとシンボル

ハッシュは配列と基本同じだが、インデクッスを整数値以外のものを使える。(他の言語では連想配列とか言ったりする。)
また、ハッシュは要素の並び順が保証されない。要素の順序が重要なら、配列を使う必要あり。
※ハッシュとブロックの{}は別物

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

ハッシュのインデックスは、キーと呼ばれる。
=>はハッシュロケットと呼ぶ。(見た目がロケットっぽい)
{"キー"=>"データ"}とすることで、キーとデータを結び付けられる。

Railsでは文字列よりもシンボルを使うほうが普通である。
Q:シンボルとは?
A:クォートで囲む代わりに、シンボルを使う。例:name

シンボルを使った例

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

他の記法として、下のような書き方もある。

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

{:キー =>"データ"} ←ハッシュロケットとシンボル
から
{キー: "データ"}  ←ハッシュロケットとシンボルの書き方を省略したもの
つまり
{ :name => "Michael Hartl" }

{ name: "Michael Hartl" }
は等価になる。
しかし、引数を伴わないname:では、意味が成り立たない。

:nameの場合は引数なくてもOKだが、name:は引数が必要。

ネストされたハッシュの例

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

:userというハッシュには、name:email:等のキーを含んだハッシュが入っている。
なので、emailだけを取り出すときは、`params[:user][:email]とすればメールアドレスが取得できる。

また、ハッシュはeachメソッドにも使える。

>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", :danger=>"It failed."}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :danger has value "It failed."

この場合、それぞれのキーと値のペア毎に処理を繰り返す。なので、一つ目はKey :success has value "It worked!"と表示され、二つ目はKey :danger has value "It failed."が表示される。

オブジェクトを表示するinspectというメソッドはpという方法でも表記できる。

>> p :name             # 'puts :name.inspect' と同じ
:name

CSS

CSSにあったこの一文について理解する。

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

Railsはスタイルシートを追加するための特殊なメソッドを使用している。
また、上のコードでは丸カッコを省略してい。丸カッコを省略しない形で書くと、、、

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

さらに、media:と部分に着目するとハッシュのようだが、{}カッコがない点に気づく。ハッシュがメソッド呼び出しの最後の引数ならば、波カッコを省略できる。

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

さらにさらに、上のコード達は改行して書いているが、Rubyは改行と空白を区別していない。行を分割しているのは、1行を80文字に収めて可読性を高めるため。

Rubyにおけるクラス

コンストラクタ
今までダブルクォートを使って文字列のインスタンスを作成していたが、この行為自体が文字列のオブジェクトを暗黙的に作成するリテラルコンストラクタというものである。

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

上のコードの場合、文字列"foobar"がクラスメソッドに応答していて、その文字列にあるクラスを返している。
上の例では、暗黙のリテラルコンストラクタだったが、明示的に名前付きコンストラクタを使うことだってできる。
クラス名に対してnewメソッドを使えば作成できる。

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

配列も文字列と同様にインスタンスを作れる。

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

newに対して呼び出される時の、このメソッドのことを、クラスメソッドと言いう。
・クラスメソッドを呼び出した結果は、オブジェクトでもあり、そのクラスのインスタンスでもある。
・インスタンスに対して呼び出されるメソッドは、インスタンスメソッドと言う。

(なんかうまく説明ができないが、クラスメソッド→インスタンス→インスタンスメソッドのように呼び出している。)

クラス継承

クラスの階層は一つだけではない。クラスは階層構造になっていて、メソッドは親から子へ引き継がれる。これを継承と呼ぶ。

image.png
参照:railsチュートリアル

Stringクラスは、Objectクラスを継承し、ObjectクラスはBasicObjectクラスを継承する。
クラスを辿れば、最終的にBasicObjectにたどり着く。

また、クラスは自分で作成できる。

>> class Word
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> :palindrome?

上記のクラスを使った例が下

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

この例は不自然な例(wordは文字列なので、stringクラスを継承するのが普通)
なので、下のようにStringクラスを継承してあげる。

>> class Word < String             # WordクラスはStringクラスを継承する
>>   # 文字列が回文であればtrueを返す
>>   def palindrome?
>>     self == self.reverse        # selfは文字列自身を表します
>>   end
>> end
=> :palindrome?

上のようにStringクラスを継承したことで、Stringクラスが扱うメソッドはWordクラスでも使えるようになる。

クラスが持つオブジェクト自身を指すときは、selfというキーワードを使って、指定できる。

self == self.reverse

省略した形で書くとこんな感じ

self == reverse

組み込みクラスの変更

上では、Wordクラスを使って、palindrome?メソッドを追加した。
このクラスを、Stringクラスに追加(拡張)すれば、わざわざWordクラスを作る必要はなくなる。
そうすれば、文字列に対してメソッドを直接実行できるのでは?といった疑問が出る。

Rubyは基本クラスを拡張できるが、正当な理由がなければ拡張するのは好ましくないとされているとのこと。

コントローラークラス

image.png
参照:railsチュートリアル

コントローラークラスは画像のような継承関係にある。

コントローラーのアクション(メソッド)を下のように呼ぶことも可能。

>> controller.home
=> nil

homeアクションの中身が空なのでnilが返されている。

ユーザークラス

今回は説明として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

ここで色々とわからない箇所が出てくるので、説明する。

・attr_accessor :name, :e-mail
 →属性に対するアクセサーを定義する。この場合、ユーザー名とメールアドレスの属性にアクセサーを作成している。

・アクセサーを作成すると、ゲッター(getter)とセッター(setter)を定義できる。今回の場合、@name@emailにアクセスするメソッドが用意される。

@name@emailのようなインスタンス変数をコントローラー内で宣言すると、ビューで使えるようになる。また、同一クラス内で使用できる。

initializeは、Rubyの特殊なメソッドで、newメソッドでインスタンスを生成すると、自動的に呼ばれる。

・アクションのformatted_emailにある"#{@name} <#{@email}>"は、上でインスタンス変数を定義しているので、使用できる。

・このファイルを読み込むには、requireを使うことでファイルが読める。

>> require './example_user'     # example_userのコードを読み込む方法
=> true
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # attributes[:name]は存在しないのでnil
=> nil
>> example.name = "Example User"           # 名前を代入する
=> "Example User"
>> example.email = "user@example.com"      # メールアドレスを代入する
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"

これでUserクラスの@name@emailに値を代入できる。

さいごに

この章は、文法や構造といったところを集中的に勉強する章であった。
ここは基礎となるところが多いので、しっかりと理解しておくと今後の章の理解がより容易になると思う。
次は第4章だー

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