目的
Ruby on Railsに触れる機会が出てきそうなので、その予習として行なったことをまとめること。
環境
前提
- Homebrew3系がインストールされていること(2系はM1Macでは動かないです)
- Rubyがインストールされていること(Macには最初から入ってる)
- yarnがインストールされていること
- Rialsに近しいMVCフレームワークを用いたweb開発経験がある方向け(説明を省いている箇所が多いです)
実装
プロジェクト作成
sudo npm install -g yarn
を実行- 前回の記事で作成したプロジェクトのルートディレクトリ上で
rails webpacker:install
を実行
Webpackerとは Webpackerは、汎用的なwebpackビルドシステムのRailsラッパーであり、標準的なwebpackの設定と合理的なデフォルト設定を提供します。
参考:
Webpackを使用してRuby on Rails上でJavaScript開発をするために必要な一連のまとまりを、標準で実装することができるgemパッケージみたいです。
- ここで下記のようなエラーが発生したため、対処
rails webpacker:install rails aborted! Don't know how to build task 'webpacker:install'
一応下記のようなissueが上がっているようですが、今回のインストール方法とは異なるため、一度初めから別のプロジェクトを作成してみます。 参考:
rails -v
でバージョン確認し、rails new sample-crud_app
でプロジェクト作成するという方法で行いますsudo npm install -g yarn
を再度実行しますrails webpacker:install
を再度実行します- 同様のエラーが確認されました
rails webpacker:install rails aborted! Don't know how to build task 'webpacker:install'
- ここで、時間をかけたくないので、記事の多いRails6系にダウングレードして検証してみます
gem uninstall rails
とgem uninstall railties -v '<バージョン>'
を実行し、rails -v
などでcommand not foundが表示されることを確認gem install -v 6.1.4.1 rails
を実行(今参照している記事のバージョンに準拠)rails -v
でバージョンが表示されることを確認rails new sample-crud_app
を再実行して、プロジェクトを作成します。- プロジェクトのディレクトリに移動して
rails webpacker:install
を再度実行します - 今度は行けました(最新バージョンはやはり不用意に触らない方が安全という教訓を得ました)
success Saved 0 new dependencies. ✨ Done in 1.13s. Webpacker successfully installed 🎉 🍰
続いて
rails webpacker:compile
を実行しますrails s
を実行して、http://127.0.0.1:3000
にアクセスし、welcomeページが表示されることを確認できればOKです
コントローラーの雛形作成
rails g controller <コントローラー名>
で作成app/controller
配下にコントローラーが作成されていることを確認
class QuestionsController < ApplicationController #質問一覧表示 def index end #質問の詳細ページ def show end #質問の作成 def new end #質問の登録 def create end #質問の更新 def update end #質問の編集 def edit end #質問の削除 def delete end end
config/routes.rb
に以下の記述を追加することで、ルーティングの自動生成を行ってくれる
resources :questions (←この名称は環境によって変わります)
rails routes
コマンドで確認
モデルの作成
- テーブル構成
rails g model<モデル名> name:string title:string content:text
で作成app/models
配下にモデルが作成されていることを確認app/db/migrate
配下にdbのスキーマが定義されていることを確認
class hogehoge < ActiveRecord::Migration[6.1] def change create_table :hoge do |t| t.string :name t.string :title t.text :content t.timestamps end end end
rails db:migrate
を実行し、app/db/schema.rb
ファイルに反映されていることを確認(rails dbconsoleコマンドでも確認できます)
ビューの作成
app/views/questions
配下にnew.html.erb
を作成(修飾子注意)rails routes
コマンドでnewへのパスを探して、アクセス。下記のようなページが表示されればOKです。
CREATE機能の実装
- Controllerに下記記述を追加
#質問の作成 def new @questions = Question.new end
- Viewを下記のように編集
<h1>質問新規作成画面です</h1> <%= form_with model: @quetion do |form| %> <div> <%= form.label :title%><br> <%= form.text_field :title %> </div> <div> <%= form.label :content%><br> <%= form.text_field :content %> </div> <div> <%= form.submit %><br> </div> <% end %>
- Form builderとかいう便利なものがあるんですね。
Action View フォームヘルパー Webアプリケーションのフォームは、ユーザー入力を扱うための重要なインターフェイスです。しかしフォームのマークアップは、フォームのコントロールの命名法や大量の属性を扱わなければならず、作成もメンテナンスも退屈な作業になりがちです。そこでRailsでは、フォームのマークアップを生成するビューヘルパーを提供し、こうした煩雑な作業を行わずに済むようにしました。しかし現実にはさまざまなユースケースがあるため、開発者はこれらを実際に使う前に、これらのよく似たヘルパーメソッド群にどのような違いがあるのかをすべて把握しておく必要があります。
参考:
- Controllerの登録用メソッドを下記のように編集します
#質問の登録 def create p params end
フォームに適当な値を入力して、登録ボタンを押下します
こんなエラーが出てきました。
/questions/new
createメソッドが呼ばれる場合のパスが/questions
になっていることが原因のRouting エラーみたいなので、routingの設定を明示すれば解決できるような気がします。(自動生成されているからこの辺りのエラーの原因とか、仕様とか把握しないと根本的な解決は厳しいですね。。。。)config/routes.rbファイルに下記のように記載したらいけるようです
post 'questions/new' => 'questions#create'
- ターミナルに下記のようなパラーメータが表示されていればOKです
"title"=>"hoge", "name"=>"hoge", "content"=>"hoeg", "commit"=>"Save "
- ただこれではフォームの改ざんが出来てしまうそうなので、ストロングパラーメータというものを使ってそれを防げるようなので実装します。コントローラに下記のように記載します。
private def questions_params params.require(:question).permit(:title, :name, :content) end
- また、下記のようにcreateメソッドを書き換えます
def create p questions_params end
- 動作確認をしてみると、以下のようなエラーになっているので対処します。
param is missing or the value is empty: question
- ここは単純にquestionが空のパラメータとなっているためエラーが発生しているっぽいので、下記のようにrequireメソッドを外してみます。
private def questions_params params.permit(:title, :name, :content) end
- そうするとエラーが変わりました。
Processing by QuestionsController#create as HTML Parameters: {"authenticity_token"=>"[FILTERED]", "title"=>"sadf", "name"=>"sdfasdf", "content"=>"sdafsa", "commit"=>"Save "} Unpermitted parameters: :authenticity_token, :commit #<ActionController::Parameters {"title"=>"sadf", "name"=>"sdfasdf", "content"=>"sdafsa"} permitted: true> No template found for QuestionsController#create, rendering head :no_content
ここでは
:authenticity_token, :commit
が許可されていないパラメータであること、createメソッドを読んだ際にレンダリングするテンプレートファイルが無いという二点が問題のようです。まずはコントローラーにリダイレクト処理を実装して、View配下に適当なテンプレートファイルを作成します。
controller
#質問の登録 def create # モデルの初期化 @question = Question.new(questions_params) # モデルをDBへ保存 @question.save # showアクションへリダイレクト redirect_to @question end
show.html.erb
<h2>Show</h2>
- フォームに値を入力して、submitすると、正常に遷移されて、コンソールを見ると正常にINSERTクエリが走っていることが分かります。(permit errorは何だったのか。。。)
Question Create (1.7ms) INSERT INTO "questions" ("name", "title", "content", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "sdfdsa"], ["title", "safs"], ["content", "sfdsadf"], ["created_at", "2022-02-07 01:22:33.402158"], ["updated_at", "2022-02-07 01:22:33.402158"]]
- 試しに名前入力フォームのname要素を下記のように書き換えてsubmitしてみると、コンソールに下記のようなエラーが出て入力が弾かれます。
Unpermitted parameters: :authenticity_token, :hoge, :commit
READ機能の実装
- showメソッドを下記のように編集
#質問の詳細ページ def show @question = Question.find(params[:id]) p @question end
- 先ほど登録したデータのIDを引数に渡すことで下記のように一件データを取得することができます。
Question Load (0.3ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
- show.html.erbファイルを下記のように編集し、画像のような画面が表示されればOKです。
<h2>Show</h2> <p> Title: <br> <%= @question.title %> </p> <p> Name: <br> <%= @question.name %> </p> <p> Content: <br> <%= @question.content %> </p>
続いて、一覧表示機能も実装していきます。
- indexメソッドを下記のように編集
#質問一覧表示 def index @questions = Question.all p @questions end
- index.html.erbファイルを作成
<h2>質問一覧</h2> <table> <tr> <th>Title</th> <th>Name</th> <th>Content</th> </tr> <% @questions.each do |question| %> <tr> <td><%= question.id %></td> <td><%= question.title %></td> <td><%= question.name %></td> <td><%= question.content %></td> <td><%= link_to 'show' , question_path(question) %></td> </tr> <% end %> </table>
- link_toヘルパーメソッドを利用することで、rails routesで表示されるメソッドをidをパスに含むことで実現できちゃうみたいです
question GET /questions/:id(.:format) questions#show PATCH /questions/:id(.:format) questions#update PUT /questions/:id(.:format) questions#update DELETE /questions/:id(.:format) questions#destroy
- 下記のような一覧画面が表示されること・リンクが正常に動作することを確認できればOKです。
UPDATE機能の実装
- updateメソッド、editメソッドを下記のように編集
#質問の更新 def update @question = Question.find(params[:id]) if @question.update(questions_params) # 更新が成功したら詳細画面にリダイレクト redirect_to @question else # 更新に失敗したら編集画面を描画 render 'edit' end end #質問の編集 def edit @question = Question.find(params[:id]) end
- edit.html.erbファイルを作成
<h2>編集画面</h2> <%= form_with model: @quetion do |form| %> <div> <%= form.label :title %><br> <%= form.text_field :title %> </div> <div> <%= form.label :name %><br> <%= form.text_field :name %> </div> <div> <%= form.label :content %><br> <%= form.text_area :content %> </div> <div> <%= form.submit %><br> </div> <% end %>
- index.html.erbファイルに編集画面へのリンクを作成
<td><%= link_to '編集' , edit_question_path(question) %></td>
リンクを押下してみると、何やらフォームに値が反映されていませんでした。
コンソールは下記のような感じになっています。
Processing by QuestionsController#edit as HTML Parameters: {"id"=>"4"} Question Load (0.2ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]] ↳ app/controllers/questions_controller.rb:44:in `edit' #<Question id: 4, name: "sdfdsa", title: "safs", content: "sfdsadf", created_at: "2022-02-07 01:22:33.402158000 +0000", updated_at: "2022-02-07 01:22:33.402158000 +0000"> Rendering layout layouts/application.html.erb Rendering questions/edit.html.erb within layouts/application Rendered questions/edit.html.erb within layouts/application (Duration: 5.2ms | Allocations: 3745) [Webpacker] Everything's up-to-date. Nothing to do Rendered layout layouts/application.html.erb (Duration: 17.3ms | Allocations: 7656) Completed 200 OK in 21ms (Views: 17.9ms | ActiveRecord: 0.2ms | Allocations: 8887)
Nothing to do Rendered layout layouts/application.html.erb
との事なので、確認しにいきます。わからんので一旦保留。。。
フォームに値を入力して、submitしコンソールにクエリが表示されかつ正常に遷移が行われていればOKです。
Question Update (1.3ms) UPDATE "questions" SET "name" = ?, "title" = ?, "content" = ?, "updated_at" = ? WHERE "questions"."id" = ? [["name", "更新"], ["title", "更新"], ["content", "更新"], ["updated_at", "2022-02-07 02:37:05.443853"], ["id", 1]]
DELETE機能の実装
- destroyメソッドを下記のように編集
#質問の削除 def destroy @question = Question.find(params[:id]) @question.destroy redirect_to questions_path end
- index.html.erbファイルに削除のリンクを作成
<td><%= link_to '削除' , question_path(question), method: :delete, data: { confirm: '本当に削除してもよろしいですか?' } %></td>
削除リンクを押下して、確認ダイアログが表示→はいを選択
画面レンダリング及び、下記のようなクエリが表示されていることを確認できれば完了です。
Question Destroy (0.6ms) DELETE FROM "questions" WHERE "questions"."id" = ? [["id", 2]]
駆け足となりましたが、今回はここまでで終了です。 お付き合い頂きありがとうございます。
ソースコードはこちら