1. 概要
追加・編集するファイル
- 追記:
config/routes.rb
- 新規:
app/models/diagnosis.rb
- 新規:
db/migrate/*_create_diagnoses.rb
- 新規:
app/controllers/diagnoses_controller.rb
- 新規:
app/views/diagnoses/index.html.erb
- 新規:
app/views/diagnoses/new.html.erb
- 新規:
app/views/diagnoses/show.html.erb
2. controller/model 作成
コマンドプロンプト
rails g controller Diagnoses index new show
コマンドプロンプト
rails g model Diagnosis result_type:string result_detail:text answers:json session_token:string
コマンドプロンプト
rails db:migrate
3. ルーティング編集
config/routes.rb
resources :diagnoses, only: [:index, :new, :create, :show, :destroy]
4. モデル編集
app/models/deagnosis.rb
class Diagnosis < ApplicationRecord
validates :result_type, :result_detail, presence: true
end
5. コントローラー
app/controllers/diagnoses_controller.rb
class DiagnosesController < ApplicationController
# =====▼ここを編集すれば問題数・選択肢数を自由化できます(3×3が雛形)▼=====
QUESTIONS = [
{
id: 1,
title: '学習を始めるとき、まずは?',
choices: [
{ id: '1a', label: '手を動かして試す', value: 1 },
{ id: '1b', label: 'ドキュメントを読む', value: 2 },
{ id: '1c', label: '動画で概要を掴む', value: 3 }
]
},
{
id: 2,
title: '復習のやり方は?',
choices: [
{ id: '2a', label: 'ノートに整理', value: 1 },
{ id: '2b', label: 'ミニアプリ作り直し', value: 2 },
{ id: '2c', label: '要点だけ見返す', value: 3 }
]
},
{
id: 3,
title: '困ったときは?',
choices: [
{ id: '3a', label: '論理的に切り分け', value: 1 },
{ id: '3b', label: 'まずは検索', value: 2 },
{ id: '3c', label: '実験して挙動を確認', value: 3 }
]
}
].freeze
# 解答は下から追加/編集してね!
TYPE_DEFS = {
A: {
title: 'タイプA:設計重視派',
detail: '事前準備や情報整理を大切にし、安定した学習ペースで力を積み上げるタイプ。'
},
B: {
title: 'タイプB:効率・ショートカット志向',
detail: '要点を素早く掴み、最短経路を探索するタイプ。試行回数を増やすと伸びやすい。'
},
C: {
title: 'タイプC:実践ドリブン型',
detail: 'まず手を動かして学び、体験から理解を深めるタイプ。小さく作って改善が得意。'
}
}.freeze
# 回答コード(例 "1-2-2")→ タイプを決めるルール
# 複数パターンを同じ結果にしたい場合は `patterns: ['1-2-2','1-1-2']` のように配列で指定
# * はすべての値
PATTERN_RULES = [
{ patterns: ['1-2-2', '1-1-2'], result_type: :A },
{ patterns: ['3-1-2'], result_type: :B },
{ patterns: ['1-*-3'], result_type: :C },
{ patterns: ['*-3-*'], result_type: :B }
# ここに追記していけばOK(上に書いたものほど優先)
].freeze
# =====▲編集ポイントここまで▲=====
DEFAULT_RESULT_TYPE = :C
before_action :init_session!
def index
@diagnoses = Diagnosis.order(created_at: :desc).limit(50)
end
def new
@step = (params[:step] || 1).to_i
@total = QUESTIONS.size
return redirect_to new_diagnosis_path(step: 1) if @step <= 0 || @step > @total
@question = QUESTIONS[@step - 1]
end
def create
step = (params[:step] || 1).to_i
question = QUESTIONS[step - 1] or return head :not_found
chosen_id = params[:choice_id].to_s.presence
unless chosen_id
redirect_to new_diagnosis_path(step: step), alert: '1つ選んでください' and return
end
choice = question[:choices].find { |c| c[:id].to_s == chosen_id }
session[:answers][question[:id].to_s] = { q: question[:title], a: choice[:label], value: choice[:value] }
next_step = step + 1
if next_step <= QUESTIONS.size
redirect_to new_diagnosis_path(step: next_step) and return
end
code = QUESTIONS.map { |q| session[:answers][q[:id].to_s][:value] }.join('-')
type_sym = find_type_by_rules(code) || DEFAULT_RESULT_TYPE
type_def = TYPE_DEFS[type_sym]
qa_array = QUESTIONS.map { |q| session[:answers][q[:id].to_s] }
diag = Diagnosis.create!(
result_type: type_def[:title],
result_detail: type_def[:detail],
answers: qa_array,
session_token: session.id.to_s
)
session[:answers] = {}
redirect_to diagnosis_path(diag)
end
def show
@diagnosis = Diagnosis.find(params[:id])
end
def destroy
Diagnosis.find(params[:id]).destroy!
redirect_to diagnoses_path, notice: '削除しました'
end
private
def init_session!
session[:answers] ||= {}
end
def wildcard_match?(pattern, code)
p = pattern.split('-'); c = code.split('-')
return false unless p.size == c.size
p.zip(c).all? { |x, y| x == '*' || x == y }
end
def find_type_by_rules(code)
PATTERN_RULES.each do |rule|
Array(rule[:patterns]).each do |pat|
return rule[:result_type] if wildcard_match?(pat, code)
end
end
nil
end
end
6. ビュー
1. 結果一覧ページ
app/views/diagnoses/index.html.erb
<h1>診断履歴</h1>
<p><a href="<%= new_diagnosis_path(step: 1) %>">診断を始める</a></p>
<% if @diagnoses.any? %>
<table>
<thead>
<tr>
<th>日時</th>
<th>結果</th>
<th>リンク</th>
<th>削除</th>
</tr>
</thead>
<tbody>
<% @diagnoses.each do |d| %>
<tr>
<td><%= d.created_at.strftime("%Y-%m-%d %H:%M") %></td>
<td><%= d.result_type %></td>
<td><%= link_to '表示', diagnosis_path(d) %></td>
<td><%= button_to '削除', diagnosis_path(d), method: :delete, data: { confirm: '削除しますか?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>まだ診断履歴はありません。</p>
<% end %>
2. 診断ページ
app/views/diagnoses/new.html.erb
<h1>診断</h1>
<% if flash[:alert] %><p><%= flash[:alert] %></p><% end %>
<h2>Q<%= @step %>/<%= @total %></h2>
<p><%= @question[:title] %></p>
<%= form_with url: diagnoses_path, method: :post do %>
<input type="hidden" name="step" value="<%= @step %>">
<ul>
<% @question[:choices].each do |c| %>
<li>
<label>
<%= radio_button_tag "choice_id", c[:id], false %>
<%= c[:label] %>
</label>
</li>
<% end %>
</ul>
<p>
<%= submit_tag(@step == @total ? '結果を見る' : '次へ') %>
<a href="<%= diagnoses_path %>">履歴へ戻る</a>
</p>
<% end %>
3. 結果ページ
app/views/diagnoses/show.html.erb
<h1>診断結果</h1>
<p><strong><%= @diagnosis.result_type %></strong></p>
<p><%= @diagnosis.result_detail %></p>
<h2>あなたの回答</h2>
<% Array(@diagnosis.answers).each_with_index do |qa, i| %>
<p>
<strong>Q<%= i+1 %>:</strong> <%= qa['q'] || qa[:q] %><br>
<strong>A:</strong> <%= qa['a'] || qa[:a] %>
</p>
<% end %>
<p>
<a href="<%= new_diagnosis_path(step: 1) %>">もう一度診断する</a> |
<a href="<%= diagnoses_path %>">履歴を見る</a>
</p>