surveyor 0.22.0 → 1.0.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.
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
data/.gitignore CHANGED
@@ -19,6 +19,7 @@ rdoc
19
19
  pkg
20
20
  Gemfile.lock
21
21
  reports
22
+ chromedriver.log
22
23
 
23
24
  ## PROJECT::SPECIFIC
24
25
  testbed
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color -fn
data/CHANGELOG.md ADDED
@@ -0,0 +1,83 @@
1
+ History for Surveyor
2
+ ====================
3
+
4
+ 1.0.0
5
+ ------
6
+
7
+ ### Features
8
+
9
+ - Official support for Rails 3.2, including declaring of all mass-assignable
10
+ attributes with `attr_accessible`.
11
+
12
+ - Breaking change: Surveys are now explicitly versioned. If you loaded a survey
13
+ when another survey with the same title/access code had already been loaded,
14
+ Surveyor would previously have appended a serial number to the title. As of
15
+ this version, Surveyor keeps the serial number in a separate `survey_version`
16
+ field. (#262)
17
+
18
+ - Add encoding comments for generated files. (#329)
19
+
20
+ - Asset pipeline support for Rails 3.1+. Rails 3.0 is still supported.
21
+ (#307, #314, #315)
22
+
23
+ - Upgrade to Formtastic 2.1. (#227)
24
+
25
+ - `:pick => :one` and `:pick => :any` questions may now have date, time,
26
+ datetime, integer, or float values, in addition to the already-supported
27
+ string values. (#207)
28
+
29
+ - Added Survey#as_json, ResponseSet#as_json. (#291)
30
+
31
+ - Changed defaults for and interpretation of for Survey#active_at and
32
+ Survey#inactive_at. (#258)
33
+
34
+ - JSON export representation of DateTimes is 2010-04-08T10:30+00:00 and Dates is
35
+ 2010-04-08 and Times is 10:30. (#355)
36
+
37
+ - JSON representation of Response includes response_group. (#349)
38
+
39
+ - Use Object#extend to inject SuryeyorParser and SurveyorRedcapParser methods into
40
+ instances of models instead of reopening classes. Move responsibility for keeping
41
+ track of and reporting duplicate and bad references from the models to the parsers.
42
+ Upgrade SurveyorRedcapParser to trace only when rake --trace is specified. (#341)
43
+
44
+ - export Question#data_export_identifier, Answer#data_export_identifier,
45
+ Answer#reference_identifier in survey JSON export. (#368)
46
+
47
+ ### Fixes
48
+
49
+ - Ensure response set is available to `render_context` (#320)
50
+
51
+ - Hide dependent rows of grids. (#343)
52
+
53
+ - Dependency condition with '!=' now returns true if there is no response. (#337)
54
+
55
+ - Properly handle multiple "exclusive" checkboxes in a single question. (#336)
56
+
57
+ - Correct storing of "correct" answers when parsing a survey. (#326)
58
+
59
+ - Restore "slider" renderer. (#230)
60
+
61
+ - Ensure that duplicate responses cannot be created by parallel AJAX requests.
62
+ (#328)
63
+
64
+ - Create default identifiers in before_create, not in the initializer. (#369)
65
+
66
+ - Eliminate unnecessary (and incorrect) access code uniqueness checks.
67
+ Use SecureRandom for generating access codes. (#370)
68
+
69
+ - Use json_spec for testing JSON responses, instead of
70
+ Surveyor::Common#deep_compare_excluding_wildcards. (#350)
71
+
72
+ - Parser now sets Question#correct_answer_id correctly. The association is changed from
73
+ Question :has_one correct_answer => Question :belongs_to correct_answer. (#365)
74
+
75
+ ### Infrastructure
76
+
77
+ - Enabled Selenium-backed cucumber features in CI. (#333)
78
+
79
+ - Added `testbed:surveys` task to load all sample surveys in the testbed.
80
+
81
+ - Begin formal changelog.
82
+
83
+ - Change test infra so that tx behavior can be tested. (#360)
@@ -4,9 +4,15 @@
4
4
  if ENV['RAILS_VERSION']
5
5
  case ENV['RAILS_VERSION']
6
6
  when /3.0$/
7
- gem 'rails', '~> 3.0.10'
7
+ gem 'rails', '~> 3.0.15'
8
8
  when /3.1$/
9
9
  gem 'rails', '~> 3.1.0'
10
+ # A JS runtime is required for Rails 3.1+
11
+ gem 'therubyracer'
12
+ when /3.2$/
13
+ gem 'rails', '~> 3.2.0'
14
+ # A JS runtime is required for Rails 3.1+
15
+ gem 'therubyracer'
10
16
  else
11
17
  fail "Unknown Rails version #{ENV['RAILS_VERSION']}"
12
18
  end
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Surveys On Rails
2
2
 
3
- Surveyor is a ruby gem and developer tool that brings surveys into Rails applications. Surveys are written in the Surveyor DSL (Domain Specific Language). Before Rails 2.3, it was implemented as a Rails Engine. It also existed previously as a plugin. Today it is a gem only.
3
+ Surveyor is a developer tool that brings surveys into Rails
4
+ applications. Surveys are written in the Surveyor DSL (Domain Specific
5
+ Language). Internally, Surveyor is a Rails engine distributed as a
6
+ ruby gem, meaning it is straightforward to override or extend its
7
+ behaviors in your Rails app without maintaining a fork.
4
8
 
5
9
  ## Why you might want to use Surveyor
6
10
 
@@ -55,7 +59,7 @@ The first question is "pick one" (radio buttons) with "other". The second questi
55
59
 
56
60
  # Installation
57
61
 
58
- 1. Add surveyor to your Gemfile:
62
+ Add surveyor to your Gemfile:
59
63
 
60
64
  gem "surveyor"
61
65
 
@@ -63,16 +67,16 @@ Then run:
63
67
 
64
68
  bundle install
65
69
 
66
- 2. Generate assets, run migrations:
70
+ Generate assets, run migrations:
67
71
 
68
72
  script/rails generate surveyor:install
69
- rake db:migrate
73
+ bundle exec rake db:migrate
70
74
 
71
- 3. Try out the "kitchen sink" survey. The rake task above generates surveys from our custom survey DSL (a good format for end users and stakeholders).
75
+ Try out the "kitchen sink" survey. The rake task above generates surveys from our custom survey DSL (a good format for end users and stakeholders).
72
76
 
73
- rake surveyor FILE=surveys/kitchen_sink_survey.rb
77
+ bundle exec rake surveyor FILE=surveys/kitchen_sink_survey.rb
74
78
 
75
- 4. Start up your app and visit http://localhost:3000/surveys
79
+ Start up your app and visit http://localhost:3000/surveys
76
80
 
77
81
  Try taking the survey and compare it to the contents of the DSL file kitchen\_sink\_survey.rb. See how the DSL maps to what you see.
78
82
 
@@ -82,14 +86,71 @@ There are two other useful rake tasks:
82
86
  * `rake surveyor:unparse` exports a survey from the application into a
83
87
  file in the surveyor DSL.
84
88
 
89
+ # Upgrading
90
+
91
+ When you are ready to update to the latest version of Surveyor, just do
92
+
93
+ bundle update surveyor
94
+
95
+ and you'll get the latest version that bundler thinks you can use based on the
96
+ constraints in your Gemfile.
97
+
98
+ After you've installed a new version, you should always re-run the surveyor
99
+ generator:
100
+
101
+ script/rails generate surveyor:install
102
+
103
+ This will copy over any new or updated assets and new migrations. Use your VCS'
104
+ utilities to review what's changed, then commit the new versions.
105
+
106
+ git status
107
+ git diff
108
+ [...]
109
+ git add -A
110
+ git commit
111
+
112
+ If there were new migrations, you'll want to be sure to run them before the next
113
+ time you start your app:
114
+
115
+ bundle exec rake db:migrate
116
+
117
+ Finally, review the [changelog][] entries corresponding to the versions between
118
+ your original version and the version you've updated to. There may be changes
119
+ which will affect your customizations. There should be hints in the changelog
120
+ or the referenced issues for what to update when necessary.
121
+
122
+ [changelog]: https://github.com/NUBIC/surveyor/blob/master/CHANGELOG.md
123
+
124
+ ## Following master
125
+
126
+ If you are following along with pre-release versions of Surveyor using a `:git`
127
+ source in your Gemfile, be particularly careful about reviewing migrations after
128
+ updating Surveyor and re-running the generator. We will never change a migration
129
+ between two released versions of Surveyor. However, we may on rare occasions
130
+ change a migration which has been merged into master. When this happens, you'll
131
+ need to assess the differences and decide on an appropriate course of action for
132
+ your app.
133
+
134
+ If you aren't sure what this means, we do not recommend that you deploy an app
135
+ that's locked to surveyor master into production.
136
+
85
137
  # Customizing surveyor
86
138
 
87
139
  Surveyor's controller, models, and views may be customized via classes in your app/models, app/helpers and app/controllers directories. To generate a sample custom controller and layout, run:
88
140
 
89
- `script/rails generate surveyor:custom`
141
+ script/rails generate surveyor:custom
90
142
 
91
143
  and read surveys/EXTENDING\_SURVEYOR
92
144
 
145
+ # The asset pipeline
146
+
147
+ Surveyor is now aware of the Rails asset pipeline (http://guides.rubyonrails.org/asset_pipeline.html). With the asset pipeline enabled `Rails.application.config.assets.enabled == true`, then the `surveyor:install` generator will generate `app/assets/stylesheets/surveyor_all.css` and `app/assets/javascripts/surveyor_all.js` manifest files and link them from the surveyor\_default layout. Assets remain in the gem and are picked up for inclusion and pre-compilation from there if `config/environments/production.rb` is set to include surveyor assets.
148
+
149
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
150
+ config.assets.precompile += %w( surveyor_all.js surveyor_all.css )
151
+
152
+ The previous copy-to-application behavior still exists in the case where the asset pipeline is missing or disabled.
153
+
93
154
  # PDF support
94
155
 
95
156
  * Add the following lines to your Gemfile:
@@ -120,17 +181,33 @@ or on OSX:
120
181
 
121
182
  # Requirements
122
183
 
123
- Surveyor depends on:
184
+ Surveyor works with:
185
+
186
+ * Ruby 1.8.7, 1.9.2, and 1.9.3
187
+ * Rails 3.0-3.2
188
+
189
+ Some key library dependencies are:
124
190
 
125
- * Ruby (1.8.7 - 1.9.2)
126
- * Rails 3.0-3.1
127
191
  * HAML
128
- * SASS
129
- * fastercsv (or CSV for ruby 1.9) for csv exports
130
- * formtastic
131
- * UUID
192
+ * Sass
193
+ * Formtastic
194
+
195
+ A more exhaustive list can be found in the gemspec.
196
+
197
+ # Support
198
+
199
+ For general discussion (e.g., "how do I do this?"), please send a message to the
200
+ [surveyor-dev][] group. This group is moderated to keep out spam; don't be
201
+ surprised if your message isn't posted immediately.
132
202
 
133
- Specific versions of the gem dependencies are listed in the gemspec.
203
+ For reproducible bugs, please file an issue on the [GitHub issue
204
+ tracker][issues]. Please include a minimal test case (a detailed description of
205
+ how to trigger the bug in a clean rails application). If you aren't sure how to
206
+ isolate the bug, send a message to [surveyor-dev][] with what you know and we'll
207
+ try to help.
208
+
209
+ [surveyor-dev]: https://groups.google.com/group/surveyor-dev
210
+ [issues]: https://github.com/NUBIC/surveyor/issues
134
211
 
135
212
  # Contributing, testing
136
213
 
@@ -155,4 +232,25 @@ to run the features and start writing tests!
155
232
 
156
233
  [bundler]: http://gembundler.com/
157
234
 
235
+ ## Selenium
236
+
237
+ Some of Surveyor's integration tests use Selenium WebDriver and Capybara. The
238
+ WebDriver-based tests default to running in Chrome due to an unfortunate
239
+ [Firefox bug][FF566671]. For them to run, you'll either need:
240
+
241
+ * Chrome and [chromedriver][] installed, or
242
+ * to switch to use Firefox instead
243
+
244
+ To use Firefox instead of Chrome, invoke one or more features with
245
+ `SELENIUM_BROWSER` set in the environment:
246
+
247
+ $ SELENIUM_BROWSER=firefox bundle exec rake cucumber
248
+ $ SELENIUM_BROWSER=firefox bundle exec cucumber features/ajax_submissions.feature
249
+
250
+ Note that when running features in Firefox, you must allow the WebDriver-driven
251
+ Firefox to retain focus, otherwise some tests will fail.
252
+
253
+ [FF566671]: https://bugzilla.mozilla.org/show_bug.cgi?id=566671
254
+ [chromedriver]: http://code.google.com/p/selenium/wiki/ChromeDriver
255
+
158
256
  Copyright (c) 2008-2011 Brian Chamberlain and Mark Yoon, released under the MIT license
data/Rakefile CHANGED
@@ -40,18 +40,14 @@ task :testbed => 'testbed:rebuild'
40
40
  namespace :testbed do
41
41
  desc 'Generate a minimal surveyor-using rails app'
42
42
  task :generate do
43
- sh 'bundle exec rails new testbed'
43
+ sh 'bundle exec rails new testbed --skip-bundle' # don't run bundle install until the Gemfile modifications
44
44
  chdir('testbed') do
45
- File.open('Gemfile', 'w') do |f|
46
- f.puts %q{source :rubygems}
47
- f.puts %q{plugin_root = File.expand_path('../..', __FILE__)}
48
- f.puts %q{eval(File.read File.join(plugin_root, 'Gemfile.rails_version'))}
49
- f.puts %q{gem 'sqlite3'}
50
- f.puts %q{gem 'surveyor', :path => plugin_root}
51
- end
45
+ gem_file_contents = File.read('Gemfile')
46
+ gem_file_contents.sub!(/^(gem 'rails'.*)$/, %Q{# \\1\nplugin_root = File.expand_path('../..', __FILE__)\neval(File.read File.join(plugin_root, 'Gemfile.rails_version'))\ngem 'surveyor', :path => plugin_root})
47
+ File.open('Gemfile', 'w'){|f| f.write(gem_file_contents) }
52
48
 
53
49
  Bundler.with_clean_env do
54
- sh 'bundle update'
50
+ sh 'bundle install' # run bundle install after Gemfile modifications
55
51
  end
56
52
  end
57
53
  end
@@ -72,6 +68,16 @@ namespace :testbed do
72
68
  end
73
69
 
74
70
  task :rebuild => [:remove, :generate, :migrate]
71
+
72
+ desc 'Load all the sample surveys into the testbed instance'
73
+ task :surveys do
74
+ cd('testbed') do
75
+ Dir[File.join('surveys', '*.rb')].each do |fn|
76
+ puts "Installing #{fn} into the testbed"
77
+ system("rake surveyor FILE='#{fn}'")
78
+ end
79
+ end
80
+ end
75
81
  end
76
82
 
77
83
  ###### CI
@@ -0,0 +1,5 @@
1
+ class QuietInput < Formtastic::Inputs::HiddenInput
2
+ def to_html
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ class SurveyorCheckBoxesInput < Formtastic::Inputs::CheckBoxesInput
2
+ include Surveyor::Helpers::FormtasticCustomInput
3
+ def to_html
4
+ super
5
+ end
6
+ def choice_html(choice)
7
+ output = ""
8
+ output << template.content_tag(:label,
9
+ hidden_fields? ?
10
+ check_box_with_hidden_input(choice) :
11
+ check_box_without_hidden_input(choice) <<
12
+ choice_label(choice),
13
+ label_html_options.merge(:for => choice_input_dom_id(choice), :class => nil)
14
+ )
15
+ output << builder.text_field(:response_other, input_html_options_with(choice, :response_other)) if options[:response_class] == "other_and_string"
16
+ output << builder.text_field(response_class_to_method(options[:response_class]), input_html_options_with(choice, options[:response_class])) if %w(date datetime time float integer string other_and_string).include? options[:response_class]
17
+ output << builder.text_area(:text_value, input_html_options_with(choice, :text_value)) if options[:response_class] == "text"
18
+ output.html_safe
19
+ end
20
+ def checked?(value)
21
+ selected_values.include?(value.to_s)
22
+ end
23
+ def disabled?(value)
24
+ disabled_values.include?(value) || input_html_options[:disabled] == true
25
+ end
26
+ def make_selected_values
27
+ if object.respond_to?(method)
28
+ selected_items = [object.send(method)].compact.flatten.map(&:to_s)
29
+
30
+ [*selected_items.map { |o| send_or_call_or_object(value_method, o) }].compact
31
+ else
32
+ []
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ class SurveyorRadioInput < Formtastic::Inputs::RadioInput
2
+ include Surveyor::Helpers::FormtasticCustomInput
3
+ def to_html
4
+ super
5
+ end
6
+ def choice_html(choice)
7
+ output = ""
8
+ output << template.content_tag(:label,
9
+ builder.radio_button(input_name, choice_value(choice), input_html_options.merge(choice_html_options(choice)).merge(:required => false)) <<
10
+ choice_label(choice),
11
+ label_html_options.merge(:for => choice_input_dom_id(choice), :class => nil)
12
+ )
13
+ output << builder.text_field(:response_other, input_html_options_with(choice, :response_other)) if options[:response_class] == "other_and_string"
14
+ output << builder.text_field(response_class_to_method(options[:response_class]), input_html_options_with(choice, options[:response_class])) if %w(date datetime time float integer string other_and_string).include? options[:response_class]
15
+ output << builder.text_area(:text_value, input_html_options_with(choice, :text_value)) if options[:response_class] == "text"
16
+ output.html_safe
17
+ end
18
+ end
@@ -4,9 +4,9 @@
4
4
  - i = response_idx(q.pick != "one") # argument will be false (don't increment i) if we're on radio buttons
5
5
  - disabled = defined?(disableFlag) ? disableFlag : false
6
6
  = f.semantic_fields_for i, r do |ff|
7
- = ff.quiet_input :question_id unless q.pick == "one" # don't repeat question_id if we're on radio buttons
8
- = ff.quiet_input :id unless q.pick == "one" or r.new_record?
9
- = ff.quiet_input :response_group, :value => rg if q.pick != "one" && g && g.display_type == "repeater"
7
+ = ff.input :question_id, :as => :quiet unless q.pick == "one" # don't repeat question_id if we're on radio buttons
8
+ = ff.input :api_id, :as => :quiet unless q.pick == "one"
9
+ = ff.input :response_group, :value => rg, :as => :quiet if q.pick != "one" && g && g.display_type == "repeater"
10
10
  - case q.pick
11
11
  - when "one"
12
12
  = ff.input :answer_id, :as => :surveyor_radio, :collection => [[a_text(a, nil, @render_context), a.id]], :label => false, :input_html => {:class => a.css_class, :disabled => disabled}, :response_class => a.response_class
@@ -14,7 +14,7 @@
14
14
  = ff.input :answer_id, :as => :surveyor_check_boxes, :collection => [[a_text(a, nil, @render_context), a.id]], :label => false, :input_html => {:class => a.css_class, :disabled => disabled}, :response_class => a.response_class
15
15
  - when "none"
16
16
  - if %w(date datetime time float integer string text).include? a.response_class
17
- = ff.quiet_input :answer_id, :input_html => {:class => a.css_class, :value => a.id}
17
+ = ff.input :answer_id, :as => :quiet, :input_html => {:class => a.css_class, :value => a.id}
18
18
  = ff.input rc_to_attr(a.response_class), :as => rc_to_as(a.response_class), :label => a_text(a, :pre, @render_context).blank? ? false : a_text(a, :pre, @render_context), :hint => a_text(a, :post, @render_context), :input_html => generate_pick_none_input_html(r.as(a.response_class), a.default_value, a.css_class, a.response_class, disabled)
19
19
  - else
20
20
  = a_text(a, nil, @render_context)
@@ -10,18 +10,18 @@
10
10
  - r = response_for(@response_set, q, nil, rg)
11
11
  - i = response_idx
12
12
  = f.semantic_fields_for i, r do |ff|
13
- = ff.quiet_input :question_id
14
- = ff.quiet_input :response_group, :value => rg if g && g.display_type == "repeater"
15
- = ff.quiet_input :id unless r.new_record?
16
- = ff.input :answer_id, :as => :select, :collection => q.answers.map{|a| [a.text, a.id]}, :label => q.text, :input_html => { :disabled => disabled }
13
+ = ff.input :question_id, :as => :quiet
14
+ = ff.input :response_group, :as => :quiet, :value => rg if g && g.display_type == "repeater"
15
+ = ff.input :api_id, :as => :quiet
16
+ = ff.input :answer_id, :as => :select, :collection => q.answers.map{|a| [a.text, a.id]}, :include_blank => (renderer != :slider), :label => q.text, :input_html => { :disabled => disabled }
17
17
  - else # :default, :inline, :inline_default
18
18
  - if q.pick == "one"
19
19
  - r = response_for(@response_set, q, nil, rg)
20
20
  - i = response_idx # increment the response index since the answer partial skips for q.pick == one
21
21
  = f.semantic_fields_for i, r do |ff|
22
- = ff.quiet_input :question_id
23
- = ff.quiet_input :response_group, :value => rg if g && g.display_type == "repeater"
24
- = ff.quiet_input :id unless r.new_record?
22
+ = ff.input :question_id, :as => :quiet
23
+ = ff.input :response_group, :as => :quiet, :value => rg if g && g.display_type == "repeater"
24
+ = ff.input :api_id, :as => :quiet
25
25
  - q.answers.each do |a|
26
26
  - next if (q.pick == "one" or q.pick == "any") and disabled and @response_set.responses.where( :question_id => q.id, :answer_id => a.id).empty?
27
27
  = render a.custom_renderer || '/partials/answer', :q => q, :a => a, :f => f, :rg => rg, :g => g, :disableFlag => disabled
@@ -6,7 +6,7 @@
6
6
  %li
7
7
  %table
8
8
  %col.pre
9
- - qs.first.answers.each do |a|
9
+ - qs.first.answers.each do |a|
10
10
  %col{:class => cycle("odd", "even")}
11
11
  %col.post
12
12
  %tbody
@@ -18,13 +18,14 @@
18
18
  %th &nbsp;
19
19
  - ten_questions.each_with_index do |q, i|
20
20
  %tr{:id => "q_#{q.id}", :class => "q_#{renderer} #{q.css_class(@response_set)}"}
21
- - if q.pick == "one"
22
- - r = response_for(@response_set, q, nil, g)
23
- - i = response_idx # increment the response index since the answer partial skips for q.pick == one
24
- = f.semantic_fields_for i, r do |ff|
25
- = ff.quiet_input :question_id
26
- = ff.quiet_input :id unless r.new_record?
27
- %th= q.split_text(:pre)
21
+ %th
22
+ = q.split_text(:pre)
23
+ - if q.pick == "one"
24
+ - r = response_for(@response_set, q, nil, g)
25
+ - i = response_idx # increment the response index since the answer partial skips for q.pick == one
26
+ = f.semantic_fields_for i, r do |ff|
27
+ = ff.input :question_id, :as => :quiet
28
+ = ff.input :api_id, :as => :quiet
28
29
  - q.answers.each do |a|
29
30
  %td= render a.custom_renderer || '/partials/answer', :g => g, :q => q, :a => a, :f => f
30
31
  %th= q.split_text(:post)
@@ -1,36 +1,39 @@
1
1
  object @survey
2
2
  attribute :title
3
- attribute :api_id => :uuid
4
- node(:description, :if => lambda {|s| !s.description.blank? }){|s| s.description }
5
- node(:reference_identifier, :if => lambda {|s| !s.reference_identifier.blank? }){|s| s.reference_identifier }
3
+ attribute :api_id => :uuid
4
+ node(:description, :if => lambda {|s| !s.description.blank? }){|s| s.description }
5
+ node(:reference_identifier, :if => lambda {|s| !s.reference_identifier.blank? }){|s| s.reference_identifier }
6
6
 
7
7
  child :sections => :sections do
8
8
  attributes :title, :display_order
9
- node(:description, :if => lambda {|s| !s.description.blank? }){|s| s.description }
10
- node(:reference_identifier, :if => lambda {|s| !s.reference_identifier.blank? }){|s| s.reference_identifier }
11
-
9
+ node(:description, :if => lambda {|s| !s.description.blank? }){|s| s.description }
10
+ node(:reference_identifier, :if => lambda {|s| !s.reference_identifier.blank? }){|s| s.reference_identifier }
11
+
12
12
  child :questions_and_groups => :questions_and_groups do
13
13
  # both questions and question_groups have uuid, text, help_text, reference_identifier, and type
14
- attribute :api_id => :uuid
15
- node(:text, :if => lambda { |q| q.is_a?(Question)}){ |q| q.split_text(:pre) }
16
- node(:text, :if => lambda { |q| q.is_a?(QuestionGroup)}){ |q| q.text }
17
- node(:help_text, :if => lambda { |q| !q.help_text.blank? }){ |q| q.help_text }
18
- node(:reference_identifier, :if => lambda { |q| !q.reference_identifier.blank? }){ |q| q.reference_identifier }
19
- node(:type, :if => lambda { |q| q.display_type != "default" }){ |q| q.display_type }
20
-
14
+ attribute :api_id => :uuid
15
+ node(:text, :if => lambda { |q| q.is_a?(Question)}){ |q| q.split_text(:pre) }
16
+ node(:text, :if => lambda { |q| q.is_a?(QuestionGroup)}){ |q| q.text }
17
+ node(:help_text, :if => lambda { |q| !q.help_text.blank? }){ |q| q.help_text }
18
+ node(:reference_identifier, :if => lambda { |q| !q.reference_identifier.blank? }){ |q| q.reference_identifier }
19
+ node(:data_export_identifier, :if => lambda { |q| !q.data_export_identifier.blank? }){ |q| q.data_export_identifier }
20
+ node(:type, :if => lambda { |q| q.display_type != "default" }){ |q| q.display_type }
21
+
21
22
  # only questions
22
- node(:pick, :if => lambda { |q| q.is_a?(Question) && q.pick != "none" }){ |q| q.pick }
23
- node(:post_text, :if => lambda { |q| q.is_a?(Question) && !q.split_text(:post).blank? }){ |q| q.split_text(:post) }
24
-
23
+ node(:pick, :if => lambda { |q| q.is_a?(Question) && q.pick != "none" }){ |q| q.pick }
24
+ node(:post_text, :if => lambda { |q| q.is_a?(Question) && !q.split_text(:post).blank? }){ |q| q.split_text(:post) }
25
+
25
26
  child :answers, :if => lambda { |q| q.is_a?(Question) && !q.answers.blank? } do
26
- attribute :api_id => :uuid
27
- node(:help_text, :if => lambda { |a| !a.help_text.blank? }){ |a| a.help_text }
28
- node(:exclusive, :if => lambda { |a| a.is_exclusive }){ |a| a.is_exclusive }
27
+ attribute :api_id => :uuid
28
+ node(:help_text, :if => lambda { |a| !a.help_text.blank? }){ |a| a.help_text }
29
+ node(:exclusive, :if => lambda { |a| a.is_exclusive }){ |a| a.is_exclusive }
29
30
  node(:text){ |a| a.split_or_hidden_text(:pre) }
30
- node(:post_text, :if => lambda { |a| !a.split_or_hidden_text(:post).blank? }){ |a| a.split_or_hidden_text(:post) }
31
- node(:type, :if => lambda { |a| a.response_class != "answer" }){ |a| a.response_class }
31
+ node(:post_text, :if => lambda { |a| !a.split_or_hidden_text(:post).blank? }){ |a| a.split_or_hidden_text(:post) }
32
+ node(:type, :if => lambda { |a| a.response_class != "answer" }){ |a| a.response_class }
33
+ node(:reference_identifier, :if => lambda { |a| !a.reference_identifier.blank? }){ |a| a.reference_identifier }
34
+ node(:data_export_identifier, :if => lambda { |a| !a.data_export_identifier.blank? }){ |a| a.data_export_identifier }
32
35
  end
33
-
36
+
34
37
  # both questions and question_groups have dependencies
35
38
  child :dependency, :if => lambda { |q| q.dependency } do
36
39
  attributes :rule
@@ -41,7 +44,7 @@ child :sections => :sections do
41
44
  node(:value, :if => lambda { |d| d.answer && d.answer.response_class != "answer" && d.as(d.answer.response_class) }){ |d| d.as(d.answer.response_class)}
42
45
  end
43
46
  end
44
-
47
+
45
48
  child(:questions, :if => lambda{|x| x.is_a?(QuestionGroup)}) do
46
49
  attributes :api_id => :uuid
47
50
  node(:text){ |q| q.split_text(:pre) }
@@ -50,7 +53,7 @@ child :sections => :sections do
50
53
  node(:reference_identifier, :if => lambda { |q| !q.reference_identifier.blank? }){ |q| q.reference_identifier }
51
54
  node(:type, :if => lambda { |q| q.display_type != "default" }){ |q| q.display_type }
52
55
  node(:pick, :if => lambda { |q| q.pick != "none" }){ |q| q.pick }
53
-
56
+
54
57
  child :answers, :if => lambda { |q| !q.answers.blank? } do
55
58
  attributes :api_id => :uuid
56
59
  node(:help_text, :if => lambda { |a| !a.help_text.blank? }){ |a| a.help_text }
@@ -59,7 +62,7 @@ child :sections => :sections do
59
62
  node(:post_text, :if => lambda { |a| !a.split_or_hidden_text(:post).blank? }){ |a| a.split_or_hidden_text(:post) }
60
63
  node(:type, :if => lambda { |a| a.response_class != "answer" }){ |a| a.response_class }
61
64
  end
62
-
65
+
63
66
  child :dependency, :if => lambda { |q| q.dependency } do
64
67
  attributes :rule
65
68
  child :dependency_conditions => :conditions do
@@ -7,13 +7,16 @@
7
7
  %br
8
8
  #survey_list
9
9
  %ul
10
- - unless @surveys.empty?
11
- - @surveys.each do |survey|
10
+ - unless @codes.empty?
11
+ - @codes.each do |access_code, values|
12
12
  %li
13
- = form_tag(take_survey_path(:survey_code => survey.access_code)) do
13
+ = form_tag take_survey_path(:survey_code => access_code) do
14
14
  = hidden_field_tag :surveyor_javascript_enabled, false
15
-
16
- = survey.title
15
+
16
+ = values[:title]
17
+ &nbsp;
18
+ = label_tag :survey_version, 'version:'
19
+ = select_tag(:survey_version, options_for_select([["-- Current version --", ""]] + values[:survey_versions]))
17
20
  &nbsp;
18
21
  = submit_tag( t('surveyor.take_it') )
19
22
  - else
@@ -4,11 +4,12 @@ attribute :created_at
4
4
  attribute :completed_at
5
5
  node(:survey_id){|rs| rs.survey.api_id }
6
6
 
7
- child :responses do
7
+ child (@response_set.responses || []) => :responses do
8
8
  attribute :api_id => :uuid
9
9
  attribute :created_at
10
10
  attribute :updated_at => :modified_at
11
+ attribute :response_group
11
12
  node(:answer_id){|r| r.answer.api_id }
12
13
  node(:question_id){|r| r.question.api_id }
13
- node(:value, :if => lambda{|r| r.answer.response_class != "answer"}){|r| r.as(r.answer.response_class) }
14
+ node(:value, :if => lambda{|r| r.answer.response_class != "answer"}){|r| r.json_value }
14
15
  end
data/ci-exec.sh CHANGED
@@ -1,19 +1,25 @@
1
1
  #!/bin/bash -xe
2
2
 
3
- BUNDLER_VERSION=1.1.rc
3
+ BUNDLER_VERSION=1.1.5
4
4
  GEMSET=surveyor
5
5
 
6
- export TMPDIR="`pwd`/tmp"
7
- mkdir -p tmp
6
+ if [ -z $CI_RUBY ]; then
7
+ echo "CI_RUBY must be set"
8
+ exit 1
9
+ fi
10
+
11
+ # TMPDIR under the workspace causes gconfd-2 to stop responding and eat all
12
+ # memory when Firefox is running for the Selenium tests. TMPDIR in user home
13
+ # seems to work fine.
14
+ export TMPDIR="${HOME}/tmp/surveyor-ci-${CI_RUBY}"
8
15
 
9
16
  if [ -n "${RAILS_VERSION}" ]; then
10
17
  GEMSET="${GEMSET}-${RAILS_VERSION}"
18
+ TMPDIR="${TMPDIR}-${RAILS_VERSION}"
11
19
  fi
12
20
 
13
- if [ -z $CI_RUBY ]; then
14
- echo "CI_RUBY must be set"
15
- exit 1
16
- fi
21
+ rm -rf "${TMPDIR}"
22
+ mkdir -p "${TMPDIR}"
17
23
 
18
24
  set +xe
19
25
  echo "Initializing RVM"
data/cucumber.yml CHANGED
@@ -4,7 +4,7 @@ rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'pr
4
4
  std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
5
5
  %>
6
6
  default: <%= std_opts %> features
7
- wip: --tags @wip:3 --wip features
7
+ wip: --tags @wip --wip features
8
8
  rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
9
- ci: --tags ~@wip --tags ~@javascript --format html --out reports/cucumber.html --format junit --out reports/cucumber-xml --format pretty --strict features
10
- ci_wip: --wip --tags @wip --tags ~@javascript --format html --out reports/cucumber_wip.html --format pretty features
9
+ ci: --tags ~@wip --format html --out reports/cucumber.html --format junit --out reports/cucumber-xml --format pretty --strict features
10
+ ci_wip: --wip --tags @wip --format html --out reports/cucumber_wip.html --format pretty features