rapidfire 1.0.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ea12eeb53ecdad94ef33490322027254065a6a9
4
- data.tar.gz: fd44602e45d96497ddbfdb4d04ef1278e0cea876
3
+ metadata.gz: bf282cfdca2422d38ece7af4cb87094810907d87
4
+ data.tar.gz: e9270a2229db1cb26aa3e502446756e0975be8e2
5
5
  SHA512:
6
- metadata.gz: cbac5a8118882bfed5d9a0e8f74fd05f56e867194411e330179ff7f52e54defc55d3191f8e09da4278335a2188549e5b9e693e4df98d3681db9dcac1b6a998ff
7
- data.tar.gz: ec8e3aeb29b4321ec31021ea9e0c67ea1fd8cf8bf6883defe6a6570119679ecdda599ce2d8bbee4ab3268a1cc71f360b149c69fca8fd984e4a81bd56c5d15fc7
6
+ metadata.gz: e06b07da985f333727aa9673f3551b1e06aa0cbfc34ab0c883d574462c22bdf6fbe13a5603c06ef685331039a410bfb7e9fed6e70d01b1e25679deda62cd565f
7
+ data.tar.gz: 55bad4a32186f3fca55958be7c5e8a7703f59d7064394cfe040c0aadc7b99961876a555cd66f361520979c4f5622c6e3d8004059eeabf7c222559318221a1703
data/README.md CHANGED
@@ -4,13 +4,15 @@
4
4
 
5
5
  One stop solution for all survey related requirements! Its tad easy!
6
6
 
7
+ This gem supports both **rails 3.2.13+** and **rails4** versions.
8
+
7
9
  You can see a demo of this gem [here](https://rapidfire.herokuapp.com).
8
10
  And the source code of demo [here](https://github.com/code-mancers/rapidfire-demo).
9
11
 
10
12
  ## Installation
11
13
  Add this line to your application's Gemfile:
12
14
 
13
- gem 'rapidfire', git: "git@github.com:code-mancers/rapidfire.git"
15
+ gem 'rapidfire'
14
16
 
15
17
  And then execute:
16
18
 
@@ -70,6 +72,79 @@ You can see them by running `bundle exec rake routes`.
70
72
  You can distribute this url so that survey takers can answer a particular survey
71
73
  of your interest.
72
74
 
75
+ ### Survey Results
76
+ A new api is released which helps in seeing results for each survey. The api is:
77
+
78
+ ```
79
+ GET /rapidfire/question_groups/<survey-id>/results
80
+ ```
81
+ This new api supports two formats: `html` and `json`. The `json` format is supported
82
+ so that end user can use any javascript based chart solutions and render results
83
+ in the format they pleased. An example can be seen [here](https://github.com/code-mancers/rapidfire-demo),
84
+ which uses chart.js to display results.
85
+
86
+ Diving into details of `json` format, all the questions can be categorized into
87
+ one of the two categories:
88
+ 1. **aggregatable**: questions like checkboxes, selects, radio buttons fall into
89
+ this category.
90
+ 2. **non-aggregatable**: questions like long answers, short answers, date, numeric
91
+ etc.
92
+
93
+ All the aggregatable answers will be returned in the form of hash, and the
94
+ non-aggregatable answers will be returned in the form of an array. A typical json
95
+ output will be like this:
96
+
97
+ ```json
98
+ [
99
+ {
100
+ "question_type": "Rapidfire::Questions::Radio",
101
+ "question_text": "Who is author of Waiting for godot?",
102
+ "results": {
103
+ "Sublime": 1,
104
+ "Emacs": 1,
105
+ "Vim": 1
106
+ }
107
+ },
108
+ {
109
+ "question_type": "Rapidfire::Questions::Checkbox",
110
+ "question_text": "Best rock band?",
111
+ "results": {
112
+ "Led Zeppelin": 2
113
+ }
114
+ },
115
+ {
116
+ "question_type": "Rapidfire::Questions::Date",
117
+ "question_text": "When is your birthday?",
118
+ "results": [
119
+ "04-02-1983",
120
+ "01/01/1970"
121
+ ]
122
+ },
123
+ {
124
+ "question_type": "Rapidfire::Questions::Long",
125
+ "question_text": "If Apple made a android phone what it will be called?",
126
+ "results": [
127
+ "Idude",
128
+ "apdroid"
129
+ ]
130
+ },
131
+ {
132
+ "question_type": "Rapidfire::Questions::Numeric",
133
+ "question_text": "Answer of life, universe and everything?",
134
+ "results": [
135
+ "42",
136
+ "0"
137
+ ]
138
+ },
139
+ {
140
+ "question_type": "Rapidfire::Questions::Select",
141
+ "question_text": "Places you want to visit after death",
142
+ "results": {
143
+ "Iran": 2
144
+ }
145
+ }
146
+ ]
147
+ ```
73
148
 
74
149
  ## How it works
75
150
  This gem gives you access to create questions in a groups, something similar to
@@ -111,9 +186,9 @@ The typical flow about how to use this gem is:
111
186
 
112
187
 
113
188
  ## 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.
189
+ 1. Add ability to sort questions, so that order is preserved.
190
+ 2. Add multi tenant support.
191
+ 3. Rename question-groups to surveys, and change routes accordingly.
117
192
 
118
193
  ## Contributing
119
194
 
@@ -1,14 +1,13 @@
1
1
  module Rapidfire
2
- class AnswerGroupsController < ApplicationController
2
+ class AnswerGroupsController < Rapidfire::ApplicationController
3
3
  before_filter :find_question_group!
4
4
 
5
5
  def new
6
- @answer_group_builder = AnswerGroupBuilder.new(current_user, @question_group)
6
+ @answer_group_builder = AnswerGroupBuilder.new(answer_group_params)
7
7
  end
8
8
 
9
9
  def create
10
- @answer_group_builder =
11
- AnswerGroupBuilder.new(current_user, @question_group, params[:answer_group])
10
+ @answer_group_builder = AnswerGroupBuilder.new(answer_group_params)
12
11
 
13
12
  if @answer_group_builder.save
14
13
  redirect_to question_groups_path
@@ -21,5 +20,10 @@ module Rapidfire
21
20
  def find_question_group!
22
21
  @question_group = QuestionGroup.find(params[:question_group_id])
23
22
  end
23
+
24
+ def answer_group_params
25
+ answer_params = { params: params[:answer_group] }
26
+ answer_params.merge(user: current_user, question_group: @question_group)
27
+ end
24
28
  end
25
29
  end
@@ -1,7 +1,8 @@
1
1
  module Rapidfire
2
- class QuestionGroupsController < ApplicationController
2
+ class QuestionGroupsController < Rapidfire::ApplicationController
3
3
  before_filter :authenticate_administrator!, except: :index
4
- respond_to :html
4
+ respond_to :html, :js
5
+ respond_to :json, only: :results
5
6
 
6
7
  def index
7
8
  @question_groups = QuestionGroup.all
@@ -14,10 +15,10 @@ module Rapidfire
14
15
  end
15
16
 
16
17
  def create
17
- @question_group = QuestionGroup.new(params[:question_group])
18
+ @question_group = QuestionGroup.new(question_group_params)
18
19
  @question_group.save
19
20
 
20
- respond_with(@question_group, location: rapidfire.question_groups_path)
21
+ respond_with(@question_group, location: rapidfire.question_groups_url)
21
22
  end
22
23
 
23
24
  def destroy
@@ -26,5 +27,22 @@ module Rapidfire
26
27
 
27
28
  respond_with(@question_group)
28
29
  end
30
+
31
+ def results
32
+ @question_group = QuestionGroup.find(params[:id])
33
+ @question_group_results =
34
+ QuestionGroupResults.new(question_group: @question_group).extract
35
+
36
+ respond_with(@question_group_results, root: false)
37
+ end
38
+
39
+ private
40
+ def question_group_params
41
+ if Rails::VERSION::MAJOR == 4
42
+ params.require(:question_group).permit(:name)
43
+ else
44
+ params[:question_group]
45
+ end
46
+ end
29
47
  end
30
48
  end
@@ -1,7 +1,7 @@
1
1
  module Rapidfire
2
- class QuestionsController < ApplicationController
2
+ class QuestionsController < Rapidfire::ApplicationController
3
3
  before_filter :authenticate_administrator!
4
- respond_to :html
4
+ respond_to :html, :js
5
5
 
6
6
  before_filter :find_question_group!
7
7
  before_filter :find_question!, :only => [:edit, :update, :destroy]
@@ -12,38 +12,34 @@ module Rapidfire
12
12
  end
13
13
 
14
14
  def new
15
- @question = QuestionProxy.new(:question_group => @question_group)
15
+ @question = QuestionForm.new(:question_group => @question_group)
16
16
  respond_with(@question)
17
17
  end
18
18
 
19
19
  def create
20
- proxy_params = params[:question].merge(:question_group => @question_group)
21
- @question = QuestionProxy.new(proxy_params)
20
+ form_params = params[:question].merge(:question_group => @question_group)
21
+ @question = QuestionForm.new(form_params)
22
22
  @question.save
23
23
 
24
- location = rapidfire.question_group_questions_path(@question_group)
25
- respond_with(@question, location: location)
24
+ respond_with(@question, location: index_location)
26
25
  end
27
26
 
28
27
  def edit
29
- @question = QuestionProxy.new(:question => @question)
28
+ @question = QuestionForm.new(:question => @question)
30
29
  respond_with(@question)
31
30
  end
32
31
 
33
32
  def update
34
- proxy_params = params[:question].merge(:question => @question)
35
- @question = QuestionProxy.new(proxy_params)
33
+ form_params = params[:question].merge(:question => @question)
34
+ @question = QuestionForm.new(form_params)
36
35
  @question.save
37
36
 
38
- location = rapidfire.question_group_questions_path(@question_group)
39
- respond_with(@question, location: location)
37
+ respond_with(@question, location: index_location)
40
38
  end
41
39
 
42
40
  def destroy
43
41
  @question.destroy
44
-
45
- location = rapidfire.question_group_questions_path(@question_group)
46
- respond_with(@question, location: location)
42
+ respond_with(@question, location: index_location)
47
43
  end
48
44
 
49
45
  private
@@ -54,5 +50,9 @@ module Rapidfire
54
50
  def find_question!
55
51
  @question = @question_group.questions.find(params[:id])
56
52
  end
53
+
54
+ def index_location
55
+ rapidfire.question_group_questions_url(@question_group)
56
+ end
57
57
  end
58
58
  end
@@ -6,7 +6,9 @@ module Rapidfire
6
6
  validates :question, :answer_group, presence: true
7
7
  validate :verify_answer_text, :if => "question.present?"
8
8
 
9
- attr_accessible :question_id, :answer_group, :answer_text
9
+ if Rails::VERSION::MAJOR == 3
10
+ attr_accessible :question_id, :answer_group, :answer_text
11
+ end
10
12
 
11
13
  private
12
14
  def verify_answer_text
@@ -4,6 +4,8 @@ module Rapidfire
4
4
  belongs_to :user, polymorphic: true
5
5
  has_many :answers, inverse_of: :answer_group, autosave: true
6
6
 
7
- attr_accessible :question_group, :user
7
+ if Rails::VERSION::MAJOR == 3
8
+ attr_accessible :question_group, :user
9
+ end
8
10
  end
9
11
  end
@@ -1,12 +1,16 @@
1
1
  module Rapidfire
2
2
  class Question < ActiveRecord::Base
3
3
  belongs_to :question_group, :inverse_of => :questions
4
- default_scope order(:position)
4
+ has_many :answers
5
+
6
+ default_scope { order(:position) }
5
7
 
6
8
  validates :question_group, :question_text, :presence => true
7
9
  serialize :validation_rules
8
10
 
9
- attr_accessible :question_group, :question_text, :validation_rules, :answer_options
11
+ if Rails::VERSION::MAJOR == 3
12
+ attr_accessible :question_group, :question_text, :validation_rules, :answer_options
13
+ end
10
14
 
11
15
  def self.inherited(child)
12
16
  child.instance_eval do
@@ -3,6 +3,8 @@ module Rapidfire
3
3
  has_many :questions
4
4
  validates :name, :presence => true
5
5
 
6
- attr_accessible :name
6
+ if Rails::VERSION::MAJOR == 3
7
+ attr_accessible :name
8
+ end
7
9
  end
8
10
  end
@@ -0,0 +1,15 @@
1
+ module Rapidfire
2
+ class QuestionResultSerializer < ActiveModel::Serializer
3
+ self.root = false
4
+
5
+ attributes :question_type, :question_text, :results
6
+
7
+ def question_type
8
+ object.question.type
9
+ end
10
+
11
+ def question_text
12
+ object.question.question_text
13
+ end
14
+ end
15
+ end
@@ -1,14 +1,9 @@
1
1
  module Rapidfire
2
- class AnswerGroupBuilder
3
- extend ActiveModel::Naming
4
- include ActiveModel::Validations
5
- include ActiveModel::Conversion
6
- def persisted?; false end
7
-
2
+ class AnswerGroupBuilder < Rapidfire::BaseService
8
3
  attr_accessor :user, :question_group, :questions, :answers, :params
9
4
 
10
- def initialize(user, question_group, params = {})
11
- @user, @question_group, @params = user, question_group, params
5
+ def initialize(params = {})
6
+ super(params)
12
7
  build_answer_group
13
8
  end
14
9
 
@@ -0,0 +1,21 @@
1
+ module Rapidfire
2
+ class BaseService
3
+ if Rails::VERSION::MAJOR == 4
4
+ include ActiveModel::Model
5
+ else
6
+ extend ActiveModel::Naming
7
+ include ActiveModel::Conversion
8
+ include ActiveModel::Validations
9
+
10
+ def persisted; false end
11
+
12
+ def initialize(params={})
13
+ params.each do |attr, value|
14
+ self.public_send("#{attr}=", value)
15
+ end if params
16
+
17
+ super()
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,8 +1,5 @@
1
1
  module Rapidfire
2
- class QuestionProxy
3
- extend ActiveModel::Naming
4
- include ActiveModel::Conversion
5
-
2
+ class QuestionForm < Rapidfire::BaseService
6
3
  AVAILABLE_QUESTIONS =
7
4
  [
8
5
  Rapidfire::Questions::Checkbox,
@@ -27,17 +24,13 @@ module Rapidfire
27
24
 
28
25
  delegate :valid?, :errors, :id, :to => :question
29
26
 
30
- def persisted?
31
- false
32
- end
33
-
34
27
  def to_model
35
28
  question
36
29
  end
37
30
 
38
31
  def initialize(params = {})
39
32
  from_question_to_attributes(params[:question]) if params[:question]
40
- params.each { |k, v| send("#{k}=", v) }
33
+ super(params)
41
34
  @question ||= question_group.questions.new
42
35
  end
43
36
 
@@ -0,0 +1,29 @@
1
+ module Rapidfire
2
+ class QuestionGroupResults < Rapidfire::BaseService
3
+ attr_accessor :question_group
4
+
5
+ # extracts question along with results
6
+ # each entry will have the following:
7
+ # 1. question type and question id
8
+ # 2. question text
9
+ # 3. if aggregatable, return each option with value
10
+ # 4. else return an array of all the answers given
11
+ def extract
12
+ @question_group.questions.collect do |question|
13
+ results =
14
+ case question
15
+ when Rapidfire::Questions::Select, Rapidfire::Questions::Radio,
16
+ Rapidfire::Questions::Checkbox
17
+ answers = question.answers.map(&:answer_text).map { |text| text.split(',') }.flatten
18
+ answers.inject(Hash.new(0)) { |total, e| total[e] += 1; total }
19
+
20
+ when Rapidfire::Questions::Short, Rapidfire::Questions::Date,
21
+ Rapidfire::Questions::Long, Rapidfire::Questions::Numeric
22
+ question.answers.pluck(:answer_text)
23
+ end
24
+
25
+ QuestionResult.new(question: question, results: results)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Rapidfire
2
+ class QuestionResult < Rapidfire::BaseService
3
+ include ActiveModel::Serialization
4
+
5
+ attr_accessor :question, :results
6
+
7
+ def active_model_serializer
8
+ Rapidfire::QuestionResultSerializer
9
+ end
10
+ end
11
+ end
@@ -10,6 +10,7 @@
10
10
  <ul class="horizontal-list">
11
11
  <li><%= link_to "Answer Questions", new_question_group_answer_group_path(question_group) %></li>
12
12
  <% if can_administer? %>
13
+ <li><%= link_to "Results", [:results, question_group] %></li>
13
14
  <li><%= link_to "Delete", [question_group], method: :delete %></li>
14
15
  <% end %>
15
16
  </ul>
@@ -0,0 +1,32 @@
1
+ <h1>Results</h1>
2
+
3
+ <%- @question_group_results.each do |result| %>
4
+ <div>
5
+ <h4><%= result.question.question_text %></h4>
6
+ <p>
7
+ <%- if result.results.is_a?(Array) %>
8
+ <ol>
9
+ <%- result.results.each do |answer| %>
10
+ <li><%= answer %></li>
11
+ <% end %>
12
+ </ol>
13
+ <% elsif result.results.is_a?(Hash) %>
14
+ <table>
15
+ <thead>
16
+ <tr>
17
+ <th>Option</th>
18
+ <th>Count</th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <%- result.results.each do |option, count| %>
23
+ <tr>
24
+ <td><%= option %></td>
25
+ <td><%= count %></td>
26
+ </tr>
27
+ <% end %>
28
+ </tbody>
29
+ </table>
30
+ <% end %>
31
+ </div>
32
+ <% end %>
@@ -10,7 +10,7 @@
10
10
  <% end %>
11
11
 
12
12
  <%= f.label :type %>
13
- <%= f.select :type, ::Rapidfire::QuestionProxy::QUESTION_TYPES, {}, id: "question_type" %>
13
+ <%= f.select :type, ::Rapidfire::QuestionForm::QUESTION_TYPES, {}, id: "question_type" %>
14
14
 
15
15
  <%= f.label :question_text %>
16
16
  <%= f.text_field :question_text %>
data/config/routes.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  Rapidfire::Engine.routes.draw do
2
2
  resources :question_groups do
3
+ get 'results', on: :member
4
+
3
5
  resources :questions
4
6
  resources :answer_groups, only: [:new, :create]
5
7
  end
@@ -1,3 +1,5 @@
1
+ require 'active_model_serializers'
2
+
1
3
  module Rapidfire
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Rapidfire
@@ -1,3 +1,3 @@
1
1
  module Rapidfire
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapidfire
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuva Kumar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-08 00:00:00.000000000 Z
11
+ date: 2013-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,20 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
20
- - - <
21
- - !ruby/object:Gem::Version
22
- version: 4.0.0
19
+ version: 3.2.13
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - '>='
28
25
  - !ruby/object:Gem::Version
29
- version: '3.0'
30
- - - <
26
+ version: 3.2.13
27
+ - !ruby/object:Gem::Dependency
28
+ name: active_model_serializers
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
31
39
  - !ruby/object:Gem::Version
32
- version: 4.0.0
40
+ version: 0.8.1
33
41
  - !ruby/object:Gem::Dependency
34
42
  name: sqlite3
35
43
  requirement: !ruby/object:Gem::Requirement
@@ -172,10 +180,8 @@ files:
172
180
  - app/helpers/rapidfire/application_helper.rb
173
181
  - app/models/rapidfire/answer.rb
174
182
  - app/models/rapidfire/answer_group.rb
175
- - app/models/rapidfire/answer_group_builder.rb
176
183
  - app/models/rapidfire/question.rb
177
184
  - app/models/rapidfire/question_group.rb
178
- - app/models/rapidfire/question_proxy.rb
179
185
  - app/models/rapidfire/questions/checkbox.rb
180
186
  - app/models/rapidfire/questions/date.rb
181
187
  - app/models/rapidfire/questions/long.rb
@@ -183,6 +189,12 @@ files:
183
189
  - app/models/rapidfire/questions/radio.rb
184
190
  - app/models/rapidfire/questions/select.rb
185
191
  - app/models/rapidfire/questions/short.rb
192
+ - app/serializers/rapidfire/question_result_serializer.rb
193
+ - app/services/rapidfire/answer_group_builder.rb
194
+ - app/services/rapidfire/base_service.rb
195
+ - app/services/rapidfire/question_form.rb
196
+ - app/services/rapidfire/question_group_results.rb
197
+ - app/services/rapidfire/question_result.rb
186
198
  - app/views/rapidfire/answer_groups/new.html.erb
187
199
  - app/views/rapidfire/answers/_checkbox.html.erb
188
200
  - app/views/rapidfire/answers/_date.html.erb
@@ -196,6 +208,7 @@ files:
196
208
  - app/views/rapidfire/question_groups/_question_group.html.erb
197
209
  - app/views/rapidfire/question_groups/index.html.erb
198
210
  - app/views/rapidfire/question_groups/new.html.erb
211
+ - app/views/rapidfire/question_groups/results.html.erb
199
212
  - app/views/rapidfire/questions/_form.html.erb
200
213
  - app/views/rapidfire/questions/_question.html.erb
201
214
  - app/views/rapidfire/questions/edit.html.erb