surveyor 0.6.1

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