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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2ea12eeb53ecdad94ef33490322027254065a6a9
4
+ data.tar.gz: fd44602e45d96497ddbfdb4d04ef1278e0cea876
5
+ SHA512:
6
+ metadata.gz: cbac5a8118882bfed5d9a0e8f74fd05f56e867194411e330179ff7f52e54defc55d3191f8e09da4278335a2188549e5b9e693e4df98d3681db9dcac1b6a998ff
7
+ data.tar.gz: ec8e3aeb29b4321ec31021ea9e0c67ea1fd8cf8bf6883defe6a6570119679ecdda599ce2d8bbee4ab3268a1cc71f360b149c69fca8fd984e4a81bd56c5d15fc7
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # Rapidfire
2
+ [![Code Climate](https://codeclimate.com/repos/51a70089f3ea000534070811/badges/aedc90c3b5481e7569bb/gpa.png)](https://codeclimate.com/repos/51a70089f3ea000534070811/feed)
3
+ [![Build Status](https://travis-ci.org/code-mancers/rapidfire.png?branch=master)](https://travis-ci.org/code-mancers/rapidfire)
4
+
5
+ One stop solution for all survey related requirements! Its tad easy!
6
+
7
+ You can see a demo of this gem [here](https://rapidfire.herokuapp.com).
8
+ And the source code of demo [here](https://github.com/code-mancers/rapidfire-demo).
9
+
10
+ ## Installation
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'rapidfire', git: "git@github.com:code-mancers/rapidfire.git"
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+ $ bundle exec rake rapidfire:install:migrations
19
+ $ bundle exec rake db:migrate
20
+
21
+ And if you want to customize rapidfire views, you can do
22
+
23
+ $ bundle exec rails generate rapidfire:views
24
+
25
+ ## Usage
26
+
27
+ Add this line to your routes will and you will be good to go!
28
+
29
+ mount Rapidfire::Engine => "/rapidfire"
30
+
31
+ And point your browser to [http://localhost:3000/rapidfire](http://localhost:3000/rapidfire)
32
+
33
+ All rapidfire controllers inherit from your `ApplicationController`. So define 2
34
+ methods `current_user` and `can_administer?` on your `ApplicationController`
35
+
36
+ 1. `current_user` : the user who is answering the survey. can be `nil`
37
+ 2. `can_administer?` : a method which determines whether current user can
38
+ create/update survey questions.
39
+
40
+ Typical implementation would be:
41
+
42
+ ```ruby
43
+ class ApplicationController < ActionController::Base
44
+ def current_user
45
+ @current_user ||= User.find(session[:user_id])
46
+ end
47
+
48
+ def can_administer?
49
+ current_user.try(:admin?)
50
+ end
51
+ end
52
+ ```
53
+
54
+ If you are using authentication gems like devise, you get `current_user` for free
55
+ and you don't have to define it.
56
+
57
+ ### Routes Information
58
+ Once this gem is mounted on, say at 'rapidfire', it generates several routes
59
+ You can see them by running `bundle exec rake routes`.
60
+
61
+ 1. The `root_path` i.e `localhost:3000/rapidfire` always points to list of
62
+ surveys {they are called question groups}. Admin can manage surveys, and
63
+ any user {who cannot administer} can see list of surveys.
64
+ 2. Optionally, each survey can by answered by visiting this path:
65
+
66
+ ```
67
+ localhost:3000/rapidfire/question_groups/<survey-id>/answer_groups/new
68
+ ```
69
+
70
+ You can distribute this url so that survey takers can answer a particular survey
71
+ of your interest.
72
+
73
+
74
+ ## How it works
75
+ This gem gives you access to create questions in a groups, something similar to
76
+ survey. Once you have created a group and add questions to it, you can pass
77
+ around the form url where others can answer your questions.
78
+
79
+ The typical flow about how to use this gem is:
80
+
81
+ 1. Create a question group by giving it a name.
82
+ 2. Once group is created, you can click on the group which takes you to another
83
+ page where you can manage questions.
84
+ 3. Create a question by clicking on add new, and you will be provided by these
85
+ options: Each question will have a type
86
+ - **Checkbox** Create a question which contains multiple checkboxes with the
87
+ options that you provide in `answer options` field. Note that each option
88
+ should be on a separate line.
89
+ - **Date** It takes date as an answer
90
+ - **Long** It needs a description as answer. Renders a textarea.
91
+ - **Numeric** It takes a number as an answer
92
+ - **Radio** It renders set of radio buttons by taking answer options.
93
+ - **Select** It renders a dropdown by taking answer options.
94
+ - **Short** It takes a string as an answer. Short answer.
95
+
96
+ 4. Once the type is filled, you can optionally fill other details like
97
+ - **Question text** What is the question?
98
+ - **Answer options** Give options separated by newline for questions of type
99
+ checkbox, radio buttons or select.
100
+ - **Answer presence** Should you mandate answering this question?
101
+ - **min and max length** Checks whether answer if in between min and max length.
102
+ Ignores if blank.
103
+ - **greater than and less than** Applicable for numeric question where answer
104
+ is validated with these values.
105
+
106
+ 5. Once the questions are populated, you can return to root_path ie by clicking
107
+ `Question Groups` and share distribute answer url so that others can answer
108
+ the questions populated.
109
+ 6. Note that answers fail to persist of the criteria that you have provided while
110
+ creating questions fail.
111
+
112
+
113
+ ## TODO
114
+ 1. Provide a way for admin to see survey results
115
+ 2. Add support for rails-4
116
+ 3. Add ability to sort questions, so that order is preserved.
117
+
118
+ ## Contributing
119
+
120
+ 1. Fork it
121
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
122
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
123
+ 4. Push to the branch (`git push origin my-new-feature`)
124
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Rapidfire'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ desc 'Default: run rspec tests.'
31
+ task :default => :rspec
32
+
33
+ desc 'Run rspec unit and integration tests'
34
+ task :rspec do |t|
35
+ exec('bundle exec rspec spec')
36
+ end
@@ -0,0 +1,12 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
@@ -0,0 +1,22 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ */
13
+
14
+ .horizontal-list {
15
+ list-style: none;
16
+ }
17
+
18
+ .horizontal-list > li {
19
+ float: left;
20
+ display: inline;
21
+ margin: 0px 5px;
22
+ }
@@ -0,0 +1,25 @@
1
+ module Rapidfire
2
+ class AnswerGroupsController < ApplicationController
3
+ before_filter :find_question_group!
4
+
5
+ def new
6
+ @answer_group_builder = AnswerGroupBuilder.new(current_user, @question_group)
7
+ end
8
+
9
+ def create
10
+ @answer_group_builder =
11
+ AnswerGroupBuilder.new(current_user, @question_group, params[:answer_group])
12
+
13
+ if @answer_group_builder.save
14
+ redirect_to question_groups_path
15
+ else
16
+ render :new
17
+ end
18
+ end
19
+
20
+ private
21
+ def find_question_group!
22
+ @question_group = QuestionGroup.find(params[:question_group_id])
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Rapidfire
2
+ class ApplicationController < ::ApplicationController
3
+ helper_method :can_administer?
4
+
5
+ def authenticate_administrator!
6
+ unless can_administer?
7
+ raise Rapidfire::AccessDenied.new("cannot administer questions")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module Rapidfire
2
+ class QuestionGroupsController < ApplicationController
3
+ before_filter :authenticate_administrator!, except: :index
4
+ respond_to :html
5
+
6
+ def index
7
+ @question_groups = QuestionGroup.all
8
+ respond_with(@question_groups)
9
+ end
10
+
11
+ def new
12
+ @question_group = QuestionGroup.new
13
+ respond_with(@question_group)
14
+ end
15
+
16
+ def create
17
+ @question_group = QuestionGroup.new(params[:question_group])
18
+ @question_group.save
19
+
20
+ respond_with(@question_group, location: rapidfire.question_groups_path)
21
+ end
22
+
23
+ def destroy
24
+ @question_group = QuestionGroup.find(params[:id])
25
+ @question_group.destroy
26
+
27
+ respond_with(@question_group)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ module Rapidfire
2
+ class QuestionsController < ApplicationController
3
+ before_filter :authenticate_administrator!
4
+ respond_to :html
5
+
6
+ before_filter :find_question_group!
7
+ before_filter :find_question!, :only => [:edit, :update, :destroy]
8
+
9
+ def index
10
+ @questions = @question_group.questions
11
+ respond_with(@questions)
12
+ end
13
+
14
+ def new
15
+ @question = QuestionProxy.new(:question_group => @question_group)
16
+ respond_with(@question)
17
+ end
18
+
19
+ def create
20
+ proxy_params = params[:question].merge(:question_group => @question_group)
21
+ @question = QuestionProxy.new(proxy_params)
22
+ @question.save
23
+
24
+ location = rapidfire.question_group_questions_path(@question_group)
25
+ respond_with(@question, location: location)
26
+ end
27
+
28
+ def edit
29
+ @question = QuestionProxy.new(:question => @question)
30
+ respond_with(@question)
31
+ end
32
+
33
+ def update
34
+ proxy_params = params[:question].merge(:question => @question)
35
+ @question = QuestionProxy.new(proxy_params)
36
+ @question.save
37
+
38
+ location = rapidfire.question_group_questions_path(@question_group)
39
+ respond_with(@question, location: location)
40
+ end
41
+
42
+ def destroy
43
+ @question.destroy
44
+
45
+ location = rapidfire.question_group_questions_path(@question_group)
46
+ respond_with(@question, location: location)
47
+ end
48
+
49
+ private
50
+ def find_question_group!
51
+ @question_group = QuestionGroup.find(params[:question_group_id])
52
+ end
53
+
54
+ def find_question!
55
+ @question = @question_group.questions.find(params[:id])
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,12 @@
1
+ module Rapidfire
2
+ module ApplicationHelper
3
+ def render_answer_form_helper(answer, form)
4
+ partial = answer.question.type.to_s.split("::").last.downcase
5
+ render partial: "rapidfire/answers/#{partial}", locals: { f: form, answer: answer }
6
+ end
7
+
8
+ def checkbox_checked?(answer, option)
9
+ answer.answer_text.to_s.split(",").include?(option)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Rapidfire
2
+ class Answer < ActiveRecord::Base
3
+ belongs_to :question
4
+ belongs_to :answer_group, inverse_of: :answers
5
+
6
+ validates :question, :answer_group, presence: true
7
+ validate :verify_answer_text, :if => "question.present?"
8
+
9
+ attr_accessible :question_id, :answer_group, :answer_text
10
+
11
+ private
12
+ def verify_answer_text
13
+ question.validate_answer(self)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Rapidfire
2
+ class AnswerGroup < ActiveRecord::Base
3
+ belongs_to :question_group
4
+ belongs_to :user, polymorphic: true
5
+ has_many :answers, inverse_of: :answer_group, autosave: true
6
+
7
+ attr_accessible :question_group, :user
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ module Rapidfire
2
+ class AnswerGroupBuilder
3
+ extend ActiveModel::Naming
4
+ include ActiveModel::Validations
5
+ include ActiveModel::Conversion
6
+ def persisted?; false end
7
+
8
+ attr_accessor :user, :question_group, :questions, :answers, :params
9
+
10
+ def initialize(user, question_group, params = {})
11
+ @user, @question_group, @params = user, question_group, params
12
+ build_answer_group
13
+ end
14
+
15
+ def to_model
16
+ @answer_group
17
+ end
18
+
19
+ def save!(options = {})
20
+ params.each do |question_id, answer_attributes|
21
+ if answer = @answer_group.answers.find { |a| a.question_id.to_s == question_id.to_s }
22
+ text = answer_attributes[:answer_text]
23
+ answer.answer_text =
24
+ text.is_a?(Array) ? strip_checkbox_answers(text).join(',') : text
25
+ end
26
+ end
27
+
28
+ @answer_group.save!(options)
29
+ end
30
+
31
+ def save(options = {})
32
+ save!(options)
33
+ rescue Exception => e
34
+ # repopulate answers here in case of failure as they are not getting updated
35
+ @answers = @question_group.questions.collect do |question|
36
+ @answer_group.answers.find { |a| a.question_id == question.id }
37
+ end
38
+ false
39
+ end
40
+
41
+ private
42
+ def build_answer_group
43
+ @answer_group = AnswerGroup.new(user: user, question_group: question_group)
44
+ @answers = @question_group.questions.collect do |question|
45
+ @answer_group.answers.build(question_id: question.id)
46
+ end
47
+ end
48
+
49
+ def strip_checkbox_answers(text)
50
+ text.reject(&:blank?).reject { |t| t == "0" }
51
+ end
52
+ end
53
+ end