surveyor 0.22.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +83 -0
  4. data/Gemfile.rails_version +7 -1
  5. data/README.md +114 -16
  6. data/Rakefile +15 -9
  7. data/app/inputs/quiet_input.rb +5 -0
  8. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  9. data/app/inputs/surveyor_radio_input.rb +18 -0
  10. data/app/views/partials/_answer.html.haml +4 -4
  11. data/app/views/partials/_question.html.haml +7 -7
  12. data/app/views/partials/_question_group.html.haml +9 -8
  13. data/app/views/surveyor/export.json.rabl +28 -25
  14. data/app/views/surveyor/new.html.haml +8 -5
  15. data/app/views/surveyor/show.json.rabl +3 -2
  16. data/ci-exec.sh +13 -7
  17. data/cucumber.yml +3 -3
  18. data/doc/REPRESENTATIONS.md +34 -0
  19. data/doc/api_id_schema.json +7 -0
  20. data/doc/response_set_schema.json +54 -0
  21. data/doc/surveyor question combinations.png +0 -0
  22. data/doc/surveyor_timestamp_schema.json +9 -0
  23. data/features/ajax_submissions.feature +140 -0
  24. data/features/export_to_json.feature +182 -34
  25. data/features/no_duplicates.feature +110 -0
  26. data/features/show_survey.feature +1 -1
  27. data/features/step_definitions/parser_steps.rb +25 -2
  28. data/features/step_definitions/surveyor_steps.rb +145 -20
  29. data/features/step_definitions/ui_steps.rb +3 -0
  30. data/features/support/database_cleaner.rb +16 -0
  31. data/features/support/env.rb +21 -17
  32. data/features/support/simultaneous_ajax.rb +101 -0
  33. data/features/support/single_quit_selenium_driver.rb +23 -0
  34. data/features/support/slow_updates.rb +18 -0
  35. data/features/surveyor.feature +174 -44
  36. data/features/surveyor_dependencies.feature +80 -39
  37. data/features/surveyor_parser.feature +114 -20
  38. data/features/z_redcap_parser.feature +0 -1
  39. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/next.gif +0 -0
  40. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/prev.gif +0 -0
  41. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/lib/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ef8c08_256x240.png +0 -0
  48. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ffffff_256x240.png +0 -0
  49. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui-timepicker-addon.js +23 -23
  50. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui.js +125 -125
  51. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  52. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.surveyor.js +52 -57
  53. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.tools.min.js +7 -7
  54. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/dateinput.css +13 -13
  55. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui-timepicker-addon.css +0 -0
  56. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui.custom.css +17 -17
  57. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/reset.css +1 -1
  58. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/results.css +0 -0
  59. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  60. data/lib/{generators/surveyor/templates/public/stylesheets/sass → assets/stylesheets}/surveyor.sass +15 -7
  61. data/lib/generators/surveyor/custom_generator.rb +3 -2
  62. data/lib/generators/surveyor/install_generator.rb +59 -17
  63. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +5 -0
  64. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  65. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +2 -1
  66. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +1 -0
  67. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +1 -0
  68. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +1 -0
  69. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +1 -0
  70. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +1 -0
  71. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +1 -0
  72. data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +1 -0
  73. data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +1 -0
  74. data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +1 -0
  75. data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +1 -0
  76. data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +1 -0
  77. data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +1 -0
  78. data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +1 -0
  79. data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
  80. data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +3 -2
  81. data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
  82. data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
  83. data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +6 -5
  84. data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +2 -1
  85. data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +2 -1
  86. data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +5 -4
  87. data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +4 -3
  88. data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +1 -0
  89. data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +10 -9
  90. data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +5 -4
  91. data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +4 -3
  92. data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +5 -4
  93. data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +3 -2
  94. data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
  95. data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
  96. data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
  97. data/lib/generators/surveyor/templates/surveys/date_survey.rb +1 -0
  98. data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -24
  99. data/lib/generators/surveyor/templates/surveys/quiz.rb +1 -0
  100. data/lib/generators/surveyor/templates/{public/stylesheets/sass → vendor/assets/stylesheets}/custom.sass +1 -1
  101. data/lib/surveyor/common.rb +16 -31
  102. data/lib/surveyor/engine.rb +2 -4
  103. data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
  104. data/lib/surveyor/helpers/formtastic_custom_input.rb +17 -0
  105. data/lib/surveyor/helpers/surveyor_helper_methods.rb +10 -8
  106. data/lib/surveyor/models/answer_methods.rb +3 -0
  107. data/lib/surveyor/models/dependency_condition_methods.rb +27 -28
  108. data/lib/surveyor/models/dependency_methods.rb +3 -0
  109. data/lib/surveyor/models/question_group_methods.rb +3 -0
  110. data/lib/surveyor/models/question_methods.rb +10 -7
  111. data/lib/surveyor/models/response_methods.rb +16 -0
  112. data/lib/surveyor/models/response_set_methods.rb +71 -64
  113. data/lib/surveyor/models/survey_methods.rb +19 -28
  114. data/lib/surveyor/models/survey_section_methods.rb +3 -0
  115. data/lib/surveyor/models/validation_condition_methods.rb +4 -2
  116. data/lib/surveyor/models/validation_methods.rb +3 -0
  117. data/lib/surveyor/parser.rb +198 -148
  118. data/lib/surveyor/redcap_parser.rb +120 -80
  119. data/lib/surveyor/surveyor_controller_methods.rb +86 -37
  120. data/lib/surveyor/version.rb +2 -2
  121. data/lib/surveyor.rb +5 -6
  122. data/lib/tasks/surveyor_tasks.rake +19 -7
  123. data/spec/controllers/surveyor_controller_spec.rb +166 -92
  124. data/spec/factories.rb +33 -32
  125. data/spec/helpers/formtastic_custom_input_spec.rb +16 -0
  126. data/spec/lib/common_spec.rb +0 -39
  127. data/spec/lib/redcap_parser_spec.rb +24 -24
  128. data/spec/models/answer_spec.rb +12 -0
  129. data/spec/models/dependency_condition_spec.rb +279 -323
  130. data/spec/models/dependency_spec.rb +10 -0
  131. data/spec/models/question_group_spec.rb +12 -0
  132. data/spec/models/question_spec.rb +12 -0
  133. data/spec/models/response_set_spec.rb +189 -139
  134. data/spec/models/response_spec.rb +60 -0
  135. data/spec/models/survey_section_spec.rb +9 -0
  136. data/spec/models/survey_spec.rb +72 -9
  137. data/spec/models/validation_condition_spec.rb +9 -1
  138. data/spec/models/validation_spec.rb +10 -0
  139. data/spec/spec_helper.rb +25 -6
  140. data/surveyor.gemspec +5 -4
  141. metadata +332 -291
  142. data/features/step_definitions/common_steps.rb +0 -3
  143. data/lib/formtastic/surveyor_builder.rb +0 -82
  144. data/lib/generators/surveyor/templates/public/javascripts/surveyor/jquery.blockUI.js +0 -499
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  class CreateSurveys < ActiveRecord::Migration
2
3
  def self.up
3
4
  create_table :surveys do |t|
@@ -15,12 +16,12 @@ class CreateSurveys < ActiveRecord::Migration
15
16
  # Expiry
16
17
  t.datetime :active_at
17
18
  t.datetime :inactive_at
18
-
19
+
19
20
  # Display
20
21
  t.string :css_url
21
-
22
+
22
23
  t.string :custom_class
23
-
24
+
24
25
  t.timestamps
25
26
  end
26
27
  end
@@ -1,13 +1,14 @@
1
+ # encoding: UTF-8
1
2
  class CreateValidationConditions < ActiveRecord::Migration
2
3
  def self.up
3
4
  create_table :validation_conditions do |t|
4
5
  # Context
5
6
  t.integer :validation_id
6
7
  t.string :rule_key
7
-
8
- # Conditional
8
+
9
+ # Conditional
9
10
  t.string :operator
10
-
11
+
11
12
  # Optional external reference
12
13
  t.integer :question_id
13
14
  t.integer :answer_id
@@ -21,7 +22,7 @@ class CreateValidationConditions < ActiveRecord::Migration
21
22
  t.string :string_value
22
23
  t.string :response_other
23
24
  t.string :regexp
24
-
25
+
25
26
  t.timestamps
26
27
  end
27
28
  end
@@ -1,15 +1,16 @@
1
+ # encoding: UTF-8
1
2
  class CreateValidations < ActiveRecord::Migration
2
3
  def self.up
3
4
  create_table :validations do |t|
4
5
  # Context
5
6
  t.integer :answer_id # the answer to validate
6
-
7
+
7
8
  # Conditional
8
9
  t.string :rule
9
10
 
10
11
  # Message
11
12
  t.string :message
12
-
13
+
13
14
  t.timestamps
14
15
  end
15
16
  end
@@ -0,0 +1,10 @@
1
+ # encoding: UTF-8
2
+ class DropUniqueIndexOnAccessCodeInSurveys < ActiveRecord::Migration
3
+ def self.up
4
+ remove_index( :surveys, :name => 'surveys_ac_idx' )
5
+ end
6
+
7
+ def self.down
8
+ add_index(:surveys, :access_code, :name => 'surveys_ac_idx')
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ class Survey < ActiveRecord::Base; end
3
+ class Question < ActiveRecord::Base; end
4
+ class QuestionGroup < ActiveRecord::Base; end
5
+ class Answer < ActiveRecord::Base; end
6
+ class Response < ActiveRecord::Base; end
7
+ class ResponseSet < ActiveRecord::Base; end
8
+
9
+ class UpdateBlankApiIdsOnQuestionGroup < ActiveRecord::Migration
10
+ def self.up
11
+ check = [Survey, Question, QuestionGroup, Answer, Response, ResponseSet]
12
+ check.each do |clazz|
13
+ clazz.where('api_id IS ?', nil).each do |c|
14
+ c.api_id = Surveyor::Common.generate_api_id
15
+ c.save!
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.down
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+ class Survey < ActiveRecord::Base; end
3
+ class UpdateBlankVersionsOnSurveys < ActiveRecord::Migration
4
+ def self.up
5
+ Survey.where('survey_version IS ?', nil).each do |s|
6
+ s.survey_version = 0
7
+ s.save!
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ end
13
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  survey "Date Survey" do
2
3
 
3
4
  section "Simple date questions" do
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  survey "Kitchen Sink survey" do
2
3
 
3
4
  section "Basic questions" do
@@ -21,7 +22,7 @@ survey "Kitchen Sink survey" do
21
22
  a_4 "yellow"
22
23
  a :omit
23
24
 
24
- # A dependent question, with conditions and rule to logically join them
25
+ # A dependent question, with conditions and rule to logically join them
25
26
  # the question's reference identifier is "2a", and the answer's reference_identifier is "1"
26
27
  # question reference identifiers used in conditions need to be unique on a survey for the lookups to work
27
28
  q_2a "Please explain why you don't like this color?"
@@ -32,7 +33,7 @@ survey "Kitchen Sink survey" do
32
33
  condition_C :q_2, "==", :a_3
33
34
  condition_D :q_2, "==", :a_4
34
35
 
35
- # A dependant question demonstrating the count operator. The
36
+ # A dependant question demonstrating the count operator. The
36
37
  # dependency condition checks the answer count for the referenced question.
37
38
  # It understands conditions of the form count> count< count>= count<=
38
39
  # count!=
@@ -44,23 +45,41 @@ survey "Kitchen Sink survey" do
44
45
  # When :pick isn't specified, the default is :none (no checkbox or radio button)
45
46
  q_montypython3 "What... is your name? (e.g. It is 'Arthur', King of the Britons)"
46
47
  a_1 :string
47
-
48
+
48
49
  # Dependency conditions can refer to any value, not just answer_id. An answer_reference still needs to be specified, to know which answer you would like to check
49
50
  q_montypython4 "What... is your quest? (e.g. To seek the Holy Grail)"
50
51
  a_1 :string
51
52
  dependency :rule => "A"
52
53
  condition_A :q_montypython3, "==", {:string_value => "It is 'Arthur', King of the Britons", :answer_reference => "1"}
53
-
54
+
54
55
  # http://www.imdb.com/title/tt0071853/quotes
55
56
  q_montypython5 "What... is the air-speed velocity of an unladen swallow? (e.g. What do you mean? An African or European swallow?)"
56
57
  a_1 :string
57
58
  dependency :rule => "A"
58
59
  condition_A :q_montypython4, "==", {:string_value => "To seek the Holy Grail", :answer_reference => "1"}
59
-
60
+
60
61
  label "Huh? I-- I don't know that! Auuuuuuuugh!"
61
62
  dependency :rule => "A"
62
63
  condition_A :q_montypython5, "==", {:string_value => "What do you mean? An African or European swallow?", :answer_reference => "1"}
63
-
64
+
65
+ q_cooling_1 "How do you cool your home?", :pick => :one
66
+ a_1 "Fans"
67
+ a_2 "Window AC"
68
+ a_3 "Central AC"
69
+ a_4 "Passive"
70
+
71
+ # When using !=, also use count>0 if you want to make sure the user responds
72
+ q_cooling_2 "How much does it cost to run your non-passive cooling solutions? (This question hidden until you respond 'How do you cool your home?')"
73
+ dependency :rule => "A and B"
74
+ condition_A :q_cooling_1, "!=", :a_4
75
+ condition_B :q_cooling_1, "count>0"
76
+ a_1 "$", :float
77
+
78
+ # Using != alone means the dependent question is shown by default
79
+ label "Please consider passive cooling solutions (This question always shown, unless you respond 'Passive')"
80
+ dependency :rule => "A"
81
+ condition_A :q_cooling_1, "!=", :a_4
82
+
64
83
  # Surveys, sections, questions, groups, and answers all take the following reference arguments
65
84
  # :reference_identifier # usually from paper
66
85
  # :data_export_identifier # data export
@@ -70,39 +89,39 @@ survey "Kitchen Sink survey" do
70
89
  a "who", :string
71
90
  a "what", :string
72
91
  a "where", :string
73
-
92
+
74
93
  # Various types of responses can be accepted, and validated
75
94
  q "How many pets do you own?"
76
95
  a :integer
77
96
  validation :rule => "A"
78
97
  condition_A ">=", :integer_value => 0
79
-
98
+
80
99
  # Surveys, sections, questions, groups, and answers also take a custom css class for covenience in custom styling
81
100
  q "What is your address?", :custom_class => 'address'
82
101
  a :text, :custom_class => 'mapper'
83
102
  # validations can use regexp values
84
103
  validation :rule => "A"
85
104
  condition_A "=~", :regexp => "[0-9a-zA-z\. #]"
86
-
105
+
87
106
  # Questions, groups, and answers take a custom renderer (a partial in the application's views dir)
88
107
  # defaults are "/partials/question_group", "/partials/question", "/partials/answer", so the custom renderers should have a different name
89
108
  q "Pick your favorite date AND time" #, :custom_renderer => "/partials/custom_question"
90
109
  a :datetime
91
-
110
+
92
111
  q_time_lunch "What time do you usually take a lunch break?"
93
112
  a_1 :time
94
-
113
+
95
114
  q "When would you like to meet for dinner?"
96
115
  a :date
97
-
116
+
98
117
  # Sliders deprecate to select boxes when javascript is off
99
- # Valid Ruby code may be used to shorted repetitive tasks
118
+ # Valid Ruby code may be used to shorten repetitive tasks
100
119
  q "Adjust the slider to reflect your level of awesomeness", :pick => :one, :display_type => :slider
101
120
  (1..10).to_a.each{|num| a num.to_s}
102
121
 
103
122
  q "How much do you like Ruby?", :pick => :one, :display_type => :slider
104
123
  ["not at all", "a little", "some", "a lot", "a ton"].each{|level| a level}
105
-
124
+
106
125
  # The "|" pipe is used to place labels before or after the input elements
107
126
  q "How much money do you want?"
108
127
  a "$|USD", :float
@@ -121,7 +140,7 @@ survey "Kitchen Sink survey" do
121
140
  q "Choose your Illinois county", :pick => :one, :display_type => :dropdown
122
141
  ["Adams","Alexander","Bond", "Boone",
123
142
  "Brown","Bureau","Calhoun","Carroll","Cass",
124
- "Champaign", "Christian", "Clark","Clay",
143
+ "Champaign", "Christian", "Clark","Clay",
125
144
  "Clinton", "Coles", "Cook", "Crawford","Cumberland","DeKalb",
126
145
  "De Witt","Douglas","DuPage","Edgar", "Edwards",
127
146
  "Effingham","Fayette", "Ford","Franklin","Fulton",
@@ -139,7 +158,7 @@ survey "Kitchen Sink survey" do
139
158
  "St. Clair","Stark", "Stephenson","Tazewell","Union",
140
159
  "Vermilion","Wabash","Warren","Washington","Wayne",
141
160
  "White","Whiteside","Will","Williamson","Winnebago","Woodford"].each{ |county| a county}
142
-
161
+
143
162
  # When an is_exclusive answer is checked, it unchecks all other options and disables them (using Javascript)
144
163
  q "Choose your favorite meats", :display_type => :inline, :pick => :any
145
164
  a "Chicken"
@@ -157,7 +176,7 @@ survey "Kitchen Sink survey" do
157
176
  end
158
177
 
159
178
  section "Complicated questions" do
160
-
179
+
161
180
  # Grids are useful for repeated questions with the same set of answers, which are specified before the questions
162
181
  grid "Tell us how often do you cover these each day" do
163
182
  a "1"
@@ -167,7 +186,7 @@ survey "Kitchen Sink survey" do
167
186
  q "Knees", :pick => :one
168
187
  q "Toes", :pick => :one
169
188
  end
170
-
189
+
171
190
  # "grid" is a shortcut for group with :display_type => :grid
172
191
  # The answers will be repeated every 10 rows, but long grids aren't recommended as they are generally tedious
173
192
  grid "Tell us how you feel today day" do
@@ -180,7 +199,7 @@ survey "Kitchen Sink survey" do
180
199
  q "sad|happy", :pick => :one
181
200
  q "limp|perky", :pick => :one
182
201
  end
183
-
202
+
184
203
  grid "For each of the car types checked, what type of options would you prefer?" do
185
204
  a "Leather seats"
186
205
  a "Shiny rims"
@@ -196,25 +215,36 @@ survey "Kitchen Sink survey" do
196
215
  q "Wagon", :pick => :any
197
216
  q "Other", :pick => :any
198
217
  end
199
-
218
+
200
219
  q "Please rank the following foods based on how much you like them"
201
220
  a "|pizza", :integer
202
221
  a "|salad", :integer
203
222
  a "|sushi", :integer
204
223
  a "|ice cream", :integer
205
224
  a "|breakfast ceral", :integer
206
-
225
+
207
226
  # :other, :string allows someone to specify both the "other" and some other information
208
227
  q "Choose your favorite utensils and enter frequency of use (daily, weekly, monthly, etc...)", :pick => :any
209
228
  a "spoon", :string
210
229
  a "fork", :string
211
230
  a "knife", :string
212
231
  a :other, :string
213
-
232
+
233
+ q "What is your birth date?", :pick => :one
234
+ a "I was born on", :date
235
+ a "Refused"
236
+
237
+ q "At what time were you born?", :pick => :any
238
+ a "I was born at", :time
239
+ a "This time is approximate"
240
+
241
+ q "When would you like to schedule your next appointment?"
242
+ a :datetime
243
+
214
244
  q_car "Do you own a car?", :pick => :one
215
245
  a_y "Yes"
216
246
  a_n "No"
217
-
247
+
218
248
  # Repeaters allow multiple responses to a question or set of questions
219
249
  repeater "Tell us about the cars you own" do
220
250
  dependency :rule => "A"
@@ -232,7 +262,7 @@ survey "Kitchen Sink survey" do
232
262
  q "Year"
233
263
  a :string
234
264
  end
235
-
265
+
236
266
  repeater "Tell us the name and age of your siblings" do
237
267
  q "Sibling"
238
268
  a "Name", :string
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  survey "Favorites" do
2
3
  section "Foods" do
3
4
  # In a quiz, both the questions and the answers need to have reference identifiers
@@ -1,5 +1,5 @@
1
1
  /* Add custom styles here. They will be
2
2
  * loaded after the surveyor stylesheets */
3
-
3
+
4
4
  #surveyor
5
5
  :font-family "Century Gothic"
@@ -1,16 +1,23 @@
1
+ require 'securerandom'
1
2
  require 'uuidtools'
2
3
 
3
4
  module Surveyor
4
5
  class Common
5
- RAND_CHARS = [('a'..'z'), ('A'..'Z'), (0..9)].map{|r| r.to_a}.flatten.join
6
6
  OPERATORS = %w(== != < > <= >= =~)
7
-
7
+
8
8
  class << self
9
- def make_tiny_code(len = 10)
10
- if RUBY_VERSION < "1.8.7"
11
- (1..len).to_a.map{|i| RAND_CHARS[rand(RAND_CHARS.size), 1] }.join
12
- else
13
- len.times.map{|i| RAND_CHARS[rand(RAND_CHARS.size), 1] }.join
9
+ if SecureRandom.respond_to?(:urlsafe_base64)
10
+ def make_tiny_code
11
+ # 7 random bytes is increased to ~10 characters (sans padding) by
12
+ # base64 encoding
13
+ SecureRandom.urlsafe_base64(7)
14
+ end
15
+ else
16
+ def make_tiny_code
17
+ s = [SecureRandom.random_bytes(7)].pack("m*")
18
+ s.delete!("\n")
19
+ s.tr!("+/", "-_")
20
+ s.delete!("=")
14
21
  end
15
22
  end
16
23
 
@@ -20,35 +27,13 @@ module Surveyor
20
27
  col_text.downcase! # Remove capitalization
21
28
  col_text.gsub!(/\"|\'/u, '') # Remove potential problem characters
22
29
  col_text.gsub!(/\(.*?\)/u,'') # Remove text inside parens
23
- col_text.gsub!(/\W/u, ' ') # Remove all other non-word characters
30
+ col_text.gsub!(/\W/u, ' ') # Remove all other non-word characters
24
31
  cols = (col_text.split(' ') - words_to_omit)
25
32
  (cols.size > 5 ? cols[-5..-1] : cols).join("_")
26
33
  end
27
-
28
- def equal_json_excluding_wildcards(a,b)
29
- return false if a.nil? or b.nil?
30
- a = a.is_a?(String) ? JSON.load(a) : JSON.load(a.to_json)
31
- b = b.is_a?(String) ? JSON.load(b) : JSON.load(b.to_json)
32
- deep_compare_excluding_wildcards(a,b)
33
- end
34
- def deep_compare_excluding_wildcards(a,b)
35
- return false if a.class != b.class
36
- if a.is_a?(Hash)
37
- return false if a.size != b.size
38
- a.each do |k,v|
39
- return false if deep_compare_excluding_wildcards(v,b[k]) == false
40
- end
41
- elsif a.is_a?(Array)
42
- return false if a.size != b.size
43
- a.each_with_index{|e,i| return false if deep_compare_excluding_wildcards(e,b[i]) == false }
44
- else
45
- return (a == "*") || (b == "*") || (a == b)
46
- end
47
- true
48
- end
49
34
 
50
35
  alias :normalize :to_normalized_string
51
-
36
+
52
37
  def generate_api_id
53
38
  UUIDTools::UUID.random_create.to_s
54
39
  end
@@ -4,9 +4,7 @@ require 'haml' # required for view resolution
4
4
 
5
5
  module Surveyor
6
6
  class Engine < Rails::Engine
7
- config.autoload_paths << File.expand_path("../../", __FILE__)
8
- # rake_tasks do
9
- # load "tasks/surveyor_tasks.rake"
10
- # end
7
+ root = File.expand_path('../../', __FILE__)
8
+ config.autoload_paths << root
11
9
  end
12
10
  end
@@ -0,0 +1,13 @@
1
+ module Surveyor
2
+ module Helpers
3
+ module AssetPipeline
4
+ ##
5
+ # Returns whether or not the asset pipeline is present and enabled.
6
+ #
7
+ # The detection scheme used here was ripped from jquery-rails.
8
+ def asset_pipeline_enabled?
9
+ ::Rails.version >= "3.1" && ::Rails.application.config.assets.enabled
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module Surveyor
2
+ module Helpers
3
+ module FormtasticCustomInput
4
+ def input_html_options_with(choice, response_class)
5
+ input_html_options.merge(choice_html_options(choice)).merge({:id => (input_html_options[:id] || "answer_id").gsub("answer_id", response_class_to_method(response_class).to_s), :class => [input_html_options[:class], response_class].join(" ")})
6
+ end
7
+ def response_class_to_method(response_class)
8
+ # doesn't handle response_class == answer, and doesn't have to
9
+ case response_class.to_s
10
+ when /^other_and_string$/ then :string_value
11
+ when /^date|time$/ then :datetime_value
12
+ else "#{response_class}_value".to_sym
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,15 +1,17 @@
1
+ require 'surveyor/helpers/asset_pipeline'
2
+
1
3
  module Surveyor
2
4
  module Helpers
3
5
  module SurveyorHelperMethods
6
+ include AssetPipeline
7
+
4
8
  # Layout: stylsheets and javascripts
5
9
  def surveyor_includes
6
- surveyor_stylsheets + surveyor_javascripts
7
- end
8
- def surveyor_stylsheets
9
- stylesheet_link_tag 'surveyor/reset', 'surveyor/dateinput', 'surveyor/jquery-ui.custom', 'surveyor/jquery-ui-timepicker-addon', 'surveyor', 'custom'
10
- end
11
- def surveyor_javascripts
12
- javascript_include_tag 'surveyor/jquery.tools.min', 'surveyor/jquery-ui', 'surveyor/jquery-ui-timepicker-addon', 'surveyor/jquery.surveyor', 'surveyor/jquery.blockUI'
10
+ if asset_pipeline_enabled?
11
+ stylesheet_link_tag('surveyor_all') + javascript_include_tag('surveyor_all')
12
+ else
13
+ stylesheet_link_tag('surveyor/reset', 'surveyor/dateinput', 'surveyor/jquery-ui.custom', 'surveyor/jquery-ui-timepicker-addon', 'surveyor', 'custom') + javascript_include_tag('surveyor/jquery.tools.min', 'surveyor/jquery-ui', 'surveyor/jquery-ui-timepicker-addon', 'surveyor/jquery.surveyor')
14
+ end
13
15
  end
14
16
  # Helper for displaying warning/notice/error flash messages
15
17
  def flash_messages(types)
@@ -96,4 +98,4 @@ module Surveyor
96
98
  end
97
99
  end
98
100
  end
99
- end
101
+ end
@@ -20,6 +20,9 @@ module Surveyor
20
20
  # base.send :validates_numericality_of, :question_id, :allow_nil => false, :only_integer => true
21
21
  @@validations_already_included = true
22
22
  end
23
+
24
+ # Whitelisting attributes
25
+ base.send :attr_accessible, :question, :question_id, :text, :short_text, :help_text, :weight, :response_class, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_order, :is_exclusive, :display_length, :custom_class, :custom_renderer, :default_value, :display_type
23
26
  end
24
27
 
25
28
  include RenderText
@@ -7,7 +7,7 @@ module Surveyor
7
7
  base.send :belongs_to, :dependency
8
8
  base.send :belongs_to, :dependent_question, :foreign_key => :question_id, :class_name => :question
9
9
  base.send :belongs_to, :question
10
-
10
+
11
11
  @@validations_already_included ||= nil
12
12
  unless @@validations_already_included
13
13
  # Validations
@@ -16,12 +16,15 @@ module Surveyor
16
16
  base.send :validates_uniqueness_of, :rule_key, :scope => :dependency_id
17
17
  # this causes issues with building and saving
18
18
  # base.send :validates_numericality_of, :question_id, :dependency_id
19
-
19
+
20
20
  @@validations_already_included = true
21
21
  end
22
-
22
+
23
23
  base.send :include, Surveyor::ActsAsResponse # includes "as" instance method
24
24
 
25
+ # Whitelisting attributes
26
+ base.send :attr_accessible, :dependency, :question, :answer, :dependency_id, :rule_key, :question_id, :operator, :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other
27
+
25
28
  # Class methods
26
29
  base.instance_eval do
27
30
  def operators
@@ -34,33 +37,29 @@ module Surveyor
34
37
  def to_hash(response_set)
35
38
  # all responses to associated question
36
39
  responses = question.blank? ? [] : response_set.responses.where("responses.answer_id in (?)", question.answer_ids).all
37
- {rule_key.to_sym => (!responses.empty? and self.is_met?(responses))}
38
- end
39
-
40
- # Checks to see if the responses passed in meet the dependency condition
41
- def is_met?(responses)
42
- # response to associated answer if available, or first response
43
- response = if self.answer_id
44
- responses.detect do |r|
45
- r.answer == self.answer
46
- end
47
- end || responses.first
48
- klass = response.answer.response_class
49
- klass = "answer" if self.as(klass).nil?
50
- return case self.operator
51
- when "==", "<", ">", "<=", ">="
52
- response.as(klass).send(self.operator, self.as(klass))
53
- when "!="
54
- !(response.as(klass) == self.as(klass))
55
- when /^count[<>=]{1,2}\d+$/
56
- op, i = self.operator.scan(/^count([<>!=]{1,2})(\d+)$/).flatten
57
- responses.count.send(op, i.to_i)
58
- when /^count!=\d+$/
59
- !(responses.count == self.operator.scan(/\d+/).first.to_i)
60
- else
61
- false
40
+ if self.operator.match /^count(>|>=|<|<=|=|!=)\d+$/
41
+ op, i = self.operator.scan(/^count(>|>=|<|<=|=|!=)(\d+)$/).flatten
42
+ # logger.warn({rule_key.to_sym => responses.count.send(op, i.to_i)})
43
+ return {rule_key.to_sym => (op == "!=" ? !responses.count.send("==", i.to_i) : responses.count.send(op, i.to_i))}
44
+ elsif operator == "!=" and (responses.blank? or responses.none?{|r| r.answer.id == self.answer.id})
45
+ # logger.warn( {rule_key.to_sym => true})
46
+ return {rule_key.to_sym => true}
47
+ elsif response = responses.detect{|r| r.answer.id == self.answer.id}
48
+ klass = response.answer.response_class
49
+ klass = "answer" if self.as(klass).nil?
50
+ case self.operator
51
+ when "==", "<", ">", "<=", ">="
52
+ # logger.warn( {rule_key.to_sym => response.as(klass).send(self.operator, self.as(klass))})
53
+ return {rule_key.to_sym => response.as(klass).send(self.operator, self.as(klass))}
54
+ when "!="
55
+ # logger.warn( {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))})
56
+ return {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))}
57
+ end
62
58
  end
59
+ # logger.warn({rule_key.to_sym => false})
60
+ {rule_key.to_sym => false}
63
61
  end
62
+
64
63
  protected
65
64
 
66
65
  def validates_operator
@@ -18,6 +18,9 @@ module Surveyor
18
18
  @@validations_already_included = true
19
19
  end
20
20
 
21
+ # Whitelisting attributes
22
+ base.send :attr_accessible, :question, :question_group, :question_id, :question_group_id, :rule
23
+
21
24
  # Attribute aliases
22
25
  base.send :alias_attribute, :dependent_question_id, :question_id
23
26
  end
@@ -7,6 +7,9 @@ module Surveyor
7
7
  # Associations
8
8
  base.send :has_many, :questions
9
9
  base.send :has_one, :dependency
10
+
11
+ # Whitelisting attributes
12
+ base.send :attr_accessible, :text, :help_text, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_type, :custom_class, :custom_renderer
10
13
  end
11
14
 
12
15
  include RenderText
@@ -9,11 +9,11 @@ module Surveyor
9
9
  base.send :belongs_to, :question_group, :dependent => :destroy
10
10
  base.send :has_many, :answers, :order => "display_order ASC", :dependent => :destroy # it might not always have answers
11
11
  base.send :has_one, :dependency, :dependent => :destroy
12
- base.send :has_one, :correct_answer, :class_name => "Answer", :dependent => :destroy
12
+ base.send :belongs_to, :correct_answer, :class_name => "Answer", :dependent => :destroy
13
13
 
14
14
  # Scopes
15
15
  base.send :default_scope, :order => "display_order ASC"
16
-
16
+
17
17
  @@validations_already_included ||= nil
18
18
  unless @@validations_already_included
19
19
  # Validations
@@ -21,10 +21,13 @@ module Surveyor
21
21
  # this causes issues with building and saving
22
22
  #, :survey_section_id
23
23
  base.send :validates_inclusion_of, :is_mandatory, :in => [true, false]
24
-
24
+
25
25
  @@validations_already_included = true
26
-
26
+
27
27
  end
28
+
29
+ # Whitelisting attributes
30
+ base.send :attr_accessible, :survey_section, :question_group, :survey_section_id, :question_group_id, :text, :short_text, :help_text, :pick, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_order, :display_type, :is_mandatory, :display_width, :custom_class, :custom_renderer, :correct_answer_id
28
31
  end
29
32
 
30
33
  include RenderText
@@ -43,7 +46,7 @@ module Surveyor
43
46
  self.short_text ||= text
44
47
  self.api_id ||= Surveyor::Common.generate_api_id
45
48
  end
46
-
49
+
47
50
  def pick=(val)
48
51
  write_attribute(:pick, val.nil? ? nil : val.to_s)
49
52
  end
@@ -71,11 +74,11 @@ module Surveyor
71
74
  def solo?
72
75
  self.question_group.nil?
73
76
  end
74
-
77
+
75
78
  def split_text(part = nil)
76
79
  (part == :pre ? text.split("|",2)[0] : (part == :post ? text.split("|",2)[1] : text)).to_s
77
80
  end
78
-
81
+
79
82
  def renderer(g = question_group)
80
83
  r = [g ? g.renderer.to_s : nil, display_type].compact.join("_")
81
84
  r.blank? ? :default : r.to_sym