目次
- 【はじめに】本記事を書こうと思った理由
- 【前提】対象読者
- 簡単なタスクアプリの仕様
- 間違ったコード10個の問題
- 間違ったコード10個の解答・原因・修正例
- 10問を通して見えてきたrailsの重要な仕組み
- 【まとめ】問題を通して
【はじめに】 本記事を書こうと思った理由
現在、RUNTEQでRails基礎を学習中です。そんな中、
- Rails基礎で学習した内容が理解できているか
- 間違ったコードを見たときに「ん?」と違和感に気づけるか
今日ではAIを使っての開発が当たり前となるなかで、こういった静的なコード確認が基礎理解を深めるうえでも有効ではないかと考え、この度、本記事を書くに至りました。
【前提】 対象読者
本記事はRails初学者向けに書いた記事となります。作成者も現在、Rails基礎を学習中の身ですので、記載内容に足りない部分もあるかと思います。
今回は記事に記載したコードの違和感に気づき、修正できるかなど初学者が理解度を測るうえでご利用ください。
また気づいた点や解答例を学習のタネに自ら調査し、理解を深めていただくとより良い学習になるかと思います。
これらの点を前提に、本記事をお読みいただけますと幸いです。
※以下のタスクアプリは実在しません。よって、こちらに記載しているコード以外をお見せすることもできません。
※以下のコードにはCSSの記述はありませんが、今回の問題ではHTML・CSSにおける誤りは問いません。
※全てのファイルを以下に記載しているわけではございません。
(例) app/views/tasks/new.html.erb や edit.html.erb や show.html.erb などは未記載
簡単なタスクアプリの仕様
- ログイン済みユーザー全員が全タスクを閲覧・編集・削除できる
- タスクの閲覧・作成・編集・削除にはログインが必要
- タスク一覧画面
- 画面タイトルと各タスクの概要を表示する
- 新規作成画面へのリンクを表示する
- タスク一覧には「未着手」「完了」の両方のタスクを、作成日時が古い順に表示する
- 各タスクの概要
- 「タスクタイトル」「担当者:⚪︎⚪︎」「タスクステータス(未着手・完了)」「期限」を表示する
- タスク詳細画面へのリンクを表示する
- タスク削除用のリンクを表示する
- タスク作成・編集画面
- タイトル(title), ステータス(status), 期限(due_on)を入力してタスク作成・編集できる
- タスク
- タイトル・ステータスは必須項目、期限は任意項目とし、due_onにはNULLを保存できる
- その他
- 省略している認証メソッド、Userモデルの関連付け、フォーム、フラッシュ表示、Turboの読み込みは実装済みとする
- 画面イメージ(問題を解くうえでのイメージとしてお使いください)
間違ったコード10個の問題
これから、以下に 『実行時エラー・仕様との不一致・性能上の問題』 を記載します。
さて、あなたは、以下に書かれたコードの違和感に気づけるでしょうか。
◆ config/routes.rb
Rails.application.routes.draw do
root "tasks#index"
resources :tasks, only: [
:index, :show, :new, :create,
:edit, :update
]
end
◆ app/controllers/tasks_controller.rb
class TasksController < ApplicationController
before_action :require_login, except: [:create]
before_action :set_task, only: [:show, :edit, :update, :destroy]
def index
@tasks = Task.order(created_at: :asc).first
end
def show
end
def new
@task = Task.new
end
def create
@task = current_user.tasks.build(task_params)
if @task.save
redirect_to tasks_path, notice: "タスクを作成しました"
else
render :index, status: :unprocessable_entity
end
end
def edit
end
def update
@task.update(task_params)
redirect_to tasks_path, notice: "タスクを更新しました"
end
def destroy
@task.destroy!
redirect_to tasks_path, notice: "タスクを削除しました", status: :see_other
end
private
def set_task
@task = Task.find(params[:id])
end
def task_params
params.require(:task).permit(:name, :status, :due_on)
end
end
◆ app/models/task.rb
class Task < ApplicationRecord
belongs_to :user
validates :title, presence: true
validates :status, inclusion: { in: %w[未着手 進行中] }
end
◆ app/views/tasks/index.html.erb
<h1>タスク一覧</h1>
<%= link_to "新規作成", new_task_path %>
<% @tasks.each do |task| %>
<div class="task">
<h2><% task.title %></h2>
<div><%= task.status %></div>
<div>担当者:<%= task.user.name %></div>
<div>期限:<%= task.due_on.strftime("%m/%d") || "未設定" %></div>
<%= link_to "詳細", task_path(task) %> |
<%= link_to "削除",
task_path(task),
data: {
turbo_method: :delete,
turbo_confirm: "削除しますか?"
} %>
</div>
<% end %>
---( 🔎 Thinking Time 🤔 )---
...さて、10個の問題にあなたは気づくことができたでしょうか?
考えた内容を、以下の解答例と比べてみてください。
間違ったコード10個の解答・原因・修正例
-
問題①:タスク削除のアクションが実行されない
-
原因と修正例
- 原因:routesにdestroyアクションが定義されていないため、DELETEリクエストに対応するルートがなく正常にルーティングできない
- 修正例:
only:オプションを外して、:destroyを含むルートを定義するconfig/routes.rb# 修正前 Rails.application.routes.draw do root "tasks#index" resources :tasks, only: [ :index, :show, :new, :create, :edit, :update ] end # 修正後 Rails.application.routes.draw do root "tasks#index" resources :tasks end
-
参考ドキュメント
-
-
問題②:タスク作成処理だけログイン認証の対象外となっている
-
原因と修正例
- 原因:TasksControllerの
before_action :require_login, except: [:create]のexcept: [:create]により、createアクションだけ除外されてしまっているため - 修正例:
except: [:create]を外して、全アクションにログイン確認を適用するapp/controllers/tasks_controller.rb# 修正前 before_action :require_login, except: [:create] # 修正後 before_action :require_login
- 原因:TasksControllerの
-
参考ドキュメント
-
-
問題③:タスク一覧画面の描画時に
<% @tasks.each do |task| %>で NoMethodError が発生する-
原因と修正例
- 原因:TasksControllerのindexアクションで
.firstが実行され、「Taskインスタンスまたはnil」が返されているにもかかわらず、@tasks.eachメソッドを使用しているため(Taskインスタンス・nilは、eachメソッドを持たないため) - 修正例:
@tasksは、tasksテーブルで保持するレコードを全て取得できるようにActiveRecord::Relationを設定する(仕様に合わせ、orderの条件は昇順とする)(※最終的には問題9の解答の通りとするとよい)app/controllers/tasks_controller.rb# 修正前 def index @tasks = Task.order(created_at: :asc).first end # 修正後 def index @tasks = Task.order(created_at: :asc) end
- 原因:TasksControllerのindexアクションで
-
参考ドキュメント
Railsガイド:Active Record クエリインターフェイス | first
-
-
問題④:タスク作成時に正常なタイトルを入力してもバリデーションエラーが発生する
-
原因と修正例
-
原因:Taskクラスで
:titleは必須とするバリデーションを設定しているのに対し、createアクションの@task.saveで保存しようとする@taskにおいて:titleが設定されていないため -
修正例:
@task = current_user.tasks.build(task_params)の task_params メソッド内のストロングパラメータの:nameを:titleへ修正するapp/controllers/tasks_controller.rb# 修正前 def task_params params.require(:task).permit(:name, :status, :due_on) end # 修正後 def task_params params.require(:task).permit(:title, :status, :due_on) end
-
-
参考ドキュメント
-
-
問題⑤:タスク保存に失敗した場合、タスク一覧画面を描画させていることで @tasks.each で NoMethodError が発生している
-
原因と修正例
-
原因:render :indexは、indexアクションを実行せずに
index.html.erbを描画するが、createアクションでは@tasksを設定しておらず、Viewでnilに対してeachを呼び出しているため -
修正例:
render :newでタスク作成画面を描画させるapp/controllers/tasks_controller.rb# 修正前 if @task.save redirect_to tasks_path, notice: "タスクを作成しました" else render :index, status: :unprocessable_entity end # 修正後 if @task.save redirect_to tasks_path, notice: "タスクを作成しました" else render :new, status: :unprocessable_entity end
-
-
参考ドキュメント
Railsガイド:レイアウトとレンダリング | renderメソッドを使う
-
-
問題⑥:更新失敗でも成功メッセージが表示されてしまう
-
原因と修正例
-
原因:TasksControllerのupdateアクションで成功・失敗の判定処理が書かれていないため
-
修正例:成功・失敗の判定処理を書く
app/controllers/tasks_controller.rb# 修正前 def update @task.update(task_params) redirect_to tasks_path, notice: "タスクを更新しました" end # 修正後 def update if @task.update(task_params) redirect_to tasks_path, notice: "タスクを更新しました" else render :edit, status: :unprocessable_entity end end
-
-
参考ドキュメント
Railsガイド:レイアウトとレンダリング | renderメソッドを使う
-
-
問題⑦:タスクのステータスを完了とすることができない
-
原因と修正例
-
原因:Taskクラスで
:statusのバリデーションとして、未着手 進行中だけが書き込めるようになっているため -
修正例:
進行中を完了に修正するapp/models/task.rb# 修正前 validates :status, inclusion: { in: %w[未着手 進行中] } # 修正後 validates :status, inclusion: { in: %w[未着手 完了] }
-
-
参考ドキュメント
-
-
問題⑧:タスク一覧画面でタスクのタイトルが表示されない
-
原因と修正例
-
原因:
index.html.erbのタイトルを表示する部分で、コードを実行するためだけのタグで囲まれ、HTMLにレンダリング(出力)されていないため -
修正例:結果をHTMLにレンダリング(出力)するためのタグで囲む
app/views/tasks/index.html.erb# 修正前 <h2><% task.title %></h2> # 修正後 <h2><%= task.title %></h2>
-
-
参考ドキュメント
Railsガイド:Action View の概要 | ERB
-
-
問題⑨:担当者を表示する処理でN+1問題が発生する可能性がある
-
原因と修正例
-
原因:担当者欄を出力する
task.user.nameまでにタスクに紐づくユーザー情報を取得できていないため -
修正例:
@tasksのインスタンス変数を設定する場面で、合わせてユーザーの情報も取得できる条件を加えるapp/controllers/tasks_controller.rb# 修正前 def index @tasks = Task.order(created_at: :asc) end # 修正後 def index @tasks = Task.includes(:user).order(created_at: :asc) end
-
-
参考ドキュメント
-
-
問題⑩:タスク一覧画面の描画時に、期限が未設定のタスクを表示すると
<div>期限:<%= task.due_on.strftime("%m/%d") %></div>で NoMethodError が発生する-
原因と修正例
-
原因:
app/models/task.rbで due_on は必須バリデーションが組まれておらず、設定値なしで登録された場合、task.due_onはnilとなり、nilはstrftimeメソッドを持たないため -
修正例:
task.due_onがnilの場合に、安全ナビゲーション演算子の&.を使い、strftimeを実行しないようにするapp/views/tasks/index.html.erb# 修正前 <div>期限:<%= task.due_on.strftime("%m/%d") || "未設定" %></div> # 修正後 <div>期限:<%= task.due_on&.strftime("%m/%d") || "未設定" %></div>
-
-
参考ドキュメント
Ruby 4.0 リファレンスマニュアル:メソッド呼び出し | 安全ナビゲーション演算子
-
10問を通して見えてきたRailsの重要な仕組み
今回の10問を通して、Railsではそれぞれのコードの役割だけでなく、処理の流れや仕組みを理解することが重要だと感じました。
例えば、Active Recordではメソッドによって戻り値やSQLの実行タイミングが異なり、それらを理解していないと、思わぬエラーやN+1問題につながることがあります。
また、ControllerとViewの関係では、renderによってViewを描画しても、別のControllerアクションが実行されるわけではないため、必要なインスタンス変数を適切に準備する必要があります。
さらに、ValidationやStrong Parameters、認証処理など、コードが動くだけでなく、意図したとおりに安全かつ正しく動作することも大切です。
Railsは便利な機能が数多く用意されていますが、その裏側で何が起きているのかを意識しながら学習することが重要ということですね。
【まとめ】 問題を通して
いかがでしたでしょうか。
10個の問題に気づき、修正することはできましたでしょうか。
全てに気づき、修正できた方は、今回扱った10のポイントについて、理解を確認できたと思います。
正解できなかった方も『しっかり理解できていないんだ。ダメだ。。。』と思うことなく、参考ドキュメントのRailsガイドやRubyリファレンスマニュアルを読んで、少しずつ理解していきましょう。
私もまだまだ勉強不足な点は多々ありますので、一緒に頑張りましょう!
以上
