Skip to content

Commit a08f801

Browse files
committed
Move type-specific question code into subclasses
* Fixes Large Class smell * Performed Replace Type Code With Subclasses refactoring * Builds Question subclasses in QuestionsController
1 parent 5125668 commit a08f801

File tree

13 files changed

+119
-111
lines changed

13 files changed

+119
-111
lines changed

example_app/app/controllers/questions_controller.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
class QuestionsController < ApplicationController
22
def new
33
@survey = Survey.find(params[:survey_id])
4-
@question = @survey.questions.new
5-
@question.options = [Option.new, Option.new, Option.new]
6-
@question.type = params[:type]
4+
build_question
5+
if @question.type == 'MultipleChoiceQuestion'
6+
@question.options = [Option.new, Option.new, Option.new]
7+
end
78
end
89

910
def create
@@ -19,12 +20,14 @@ def create
1920
private
2021

2122
def build_question
22-
@question = @survey.questions.new(question_params, without_protection: true)
23+
type = params[:question][:type]
24+
@question = type.constantize.new(question_params)
25+
@question.survey = @survey
2326
end
2427

2528
def question_params
2629
params.
2730
require(:question).
28-
permit(:type, :title, :options_attributes, :minimum, :maximum)
31+
permit(:title, :options_attributes, :minimum, :maximum)
2932
end
3033
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
11
class MultipleChoiceQuestion < Question
2+
has_many :options, foreign_key: :question_id
3+
4+
accepts_nested_attributes_for :options, reject_if: :all_blank
5+
6+
def summary
7+
total = answers.count
8+
counts = answers.group(:text).order('COUNT(*) DESC').count
9+
percents = counts.map do |text, count|
10+
percent = (100.0 * count / total).round
11+
"#{percent}% #{text}"
12+
end
13+
percents.join(', ')
14+
end
215
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
class OpenQuestion < Question
2+
def summary
3+
answers.order(:created_at).pluck(:text).join(', ')
4+
end
25
end

example_app/app/models/question.rb

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,9 @@ class Question < ActiveRecord::Base
33

44
QUESTION_TYPES = %w(OpenQuestion MultipleChoiceQuestion ScaleQuestion).freeze
55

6-
validates :maximum, presence: true, if: :scale?
7-
validates :minimum, presence: true, if: :scale?
86
validates :type, presence: true, inclusion: QUESTION_TYPES
97
validates :title, presence: true
108

119
belongs_to :survey
1210
has_many :answers
13-
has_many :options
14-
15-
accepts_nested_attributes_for :options, reject_if: :all_blank
16-
17-
def summary
18-
case type
19-
when 'MultipleChoiceQuestion'
20-
summarize_multiple_choice_answers
21-
when 'OpenQuestion'
22-
summarize_open_answers
23-
when 'ScaleQuestion'
24-
summarize_scale_answers
25-
end
26-
end
27-
28-
def steps
29-
(minimum..maximum).to_a
30-
end
31-
32-
private
33-
34-
def scale?
35-
type == 'ScaleQuestion'
36-
end
37-
38-
def summarize_multiple_choice_answers
39-
total = answers.count
40-
counts = answers.group(:text).order('COUNT(*) DESC').count
41-
percents = counts.map do |text, count|
42-
percent = (100.0 * count / total).round
43-
"#{percent}% #{text}"
44-
end
45-
percents.join(', ')
46-
end
47-
48-
def summarize_open_answers
49-
answers.order(:created_at).pluck(:text).join(', ')
50-
end
51-
52-
def summarize_scale_answers
53-
sprintf('Average: %.02f', answers.average('text'))
54-
end
5511
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
11
class ScaleQuestion < Question
2+
validates :maximum, presence: true
3+
validates :minimum, presence: true
4+
5+
def steps
6+
(minimum..maximum).to_a
7+
end
8+
9+
def summary
10+
sprintf('Average: %.02f', answers.average('text'))
11+
end
212
end

example_app/app/views/questions/new.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<%= simple_form_for @question, url: survey_questions_path(@survey) do |form| -%>
1+
<%= simple_form_for @question, as: :question, url: survey_questions_path(@survey) do |form| -%>
22
<%= form.hidden_field :type %>
33

44
<%= form.input :title %>
@@ -14,5 +14,5 @@
1414
<%= form.input :maximum %>
1515
<% end -%>
1616

17-
<%= form.submit %>
17+
<%= form.submit 'Create Question' %>
1818
<% end -%>

example_app/app/views/surveys/show.html.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33

44
<%= link_to(
55
'Add Multiple Choice Question',
6-
new_survey_question_path(@survey, type: 'MultipleChoiceQuestion')
6+
new_survey_question_path(@survey, question: { type: 'MultipleChoiceQuestion' })
77
) %>
88

99
<%= link_to(
1010
'Add Open Question',
11-
new_survey_question_path(@survey, type: 'OpenQuestion')
11+
new_survey_question_path(@survey, question: { type: 'OpenQuestion' })
1212
) %>
1313

1414
<%= link_to(
1515
'Add Scale Question',
16-
new_survey_question_path(@survey, type: 'ScaleQuestion')
16+
new_survey_question_path(@survey, question: { type: 'ScaleQuestion' })
1717
) %>
1818

1919
<%= simple_form_for [@survey, @completion] do |form| -%>

example_app/spec/factories/application.rb

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@
1515
text 'Hello'
1616
end
1717

18-
factory :question do
18+
factory :question, class: 'OpenQuestion' do
1919
survey
2020
title 'Question'
21-
type 'OpenQuestion'
2221

23-
factory :multiple_choice_question do
22+
factory :multiple_choice_question, class: 'MultipleChoiceQuestion' do
2423
ignore do
2524
options_texts { [] }
2625
end
@@ -30,16 +29,12 @@
3029
FactoryGirl.build(:option, text: text, question_id: attributes.id)
3130
end
3231
end
33-
34-
type 'MultipleChoiceQuestion'
3532
end
3633

37-
factory :open_question do
38-
type 'OpenQuestion'
34+
factory :open_question, class: 'OpenQuestion' do
3935
end
4036

41-
factory :scale_question do
42-
type 'ScaleQuestion'
37+
factory :scale_question, class: 'ScaleQuestion' do
4338
end
4439
end
4540

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
describe MultipleChoiceQuestion do
2+
it { should have_many(:options) }
3+
end
4+
5+
describe MultipleChoiceQuestion, '#summary' do
6+
it 'returns a percentage breakdown' do
7+
survey = create(:survey)
8+
question = create(
9+
:multiple_choice_question,
10+
options_texts: %w(Blue Red),
11+
survey: survey
12+
)
13+
taker = SurveyTaker.new(survey)
14+
taker.answer question, 'Red'
15+
taker.answer question, 'Blue'
16+
taker.answer question, 'Red'
17+
18+
question.summary.should eq '67% Red, 33% Blue'
19+
end
20+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
describe OpenQuestion, '#summary' do
2+
it 'returns all answers' do
3+
survey = create(:survey)
4+
question = create(:open_question, survey: survey)
5+
taker = SurveyTaker.new(survey)
6+
7+
taker.answer question, 'Hey'
8+
taker.answer question, 'Hi'
9+
taker.answer question, 'Hello'
10+
11+
question.summary.should eq 'Hey, Hi, Hello'
12+
end
13+
end

0 commit comments

Comments
 (0)