rethinking question CRUD
This commit is contained in:
		| @@ -41,7 +41,7 @@ Style/StringLiterals: | ||||
| Metrics/AbcSize: | ||||
|   Exclude: | ||||
|     - db/migrate/**/* | ||||
|   Max: 27 | ||||
|   Max: 18 | ||||
|  | ||||
| Metrics/LineLength: | ||||
|   Max: 110 | ||||
| @@ -54,4 +54,4 @@ Metrics/LineLength: | ||||
| Metrics/MethodLength: | ||||
|   Exclude: | ||||
|     - db/migrate/* | ||||
|     - app/controllers/* | ||||
|     # - app/controllers/* | ||||
|   | ||||
| @@ -28,4 +28,8 @@ class ApplicationController < ActionController::Base | ||||
|     def authorize_reviewer | ||||
|       redirect_to review_login_path unless current_reviewer | ||||
|     end | ||||
|  | ||||
|     def authorize_candidate | ||||
|       redirect_to welcome_path unless current_candidate | ||||
|     end | ||||
| end | ||||
|   | ||||
| @@ -1,33 +1,30 @@ | ||||
| class CandidateController < ApplicationController | ||||
|   before_action :authorize_candidate, except: [:welcome, :validate] | ||||
|  | ||||
|   def welcome | ||||
|   end | ||||
|  | ||||
|   def saved | ||||
|   end | ||||
|  | ||||
|   def thankyou | ||||
|     redirect_to root_path if session[:test_id].nil? | ||||
|     reset_session | ||||
|   end | ||||
|  | ||||
|   def question | ||||
|     @status = QuizStatus.new(current_candidate) | ||||
|     qid = @status.current_question_id | ||||
|  | ||||
|     redirect_to :summary and return if qid.nil? | ||||
|  | ||||
|     @question = current_candidate.fetch_question(qid) | ||||
|     prep_question qid | ||||
|     @answer = Answer.new | ||||
|   end | ||||
|  | ||||
|   def update_answer | ||||
|     answer_ids = { question_id: answer_params[:question_id], candidate_id: current_candidate.to_i } | ||||
|     @answer = Answer.find_or_create_by!(answer_ids) | ||||
|     @answer.answer = answer_for_type | ||||
|     @answer.saved = answer_params[:save] | ||||
|     @answer.submitted = answer_params[:next] | ||||
|  | ||||
|     if @answer.save | ||||
|       redirect_to :summary and return if params.key?(:update) | ||||
|       redirect_to :saved and return if params.key?(:save) | ||||
|       redirect_to :question | ||||
|     else | ||||
|       flash[:error] = [answer_params[:question_id]] | ||||
|       @question = current_candidate.fetch_question(qid) | ||||
|       render :question | ||||
|     end | ||||
|   def live_coder | ||||
|     question | ||||
|     render layout: false | ||||
|   end | ||||
|  | ||||
|   def summary | ||||
| @@ -37,50 +34,82 @@ class CandidateController < ApplicationController | ||||
|     redirect_to :question and return unless @status.current_question_id.nil? | ||||
|   end | ||||
|  | ||||
|   def update_text | ||||
|     @answer = prep_answer | ||||
|     @answer.update(answer: answer_params[:text], | ||||
|                    saved: answer_params[:save], | ||||
|                    submitted: answer_params[:next]) | ||||
|     validate_answer | ||||
|   end | ||||
|  | ||||
|   def update_radio | ||||
|     @answer = prep_answer | ||||
|     @answer.update(answer: answer_params[:radio], | ||||
|                    saved: answer_params[:save], | ||||
|                    submitted: answer_params[:next]) | ||||
|     validate_answer | ||||
|   end | ||||
|  | ||||
|   def update_checkbox | ||||
|     @answer = prep_answer | ||||
|     @answer.update(answer: answer_params[:checkbox], | ||||
|                    saved: answer_params[:save], | ||||
|                    submitted: answer_params[:next]) | ||||
|     validate_answer | ||||
|   end | ||||
|  | ||||
|   def update_live_code | ||||
|     @answer = prep_answer | ||||
|     @answer.update(answer: answer_params[:live_code], | ||||
|                    saved: answer_params[:save], | ||||
|                    submitted: answer_params[:next]) | ||||
|     validate_answer | ||||
|   end | ||||
|  | ||||
|   # TODO | ||||
|   def update_summary | ||||
|     redirect_to :summary | ||||
|   end | ||||
|  | ||||
|   def thankyou | ||||
|     redirect_to root_path if session[:test_id].nil? | ||||
|     reset_session | ||||
|   end | ||||
|  | ||||
|   def saved | ||||
|     # redirect_to :summary | ||||
|   end | ||||
|  | ||||
|   def validate | ||||
|     candidate = Candidate.find_by(test_hash: params['test_id']) | ||||
|     redirect_to(root_path, alert: "Sorry, incorrect test id") and return if candidate.nil? | ||||
|  | ||||
|     if candidate.nil? | ||||
|       reset_session | ||||
|       redirect_to root_path, alert: "Sorry, incorrect test id" | ||||
|     else | ||||
|       session[:test_id] = candidate.test_hash | ||||
|       redirect_to :question | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def live_coder | ||||
|     @question = Question.find(params[:question_id]) | ||||
|     @answer = @question.answers.order("RAND()").first.answer | ||||
|     render layout: false | ||||
|     session[:test_id] = candidate.test_hash | ||||
|     redirect_to :thankyou and return if candidate.completed? | ||||
|     redirect_to :question | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|     def prep_question qid | ||||
|       @question = current_candidate.fetch_question(qid) | ||||
|     end | ||||
|  | ||||
|     def answer_params | ||||
|       params.require(:answer).permit( | ||||
|         :question_id, :answer_id, | ||||
|         :save, :next, :summary, | ||||
|         :radio, :text, checkbox: [], live_coder: [] | ||||
|         :radio, :text, checkbox: [], live_code: [] | ||||
|       ) | ||||
|     end | ||||
|  | ||||
|     def answer_for_type | ||||
|       return answer_params[:radio] unless answer_params[:radio].nil? | ||||
|       return answer_params[:text] unless answer_params[:text].nil? | ||||
|       return answer_params[:checkbox] unless answer_params[:checkbox].nil? | ||||
|       return answer_params[:live_coder] unless answer_params[:live_coder].nil? | ||||
|     def prep_answer | ||||
|       answer_ids = { question_id: answer_params[:question_id], candidate_id: current_candidate.to_i } | ||||
|       answer = Answer.find_or_create_by(answer_ids) | ||||
|       answer | ||||
|     end | ||||
|  | ||||
|     def validate_answer | ||||
|       if @answer.errors.present? | ||||
|         flash[:error] = [answer_params[:question_id]] | ||||
|         prep_question answer_params[:question_id] | ||||
|         render :question | ||||
|       else | ||||
|         # TODO: change params.key? to submit = save/next/summary | ||||
|         redirect_to :summary and return if params.key?(:update) | ||||
|         redirect_to :saved and return if params.key?(:save) | ||||
|         redirect_to :question | ||||
|       end | ||||
|     end | ||||
| end | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|   content_for :footer_title, "Skills Assessment" | ||||
| %> | ||||
|  | ||||
| <%= form_for(@answer, url: post_answer_path(@answer.id), html:{id: 'answer-form', data: {qid: @question.question_id}}) do |form| %> | ||||
| <%= form_for(@answer, url: send("post_#{@question.input_type}_path", @answer.id), html:{id: 'answer-form', data: {qid: @question.question_id}}) do |form| %> | ||||
|   <main class="questions_tpl"> | ||||
|     <h2 class="question-text"><%= @question.question %></h2> | ||||
|  | ||||
|   | ||||
| @@ -1,28 +1,31 @@ | ||||
| Rails.application.routes.draw do | ||||
|   post "/validate",                      to: "candidate#validate",       as: :validate_candidate | ||||
|    get "/welcome",                       to: "candidate#welcome",        as: :welcome | ||||
|    get "/thankyou",                      to: "candidate#thankyou",       as: :thankyou | ||||
|    get "/saved",                         to: "candidate#saved",          as: :saved | ||||
|   post "/validate",                        to: "candidate#validate",         as: :validate_candidate | ||||
|    get "/welcome",                         to: "candidate#welcome",          as: :welcome | ||||
|    get "/thankyou",                        to: "candidate#thankyou",         as: :thankyou | ||||
|    get "/saved",                           to: "candidate#saved",            as: :saved | ||||
|  | ||||
|   post "/question(/:answer_id)",         to: "candidate#update_answer",  as: :post_answer | ||||
|    get "/question(/:question_id)",       to: "candidate#question",       as: :question | ||||
|    get "/live-coder-entry/:question_id", to: "candidate#live_coder",     as: :live_coder | ||||
|   post "/question/text(/:answer_id)",      to: "candidate#update_text",      as: :post_text | ||||
|   post "/question/radio(/:answer_id)",     to: "candidate#update_radio",     as: :post_radio | ||||
|   post "/question/checkbox(/:answer_id)",  to: "candidate#update_checkbox",  as: :post_checkbox | ||||
|   post "/question/live-code(/:answer_id)", to: "candidate#update_live_code", as: :post_live_code | ||||
|    get "/question(/:question_id)",         to: "candidate#question",         as: :question | ||||
|    get "/live-coder-entry/:question_id",   to: "candidate#live_coder",       as: :live_coder | ||||
|  | ||||
|   post "/summary",                       to: "candidate#update_summary", as: :post_summary | ||||
|    get "/summary",                       to: "candidate#summary",        as: :summary | ||||
|   post "/summary",                         to: "candidate#update_summary",   as: :post_summary | ||||
|    get "/summary",                         to: "candidate#summary",          as: :summary | ||||
|  | ||||
|    get "/review/logout",                 to: "review#logout",            as: :review_logout | ||||
|   post "/review/login",                  to: "review#auth",              as: :review_auth | ||||
|    get "/review/login",                  to: "review#login",             as: :review_login | ||||
|    get "/review",                        to: "review#index",             as: :review | ||||
|    get "/review/:test_hash",             to: "review#view",              as: :review_test | ||||
|    get "/review/logout",                   to: "review#logout",              as: :review_logout | ||||
|   post "/review/login",                    to: "review#auth",                as: :review_auth | ||||
|    get "/review/login",                    to: "review#login",               as: :review_login | ||||
|    get "/review",                          to: "review#index",               as: :review | ||||
|    get "/review/:test_hash",               to: "review#view",                as: :review_test | ||||
|  | ||||
|    get "/recruiter",                     to: "recruiter#index",          as: :recruiter | ||||
|    get "/recruiter/new-candidate",       to: "recruiter#new",            as: :new_candidate | ||||
|   post "/recruiter/new-candidate",       to: "recruiter#create",         as: :create_candidate | ||||
|    get "/recruiter/logout",              to: "recruiter#logout",         as: :recruiter_logout | ||||
|    get "/recruiter/login",               to: "recruiter#login",          as: :recruiter_login | ||||
|   post "/recruiter/login",               to: "recruiter#auth",           as: :recruiter_auth | ||||
|    get "/recruiter",                       to: "recruiter#index",            as: :recruiter | ||||
|    get "/recruiter/new-candidate",         to: "recruiter#new",              as: :new_candidate | ||||
|   post "/recruiter/new-candidate",         to: "recruiter#create",           as: :create_candidate | ||||
|    get "/recruiter/logout",                to: "recruiter#logout",           as: :recruiter_logout | ||||
|    get "/recruiter/login",                 to: "recruiter#login",            as: :recruiter_login | ||||
|   post "/recruiter/login",                 to: "recruiter#auth",             as: :recruiter_auth | ||||
|  | ||||
|   root to: "candidate#welcome" | ||||
|  | ||||
|   | ||||
| @@ -6,4 +6,12 @@ select c.id candidate_id | ||||
| from candidates c | ||||
|   inner join questions q on q.quiz_id = c.quiz_id | ||||
|   left join answers a on a.candidate_id = c.id AND a.question_id = q.id | ||||
|  | ||||
| where c.test_hash = 'R67PmfDHGiw' -- and q.input_type = 'radio' | ||||
|   | ||||
| order by c.name, q.sort; | ||||
|  | ||||
|  | ||||
|  | ||||
| -- delete from answers where id = 1008398109 | ||||
|  | ||||
|   | ||||
							
								
								
									
										54
									
								
								test/controllers/candidate_controller_test.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								test/controllers/candidate_controller_test.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| require 'test_helper' | ||||
|  | ||||
| class CandidateControllerTest < ActionDispatch::IntegrationTest | ||||
|   def setup_auth candidate | ||||
|     post validate_candidate_url, params: { test_id: candidate.test_hash } | ||||
|   end | ||||
|  | ||||
|   test "should get login" do | ||||
|     get welcome_path | ||||
|     assert_response :success | ||||
|   end | ||||
|  | ||||
|   test "should require auth or redirect" do | ||||
|     get saved_path | ||||
|     assert_redirected_to welcome_path | ||||
|  | ||||
|     get thankyou_path | ||||
|     assert_redirected_to welcome_path | ||||
|  | ||||
|     get summary_path | ||||
|     assert_redirected_to welcome_path | ||||
|  | ||||
|     get question_path | ||||
|     assert_redirected_to welcome_path | ||||
|  | ||||
|     get question_path(questions(:fed1).id) | ||||
|     assert_redirected_to welcome_path | ||||
|  | ||||
|     get live_coder_path(questions(:fed1).id) | ||||
|     assert_redirected_to welcome_path | ||||
|   end | ||||
|  | ||||
|   test "should auth to question" do | ||||
|     setup_auth candidates(:martha) | ||||
|  | ||||
|     assert_redirected_to question_path | ||||
|     assert session[:test_id].present? | ||||
|   end | ||||
|  | ||||
|   test "should redirect to thankyou when completed" do | ||||
|     setup_auth candidates(:richard) | ||||
|  | ||||
|     assert_redirected_to thankyou_path | ||||
|   end | ||||
|  | ||||
|   test "should get summary if complete but not submitted" do | ||||
|     setup_auth candidates(:dawn) | ||||
|  | ||||
|     get summary_url | ||||
|     assert_response :success | ||||
|   end | ||||
|  | ||||
|   # should get flash message on bad question | ||||
| end | ||||
							
								
								
									
										8
									
								
								test/fixtures/answers.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								test/fixtures/answers.yml
									
									
									
									
										vendored
									
									
								
							| @@ -39,7 +39,7 @@ dawn2: | ||||
| dawn3: | ||||
|   candidate: dawn | ||||
|   question: fed3 | ||||
|   answer: {html: "<h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''} | ||||
|   answer: {html: "dawn3 <h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''} | ||||
|   saved: 0 | ||||
|   submitted: true | ||||
|   created_at: <%= DateTime.now() - 38.hours - 50.minutes %> | ||||
| @@ -75,7 +75,7 @@ dawn6: | ||||
| dawn7: | ||||
|   candidate: dawn | ||||
|   question: fed7 | ||||
|   answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} | ||||
|   answer: {html: 'dawn7 <p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} | ||||
|   saved: 0 | ||||
|   submitted: true | ||||
|   created_at: <%= DateTime.now() - 38.hours - 34.minutes %> | ||||
| @@ -129,7 +129,7 @@ richard2: | ||||
| richard3: | ||||
|   candidate: richard | ||||
|   question: fed3 | ||||
|   answer: {html: '<h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: ''} | ||||
|   answer: {html: 'richard3 <h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: ''} | ||||
|   saved: 0 | ||||
|   submitted: true | ||||
|   created_at: <%= DateTime.now() - 36.hours - 26.minutes %> | ||||
| @@ -165,7 +165,7 @@ richard6: | ||||
| richard7: | ||||
|   candidate: richard | ||||
|   question: fed7 | ||||
|   answer: {html: '<p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} | ||||
|   answer: {html: 'richard7 <p>This means <strong>jQuery</strong> needs to be available in live-coder!</p>', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} | ||||
|   saved: 0 | ||||
|   submitted: true | ||||
|   created_at: <%= DateTime.now() - 36.hours - 34.minutes %> | ||||
|   | ||||
							
								
								
									
										4
									
								
								test/fixtures/questions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								test/fixtures/questions.yml
									
									
									
									
										vendored
									
									
								
							| @@ -22,7 +22,7 @@ fed3: | ||||
|   quiz: fed | ||||
|   question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area? | ||||
|   category: CSS | ||||
|   input_type: live-coder | ||||
|   input_type: live_code | ||||
|   input_options: | ||||
|   sort: 2 | ||||
|   active: true | ||||
| @@ -58,7 +58,7 @@ fed7: | ||||
|   quiz: fed | ||||
|   question: Provide a code example to manipulate the DOM using jQuery/JavaScript to change the classname of a div 'classB' to 'classC', only if the div 'classA' exists in the page? | ||||
|   category: Javascript | ||||
|   input_type: live-coder | ||||
|   input_type: live_code | ||||
|   input_options: | ||||
|   sort: 6 | ||||
|   active: true | ||||
|   | ||||
		Reference in New Issue
	
	Block a user