rapidfire 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|