第4章
第4章では、文法や構造といった部分に着目し勉強することが大半となる。
基本となるところなので、内容を押さえておきたい章である。
この章に来るまでは、rails
の便利機能によって進めてきたが、今回はrubyに注目することとなる。
カスタムヘルパー
<%= 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
ヘルパーを定義
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>
すごくシンプルになった。
コードを変更したので、Home
という文字が表示されてないことを確認するテストに変更
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)
puts
とprint
の違い
・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|
となる。これは、ローカル変数といって、そのブロック内で使えるもの。
他にもdo
とend
で囲んだブロックの作り方もある。
>> (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
に対して呼び出される時の、このメソッドのことを、クラスメソッドと言いう。
・クラスメソッドを呼び出した結果は、オブジェクトでもあり、そのクラスのインスタンスでもある。
・インスタンスに対して呼び出されるメソッドは、インスタンスメソッドと言う。
(なんかうまく説明ができないが、クラスメソッド→インスタンス→インスタンスメソッドのように呼び出している。)
クラス継承
クラスの階層は一つだけではない。クラスは階層構造になっていて、メソッドは親から子へ引き継がれる。これを継承と呼ぶ。
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は基本クラスを拡張できるが、正当な理由がなければ拡張するのは好ましくないとされているとのこと。
コントローラークラス
コントローラークラスは画像のような継承関係にある。
コントローラーのアクション(メソッド)を下のように呼ぶことも可能。
>> controller.home
=> nil
home
アクションの中身が空なのでnil
が返されている。
ユーザークラス
今回は説明としてUser
クラスを最初から作成する。
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章だー