Ruby on Railsに入門してみる。
入門レベルの文法はプロゲート。
railsはRuby on Rails チュートリアル
railsのltsは5らしいけど、勉強用だし新しいほうが気持ちいいので7で行く。
Ruby on Rails チュートリアルは1000円使い切りみたいなので、課金予定。
rubyの初歩文法をprogateにて終了。
phpとの違い、気づいたことなど。
不明点
- ⚠️インスタンスメソッドと、クラスメソッドの違いは?
- インスタンスメソッドはインスタンス化して呼び出す。クラスメソッドはインスタンスしなくても呼び出すことができる?
基本
-
echo
等出力は、puts
- if文などは、{中括弧}いらない。閉じるときは、
end
必須。(pythonと同じ?) - 配列はphpとほぼ同じ。
- phpの
foreach
は、
arrays.each do | array |
# 処理
end
- phpの連想配列は、rubyだとハッシュという
exam = {"subject" => "Math", "score" => 80}
# キー「"subject"」の値を「"Science"」に更新してください
exam['subject'] = 'Science'
# キーが「"grade"」、値が「"good"」の要素を追加してください
exam['grade'] = 'good'
# キー「"grade"」の値を出力してください
puts exam['grade']
- 文字列とは別にシンボルがある。
puts "ruby"
はputs :ruby
とかける。- ⚠️明確な違い等は、要確認
- シンボルは、ハッシュのキーとしてよく使われるらしい。
- ハッシュのキーの部分を「:name」という様に、シンボルを用いた場合には、 その値を用いる時も「user[:name]」とシンボルで指定する必要がある。
# キーをシンボルで書き換えてください
exam = {:subject => "Math", :score => 80}
# キー「:score」の値を出力してください
puts exam[:score]
# 要素がハッシュの配列を作成してください
exams =[
{subject: "Math", score: 80},
{subject: "Science", score: 55}
]
# インデックス番号が1の要素の値を出力してください
puts exams[1]
puts exams[1][:score]
関数
- 関数の基本形は以下の通り。
- やはり
end
を忘れないようにする。
- やはり
def introduce
puts "こんにちは"
puts "私はにんじゃわんこです"
# 出力を追加してください
end
puts "-----自己紹介-----"
# introduceメソッドを呼び出してください
introduce
#引数あり
def print_info(item)
puts "わんこでんきへようこそ!"
puts "今日は#{item}がセール中です!"
end
print_info("ヘッドホン")
# reutrn
def discount(price)
# 「price / 2」を戻り値として返してください
return price / 2
end
puts "テレビがセール中です!"
half_price = discount(15000)
puts "特別価格で#{half_price}円です"
- 関数に
?
つけるとbooleanを返す関数!!!
# shipping_free?メソッドを定義してください
def shipping_free?(price)
return price >= 5000
end
# if文の条件式でshipping_free?メソッドを呼び出してください
if shipping_free?(3000)
puts "5000円以上のお買い上げなので送料はいただきません"
else
puts "追加で送料をいただきます"
end
- 引数は、キーワード引数も可能。
# キーワード引数を使うように書き換えてください
def buy(item:, price:, count:)
puts "#{item}を#{count}台のお買い上げです"
puts "合計金額は#{price * count}円です"
end
# キーワード引数を使うように書き換えてください
buy(item: "テレビ", price: 15000, count: 2)
クラス
- クラスは、 クラスの作成→インスタンスの利用、という流れになる。
- class名の頭文字は大文字
- 最後はendでしめる。
- インスタンス変数は、
attr_accessor :name
のように書く。
class Menu
# インスタンス変数
attr_accessor :name
attr_accessor :price
def info
# インスタンス変数は、self.var
return "#{self.name} #{self.price}円"
end
end
# Menuクラスのインスタンスを生成
menu1 = Menu.new
# menu1のprice, nameに代入
menu1.name = "ピザ"
menu1.price = 800
# menu1のpriceを出力
puts menu1.price
# info関数を呼び出す
puts menu1.info
- コンストラクタは
initialize
を利用する。
class Menu
attr_accessor :name
attr_accessor :price
def initialize(name:, price:)
self.name = name
self.price = price
end
def info
return "#{self.name} #{self.price}円"
end
def get_total_price(count)
total_price = self.price * count
if count >= 3
total_price -= 100
end
return total_price
end
end
# 引数を渡してインスタンスを生成してください
menu1 = Menu.new(name: 'すし', price: 1000)
puts menu1.info
# ----------------------------------------
menu1 = Menu.new(name: "ピザ", price: 800)
menu2 = Menu.new(name: "すし", price: 1000)
menu3 = Menu.new(name: "コーラ", price: 300)
menu4 = Menu.new(name: "お茶", price: 200)
# 変数menusを定義して配列を代入してください
menus = [menu1, menu2, menu3, menu4]
# 変数indexを定義して「0」を代入してください
# phpのforeachみたいにkeyはないから、外から呼ぶ。
index = 0
menus.each do |menu|
# 番号をつけてメニューの内容が出力されるように書き換えてください
puts "#{index}. " + menu.info
# 変数indexに1を加えて値を更新してください
index += 1
end
- 継承は、class 小クラス < 親クラス
class Drink < Menu
# 継承先のインスタンス変数
attr_accessor :amount
end
- オーバーライド(メソッド)は小クラスに、親クラスと同じメソッドを書いてあげる。
class Menu
attr_accessor :name
attr_accessor :price
def initialize(name:, price:)
self.name = name
self.price = price
end
def info
return "#{self.name} #{self.price}円"
end
def get_total_price(count)
total_price = self.price * count
if count >= 3
total_price -= 100
end
return total_price
end
end
class Food < Menu
attr_accessor :calorie
# infoメソッドのオーバーライド。親要素と同じメソッド名なので、こちらが優先される。
def info
return "#{self.name} #{self.price}円 (#{self.calorie}kcal)"
# return "#{self.name} #{self.price}円"
end
def calorie_info
return "#{self.name}は#{self.calorie}kcalです"
end
end
- コンストラクタのメソッドは、super
class Menu
attr_accessor :name
attr_accessor :price
def initialize(name:, price:)
self.name = name
self.price = price
end
def info
return "#{self.name} #{self.price}円"
end
def get_total_price(count)
total_price = self.price * count
if count >= 3
total_price -= 100
end
return total_price
end
end
class Food < Menu
attr_accessor :calorie
def initialize(name:, price:, calorie:)
# superを使って書き換えてください
super(name: name, price:price)
end
def info
return "#{self.name} #{self.price}円 (#{self.calorie}kcal)"
end
def calorie_info
return "#{self.name}は#{self.calorie}kcalです"
end
end
- 既存のクラス(埋め込みクラス?)
Date
を利用する。
# 呼び出すときは、./ いらない。
require "date"
# 変数todayに、Date.todayの戻り値代入。
# Dateクラスのメソッドは他にもたくさんあり。
today = Date.today
# 変数todayをputsしてください
puts today
# 変数todayに対してsunday?
# これは、日曜日ならTrue, 違ったらFalseを返す。
puts today.sunday?
シンボルについてはこちらを参考にさせていただく。
- シンボルはオブジェクト
- シンボルはImmutable(文字列はmutable)
- シンボルは同値ならば必ず同一
- 比較の処理はシンボルの方が高速
どっちでも問題ない場合は、シンボル利用したほうが良さそうだ。
-
初心者向けのざっくりとした指針を示すなら、「クラスメソッドとインスタンスメソッドの使い分けに迷ったら、とりあえずインスタンスメソッドにしておけ」と答えます。
-
インスタンス変数を全く使ってないので、この場合は次のようにクラスメソッドにしても問題ないですね。
rubyのgem って、 phpでいうcomposerかな。
railsは、appディレクトリの中に、「models」「views」「controllers」が含まれている形
routingの基本は、
config/routes.rb
に、
Rails.application.routes.draw do
root "controller_name#action_name"
end
の形。
まだ、routingの関数の中身がわかってないな、、、。
下記の場合は、hello関数で、中身がよくわからない。
雰囲気で、hello, world!
をフロントにレンダリングするんだろうなとはわかるんだが、一語一句わかったわけではない。
#app/controllers/application_controller.rb
def hello
render html: "hello, world!"
end
ルーティングファイルもわかっていない。
Rails.application.routes.draw do
root 'application#hello'
end
チュートリアルのマイクロポストでは、user
1 対 多 microposts
で作成する
Laravelので言う、
php artisan make:model User -c
が、
rails generate scaffold User name:string email:string
ぽい。
更に、php artisan
がrails generate
にあたりそう。
さらにさらに、laravelだと --resource
でCRUD
のルート等を作成してくれるが、
generate scaffold
はそれをそのまま同じように実行してくれる。
generate scaffold で、ルーティングにはresources :users
が追加される
また、User controllerも追加されて、中にCRUDの処理が勝手に書かれる。
Laravelのphp artisan migrate
は、
Railsのrails db:migrate
2.1 演習
- (CSSの知識がある人向け)新しいユーザーを作成し、ブラウザのHTML検証機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
-
<p style="color: green">User was successfully destroyed.</p>
とあった記述が消える。Flushメッセージになっている。
- emailを入力せず、名前だけを入力しようとした場合、どうなるでしょうか?
- 空白でも登録できた。
- 「@example.com」のような間違ったメールアドレスを入力して更新しようとした場合、どうなるでしょうか?
- 登録できた。本当はバリデーションがあるのか?
- 上記の演習で作成したユーザーを削除してみてください。ユーザーを削除したとき、Railsはどんなメッセージを表示するでしょうか?
<p style="color: green">User was successfully destroyed.</p>
routingの
root 'users#index'は、
/
にアクセスしたときに表示するやつか。
@users = User.all
に関して。
User.all
は、User Modelから全部抜き出すということっぽい?
@users
はインスタンス変数の記述があるので、User model objectをインスタンスにしたものだろうか。
class UsersController < ApplicationController
def index
@users = User.all
end
# リスト 2.10の下
@記号で始まる変数をRubyではインスタンス変数と呼び、Railsのコントローラ内で宣言したインスタンス変数はビューでも使えるようになります。この場合、リスト 2.11のindex.html.erbビューは@usersの一覧を並べ、1行ごとにHTMLの行として出力します(今はこのコードの意味がわからなくても問題ありません。これはあくまで説明のためのものです)。
疑問
laravelでは、controllerないのメソッドで、return viewすることで、どのviewファイルを利用するか選択した。
今回、rails generate scaffold User
で作ったコントローラーには、どのコントローラーがどのviewに返すのかを示していない。
今回は、generate scaffold
だから勝手にやってくれているのか?
本来は、どのviewに返すのかをどこに明示する必要があるのか?
2.1 演習
-
図 2.12を参考にしながら、/users/1/editというURLにアクセスしたときの振る舞いについて図を書いてみてください。
-
図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。(ヒント: set_userという特殊な場所の中にあります。)
-
user controller
の一番下のprivate mthod
にある。
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
- ユーザーの情報を編集するページのファイル名は何でしょうか?
-
edit.html.erb
- ※編集するページが何をさすのかイマイチわからない。
2.15 演習
- (CSSの知識がある人向け)新しいマイクロポストを作成し、ブラウザのHTML検証機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
- 省略
- (CSSの知識がある人向け)新しいマイクロポストを作成し、ブラウザのHTML検証機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
- 作成できちゃう。
- 141文字以上の文字列をContentに入力した状態で、マイクロポストを作成しようとするとどうなるでしょうか?
- 作成できる。
- 上記の演習で作成したマイクロポストを削除してみましょう。
- 省略
リレーションは、Laravelのそれとそっくり。
以下、ユーザー1に対してpostsが複数ある場合
class Micropost < ApplicationRecord
belongs_to :user
validates :content, length: { maximum: 140 }
end
class User < ApplicationRecord
has_many :microposts
end
2.17 演習
- ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください(コラム 1.2で紹介した技術の出番です)。うまく表示できたかどうか、/users/1にアクセスして確認してみましょう。
- こう?
#user controller
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
@users_microposts_first = @user.microposts.first
end
# show.html.erb
<p style="color: green"><%= notice %></p>
<%= render @user %>
<%= render @users_microposts_first %>
- リスト 2.18は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう(図 2.18のようになっていると成功です)。
- 省略
- リスト 2.19の(コードを書き込む)となっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください(図 2.19)。
- こう?
#user model
class User < ApplicationRecord
has_many :microposts
# バリデーション
validates :name, presence: true
validates :email, presence: true
end
追記
1番はこれでいけるわ。
# show.html.erb
<p style="color: green"><%= notice %></p>
<%= render @user %>
<%= @user.microposts.first.content %>
2.3.4 演習
Applicationコントローラのファイルを開き、ApplicationControllerがActionController::Baseを継承している部分のコードを探してみてください。
ここ↓
class ApplicationController < ActionController::Base
def hello
render html: "hello, world!"
end
end
ApplicationRecordがActiveRecord::Baseを継承しているコードはどこにあるでしょうか? 先ほどの演習を参考に、探してみてください。(ヒント: コントローラと本質的には同じ仕組みなので、app/modelsディレクトリ内にあるファイルを調べてみましょう。)
ここ↓
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
end
rails generateはrails gという短縮形でも動作します。
まじかよ!
Rubyがクラス名にキャメルケースを使う慣習があり
おk
3.2.1 演習
Fooというコントローラを生成し、その中にbarとbazアクションを追加してみてください。
↓
rails generate controller Foo bar baz
コラム 3.1で紹介したテクニックを駆使して、Fooコントローラとそれに関連するアクションを削除してみてください
↓
rails destroy controller Foo bar baz
テスト
rails generate controller
すると、testディレクトリにテストコードが入っている。
実行は、rails test
例えば、config/routes.rb
に、
Rails.application.routes.draw do
get "static_pages/about"
end
というルートを加える。
この結果、自動的にstatic_pages_about_url
というヘルパーが使えるようになります。
testコードでは、
test "should get about" do
get static_pages_about_url # ←←←←
assert_response :success
end
を確認することができる。
コラム 3.3に記載されている、なぜテストをするのか?
- テストが揃っていれば、機能停止に陥るような回帰バグ(Regression Bug: 以前のバグが再発したり機能の追加/変更に副作用が生じたりすること)を防止できる。
- テストが揃っていれば、コードを安全にリファクタリング(機能を変更せずにコードを改善)できる。
- テストコードは、アプリケーションコードから見ればクライアントとして動作するので、アプリケーションの設計やシステムの他の部分とのインターフェイスを決めるときにも役に立つ。
「テスト駆動」にするか「一括テスト」にするか
「テスト駆動」にするか「一括テスト」にするかを決める目安となるガイドラインがあると便利です。著者の経験を元に、次のようにまとめてみました。
- アプリケーションのコードよりも明らかにテストコードの方が短くシンプルになる(=簡単に書ける)のであれば、「先に」書く
- 動作の仕様がまだ固まりきっていない場合、アプリケーションのコードを先に書き、期待する動作を「後で」書く
- セキュリティが重要な課題またはセキュリティ周りのエラーが発生した場合、テストを「先に」書く
バグを見つけたら、そのバグを再現するテストを「先に」書き、回帰バグを防ぐ体制を整えてから修正に取りかかる - すぐにまた変更しそうなコード(HTML構造の細部など)に対するテストは「後で」書く
リファクタリングするときは「先に」テストを書く。特に、エラーを起こしそうなコードや止まってしまいそうなコードを集中的にテストする
HTMLビューのファイルの拡張子が.html.erbやつ。
これは、ERB(またはERb)
と呼ばれている。
中で、rubyを書きたい場合は、
<% provide(:title, "Home") %>
のように、<% %>
で囲む。
出力したい場合は、=
を加えて、 <%= ... %>
と記載する。
view
-
application.html.erb
で骨子を用意。中に挿入するための<%= yield %>
を用意してあげる。
# /sample_app/app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
</head>
<body>
# ↓これ
<%= yield %>
</body>
</html>
-
yield
に入れるための各ページ特有のレイアウトを作成する。
# /sample_app/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>
リスト 3.41 演習
サンプルアプリケーションにContact(問い合わせ先)ページを作成してください17 。(ヒント: まずはリスト 3.17を参考にして、/static_pages/contactというURLのページに「Contact | Ruby on Rails Tutorial Sample App」というタイトルが存在するかどうかを確認するテストを最初に作成しましょう。次に、3.3.3でAboutページを作ったときのと同じように、Contactページにもリスト 3.42のコンテンツを表示してみましょう。)
# Contactページで使うコード
# app/views/static_pages/contact.html.erb
<% provide(:title, "Contact") %>
<h1>Contact</h1>
<p>
Contact the Ruby on Rails Tutorial about the sample app at the
<a href="https://railstutorial.jp/contact">contact page</a>.
</p>
- test コードに以下追記
test "should get contact" do
get static_pages_contact_url
assert_response :success
assert_select "title", "Contact | #{@base_title}"
end
- ルーティングに以下追加
Rails.application.routes.draw do
# 略
get 'static_pages/contact'
# 略
end
- controllerに以下追記
def contact
end
-
$ touch /sample_app/app/views/static_pages/contact.html.erb
したあと、以下追記
<% provide(:title, "Contact") %>
<h1>Contact</h1>
<p>
Contact the Ruby on Rails Tutorial about the sample app at the
<a href="https://railstutorial.jp/contact">contact page</a>.
</p>
第4章。
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
stylesheet_link_tagについては、公式
使い方は。
stylesheet_link_tag(スタイルシートへのパス..)
なるほど、assets配下にあるファイルへのリンクを自動生成してくれる、、、のかな?
以下公式から。
外部スタイルシートを指定するリンクタグを生成
stylesheet_link_tag "style"
#=> <link href="/assets/style.css" media="screen" rel="stylesheet" />
# 拡張子まで指定
stylesheet_link_tag "style.css"
#=> <link href="/assets/style.css" media="screen" rel="stylesheet" />
# URLで指定
stylesheet_link_tag "http://www.example.com/style.css"
#=> <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
# 複数指定
stylesheet_link_tag "random.styles", "/css/stylish"
#=> <link href="/assets/random.styles" media="screen" rel="stylesheet" /><link href="/css/stylish.css" media="screen" rel="stylesheet" />
view で利用できるhelperは、app/helpers/application_helper.rb
に記載する。
$ rails console
は、$ php artisan tinker
的なものか?
なるほど、
puts
は勝手に改行してくれる。戻りはnil
print
は改行しない。戻りはnil
>> puts "foo"
foo
=> nil
>> print "foo" # 文字列の画面出力 (putsと同じだが改行がない)
foo=> nil
4.2.1 演習
city変数に適当な市区町村名を、prefecture変数に適当な都道府県名を代入してください
>> city = '港区'
=> "港区"
>> prefecture = '東京都'
=> "東京都"
先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。
>> puts city + ' ' + prefecture
港区 東京都
=> nil
上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です。)
>> puts city + "\t" + prefecture
港区 東京都
=> nil
タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
>> puts city + '/t' + prefecture
港区/t東京都
=> nil
>>
boolの評価の仕方が?なの、わかりやすいけど慣れない。。。
>> "foobar".empty?
=> false
>> "".empty?
=> true
empty?メソッドの末尾にある疑問符にご注目ください。Rubyでは、メソッドがtrueまたはfalseという論理値(boolean)を返すことを、末尾の疑問符で示す慣習があります。 論理値は、特に処理の流れを変更するときに有用です。
ifの使い方。
この場合は、x
がemptyじゃなければx is not empty
が表示される。
unless
に置き換えることも可能。
>>x = 'test'
test
>> puts "x is not empty" if !x.empty?
x is not empty
>> puts "x is not empty" unless x.empty?
x is not empty
4.2.2 演習
"racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
>> 'racecar'.length
=> 7
reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
>> 'racecar'.reverse
=> "racecar"
変数sに "racecar" を代入してください。その後、比較演算子(==)を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
>> s = 'racecar'
=> "racecar"
>> s == s.reverse
=> true
変数sに "racecar" を代入してください。その後、比較演算子(==)を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
>> 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でもreturn
使えるんだ。
けど、みんな省略してるんだ。
def string_message(str = '')
return "It's an empty string!" if str.empty?
return "The string is nonempty."
end
4.2.3 演習 メソッドの定義
回答まとめ↓
def palindrome_tester(s)
if s == s.reverse
puts "It's a palindromeです!!"
else
puts "It's not a palindromeだ!"
end
end
palindrome_tester('racecar')
palindrome_tester('onomatopoeia')
palindrome_tester('racecar').nil?
palindrome_tester('onomatopoeia').nil?
Rubyのモジュールについて。
例えば、app/helpers/application_helper.rb
の文頭にある
module ApplicationHelper
includeメソッドを使ってモジュールを読み込むことができます。
これをミックスイン(mixed in)とも呼びます。
単なるRubyのコードを書くのであれば、モジュールを作成するたびに明示的に読み込んで使うのが普通です。
つまり、モジュールAを作成したら、それを使いたいファイルでincludeしてあげる。
一方Railsでは自動的に(すべてのファイルで)ヘルパーモジュールを読み込んでくれるので、include行をわざわざ書く必要がありません。
つまり、このfull_titleメソッドは自動的にすべてのビューで利用できるようになっているということです。
→ここに書けば、わざわざincludeする必要ない。
→複数ファイルで読み込みたい場合は個々に書いてあげる。
配列
配列の添字には、マイナス も利用可能。
>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0] # Rubyでは角カッコで配列にアクセスする
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1] # 配列の添字はマイナスにもなれる!
=> 17
配列に対してのメソッドは通常非破壊的
>> a = [42, 8, 17]
=> [42, 8, 17]
>> a
=> [42, 8, 17]
>> a.sort
=> [8, 17, 42]
>> a
=> [42, 8, 17]
破壊的メソッドを使うには、元のメソッドの末尾に「!」を追加したものを使う。
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]
範囲(range)は以下のように記す。
0..9
配列化するには、to_a
を利用。
※丸括弧で配列化する文字列を囲ってあげる必要あり。
>> (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a-zは、
('a'..'z').to_a
範囲は、配列の要素を取り出すことが可能。
>> a = %w[foo bar baz quux] # %wを使って文字列の配列に変換
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]
4.3.1 演習 配列と範囲演算子
文字列「A man, a plan, a canal, Panama」を ", " で分割して配列にし、変数aに代入してみてください。
>> a = 'A man, a plan, a canal, Panama'.split(',')
=> ["A man", " a plan", " a canal", " Panama"]
今度は、変数aの要素を連結した結果(文字列)を、変数sに代入してみてください。
>> s = a.join
=> "A man a plan a canal Panama"
変数sを半角スペースで分割した後、もう一度連結して文字列にしてください。(ヒント: メソッドチェーンを使うと1行でもできます。)
リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ)変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。
palindrome_tester(s.split(' ').join)
palindrome_tester(s.split(' ').join.downcase)
aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。
##同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください。)
alpabet = ('a'..'z').to_a
alpabet[7]
alpabet[-7]
範囲オブジェクトに対してeachメソッドがある。
(1 .. 5).each { |i| puts i ** 2 }
(1 .. 5).each do | i |
puts i ** 2
end
eachのあとの、
{ |i| puts i ** 2 }
もしくは、
do | i |
puts i ** 2
end
の部分をブロックという。
i
はそれぞれブロック変数という。
phpでいうforeach()
のvalue
の部分的なやつ。
単体テストにもブロックがある。
test "should get home" do
get static_pages_home_url
assert_response :success
assert_select "title", "Ruby on Rails Tutorial Sample App"
end
これは、test
メソッドが、引数に文字列とブロックを利用している。
4.3.2ブロック
範囲オブジェクト0..16を使って、各要素の2乗を出力してください
(0 .. 16).map do | num |
puts num ** 2
end
?> (0 .. 16).map do | num |
?> puts num ** 2
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
yeller(大声で叫ぶ)というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。(ヒント: mapとupcaseとjoinメソッドを使ってみましょう。)
?> def yeller(str_ary)
?> puts str_ary.join.upcase
=> :yeller
>> yeller(['o', 'l', 'd'])
OLD
=> nil
>>
shuffled_subdomainというメソッドを定義してください。このメソッドは、完全にシャッフルされたアルファベット8文字を文字列として返します。(ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。)
def shuffled_subdomain()
puts ('a' .. 'z').to_a.shuffle[0 .. 7].join
end
>> shuffled_subdomain()
gldpcmns
=> nil
リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。(ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列(引数)をシャッフルさせることができます。)
# 文字列をシャッフルするメソッド(「?」を置き換えてください)
>> def string_shuffle(s)
>> s.?('').?.?
>> end
>> string_shuffle("foobar")
=> "oobfra"
def string_shuffle(s)
s.split('').shuffle.join
end
string_shuffle("foobar")
ハッシュ
- インデックスとして整数値以外のものも使える
- つまり、phpの連想配列
>> 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"}
シンボル
- ハッシュのキーにする。
{ :name => "Michael Hartl", :email => "michael@example.com" }
- これは、下のように書ける。
{ name : "Michael Hartl", email : "michael@example.com" }
ハッシュは配列と同じように、each
メソッドが使える。
ハッシュは、key, valueあるので、この場合のeachメソッドもkey, valueが使える。
flash = { success: "It worked!", danger: "It failed." }
flash.each do |key, val|
puts "Key #{key.inspect} has value #{val.inspect}"
end
4.3.3ハッシュとシンボル
person1、person2、person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値(名前など)を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。1)キーparams[:father]の値にperson1を代入、2)キーparams[:mother]の値にperson2を代入、3)キーparams[:child]の値にperson3を代入。最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)
person1 = { first: '山田', last: '太郎' }
person2 = { first: '田中', last: '二郎' }
person3 = { first: '木村', last: '三郎' }
params = { father: person1, mother: person2, child: person3 }
puts params[:father][:first]
puts params[:mother][:last]
puts params[:child][:first]
userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています
user = { name: 'ふくしま', email: 'example@example.com', password_digest: ('a'..'z').to_a.shuffle[0..15].join }
puts user[:name]
puts user[:email]
puts user[:password_digest]
「Ruby API」や「るりまサーチ」を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう
省略
まじかよ!
実は、Ruby ではメソッド呼び出しの丸カッコは省略しても構いません。次の2つの行は等価です
# メソッド呼び出しの丸カッコは省略可能。
stylesheet_link_tag("application", "data-turbo-track": "reload")
# 上は以下のように書いても同じ
stylesheet_link_tag "application", "data-turbo-track": "reload"
隠してんじゃね〜よ!
def plus (x, y)
puts x + y
end
# 以下2つは同じ。
plus(2, 8)
plus 2, 8
ハッシュがメソッド呼び出しの最後の引数である場合は、波カッコを省略できます。
隠し事多いな。
# 最後の引数がハッシュの場合、波カッコは省略可能。
stylesheet_link_tag("application", { "data-turbo-track": "reload" })
# 上は以下のように書いても同じ
stylesheet_link_tag("application", "data-turbo-track": "reload")
ってことは、stylesheet_link_tag
は第1引数に文字列、第2引数にハッシュを求めるんだ。
cookpadのスタイルガイドいいね
4.4.1 コンストラクタ
1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか?(復習です)
num = (1 .. 10)
今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。(ヒント: newメソッドに2つの引数を渡す必要があります。)
num2 = Range.new(1, 10)
比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください
puts num == num2
true
=> nil
4.4.2クラスの継承
Rangeクラスの継承階層を調べてみてください。同様にして、HashとSymbolクラスの継承階層も調べてみてください
num = Range.new(1, 10)
num.class.superclass
num.class.superclass.superclass
hush = {}
hush.class
hush.class.superclass
hush.class.superclass.superclass
sym = :sym
sym.class
sym.class.superclass
sym.class.superclass.superclass
リスト 4.15にあるself.reverseのselfを省略し、reverseと書いてもうまく動くことを確認してみてください
class Word < String
def palindrome?
self == reverse
end
end
=> :palindrome?
s = Word.new("level")
s.palindrome?
驚いたことに、Rubyに組み込まれている基本クラスは拡張可能なのです。
!?
組み込みクラスの変更はきわめて強力なテクニックですが、大いなる力には大いなる責任が伴います27 。このため、真に正当な理由がない限り、組み込みクラスにメソッドを追加することは無作法であると考えられています。
4.4.3組み込みクラスの変更
palindrome?メソッドを使って、“racecar”が回文であり、“onomatopoeia”が回文でないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか? (ヒント: downcaseメソッドで小文字にすることをお忘れなく。)
class String
def palindrome?
self == reverse
end
end
"racecar".palindrome?
"onomatopoeia".palindrome?
"Malayalam".downcase.palindrome?
リスト 4.16を参考に、Stringクラスにshuffleメソッドを追加してみてください。(ヒント: リスト 4.12も参考になります。)
class String
def shuffle
self.split('').shuffle.join
end
end
"foobar".shuffle
リスト 4.16のコードにおいて、self.を削除してもうまく動くことを確認してください
class String
def shuffle
split('').shuffle.join
end
end
"foobar".shuffle
4.4.4コントローラクラス
第2章で作ったToyアプリケーションのディレクトリでRailsコンソールを開き、User.newと実行することでuserオブジェクトが生成できることを確認してみましょう
生成したuserオブジェクトのクラスの継承階層を調べてみてください
>> user = User.new
=> #<User:0x00007fd03b3cd670 id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> user.class
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime)
=> ApplicationRecord(abstract)
=> ActiveRecord::Base
=> Object
=> BasicObject
=> nil
>>
<%= link_to "sample app", '#', id: "logo" %>
link_to
は<a>
タグの作成。
引数は、
- リンクテキスト
- リンク先URL
- html idの設定
演習 5.1構造を追加する
Webページと言ったらネコ画像、というぐらいにはWebにはネコ画像が溢れていますよね。リスト 5.4のコマンドを使って、図 5.3のネコ画像をダウンロードしてきましょう11
curl -OL https://cdn.learnenough.com/kitten.jpg
mvコマンドを使って、ダウンロードしたkitten.jpgファイルを適切なアセットディレクトリに移動してください(参考: 5.2.1)
mv kitten.jpg app/assets/images/
image_tagを使って、kitten.jpg画像を表示してみてください(図 5.4)
# /sample_app/app/views/static_pages/home.html.erb
# 以下追記
<%= link_to image_tag("kitten.jpg", alt: "子猫", width: "200") %>
演習 5.1.2BootstrapとカスタムCSS
リスト 5.10を参考にして、5.1.1.1の演習で使ったネコ画像をコメントアウトしてみてください。また、ブラウザのHTMLインスペクタ機能を使って、コメントアウトするとHTMLのソースからも消えていることを確認してみてください
<%#= image_tag("kitten.jpg", alt: "Kitten") %>
HTMLのソースからも消えていることを確認は省略。(dev toolsで確認)
リスト 5.11のコードをcustom.scssに追加し、すべての画像を非表示にしてみてください。うまくいけば、Railsのロゴ画像がHomeページから消えるはずです。先ほどと同様にインスペクタ機能を使って、今度はHTMLのソースコードは残ったままで、画像だけが表示されなくなっていることを確認してみてください。この演習による変更は、終わった後必ず元に戻してください(そうしないとこの後の画像が正しく表示されません)
省略。
パーシャル(partial)という機能
これはコンポーネント的なことか?
<%= render 'layouts/header' %>
これで、app/views/layouts/_header.html.erb
を表示する。
ファイル名_header.html.erbの先頭にあるアンダースコアに注目してください。このアンダースコアは、パーシャルで使う普遍的な命名規約であり、また、一目見ただけでディレクトリ中のすべてのパーシャルを識別することが可能になります。
なるほど。
パーシャルで利用したい場合は、_
をファイル名につけて上げればいいのか。
演習 5.1.3パーシャル(partial)
Railsがデフォルトで生成するheadタグの部分を、リスト 5.17のようにrenderに置き換えてみてください。(ヒント: 単純に削除してしまうと後でパーシャルを1から書き直す必要が出てくるので、削除する前にどこかに退避しておきましょう。)
# app/views/layouts/application.html.erb
<%= render 'layouts/rails_default' %>
リスト 5.17のようなパーシャルはまだ作っていないので、現時点ではテストは red になっているはずです。実際にテストを実行して確認してみましょう
省略。
layoutsディレクトリにheadタグ用のパーシャルを作成し、先ほど退避しておいたコードを書き込み、最後にテストが green に戻ることを確認しましょう
touch app/views/layouts/_rails_default.html.erb
# app/views/layouts/_rails_default.html.erbの中身をいかに変更
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
テストはgreenになったことを確認。
アセットディレクトリ
Railsのアセットパイプラインでは、静的ファイルを目的別に分類する、標準的な3つのディレクトリが使われています。
-
app/assets
: 現在のアプリケーション固有のアセット -
lib/assets
: あなたの開発チームによって作成されたライブラリ用のアセット -
vendor/assets
: サードパーティのアセット(デフォルトでは存在しません)
静的ファイル(アセット)を上記の場所へそれぞれ配置すれば、マニフェストファイルを使って、それらをどのように1つのファイルにまとめるのかをRailsに指示することができます。
実際にアセットをまとめる処理を行うのはSprocketsというgemです。
また、マニフェストファイルはCSSとJavaScriptには適用されますが、画像ファイルには適用されません。1つの具体例として、アプリケーションのCSS用マニフェストファイルを見てみましょう
ルーティングの修正
helpページへのルーティングは例えば以下のようにする。
# getで /helpにアクセスした時にstatic_pagesコントローラーのhelpメソッドが呼ばれる。
get "/help", to: "static_pages#help"
また、このように設定すると、
help_path
help_url
が利用できるようになる。
help_path -> '/help'
help_url -> 'https://www.example.com/help'
更にas
を利用すれば、名前をつけることも可能。
get "/help", to: "static_pages#help", as: 'helf'
演習 5.3.2RailsのルートURL
実は名前付きルーティングは、as:オプションを使って変更することができます。有名なFar Sideの漫画に倣って、Helpページの名前付きルーティングをhelfに変更してみてください(リスト 5.28)
# routes.rb
Rails.application.routes.draw do
root "static_pages#home"
get "/help", to: "static_pages#help", as: 'helf'
get "/about", to: "static_pages#about"
get "/contact", to: "static_pages#contact"
end
先ほどの変更により、テストが red になっていることを確認してください。リスト 5.27を参考にルーティングを更新して、テストを green にして見てください
test "should get help" do
get helf_path
assert_response :success
assert_select "title", "Help | #{@base_title}"
end
エディタのUndo機能を使って、今回の演習で行った変更を元に戻してください
省略
演習 5.3.3名前付きルーティング
リスト 5.28のようにhelfルーティングを作成し、レイアウトのリンクを更新してみてください
# routes.rb
Rails.application.routes.draw do
root "static_pages#home"
get "/help", to: "static_pages#help", as: 'helf'
get "/about", to: "static_pages#about"
get "/contact", to: "static_pages#contact"
end
# _header.html.erb
<nav>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", helf_path %></li>
<li><%= link_to "Log in", '#' %></li>
</ul>
</nav>
前回の演習と同様に、エディタのUndo機能を使ってこの演習で行った変更を元に戻してみてください
省略
統合テスト(Integration Test)
リンクが動くかチェックする。
$ rails generate integration_test site_layout
- ルートURL(Homeページ)にGETリクエストを送る。
- 正しいページテンプレートが描画されているかどうか確かめる。
- Home、Help、About、Contactの各ページへのリンクが正しく動くか確かめる。
/sample_app/test/integration/site_layout_test.rb
require "test_helper"
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
end
end
例えば、assert_select "a[href=?]", about_path
は?
の部分に第2引数のabout_path
を入れている。
結果、<a href="/about">...</a>
というリンクがHTMLの中にあるか確認する。
同じリンクがX個ある場合は、
count: X
と追記する。
assert_select HOGEHOGE
には他にもたくさん種類があるよ!
test実行は、$ rails test:integration
演習 5.3.4リンクのテスト
footerパーシャルのabout_pathをcontact_pathに変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認してみてください
省略。なった。
リスト 5.34で示すように、Applicationヘルパーで使っているfull_titleヘルパーを、test環境でも使えるようにすると便利です。こうしておくと、リスト 5.35のようなコードを使って、正しいタイトルをテストすることができます。ただし、これは完璧なテストではありません。例えばベースタイトルに「Ruby on Rails Tutoial」といった誤字があったとしても、このテストでは発見することができないでしょう。この問題を解決するためには、full_titleヘルパーに対するテストを書く必要があります。そこで、Applicationヘルパーをテストするファイルを作成し、リスト 5.36の(コードを書き込む)の部分を適切なコードに置き換えてみてください。(ヒント: リスト 5.36ではassert_equal <期待される値>, <実際の値>といった形で使っていましたが、内部では==演算子で期待される値と実際の値を比較し、正しいかどうかのテストをしています。)
# test/helpers/application_helper_test.rb
require 'test_helper'
class ApplicationTest < ActionView::TestCase
test "full title helper" do
assert_equal full_title, "Ruby on Rails Tutorial Sample App"
assert_equal full_title("Help"), "Help | Ruby on Rails Tutorial Sample App"
end
end
演習 5.4.1Usersコントローラ
表 5.1を参考にしながらリスト 5.40を変更し、users_new_urlではなくsignup_pathを使えるようにしてみてください
# routes.rb
Rails.application.routes.draw do
root "static_pages#home"
get "/help", to: "static_pages#help"
get "/about", to: "static_pages#about"
get "/contact", to: "static_pages#contact"
# user
get '/signup', to: 'users#new'
end
# users_controller_test.rb
require "test_helper"
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get signup_path
assert_response :success
end
end
先ほどの変更を加えたことにより、テストが red になったことを確認してください。なお、この演習はテスト駆動開発(コラム 3.3)で説明した red / green のリズムを作ることを目的としています。このテストは次の5.4.2で green になるよう修正します
省略
5.4.2ユーザー登録用URL
もしまだ5.4.1.1の演習に取り掛かっていなければ、まずはリスト 5.40のように変更し、名前付きルーティングsignup_pathを使えるようにしてください。また、リスト 5.42で名前付きルーティングが使えるようになったので、現時点でテストが green になっていることを確認してください
対応済みなので省略
先ほどのテストが正しく動いていることを確認するため、signupルートの部分をコメントアウトし、テスト red になることを確認してください。確認できたら、コメントアウトを解除して green の状態に戻してください
省略(問題なくできた。
リスト 5.31の統合テストにsignupページにアクセスするコードを追加してください(getメソッドを使います)。コードを追加したら実際にテストを実行し、結果が正しいことを確認してください。(ヒント: リスト 5.35で紹介したfull_titleヘルパーを使ってみてください。)
# site_layout_test.rb
require "test_helper"
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
assert_select "a[href=?]", signup_path
get contact_path
assert_select "title", full_title("Contact")
get signup_path
assert_select "title", full_title("Sign up")
end
end
model作成は、
$ rails generate model User name:string email:string
name:stringやemail:stringオプションのパラメータを渡すことによって、データベースで使いたい2つの属性をRailsに伝えます。このときに、これらの属性の型情報(この場合はstring)も一緒に渡します。
db/migrate/20230206131623_create_users.rb
といった感じで作成される。
テーブル名は通常複数形。
timestampsは自動(creared_at,updated_at)
ここらへんも、laravelのmigrationにそっくり。
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
これらが作成されたら、マイグレーションの実行を行う。
$ rails db:migrate
演習 6.1Userモデル
省略。
確認してね系だけしかないので。
演習 6.1.2モデルファイル
Railsコンソールを開き、User.newでUserクラスのオブジェクトが生成されること、そしてそのオブジェクトがApplicationRecordを継承していることを確認してみてください。(ヒント: 4.4.4で紹介したテクニックを使ってみてください。)
同様の方法で、ApplicationRecordがActiveRecord::Baseを継承していることも確認してみてください
>> User.new
>> user.class.superclass
=> ApplicationRecord(abstract)
>> user.class.superclass.superclass
=> ActiveRecord::Base
>> user.class.superclass.superclass.superclass
=> Object
>> user.class.superclass.superclass.superclass.superclass
=> BasicObject
>> user.class.superclass.superclass.superclass.superclass.superclass
=> nil
>>
consoleにてサンドボックス状態(DBに保存されない = rollbackされる)で利用するには。
$ rails console --sandbox
6.1.3ユーザーオブジェクトを作成する
user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください
>> user.name.class
=> String
>> user.email.class
=> String
created_atとupdated_atは、どのクラスのインスタンスでしょうか?
>> user.created_at.class
=> ActiveSupport::TimeWithZone
>> user.updated_at.class
=> ActiveSupport::TimeWithZone
userの作成方法
1つ目
user = User.new(hogefuga)
user.save
2つ目
User.create(hogefuga)
userの検索
# idで検索
User.find(1)
# 属性を指定して検索
User.find_by(email: "michael@example.com")
# 最初のデータを取得
User.first
# 全部取得
User.all
更新
user.email = "foo@bar.com"
user.save
わざわざsaveめんどい
user.update(name: "The Dude", email: "dude@abides.org")
バリデーションが1つでも失敗すると、updateの呼び出しは失敗するので特定の属性だけやりたい場合は↓
user.update_attribute(:name, "El Duderino")
演習 6.1.5ユーザーオブジェクトを更新する
userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください
>> user
=>
id: 1, _attribute(:name, "El Duderino")
name: "El Duderino", SAVEPOINT active_record_1
email: "dude@abides.org",ATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "El Duderino"], ["updated_at", "2023-02-06 13:47:02.208205 created_at: Mon, 06 Feb 2023 13:31:14.133761000 UTC +00:00,
updated_at: Mon, 06 Feb 2023 13:47:02.208205000 UTC +00:00>
>>
>> user.name = "pero"
=> "pero"
user.save
TRANSACTION (0.1ms) SAVEPOINT active_record_1
User Update (0.2ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "pero"], ["updated_at", "2023-02-06 13:50:22.800382"], ["id", 1]]
TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1
=> true
>>
>> user
=>
#<User:0x00007fee2432baf82023 13:47:02.208205000 UTC +00:00>
id: 1, "pero"
name: "pero",
email: "dude@abides.org",EPOINT active_record_1
created_at: Mon, 06 Feb 2023 13:31:14.133761000 UTC +00:00,ed_at" = ? WHERE "users"."id" = ? [["name", "pero"], ["updated_at", "2023-02-06 13:50:22.800382"], ["i updated_at: Mon, 06 Feb 2023 13:50:22.800382000 UTC +00:00>
>>
今度はupdateを使って、email属性を更新および保存してみてください
user.update(email: "test@example.com")
TRANSACTION (0.1ms) SAVEPOINT active_record_1
User Update (0.1ms) UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ? [["email", "test@example.com"], ["updated_at", "2023-02-06 13:51:23.843930"], ["id", 1]]
TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1
=> true
>> user
=> #<User:0x00007fee2432baf82023 13:31:14.133761000 UTC +00:00,ed_at" = ? WHERE "users"."id" = ? [["name", "pero"], ["updated_at", "2023-02-06 13:50:22.800382"], ["i id: 1, on, 06 Feb 2023 13:50:22.800382000 UTC +00:00>
name: "pero",(email: "test@example.com")
email: "test@example.com",POINT active_record_1
created_at: Mon, 06 Feb 2023 13:31:14.133761000 UTC +00:00,ted_at" = ? WHERE "users"."id" = ? [["email", "test@example.com"], ["updated_at", "2023-02-06 13:51:23 updated_at: Mon, 06 Feb 2023 13:51:23.843930000 UTC +00:00>
>>
同様に、マジックカラムであるcreated_atも直接更新できることを確認してみてください。(ヒント: 1.year.agoで更新すると便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の日時を算出してくれます。)
user.update_attribute(:created_at, 1.year.ago)
>> user
=>
#<User:0x00007fee2432baf82023 13:31:14.133761000 UTC +00:00,ted_at" = ? WHERE "users"."id" = ? [["email", "test@example.com"], ["updated_at", "2023-02-06 13:51:23 id: 1, on, 06 Feb 2023 13:51:23.843930000 UTC +00:00>
name: "pero",_attribute(:created_at, 1.year.ago)
email: "test@example.com",POINT active_record_1
created_at: Sun, 06 Feb 2022 13:52:36.331750000 UTC +00:00,"updated_at" = ? WHERE "users"."id" = ? [["created_at", "2022-02-06 13:52:36.331750"], ["updated_at", updated_at: Mon, 06 Feb 2023 13:52:36.332187000 UTC +00:00>
>>
演習 6.2.2存在性を検証する
nameもemailも空の新しいユーザーuを作成し、作成した時点では有効ではない(invalid)ことを確認してください。なぜ有効ではないのでしょうか? エラーメッセージを確認してみましょう。
>> u = User.new(name: " ", email: " ")
=> #<User:0x00007f1811459c98 id: nil, name: " ", email: " ", created_at: nil, updated_at: nil>
>> u.save
=> false
>> u.valid?
=> false
>> u.errors.messages
=> {:name=>["can't be blank"], :email=>["can't be blank"]}
>>
u.errors.messagesを実行すると、ハッシュ形式でエラーが取得できることを確認してください。emailに関するエラー情報だけを取得したい場合、どうやって取得すれば良いでしょうか?
>> u.errors.messages[:email]
=> ["can't be blank"]
演習 6.2.2存在性を検証する
nameもemailも空の新しいユーザーuを作成し、作成した時点では有効ではない(invalid)ことを確認してください。なぜ有効ではないのでしょうか? エラーメッセージを確認してみましょう。
>> u = User.new(name: " ", email: " ")
=> #<User:0x00007f1811459c98 id: nil, name: " ", email: " ", created_at: nil, updated_at: nil>
u.name = 'testtest' * 10
=> "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"
u.email = 'test' * 100 + 'example.co.jp'
=> "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestte...
>> u.save
=> false
u.errors.messages
=>
{:name=>
["is too long (maximum is 50 characters)",
"is too long (maximum is 50 characters)",
"is too long (maximum is 50 characters)",
"is too long (maximum is 50 characters)"],
:email=>
["is too long (maximum is 255 characters)",
"is too long (maximum is 255 characters)",
"is too long (maximum is 255 characters)",
"is too long (maximum is 255 characters)"]}
>>
長さに関するバリデーションが失敗した時、どんなエラーメッセージが生成されるでしょうか? 確認してみてください。
省略
6.2.4フォーマットを検証する
Rubularの対応なので、省略
6.3.2ユーザーがセキュアなパスワードを持っている
この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、valid?で失敗してしまうことを確認してみてください。
>> user = User.new(name: "test", email: "test@example.com")
=> #<User:0x00007fa33da616d8 id: nil, name: "test", email: "test@example.com", created_at: nil, updated_at: nil, password_digest: nil>
user.valid?
User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "test@example.com"], ["LIMIT", 1]]
=> false
なぜ失敗してしまうのでしょうか? エラーメッセージを確認してみてください
>> user.errors.messages
=> {:password=>["can't be blank"]}
6.3.3パスワードの最小文字数
有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。
上で失敗した時、どんなエラーメッセージになるでしょうか? 確認してみましょう
user = User.new(name: "test", email: "test@example.com", "password": "abc")
Loading development environment (Rails 7.0.4)
=> #<User:0x00007f12c5d09678 id: nil, name: "test", email: "test@example.com", created_at: nil, updated_at: nil, password_digest: "[FILTERED]">
user.valid?
User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "test@example.com"], ["LIMIT", 1]]
=> false
>> user.errors.messages
=> {:password=>["is too short (minimum is 6 characters)"]}
debugは以下で行ける。
var_dump的な。
<%= debug(params) if Rails.env.development? %>
7.1.1デバッグとRails環境
ブラウザで/aboutにアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか?paramsの内容から確認してみましょう。
params
には以下の記述がある。
#<ActionController::Parameters {"controller"=>"static_pages", "action"=>"about"} permitted: false>
controller
はstatic_pages
、action
はabout
というのがわかる。
Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。
多分、
puts user.attributes.to_yaml
と、
y user.attributes
は内容変わってないっぽい
>> user = User.find(1)
User Load (1.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
#<User:0x00007ff452f192c8
...
>>
>> puts user.attributes.to_yaml
---
id: 1
name: " "
email: " "
created_at: !ruby/object:ActiveSupport::TimeWithZone
utc: 2023-02-11 08:38:40.308540000 Z
zone: &1 !ruby/object:ActiveSupport::TimeZone
name: Etc/UTC
time: 2023-02-11 08:38:40.308540000 Z
updated_at: !ruby/object:ActiveSupport::TimeWithZone
utc: 2023-02-11 08:38:40.308540000 Z
zone: *1
time: 2023-02-11 08:38:40.308540000 Z
password_digest:
=> nil
>>
>> y user.attributes
---
id: 1
name: " "
email: " "
created_at: !ruby/object:ActiveSupport::TimeWithZone
utc: 2023-02-11 08:38:40.308540000 Z
zone: &1 !ruby/object:ActiveSupport::TimeZone
name: Etc/UTC
time: 2023-02-11 08:38:40.308540000 Z
updated_at: !ruby/object:ActiveSupport::TimeWithZone
utc: 2023-02-11 08:38:40.308540000 Z
zone: *1
time: 2023-02-11 08:38:40.308540000 Z
password_digest:
=> nil
>>
user = User.find(1)
ルーティングに
resources :users
の1行追加するだけでREST fulなルートが提供される!
laravelの
Route::resource('rest','RestappController');
だ。
7.1.2Usersリソース
ERBを使って、マジックカラム(created_atとupdated_at)の値をshowページに表示してみましょう(リスト 7.4)。
<%= @user.name %>, <%= @user.email %>
<br>
<%= @user.created_at %>, <%= @user.updated_at %>
ERBを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると結果がどう変わるかも確認してみてください。
<%= @user.name %>, <%= @user.email %>
<br>
<%= @user.created_at %>, <%= @user.updated_at %>
<br>
<%= Time.now %>
→ 多分UTCっぽい時刻が表示されている。
laravelのdd()
はdebugger
ぽい
※consoleに表示された。