rapidfire 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +124 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/javascripts/rapidfire/application.js +12 -0
  6. data/app/assets/stylesheets/rapidfire/application.css +22 -0
  7. data/app/controllers/rapidfire/answer_groups_controller.rb +25 -0
  8. data/app/controllers/rapidfire/application_controller.rb +11 -0
  9. data/app/controllers/rapidfire/question_groups_controller.rb +30 -0
  10. data/app/controllers/rapidfire/questions_controller.rb +58 -0
  11. data/app/helpers/rapidfire/application_helper.rb +12 -0
  12. data/app/models/rapidfire/answer.rb +16 -0
  13. data/app/models/rapidfire/answer_group.rb +9 -0
  14. data/app/models/rapidfire/answer_group_builder.rb +53 -0
  15. data/app/models/rapidfire/question.rb +40 -0
  16. data/app/models/rapidfire/question_group.rb +8 -0
  17. data/app/models/rapidfire/question_proxy.rb +92 -0
  18. data/app/models/rapidfire/questions/checkbox.rb +21 -0
  19. data/app/models/rapidfire/questions/date.rb +16 -0
  20. data/app/models/rapidfire/questions/long.rb +6 -0
  21. data/app/models/rapidfire/questions/numeric.rb +21 -0
  22. data/app/models/rapidfire/questions/radio.rb +6 -0
  23. data/app/models/rapidfire/questions/select.rb +19 -0
  24. data/app/models/rapidfire/questions/short.rb +6 -0
  25. data/app/views/rapidfire/answer_groups/new.html.erb +11 -0
  26. data/app/views/rapidfire/answers/_checkbox.html.erb +11 -0
  27. data/app/views/rapidfire/answers/_date.html.erb +4 -0
  28. data/app/views/rapidfire/answers/_errors.html.erb +7 -0
  29. data/app/views/rapidfire/answers/_long.html.erb +4 -0
  30. data/app/views/rapidfire/answers/_numeric.html.erb +4 -0
  31. data/app/views/rapidfire/answers/_radio.html.erb +9 -0
  32. data/app/views/rapidfire/answers/_select.html.erb +4 -0
  33. data/app/views/rapidfire/answers/_short.html.erb +4 -0
  34. data/app/views/rapidfire/question_groups/_form.html.erb +15 -0
  35. data/app/views/rapidfire/question_groups/_question_group.html.erb +17 -0
  36. data/app/views/rapidfire/question_groups/index.html.erb +23 -0
  37. data/app/views/rapidfire/question_groups/new.html.erb +1 -0
  38. data/app/views/rapidfire/questions/_form.html.erb +40 -0
  39. data/app/views/rapidfire/questions/_question.html.erb +9 -0
  40. data/app/views/rapidfire/questions/edit.html.erb +1 -0
  41. data/app/views/rapidfire/questions/index.html.erb +21 -0
  42. data/app/views/rapidfire/questions/new.html.erb +1 -0
  43. data/config/database.yml +8 -0
  44. data/config/routes.rb +8 -0
  45. data/db/migrate/20130502170733_create_rapidfire_question_groups.rb +8 -0
  46. data/db/migrate/20130502195310_create_rapidfire_questions.rb +15 -0
  47. data/db/migrate/20130502195415_create_rapidfire_answer_groups.rb +12 -0
  48. data/db/migrate/20130502195504_create_rapidfire_answers.rb +13 -0
  49. data/lib/generators/rapidfire/views_generator.rb +23 -0
  50. data/lib/rapidfire.rb +6 -0
  51. data/lib/rapidfire/engine.rb +5 -0
  52. data/lib/rapidfire/version.rb +3 -0
  53. data/lib/tasks/rapidfire_tasks.rake +4 -0
  54. 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,8 @@
1
+ module Rapidfire
2
+ class QuestionGroup < ActiveRecord::Base
3
+ has_many :questions
4
+ validates :name, :presence => true
5
+
6
+ attr_accessible :name
7
+ end
8
+ 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,6 @@
1
+ module Rapidfire
2
+ module Questions
3
+ class Long < Rapidfire::Question
4
+ end
5
+ end
6
+ 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,6 @@
1
+ module Rapidfire
2
+ module Questions
3
+ class Radio < Select
4
+ end
5
+ end
6
+ 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,6 @@
1
+ module Rapidfire
2
+ module Questions
3
+ class Short < Rapidfire::Question
4
+ end
5
+ end
6
+ 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,4 @@
1
+ <%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
2
+
3
+ <%= f.label :answer_text, answer.question.question_text %>
4
+ <%= f.text_field :answer_text %>
@@ -0,0 +1,7 @@
1
+ <%- if answer.errors.any? %>
2
+ <ul>
3
+ <%- answer.errors.full_messages.each do |message| %>
4
+ <li><%= message %></li>
5
+ <% end %>
6
+ </ul>
7
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
2
+
3
+ <%= f.label :answer_text, answer.question.question_text %>
4
+ <%= f.text_area :answer_text, :rows => 5 %>
@@ -0,0 +1,4 @@
1
+ <%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
2
+
3
+ <%= f.label :answer_text, answer.question.question_text %>
4
+ <%= f.text_field :answer_text %>
@@ -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,4 @@
1
+ <%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
2
+
3
+ <%= f.label :answer_text, answer.question.question_text %>
4
+ <%= f.select :answer_text, answer.question.options, include_blank: true %>
@@ -0,0 +1,4 @@
1
+ <%= render partial: "rapidfire/answers/errors", locals: {answer: answer} %>
2
+
3
+ <%= f.label :answer_text, answer.question.question_text %>
4
+ <%= f.text_field :answer_text %>
@@ -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>