From dfd582d251ef5701f7dfe5e69c9fec1b22f5b1a1 Mon Sep 17 00:00:00 2001 From: Mark Moser Date: Sun, 31 Jul 2016 18:54:12 -0500 Subject: [PATCH] rethinking question CRUD --- .rubocop.yml | 4 +- app/controllers/application_controller.rb | 4 + app/controllers/candidate_controller.rb | 119 +++++++++++------- ...ive-coder.html.erb => _live_code.html.erb} | 0 ...live_coder.html.erb => live_code.html.erb} | 0 app/views/candidate/question.html.erb | 2 +- config/routes.rb | 43 ++++--- db/sql/candidate_quiz.sql | 8 ++ test/controllers/candidate_controller_test.rb | 54 ++++++++ test/fixtures/answers.yml | 8 +- test/fixtures/questions.yml | 4 +- 11 files changed, 172 insertions(+), 74 deletions(-) rename app/views/candidate/{_live-coder.html.erb => _live_code.html.erb} (100%) rename app/views/candidate/{live_coder.html.erb => live_code.html.erb} (100%) create mode 100644 test/controllers/candidate_controller_test.rb diff --git a/.rubocop.yml b/.rubocop.yml index 47bed8a..bede808 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -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/* diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5a9a581..c2965a3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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 diff --git a/app/controllers/candidate_controller.rb b/app/controllers/candidate_controller.rb index 5cd5756..0daca3d 100644 --- a/app/controllers/candidate_controller.rb +++ b/app/controllers/candidate_controller.rb @@ -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 diff --git a/app/views/candidate/_live-coder.html.erb b/app/views/candidate/_live_code.html.erb similarity index 100% rename from app/views/candidate/_live-coder.html.erb rename to app/views/candidate/_live_code.html.erb diff --git a/app/views/candidate/live_coder.html.erb b/app/views/candidate/live_code.html.erb similarity index 100% rename from app/views/candidate/live_coder.html.erb rename to app/views/candidate/live_code.html.erb diff --git a/app/views/candidate/question.html.erb b/app/views/candidate/question.html.erb index 34eafc1..d0572d8 100644 --- a/app/views/candidate/question.html.erb +++ b/app/views/candidate/question.html.erb @@ -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| %>

<%= @question.question %>

diff --git a/config/routes.rb b/config/routes.rb index 22e7fbd..5ff2be6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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" diff --git a/db/sql/candidate_quiz.sql b/db/sql/candidate_quiz.sql index 0254f2d..97143f8 100644 --- a/db/sql/candidate_quiz.sql +++ b/db/sql/candidate_quiz.sql @@ -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 + diff --git a/test/controllers/candidate_controller_test.rb b/test/controllers/candidate_controller_test.rb new file mode 100644 index 0000000..aab1e34 --- /dev/null +++ b/test/controllers/candidate_controller_test.rb @@ -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 diff --git a/test/fixtures/answers.yml b/test/fixtures/answers.yml index ef29517..a46385b 100644 --- a/test/fixtures/answers.yml +++ b/test/fixtures/answers.yml @@ -39,7 +39,7 @@ dawn2: dawn3: candidate: dawn question: fed3 - answer: {html: "

I'm a little tealpot

", css: 'h1 {color: teal;}', js: ''} + answer: {html: "dawn3

I'm a little tealpot

", 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: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + answer: {html: 'dawn7

This means jQuery needs to be available in live-coder!

', 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: '

Salmon

', css: 'h1 {color: salmon;}', js: ''} + answer: {html: 'richard3

Salmon

', 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: '

This means jQuery needs to be available in live-coder!

', css: "strong {font-size: 1.6em;}\n.green {color: green;}", js: '$("strong").addClass("green");'} + answer: {html: 'richard7

This means jQuery needs to be available in live-coder!

', 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 %> diff --git a/test/fixtures/questions.yml b/test/fixtures/questions.yml index 6302ed7..de67303 100644 --- a/test/fixtures/questions.yml +++ b/test/fixtures/questions.yml @@ -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