rapidfire 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +124 -0
- data/Rakefile +36 -0
- data/app/assets/javascripts/rapidfire/application.js +12 -0
- data/app/assets/stylesheets/rapidfire/application.css +22 -0
- data/app/controllers/rapidfire/answer_groups_controller.rb +25 -0
- data/app/controllers/rapidfire/application_controller.rb +11 -0
- data/app/controllers/rapidfire/question_groups_controller.rb +30 -0
- data/app/controllers/rapidfire/questions_controller.rb +58 -0
- data/app/helpers/rapidfire/application_helper.rb +12 -0
- data/app/models/rapidfire/answer.rb +16 -0
- data/app/models/rapidfire/answer_group.rb +9 -0
- data/app/models/rapidfire/answer_group_builder.rb +53 -0
- data/app/models/rapidfire/question.rb +40 -0
- data/app/models/rapidfire/question_group.rb +8 -0
- data/app/models/rapidfire/question_proxy.rb +92 -0
- data/app/models/rapidfire/questions/checkbox.rb +21 -0
- data/app/models/rapidfire/questions/date.rb +16 -0
- data/app/models/rapidfire/questions/long.rb +6 -0
- data/app/models/rapidfire/questions/numeric.rb +21 -0
- data/app/models/rapidfire/questions/radio.rb +6 -0
- data/app/models/rapidfire/questions/select.rb +19 -0
- data/app/models/rapidfire/questions/short.rb +6 -0
- data/app/views/rapidfire/answer_groups/new.html.erb +11 -0
- data/app/views/rapidfire/answers/_checkbox.html.erb +11 -0
- data/app/views/rapidfire/answers/_date.html.erb +4 -0
- data/app/views/rapidfire/answers/_errors.html.erb +7 -0
- data/app/views/rapidfire/answers/_long.html.erb +4 -0
- data/app/views/rapidfire/answers/_numeric.html.erb +4 -0
- data/app/views/rapidfire/answers/_radio.html.erb +9 -0
- data/app/views/rapidfire/answers/_select.html.erb +4 -0
- data/app/views/rapidfire/answers/_short.html.erb +4 -0
- data/app/views/rapidfire/question_groups/_form.html.erb +15 -0
- data/app/views/rapidfire/question_groups/_question_group.html.erb +17 -0
- data/app/views/rapidfire/question_groups/index.html.erb +23 -0
- data/app/views/rapidfire/question_groups/new.html.erb +1 -0
- data/app/views/rapidfire/questions/_form.html.erb +40 -0
- data/app/views/rapidfire/questions/_question.html.erb +9 -0
- data/app/views/rapidfire/questions/edit.html.erb +1 -0
- data/app/views/rapidfire/questions/index.html.erb +21 -0
- data/app/views/rapidfire/questions/new.html.erb +1 -0
- data/config/database.yml +8 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20130502170733_create_rapidfire_question_groups.rb +8 -0
- data/db/migrate/20130502195310_create_rapidfire_questions.rb +15 -0
- data/db/migrate/20130502195415_create_rapidfire_answer_groups.rb +12 -0
- data/db/migrate/20130502195504_create_rapidfire_answers.rb +13 -0
- data/lib/generators/rapidfire/views_generator.rb +23 -0
- data/lib/rapidfire.rb +6 -0
- data/lib/rapidfire/engine.rb +5 -0
- data/lib/rapidfire/version.rb +3 -0
- data/lib/tasks/rapidfire_tasks.rake +4 -0
- metadata +241 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
class Question < ActiveRecord::Base
|
3
|
+
belongs_to :question_group, :inverse_of => :questions
|
4
|
+
default_scope order(:position)
|
5
|
+
|
6
|
+
validates :question_group, :question_text, :presence => true
|
7
|
+
serialize :validation_rules
|
8
|
+
|
9
|
+
attr_accessible :question_group, :question_text, :validation_rules, :answer_options
|
10
|
+
|
11
|
+
def self.inherited(child)
|
12
|
+
child.instance_eval do
|
13
|
+
def model_name
|
14
|
+
Question.model_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def rules
|
22
|
+
validation_rules || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# answer will delegate its validation to question, and question
|
26
|
+
# will inturn add validations on answer on the fly!
|
27
|
+
def validate_answer(answer)
|
28
|
+
if rules[:presence] == "1"
|
29
|
+
answer.validates_presence_of :answer_text
|
30
|
+
end
|
31
|
+
|
32
|
+
if rules[:minimum].present? || rules[:maximum].present?
|
33
|
+
min_max = { minimum: rules[:minimum].to_i }
|
34
|
+
min_max[:maximum] = rules[:maximum].to_i if rules[:maximum].present?
|
35
|
+
|
36
|
+
answer.validates_length_of :answer_text, min_max
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
class QuestionProxy
|
3
|
+
extend ActiveModel::Naming
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
|
6
|
+
AVAILABLE_QUESTIONS =
|
7
|
+
[
|
8
|
+
Rapidfire::Questions::Checkbox,
|
9
|
+
Rapidfire::Questions::Date,
|
10
|
+
Rapidfire::Questions::Long,
|
11
|
+
Rapidfire::Questions::Numeric,
|
12
|
+
Rapidfire::Questions::Radio,
|
13
|
+
Rapidfire::Questions::Select,
|
14
|
+
Rapidfire::Questions::Short,
|
15
|
+
]
|
16
|
+
|
17
|
+
QUESTION_TYPES = AVAILABLE_QUESTIONS.inject({}) do |result, question|
|
18
|
+
question_name = question.to_s.split("::").last
|
19
|
+
result[question_name] = question.to_s
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :question_group, :question,
|
24
|
+
:type, :question_text, :answer_options, :answer_presence,
|
25
|
+
:answer_minimum_length, :answer_maximum_length,
|
26
|
+
:answer_greater_than_or_equal_to, :answer_less_than_or_equal_to
|
27
|
+
|
28
|
+
delegate :valid?, :errors, :id, :to => :question
|
29
|
+
|
30
|
+
def persisted?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_model
|
35
|
+
question
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(params = {})
|
39
|
+
from_question_to_attributes(params[:question]) if params[:question]
|
40
|
+
params.each { |k, v| send("#{k}=", v) }
|
41
|
+
@question ||= question_group.questions.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
@question.new_record? ? create_question : update_question
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def create_question
|
50
|
+
klass = nil
|
51
|
+
if QUESTION_TYPES.values.include?(type)
|
52
|
+
klass = type.constantize
|
53
|
+
else
|
54
|
+
errors.add(:type, :invalid)
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
@question = klass.create(to_question_params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_question
|
62
|
+
@question.update_attributes(to_question_params)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_question_params
|
66
|
+
{
|
67
|
+
:question_group => question_group,
|
68
|
+
:question_text => question_text,
|
69
|
+
:answer_options => answer_options,
|
70
|
+
:validation_rules => {
|
71
|
+
:presence => answer_presence,
|
72
|
+
:minimum => answer_minimum_length,
|
73
|
+
:maximum => answer_maximum_length,
|
74
|
+
:greater_than_or_equal_to => answer_greater_than_or_equal_to,
|
75
|
+
:less_than_or_equal_to => answer_less_than_or_equal_to
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def from_question_to_attributes(question)
|
81
|
+
self.type = question.type
|
82
|
+
self.question_group = question.question_group
|
83
|
+
self.question_text = question.question_text
|
84
|
+
self.answer_options = question.answer_options
|
85
|
+
self.answer_presence = question.rules[:presence]
|
86
|
+
self.answer_minimum_length = question.rules[:minimum]
|
87
|
+
self.answer_maximum_length = question.rules[:maximum]
|
88
|
+
self.answer_greater_than_or_equal_to = question.rules[:greater_than_or_equal_to]
|
89
|
+
self.answer_less_than_or_equal_to = question.rules[:less_than_or_equal_to]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
module Questions
|
3
|
+
class Checkbox < Rapidfire::Question
|
4
|
+
validates :answer_options, :presence => true
|
5
|
+
|
6
|
+
def options
|
7
|
+
answer_options.split(/\r?\n/)
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_answer(answer)
|
11
|
+
super(answer)
|
12
|
+
|
13
|
+
if rules[:presence] == "1" || answer.answer_text.present?
|
14
|
+
answer.answer_text.split(",").each do |value|
|
15
|
+
answer.errors.add(:answer_text, :invalid) unless options.include?(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
module Questions
|
3
|
+
class Date < Rapidfire::Question
|
4
|
+
def validate_answer(answer)
|
5
|
+
super(answer)
|
6
|
+
|
7
|
+
if rules[:presence] == "1" || answer.answer_text.present?
|
8
|
+
begin ::Date.parse(answer.answer_text.to_s)
|
9
|
+
rescue ArgumentError => e
|
10
|
+
answer.errors.add(:answer_text, :invalid)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
module Questions
|
3
|
+
class Numeric < Rapidfire::Question
|
4
|
+
def validate_answer(answer)
|
5
|
+
super(answer)
|
6
|
+
|
7
|
+
if rules[:presence] == "1" || answer.answer_text.present?
|
8
|
+
gt_or_lt = {}
|
9
|
+
if rules[:greater_than_or_equal_to].present?
|
10
|
+
gt_or_lt[:greater_than_or_equal_to] = rules[:greater_than_or_equal_to].to_i
|
11
|
+
end
|
12
|
+
if rules[:less_than_or_equal_to].present?
|
13
|
+
gt_or_lt[:less_than_or_equal_to] = rules[:less_than_or_equal_to].to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
answer.validates_numericality_of :answer_text, gt_or_lt
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rapidfire
|
2
|
+
module Questions
|
3
|
+
class Select < Rapidfire::Question
|
4
|
+
validates :answer_options, :presence => true
|
5
|
+
|
6
|
+
def options
|
7
|
+
answer_options.split(/\r?\n/)
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_answer(answer)
|
11
|
+
super(answer)
|
12
|
+
|
13
|
+
if rules[:presence] == "1" || answer.answer_text.present?
|
14
|
+
answer.validates_inclusion_of :answer_text, :in => options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<h2>Please answer these Questions</h2>
|
2
|
+
<hr/>
|
3
|
+
|
4
|
+
<%= form_for([@question_group, @answer_group_builder]) do |f| %>
|
5
|
+
<%- @answer_group_builder.answers.each do |answer| %>
|
6
|
+
<%= f.fields_for("#{answer.question.id}", answer) do |answer_form| %>
|
7
|
+
<%= render_answer_form_helper(answer, answer_form) %>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
10
|
+
<%= f.submit "Save" %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
|
2
|
+
|
3
|
+
<%= f.label :answer_text, answer.question.question_text %>
|
4
|
+
<%= f.fields_for :answer_text do |af| %>
|
5
|
+
<%- answer.question.options.each do |option| %>
|
6
|
+
<%= af.label option do %>
|
7
|
+
<%= af.check_box nil, { id: nil, checked: checkbox_checked?(answer, option) }, option %>
|
8
|
+
<%= option %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
|
2
|
+
|
3
|
+
<%= f.label :answer_text, answer.question.question_text %>
|
4
|
+
<%- answer.question.options.each do |option| %>
|
5
|
+
<%= f.label "answer_text_#{option}", option do %>
|
6
|
+
<%= f.radio_button :answer_text, option %>
|
7
|
+
<%= option %>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h3><%= question_group.new_record? ? "New Group" : "Edit Group" %></h3>
|
2
|
+
|
3
|
+
<%= form_for question_group do |f| %>
|
4
|
+
<%- if question_group.errors.any? %>
|
5
|
+
<ul>
|
6
|
+
<%- question_group.errors.full_messages.each do |message| %>
|
7
|
+
<li><%= message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= f.label :name %>
|
13
|
+
<%= f.text_field :name %>
|
14
|
+
<%= f.submit %>
|
15
|
+
<% end %>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<tr id= "question_group_<%= question_group.id %>">
|
2
|
+
<td>
|
3
|
+
<% if can_administer? %>
|
4
|
+
<%= link_to question_group.name, question_group_questions_path(question_group) %>
|
5
|
+
<% else %>
|
6
|
+
<%= question_group.name %>
|
7
|
+
<% end %>
|
8
|
+
</td>
|
9
|
+
<td>
|
10
|
+
<ul class="horizontal-list">
|
11
|
+
<li><%= link_to "Answer Questions", new_question_group_answer_group_path(question_group) %></li>
|
12
|
+
<% if can_administer? %>
|
13
|
+
<li><%= link_to "Delete", [question_group], method: :delete %></li>
|
14
|
+
<% end %>
|
15
|
+
</ul>
|
16
|
+
</td>
|
17
|
+
</tr>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<h2>
|
2
|
+
Question Groups
|
3
|
+
<% if can_administer? %>
|
4
|
+
<%= link_to "New Group", new_question_group_path %>
|
5
|
+
<% end %>
|
6
|
+
</h2>
|
7
|
+
<hr/>
|
8
|
+
|
9
|
+
<table>
|
10
|
+
<colgroup>
|
11
|
+
<col>
|
12
|
+
<col width="250">
|
13
|
+
</colgroup>
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th>Question Group</th>
|
17
|
+
<th></th>
|
18
|
+
</tr>
|
19
|
+
</thead>
|
20
|
+
<tbody>
|
21
|
+
<%= render partial: "question_group", collection: @question_groups %>
|
22
|
+
</tbody>
|
23
|
+
</table>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: 'form', locals: {question_group: @question_group} %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<h3>Question</h3>
|
2
|
+
|
3
|
+
<%= form_for [@question_group, question] do |f| %>
|
4
|
+
<%- unless question.errors.empty? %>
|
5
|
+
<ul>
|
6
|
+
<%- question.errors.full_messages.each do |message| %>
|
7
|
+
<li><%= message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= f.label :type %>
|
13
|
+
<%= f.select :type, ::Rapidfire::QuestionProxy::QUESTION_TYPES, {}, id: "question_type" %>
|
14
|
+
|
15
|
+
<%= f.label :question_text %>
|
16
|
+
<%= f.text_field :question_text %>
|
17
|
+
|
18
|
+
<%= f.label :answer_options %>
|
19
|
+
<%= f.text_area :answer_options, rows: 5 %>
|
20
|
+
|
21
|
+
<h4>Other options</h4>
|
22
|
+
<hr/>
|
23
|
+
|
24
|
+
<%= f.label :answer_presence %>
|
25
|
+
<%= f.check_box :answer_presence %>
|
26
|
+
|
27
|
+
<%= f.label :answer_minimum_length %>
|
28
|
+
<%= f.text_field :answer_minimum_length %>
|
29
|
+
|
30
|
+
<%= f.label :answer_maximum_length %>
|
31
|
+
<%= f.text_field :answer_maximum_length %>
|
32
|
+
|
33
|
+
<%= f.label :answer_greater_than_or_equal_to %>
|
34
|
+
<%= f.text_field :answer_greater_than_or_equal_to %>
|
35
|
+
|
36
|
+
<%= f.label :answer_less_than_or_equal_to %>
|
37
|
+
<%= f.text_field :answer_less_than_or_equal_to %>
|
38
|
+
|
39
|
+
<%= f.submit %>
|
40
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<tr id="question_<%= question.id %>">
|
2
|
+
<td><%= question.question_text %></td>
|
3
|
+
<td>
|
4
|
+
<ul class="horizontal-list">
|
5
|
+
<li><%= link_to "Edit", [:edit, question.question_group, question] %></li>
|
6
|
+
<li><%= link_to "Delete", [question.question_group, question], method: :delete %></li>
|
7
|
+
</ul>
|
8
|
+
</td>
|
9
|
+
</tr>
|