LoginSignup
17
5

More than 3 years have passed since last update.

Ruby on Rails チュートリアル 第4章 Rubyの基本(組み込み関数 メソッド ハッシュ シンボル)を解説

Last updated at Posted at 2018-12-14

近況報告

エンジニア転職成功しました。YouTubeもはじめました。

前回の続き

著者略歴
YUUKI
ポートフォリオサイト:Pooks
現在:RailsTutorial2周目

第4章 難易度 ★★★ 8時間

挫折しないRailsチュートリアルの進め方を先にお読みください↓↓

Railsチュートリアルで挫折しない3つのポイント

この章ではRubyの基礎について解説する。

組み込みヘルパー

まずはブランチを切る。

$ git checkout -b rails-flavored-ruby

とりあえずapplication.html.erbファイルを開く。

以下の行に注目

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

ここではRubyの概念である

  • Railsの組み込み関数
  • カッコを使わないメソッド呼び出し
  • シンボル
  • ハッシュ

この4つが使われているので、これから説明していく。

カスタムヘルパー

新しく作ったメソッドは「カスタムヘルパー」と呼ぶ。
作成の仕方の解説する前に、まずは各ビューのタイトルで使われている組み込み関数に注目。

home.hrml.erb
<% provide(:title, "Home") %>
application.html.erb
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>

yieldメソッドにシンボルでタイトルを付け、provideメソッドでHomeという文字列を呼び出している。
すると、各ページでそれぞれ指定したタイトル文字が表示される。

しかし、このタイトルを与えていなければタイトルが空欄になってしまう。

もし与えられていない場合、| となり、ページタイトルが正しく表示されないので、この問題を解決する為にfull_tittleというヘルパー(新しく作ったfull_titleというメソッド)を使う。

ここでif文を用いてこう記述する。

application_helper.rb
  # ページごとの完全なタイトルを返す。
  def full_title(page_title = '')
    base_title = 'Ruby on Rails Tutorial Sample App'
    if page_title.empty? #page_titleは空でしょうか?
      base_title
    else
      page_title + " | " + base_title 
    end
  end

※if文の処理の中でコメントアウトするとエラーになるので注意。

こうすると、page_titleが空だった場合にbase-title変数を出力。
空じゃなかった場合に"|" を付け加えた文字を出力する。

これでapplication.html.erbのタイトルタグがスッキリ♪

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

コードを変えたのでテストも追加。

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

  test "should get help" do
    get static_pages_help_url
    assert_response :success
    assert_select "title",  "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get static_pages_about_url
    assert_response :success
    assert_select "title",  "About | Ruby on Rails Tutorial Sample App"
  end

  test "should get contact" do
    get static_pages_contact_url
    assert_response :success
    assert_select "title",  "Contact | Ruby on Rails Tutorial Sample App"
  end

テストを確認するとエラー

4 tests, 8 assertions, 1 failures, 0 errors, 0 skips

HomeページにHome文字列が含まれているので当たり前ですね。

home.html.erb
<% provide(:title, "Home") %>    

これを消して再テスト。

4 tests, 8 assertions, 0 failures, 0 errors, 0 skips

OK

ここまでで、モジュール、メソッド定義、文字列の結合、戻り値というRubyの技術を使った。(らしい)
これから覚えていこう。

文字列とメソッド

Railsにはirbの派生技Railsコンソールがあるが、クラウドIDE利用の場合はirbを使いやすくする為、とある設定をしておく。

nano ~/.irbc

でっかい画面が出てくるが、Tutorialの指示通りに設定して.irbrcファイルに設定を保存。

コンソールを起動。

$ rails c
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.

ファイルの監視がどーたらこーたらのエラーが出たので、指示通り以下のコマンドを実行。

$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

再び起動。

$ rails c
Running via Spring preloader in process 13064
Loading development environment (Rails 5.1.6)
>> 

できた。

なんかテキトーに打ってみよう。

>> rails = "Tutorialは難しい"
=> "Tutorialは難しい"
>> if rails == "Tutorialは簡単"
>> puts "凄いな"
>> else puts "難しいよね"
>> end
難しいよ
=> nil

おもろ。

文字列

文字列を入力。

>> ""  #空の文字列
=> ""
>> "foo"  #空ではない文字列
=> "foo"

これを「文字列リテラル」と呼ぶ。

文字列を結合してみる。

>> "foo" + "bar"
=> "foobar"

結合できた。

文字列を組み立てる他の方法に式展開があるのでやってみる。

>> first_name = "Michael"
=> "Michael"
>> "#{first_name} Hartl"                                                                                                                                       
=> "Michael Hartl"
>> 

""の中に#{}を入れることで、変数を文字列の中に埋め込むことができる。
これを「式展開」と言う。

>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name
=> "Michael Hartl"
>> "#{first_name} #{last_name}                                                                                                                                 
"
=> "Michael Hartl\n"
>> 

苗字と名前を変数に入れて出力したりできる。

出力

文字列を出力する時はputsメソッドを使う。

>> puts "foo"
foo
=> nil

putsメソッドの戻り値にはnilが返ってくる(nil=何も無い)
putsを使って出力すると、改行文字である/nが自動的に出力の末尾に追加される。

putsと同じようなメソッドにprintがあるが、これの場合は改行文字は追加されない。

>> print "foo"
foo=> nil

なるほど。

意図的に改行する場合、\nを文字列の末尾に追加する。

>> print "foo\n"
foo
=> nil

なお、Macの場合バックスラッシュはoption + ¥ コマンドで出る。

文字列はシングルクォートでも出力できる。

>> 'foo'
=> "foo"

ただし、シングルクォート文字列の中では式展開はできない。

>> '#{foo} bar'
=> "\#{foo} bar"

このように、変数がfooがそのまま表示される。

ではダブルクォートで#のような特殊文字を使うには??
\(バックスラッシュ)でエスケープする。

>> foo = "oraora"
=> "oraora"
>> "#{foo}"
=> "oraora"
>> "\#{foo}"
=> "\#{foo}"

シングルクォートの使い道として、入力した文字をエスケープせずに、そのまま保持するときに使う。

>> "#{foo}"
=> "oraora"
>> '#{foo}'
=> "\#{foo}"

なるほど。つまり、いちいち\でエスケープせずに済むってことね。

>> '\n'
=> "\\n"

なお、Rubyで\そのものをエスケープする場合は、もう一つ\が必要。

>> 'Newlines (\n) and tabs (\t) both use the backslash character \.'
=> "Newlines (\\n) and tabs (\\t) both use the backslash character \\."

んで、演習の通り東京都 新宿区の間のスペースをtabキーに置き換えてみる。


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

もしくは

>> puts "#{city}\t#{ku}"
東京都  新宿区
=> nil

後者の方が楽やな。

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

オブジェクトはどんな場合でもメッセージに応答する。
例えば

>> "foobar".length
=> 6

footerという文字列にlengthというメッセージを送っている。
これは文字数を返すメソッドである。

他にも

>> "foobat".empty?
=> false
>> "".empty?
=> true

empty?メソッドは対象のオブジェクトが空かどうか聞いている。
1回目では空でないのでfalse,2回目は空なのでtrue

empty?には?が付いており、このメソッドに対してtrueまたはfalseで返すことを
「論理値を返す」と言う。
メソッドに疑問符が付いた場合は、論理値を返す。

if と else で論理値を返すと以下の通り。

>> s = "foobar"
=> "foobar"
>> if s.empty?
>> "The string is empty"
>> else
?> "The string is nonempty"
>> end
=> "The string is nonempty"

条件を複数指定する時はelsifを使う。(elseifではないので注意)

>> if s.nil?
>> "The yariable is nil"
>> elsif s.empty?
>>  "The string is empty"
>> elsif s.include?("foo")
>> "The string includes 'foo'"
>> end
=> "The string includes 'foo'"

変数sの引数に文字列fooが含まれていたので、The string includes fooを返す。

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Both strings are empty "
Both strings are empty 
=> nil
>> 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

!x.empty?は変数xが空じゃない(not)場合x is not emptyを返す。

Rubyではあらゆるものがオブジェクトで、nilもオブジェクトに入る。

>> nil.to_s
=> ""
>> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
        from (irb):50
>> nil.to_s.empty?
=> true

nil自身は存在自体が空なのでempty?だとエラーになるが、メソッドチェーンでto_s(文字情報)を持たせることにより、empty?メソッドが使えてtrue(空ですよ)と返ってくる。

また、Rubyでは後続ifと言って、 処理を書いた後に条件式を書くという手法がある。

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

if文が1行で書けるので簡潔で美しいコードになる。らしい。

バンバン(!!)について

パコるのパンパンではなくバンバン。
!!は演算子で、指定したオブジェクトを二回否定することで、どんなオブジェクトも強制的に論理値に変換してしまう。

>> !!nil
=> false

上記の!!nilでは、nilを二回否定している。nilは空なので、空を二回否定すると
nil→空じゃない→true→やっぱり空→false
となる。

nil以外だと、どんな値でもtrueになる。

>> !!0
=> true

ここで、色々書いてみる。

>> racecar.length
NameError: undefined local variable or method `racecar' for main:Object
Did you mean?  trace_var
        from (irb):52
>> "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

reverseメソッドはオブジェクトを逆にするので、sに代入されたrececarを逆にしている。

メソッドの定義

ターミナルでメソッドを定義して、引数に``を指定する。

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

こうすると、変数が空だったらtrueの処理が返されて、空でなければfalseの処理が返される。
実際に定義したメソッドを使ってputsで引数に文字を代入すると、trueの処理が返されている。

見てわかる通り、falseを返すには引数を省略しても可

このように

def string_message(str = '')

書くと、str変数に引数を渡すことも渡さないこともできる。
引数を渡さない場合は、指定のデフォルト値が自動で返される。

暗黙の戻り値

メソッド内で最後に評価された式の値が自動的に返されるという意味。
具体的には

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

このような記述の場合、空ならreturn "it〜"が返される。
一行目のreturnで値が返されたあと、ifでstrが空でない場合は2行目のreturnの値が返される

しかし、「暗黙の戻り値」の理論からいくと2行目の「return」は必要ない。
なぜなら、最後に評価された式の値は自動で返されるから。

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

returnを抜いても自動で文字列が返されている。

同じように、引数の変数名にどんな名前を使っても、呼び出し側には何の影響も生じない。

>> 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!
=> nil
>> puts string_message("aa")
The string is nonempty.
=> nil

ここで、回文に対する簡単なテストをしてみる。

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

=> nil
>> puts palindrome_tester("onomatopoeia")
It's not a palindrome.

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

racecarは回文なのでtrueの処理が返される。
onomatopoeiaは回分ではないのでfalseの処理が返される。

titleヘルパー、再び

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

Rubyのコードを書く場合、モジュールを作成するたびに関連したメソッドを明示的に読み込んで使うのが普通だが、
Railsでは自動的にヘルパーモジュールを読み込んでくれるので、includeを使う必要がない。

これにより、full_titleメソッドは自動的にすべてのビューで利用できるようになっている。

application_helper
module ApplicationHelper

  # ページごとの完全なタイトルを返す                    #コメント行
  def full_title(page_title = '')                       #メソッド定義とオプション引数   
    base_title = "Ruby on Rails Tutorial Sample App"    #変数への代入
    if page_title.empty? #page_titleは空でしょうか?    #論理値テスト
      base_title                                        #暗黙の戻り値
    else
      page_title + " | " + base_title                   #文字列の結合
    end
  end
end

データ構造

データ構造とは、データを効率よく処理するための配置方法のこと。

その中で重要な「配列」について説明する。

>> "foo bar   baz".split
=> ["foo", "bar", "baz"]

これはfoobarbazと言う三つの文字列からなる配列である。

spiltメソッドでは、文字を指定して区切ることも可能。

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

上記では文字列xで区切って配列を作っている。

また、配列の要素番号の数え方は他の言語同様、0から始まる0オリジンが採用されている。

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a
=> [42, 8, 17]
>> a[0]
=> 42
>> a[1]
=> 8
>> a[2]
=> 17

aに三つの数字を配列として代入すると、最初の要素のデータが42で、要素番号(添字)が0となっていることがわかる。

他にも、firstやsecondを使って各要素にアクセすることができる。

>> a
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]
=> true

a.last == a[-1]では、a[3]とa[-1]が等しいこと意味している。

>> x = a.length
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

上記ではaの文字列の長さをxに代入しているので、3が返される。

!=はイコールでないなので、xは1より大きい為trueが返されている。

>> a
=> [42, 8, 17]  #配列aの中身
>> a.empty? #配列aは空か聞いている
=> false
>> a.include?(42) #配列aに42が含まれているか聞いている
=> true
>> a.sort #配列aを昇順ソート(小さい順)に並べ替える
=> [8, 17, 42]
>> a.reverse #配列aを逆に並べ替える
=> [17, 8, 42]
>> a.shuffle #配列aをシャッフルさせる
=> [8, 42, 17]
>> a #配列aの中身は変わっていない
=> [42, 8, 17]

ここで、配列aの中身を変えてみる。

>> a
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]

!をメソッドの末尾に添えることで、配列の内容を変更できる。

>> a
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]
>> a.push(6)
=> [8, 17, 42, 6]
>> a << 7
=> [8, 17, 42, 6, 7]
>> a << "foo" << "bar"
=> [8, 17, 42, 6, 7, "foo", "bar"]

push<<でデータを後ろに追加させていく。
foobar<<のように、メソッドチェーンで連結させることも可能。

>> a
=> [8, 17, 42, 6, 7, "foo", "bar"]
>> a.join
=> "8174267foobar"
>> a.join(',')
=> "8,17,42,6,7,foo,bar"

joinメソッドでは配列の各要素を文字列に変換し、結合した文字列を返す。
引数に,を指定すると、区切り文字として結合した文字列を返す。

>> 0..9
=> 0..9
>> 0..9.to_a
NoMethodError: undefined method `to_a' for 9:Integer
Did you mean?  to_c
               to_f
               to_d
               to_i
               to_r
               to_s
        from (irb):47
>> (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

to_aメソッドで途中の配列を全て得ることができる。

>> a = %w[foo bar baz quux]
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]

%wでそれぞれの配列を文字列に変換できる。

インデックスに-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]

lengthの-1、つまり0の一個後ろの9番目までの要素を返し、
a[2..-1]で要素番号9までを返している。

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

文字列に対しても範囲オブジェクトが使える。

色々やってみる

>> a = %w["A man,a plan,a canal,Panama"]                                                                                                                    
=> ["\"A", "man,a", "plan,a", "canal,Panama\""]

%wで文字列をコンマで区切りに、配列にする。

>> def palindroe_tester(s)
>> if s==s.reverse
>> puts "It's a palindrome!"
>> else
?> puts "It's not a palindrome."
>> end
>> end
=> :palindroe_tester
>> palindroe_tester(s)
It's not a palindrome.
=> nil

変数sが回文であればtrue処理を行い、回文でなければfalse処理を行う。

>> def palindrome_tester(s)
>> if s =s.reverse
>> puts "It's a palindrome!"
>> else
?> puts "It's not a palindrome!"
>> end
>> end
=> :palindrome_tester
>> palindrome_tester(s.split.join.downcase)
It's a palindrome!
=> nil

変数sを分割し、さらに連結させて文字列に、そしてs.downcase
が回文であるかチェック。
(結果はtrue回文)

>> you = ('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"]
>> you
=> ["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"]
>> you[6]
=> "g"
>> you[-7]
=> "t"

変数youにアルファベット文字を代入し、7番目の文字と後ろから7番目の文字を取り出した。

ブロック

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

eachのあとの{}の部分をブロックと呼ぶ。
具体的には、iと言うローカル変数に1〜5までの数値を一個ずつ渡して、それぞれ実行している。

因みに、ブロック部分をdoとendで囲うことも可能。

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

do〜でブロックを示し、endで処理を実行している。

この記法はブロックを複数指定する場合に使いやすい。

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

このように--を追加で行に挿入することができる。

ブロックの部分は変えても大丈夫。

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

mapメソッドを使うことで、各要素をブロックで処理したあと配列にすることもできる。

ここで、単体テストのコードの確認。

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

ここにdoが使われていることに注目。
このtestメソッドでは、文字列とブロックを引数にとって、テストが実行される時にブロック内のコードが実行される、というような解釈ができる。

演習をやってみる。

1:範囲オブジェクト0..16を使い、各要素の2乗を出力

>> (0..16).each do |jyo|
?> puts jyo ** 2
>> end
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 0..16

2:yellerと言うメソッドを定義し、文字列の要素で構成された配列を受け取り、各要素を連結後に大文字にして返す。

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

3:random_subdomailというメソッドを定義し、ランダムな8文字を生成し、文字列として返す

>> def string_shuffle(s)
>> ('a'..'z').to_a.shuffle[0..7].join
>> end
=> :string_shuffle
>> string_shuffle('a'..'z')
=> "aofpcugm"

4:?の部分を、それぞれ適切なメソッドに置き換えてみる。

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

メソッドに渡された文字をシャッフルしている。

ハッシュとシンボル

ハッシュは基本的には配列として同じ。
配列と異なるのは、整数値以外のインデックス(要素の番号のこと)が使えことと、要素の並び順が保証されないことにある。

>> user = {}   #{}は空のハッシュ
=> {}
>> user["first_name"] = "Michael" #キーが"first_name"、値が"Michael"
=> "Michael"
>> user["last_name"] = "Hartl" #キーが"last_name"、値が"Hartl"
=> "Hartl"
>> user["first_name"]  #キー"first_name"にアクセする。
=> "Michael" #データの値の"Michael"が返される。
>> user
=> {"first_name"=>"Michael", "last_name"=>"Hartl"} #リベラル表記

ハッシュのインデックスをキーと呼ぶ。
=>はハッシュロケットと呼ぶ。
通常、ハッシュは{"キー値"}=>"データの値"このように表記される。

シンボル

シンボルとは、ハッシュのキーを:シンボルという書き方で表す手法。

>> {:user => "Yuuki",:name => "Tetsuya"} #基本的な書き方
=> {:user=>"Yuuki", :name=>"Tetsuya"}

>> {user: "baka", name: "aho"} #省略した書き方
=> {:user=>"baka", :name=>"aho"}

このように、:を前にして書くのが基本だが、
後ろにして=>を無くし、省略することも可能。(こちらが一般的な書き方)

>> "name".split('')
=> ["n", "a", "m", "e"]
>> :name.split('')
NoMethodError: undefined method `split' for :name:Symbol
        from (irb):11
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol
        from (irb):13

シンボルは Ruby以外ではごく一部の言語でしか採用されない特殊なデータ形式である。
また、全ての文字が使える訳ではなく、例えば-2は使えない。

>> :foo-bar
NameError: undefined local variable or method `bar' for main:Object
        from (irb):26
>> :2foo
SyntaxError: (irb):27: syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
:2foo
  ^

では、実際にシンボルで書いてみてデータの値を呼び出してみる。

>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]
=> "Michael Hartl"
>> user[:email]
=> "michael@example.com"
>> user[:password]
=> nil

このように、変数userにシンボルでnameとemailを指定すると、配列と同じように呼び出せる。
ただ、未定義のシンボルはnilが返される。これはつまり、ハッシュ値は単純にnilであることがわかる。

>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h3 = { name: "Michael Hartl", email: "michael@example.com"}
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h1 == h3
=> true

このように、省略した書き方やスペースがあってもなくてもデータの値は同じであることがわかる。

因みに、省略した記法を「ハッシュ記法」と呼ぶ。JavaScriptや他の言語で一般的に使用されている記法である。

省略記法の注意点として、単独のシンボルでは意味が成り立たない。
たとえば

>> :name
=> :name
>> name:
?> => "MM"
SyntaxError: (irb):32: syntax error, unexpected ':', expecting end-of-input
name:

引数を伴わないname:では意味が成り立たない。

>> {name: "aaa"}
=> {:name=>"aaa"}

このように記せば返ってくる。
省略しない記法の使い方として、シンボルを強調させたい場合に使ったりする。

次に、params変数を使ってシンボルを使ってみる。

>> params = {}  #'params' というハッシュを定義する。
=> {}
>> params[:user] = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> params
=> {:user=>{:name=>"Michael Hartl", :email=>"michael@example.com"}}
>>  params[:user][:name] #'params'というハッシュのuserシンボルとnameシンボルを呼び出す。
=> "Michael Hartl"
>> 

このように、Railsではネストされたハッシュも使われることが多い。

さらに、ハッシュとこれまでに習ったeachメソッドや変数展開を組み合わせて使ってみる。

>> flash = { success: "It worked!", danger: "It falied." } #`flash`と言う名前のハッシュを作成。
=> {:success=>"It worked!", :danger=>"It falied."} 
>> flash.each do |key, value| #`flash`ハッシュに`key`と`value`と言うブロック変数を作る。
?> puts "key #{key.inspect} has value #{value.inspect}" #ブロック変数keyの値を文字列として返し、さらにブロック変数valueの値を文字列として返す。
>> end
key :success has value "It worked!"
key :danger has value "It falied."
=> {:success=>"It worked!", :danger=>"It falied."}

ハッシュのeachメソッドの場合、キーと値を二つに設定している。
この場合、一つ目のキーと値と、二つ目のキーと値、これをそれぞれペアごとに処理を繰り返す点に注意。

inspectはpメソッドというショートカットキーでも書ける。

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

ここで演習をやってみる。

1:指定されたキーと値をそれぞれ出力

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

2:3つのハッシュを作成し、キーと値を追加、そしてparamsというハッシュのハッシュを作成。
その後、ハッシュのハッシュの値が正しい値か確認。

>> params = {}
=> {}
>> params[:father] = person1                                                                                                                                
=> {:first=>"こんにちは", :last=>"にんにくです"}
>> params[:mother] = person2
=> {:first=>"おはよう", :last=>"魚です"}
>> params[:child] = person3
=> {:first=>"こんばんは", :last=>"おやすみ"}
>> params[:father][:first]
=> "こんにちは"
>> params[:father][:first] == person1[:first]
=> true
>> params[:mother][:first] == person2[:first]                                                                                                              
=> true
>> params[:child][:first] == person3[:first]                                                                                                               
=> true

3:userハッシュに三つのキーに適当な値を付けて、password_digestキーのみ16文字からなるランダム文字列を代入する。

>> user = {name: "YUUKI" , email: "yuukitetsuya@gggdgd" ,password_digest: ('a'..'z').to_a.shuffle[0..15].join}   #a~zまでの文字を返しシャッフルメソッドで16文字列を選び繋げる                              
=> {:name=>"YUUKI", :email=>"yuukitetsuya@gggdgd", :password_digest=>"mhpfzsqivlkwojcd"}

4:mergeメソッドを実行した結果を推測

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

末尾のキーと値に結合するようだ。
デフォルト値は変わらない模様。

CSS

ここでCSSの読み込みについて確認してみる。

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

この文ではstylesheet_link_tagメソッドを呼び、applicationと言う引数でスタイルシートへのパスを示し、次の二つの引数(ハッシュ)で二つのキーと値を呼び出している。
mediaはメディアタイプを示し、data-turbolinks-trackはコードが組み込み関数(ERb)のテンプレートに挿入され、実行が反映されるようになる設定を示している。

実際のソースコードを確認してみる。

<link rel="stylesheet" media="all" href="/assets/application.self-f0d704deea029cf000697e2c0181ec173a1b474645466ed843eb5ee7bb215794.css?body=1" data-turbolinks-track="reload">

なるほど。

Rubyにおけるクラス

Rubyでは""で囲んだ文字列もオブジェクト。
つまり、
文字列のインスタンスを作成=文字列のオブジェクトを作成。

これをリテラルコンストラクタと言う

>> s = "foobar" #ダブルクォートで囲んだ文字列は、文字列のコンストラクタである。
=> "foobar"
>> s.class
=> String

変数sのclassはString(データ型:文字列)であることがわかる。

>> s = String.new("foobar") #String(文字列)の名前付きコンストラクタを作成
=> "foobar"
>> s.class
=> String
>> s == "foobar" #sと文字列"foobar"が同じである
=> true

配列の場合。

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

同じようにインスタンスが生成される。

ハッシュの場合。

>> h = Hash.new
=> {}
>> h[:foo]
=> nil
>> h = Hash.new(0)
=> {}
>> h[:foo]
=> 0
>> h.length
=> 0

Hashと言うクラス自身に対して呼び出されるメソッド(newの部分)=クラスメソッド
Hash.newで呼び出した結果=クラスのオブジェクト、インスタンス
h.lengthのようなインスタンスに対して呼び出すメソッド=インスタンスメソッド

クラス継承

superclassメソッドを使うことでクラス階層を調べることができる。

>> s = String.new("foobar")
=> "foobar"
>> s.class
=> String
>> s.class.superclass #Stringクラスの親クラスを調べる
=> Object
>> s.class.superclass.superclass #Objectクラスの親クラスを調べる
=> BasicObject
>> s.class.superclass.superclass.superclass #BasicObjectクラスの親クラスを調べる
=> nil #なし

Rubyに置ける全てのクラスは、最終的にスーパークラスを持たない**BasicObjectクラスを継承している。

image.png

出典:図 4.1: Stringクラスの継承階層

ここで、自分でクラスを作成してみる。

>> class Word
>> def palindrome?(string)
>> string == string.reverse
>> end
>> end
=> :palindrome?
>> w = Word.new
=> #<Word:0x000000051b71a0>
>> w.palindrome?("fober")
=> false
>> w.palindrome?("foober")
=> false
>> w.palindrome?("level")
=> true

この書き方だと不自然らしい。
と言うのも、文字列を引数に取るメソッドを作るためだけにわざわざ新しいクラスを作る必要はないとのこと。

つまり、親クラスを継承したクラスを定義した方が良い

>> class Word < String
>> def palindrome?
>> self == self.reverse
>> end
>> end
=> :palindrome?
>> s = Word.new("level") #新しいWordを作成、`level`で初期化
=> "level"
>> s.palindrome? #Word(level)が回文かどうか
=> true
>> s.length "levelの文字数を調べる。
=> 5
>> 

ちなみに、疑問符は真の場合trueを返す。
また、selfは文字列自身を返す

WordはStringクラスを継承している。
Stringクラスは文字列を表すオブジェクトを提供しているため、lengthメソッドが使える。

sの親クラスを遡ってみる。

>> s.class
=> Word
>> s.class.superclass
=> String
>> s.class.superclass.superclass
=> Object
>> 

wordクラスの中では、selfはオブジェクト自身を表す。
つまり

self == self.reverse

の部分は、単語が回文であるかどうかを確認できる。

self == reverse

このように省略しても同じく動く。

組み込みクラスの変更

継承を使わずとも、palindrome?メソッドをStringクラスに追加できる。
Rubyでは組み込みの基本クラスの拡張が可能。

>> class String
>> def palindrome?
>> self == self.reverse
>> end
>> end
=> :palindrome?
>> "deified".palindrome?
=> true

しかし、組み込みクラスの変更は極めて強力なものなので、正当な理由がない限り使わない方がよいらしい。

>> "".blank?
=> true
>> "     ".empty?
=> false
>> "     ".blank?
=> true
>> nil.blank?
=> true

nil.blank?では、Stringクラスではなく、基底クラス(オブジェクト自身)を指している。

演習をやってみる。

1:回文調査。

>> def palindrome?
>> "racecar" == "racecar".reverse
>> "onomatopoeia" == "onomatopoeia".reverse?
>> end
=> :palindrome?
>> "racecar".palindrome?
=> true
>> "onomatopoeia".palindrome?
=> false
>> "Malayalam".reverse
=> "malayalaM"
>> "Malayalam".downcase.palindrome?
=> true

これだと長いのか。
クラス定義してちゃんとやってみよう。

>> class String 
>> def palindrome?
>> self == self.reverse
>> end
>> end
=> :palindrome?
>> "racecar".palindrome?
=> true
>> "onomatopoeia".palindrome?
=> false
>> "Malayalam".downcase.palindrome?
=> true

2:shuffleメソッドの追加

>> class String
>> def shuffle
>> self.split('').shuffle.join
>> end
>> end
=> :shuffle
>> "racecar".shuffle
=> "arcecra"

3:selfを消してもうまく動くか

>> class String
>> def shuffle
>> split('').shuffle.join
>> end
>> end
=> :shuffle
>> "racecar".shuffle
=> "acrarce"

コントローラクラス

今回はStaticPagesControllerを新たに生成し、コントローラの継承を調べてみる。

>> controller = StaticPagesController.new
=> #<StaticPagesController:0x00000004e213d8 @_action_has_layout=true, @_routes=nil, @_request=nil, @_response=nil>
>> controller.class
=> StaticPagesController
>> controller.class.superclass
=> ApplicationController
>> controller.class.superclass.superclass
=> ActionController::Base
>> controller.class.superclass.superclass.superclass
=> ActionController::Metal
>> controller.class.superclass.superclass.superclass.superclass
=> AbstractController::Base
>> controller.class.superclass.superclass.superclass.superclass.superclass
=> Object

図で表すと

image.png
出典:図 4.3: StaticPagesコントローラの継承階層

Railsコンソールでは、コントローラの中のアクションも呼び出すことができる。

>> controller.home
=> nil

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

Railsアクションには戻り値がない。しかも、第3章ではStaticPagesController.newを実行せずともControllerが動いていた。これはRubyとRailsが別物の振る舞いをしているためであり、この二つは全く別のもので考えなければならない。

ユーザークラス

RailsConsoleで散々勉強した内容を実装する。
今回はユーザークラスをしてみる。

example_user.rb
class User
 attr_accessor :name, :email  #インスタンス変数を読み書きするアクセサメソッドを生成。 

 def initialize(attributes = {}) #Userクラス生成時に自動実行されるメソッドを定義
  @name  = attributes[:name]  #nameキーが存在しない場合、ハッシュはnilを返す
  @email = attributes[:email] #emailキーが存在しない場合、ハッシュはnilを返す
 end

 def formatted_email #インスタンス変数に割り当てられた値をユーザーのメールアドレスとして構成する。
  "#{@name} <#{@email}>" #@nameと@emailに割り当てられた値を表示
 end
end

実際にRails Consoleで自作したクラスを試してみる。

>> require './example_user' #example_userの読み込み
=> true
>> example = User.new
=> #<User:0x000000053ec7e0 @name=nil, @email=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>"

ここで重要なのはexample.name@nameexample.email@emailであること。
さっき書いたコードの内容が反映されている。

ここで、新たにユーザーを作成してみる。

>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #<User:0x000000053b9778 @name="Michael Hartl", @email="mhartl@example.com">
>> user.formatted_email
=> "Michael Hartl <mhartl@example.com>"

User.newの引数に書いた:name:emailinitializeメソッドにハッシュを渡していることになるので、@name@emailに値が渡されている。

user.formatted_emailでは、実際に代入した値を表示している。
ちなみに、波括弧省略の記法を使っている。

演習1をやってみる

example_user
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}>" #`self`で`full_name`メソッドを呼び出すのがコツ
  end
end

selfを使ってる点に注目。組み込み関数を使うためにselfを使う。

>> require './example_user'
=> true
>> example = User.new
=> #<User:0x0000000546b4c8 @first_name=nil, @last_name=nil, @email=nil>
>> example.first_name
=> nil
>> example.last_name
=> nil
>> example.first_name = "Michael"
=> "Michael"
>> example.last_name = "Hartl"                                                                                                                       
=> "Hartl"
>> example.email = "mhartl@example.com"
=> "mhartl@example.com"
>> example.formatted_email
=> "Michael Hartl <mhartl@example.com>"
>> 

演習2

example_user
  def alphabetical_name
    "#{@last_name}, #{@first_name}"
  end

上記の文を追加した。

>> require './example_user'
=> true
>> user = User.new
=> #<User:0x00000004105820 @first_name=nil, @last_name=nil, @email=nil>
>> user.first_name
=> nil
>> user.first_name = "Michael"
=> "Michael"
>> user.last_name = "Hartl"
=> "Hartl"
>> user.email = "mhartl@example.com"
=> "mhartl@example.com"
>> user.formatted_email
=> "Michael Hartl <mhartl@example.com>"
>> user.alphabetical_name
=> "Hartl, Michael"

できてるっぽい?

演習3

>> require './example_user'
=> true
>> user = User.new
=> #<User:0x00000003ca3278 @first_name=nil, @last_name=nil, @email=nil>
>> user.first_name = "Michael"
=> "Michael"
>> user.last_name = "Hartl"
=> "Hartl"
>> user.email = "example@user.com"
=> "example@user.com"
>> user.full_name.split == user.alphabetical_name.split(',').reverse
=> true

よし、true。

Git

やっと終わった。。
example_user.rbを削除して、コミット後にmasterブランチにマージ。

rm example_user.rb
$ git commit -am "Add a full_title helper"
[rails-flavored-ruby 3a37e004] Add a full_title helper
 7 files changed, 21 insertions(+), 9 deletions(-)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
ec2-user:~/environment/sample_app (master)
$ git merge rails-flavored-ruby

あ、一応テストもしておこう。

$ rails t
4 tests, 8 assertions, 0 failures, 0 errors, 0 skips

まぁguardで自動テストできるんだけど。

最後にpush,デプロイ

$ git push
$ git push heroku

やっっとおわったー!!!
なげえええよwww

とりあえずRubyの基本構文とクラスの仕組み、メソッドの読み出しのルールは今回の章で覚えられたかな。
まだ4章とか死にそう。

第5章へ

単語集

  • irb

インタラクティブRubyのこと。
入力したRubyの文を実行して結果を出力するプログラム。
立ち上げ方はターミナルでirbreturn。

nil?で空かどうか聞いているので、nilなのでtrueが表示される。

$ irb
2.4.1 :001 > 

あとは実験的にRubyのコードを書けばOK。

  • バックスラッシュ

option + ¥コマンドで\

  • エスケープ

特殊文字の無効化のこと。例えば、\を無効化すること。

  • メソッド

オブジェクトに渡されるメッセージのこと。
実体は「そのオブジェクト内で定義されたメソッド」

  • include?("test")

オブジェクトの引数に文字列(この場合はtest)が含まれていればtrue,含まれていなければfalseを返す。

  • to_s

配列に数値を文字列として格納する。

s = 12.to_s

変数sに12を文字列として格納する。

  • メソッドチェーン

メソッドを連結させること。

  • unless

条件式が偽の時に使うメソッド。if分で偽の部分だけ処理したい場合に使う。

  • ミックスイン

関連したメソッドをまとめる方法の一つ。includeメソッドを使ってモジュールを読み込める。

  • split

文字列を自然に配列に変換するメソッドのこと。

  • ゼロオリジン

配列の要素番号がインデックス0から始まること。
1から始まる「1オリジン」もある。
0オリジンでは0〜9で10個、1オリジンでは1~10で10個と違いがある。

  • push

pushとは、スタックというデータを一時的に格納しておくための配列「バッファ」の一つである
「スタック」にデータを格納すること。スタックはLIFO(Last In Fisrt Out)
と言う形式でデータを取り出す。

  • to_a

範囲オブジェクトのこと。指定された範囲のオブジェクトを返す。

  • %w

配列を作るメソッド。文字列の配列に変換する。

  • downcase

指定したオブジェクトの大文字を小文字に変えるメソッド。

  • each

オブジェクトに含まれている要素を順に取り出すメソッド。配列や、範囲オブジェクトで使われる。
do~endで囲めば要素がなくなるまで繰り返し処理される。
ブロック変数の指定は||で囲む。

(1..5).each { |i| puts 2* i } #1〜5までの数値をiに代入し、それぞれ2を掛けて処理する。
2
4
6
8
10
=> 1..5
  • times

指定した回数を繰り返すメソッド。

3.times do
puts "tes"
end

tes
tes
tes #tesが三回繰り返される

timesメソッドではブロックに変数を使う必要がない

  • map

渡された要素の数だけ繰り返しブロックを処理し、配列にして返す

  • ネスト

子要素のこと。たとえば 

.ccc  {
 .bbb {
 }
}

.bbb.cccにネストされたセレクタである。

  • inspect

要求されたオブジェクトを表現する文字列を返す。

  • コンストラクタ

インスタンスを作成したタイミングで実行されるメソッド。

  • blank?

nilまたは空のオブジェクトをチェックできる。
nilまたは空の時にtrueを返す。RubyのメソッドではなくRailsで拡張されたメソッドのため、Rubyのみでは使えない。

  • 相対パス

カレントディレクトリ(現在の場所)から見たパス。pwdコマンドで確認できる

  • 絶対パス

全体から見たパス。

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