surveyor 0.6.1

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 (137) hide show
  1. data/.gitignore +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +104 -0
  4. data/Rakefile +50 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/surveyor_controller.rb +117 -0
  7. data/app/helpers/survey_form_builder.rb +37 -0
  8. data/app/helpers/surveyor_helper.rb +66 -0
  9. data/app/models/answer.rb +21 -0
  10. data/app/models/dependency.rb +46 -0
  11. data/app/models/dependency_condition.rb +79 -0
  12. data/app/models/question.rb +54 -0
  13. data/app/models/question_group.rb +18 -0
  14. data/app/models/response.rb +47 -0
  15. data/app/models/response_set.rb +185 -0
  16. data/app/models/survey.rb +57 -0
  17. data/app/models/survey_section.rb +15 -0
  18. data/app/views/layouts/surveyor_default.html.erb +13 -0
  19. data/app/views/partials/_answer.html.haml +49 -0
  20. data/app/views/partials/_question.html.haml +73 -0
  21. data/app/views/partials/_question_group.html.haml +41 -0
  22. data/app/views/surveyor/edit.html.haml +32 -0
  23. data/app/views/surveyor/new.html.haml +17 -0
  24. data/app/views/surveyor/show.html.haml +12 -0
  25. data/config/routes.rb +10 -0
  26. data/generators/surveyor/EXTENDING_SURVEYOR +12 -0
  27. data/generators/surveyor/extend_surveyor_generator.rb +22 -0
  28. data/generators/surveyor/surveyor_generator.rb +60 -0
  29. data/generators/surveyor/templates/README +10 -0
  30. data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_leftright.gif +0 -0
  31. data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_updown.gif +0 -0
  32. data/generators/surveyor/templates/assets/images/222222_11x11_icon_close.gif +0 -0
  33. data/generators/surveyor/templates/assets/images/222222_11x11_icon_doc.gif +0 -0
  34. data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_closed.gif +0 -0
  35. data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_open.gif +0 -0
  36. data/generators/surveyor/templates/assets/images/222222_11x11_icon_minus.gif +0 -0
  37. data/generators/surveyor/templates/assets/images/222222_11x11_icon_plus.gif +0 -0
  38. data/generators/surveyor/templates/assets/images/222222_11x11_icon_resize_se.gif +0 -0
  39. data/generators/surveyor/templates/assets/images/222222_35x9_colorpicker_indicator.gif.gif +0 -0
  40. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_down.gif +0 -0
  41. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_left.gif +0 -0
  42. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_right.gif +0 -0
  43. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_up.gif +0 -0
  44. data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_leftright.gif +0 -0
  45. data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_updown.gif +0 -0
  46. data/generators/surveyor/templates/assets/images/454545_11x11_icon_close.gif +0 -0
  47. data/generators/surveyor/templates/assets/images/454545_11x11_icon_doc.gif +0 -0
  48. data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_closed.gif +0 -0
  49. data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_open.gif +0 -0
  50. data/generators/surveyor/templates/assets/images/454545_11x11_icon_minus.gif +0 -0
  51. data/generators/surveyor/templates/assets/images/454545_11x11_icon_plus.gif +0 -0
  52. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_down.gif +0 -0
  53. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_left.gif +0 -0
  54. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_right.gif +0 -0
  55. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_up.gif +0 -0
  56. data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_leftright.gif +0 -0
  57. data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_updown.gif +0 -0
  58. data/generators/surveyor/templates/assets/images/888888_11x11_icon_close.gif +0 -0
  59. data/generators/surveyor/templates/assets/images/888888_11x11_icon_doc.gif +0 -0
  60. data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_closed.gif +0 -0
  61. data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_open.gif +0 -0
  62. data/generators/surveyor/templates/assets/images/888888_11x11_icon_minus.gif +0 -0
  63. data/generators/surveyor/templates/assets/images/888888_11x11_icon_plus.gif +0 -0
  64. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_down.gif +0 -0
  65. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_left.gif +0 -0
  66. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_right.gif +0 -0
  67. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_up.gif +0 -0
  68. data/generators/surveyor/templates/assets/images/dadada_40x100_textures_02_glass_75.png +0 -0
  69. data/generators/surveyor/templates/assets/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
  70. data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_01_flat_0.png +0 -0
  71. data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_02_glass_65.png +0 -0
  72. data/generators/surveyor/templates/assets/javascripts/accessibleUISlider.jQuery.js +201 -0
  73. data/generators/surveyor/templates/assets/javascripts/jquery-1.2.6.js +3549 -0
  74. data/generators/surveyor/templates/assets/javascripts/jquery-ui-personalized-1.5.3.js +7616 -0
  75. data/generators/surveyor/templates/assets/javascripts/jquery.form.js +637 -0
  76. data/generators/surveyor/templates/assets/javascripts/surveyor.js +28 -0
  77. data/generators/surveyor/templates/assets/stylesheets/jquery-ui-slider-additions.css +71 -0
  78. data/generators/surveyor/templates/assets/stylesheets/reset.css +46 -0
  79. data/generators/surveyor/templates/assets/stylesheets/sass/surveyor.sass +245 -0
  80. data/generators/surveyor/templates/assets/stylesheets/surveyor.css +231 -0
  81. data/generators/surveyor/templates/assets/stylesheets/ui.theme.css +851 -0
  82. data/generators/surveyor/templates/extensions/survey_extensions.rb +18 -0
  83. data/generators/surveyor/templates/extensions/surveyor_controller_extensions.rb +28 -0
  84. data/generators/surveyor/templates/extensions/surveyor_custom.html.erb +13 -0
  85. data/generators/surveyor/templates/extensions/surveyor_helper_extensions.rb +18 -0
  86. data/generators/surveyor/templates/initializers/haml.rb +8 -0
  87. data/generators/surveyor/templates/initializers/surveyor.rb +11 -0
  88. data/generators/surveyor/templates/migrate/create_answers.rb +45 -0
  89. data/generators/surveyor/templates/migrate/create_dependencies.rb +21 -0
  90. data/generators/surveyor/templates/migrate/create_dependency_conditions.rb +29 -0
  91. data/generators/surveyor/templates/migrate/create_question_groups.rb +18 -0
  92. data/generators/surveyor/templates/migrate/create_questions.rb +33 -0
  93. data/generators/surveyor/templates/migrate/create_response_sets.rb +22 -0
  94. data/generators/surveyor/templates/migrate/create_responses.rb +33 -0
  95. data/generators/surveyor/templates/migrate/create_survey_sections.rb +25 -0
  96. data/generators/surveyor/templates/migrate/create_surveys.rb +25 -0
  97. data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +201 -0
  98. data/generators/surveyor/templates/tasks/surveyor.rb +4 -0
  99. data/init.rb +1 -0
  100. data/install.rb +1 -0
  101. data/lib/surveyor.rb +7 -0
  102. data/lib/surveyor/config.rb +45 -0
  103. data/lib/tasks/surveyor_tasks.rake +33 -0
  104. data/lib/tiny_code.rb +58 -0
  105. data/lib/xml_formatter.rb +12 -0
  106. data/script/surveyor/answer.rb +84 -0
  107. data/script/surveyor/columnizer.rb +36 -0
  108. data/script/surveyor/dependency.rb +43 -0
  109. data/script/surveyor/dependency_condition.rb +74 -0
  110. data/script/surveyor/question.rb +76 -0
  111. data/script/surveyor/question_group.rb +33 -0
  112. data/script/surveyor/specs/answer_spec.rb +66 -0
  113. data/script/surveyor/specs/question_dependency_spec.rb +46 -0
  114. data/script/surveyor/specs/question_group_spec.rb +9 -0
  115. data/script/surveyor/specs/question_spec.rb +111 -0
  116. data/script/surveyor/specs/section_spec.rb +58 -0
  117. data/script/surveyor/survey.rb +108 -0
  118. data/script/surveyor/survey_parser.rb +64 -0
  119. data/script/surveyor/survey_section.rb +153 -0
  120. data/script/surveyor/whr_dsl.tmproj +244 -0
  121. data/spec/controllers/surveyor_controller_spec.rb +328 -0
  122. data/spec/factories.rb +107 -0
  123. data/spec/models/answer_spec.rb +29 -0
  124. data/spec/models/dependency_condition_spec.rb +338 -0
  125. data/spec/models/dependency_spec.rb +82 -0
  126. data/spec/models/question_group_spec.rb +11 -0
  127. data/spec/models/question_spec.rb +75 -0
  128. data/spec/models/response_set_spec.rb +153 -0
  129. data/spec/models/response_spec.rb +94 -0
  130. data/spec/models/survey_section_spec.rb +34 -0
  131. data/spec/models/survey_spec.rb +72 -0
  132. data/spec/rcov.opts +2 -0
  133. data/spec/spec.opts +4 -0
  134. data/spec/spec_helper.rb +14 -0
  135. data/surveyor.gemspec +187 -0
  136. data/uninstall.rb +1 -0
  137. metadata +211 -0
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ surveys/fixtures/*.yml
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Brian Chamberlain and Mark Yoon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,104 @@
1
+ # Survey On Rails
2
+
3
+ Surveyor is a rails (gem) plugin, that brings surveys to your rails app. Before Rails 2.3, it was implemented as a Rails Engine. Surveys are written in a DSL (Domain Specific Language), with examples available in the "kitchen sink" survey.
4
+
5
+ # Installation
6
+
7
+ As a plugin:
8
+
9
+ sudo gem install haml
10
+ script/plugin install git://github.com/breakpointer/surveyor.git
11
+
12
+ Or as a gem plugin:
13
+
14
+ # in environment.rb
15
+ config.gem "surveyor", :version => '>=0.4.1', :lib => false
16
+
17
+ sudo rake gems:install
18
+
19
+ Generate assets, run migrations:
20
+
21
+ script/generate surveyor
22
+ rake db:migrate
23
+
24
+ Try out the "kitchen sink" survey:
25
+
26
+ rake surveyor FILE=surveys/kitchen_sink_survey.rb
27
+
28
+ # Configuration
29
+
30
+ The surveyor generator creates config/initializers/surveyor.rb. There, you can specify:
31
+
32
+ - your own relative root for surveys ('/' is not recommended as any path will be interpreted as a survey name)
33
+ - your own custom title (string) for the survey list page
34
+ - your own custom layout file name, in your app/views/layouts folder
35
+ - your own custom finish url for all surveys. you can give a string (a path), a symbol (the name of a method in ApplicationController)
36
+ - if you would like surveys to require authorization via the restful_authentication plugin
37
+ - if you would like to extend the surveyor_controller (see Extending Surveyor below)
38
+
39
+ The initializer runs once, when the app starts. The block style is used to keep multiple options DRY (defaults below):
40
+
41
+ Surveyor::Config.run do |config|
42
+ config['default.relative_url_root'] = "surveys/" # should end with '/'
43
+ config['default.title'] = "You can take these surveys:"
44
+ config['default.layout'] = "surveyor_default"
45
+ config['default.finish'] = "/surveys"
46
+ config['use_restful_authentication'] = false
47
+ config['extend_controller'] = false
48
+ end
49
+
50
+ You can update surveyor's at any time. Use the block style (above), or the individual style:
51
+
52
+ Surveyor::Config['default.title'] = "Cheese is great!"
53
+
54
+ To look at the current surveyor configuration:
55
+
56
+ Surveyor::Config.to_hash.inspect
57
+
58
+ # Extending surveyor
59
+
60
+ Surveyor's models, helper, and controller can be extended from custom modules your app/models, app/helpers and app/controllers directories. To generate the sample files and sample layout, run:
61
+
62
+ script/generate extend_surveyor
63
+
64
+ Any of surveyor's models class_eval, class methods, and instance methods can be modified. Include the following in config/initializers/surveyor.rb:
65
+
66
+ require 'models/survey_extensions' # Extended the survey model
67
+
68
+ SurveyorHelper class_eval and instance methods can be modified. Include the following in config/initializers/surveyor.rb:
69
+
70
+ require 'helpers/surveyor_helper_extensions' # Extend the surveyor helper
71
+
72
+ SurveyorController class_eval, class methods, instance methods, and actions can be modified. Action methods should be specified separately in the Actions submodule. Set the following option in config/initializers/surveyor.rb Surveyor::Config block:
73
+
74
+ config['extend_controller'] = true
75
+
76
+ # Sample layout
77
+
78
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
79
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
80
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
81
+ <head>
82
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
83
+ <title>Survey: <%= controller.action_name %></title>
84
+ <%= surveyor_includes # calls surveyor_javascripts + surveyor_stylesheets %>
85
+ </head>
86
+ <body>
87
+ <div id="flash"><%= flash[:notice] %></div>
88
+ <div id="survey_with_menu">
89
+ <%= yield %>
90
+ </div>
91
+ </body>
92
+ </html>
93
+
94
+ The <code>surveyor\_includes</code> helper just calls <code>surveyor\_stylsheets + surveyor\_javascripts</code> which in turn call:
95
+
96
+ stylesheet_link_tag 'surveyor/reset', 'surveyor/surveyor', 'surveyor/ui.theme.css','surveyor/jquery-ui-slider-additions'
97
+
98
+ javascript_include_tag 'surveyor/jquery-1.2.6.js', 'surveyor/jquery-ui-personalized-1.5.3.js', 'surveyor/accessibleUISlider.jQuery.js','surveyor/jquery.form.js', 'surveyor/surveyor.js'
99
+
100
+ # Dependencices
101
+
102
+ Surveyor depends on Rails 2.3 and the SASS style sheet language, part of HAML (http://haml.hamptoncatlin.com/download)
103
+
104
+ Copyright (c) 2008-2009 Brian Chamberlain and Mark Yoon, released under the MIT license
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "surveyor"
8
+ gem.summary = %Q{A rails (gem) plugin to enable surveys in your application}
9
+ gem.email = "yoon@northwestern.edu"
10
+ gem.homepage = "http://github.com/breakpointer/surveyor"
11
+ gem.authors = ["Brian Chamberlain", "Mark Yoon"]
12
+ gem.add_dependency 'haml'
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+
34
+ task :default => :spec
35
+
36
+ require 'rake/rdoctask'
37
+ Rake::RDocTask.new do |rdoc|
38
+ if File.exist?('VERSION.yml')
39
+ config = YAML.load(File.read('VERSION.yml'))
40
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "surveyor #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
50
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.1
@@ -0,0 +1,117 @@
1
+ # The Surveyor controller a user taking a survey. It is semi-RESTful since it does not have a concrete representation model.
2
+ # The "resource" is a survey attempt/session populating a response set.
3
+
4
+ class SurveyorController < ApplicationController
5
+
6
+ # Layout
7
+ layout Surveyor::Config['default.layout'] || 'surveyor_default'
8
+
9
+ # Extending surveyor
10
+ include SurveyorControllerExtensions if Surveyor::Config['extend_controller'] && defined? SurveyorControllerExtensions
11
+ before_filter :extend_actions
12
+
13
+ # RESTful authentication
14
+ if Surveyor::Config['use_restful_authentication']
15
+ include AuthenticatedSystem
16
+ before_filter :login_required
17
+ end
18
+
19
+ # Get the response set or current_user
20
+ # before_filter :get_response_set, :except => [:new, :create]
21
+ before_filter :get_current_user, :only => [:new, :create]
22
+
23
+ # Actions
24
+ def new
25
+ @surveys = Survey.find(:all)
26
+ end
27
+ def create
28
+ if (@survey = Survey.find_by_access_code(params[:survey_code])) && (@response_set = ResponseSet.create(:survey => @survey, :user_id => @current_user))
29
+ flash[:notice] = "Survey was successfully started."
30
+ redirect_to(edit_my_survey_path(:survey_code => @survey.access_code, :response_set_code => @response_set.access_code))
31
+ else
32
+ flash[:notice] = "Unable to find that survey"
33
+ redirect_to(available_surveys_path)
34
+ end
35
+ end
36
+ def show
37
+ end
38
+ def edit
39
+ if @response_set = ResponseSet.find_by_access_code(params[:response_set_code], :include => {:responses => [:question, :answer]})
40
+ @survey = Survey.with_sections.find_by_id(@response_set.survey_id)
41
+ @sections = @survey.sections
42
+ @section = params[:section] ? @sections.with_includes.find(section_id_from(params[:section])) || @sections.with_includes.first : @sections.with_includes.first
43
+ else
44
+ flash[:notice] = "Unable to find your responses to the survey"
45
+ redirect_to(available_surveys_path)
46
+ end
47
+
48
+ @dependents = (@response_set.unanswered_dependencies - @section.questions) || []
49
+ end
50
+ def update
51
+ if @response_set = ResponseSet.find_by_access_code(params[:response_set_code], :include => {:responses => :answer})
52
+ @response_set.current_section_id = params[:current_section_id]
53
+ else
54
+ flash[:notice] = "Unable to find your responses to the survey"
55
+ redirect_to(available_surveys_path)
56
+ end
57
+
58
+ if params[:responses] or params[:response_groups]
59
+ @response_set.clear_responses
60
+ saved = @response_set.update_attributes(:response_attributes => (params[:responses] || {}).dup , :response_group_attributes => (params[:response_groups] || {}).dup) #copy (dup) to preserve params because we manipulate params in the response_set methods
61
+ if (saved && params[:finish])
62
+ @response_set.complete!
63
+ saved = @response_set.save!
64
+ end
65
+ end
66
+ respond_to do |format|
67
+ format.html do
68
+ if saved && params[:finish]
69
+ flash[:notice] = "Completed survey"
70
+ redirect_to surveyor_default_finish
71
+ else
72
+ flash[:notice] = "Unable to update survey" if !saved and !saved.nil? # saved.nil? is true if there are no questions on the page (i.e. if it only contains a label)
73
+ redirect_to :action => "edit", :anchor => anchor_from(params[:section]), :params => {:section => section_id_from(params[:section])}
74
+ end
75
+ end
76
+ # No redirect needed if we're talking to the page via json
77
+ format.js do
78
+ render :json => @response_set.all_dependencies.to_json
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # Filters
86
+ def get_current_user
87
+ @current_user = self.respond_to?(:current_user) ? self.current_user : nil
88
+ end
89
+
90
+ # Params: the name of some submit buttons store the section we'd like to go to. for repeater questions, an anchor to the repeater group is also stored
91
+ # e.g. params[:section] = {"1"=>{"question_group_1"=>"<= add row"}}
92
+ def section_id_from(p)
93
+ p.respond_to?(:keys) ? p.keys.first : p
94
+ end
95
+ def anchor_from(p)
96
+ p.respond_to?(:keys) && p[p.keys.first].respond_to?(:keys) ? p[p.keys.first].keys.first : nil
97
+ end
98
+
99
+ # Extending surveyor
100
+ def surveyor_default_finish
101
+ # http://www.postal-code.com/mrhappy/blog/2007/02/01/ruby-comparing-an-objects-class-in-a-case-statement/
102
+ # http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/
103
+ case finish = Surveyor::Config['default.finish']
104
+ when String
105
+ return finish
106
+ when Symbol
107
+ return self.send(finish)
108
+ else
109
+ return '/surveys'
110
+ end
111
+ end
112
+ def extend_actions
113
+ # http://blog.mattwynne.net/2009/07/11/rails-tip-use-polymorphism-to-extend-your-controllers-at-runtime/
114
+ self.extend SurveyorControllerExtensions::Actions if Surveyor::Config['extend_controller'] && defined? SurveyorControllerExtensions::Actions
115
+ end
116
+
117
+ end
@@ -0,0 +1,37 @@
1
+ class SurveyFormBuilder < ActionView::Helpers::FormBuilder
2
+ def survey_check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
3
+ fields = @template.survey_check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
4
+ fields[1]
5
+ end
6
+ end
7
+
8
+ module ActionView
9
+ module Helpers
10
+ module FormHelper
11
+ def survey_check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
12
+ if (Rails::VERSION::STRING.to_f > 2.1)
13
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_survey_check_box_tag(options, checked_value, unchecked_value)
14
+ else
15
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_survey_check_box_tag(options, checked_value, unchecked_value)
16
+ end
17
+ end
18
+ end
19
+
20
+ class InstanceTag
21
+ def to_survey_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
22
+ options = options.stringify_keys
23
+ options["type"] = "checkbox"
24
+ options["value"] = checked_value
25
+ if options.has_key?("checked")
26
+ cv = options.delete "checked"
27
+ checked = cv == true || cv == "checked"
28
+ else
29
+ checked = self.class.check_box_checked?(value(object), checked_value)
30
+ end
31
+ options["checked"] = "checked" if checked
32
+ add_default_name_and_id(options)
33
+ [tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value), tag("input", options)]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,66 @@
1
+ module SurveyorHelper
2
+
3
+ # Configuration
4
+ def surveyor_config
5
+ Surveyor::Config
6
+ end
7
+
8
+ # Layout: stylsheets and javascripts
9
+ def surveyor_includes
10
+ surveyor_stylsheets + surveyor_javascripts
11
+ end
12
+ def surveyor_stylsheets
13
+ stylesheet_link_tag 'surveyor/reset', 'surveyor/surveyor', 'surveyor/ui.theme.css','surveyor/jquery-ui-slider-additions'
14
+ end
15
+ def surveyor_javascripts
16
+ javascript_include_tag 'surveyor/jquery-1.2.6.js', 'surveyor/jquery-ui-personalized-1.5.3.js', 'surveyor/accessibleUISlider.jQuery.js','surveyor/jquery.form.js', 'surveyor/surveyor.js'
17
+ end
18
+
19
+ # Section: dependencies, menu, previous and next
20
+ def dependency_explanation_helper(question,response_set)
21
+ # Attempts to explain why this dependent question needs to be answered by referenced the dependent question and users response
22
+ trigger_responses = []
23
+ dependent_questions = Question.find_all_by_id(question.dependency.dependency_conditions.map(&:question_id)).uniq
24
+ response_set.responses.find_all_by_question_id(dependent_questions.map(&:id)).uniq.each do |resp|
25
+ trigger_responses << resp.to_s
26
+ end
27
+ "&nbsp;&nbsp;You answered &quot;#{trigger_responses.join("&quot; and &quot;")}&quot; to the question &quot;#{dependent_questions.map(&:text).join("&quot;,&quot;")}&quot;"
28
+ end
29
+ def menu_button_for(section)
30
+ submit_tag(section.title, :name => "section[#{section.id}]")
31
+ end
32
+ def previous_section
33
+ # submit_tag("&laquo; Previous section", :name => "section[#{@section.previous.id}]") unless @section.previous.nil?
34
+ # refactored to use copy in memory instead of making extra db calls
35
+ submit_tag("&laquo; Previous section", :name => "section[#{@sections[@sections.index(@section)-1].id}]") unless @sections.first == @section
36
+ end
37
+ def next_section
38
+ # @section.next.nil? ? submit_tag("Click here to finish", :name => "finish") : submit_tag("Next section &raquo;", :name => "section[#{@section.next.id}]")
39
+ # refactored to use copy in memory instead of making extra db calls
40
+ @sections.last == @section ? submit_tag("Click here to finish", :name => "finish") : submit_tag("Next section &raquo;", :name => "section[#{@sections[@sections.index(@section)+1].id}]")
41
+ end
42
+
43
+ # Questions
44
+ def next_number
45
+ @n ||= 0
46
+ "#{@n += 1}<span style='padding-left:0.1em;'>)</span>"
47
+ end
48
+ def split_text(text = "") # Split text into with "|" delimiter - parts to go before/after input element
49
+ {:prefix => text.split("|")[0].blank? ? "&nbsp;" : text.split("|")[0], :postfix => text.split("|")[1] || "&nbsp;"}
50
+ end
51
+ def question_help_helper(question)
52
+ question.help_text.blank? ? "" : %Q(<span class="question-help">#{question.help_text}</span>)
53
+ end
54
+
55
+ # Answers
56
+ def fields_for_response(response, &block)
57
+ fields_for("responses[#{response.question_id}][#{response.answer_id}]", response, :builder => SurveyFormBuilder, &block)
58
+ end
59
+ def fields_for_repeater_response(response, response_group, &block) # Changes the response hash to accept response groups for repeater elements
60
+ fields_for("response_groups[#{response.question_id}][#{response_group}][#{response.answer_id}]", response, :builder => SurveyFormBuilder, &block)
61
+ end
62
+ def fields_for_radio(response, &block)
63
+ fields_for("responses[#{response.question_id}]", response, :builder => SurveyFormBuilder, &block)
64
+ end
65
+
66
+ end
@@ -0,0 +1,21 @@
1
+ class Answer < ActiveRecord::Base
2
+
3
+ # Associations
4
+ belongs_to :question
5
+ has_many :responses
6
+
7
+ # Scopes
8
+ default_scope :order => "display_order ASC"
9
+
10
+ # Validations
11
+ validates_presence_of :text
12
+ validates_numericality_of :question_id, :allow_nil => false, :only_integer => true
13
+ #validates_uniqueness_of :reference_identifier
14
+
15
+ # Methods
16
+ def renderer(q = question)
17
+ r = [q.pick.to_s, self.response_class].compact.join("_")
18
+ r.blank? ? :default : r.to_sym
19
+ end
20
+
21
+ end
@@ -0,0 +1,46 @@
1
+ class Dependency < ActiveRecord::Base
2
+ # Associations
3
+ belongs_to :question
4
+ has_many :dependency_conditions
5
+
6
+ # Scopes
7
+ named_scope :depending_on_questions, lambda {|question_ids| {:include => :dependency_conditions, :conditions => {:dependency_conditions => {:question_id => question_ids}} }}
8
+
9
+ # Validations
10
+ validates_presence_of :rule
11
+ validates_format_of :rule, :with => /^(?:and|or|\)|\(|\d|\s)+$/ #TODO properly formed parenthesis etc.
12
+ validates_numericality_of :question_id
13
+
14
+ # Attribute aliases
15
+ alias_attribute :dependent_question_id, :question_id
16
+
17
+ # Is the method that determines if this dependency has been met within
18
+ # the provided response set
19
+ def met?(response_set)
20
+ if keyed_pairs = keyed_conditions(response_set)
21
+ return(rule_evaluation(keyed_pairs))
22
+ else
23
+ return(false)
24
+ end
25
+ end
26
+
27
+ # Pairs up the substitution key with the evaluated condition result for substitution into the rule
28
+ # Example: If you have two dependency conditions with rule keys "A" and "B" in the rule "A or B"
29
+ # calling keyed_condition_pairs will return {:A => true, :B => false}
30
+ def keyed_conditions(response_set)
31
+ keyed_pairs = {}
32
+ self.dependency_conditions.each do |dc|
33
+ keyed_pairs.merge!(dc.to_evaluation_hash(response_set))
34
+ end
35
+ return(keyed_pairs)
36
+ end
37
+
38
+ # Does the substiution and evaluation of the dependency rule with the keyed pairs
39
+ def rule_evaluation(keyed_pairs)
40
+ # subtitute into rule for evaluation
41
+ rgx = Regexp.new(self.dependency_conditions.map{|dc| dc.rule_key}.join("|")) # Making a regexp to only look for the keys used in the child conditions
42
+ #logger.debug("rexp: #{rgx.inspect} FOO: #{keyed_pairs.inspect} --- subbed rules: #{rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}} --> #{eval(self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]})}")
43
+ eval(self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}) # returns the evaluation of the rule and the conditions
44
+ end
45
+
46
+ end