- 1. 7.1.1 デバッグとRails環境
- 2. 7.1.2 Usersリソース
- 3. 7.1.3 debuggerメソッド
- 3.1. 1.showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?
- 3.2. 2.newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。
- 4. 7.1.4 Gravatar画像とサイドバー
- 4.1. 1.(任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
- 4.2. 2. 7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
- 4.3. 3.オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
- 5. 7.2.1 form_forを使用する
- 6. 7.2.2 フォームHTML
- 7. 7.3.2 Strong Parameters
- 8. 7.3.3 エラーメッセージ
- 9. 7.3.4 失敗時のテスト
- 9.1. 1.リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
- 9.2. 2.未送信のユーザー登録フォームと送信直後のURLは、それぞれ /signup と /users になり、URLが異なっています。これは、リスト 5.43で追加した名前付きルートと、デフォルトのRESTfulなルーティング (リスト 7.3) を設定したことによって生じた差異です。リスト 7.26とリスト 7.27の内容を追加し、この問題を解決してみてください。うまくいけば、いずれのURLも /signup となるはずです。あれ、でもテストは greenのままになっていますね…、なぜでしょうか? (考えてみてください)
- 9.3. 3.リスト 7.25のpost部分を変更して、先ほどの演習課題で作った新しいURL (/signup) に合わせてみましょう。また、テストが greenのままになっている点も確認してください。
- 9.4. 4.リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、’form[action=”/signup”]’という部分が存在するかどうかに着目してテストしてみましょう。
- 10. 7.4.1 登録フォームの完成
- 11. 7.4.2 flash
- 12. 7.4.3 実際のユーザー登録
- 13. 7.4.4 成功時のテスト
- 13.1. 1.7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。
- 13.2. 2.本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
- 13.3. 3.リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
- 13.4. 4.リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
- 14. 7.5.3 本番環境へのデプロイ
7.1.1 デバッグとRails環境
1.ブラウザから /about にアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか? paramsの内容から確認してみましょう。
解答
デバッグ情報が表示されています。
controller:static_pages
action:about
と表示されていますね。
2.Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。
解答
結論、同じになります。
irb(main):002:0> puts user.attributes.to_yaml --- id: 1 name: Michael Hartl email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2018-07-03 12:39:58.464176000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2018-07-03 12:39:58.464176000 Z zone: *2 time: *3 password_digest: "$2a$10$BWPb8gf1HqTQkVKVWEmbfuEBNKHBk2R29RXtUrQhdL.vrKfbWEg6q" => nil irb(main):003:0> y user.attributes --- id: 1 name: Michael Hartl email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2018-07-03 12:39:58.464176000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2018-07-03 12:39:58.464176000 Z zone: *2 time: *3 password_digest: "$2a$10$BWPb8gf1HqTQkVKVWEmbfuEBNKHBk2R29RXtUrQhdL.vrKfbWEg6q" => nil
7.1.2 Usersリソース
1.埋め込みRubyを使って、マジックカラム (created_atとupdated_at) の値をshowページに表示してみましょう (リスト 7.4)。
解答
created_atとupdated_atを同じ要領で付け足せばOKです。
<%= @user.name %>,<%= @user.email %> <%= @user.created_at %>,<%= @user.updated_at %>
2.埋め込みRubyを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると、その結果はどう変わっていますか? 確認してみてください。
解答
Time.now付け足します。
行が変わるようにpタグで囲んでます。
<%= @user.name %>,<%= @user.email %> <%= @user.created_at %>,<%= @user.updated_at %> <p><%= Time.now %></p>
表示してみます。
しばらくして再読み込みします。
変わりましたねーめでたし。
7.1.3 debuggerメソッド
1.showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?
解答
普通にputsするだけだとハッシュですが、to_yamlで変換することによってYAML形式になります。
(byebug) puts params {"controller"=>"users", "action"=>"show", "id"=>"1"} nil (byebug) puts params.to_yaml --- !ruby/object:ActionController::Parameters parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess controller: users action: show id: '1' permitted: false nil
2.newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。
解答
debuggerをnewアクションに差し込みました。
[1, 10] in /home/vagrant/tutorial2/sample_app/app/controllers/users_controller.rb 1: class UsersController < ApplicationController 2: def show 3: @user = User.find(params[:id]) 4: #debugger 5: end 6: 7: def new 8: debugger => 9: end 10: end (byebug) puts @user nil
@userの内容はnilでした。
7.1.4 Gravatar画像とサイドバー
1.(任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
解答
調べてて分かったんですけど、
gravatar_url = “http://secure.gravatar.com/avatar/#{gravatar_id}”
が日本では
gravatar_url = “http://ja.gravatar.com/avatar/#{gravatar_id}”
となるようです。
こんな感じで自分のプロフィール画像を表示できました。
2. 7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
解答
リスト7.12に従えば良いです。
# 引数で与えられたユーザーのGravatar画像を返す def gravatar_for(user,options = { size: 80 }) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) size = options[:size] gravatar_url = "http://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" image_tag(gravatar_url,alt: user.name,class: "gravatar") end
3.オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
解答
リスト7.13に従えば良いです。
module UsersHelper # 引数で与えられたユーザーのGravatar画像を返す def gravatar_for(user, size: 80) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end
違いとしてはハッシュから変数になったことですね。
7.2.1 form_forを使用する
1.試しに、リスト 7.15にある:nameを:nomeに置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?
解答
NoMethodError in Users#new
が表示されます。
2.試しに、ブロックの変数fをすべてfoobarに置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobarとするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
解答
置換するだけなので置換後のコードは省略します。
その代わりvimの便利コマンド紹介します。
:%s/f/foobar/gc
:%s/検索文字列/置換文字列/gcとなります。
置換は:sでできます。:sだけだとカーソル位置以降を検索しますが、
:%sとすることでカーソル位置に依存せずに頭から置換できます。
後ろのgは一行の中で行末まで全てを検索します。
cは1つずつ検索します。
上記コマンドはファイル内全ての[f]を1つずつ確認しながら[foobar]に置換するコマンドになります。
7.2.2 フォームHTML
1.Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜformタグを使わなかったのでしょうか? 理由を考えてみてください。
解答
formタグってのは入力・送信フォームを作成する時に使うタグですね。
Learn Enough HTML to Be Dangerousの中では入力・送信フォームを作成していないですね。
なので、解答としては入力・送信フォームを作成していないから。
ということで良いかと思います。
7.3.2 Strong Parameters
1./signup?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。
解答
パラメータ情報にadminが含まれています。
debuggerで確認してもわかりますね。
7.3.3 エラーメッセージ
1.最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。
解答
というわけで最小文字数を5にしてみます。
class User < ApplicationRecord #before_save {self.email = email.downcase} before_save {email.downcase!} validates :name,presence: true,length: {maximum:50} #VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i validates :email,presence: true,length: {maximum:255}, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false} has_secure_password validates :password, presence: true, length: { minimum: 5 } end
「Password is too short (minimum is 5 characters)」
に変わりました。
2.未送信のユーザー登録フォーム (図 7.12) のURLと、送信済みのユーザー登録フォーム (図 7.18) のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。
解答
ルーティング見てみましょう。
$ rails routes Prefix Verb URI Pattern Controller#Action root GET / static_pages#home help GET /help(.:format) static_pages#help about GET /about(.:format) static_pages#about contact GET /contact(.:format) static_pages#contact signup GET /signup(.:format) users#new users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy
最初は/signupですが、エラー時にはcreateアクションが/usersを呼び出します。
renderはnewを通りますが、renderがviewを呼び出すときはアクションを経由しないために
URLはcreateアクションの時に指定されてる/usersとなります。
んー説明が下手な気がしますね…。申し訳ないです。
7.3.4 失敗時のテスト
ここの演習問題結構難易度高めかと思います。
1.リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
解答
assert_selectのセレクタを書く練習ですね。
テンプレートに従って、今回は
- id:error_explanation
- class:alert
- class:alert-danger
の3つが定義されていることを確認することにしましょう。
class UserSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path assert_no_difference 'User.count' do post users_path, params: { user: { name: "", email: "user@invalid", password: "foo", password_confirmation: "bar"}} end assert_template 'users/new' assert_select 'div#error_explanation' assert_select 'div.alert' assert_select 'div.alert-danger' end end
リスト7.25を参考に書いてみました。
$rails test (中略) # Running: . Finished in 0.84298s 19 tests, 43 assertions, 0 failures, 0 errors, 0 skips
テストも通っています。
2.未送信のユーザー登録フォームと送信直後のURLは、それぞれ /signup と /users になり、URLが異なっています。これは、リスト 5.43で追加した名前付きルートと、デフォルトのRESTfulなルーティング (リスト 7.3) を設定したことによって生じた差異です。リスト 7.26とリスト 7.27の内容を追加し、この問題を解決してみてください。うまくいけば、いずれのURLも /signup となるはずです。あれ、でもテストは greenのままになっていますね…、なぜでしょうか? (考えてみてください)
解答
リスト 7.26とリスト 7.27の内容を追加すればURLは合わさります。
ルーティングを確認すればテストがgreenになることがわかります。
$ rails routes Prefix Verb URI Pattern Controller#Action root GET / static_pages#home help GET /help(.:format) static_pages#help about GET /about(.:format) static_pages#about contact GET /contact(.:format) static_pages#contact signup GET /signup(.:format) users#new POST /signup(.:format) users#create users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy
users_pathのcreateアクションが残っています。
なので、テストに影響を及ぼさずgreenとなります。
3.リスト 7.25のpost部分を変更して、先ほどの演習課題で作った新しいURL (/signup) に合わせてみましょう。また、テストが greenのままになっている点も確認してください。
解答
testの中のusers_pathをsignup_pathに書き換えます。
require 'test_helper' class UserSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path assert_no_difference 'User.count' do post signup_path, params: { user: { name: "", email: "user@invalid", password: "foo", password_confirmation: "bar"}} end assert_template 'users/new' assert_select 'div#error_explanation' assert_select 'div.alert' assert_select 'div.alert-danger' end end
テストも通ります。
$rails test (中略) Finished in 0.871412s, 21.8037 runs/s, 49.3452 assertions/s. 19 runs, 43 assertions, 0 failures, 0 errors, 0 skips
4.リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、’form[action=”/signup”]’という部分が存在するかどうかに着目してテストしてみましょう。
解答
リスト7.20の状態に戻して以下を追加します。
assert_select 'form[action="/signup"]'
テストがREDになります。
その後リスト7.27に戻してテストするとGREENになります。
7.4.1 登録フォームの完成
1.有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
解答
省略(言われた通りやるだけなのでやってみてください!)
2.リスト 7.28を更新し、redirect_to user_url(@user)とredirect_to @userが同じ結果になることを確認してみましょう。
解答
省略(言われた通りやるだけなのでやってみてください!)
7.4.2 flash
1.コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば”#{:success}”といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。
解答
[success]と表示されます。
irb(main):007:0> "#{:success}" => "success"
2.先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
解答
flashだとハッシュで全て出てきます。
flash[:success],flash[:danger]は設定した文字列が出力されます。
flash:errorは空文字が出力されます。
irb(main):017:0> "#{flash}" => "{:success=>\"It worked!\", :danger=>\"It failed.\"}" irb(main):018:0> "#{flash[:success]}" => "It worked!" irb(main):019:0> "#{flash[:danger]}" => "It failed." irb(main):020:0> "#{flash[:error]}" => ""
7.4.3 実際のユーザー登録
1.Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
解答
指示に従うだけです。省略。
2.自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
解答
やってみましょう。いぇ〜い!
7.4.4 成功時のテスト
1.7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。
解答
空かどうか判定するためにはemptyメソッドを使います。
test "valid signup information" do get signup_path assert_difference 'User.count',1 do post signup_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password"}} end follow_redirect! assert_template 'users/show' assert_not flash.empty? end
2.本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
解答
指示に従うだけなので省略。
3.リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
解答
コメントアウトしてみました。
test "valid signup information" do get signup_path assert_difference 'User.count',1 do post signup_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password"}} end #follow_redirect! assert_template 'users/show' assert_not flash.empty? end
エラーになってますね。
Failure: UserSignupTest#test_valid_signup_information [/home/vagrant/tutorial2/sample_app/test/integration/user_signup_test.rb:30]: expecting <"users/show"> but rendering with <[]> bin/rails test test/integration/user_signup_test.rb:21 (中略) 20 tests, 46 assertions, 1 failures, 0 errors, 0 skips
4.リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
解答
パスワード一致しないようにしてみました。
test "valid signup information" do get signup_path assert_difference 'User.count',1 do post signup_path, params: { user: { name: "Example User", email: "user@example.com", password: "passwor", password_confirmation: "password"}} end #follow_redirect! assert_template 'users/show' assert_not flash.empty? end
User.countが変わらなかったぞ、と怒られました。
UserSignupTest#test_valid_signup_information [/home/vagrant/tutorial2/sample_app/test/integration/user_signup_test.rb:23]: "User.count" didn't change by 1. Expected: 1 Actual: 0 bin/rails test test/integration/user_signup_test.rb:21 Finished in 0.89278s 20 tests, 45 assertions, 1 failures, 0 errors, 0 skips
7.5.3 本番環境へのデプロイ
1.ブラウザから本番環境 (Heroku) にアクセスし、SSLの鍵マークがかかっているか、URLがhttpsになっているかどうかを確認してみましょう。
解答
鍵が付きましたー。
2.本番環境でユーザーを作成してみましょう。Gravatarの画像は正しく表示されているでしょうか?
解答
本番環境でもできましたー!
もしかしたら誤答もあるかもしれないな。。。
ちょっと不安になってきているのですが、続きつくっていきます!