census 0.1.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.
- data/LICENSE +20 -0
- data/README.rdoc +5 -0
- data/Rakefile +70 -0
- data/VERSION +1 -0
- data/app/controllers/census/data_groups_controller.rb +44 -0
- data/app/helpers/census_helper.rb +15 -0
- data/app/models/answer.rb +43 -0
- data/app/models/choice.rb +9 -0
- data/app/models/data_group.rb +10 -0
- data/app/models/question.rb +54 -0
- data/app/views/census/_question_fields.html.erb +40 -0
- data/app/views/census/_user_answers.html.erb +10 -0
- data/app/views/census/_user_questions.html.erb +10 -0
- data/app/views/census/data_groups/_choice_fields.html.erb +4 -0
- data/app/views/census/data_groups/_form.html.erb +11 -0
- data/app/views/census/data_groups/_question_fields.html.erb +22 -0
- data/app/views/census/data_groups/edit.html.erb +7 -0
- data/app/views/census/data_groups/index.html.erb +10 -0
- data/app/views/census/data_groups/new.html.erb +7 -0
- data/config/routes.rb +8 -0
- data/generators/census/USAGE +1 -0
- data/generators/census/census_generator.rb +39 -0
- data/generators/census/lib/insert_commands.rb +33 -0
- data/generators/census/lib/rake_commands.rb +22 -0
- data/generators/census/templates/README +29 -0
- data/generators/census/templates/census.js +10 -0
- data/generators/census/templates/census.rb +14 -0
- data/generators/census/templates/factories.rb +24 -0
- data/generators/census/templates/migrations/with_users.rb +60 -0
- data/generators/census/templates/migrations/without_users.rb +54 -0
- data/generators/census/templates/user.rb +3 -0
- data/lib/census/data_type.rb +33 -0
- data/lib/census/user.rb +85 -0
- data/lib/census.rb +1 -0
- data/rails/init.rb +7 -0
- data/shoulda_macros/census.rb +35 -0
- data/test/controllers/data_groups_controller_test.rb +165 -0
- data/test/models/answer_test.rb +44 -0
- data/test/models/choice_test.rb +24 -0
- data/test/models/data_group_test.rb +12 -0
- data/test/models/question_test.rb +213 -0
- data/test/models/user_test.rb +9 -0
- data/test/rails_root/app/controllers/application_controller.rb +10 -0
- data/test/rails_root/app/helpers/application_helper.rb +3 -0
- data/test/rails_root/app/models/user.rb +3 -0
- data/test/rails_root/config/boot.rb +110 -0
- data/test/rails_root/config/environment.rb +8 -0
- data/test/rails_root/config/environments/development.rb +17 -0
- data/test/rails_root/config/environments/production.rb +28 -0
- data/test/rails_root/config/environments/test.rb +32 -0
- data/test/rails_root/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_root/config/initializers/census.rb +14 -0
- data/test/rails_root/config/initializers/inflections.rb +10 -0
- data/test/rails_root/config/initializers/mime_types.rb +5 -0
- data/test/rails_root/config/initializers/new_rails_defaults.rb +21 -0
- data/test/rails_root/config/initializers/session_store.rb +15 -0
- data/test/rails_root/config/routes.rb +43 -0
- data/test/rails_root/db/migrate/20100406160306_create_census_tables.rb +60 -0
- data/test/rails_root/db/schema.rb +58 -0
- data/test/rails_root/test/factories/census.rb +24 -0
- data/test/rails_root/test/performance/browsing_test.rb +9 -0
- data/test/rails_root/test/test_helper.rb +38 -0
- data/test/rails_root/vendor/gems/acts_as_list-0.1.2/lib/acts_as_list.rb +254 -0
- data/test/rails_root/vendor/gems/acts_as_list-0.1.2/test/list_test.rb +369 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/install.rb +1 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/lib/inverse_of.rb +293 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/rails/init.rb +1 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/cases/helper.rb +28 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/cases/inverse_associations_test.rb +567 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/club.rb +13 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/face.rb +7 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/interest.rb +5 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/man.rb +9 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/sponsor.rb +4 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/models/zine.rb +3 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/test/schema/schema.rb +32 -0
- data/test/rails_root/vendor/gems/inverse_of-0.0.1/uninstall.rb +1 -0
- data/test/test_helper.rb +19 -0
- metadata +190 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class CreateCensusTables < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :data_groups do |t|
|
|
4
|
+
t.string :name
|
|
5
|
+
t.integer :position
|
|
6
|
+
t.timestamps
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
create_table :questions do |t|
|
|
10
|
+
t.integer :data_group_id
|
|
11
|
+
t.string :data_type
|
|
12
|
+
t.string :prompt
|
|
13
|
+
t.boolean :multiple
|
|
14
|
+
t.boolean :other
|
|
15
|
+
t.integer :position
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
add_index :questions, :data_group_id
|
|
20
|
+
|
|
21
|
+
create_table :choices do |t|
|
|
22
|
+
t.integer :question_id
|
|
23
|
+
t.string :value
|
|
24
|
+
t.integer :position
|
|
25
|
+
t.timestamps
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_index :choices, :question_id
|
|
29
|
+
|
|
30
|
+
create_table :answers do |t|
|
|
31
|
+
t.integer :question_id
|
|
32
|
+
t.integer :user_id
|
|
33
|
+
t.string :data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
add_index :answers, :question_id
|
|
37
|
+
add_index :answers, :user_id
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.down
|
|
42
|
+
remove_index :answers, :question_id
|
|
43
|
+
remove_index :answers, :user_id
|
|
44
|
+
drop_table :answers
|
|
45
|
+
|
|
46
|
+
remove_index :choices, :question_id
|
|
47
|
+
drop_table :choices
|
|
48
|
+
|
|
49
|
+
remove_index :questions, :data_group_id
|
|
50
|
+
drop_table :questions
|
|
51
|
+
|
|
52
|
+
drop_table :data_groups
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Census
|
|
2
|
+
class DataType
|
|
3
|
+
attr_accessor :name
|
|
4
|
+
attr_accessor :sql_transform
|
|
5
|
+
attr_accessor :format_data
|
|
6
|
+
attr_accessor :validate_data
|
|
7
|
+
|
|
8
|
+
@@data_types = []
|
|
9
|
+
|
|
10
|
+
def self.all
|
|
11
|
+
@@data_types
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.define(name, options = {})
|
|
15
|
+
@@data_types << DataType.new(name, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.find(name)
|
|
19
|
+
@@data_types.select {|dt| dt.name == name}.try(:first)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(name, options)
|
|
23
|
+
@name = name
|
|
24
|
+
@sql_transform = options[:sql_transform] || lambda {|column_name| "#{column_name}"}
|
|
25
|
+
@format_data = options[:format_data] || lambda {|data| data}
|
|
26
|
+
@validate_data = options[:validate_data] || lambda {|data| nil}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
@name
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/census/user.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Census
|
|
2
|
+
module User
|
|
3
|
+
|
|
4
|
+
# Hook for all Census::User modules.
|
|
5
|
+
#
|
|
6
|
+
# If you need to override parts of Census::User,
|
|
7
|
+
# extend and include à la carte.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# extend ClassMethods
|
|
11
|
+
# include InstanceMethods
|
|
12
|
+
# include Callbacks
|
|
13
|
+
#
|
|
14
|
+
# @see ClassMethods
|
|
15
|
+
# @see InstanceMethods
|
|
16
|
+
# @see Callbacks
|
|
17
|
+
def self.included(model)
|
|
18
|
+
model.extend(ClassMethods)
|
|
19
|
+
|
|
20
|
+
model.send(:include, InstanceMethods)
|
|
21
|
+
model.send(:include, Associations)
|
|
22
|
+
model.send(:include, Callbacks)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Associations
|
|
26
|
+
# Hook for defining associations.
|
|
27
|
+
def self.included(model)
|
|
28
|
+
model.class_eval do
|
|
29
|
+
has_many :answers, :dependent => :destroy
|
|
30
|
+
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:data].blank? }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Callbacks
|
|
36
|
+
# Hook for callbacks.
|
|
37
|
+
#
|
|
38
|
+
# empty answers are removed after_save.
|
|
39
|
+
def self.included(model)
|
|
40
|
+
model.class_eval do
|
|
41
|
+
after_save :remove_empty_answers
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module InstanceMethods
|
|
47
|
+
#
|
|
48
|
+
# Returns this user's first answer for the given question, or a new empty
|
|
49
|
+
# answer if the user has not answered the question.
|
|
50
|
+
#
|
|
51
|
+
def first_answer_for(question)
|
|
52
|
+
answers.select {|a| a.question == question}.first || answers.build(:question => question, :data => '')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Returns an array of this user's answers for the given question. The returned
|
|
57
|
+
# array will be empty if the user has not answered this question.
|
|
58
|
+
#
|
|
59
|
+
def all_answers_for(question)
|
|
60
|
+
answers.select {|a| a.question == question}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
#
|
|
64
|
+
# Returns this user's answer for a specific choice under a multiple-choice
|
|
65
|
+
# question, or a new empty answer if the user did not select the given choice.
|
|
66
|
+
#
|
|
67
|
+
def answer_for_choice(choice)
|
|
68
|
+
answers.select {|a| a.question == choice.question && a.data == choice.value}.first || answers.build(:question => choice.question, :data => '')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# After save callback, used to remove blank answers
|
|
75
|
+
#
|
|
76
|
+
def remove_empty_answers
|
|
77
|
+
answers.each {|answer| answer.destroy if answer.data.blank? }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module ClassMethods
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/census.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'census/user'
|
data/rails/init.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Census
|
|
2
|
+
module Shoulda
|
|
3
|
+
|
|
4
|
+
def should_accept_nested_attributes_for(*attr_names)
|
|
5
|
+
klass = self.name.gsub(/Test$/, '').constantize
|
|
6
|
+
|
|
7
|
+
context "#{klass}" do
|
|
8
|
+
attr_names.each do |association_name|
|
|
9
|
+
should "accept nested attrs for #{association_name}" do
|
|
10
|
+
meth = "#{association_name}_attributes="
|
|
11
|
+
assert ([meth,meth.to_sym].any?{ |m| klass.instance_methods.include?(m) }),
|
|
12
|
+
"#{klass} does not accept nested attributes for #{association_name}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def should_act_as_list
|
|
19
|
+
klass = self.name.gsub(/Test$/, '').constantize
|
|
20
|
+
|
|
21
|
+
context "To support acts_as_list" do
|
|
22
|
+
should_have_db_column('position', :type => :integer)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should "include ActsAsList methods" do
|
|
26
|
+
assert klass.include?(ActsAsList::InstanceMethods)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
should_have_instance_methods :acts_as_list_class, :position_column, :scope_condition
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Test::Unit::TestCase.extend(Census::Shoulda)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class DataGroupsControllerTest < ActionController::TestCase
|
|
4
|
+
|
|
5
|
+
tests Census::DataGroupsController
|
|
6
|
+
|
|
7
|
+
should_route :get, '/census/admin',
|
|
8
|
+
:controller => 'census/data_groups', :action => 'index'
|
|
9
|
+
|
|
10
|
+
should_route :get, '/census/data_groups/new',
|
|
11
|
+
:controller => 'census/data_groups', :action => 'new'
|
|
12
|
+
|
|
13
|
+
should_route :post, '/census/data_groups',
|
|
14
|
+
:controller => 'census/data_groups', :action => 'create'
|
|
15
|
+
|
|
16
|
+
should_route :get, '/census/data_groups/1/edit',
|
|
17
|
+
:controller => 'census/data_groups', :action => 'edit', :id => '1'
|
|
18
|
+
|
|
19
|
+
should_route :put, '/census/data_groups/1',
|
|
20
|
+
:controller => 'census/data_groups', :action => 'update', :id => '1'
|
|
21
|
+
|
|
22
|
+
should_route :delete, '/census/data_groups/1',
|
|
23
|
+
:controller => 'census/data_groups', :action => 'destroy', :id => '1'
|
|
24
|
+
|
|
25
|
+
context 'The Census::DataGroupsController' do
|
|
26
|
+
|
|
27
|
+
context 'using GET to index' do
|
|
28
|
+
|
|
29
|
+
setup do
|
|
30
|
+
@group = Factory(:data_group)
|
|
31
|
+
get :index
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
should_respond_with :success
|
|
35
|
+
should_respond_with_content_type :html
|
|
36
|
+
should_render_template :index
|
|
37
|
+
should_assign_to :data_groups
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'using GET to new' do
|
|
42
|
+
|
|
43
|
+
setup do
|
|
44
|
+
get :new
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
should_respond_with :success
|
|
48
|
+
should_respond_with_content_type :html
|
|
49
|
+
should_render_template :new
|
|
50
|
+
should_assign_to :data_group
|
|
51
|
+
|
|
52
|
+
should 'have a new data group record' do
|
|
53
|
+
assert assigns(:data_group).new_record?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context 'using POST to create' do
|
|
59
|
+
|
|
60
|
+
context 'with invalid attributes' do
|
|
61
|
+
|
|
62
|
+
setup do
|
|
63
|
+
DataGroup.any_instance.stubs(:valid?).returns(false)
|
|
64
|
+
post :create, :data_group => Factory.attributes_for(:data_group)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
should_respond_with :unprocessable_entity
|
|
68
|
+
should_respond_with_content_type :html
|
|
69
|
+
should_render_template :new
|
|
70
|
+
should_assign_to :data_group
|
|
71
|
+
|
|
72
|
+
should 'not create the data group' do
|
|
73
|
+
assert assigns(:data_group).new_record?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'with valid attributes' do
|
|
79
|
+
|
|
80
|
+
setup do
|
|
81
|
+
post :create, :data_group => Factory.attributes_for(:data_group)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should_respond_with :redirect
|
|
85
|
+
should_assign_to :data_group
|
|
86
|
+
should_redirect_to('census admin page') { census_admin_url }
|
|
87
|
+
|
|
88
|
+
should 'create the data group' do
|
|
89
|
+
assert !assigns(:data_group).new_record?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context 'using GET to edit' do
|
|
97
|
+
|
|
98
|
+
setup do
|
|
99
|
+
@group = Factory(:data_group)
|
|
100
|
+
get :edit, :id => @group.to_param
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
should_respond_with :success
|
|
104
|
+
should_respond_with_content_type :html
|
|
105
|
+
should_render_template :edit
|
|
106
|
+
should_assign_to(:data_group) { @group }
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'using PUT to update' do
|
|
111
|
+
|
|
112
|
+
context 'with valid attributes' do
|
|
113
|
+
|
|
114
|
+
setup do
|
|
115
|
+
@group = Factory(:data_group)
|
|
116
|
+
put :update, :id => @group.to_param, :data_group => {:name => 'CHANGED'}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
should_respond_with :redirect
|
|
120
|
+
should_assign_to(:data_group) { @group }
|
|
121
|
+
should_redirect_to('census admin page') { census_admin_url }
|
|
122
|
+
|
|
123
|
+
should 'update the data group' do
|
|
124
|
+
assert_equal('CHANGED', @group.reload.name)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context 'with invalid attributes' do
|
|
130
|
+
|
|
131
|
+
setup do
|
|
132
|
+
@group = Factory(:data_group)
|
|
133
|
+
DataGroup.any_instance.stubs(:valid? => false)
|
|
134
|
+
put :update, :id => @group.to_param, :data_group => {:name => 'CHANGED'}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
should_respond_with :unprocessable_entity
|
|
138
|
+
should_respond_with_content_type :html
|
|
139
|
+
should_assign_to(:data_group) { @group }
|
|
140
|
+
should_render_template :edit
|
|
141
|
+
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context 'using DELETE to destroy' do
|
|
147
|
+
|
|
148
|
+
setup do
|
|
149
|
+
@group = Factory(:data_group)
|
|
150
|
+
delete :destroy, :id => @group.to_param
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
should_respond_with :redirect
|
|
154
|
+
should_assign_to(:data_group) { @group }
|
|
155
|
+
should_redirect_to('census admin page') { census_admin_url }
|
|
156
|
+
|
|
157
|
+
should 'destroy the data group' do
|
|
158
|
+
assert_nil(DataGroup.find_by_id(@group.id))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class AnswerTest < ActiveSupport::TestCase
|
|
4
|
+
|
|
5
|
+
context "An Answer" do
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
@answer = Factory(:answer)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
subject { @answer }
|
|
12
|
+
|
|
13
|
+
should_belong_to :question
|
|
14
|
+
should_belong_to :user
|
|
15
|
+
|
|
16
|
+
should_validate_presence_of :question,
|
|
17
|
+
:user
|
|
18
|
+
|
|
19
|
+
should_allow_mass_assignment_of :data
|
|
20
|
+
|
|
21
|
+
context "getting formatted data" do
|
|
22
|
+
|
|
23
|
+
should "format strings" do
|
|
24
|
+
a = Factory(:answer, :question => Factory(:question, :data_type => 'String'), :data => 'abc123')
|
|
25
|
+
assert_equal 'abc123', a.formatted_data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
should "format numbers" do
|
|
29
|
+
a = Factory(:answer, :question => Factory(:question, :data_type => 'Number'), :data => '5389')
|
|
30
|
+
assert_equal 5389, a.formatted_data
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
should "format booleans" do
|
|
34
|
+
a = Factory(:answer, :question => Factory(:question, :data_type => 'Yes/No'), :data => '0')
|
|
35
|
+
assert_equal false, a.formatted_data
|
|
36
|
+
a.data = '1'
|
|
37
|
+
assert_equal true, a.formatted_data
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ChoiceTest < ActiveSupport::TestCase
|
|
4
|
+
|
|
5
|
+
context "A Choice" do
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
@choice = Factory(:choice)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
subject { @choice }
|
|
12
|
+
|
|
13
|
+
should_belong_to :question
|
|
14
|
+
|
|
15
|
+
should_act_as_list
|
|
16
|
+
|
|
17
|
+
should_validate_presence_of :value,
|
|
18
|
+
:question
|
|
19
|
+
|
|
20
|
+
should_allow_mass_assignment_of :value
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class QuestionTest < ActiveSupport::TestCase
|
|
4
|
+
|
|
5
|
+
context "A Question" do
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
@question = Factory(:question)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
subject { @question }
|
|
12
|
+
|
|
13
|
+
should_belong_to :data_group
|
|
14
|
+
should_have_many :choices, :dependent => :destroy
|
|
15
|
+
should_have_many :answers, :dependent => :destroy
|
|
16
|
+
|
|
17
|
+
should_act_as_list
|
|
18
|
+
|
|
19
|
+
should_validate_presence_of :prompt,
|
|
20
|
+
:data_group
|
|
21
|
+
|
|
22
|
+
should_allow_mass_assignment_of :data_type,
|
|
23
|
+
:prompt,
|
|
24
|
+
:multiple,
|
|
25
|
+
:other
|
|
26
|
+
|
|
27
|
+
should_accept_nested_attributes_for :choices
|
|
28
|
+
|
|
29
|
+
should "return a default sql transform" do
|
|
30
|
+
assert '"?"', @question.sql_transform
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "with choices" do
|
|
34
|
+
|
|
35
|
+
setup do
|
|
36
|
+
@choice1 = Factory(:choice, :value => 'Choice 1', :question => @question)
|
|
37
|
+
@choice2 = Factory(:choice, :value => 'Choice 2', :question => @question)
|
|
38
|
+
@choice3 = Factory(:choice, :value => 'Choice 3', :question => @question)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
should "allow answers that match the choices" do
|
|
42
|
+
assert Factory.build(:answer, :question => @question, :data => 'Choice 1').valid?
|
|
43
|
+
assert Factory.build(:answer, :question => @question, :data => 'Choice 3').valid?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
should "not allow answers that don't match the choices" do
|
|
47
|
+
assert !Factory.build(:answer, :question => @question, :data => 'Blah').valid?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "that allows user-supplied 'other' answer" do
|
|
51
|
+
|
|
52
|
+
setup do
|
|
53
|
+
@question.update_attribute(:other, true)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
should "allow answers that don't match the choices" do
|
|
57
|
+
assert Factory.build(:answer, :question => @question, :data => 'Blah').valid?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context "that can't have multiple answers" do
|
|
65
|
+
|
|
66
|
+
setup do
|
|
67
|
+
@question.update_attribute(:multiple, false)
|
|
68
|
+
@user = Factory(:user)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
should "not be able to create multiple answers" do
|
|
72
|
+
assert @question.answers.create(:data => 'Answer 1', :user => @user)
|
|
73
|
+
assert !Factory.build(:answer, :question => @question, :data => 'Answer 2', :user => @user).valid?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context "that can have multiple answers" do
|
|
79
|
+
|
|
80
|
+
setup do
|
|
81
|
+
@question.update_attribute(:multiple, true)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should "be able to create multiple answers" do
|
|
85
|
+
assert @question.answers.create(:data => 'Answer 1')
|
|
86
|
+
assert @question.answers.create(:data => 'Answer 2')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context "with String data type" do
|
|
92
|
+
|
|
93
|
+
setup do
|
|
94
|
+
@question.data_type = 'String'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
should "return a default sql transform" do
|
|
98
|
+
assert_equal "?", @question.sql_transform
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "with answers" do
|
|
102
|
+
|
|
103
|
+
setup do
|
|
104
|
+
@answer1 = Factory(:answer, :question => @question, :data => 'findme')
|
|
105
|
+
@answer2 = Factory(:answer, :question => @question, :data => 'dont_findme')
|
|
106
|
+
@answer3 = Factory(:answer, :question => @question, :data => 'findme_too')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
should "find answers matching a given string" do
|
|
110
|
+
assert @question.find_answers_matching('findme').include?(@answer1)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
should "not find answers not matching the given string" do
|
|
114
|
+
assert !@question.find_answers_matching('findme').include?(@answer2)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
should "find answers matching an array of strings" do
|
|
118
|
+
result = @question.find_answers_matching(['findme', 'findme_too'])
|
|
119
|
+
assert result.include?(@answer1)
|
|
120
|
+
assert result.include?(@answer3)
|
|
121
|
+
assert !result.include?(@answer2)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "with Number data type" do
|
|
129
|
+
|
|
130
|
+
setup do
|
|
131
|
+
@question.data_type = 'Number'
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
should "return an integer sql transform" do
|
|
135
|
+
assert_equal "CAST(? AS SIGNED INTEGER)", @question.sql_transform
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context "with answers" do
|
|
139
|
+
|
|
140
|
+
setup do
|
|
141
|
+
@answer1 = Factory(:answer, :question => @question, :data => '123')
|
|
142
|
+
@answer2 = Factory(:answer, :question => @question, :data => '125')
|
|
143
|
+
@answer3 = Factory(:answer, :question => @question, :data => '127')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
should "find answers matching a given string" do
|
|
147
|
+
assert @question.find_answers_matching('123').include?(@answer1)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
should "not find answers not matching the given string" do
|
|
151
|
+
assert !@question.find_answers_matching('123').include?(@answer2)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
should "find answers matching a given number" do
|
|
155
|
+
assert @question.find_answers_matching(123).include?(@answer1)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
should "not find answers not matching the given number" do
|
|
159
|
+
assert !@question.find_answers_matching(123).include?(@answer2)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
should "find answers in a given range" do
|
|
163
|
+
result = @question.find_answers_matching(123..126)
|
|
164
|
+
assert result.include?(@answer1)
|
|
165
|
+
assert result.include?(@answer2)
|
|
166
|
+
assert !result.include?(@answer3)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context "with Yes/No data type" do
|
|
174
|
+
|
|
175
|
+
setup do
|
|
176
|
+
@question.data_type = 'Yes/No'
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
should "return a boolean sql transform" do
|
|
180
|
+
assert_equal "CAST(? AS CHAR)", @question.sql_transform
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
context "with answers" do
|
|
184
|
+
|
|
185
|
+
setup do
|
|
186
|
+
@answer1 = Factory(:answer, :question => @question)
|
|
187
|
+
@answer1.update_attribute(:data, false)
|
|
188
|
+
@answer2 = Factory(:answer, :question => @question, :data => true)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
should "find answers matching true" do
|
|
192
|
+
assert @question.find_answers_matching(true).include?(@answer2)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
should "not find answers not matching true" do
|
|
196
|
+
assert !@question.find_answers_matching(true).include?(@answer1)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
should "find answers matching false" do
|
|
200
|
+
assert @question.find_answers_matching(false).include?(@answer1)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
should "not find answers not matching false" do
|
|
204
|
+
assert !@question.find_answers_matching(false).include?(@answer2)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
end
|