rails tutorial 第7章


はじめに

独学でrails tutorialを進めていく過程を投稿していきます。

進めていく上でわからなかった単語、詰まったエラーなどに触れています。

個人の学習のアウトプットなので間違いなどあればご指摘ください。

初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。

第7章 ユーザー登録

7.1.2 Usersリソース

RESTアーキテクチャの習慣に従ってユーザー情報をWebアプリケーション上に表示する。

RESTってなんだっけ、、、


RESTとは、アプリケーションを構成するコンポーネント(ユーザーやマイクロポストなど)を「リソース」としてモデル化することを指します。これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除(Create/Read/Update/Delete: CRUD)操作と、4つの基本的なHTTP requestメソッド(POST/GET/PATCH/DELETE)の両方に対応しています。
コラム2.2(rails tutorial 2章より引用)


つまりはusersモデルをリソースとし、データ(作成、表示、更新、削除)操作とrequestメソッド(POST/GET/PATCH/DELETE)の両方に対応させるとういうイメージでしょうか。

そうすることで
・コントーラーやアクションの決定を簡略化
(リソースとすれば自動で設定してくれる)
・動的なWebページの作成
(ユーザー情報をWebアプリケーション上に表示するなど)
を実現できるということですかね。

usersモデルをリソースとする際はルーティングで

resources :users

と記入するようです。

7.2.2 フォームHTML

新規ユーザーの登録フォームを作りました。分かりにくかった所だけ補足します。

<%= form_with(model: @user, local: true) do |f| %>
    <%= f.label :name %>
    <%= f.text_field :name %>

    <%= f.label :email %>
    <%= f.email_field :email %>

    <%= f.label :password %>
    <%= f.password_field :password %>

    <%= f.label :password_confirmation, "Confirmation" %>
    <%= f.password_field :password_confirmation %>

    <%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>

form_withはデフォルトで“remote” XHR requestを送信しますが、ここではエラーメッセージをほぼ確実に表示するために通常の“local”フォームリクエストを送信したいのです( 7.3.3)。(rails tutorial 7章より引用)


どういうことだろう、、、
“remote” XHR requestって??

調べました。
XHRはAjaxと呼ばれるようなものらしい。
そしてajaxとは非同期通信のことで、ざっくり言うとjs形式のリクエストによってページ遷移を行わずページの一部の表示を変更したりできるものらしい。

参考
https://qiita.com/__tambo__/items/45211df065e0c037d032

rails tutorialではエラーメッセージをほぼ確実に表示するためhtml形式のリクエスト、つまり通常のlocalフォームリクエストを送信したいようです。
なぜremoteリクエストだとエラーメッセージの表示が確実ではないのだろう、、、
まぁ今作成しているsample_appではlocalリクエストで十分というのはわかりますけれど。

余談ですがrails 5以前のform_tagやform_forではデフォルトがリモートフォームではなくローカルフォームだったようです。


次にHTMLのフォームタグについても無知でしたので調べました。

参考記事①
http://www.htmq.com/html5/form.shtml
フォームタグについてのざっくりとした説明。

参考記事②
https://developer.mozilla.org/ja/docs/Learn/Forms/How_to_structure_an_HTML_form
labelついてなども書かれています。


ブロック変数(f)はform_withの第一引数に渡したオブジェクト(今回で言えば@user)の属性に対するinputタグを生成するメソッドを呼び出すもの。

参考
https://rakuda3desu.net/rakudas-rails-tutorial7-2/


Railsは@userのクラスがUserであることを認識します。また、@userは新しいユーザーなので、 Railsはpostメソッドを使ってフォームを構築すべきだと判断します。(rails tutorial 7章より引用)]

この部分についてどういうことか調べました。

参考記事①
https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2
関連するモデルがあり@userがDBに存在するときはupdateアクションに、ないときはcreateアクションに飛ぶということのようです。

参考記事②
https://pikawaka.com/rails/form_with
こちらの記事もわかりやすかったです。

コントローラーで作成したインスタンスがnewメソッドで新たに作成されて何も情報を持っていなければ自動的にcreateアクションへ、findメソッドなどで作成され、すでに情報を持っている場合はupdateアクションへ自動的に振り分けてくれます。(上の記事より引用)

大分よく理解出来た気がします、、、

7.3.1 正しいフォーム

paramsには複数のハッシュに対するハッシュ(hash-of-hashes: 入れ子になったハッシュ)が含まれます。デバッグ情報では、フォーム送信の結果が、送信された値に対応する属性とともにuserハッシュに保存されています。このハッシュのキーが、inputタグにあったname属性の値になります。例えば次のように

<input id="user_email" name="user[email]" type="email" />

"user[email]"という値は、userハッシュの:emailキーの値と一致します。(rails tutorial 7章より引用)]

ここ大事なポイント!!

7.3.2 Strong Parameters

params.require(:user).permit(:name, :email, :password, :password_confirmation)

requireメソッドって?
受け取るパラメータ群を指定

permitメソッドって?
利用可能なパラメータ名を指定

参考
https://techacademy.jp/magazine/22078

7.3.4 失敗時のテスト

演習1
エラー発生!!

test/integration/users_signup_test.rb
#リスト 7.25: エラーメッセージをテストするためのテンプレート
require 'test_helper'
class UsersSignupTest < 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#<CSS id for error explanation>'
    assert_select 'div.<CSS class for field with error>'
  end
  .
  .
  .
end

リスト7.25を記述してtestを行ったら

ERROR["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x000000000adddc40 @name="UsersSignupTest">, 0.3698949000099674]
 test_invalid_signup_information#UsersSignupTest (0.37s)
Nokogiri::CSS::SyntaxError:         Nokogiri::CSS::SyntaxError: unexpected '#' after '[#<Nokogiri::CSS::Node:0x000000000addfc70 @type=:ELEMENT_NAME, @value=["div"]>]'
            test/integration/users_signup_test.rb:13:in `block in <class:UsersSignupTest>'

とエラーが、、

リスト7.25ので追記されているは

assert_select 'div#<CSS id for error explanation>'
assert_select 'div.<CSS class for field with error>'

以上の2文です。

<CSS id for error explanation>

あ。。。
ここはヒントが書かれているだけであって自分でそのidを書かないといけないじゃん、、、
ということで

assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'

と修正しました。

7.4.1 登録フォームの完成

app/controllers/users_controller.rb
#リスト 7.26: 保存とリダイレクトを行う、userのcreateアクション
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

redirect_to @user

上記のコードについて何となく意味はわかるが詳しく書かれている記事があるため参考にしました。

参考
https://qiita.com/Kawanji01/items/96fff507ed2f75403ecb

しかし、 チュートリアル5.3.2 によると、Railsの規約では、基本的にはリンクには相対パスを使うべきですが、リダイレクトのリンクでは絶対パスを利用すべきとのことです。(上の記事より引用)

あれそうだったっけ?と調べると

Railsチュートリアルでは一般的な規約に従い、基本的には_path書式を使い、リダイレクトの場合のみ_url書式を使うようにします。これはHTTPの標準としては、リダイレクトのときに完全なURLが要求されるためです。(rails tutorial 5章より引用)]

本当でした、、、

7.4.3 実際のユーザー登録

問題発生!!
データベースをリセットするため、

rails db:migrate:reset

を実行。
しかしリセットできない。

ermission denied @ apply2files - C:/environment/sample_app/db/development.sqlite3
Couldn't drop database 'db/development.sqlite3'
rails aborted!
Errno::EACCES: Permission denied @ apply2files - C:/Users/81801/environment/sample_app/db/development.sqlite3
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:drop:_unsafe
(See full trace by running task with --trace)

解決
以下の記事を参考に解決しました。
https://teratail.com/questions/67393
https://qiita.com/Toshiki23/items/f366504844fd22ad87d9

どうやらwindowsでデータベースにsqlite3を利用していると
rails dropコマンド
rails db:resetコマンド
rails db:migrate:resetコマンド
において問題が発生してしまうようです。
悲しいかなwindows...

記事にあるようdb/development.sqlite3ファイルをエディタのエクスプローラーより手動で消し(コマンドプロンプトではrmコマンドがフォローされていなかったため)、

rails db:create db:migrate

を改めて実行したところ

Database 'db/development.sqlite3' already exists
Database 'db/test.sqlite3' already exists

あれ?消したはずなのに残っている、、、
使用しているエディタからは確かにdevelopment.sqlite3ファイルは消えている、、、

、、、、どうしよう!!!!!
とりあえず削除したdevelopment.sqlite3ファイルの行方を捜してみました。

参考
https://maitakeramen.hatenablog.com/entry/2018/02/08/131755

ゴミ箱を探してもなぜか見当たらない、、、
(やっぱり削除されていないからか??)

探しに探し、windowsのエクスプローラーからsample_app/dbディレクトリを見てみるとdevelopment.sqlite3ファイルがありました!!

とりあえず開いているエディタのdbディレクトリにコピーしました。

試しにDB Browser for SQliteでデータを覗いて見たらusersテーブルのデータは空になっていました。

原因を調べてみましたがわかりませんでした。
とりあえずデータベースのリセットは達成したこととし、次に進むことにします、、、

余談
この後に、Git Bashを使えばLinuxコマンドも使えるということを教えてもらい、以降はそちらも使って学習を進めることとしました。

7.4.4 成功時のテスト

演習2
演習は問題なくクリア出来ましたが、content_tagヘルパーについて調べました。
content_tagヘルパー
content_tag(:要素名, 表示させる内容, オプション)

参考
https://techacademy.jp/magazine/42167

終わりに

内容が急に難しくなってきました。
tutorialでは当たり前に使われていたUserリソースのような概念を理解するのも大変でした。
form_withヘルパーを使ったフォームの生成も一度ではなかなか理解できず、何度か読み返しました。
度々エラーにも遭遇し、時間がかかりました。