rapidfire 1.0.0 → 1.2.0

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