記事内で紹介する商品を購入することで、当サイトに売り上げの一部が還元されることがあります。
「一問一答アプリ × ChatGPT」
先日、ChatGPTを活用して一問一答アプリケーション「情報1 一問一答トレーニングGYM」を開発したことをお伝えしました。
▶︎こちらの記事で紹介しています。
【ChatGPTと開発】高校「情報1」一問一答アプリ私が開発したこのアプリは、ChatGPTの力を借りながら、実装していきました。
この記事では、一問一答アプリ × ChatGPTの開発の舞台裏や、実際の活用方法について詳しく解説します。
今回は
- リファクタリング
するにあたって、どのように役立ったのか、その様子を紹介します!
ぜひこのシリーズの記事をチェックして、一問一答アプリの新たな魅力とChatGPTとの相乗効果を体感してください!
これまでひとしきり機能を実装してきましたが、必要な処理をコントローラーに全て書いていたので、あまりに冗長で不恰好になってしまっていたので、リファクタリングを実施しました。
リファクタリングを楽にできたり、どういう観点で行おうかとchatGPTに相談してみました。
リファクタリングしたいと思います。下記のコントローラーのコードが冗長であるため、モデルが行う処理と分割したいと考えています。今のコードによる挙動をそのまま維持しながら必要に応じてメソッドを定義し、リファクタリングしてください。▼コード:〜
現在のコントローラーのコードはかなり冗長ですが、問題を分離してメソッドに分割することで、コードを簡潔にできます。
まずは、セッションの初期化と取得する処理を、メソッドinitialize_session
として分割しましょう。
def ask
if request.get?
render html: '<script>alert("選択内容が解除されトップページに戻ります。");window.location.href = "/";</script>'.html_safe
elsif request.post?
initialize_session
# 出題範囲が選択されているか否かで条件分岐
# 出題範囲が選択されていない場合
if params[:genre].blank?
# Genreモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
genre_ids = Genre.pluck(:id)
session[:selected_genre_ids] << genre_ids
else
# 選択された分野をparamsから受け取り、セッションに格納する
genre_ids = params[:genre][:genre_ids].reject(&:empty?)
if !session[:selected_genre_ids].include?(genre_ids)
session[:selected_genre_ids] << genre_ids
end
end
# 難易度をパラムスから取得してモデルに渡す
if params[:level].blank?
# Levelモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
level_ids = Level.pluck(:id)
session[:selected_level_ids] << level_ids
else
# 選択された難易度をparamsから受け取り、セッションに格納する
level_ids = params[:level][:level_ids].reject(&:empty?)
if !session[:selected_level_ids].include?(level_ids)
session[:selected_level_ids] << level_ids
end
end
# 出題数を格納
@question_nums = Question.where(genre_id: genre_ids, level_id: level_ids).count
session[:question_nums] << @question_nums
# genre_idsに入っている分野idと一致するインスタンスを1つ取得する
@question = Question.where(genre_id: genre_ids,level_id: level_ids).order("RAND()").first
if @question.present?
# session[:asked_question_ids]に取得したインスタンスの問題idを格納する。
if !session[:asked_question_ids].include?(@question.id)
session[:asked_question_ids] << @question.id
end
#取得した問題インスタンスに付随する選択肢を取得する。
@choices = @
…(省略)
ただ、リファクタリングそのものを依頼するとコードの表示が切れるため、観点を整理するくらいが良いのかもしれません。
コントローラーでは種々の条件を満たすレコードをモデルが引っ張ってくる、という挙動だけにすることを中心にします。
また、モデルはデフォルトではセッションを操作することができないため。なるべくセッションはRailsのコントローラーの中で実行されるアクションにする。(モデルでセッションを使いたい場合、コントローラーからセッションを渡して、それをモデル内で利用する必要があります。例えば、コントローラーからモデルのメソッドを呼び出す際に、引数としてセッションを渡す)。
これらを整理して、リファクタリングを実施しました。
コントローラーについてはいくつかのメソッドを省略しています。
class QuestionsController < ApplicationController
before_action :renderHTML, only: [:index, :ask, :next_question, :retry, :print]
def index
end
def ask
initialize_session
genre_ids = Genre.select_genre(session, params)
level_ids = Level.select_level(session, params)
# 出題数を格納
@question_nums = Question.where(genre_id: genre_ids, level_id: level_ids).count
session[:question_nums] << @question_nums
# genre_idsに入っている分野idと一致するインスタンスを1つ取得する
@question = Question.where(genre_id: genre_ids,level_id: level_ids).order("RAND()").first
if @question.present?
# session[:asked_question_ids]に取得したインスタンスの問題idを格納する。
if !session[:asked_question_ids].include?(@question.id)
session[:asked_question_ids] << @question.id
end
#取得した問題インスタンスに付随する選択肢を取得する。
@choices = @question.choices.includes(:question) #questionとアソシエーション関係なので()の中にはカラム名ではなく関係のあるモデル名にする「_id」は不要
@answer = Answer.find(@question.id)
else
redirect_to root_path, alert: "出題できる問題がありません。"
end
end
def next_question
session = request.session
@question = Question.selectQuestion(session)
# session[:asked_question_ids]に取得したインスタンスの問題idを追加格納する。
if @question.present?
session[:asked_question_ids] << @question.id
@choices = @question.choices.includes(:question) #questionとアソシエーション関係なので()の中にはカラム名ではなく関係のあるモデル名にする「_id」は不要
@answer = Answer.find(@question.id)
if session[:question_nums].present?
@question_nums = session[:question_nums]
end
# -1は出題数を調整するため
@asked_question_count = count_asked_question
@correct_counts = count_correct_counts
# to_fは浮動小数(float)への変換
@correct_rate = Question.calcCorrectRate(@asked_question_count, @correct_count) # 呼び出すビューファイルはaskアクションのビューファイル
render :ask
else
redirect_to score_questions_path
end
end
def retry
# セッションを調節し、間違えた問題を取得する
incorrect_question_ids = adjustSession
# 出題数を格納
@question_nums = incorrect_question_ids.length
session[:question_nums] << @question_nums
# 不正解問題のidでレコードを取得する
@question = Question.where(id: incorrect_question_ids).order("RAND()").first
if @question.present?
# 不正解問題のidを出題済みセッションに格納する
session[:asked_question_ids] << @question.id
@choices = @question.choices.includes(:question) #questionとアソシエーション関係なので()の中にはカラム名ではなく関係のあるモデル名にする「_id」は不要
@answer = Answer.find(@question.id)
render :ask
else
redirect_to score_questions_path
end
end
def print
# 出題数をパラムスより取得する
question_count = params[:question_count].to_i
genre_ids = Genre.select_genre(params)
level_ids = Level.select_level(params)
# 選択された出題形式を読み取る
question_order = params[:question_order].to_i
@questions = Question.selectQuestionByOrder(question_order, genre_ids, level_ids, question_count)
@style_id = params[:style_id].to_i
end
def initialize_session
# まずはセッション変数を取得する
# next_questionメソッドで使うため
# session[:asked_question_ids] ||= [] でセッションが未定義なら新しく作るという意味になる
session[:asked_question_ids] ||= []
session[:selected_genre_ids] ||= []
# 正答数を計上するためのセッションを取得する
session[:correct_answers]||= []
# 難易度を格納するためのセッション
session[:selected_level_ids] ||= []
# 出題数を格納するためのセッション
session[:question_nums] ||= []
# 間違えた問題を格納するためのセッション
session[:incorrect_answers] ||= []
end
def adjustSession
# いくつかのセッションをリセットする
session[:asked_question_ids] = []
# 出題数を格納するためのセッション
session[:question_nums] = []
# 不正解問題のidを取得する
incorrect_question_ids = session[:incorrect_answers]
session[:incorrect_answers] = []
if session[:question_range].present?
session[:question_range] = []
else
session[:question_range]||= []
end
session[:question_range] << incorrect_question_ids
end
private
def renderHTML
if request.post?
return
else
render html: '<script>alert("選択内容が解除されトップページに戻ります。");window.location.href = "/";</script>'.html_safe
end
end
def count_asked_question
return (session[:asked_question_ids].length - 1)
end
def count_correct_counts
return session[:correct_answers].length
end
end
def self.selectQuestion(session)
if session[:question_range] .present?
question = Question.where(id: session[:question_range]).where.not(id: session[:asked_question_ids]).order("RAND()").first
else
#当メソッドを呼び出す際には最低1回はask~resultメソッドを実行している。そのため、セッションを参考にして分野を指定しながら出題済みの問題を避けて次の問題(インスタンス)を取得できる。
question = Question.where(genre_id: session[:selected_genre_ids],level_id: session[:selected_level_ids]).where.not(id: session[:asked_question_ids]).order("RAND()").first
end
return question
end
def self.calcCorrectRate(asked_question_count, correct_counts)
# to_fは浮動小数(float)への変換
correct_rate = ((correct_counts.to_f / asked_question_count.to_f) * 100).round(1)
return correct_rate
end
def self.selectQuestionByOrder(question_order)
# 出題形式が「ランダム」の場合
if question_order == 1
questions = Question.where(genre_id: genre_ids, level_id: level_ids).order("RAND()").limit(question_count)
# 出題形式が「番号順」の場合
elsif question_order == 2
questions = Question.where(genre_id: genre_ids, level_id: level_ids).order(id: "ASC").limit(question_count)
end
return questions
end
def self.select_genre(session, params)
# 出題範囲が選択されているか否かで条件分岐
# 出題範囲が選択されていない場合
if params[:genre].blank?
# Genreモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
genre_ids = Genre.pluck(:id)
session[:selected_genre_ids] << genre_ids
else
# 選択された分野をparamsから受け取り、セッションに格納する
genre_ids = params[:genre][:genre_ids].reject(&:empty?)
if !session[:selected_genre_ids].include?(genre_ids)
session[:selected_genre_ids] << genre_ids
end
end
return genre_ids
end
def self.select_genre(params)
# 出題範囲が選択されているか否かで条件分岐
# 出題範囲が選択されていない場合
if params[:genre].blank?
# Genreモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
genre_ids = Genre.pluck(:id)
else
# 選択された分野をparamsから受け取り、セッションに格納する
genre_ids = params[:genre][:genre_ids].reject(&:empty?)
end
return genre_ids
end
def self.select_level(session, params)
# 難易度をパラムスから取得してモデルに渡す
if params[:level].blank?
# Levelモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
level_ids = Level.pluck(:id)
session[:selected_level_ids] << level_ids
else
# 選択された難易度をparamsから受け取り、セッションに格納する
level_ids = params[:level][:level_ids].reject(&:empty?)
if !session[:selected_level_ids].include?(level_ids)
session[:selected_level_ids] << level_ids
end
end
return level_ids
end
def self.select_level(params)
# 難易度をパラムスから取得してモデルに渡す
if params[:level].blank?
# Levelモデルからカラム「id」の値だけを取得し、セッションに格納する(これで全ての分野のidを取得できる)
level_ids = Level.pluck(:id)
else
# 選択された難易度をparamsから受け取り、セッションに格納する
level_ids = params[:level][:level_ids].reject(&:empty?)
end
return level_ids
end
今回の開発作業で
- リファクタリング
を実現することができました。
ChatGPTを利用すると開発がグンと楽になりますね!ChatGPTの可能性を体験してみてはいかがでしょうか?
ChatGPTを利用すると開発がグンと楽になりますね!
最後まで読んでいただきありがとうございました。
この記事が少しでも参考になれば幸いです。
それではまたここで会いましょう!
にほんブログ村ブログ村に参加中です。上のバナーをクリックいただくだけで当ブログにポイントが入ります。いつも応援クリックありがとうございます。
Twitter やってます。
新着記事のお知らせをしていますのでぜひフォローしてください!スポンサーリンク