Railsチュートリアル 第4章

Railsチュートリアルの第4章です. この章はわりと演習が楽しかったですね.

4.1 動機

トピックブランチを作成し, チェックアウトします.

4.1.1

省略.

4.1.2

カスタムヘルパーの作成は, 4.2を終えてからにします.

4.2 文字列とメソッド

4.2.1

省略.

4.2.2

省略.

4.2.3

ぱっと見, 文字列展開ちゃんとされるの?と思いましたが大丈夫でしたね.

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

演習1

省略.

演習2

リバースしたかどうかが分かる単語でも試してみるとよいですね.

演習3

trueが返ってきました.

演習4

nilが返ってきました.

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

4.2.4

演習1, 2

真似して書くだけですね.

演習3

次のようになりました.

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

4.2.5

ここでやり残していたカスタムヘルパーの作成を行いました.

テストコードを書き直します.

# test/controllers/static_pages_controller_test.rb
# の一部を抜粋しています

def setup
  @base_title = "Ruby on Rails Tutorial Sample App"
end

test "should get root" do
  get root_url
  assert_response :success
end

test "should get home" do
  get static_pages_home_url
  assert_response :success
  assert_select "title", @base_title #書き直し
end

レイアウトを変更します.

# app/views/layouts/application.html.erb
# の一部を抜粋しています

<title><%= full_title(yield(:title)) %></title>

Homeではbase_titleしか表示させないので, タイトル設定を消します.

# app/views/static_pages/home.html.erb

<% provide(:title, "Home") %>  # この行を削除
<h1>Sample App</h1>
<p>
        This is the home page for the 
        <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> 
        sample application.
</p>

4.3 他のデータ構造

4.3.1

演習1, 2, 3

# 指定された文字列を変数sに格納
>> s = "A man, a plan, a canal, Panama"
=> "A man, a plan, a canal, Panama"

# 文字列を分割して配列にする
>> a = s.split(", ")
=> ["A man", "a plan", "a canal", "Panama"]

# 再びひとつの文字列に
>> s = a.join
=> "A mana plana canalPanama"

# スペースを無くします
>> s = s.split.join
=> "AmanaplanacanalPanama"

# このままでは回文ではありませんが,
>> s == s.reverse
=> false

# 小文字に揃えれば回文です
# メソッドチェーンの順番を変えたものもチェックしておきました.
>> s.downcase == s.downcase.reverse
=> true
>> s.downcase == s.reverse.downcase
=> true

演習4

# 範囲オブジェクトを配列に変換し, 変数tに格納
>> t = ("a".."z").to_a   
=> ["a", "b", (途中省略), "z"]

# 指定された2つとも取り出せるかと思いきやダメ
>> t[7,-7]
=> nil
>> t[7]
=> "h"
>> t[-7]
=> "t"

# インデックス2以降から3つ取り出す, という意味なんですね
>> t[2,3]
=> ["c", "d", "e"]

4.3.2

演習1

範囲オブジェクトをかっこで囲うのを忘れてはいけませんね. またputsを書かないと出力が目に見えません.

>> (0..16).each {|n| puts n**2}
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 0..16

演習2

ブロックを使わずとも定義できてしまいますね.

>> def yeller (array)
>>   array.join.upcase
>> end
=> :yeller

>> yeller("abc".split)
=> "ABC"

ブロックを使うなら以下のようになりますかね.

>> def yeller (array)
>>   array.map {|s| s.upcase}.join
>> end
=> :yeller

>> yeller("abc".split)
=> "ABC"

演習3

本文中にある方法だと, 異なる8文字の順列を作ることになります. ここはもう一歩踏み込んで, 重複ありの文字列を生成させてみます. 以下のような実装を思いつきました.

>> def random_dubdomain
>>   (0..7).map do
>>     ("a".."z").to_a.shuffle[0]
>>   end
>>   .join
>> end
=> :random_dubdomain

>> random_dubdomain
=> "uknjxsoa"
>> random_dubdomain
=> "htoenews"

はじめ8.timesを使うことを思いつきましたが, 無駄にローカル変数を定義することになりそうです.

>> def random_subdomain
>>   tmp = []
>>   8.times do 
>>     tmp << ("a".."z").to_a.shuffle[0]
>>   end
>>   tmp.join
>> end
=> :random_subdomain

>> random_subdomain
=> "xruoispc"
>> random_subdomain
=> "wqdizcoc"

演習4

ヒント通りにやりました. split('')がポイントですね.

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

>> string_shuffle("foobar")
=> "ofbaro"

4.3.3

演習1

rails consoleでは日本語が打てないので英語で対処しました.

>> hash = { "one" => "uno", "two" => "dos", "three" => "tres" }
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}
>> hash.each do |key, value|
>>   puts "'#{key}' is  '#{value}' in Spanish" 
>> end
'one' is  'uno' in Spanish
'two' is  'dos' in Spanish
'three' is  'tres' in Spanish
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}

演習2

サザエさんにご登場いただきました. 本文にあるように, 中括弧{ }の内側には空白を入れています.

>> person1 = { first: "Masuo", last: "Fuguta" }
=> {:first=>"Masuo", :last=>"Fuguta"}
>> person2 = { first: "Sazae", last: "Fuguta" }
=> {:first=>"Sazae", :last=>"Fuguta"}
>> person3 = { first: "Tarao", last: "Fuguta" }
=> {:first=>"Tarao", :last=>"Fuguta"}

>> params = { father: person1, mather: person2, child: person3 }
=> {:father=>{:first=>"Masuo", :last=>"Fuguta"}, :mather=>{:first=>"Sazae", :last=>"Fuguta"}, :child=>{:first=>"Tarao", :last=>"Fuguta"}}

>> params[:father][:first] == person1[:first]
=> true

演習3

ランダムな文字列を作るメソッドを定義します.

>> def random_string
>>   (0..15).map do
>>     ("a".."z").to_a.shuffle[0]
>>   end
>>   .join
>> end
=> :random_string

userを作ります.

>> user = { name: "logicoffee", email: "logicoffee@example.com", password_digest: random_string }
=> {:name=>"logicoffee", :email=>"logicoffee@example.com", :password_digest=>"wjvqvczpmxmmvdwe"}

演習4

ここを見てみると, 2つのハッシュを組み合わせて新たなハッシュを返すメソッドのようですね. キーが重複している場合は上書きされるようです. やってみましょう.

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

やはりそのようですね.

4.4 Rubyにおけるクラス

4.4.1

演習1, 2, 3

範囲オブジェクトの比較には注意が必要ですね.

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

>> r == 0..10
Traceback (most recent call last):
        1: from (irb):4
ArgumentError (bad value for range)
  
>> r == (0..10) 
=> true

4.4.2

演習1

>> r = 0..10
=> 0..10
>> r.class
=> Range
>> r.class.superclass
=> Object
>> r.class.superclass.superclass
=> BasicObject

>> h = { name: "Taro" }
=> {:name=>"Taro"}
>> h.class
=> Hash
>> h.class.superclass
=> Object
>> h.class.superclass.superclass
=> BasicObject

>> s = :name
=> :name
>> s.class
=> Symbol
>> s.class.superclass
=> Object
>> s.class.superclass.superclass
=> BasicObject

演習2

たしかにselfキーワードを省略できますね.

>> class Word < String
>>  def palindrome?
>>     self == reverse
>>  end
>> end
=> :palindrome?

>> s = Word.new("level")
=> "level"

>> s.palindrome?
=> true

4.4.3

演習1

例のように=> :Stringとはなりませんでした.

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

>> "racecar".palindrome?
=> true
>> "onomatopoeia".palindrome?
=> false
>> "Malayalam".downcase.palindrome?
=> true

演習2, 3

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

>> "abcdefg".shuffle
=> "dgcbeaf"

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

>> "onomatopoeia".shuffle
=> "eopiooataonm"

4.4.4

省略します.

4.4.5

演習1, 2, 3

example_user.rbを用意します. full_nameメソッドと, alphabetical_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 alphabetical_name
        "#{@last_name}, #{@first_name}"
    end

    def formatted_email
        "#{full_name} <#{email}>"
    end
end

特に問題なくできました.

>> require './example_user.rb'
=> true

>> user = User.new(first_name: "Michael", last_name: "Hartle", email: "mhartl@example.com")
=> #<User:0x000055993280b6c0 @first_name="Michael", @last_name="Hartle", @email="mhartl@example.com">

>> user.full_name
=> "Michael Hartle"
>> user.alphabetical_name
=> "Hartle, Michael"
>> user.formatted_email
=> "Michael Hartle <mhartl@example.com>"
>> user.full_name.split == user.alphabetical_name.split(', ').reverse
=> true