completes #17 - live-coder plus text input type

Squashed commit of the following:

commit d41cbf66eb2a9ee705ab60bb156eed95881fa193
Author: Mark Moser <mark.moser@perficient.com>
Date:   Thu Aug 4 19:58:20 2016 -0500

    live-coder-text validations

commit 866bfeb863516a8656bc26b10f967d0b9b8d8505
Author: Mark Moser <mark.moser@perficient.com>
Date:   Thu Aug 4 11:57:57 2016 -0500

    getting things green again and rebasing develop

commit 28a23200f291e30c690b71e9bcae3e1a69eb3093
Author: Derek Montgomery <montgomerygraphics@gmail.com>
Date:   Thu Aug 4 10:14:23 2016 -0500

    Progress on live coder text field
This commit is contained in:
Mark Moser 2016-08-04 20:01:17 -05:00
parent 635297884d
commit fc784ffcb1
11 changed files with 136 additions and 23 deletions

View File

@ -47,6 +47,7 @@ Metrics/AbcSize:
Metrics/ClassLength: Metrics/ClassLength:
Exclude: Exclude:
- app/controllers/candidate_controller.rb - app/controllers/candidate_controller.rb
- test/**/*
Metrics/LineLength: Metrics/LineLength:
Max: 110 Max: 110

View File

@ -93,7 +93,7 @@ function indentSelection(e){
} }
function loadLiveCoders(){ function loadLiveCoders(){
$.each($('.answer-sec.live_code-type'), function(index, elem){ $.each($('.answer-sec.live_code-type, .answer-sec.live_code_text-type'), function(index, elem){
var qid = $(elem).data('qid'); var qid = $(elem).data('qid');
$(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){ $(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){
$(elem).find('.js-error').addClass('hidden'); $(elem).find('.js-error').addClass('hidden');
@ -112,7 +112,7 @@ $(function(){
// wait a half second before updating results // wait a half second before updating results
// restart the timer if they resume typing // restart the timer if they resume typing
$('html').on('keyup', '.code-input textarea', function(){ $('html').on('keyup', '.code-input textarea', function(){
var elem = $(this).closest('.answer-sec.live_code-type'); var elem = $(this).closest('.answer-sec.live_code-type, .answer-sec.live_code_text-type');
if (timer) { clearTimeout(timer); } if (timer) { clearTimeout(timer); }
timer = setTimeout(updateResults(elem), 500); timer = setTimeout(updateResults(elem), 500);
}); });

View File

@ -173,7 +173,7 @@ $('.answer-sec')
.on('click', '.button-save', saveClickHandler); .on('click', '.button-save', saveClickHandler);
// Dynamically load in coders // Dynamically load in coders
$.each($('.answer-sec.live-coder-type'), function(index, elem){ $.each($('.answer-sec.live_code-type, .answer-sec.live_code_text-type'), function(index, elem){
var qid = $(elem).data('qid'); var qid = $(elem).data('qid');
$(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){ $(elem).find("[data-id='live-coder-answer']").load("/live-coder-entry/" + qid, function(){
$(elem).find('.js-error').addClass('hidden'); $(elem).find('.js-error').addClass('hidden');

View File

@ -42,7 +42,7 @@ class CandidateController < ApplicationController
prep_question params[:question_id] prep_question params[:question_id]
prep_instance_answer @question prep_instance_answer @question
prep_answer params[:question_id] prep_answer params[:question_id]
render layout: false render @question.input_type, layout: false
end end
def summary def summary
@ -95,8 +95,13 @@ class CandidateController < ApplicationController
def answer_params def answer_params
params.require(:answer).permit( params.require(:answer).permit(
:question_id, :answer_id, :question_id,
:radio, :text, checkbox: [], live_code: [:later, :html, :css, :js] :answer_id,
:radio,
:text,
checkbox: [],
live_code: [:later, :html, :css, :js],
live_code_text: [:later, :html, :css, :js, :text]
) )
end end
@ -148,4 +153,11 @@ class CandidateController < ApplicationController
submitted: params.key?(:submit)) submitted: params.key?(:submit))
route_answer route_answer
end end
def process_live_code_text
@answer.update(answer: answer_params[:live_code_text].to_h,
saved: params.key?(:save),
submitted: params.key?(:submit))
route_answer
end
end end

View File

@ -14,7 +14,7 @@ class AnswerFormatValidator < ActiveModel::EachValidator
end end
if clean_val.length > 1000 if clean_val.length > 1000
record.errors[attribute] << (options[:message] || "The character limit for a textarea answer is 1000") record.errors[attribute] << (options[:message] || "The character limit for this answer is 1000.")
end end
end end
@ -33,11 +33,40 @@ class AnswerFormatValidator < ActiveModel::EachValidator
def live_code record, attribute, value def live_code record, attribute, value
return unless value.nil? || value.values.join.blank? return unless value.nil? || value.values.join.blank?
msg = if value.present? && value.keys.count == 1 record.errors[attribute] << (options[:message] || live_code_error_message(value))
"Please check that you will come back to complete the code example." end
else
"You must write code in one of the above textareas to progress." def live_code_text record, attribute, value
end return if value.present? && live_code_text_value_check(value)
record.errors[attribute] << (options[:message] || msg)
record.errors[attribute] << (options[:message] || live_code_text_error_message(value))
end
#################################
def live_code_text_error_message value
return 'You must write code in one of the above textareas to progress.' if value.nil?
code_present = value.values_at(:html, :css, :js).join.present?
reason_present = value[:text].present?
later_present = value.keys.count == 1
return 'Please check that you will come back to complete the code example.' if later_present
return 'You must write code in one of the above textareas to progress.' unless code_present
return 'You must provide an answer in the reason field.' unless reason_present
end
def live_code_error_message value
if value.present? && value.keys.count == 1
return "Please check that you will come back to complete the code example."
end
"You must write code in one of the above textareas to progress."
end
def live_code_text_value_check value
later_present = value[:later].present? && value.keys.count == 1
code_present = value.values_at(:html, :css, :js).join.present?
reason_present = value[:text].present?
later_present || (code_present && reason_present)
end end
end end

View File

@ -0,0 +1 @@
<%= render partial: "candidate/live_code", locals: {question: question, form: form} %>

View File

@ -0,0 +1,21 @@
<% answer = @answer.answer || {} %>
<label for="answer_live_code_text">Reasoning</label>
<%= text_area_tag 'answer[live_code_text][text]', (answer['text']) %>
<div class="code-input">
<label for="answer_live_code_html">HTML</label>
<%= text_area_tag 'answer[live_code_text][html]', (answer['html']), { 'data-id' => 'code-html', class: 'code-answer code-html' } %>
</div>
<div class="code-input">
<label for="answer_live_code_css">CSS</label>
<%= text_area_tag 'answer[live_code_text][css]', (answer['css']), { 'data-id' => 'code-css', class: 'code-answer code-css' } %>
</div>
<div class="code-input">
<label for="answer_live_code_js">JS</label>
<%= text_area_tag 'answer[live_code_text][js]', (answer['js']), { 'data-id' => 'code-js', class: 'code-answer code-js' } %>
</div>
<div class="results" data-id="results"></div>

View File

@ -39,7 +39,7 @@ dawn2:
dawn3: dawn3:
candidate: dawn candidate: dawn
question: fed3 question: fed3
answer: {html: "dawn3 <h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''} answer: {html: "<h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: '', text: 'I did this because reasons.'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 50.minutes %> created_at: <%= DateTime.now() - 38.hours - 50.minutes %>
@ -75,7 +75,7 @@ dawn6:
dawn7: dawn7:
candidate: dawn candidate: dawn
question: fed7 question: fed7
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");'} 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");'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 34.minutes %> created_at: <%= DateTime.now() - 38.hours - 34.minutes %>
@ -129,7 +129,7 @@ peggy2:
peggy3: peggy3:
candidate: peggy candidate: peggy
question: fed3 question: fed3
answer: {html: "peggy3 <h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: ''} answer: {html: "<h1>I'm a little tealpot</h1>", css: 'h1 {color: teal;}', js: '', text: 'I like turtles.'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 50.minutes %> created_at: <%= DateTime.now() - 38.hours - 50.minutes %>
@ -165,7 +165,7 @@ peggy6:
peggy7: peggy7:
candidate: peggy candidate: peggy
question: fed7 question: fed7
answer: {html: 'peggy7 <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: '<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 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 38.hours - 34.minutes %> created_at: <%= DateTime.now() - 38.hours - 34.minutes %>
@ -220,7 +220,7 @@ richard2:
richard3: richard3:
candidate: richard candidate: richard
question: fed3 question: fed3
answer: {html: 'richard3 <h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: ''} answer: {html: '<h1>Salmon</h1>', css: 'h1 {color: salmon;}', js: '', text: 'Gotta lotta GOOD things on sale, strangah.'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 36.hours - 26.minutes %> created_at: <%= DateTime.now() - 36.hours - 26.minutes %>
@ -256,7 +256,7 @@ richard6:
richard7: richard7:
candidate: richard candidate: richard
question: fed7 question: fed7
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");'} 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");'}
saved: 0 saved: 0
submitted: true submitted: true
created_at: <%= DateTime.now() - 36.hours - 34.minutes %> created_at: <%= DateTime.now() - 36.hours - 34.minutes %>

View File

@ -22,7 +22,7 @@ fed3:
quiz: fed quiz: fed
question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area? question: How would you create a widget that would fit in a 250px wide area as well as a 400px wide area?
category: CSS category: CSS
input_type: live_code input_type: live_code_text
input_options: input_options:
sort: 2 sort: 2
active: true active: true

View File

@ -77,7 +77,7 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase
obj.answer = SecureRandom.urlsafe_base64(1001) obj.answer = SecureRandom.urlsafe_base64(1001)
refute obj.valid? refute obj.valid?
assert_match(/char.*limit.*1000$/, obj.errors.messages[:answer][0]) assert_match(/char.*limit.*1000.$/, obj.errors.messages[:answer][0])
end end
# input_type CHECK BOX # input_type CHECK BOX
@ -141,7 +141,7 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase
test "live_code should FAIL without checking finish later" do test "live_code should FAIL without checking finish later" do
obj = AnswerValidatable.new('live_code') obj = AnswerValidatable.new('live_code')
obj.answer = { "later" => "" } obj.answer = { later: "" }
refute obj.valid? refute obj.valid?
assert_match(/come back/, obj.errors.messages[:answer][0]) assert_match(/come back/, obj.errors.messages[:answer][0])
@ -149,9 +149,58 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase
test "live_code should FAIL without values" do test "live_code should FAIL without values" do
obj = AnswerValidatable.new('live_code') obj = AnswerValidatable.new('live_code')
obj.answer = { "later" => "", "html" => "", "css" => "", "js" => "" } obj.answer = { later: "", html: "", css: "", js: "" }
refute obj.valid? refute obj.valid?
assert_match(/write.*code/, obj.errors.messages[:answer][0]) assert_match(/write.*code/, obj.errors.messages[:answer][0])
end end
# input_type LIVE CODER TEXT
test "live_code_text should PASS with populated hash" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = { html: 'this is html', css: '', js: '', text: 'some reasons' }
assert obj.valid?
assert obj.errors.messages.empty?
end
test "live_code_text should PASS with finish later" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = { later: "true" }
assert obj.valid?
assert obj.errors.messages.empty?
end
test "live_code_text should FAIL with nil" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = nil
refute obj.valid?
assert_match(/write.*code/, obj.errors.messages[:answer][0])
end
test "live_code_text should FAIL without checking finish later" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = { later: "" }
refute obj.valid?
assert_match(/come back/, obj.errors.messages[:answer][0])
end
test "live_code_text should FAIL without values" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = { later: "", html: "", css: "", js: "", text: "" }
refute obj.valid?
assert_match(/write.*code/, obj.errors.messages[:answer][0])
end
test "live_code_text should FAIL without text" do
obj = AnswerValidatable.new('live_code_text')
obj.answer = { later: "", html: "some html", css: "", js: "", text: "" }
refute obj.valid?
assert_match(/provide.*reason/, obj.errors.messages[:answer][0])
end
end end