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.
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>