├── .gitignore ├── Gemfile ├── MIT-LICENSE ├── README.rdoc ├── Rakefile ├── app ├── assets │ ├── images │ │ └── qwester │ │ │ └── .gitkeep │ ├── javascripts │ │ └── qwester │ │ │ └── application.js │ └── stylesheets │ │ └── qwester │ │ └── application.css ├── controllers │ └── qwester │ │ ├── application_controller.rb │ │ └── questionnaires_controller.rb ├── helpers │ └── qwester │ │ ├── application_helper.rb │ │ └── questionnaires_helper.rb ├── models │ └── qwester │ │ ├── answer.rb │ │ ├── answer_store.rb │ │ ├── presentation.rb │ │ ├── presentation_questionnaire.rb │ │ ├── question.rb │ │ ├── questionnaire.rb │ │ ├── questionnaires_question.rb │ │ └── rule_set.rb └── views │ ├── layouts │ └── qwester │ │ └── application.html.erb │ └── qwester │ └── questionnaires │ ├── _errors.html.erb │ ├── index.html.erb │ └── show.html.erb ├── config └── routes.rb ├── db └── migrate │ ├── 20121008151526_create_questions.rb │ ├── 20121008152106_create_answers.rb │ ├── 20121008153758_create_rule_sets.rb │ ├── 20121009080219_create_questionnaires.rb │ ├── 20121009095600_create_answer_stores.rb │ ├── 20121009160807_add_questionnaire_id_to_answers.rb │ ├── 20121009161206_change_answers_value_to_string.rb │ ├── 20121019111051_add_button_image_to_questionnaires.rb │ ├── 20121114084347_create_ckeditor_assets.rb │ ├── 20121115114408_add_ref_to_questions.rb │ ├── 20121120092108_add_ids_to_questionnaires_questions.rb │ ├── 20121122130930_add_position_to_answers.rb │ ├── 20121122141628_remove_questionnaire_from_answer.rb │ ├── 20121126152146_add_rule_to_rule_sets.rb │ ├── 20121203150555_add_multi_answer_to_questions.rb │ ├── 20121204082213_create_answer_stores_questionnaires.rb │ ├── 20121204100803_add_link_text_to_rule_sets.rb │ ├── 20130306114331_add_preserved_to_answer_stores.rb │ ├── 20130315112847_create_qwester_presentations.rb │ ├── 20130315113027_create_qwester_presentation_questionnaires.rb │ ├── 20130318102537_add_presentation_to_rule_sets.rb │ ├── 20130402074240_add_weighting_to_qwester_answers.rb │ ├── 20130502082540_add_position_to_qwester_presentation_questionnaires.rb │ └── 20131119142009_add_must_complete_to_qwester_questionnaires.rb ├── lib ├── active_admin │ └── admin │ │ ├── answers.rb │ │ ├── presentations.rb │ │ ├── questionnaires.rb │ │ ├── questions.rb │ │ └── rule_sets.rb ├── qwester.rb ├── qwester │ ├── engine.rb │ └── version.rb ├── rails │ └── actionpack │ │ └── lib │ │ └── action_controller │ │ └── base.rb ├── random_string.rb └── tasks │ └── qwester_tasks.rake ├── qwester.gemspec ├── script └── rails └── test ├── dummy ├── README.rdoc ├── Rakefile ├── app │ ├── admin │ │ ├── admin_user.rb │ │ └── dashboard.rb │ ├── assets │ │ ├── javascripts │ │ │ ├── active_admin.js │ │ │ ├── application.js │ │ │ └── questionnaires.js │ │ └── stylesheets │ │ │ ├── active_admin.css.scss │ │ │ ├── application.css │ │ │ └── questionnaires.css.scss │ ├── controllers │ │ └── application_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── mailers │ │ └── .gitkeep │ ├── models │ │ ├── .gitkeep │ │ └── admin_user.rb │ └── views │ │ └── layouts │ │ └── application.html.erb ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── active_admin.rb │ │ ├── backtrace_silencers.rb │ │ ├── devise.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── qwester.rb │ │ ├── secret_token.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ ├── devise.en.yml │ │ └── en.yml │ └── routes.rb ├── db │ ├── migrate │ │ ├── 20130110115938_devise_create_admin_users.rb │ │ ├── 20130110115940_create_admin_comments.rb │ │ ├── 20130314103645_create_questions.qwester.rb │ │ ├── 20130314103646_create_answers.qwester.rb │ │ ├── 20130314103647_create_rule_sets.qwester.rb │ │ ├── 20130314103648_create_questionnaires.qwester.rb │ │ ├── 20130314103649_create_answer_stores.qwester.rb │ │ ├── 20130314103650_add_questionnaire_id_to_answers.qwester.rb │ │ ├── 20130314103651_change_answers_value_to_string.qwester.rb │ │ ├── 20130314103652_add_button_image_to_questionnaires.qwester.rb │ │ ├── 20130314103653_create_ckeditor_assets.qwester.rb │ │ ├── 20130314103654_add_ref_to_questions.qwester.rb │ │ ├── 20130314103655_add_ids_to_questionnaires_questions.qwester.rb │ │ ├── 20130314103656_add_position_to_answers.qwester.rb │ │ ├── 20130314103657_remove_questionnaire_from_answer.qwester.rb │ │ ├── 20130314103658_add_rule_to_rule_sets.qwester.rb │ │ ├── 20130314103659_add_multi_answer_to_questions.qwester.rb │ │ ├── 20130314103660_create_answer_stores_questionnaires.qwester.rb │ │ ├── 20130314103661_add_link_text_to_rule_sets.qwester.rb │ │ ├── 20130314103662_add_preserved_to_answer_stores.qwester.rb │ │ ├── 20130315113346_create_qwester_presentations.qwester.rb │ │ ├── 20130315113347_create_qwester_presentation_questionnaires.qwester.rb │ │ ├── 20130402085423_add_presentation_to_rule_sets.qwester.rb │ │ ├── 20130402085424_add_weighting_to_qwester_answers.qwester.rb │ │ ├── 20130502082731_add_position_to_qwester_presentation_questionnaires.qwester.rb │ │ └── 20131119142050_add_must_complete_to_qwester_questionnaires.qwester.rb │ ├── schema.rb │ ├── seeds.rb │ └── seeds │ │ └── questions.yml ├── lib │ └── assets │ │ └── .gitkeep ├── log │ └── .gitkeep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ └── favicon.ico ├── script │ └── rails └── test │ ├── fixtures │ ├── admin_users.yml │ └── qwester │ │ ├── answer_stores.yml │ │ ├── answers.yml │ │ ├── presentation_questionnaires.yml │ │ ├── presentations.yml │ │ ├── questionnaires.yml │ │ ├── questionnaires_questions.yml │ │ ├── questions.yml │ │ └── rule_sets.yml │ ├── functional │ └── qwester │ │ └── questionnaires_controller_test.rb │ ├── test_helper.rb │ └── unit │ ├── admin_user_test.rb │ └── helpers │ └── questionnaires_helper_test.rb ├── integration └── navigation_test.rb ├── test_helper.rb └── unit ├── qwester ├── answer_store_test.rb ├── answer_test.rb ├── presentation_questionnaire_test.rb ├── presentation_test.rb ├── question_test.rb ├── questionnaire_test.rb ├── questionnaires_question_test.rb └── rule_set_test.rb └── qwester_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/log/*.log 6 | test/dummy/tmp/ 7 | test/dummy/.sass-cache 8 | test/dummy/public/system/* 9 | .rvmrc 10 | .ruby-* 11 | *.gem 12 | Gemfile.lock -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Declare your gem's dependencies in qwester.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | # jquery-rails is used by the dummy application 9 | #gem "jquery-rails" 10 | 11 | # Declare any dependencies that are still in development here instead of in 12 | # your gemspec. These might include edge Rails or gems from your path or 13 | # Git. Remember to move these dependencies to your gemspec before releasing 14 | # your gem to rubygems.org. 15 | 16 | # To use debugger 17 | # gem 'debugger' 18 | 19 | 20 | 21 | # I can't get activeadmin and jquery (which it needs) to work in 22 | # the test/dummy environment without these gem declarations. 23 | 24 | gem 'activeadmin' 25 | gem 'bourbon' 26 | group :assets do 27 | gem 'coffee-script' 28 | gem 'jquery-rails' 29 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 30 | gem 'therubyracer', '0.10.2', :platforms => :ruby 31 | end 32 | 33 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Rob Nichols 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. 21 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | {}[https://codeclimate.com/github/reggieb/qwester] 2 | = Qwester 3 | 4 | A rails engine used to add questionnaires to rails applications 5 | 6 | == Installation 7 | 8 | Add this to your Gemfile 9 | 10 | gem 'qwester' 11 | 12 | === Migrations 13 | 14 | Qwester includes a set of migrations to build the database tables to match 15 | qwester's models. To use these migrations within the host application, run 16 | this rake task: 17 | 18 | rake qwester:install:migrations 19 | 20 | This will copy the migrations to the host application's db/migrate folder. 21 | They will then be run next time the db:migrate task is run: 22 | 23 | rake db:migrate 24 | 25 | If you only wish to run the qwester migrations use a scope option: 26 | 27 | rake db:migrate SCOPE=qwester 28 | 29 | === Mounting the engine 30 | 31 | To mount qwester within your rails application, add this to config/routes.rb: 32 | 33 | mount Qwester::Engine => "/questionnaires" 34 | 35 | == Questionnaire 36 | 37 | To create a new questionnaire, first create a set of questions that you 38 | wish to appear within the questionnaire. Then create a questionnaire and 39 | associate the questions with that questionnaire. Questions can be used in 40 | multiple questionnaires, and can be ordered within each questionnaire. That 41 | is, the first question in one questionnaire, can be displayed as the second 42 | question in another questionnaire. 43 | 44 | === Questions and Answers 45 | 46 | Each question has a number of answers associated with it. The default set of 47 | answers is given by: 48 | 49 | Qwester::Answer.standard_values 50 | 51 | However, these can be modified and added to as you require. That is, each 52 | question has a unique set of answers that can be modified as required. 53 | 54 | === Answer selection list helper 55 | 56 | The helper method qwester_answers_selection_list will output a set of form 57 | elements matching the answers for the given question, as an unordered list. 58 | The form elements will be radio buttons unless the question is marked as 59 | 'multi-answer', in which case check boxes will be used. 60 | 61 | <%= qwester_answers_selection_list(question) %> 62 | 63 | The controller method update_qwester_answer_store can be used to manage the 64 | data submitted from these form elements. It will add the answers to the 65 | current answer store. 66 | 67 | === Answer store 68 | 69 | Submitted answers are stored in an AnswerStore object. On creation, each 70 | answer_store is assigned a session_id that can then be used within session, 71 | to identify the current user's answer_store. In this way, questionnaire 72 | submissions can be tracked across multiple submissions. 73 | 74 | By default the current answer_store.session_id is stored in 75 | session[:qwester_answer_store]. If you wish to use a different session key, 76 | set Qwester.session_key in the qwester initializer: 77 | 78 | Qwester.session_key = :your_preferred_key 79 | 80 | === Preservation 81 | 82 | To preserve an answer store, call its preserve method: 83 | 84 | preserved_answer_store = answer_store.preserve 85 | 86 | This will create a copy of the original answer_store. This copy will have its 87 | own unique session_id. This session_id can then be used restore the answer_store 88 | at a later date. The answer_store#preserved field stores the datetime 89 | when it was preserved. 90 | 91 | preserved_answer_store.preserved? == true 92 | preserved_answer_store.preserved --> datetime of preservation 93 | 94 | The answers and questionnaires are copied over to the preserved answers store 95 | 96 | answer_store.questionnaires == preserved_answer_store.questionnaires 97 | answer_store.answers == preserved_answer_store.answers 98 | 99 | Note that it is the answer store copy that is preserved, and not the original 100 | 101 | answer_store.preserved? == false 102 | 103 | A preserved answer store therefore acts as a snap shot of an answer_store. 104 | 105 | === Restoration 106 | 107 | Restoring a preserved answer_store, creates a new copy of the answer store. This 108 | restored copy can then be used without alteration of the preserved snap shot. 109 | The restored copy will also have its own session_id. 110 | 111 | restored_answer_store = preserved_answer_store.restore 112 | restored_answer_store.preserved? == false 113 | restored_answer_store.questionnaires == preserved_answer_store.questionnaires 114 | restored_answer_store.answers == preserved_answer_store.answers 115 | 116 | === Clean up answer stores 117 | 118 | A rake task is available that will remove all the unpreserved answer stores that 119 | are more than a day old. 120 | 121 | rake qwester:destroy_unpreserved_answer_stores RAILS_ENV=production 122 | 123 | === Rule set 124 | 125 | Groups of answers can be matched to rule sets using: 126 | 127 | RuleSet.matching(answers) 128 | 129 | which will return an array of rule sets that match those answers. 130 | 131 | Each rule set has an url associated with it, and this url should lead a user 132 | to a resource either within or outside the app. 133 | 134 | RuleSet uses array_logic[http://github.com/reggieb/array_logic] to manage 135 | the logic used to compare each rule set with the array of answers. 136 | 137 | == Presentation 138 | 139 | Questionnaires can be grouped into Presentations, and these are used to 140 | control which questionnaires are displayed (presented to the user) at 141 | any time. 142 | 143 | All questionnaires are display at the engine root unless: 144 | 145 | * A presentation is set as 'default' 146 | 147 | * An existing presentation's name is added to session[:presentations] (an array) 148 | 149 | === Restricting the questionnaires displayed 150 | 151 | If you do not want to display all the available questionnaires, create 152 | a presentation, set it as 'default' and add to it the questionnaires you 153 | wish to display. 154 | 155 | === Using a questionnaire to control access to other questionnaires 156 | 157 | Say you have three presentations of questionnaires, and you want to display 158 | one initially. Then you want to use the answers submitted from that 159 | questionnaire, to control which of the other two sets of questionnaires are 160 | displayed next. 161 | 162 | To do this set the initial presentation as default. Then create two rule 163 | sets, one for each of the other presentations. Update each rule set to 164 | match the answer pattern that should be submitted for the associated 165 | pattern to be displayed, and set rule_set#presentation to the name of 166 | the presentation you wish displayed when the rule is matched. 167 | 168 | Then when the first set of questionnaires are submitted, the rules will be 169 | checked, and if one of the two rules sets matches the submitted answers, 170 | the associated presentation's questionnaires will be displayed. 171 | 172 | === Simple rule matching for presentations 173 | 174 | The rule matching is simple and if two presentation rules match, the system 175 | will not try to work out which one it should display. It will just show the 176 | last one it finds. So some care is required when setting up presentations 177 | with matching rule sets to avoid overlaps and clashes. 178 | 179 | === Once only match 180 | 181 | Once a presentation has been matched, it cannot be returned to later. That 182 | is if there are two presentations: 'one' and 'two', you cannot have a work 183 | flow that goes from one to two and then back to one, unless you reset or 184 | otherwise manipulate session[:presentations]. Instead you should clone 'one' 185 | as a new presentation e.g. 'three', and then go from one to two to three. 186 | 187 | == Answer weighting 188 | 189 | Answers have a weighting field (float) to give selected answers more weight 190 | when comparing answers. You can also define an alias for this field, to give 191 | it a name that is more appropriate to your application. 192 | 193 | For example, you want to use qwester to create a multiple choice test, and each 194 | answer needs to be assigned a 'score'. First you will need to assign :score as 195 | the weighting alias. In an initializer add this: 196 | 197 | Qwester::Answer.weighting_alias = :score 198 | 199 | This will add a 'score' instance method to Answers, that will return the 200 | weighting for that answer. 201 | 202 | Then apply a score/weighting to each correct answer in the database. 203 | 204 | You can then set up a rule to work with the score method. For example: 205 | 206 | rule_set.rule = 'sum(:score) >= 10' 207 | 208 | See array_logic[http://github.com/reggieb/array_logic] for a list of functions 209 | available. 210 | 211 | The default value of weighting is zero. 212 | 213 | == Dummy 214 | 215 | A test app is present within this engine, and provides an example of how 216 | Qwester can be used within a Rails app. See test/dummy. 217 | 218 | However, look at the notes in Gemfile. You may need to uncomment two gem 219 | declarations in the gemfile, before jquery and active_admin work correctly in 220 | the test/dummy environment. 221 | 222 | == Testing 223 | 224 | Note that the test datebase is not updated when running the root tests. 225 | 226 | To create the test database run the following at test/dummy 227 | 228 | rake db:schema:load RAILS_ENV=test 229 | 230 | To keep the test database in step with new migrations, at test/dummy run 231 | 232 | rake db:migrate RAILS_ENV=test 233 | 234 | To run all the tests, run rake test at the engine root. 235 | 236 | == Integration with ActiveAdmin 237 | 238 | Qwester contains a set of ActiveAdmin register files, that allow Qwester 239 | models to be managed from within the parent app's active_admin space. Of course 240 | ActiveAdmin needs to be installed and working in the parent rails application, 241 | for this to work. 242 | 243 | To use the Qwester ActiveAdmin register files, add this to the active_admin 244 | initializer in your application. 245 | 246 | config.load_paths << Qwester.active_admin_load_path 247 | 248 | See test/dummy/config/initializers/active_admin.rb for an example 249 | 250 | === Local modifications of qwester active admin pages 251 | If you wish to over-ride some of Qwester's active admin registers you will 252 | need to reorder the active_admin load_paths. In this case, use this form of 253 | the load_paths declaration: 254 | 255 | config.load_paths = [Qwester.active_admin_load_path] + config.load_paths 256 | 257 | One side-effect of this that I have been unable to solve, is that if you modify 258 | an over-riding register file while the app is running, the load order is altered 259 | and the qwester register file over-rides the local app's register file until the 260 | app is restarted. Therefore, you have to restart the app before modifications 261 | to over-riding register files take effect. In practice this only affects the 262 | development environment, as in both test and production the app is restarted 263 | after a change. 264 | 265 | === Active admin menus 266 | Links to the admin pages for Qwester models will appear in a 'Qwester' sub-menu. 267 | If you wish to change the name of the menu parent group, add this to an 268 | initializer: 269 | 270 | Qwester.active_admin_menu = 'menu name' 271 | 272 | Alternatively, if you want the Qwester models not to be grouped add this to an 273 | initializer: 274 | 275 | Qwester.active_admin_menu = 'none' 276 | 277 | See test/dummy/config/initializers/qwester.rb for an example 278 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | begin 3 | require 'bundler/setup' 4 | rescue LoadError 5 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 6 | end 7 | begin 8 | require 'rdoc/task' 9 | rescue LoadError 10 | require 'rdoc/rdoc' 11 | require 'rake/rdoctask' 12 | RDoc::Task = Rake::RDocTask 13 | end 14 | 15 | RDoc::Task.new(:rdoc) do |rdoc| 16 | rdoc.rdoc_dir = 'rdoc' 17 | rdoc.title = 'Qwester' 18 | rdoc.options << '--line-numbers' 19 | rdoc.rdoc_files.include('README.rdoc') 20 | rdoc.rdoc_files.include('lib/**/*.rb') 21 | end 22 | 23 | APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) 24 | load 'rails/tasks/engine.rake' 25 | 26 | 27 | 28 | Bundler::GemHelper.install_tasks 29 | 30 | require 'rake/testtask' 31 | 32 | Rake::TestTask.new(:test) do |t| 33 | t.libs << 'lib' 34 | t.libs << 'test' 35 | t.pattern = 'test/**/*_test.rb' 36 | t.verbose = false 37 | end 38 | 39 | 40 | task :default => :test 41 | -------------------------------------------------------------------------------- /app/assets/images/qwester/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/app/assets/images/qwester/.gitkeep -------------------------------------------------------------------------------- /app/assets/javascripts/qwester/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/qwester/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /app/controllers/qwester/application_controller.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class ApplicationController < ActionController::Base 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/qwester/questionnaires_controller.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class QuestionnairesController < ApplicationController 3 | 4 | def index 5 | @questionnaires = current_questionnaires 6 | end 7 | 8 | def show 9 | @questionnaire = Qwester::Questionnaire.find(params[:id]) 10 | end 11 | 12 | def update 13 | @questionnaire = Qwester::Questionnaire.find(params[:id]) 14 | update_qwester_answer_store 15 | if @questionnaire.errors.empty? 16 | redirect_to questionnaires_path 17 | else 18 | render :show 19 | end 20 | end 21 | 22 | def reset 23 | get_qwester_answer_store 24 | @qwester_answer_store.reset 25 | redirect_to questionnaires_path 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/helpers/qwester/application_helper.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | module ApplicationHelper 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/qwester/questionnaires_helper.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | module QuestionnairesHelper 3 | def qwester_answers_selection_list(question, list_class = 'qwester_options') 4 | button_name = "question_id[#{question.id}][answer_ids][]" 5 | answers = question.answers 6 | buttons = answers.collect do |answer| 7 | if question.multi_answer? 8 | check_box_id = "question_id[#{question.id}][answer_ids][#{answer.id}]" 9 | button = check_box_tag( 10 | check_box_id, 11 | answer.id, 12 | answer_checked(answer), 13 | name: button_name 14 | ) 15 | else 16 | button = radio_button_tag(button_name, answer.id, answer_checked(answer)) 17 | end 18 | id = button.match(/id=\"(\w+)\"/)[1] 19 | text = label_tag(id, answer.value) 20 | content_tag('li', "#{button}#{text}".html_safe) 21 | end 22 | content_tag('ul', buttons.join.html_safe, :class => list_class) 23 | end 24 | 25 | def answer_checked(answer) 26 | answer_store_answers.include?(answer) || params_includes_answer(answer) 27 | end 28 | 29 | def answer_store_answers 30 | answer_store = get_qwester_answer_store 31 | answer_store ? answer_store.answers : [] 32 | end 33 | 34 | # params should be of form: "question_id"=>{"1"=>{"answer_ids"=>["1"]}} 35 | def params_includes_answer(answer) 36 | question_ids = params[:question_id] 37 | return nil unless question_ids.kind_of? Hash 38 | answers = question_ids[answer.question_id.to_s] 39 | return nil unless answers.kind_of? Hash 40 | answers.values.flatten.include?(answer.id.to_s) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/models/qwester/answer.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class Answer < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :value, :question_id, :position, :weighting 5 | end 6 | 7 | 8 | DEFAULT_VALUE = 'Not applicable' 9 | STANDARD_VALUES = ['Yes', 'No', DEFAULT_VALUE] 10 | 11 | has_and_belongs_to_many( 12 | :rule_sets, 13 | :join_table => :qwester_answers_rule_sets 14 | ) 15 | 16 | has_and_belongs_to_many( 17 | :answer_stores, 18 | :join_table => :qwester_answer_stores_answers 19 | ) 20 | 21 | belongs_to :question 22 | 23 | acts_as_list :scope => :question 24 | 25 | validates :value, :presence => true 26 | 27 | def self.find_first_or_create(attributes) 28 | where(attributes).first || create(attributes) 29 | end 30 | 31 | def self.standard_values 32 | STANDARD_VALUES 33 | end 34 | 35 | def self.default_value 36 | DEFAULT_VALUE 37 | end 38 | 39 | def self.rule_label_prefix 40 | @rule_label_prefix ||= 'a' 41 | end 42 | 43 | def self.weighting_alias 44 | @weighting_alias 45 | end 46 | 47 | def self.weighting_alias=(name) 48 | if name 49 | @weighting_alias = name 50 | define_method(name.to_sym) {send(:weighting)} 51 | else 52 | remove_weighting_alias 53 | end 54 | end 55 | 56 | def self.weighting_alias_name 57 | name = weighting_alias || :weighting 58 | name.to_s 59 | end 60 | 61 | def self.remove_weighting_alias 62 | if weighting_alias 63 | remove_method weighting_alias.to_sym 64 | @weighting_alias = nil 65 | end 66 | end 67 | 68 | def rule_label 69 | "#{self.class.rule_label_prefix}#{self.id}" 70 | end 71 | end 72 | end -------------------------------------------------------------------------------- /app/models/qwester/answer_store.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class AnswerStore < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible 5 | end 6 | 7 | has_and_belongs_to_many( 8 | :answers, 9 | :class_name => 'Qwester::Answer', 10 | :join_table => :qwester_answer_stores_answers, 11 | :uniq => true 12 | ) 13 | has_and_belongs_to_many( 14 | :questionnaires, 15 | :class_name => 'Qwester::Questionnaire', 16 | :join_table => :qwester_answer_stores_questionnaires, 17 | :uniq => true 18 | ) 19 | 20 | before_save :generate_session_id 21 | 22 | def self.destroy_unpreserved 23 | where("updated_at < '#{1.day.ago.to_s(:db)}' AND preserved IS NULL").destroy_all 24 | end 25 | 26 | def reset 27 | answers.clear 28 | questionnaires.clear 29 | end 30 | 31 | def to_param 32 | session_id 33 | end 34 | 35 | def preserve 36 | make_copy({:preserved => Time.now}, :without_protection => true) 37 | end 38 | 39 | def restore 40 | make_copy 41 | end 42 | 43 | def completed_questionnaires 44 | questionnaires.select{|q| (q.questions.collect(&:id) - completed_question_ids).empty?} 45 | end 46 | 47 | private 48 | 49 | def completed_question_ids 50 | answers.collect(&:question_id) 51 | end 52 | 53 | def generate_session_id 54 | if !self.session_id or self.session_id.empty? 55 | self.session_id = session_id_not_in_database 56 | end 57 | end 58 | 59 | def make_copy(*args) 60 | copy = self.class.create(*args) 61 | copy.answers = answers 62 | copy.questionnaires = questionnaires 63 | return copy if copy.save 64 | end 65 | 66 | def session_id_not_in_database 67 | session_ids = get_session_ids 68 | random_string = RandomString.new(15) until random_string and !session_ids.include?(random_string) 69 | return random_string 70 | end 71 | 72 | def get_session_ids 73 | self.class.select(:session_id).collect(&:session_id) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /app/models/qwester/presentation.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class Presentation < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :description, :name, :title, :questionnaire_ids, :default 5 | end 6 | 7 | has_many( 8 | :presentation_questionnaires, 9 | :order => 'position' 10 | ) 11 | 12 | has_many( 13 | :questionnaires, 14 | :through => :presentation_questionnaires, 15 | :uniq => true, 16 | :order => 'position' 17 | ) 18 | accepts_nested_attributes_for :questionnaires 19 | 20 | before_save :before_save_tasks 21 | after_save :after_save_tasks 22 | 23 | validates( 24 | :name, 25 | :format => { 26 | :with => /\A\w+\z/, 27 | :message => "must comprise letters or numbers with underscores separating words" 28 | }, 29 | :presence => true, 30 | :uniqueness => true 31 | ) 32 | 33 | private 34 | def before_save_tasks 35 | update_title_from_name 36 | end 37 | 38 | def after_save_tasks 39 | undefault_others if self.default? 40 | end 41 | 42 | def undefault_others 43 | current_defaults = self.class.find_all_by_default(true) 44 | current_defaults.each{|p| p.update_attribute(:default, false) unless p == self} 45 | end 46 | 47 | def update_title_from_name 48 | self.title = self.name.humanize unless self.title.present? 49 | end 50 | 51 | def method_missing(symbol, *args, &block) 52 | if symbol.to_sym == :position || acts_as_list_method?(symbol) 53 | pass_acts_as_list_method_to(presentation_questionnaires, symbol, args.first) 54 | else 55 | super 56 | end 57 | end 58 | 59 | # Allows acts_as_list methods to be used within the questionnaire. 60 | # 61 | # Usage: 62 | # 63 | # questionnaire.move_to_top(question) 64 | # questionnaire.last?(question) 65 | # 66 | def pass_acts_as_list_method_to(presentation_questionnaires, symbol, questionnaire) 67 | raise "A Questionnaire is needed to identify the PresentationQuesti" unless questionnaire.kind_of? Questionnaire 68 | presentation_questionnaire = presentation_questionnaires.where(:questionnaire_id => questionnaire.id).first 69 | presentation_questionnaire.send(symbol) if presentation_questionnaire 70 | end 71 | 72 | def acts_as_list_method?(symbol) 73 | methods = ActiveRecord::Acts::List::InstanceMethods.instance_methods + ActiveRecord::Acts::List::InstanceMethods.private_instance_methods 74 | methods.include?(symbol.to_sym) 75 | end 76 | 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /app/models/qwester/presentation_questionnaire.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class PresentationQuestionnaire < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :presentation_id, :questionnaire_id 5 | end 6 | 7 | belongs_to :presentation 8 | belongs_to :questionnaire 9 | 10 | acts_as_list :scope => :presentation 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/qwester/question.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class Question < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :title, :description, :ref, :answers_attributes, :multi_answer 5 | end 6 | 7 | has_many( 8 | :answers, 9 | :order => 'position', 10 | :dependent => :destroy 11 | ) 12 | accepts_nested_attributes_for :answers 13 | 14 | has_many :questionnaires_questions 15 | 16 | has_many( 17 | :questionnaires, 18 | :through => :questionnaires_questions, 19 | :uniq => true 20 | ) 21 | 22 | validates :title, :presence => true 23 | 24 | def build_standard_answers 25 | created_answers = Array.new 26 | Answer.standard_values.each_with_index do |value, index| 27 | answer = answers.find_or_initialize_by_value(value) 28 | answer.position = index + 1 29 | created_answers << answer 30 | end 31 | return created_answers 32 | end 33 | 34 | def create_standard_answers 35 | build_standard_answers.each(&:save) 36 | end 37 | end 38 | end -------------------------------------------------------------------------------- /app/models/qwester/questionnaire.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class Questionnaire < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :title, :description, :button_image, :question_ids, :must_complete 5 | end 6 | 7 | has_many( 8 | :questionnaires_questions, 9 | :order => 'position' 10 | ) 11 | 12 | has_many( 13 | :questions, 14 | :class_name => 'Qwester::Question', 15 | :uniq => true, 16 | :through => :questionnaires_questions, 17 | :order => 'position' 18 | ) 19 | 20 | has_many( 21 | :answers, 22 | :class_name => 'Qwester::Answer', 23 | :through => :questions, 24 | :uniq => true 25 | ) 26 | accepts_nested_attributes_for :answers 27 | 28 | has_and_belongs_to_many( 29 | :answer_stores, 30 | :join_table => :qwester_answer_stores_questionnaires, 31 | :uniq => true 32 | ) 33 | 34 | has_many :presentation_questionnaires 35 | 36 | has_many( 37 | :presentations, 38 | :through => :presentation_questionnaires 39 | ) 40 | 41 | validates :title, :presence => true 42 | 43 | private 44 | def method_missing(symbol, *args, &block) 45 | if symbol.to_sym == :position || acts_as_list_method?(symbol) 46 | pass_acts_as_list_method_to_questionnaires_question(symbol, args.first) 47 | else 48 | super 49 | end 50 | end 51 | 52 | # Allows acts_as_list methods to be used within the questionnaire. 53 | # 54 | # Usage: 55 | # 56 | # questionnaire.move_to_top(question) 57 | # questionnaire.last?(question) 58 | # 59 | def pass_acts_as_list_method_to_questionnaires_question(symbol, question) 60 | raise "A Question is needed to identify the QuestionnairesQuestion" unless question.kind_of? Question 61 | questionnaires_question = questionnaires_questions.where(:question_id => question.id).first 62 | questionnaires_question.send(symbol) if questionnaires_question 63 | end 64 | 65 | def acts_as_list_method?(symbol) 66 | ActiveRecord::Acts::List::InstanceMethods.instance_methods.include?(symbol.to_sym) 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/models/qwester/questionnaires_question.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class QuestionnairesQuestion < ActiveRecord::Base 3 | if Qwester.rails_three? 4 | attr_accessible :position 5 | end 6 | 7 | belongs_to :questionnaire 8 | belongs_to :question 9 | 10 | acts_as_list :scope => :questionnaire 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/qwester/rule_set.rb: -------------------------------------------------------------------------------- 1 | require 'array_logic' 2 | module Qwester 3 | class RuleSet < ActiveRecord::Base 4 | if Qwester.rails_three? 5 | attr_accessible :title, :description, :answers, :url, :rule, :answer_ids, :link_text, :warning_id, :presentation 6 | end 7 | 8 | before_save :keep_answers_in_step_with_rule 9 | 10 | DEFAULT_RULE_JOIN = 'or' 11 | ANSWERS_LIMIT = 10 12 | 13 | has_and_belongs_to_many( 14 | :answers, 15 | :uniq => true, 16 | :join_table => :qwester_answers_rule_sets 17 | ) 18 | accepts_nested_attributes_for :answers 19 | 20 | has_many( 21 | :questions, 22 | :through => :answers 23 | ) 24 | 25 | validate :check_rule_is_valid 26 | validate :check_answers_exist_with_ids_matching_those_in_rule 27 | 28 | validates :title, :presence => true 29 | validates :url, :presence => {:unless => :presentation?} 30 | 31 | 32 | def self.matching(answers) 33 | all.select{|rule_set| rule_set.match(answers)} 34 | end 35 | 36 | def match(answers_to_check = nil) 37 | return unless answers_to_check and !answers_to_check.empty? 38 | generate_default_rule && save 39 | logic.match(answers_to_check) 40 | end 41 | 42 | def logic 43 | @logic || get_logic 44 | end 45 | 46 | def matching_answer_sets 47 | @matching_answer_sets ||= logic.matching_combinations 48 | end 49 | 50 | def blocking_answer_sets 51 | @blocking_answer_set ||= logic.blocking_combinations 52 | end 53 | 54 | def default_rule 55 | answers.collect(&:rule_label).join(" #{DEFAULT_RULE_JOIN} ") 56 | end 57 | 58 | private 59 | def get_logic 60 | @logic = ArrayLogic::Rule.new rule 61 | end 62 | 63 | def generate_default_rule 64 | if (!self.rule or self.rule.empty?) and answers.length > 0 65 | self.rule = default_rule 66 | end 67 | end 68 | 69 | def keep_answers_in_step_with_rule 70 | generate_default_rule 71 | self.answers = get_logic.object_ids_used.collect{|id| Answer.find(id)} # need to use get_logic rather than caching logic 72 | end 73 | 74 | def check_rule_is_valid 75 | if rule? 76 | begin 77 | logic.send :check_rule 78 | rescue => e 79 | errors.add(:rule, "error: #{e.message}") 80 | end 81 | end 82 | end 83 | 84 | def answer_ids_in_rule 85 | rule? ? logic.object_ids_used : [] 86 | end 87 | 88 | def answers_ids_in_database_also_found_in_rule 89 | Qwester::Answer.where(id: answer_ids_in_rule).pluck(:id) 90 | end 91 | 92 | def check_answers_exist_with_ids_matching_those_in_rule 93 | mismatch = answer_ids_in_rule - answers_ids_in_database_also_found_in_rule 94 | return if mismatch.empty? 95 | mismatch_report = mismatch.collect{|a| "a#{a}"}.join(", ") 96 | errors.add(:rule, "Answer labels [#{mismatch_report}] do not have corresponding answers in database") 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /app/views/layouts/qwester/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Qwester 5 | <%= stylesheet_link_tag "qwester/application", :media => "all" %> 6 | <%= javascript_include_tag "qwester/application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/views/qwester/questionnaires/_errors.html.erb: -------------------------------------------------------------------------------- 1 | <% if @questionnaire and @questionnaire.errors.present? %> 2 |
3 |

Error detected

4 |
9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/qwester/questionnaires/index.html.erb: -------------------------------------------------------------------------------- 1 |

Questionnaires

2 |
3 | <% @questionnaires.each do |questionnaire| %> 4 |
5 | <%= link_to( 6 | questionnaire.title, 7 | questionnaire_path(questionnaire), 8 | :class => 'text_link' 9 | ) 10 | %> 11 |
12 | <% end %> 13 |
14 | 15 | <% if @qwester_rule_sets.present? %> 16 |

Matching rule sets

17 | 25 | <% end %> 26 | 27 | <%= 28 | content_tag( 29 | 'p', 30 | link_to('Clear answers and start again', reset_questionnaires_path) 31 | ) if @qwester_answer_store and @qwester_answer_store.answers.present? 32 | %> 33 | -------------------------------------------------------------------------------- /app/views/qwester/questionnaires/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @questionnaire.title %>

2 | 3 | <% if @questionnaire.description.present? %> 4 | <%= content_tag('div', sanitize(@questionnaire.description)) %> 5 | <% else %> 6 |

Questions

7 | <% end %> 8 | <%= render 'errors' %> 9 | <%= form_for(@questionnaire, :url => questionnaire_path(@questionnaire)) do |form| %> 10 | 27 | 28 |

29 | <%= form.submit('Submit') %> 30 | <%= link_to('Back to the menu', questionnaires_path) %> 31 |

32 | <% end %> 33 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Qwester::Engine.routes.draw do 2 | 3 | root :to => 'questionnaires#index' 4 | 5 | resources :questionnaires, :only => [:index, :show, :update] do 6 | collection do 7 | get :reset 8 | end 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121008151526_create_questions.rb: -------------------------------------------------------------------------------- 1 | class CreateQuestions < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_questions do |t| 4 | t.string :title 5 | t.text :description 6 | t.integer :parent_id 7 | t.integer :lft 8 | t.integer :rgt 9 | t.integer :depth 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20121008152106_create_answers.rb: -------------------------------------------------------------------------------- 1 | class CreateAnswers < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_answers do |t| 4 | t.integer :question_id 5 | t.boolean :value 6 | t.timestamps 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /db/migrate/20121008153758_create_rule_sets.rb: -------------------------------------------------------------------------------- 1 | class CreateRuleSets < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_rule_sets do |t| 4 | t.string :title 5 | t.text :description 6 | t.string :url 7 | t.timestamps 8 | end 9 | 10 | create_table :qwester_answers_rule_sets, :id => false do |t| 11 | t.integer :answer_id 12 | t.integer :rule_set_id 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20121009080219_create_questionnaires.rb: -------------------------------------------------------------------------------- 1 | class CreateQuestionnaires < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_questionnaires do |t| 4 | t.string :title 5 | t.text :description 6 | t.timestamps 7 | end 8 | 9 | create_table :qwester_questionnaires_questions, :id => false do |t| 10 | t.integer :questionnaire_id 11 | t.integer :question_id 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20121009095600_create_answer_stores.rb: -------------------------------------------------------------------------------- 1 | class CreateAnswerStores < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_answer_stores do |t| 4 | t.string :session_id 5 | t.timestamps 6 | end 7 | 8 | create_table :qwester_answer_stores_answers, :id => false do |t| 9 | t.integer :answer_id 10 | t.integer :answer_store_id 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20121009160807_add_questionnaire_id_to_answers.rb: -------------------------------------------------------------------------------- 1 | class AddQuestionnaireIdToAnswers < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_answers, :questionnaire_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121009161206_change_answers_value_to_string.rb: -------------------------------------------------------------------------------- 1 | class ChangeAnswersValueToString < ActiveRecord::Migration 2 | def up 3 | change_column(:qwester_answers, :value, :string) 4 | end 5 | 6 | def down 7 | change_column(:qwester_answers, :value, :boolean) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20121019111051_add_button_image_to_questionnaires.rb: -------------------------------------------------------------------------------- 1 | require 'paperclip' 2 | ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Paperclip::Schema::Statements 3 | class AddButtonImageToQuestionnaires < ActiveRecord::Migration 4 | def change 5 | add_attachment :qwester_questionnaires, :button_image 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20121114084347_create_ckeditor_assets.rb: -------------------------------------------------------------------------------- 1 | class CreateCkeditorAssets < ActiveRecord::Migration 2 | def self.up 3 | create_table :qwester_ckeditor_assets do |t| 4 | t.string :data_file_name, :null => false 5 | t.string :data_content_type 6 | t.integer :data_file_size 7 | 8 | t.integer :assetable_id 9 | t.string :assetable_type, :limit => 30 10 | t.string :type, :limit => 30 11 | 12 | # Uncomment it to save images dimensions, if your need it 13 | t.integer :width 14 | t.integer :height 15 | 16 | t.timestamps 17 | end 18 | 19 | add_index "qwester_ckeditor_assets", ["assetable_type", "type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable_type" 20 | add_index "qwester_ckeditor_assets", ["assetable_type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable" 21 | end 22 | 23 | def self.down 24 | drop_table :qwester_ckeditor_assets 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20121115114408_add_ref_to_questions.rb: -------------------------------------------------------------------------------- 1 | class AddRefToQuestions < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_questions, :ref, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121120092108_add_ids_to_questionnaires_questions.rb: -------------------------------------------------------------------------------- 1 | class AddIdsToQuestionnairesQuestions < ActiveRecord::Migration 2 | def up 3 | add_column qq_table, :id, :primary_key 4 | add_column qq_table, :position, :integer 5 | add_column qq_table, :created_at, :datetime 6 | add_column qq_table, :updated_at, :datetime 7 | 8 | execute "UPDATE #{qq_table} SET created_at = '#{Time.now.to_s(:db)}', updated_at = '#{Time.now.to_s(:db)}';" 9 | 10 | puts "==============================================" 11 | puts "Now run 'rake qwester:reset_positions RAILS_ENV=#{Rails.env}' to set the positions" 12 | puts "==============================================" 13 | end 14 | 15 | def down 16 | remove_column qq_table, :id 17 | remove_column qq_table, :position 18 | remove_column qq_table, :created_at 19 | remove_column qq_table, :updated_at 20 | end 21 | 22 | def qq_table 23 | :qwester_questionnaires_questions 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /db/migrate/20121122130930_add_position_to_answers.rb: -------------------------------------------------------------------------------- 1 | class AddPositionToAnswers < ActiveRecord::Migration 2 | def change 3 | # Adds unless clause, because original version of migration added another field 4 | # and file name was changed when this field was removed. This can cause this 5 | # migration to appear a second time if rake qwester:install:migrations is 6 | # run again in an app created before the change was made. 7 | unless column_exists?(:qwester_answers, :position) 8 | add_column :qwester_answers, :position, :integer 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121122141628_remove_questionnaire_from_answer.rb: -------------------------------------------------------------------------------- 1 | class RemoveQuestionnaireFromAnswer < ActiveRecord::Migration 2 | def up 3 | remove_column :qwester_answers, :questionnaire_id 4 | end 5 | 6 | def down 7 | add_column :qwester_answers, :questionnaire_id, :integer 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20121126152146_add_rule_to_rule_sets.rb: -------------------------------------------------------------------------------- 1 | class AddRuleToRuleSets < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_rule_sets, :rule, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121203150555_add_multi_answer_to_questions.rb: -------------------------------------------------------------------------------- 1 | class AddMultiAnswerToQuestions < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_questions, :multi_answer, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121204082213_create_answer_stores_questionnaires.rb: -------------------------------------------------------------------------------- 1 | class CreateAnswerStoresQuestionnaires < ActiveRecord::Migration 2 | def change 3 | 4 | create_table :qwester_answer_stores_questionnaires, :id => false do |t| 5 | t.integer :questionnaire_id 6 | t.integer :answer_store_id 7 | end 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20121204100803_add_link_text_to_rule_sets.rb: -------------------------------------------------------------------------------- 1 | class AddLinkTextToRuleSets < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_rule_sets, :link_text, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130306114331_add_preserved_to_answer_stores.rb: -------------------------------------------------------------------------------- 1 | class AddPreservedToAnswerStores < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_answer_stores, :preserved, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130315112847_create_qwester_presentations.rb: -------------------------------------------------------------------------------- 1 | class CreateQwesterPresentations < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_presentations do |t| 4 | t.string :name 5 | t.string :title 6 | t.text :description 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130315113027_create_qwester_presentation_questionnaires.rb: -------------------------------------------------------------------------------- 1 | class CreateQwesterPresentationQuestionnaires < ActiveRecord::Migration 2 | def change 3 | create_table :qwester_presentation_questionnaires do |t| 4 | t.integer :questionnaire_id 5 | t.integer :presentation_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130318102537_add_presentation_to_rule_sets.rb: -------------------------------------------------------------------------------- 1 | class AddPresentationToRuleSets < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_rule_sets, :presentation, :string 4 | add_column :qwester_presentations, :default, :boolean 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20130402074240_add_weighting_to_qwester_answers.rb: -------------------------------------------------------------------------------- 1 | class AddWeightingToQwesterAnswers < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_answers, :weighting, :float, :default => 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130502082540_add_position_to_qwester_presentation_questionnaires.rb: -------------------------------------------------------------------------------- 1 | class AddPositionToQwesterPresentationQuestionnaires < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_presentation_questionnaires, :position, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131119142009_add_must_complete_to_qwester_questionnaires.rb: -------------------------------------------------------------------------------- 1 | class AddMustCompleteToQwesterQuestionnaires < ActiveRecord::Migration 2 | def change 3 | add_column :qwester_questionnaires, :must_complete, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/active_admin/admin/answers.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | 3 | ActiveAdmin.register Answer do 4 | 5 | menu_label = 'Answers' 6 | menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu 7 | menu :parent => Qwester.active_admin_menu, :label => menu_label 8 | 9 | actions :all, :except => [:edit, :new] 10 | config.batch_actions = false 11 | 12 | filter :question 13 | filter :value 14 | filter :created_at 15 | filter :updated_at 16 | filter :position 17 | filter :weighting, :label => Answer.weighting_alias_name.humanize 18 | 19 | 20 | index do 21 | column :id 22 | column :value 23 | column 'Question (edit answer via question)', :question do |answer| 24 | link_to(answer.question.title, edit_admin_qwester_question_path(answer.question)) if answer.question 25 | end 26 | column Answer.weighting_alias_name.humanize, :weighting 27 | column :position 28 | default_actions 29 | end 30 | 31 | show do |ad| 32 | attributes_table do 33 | row :question 34 | row :value 35 | row :updated_at 36 | row :position 37 | row Answer.weighting_alias_name.humanize do |answer| 38 | answer.weighting 39 | end 40 | end 41 | active_admin_comments 42 | end 43 | 44 | member_action :move_up do 45 | answer = Answer.find(params[:id]) 46 | answer.move_higher 47 | redirect_to admin_qwester_question_path(answer.question) 48 | end 49 | 50 | member_action :move_down do 51 | answer = Answer.find(params[:id]) 52 | answer.move_lower 53 | redirect_to admin_qwester_question_path(answer.question) 54 | end 55 | 56 | member_action :remove do 57 | answer = Answer.find(params[:id]) 58 | answer.destroy 59 | redirect_to admin_qwester_question_path(answer.question) 60 | end 61 | 62 | 63 | end if defined?(ActiveAdmin) 64 | 65 | end -------------------------------------------------------------------------------- /lib/active_admin/admin/presentations.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | ActiveAdmin.register Presentation do 3 | 4 | menu_label = 'Presentations' 5 | menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu 6 | menu :parent => Qwester.active_admin_menu, :label => menu_label 7 | 8 | config.batch_actions = false 9 | 10 | index do 11 | column :name do |presentation| 12 | link_to presentation.name, admin_qwester_presentation_path(presentation) 13 | end 14 | column :title 15 | column :default do |presentation| 16 | 'default' if presentation.default? 17 | end 18 | column :questionnaires do |presentation| 19 | presentation.questionnaires.collect(&:title).join(', ') 20 | end 21 | default_actions 22 | end 23 | 24 | show do 25 | h2 qwester_presentation.title 26 | div sanitize(qwester_presentation.description) if qwester_presentation.description.present? 27 | qwester_presentation.questionnaires.each do |questionnaire| 28 | div(:style => 'display:inline-block;margin-right:20px;') do 29 | para image_tag(questionnaire.button_image.url(:thumbnail)) 30 | para questionnaire.title 31 | move_up_link = " " 32 | move_down_link = " " 33 | unless qwester_presentation.first?(questionnaire) 34 | move_up_link = link_to('<', move_up_admin_qwester_presentation_path(qwester_presentation, :questionnaire_id => questionnaire.id)) 35 | end 36 | unless qwester_presentation.last?(questionnaire) 37 | move_down_link = link_to('>', move_down_admin_qwester_presentation_path(qwester_presentation, :questionnaire_id => questionnaire.id)) 38 | end 39 | para [move_up_link, 'move', move_down_link].join(' ').html_safe 40 | end 41 | end 42 | para "Default: Will be inital presentation of quesitonnaires" if qwester_presentation.default? 43 | end 44 | 45 | form do |f| 46 | f.inputs "Details" do 47 | f.input :name 48 | f.input :title, :label => "Title (or create from name)" 49 | f.input :default, :label => 'Set as default (that is, the first presentation displayed to a user). If no default, all questionnaires will be displayed' 50 | if defined?(Ckeditor) 51 | f.input :description, :as => :ckeditor, :input_html => { :height => 100} 52 | else 53 | f.input :description, :input_html => { :rows => 3} 54 | end 55 | f.input :questionnaires, :as => :check_boxes, :collection => Questionnaire.all 56 | end 57 | f.actions 58 | end 59 | 60 | controller do 61 | def permitted_params 62 | params.permit( 63 | qwester_presentation: [ 64 | :name, :title, :default, :description, 65 | {questionnaire_ids: []} 66 | ] 67 | ) 68 | end 69 | end unless Qwester.rails_three? 70 | 71 | member_action :move_up do 72 | presentation = Presentation.find(params[:id]) 73 | questionnaire = Questionnaire.find(params[:questionnaire_id]) 74 | presentation.move_higher(questionnaire) 75 | redirect_to admin_qwester_presentation_path(presentation) 76 | end 77 | 78 | member_action :move_down do 79 | presentation = Presentation.find(params[:id]) 80 | questionnaire = Questionnaire.find(params[:questionnaire_id]) 81 | presentation.move_lower(questionnaire) 82 | redirect_to admin_qwester_presentation_path(presentation) 83 | end 84 | 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/active_admin/admin/questionnaires.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | 3 | ActiveAdmin.register Questionnaire do 4 | 5 | menu_label = 'Questionnaires' 6 | menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu 7 | menu :parent => Qwester.active_admin_menu, :label => menu_label 8 | 9 | config.batch_actions = false 10 | 11 | index do 12 | column :title 13 | column :questions do |questionnaire| 14 | questionnaire.questions.count 15 | end 16 | column :must_complete do |questionnaire| 17 | questionnaire.must_complete? ? 'Yes' : 'No' 18 | end 19 | default_actions 20 | end 21 | 22 | form do |f| 23 | f.inputs "Details" do 24 | f.input :title 25 | if defined?(Ckeditor) 26 | f.input :description, :as => :ckeditor, :input_html => { :height => 100 } 27 | else 28 | f.input :description, :input_html => { :rows => 3} 29 | end 30 | f.input :must_complete 31 | f.input :questions, :as => :check_boxes, :collection => Question.all 32 | end 33 | f.actions 34 | end 35 | 36 | controller do 37 | def permitted_params 38 | params.permit( 39 | qwester_questionnaire: [ 40 | :title, :description, :button_image, :must_complete, 41 | {question_ids: []} 42 | ] 43 | ) 44 | end 45 | end unless Qwester.rails_three? 46 | 47 | show do 48 | div do 49 | sanitize(qwester_questionnaire.description.html_safe ) if qwester_questionnaire.description.present? 50 | end 51 | 52 | div do 53 | h3 'Questions' 54 | para("#{qwester_questionnaire.must_complete? ? 'A' : 'Not a'}ll must be completed") 55 | ul :id => 'question_list' 56 | qwester_questionnaire.questions.each do |question| 57 | li do 58 | text = [question.title] 59 | text << link_to('Up', move_up_admin_qwester_questionnaire_path(qwester_questionnaire, :question_id => question)) unless qwester_questionnaire.first?(question) 60 | text << link_to('Down', move_down_admin_qwester_questionnaire_path(qwester_questionnaire, :question_id => question)) unless qwester_questionnaire.last?(question) 61 | text.join(' ').html_safe 62 | end 63 | end 64 | end 65 | end 66 | 67 | member_action :move_up do 68 | questionnaire = Questionnaire.find(params[:id]) 69 | question = Question.find(params[:question_id]) 70 | questionnaire.move_higher(question) 71 | redirect_to admin_qwester_questionnaire_path(questionnaire) 72 | end 73 | 74 | member_action :move_down do 75 | questionnaire = Questionnaire.find(params[:id]) 76 | question = Question.find(params[:question_id]) 77 | questionnaire.move_lower(question) 78 | redirect_to admin_qwester_questionnaire_path(questionnaire) 79 | end 80 | 81 | end if defined?(ActiveAdmin) 82 | 83 | end 84 | -------------------------------------------------------------------------------- /lib/active_admin/admin/questions.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | 3 | ActiveAdmin.register Question do 4 | 5 | menu_label = 'Questions' 6 | menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu 7 | menu :parent => Qwester.active_admin_menu, :label => menu_label 8 | 9 | config.batch_actions = false 10 | 11 | index do 12 | column :ref 13 | column :title do |qwester_question| 14 | link_to(qwester_question.title, '#', :title => qwester_question.description, :class => 'no_decoration') 15 | end 16 | column :answers do |qwester_question| 17 | qwester_question.answers.count 18 | end 19 | column :multi_answer do |qwester_question| 20 | qwester_question.multi_answer? ? 'Any' : 'One' 21 | end 22 | default_actions 23 | end 24 | 25 | show do 26 | para("References: #{qwester_question.ref}") if qwester_question.ref.present? 27 | para(qwester_question.description) 28 | h3 "Answers" 29 | para "User can select one#{' or many' if qwester_question.multi_answer?} of:" 30 | 31 | table :class => 'sortable_list' do 32 | tr do 33 | th "Value (#{Answer.weighting_alias_name.humanize})" 34 | end 35 | qwester_question.answers.each do |answer| 36 | tr do 37 | td "#{answer.value} (#{answer.weighting})" 38 | td(answer.first? ? " ".html_safe : link_to('Up', move_up_admin_qwester_answer_path(answer))) 39 | td(answer.last? ? " ".html_safe : link_to('Down', move_down_admin_qwester_answer_path(answer))) 40 | td link_to('Delete', remove_admin_qwester_answer_path(answer), :confirm => "Are you sure you want to delete the answer '#{answer.value}'?") 41 | end 42 | end 43 | end 44 | end 45 | 46 | form do |f| 47 | f.inputs "Details" do 48 | f.input :ref, :label => 'Reference' 49 | f.input :title 50 | f.input :description, :input_html => { :rows => 2} 51 | f.input :multi_answer, :label => 'User may select more than one answer to this question?' 52 | end 53 | 54 | f.inputs do 55 | f.has_many :answers do |answer_form| 56 | answer_form.input :value 57 | answer_form.input :position 58 | answer_form.input :weighting, :label => Answer.weighting_alias_name.humanize 59 | end 60 | end 61 | 62 | f.actions 63 | end 64 | 65 | controller do 66 | 67 | def new 68 | @qwester_question = Question.new 69 | @qwester_question.build_standard_answers 70 | end 71 | 72 | end 73 | 74 | controller do 75 | 76 | def permitted_params 77 | params.permit( 78 | qwester_question: [ 79 | :ref, :title, :description, :multi_answer, 80 | {answers_attributes: [:value, :position, :weighting]} 81 | ] 82 | ) 83 | end 84 | 85 | end unless Qwester.rails_three? 86 | 87 | end if defined?(ActiveAdmin) 88 | 89 | end 90 | -------------------------------------------------------------------------------- /lib/active_admin/admin/rule_sets.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | 3 | ActiveAdmin.register RuleSet do 4 | 5 | menu_label = 'Rule Sets' 6 | menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu 7 | menu :parent => Qwester.active_admin_menu, :label => menu_label 8 | 9 | config.batch_actions = false 10 | 11 | index do 12 | column :title 13 | column :presentation 14 | column :answers do |rule_set| 15 | rule_set.answers.count 16 | end 17 | column :rule 18 | default_actions 19 | end 20 | 21 | sidebar :rule_syntax do 22 | para 'R1 = "(a1 and a2) or (a3 and a4)"' 23 | 24 | para 'R2 = "a1 and not a2"' 25 | 26 | para 'R3 = "sum(:id) == 7"' 27 | 28 | para 'R4 = "(2 in a1 a2 a3) and (average(:id) == 3)"' 29 | 30 | table(:class => 'small') do 31 | tr do 32 | th "Input" 33 | th "R1" 34 | th "R2" 35 | th "R3" 36 | th "R4" 37 | end 38 | tr do 39 | th "[a1, a2]" 40 | th "true" 41 | th "false" 42 | th "false" 43 | th "false" 44 | end 45 | tr do 46 | th "[a3, a4]" 47 | th "true" 48 | th "false" 49 | th "true" 50 | th "false" 51 | end 52 | tr do 53 | th "[a1, a3, a5]" 54 | th "false" 55 | th "true" 56 | th "false" 57 | th "true" 58 | end 59 | end 60 | 61 | para link_to('Rule engine documentation', 'https://github.com/reggieb/array_logic', :target => '_blank') 62 | 63 | end 64 | 65 | sidebar :presentations do 66 | para < :ckeditor, :input_html => { :height => 100 } 89 | else 90 | f.input :description, :input_html => { :rows => 3} 91 | end 92 | end 93 | 94 | f.inputs "Output Link" do 95 | f.input :url 96 | f.input :link_text 97 | end 98 | 99 | f.inputs "Change Presentation of Questionnaires" do 100 | f.input :presentation, :as => :select, :collection => Presentation.all.collect(&:name), :include_blank => 'No effect on presentation' 101 | end 102 | 103 | f.inputs "Logic" do 104 | f.input :rule, :input_html => { :rows => 3} 105 | end 106 | 107 | f.inputs("Questions") do 108 | 109 | if qwester_rule_set.questions.empty? 110 | 111 | f.input( 112 | :answers, 113 | :as => :check_boxes, 114 | :member_label => lambda {|a| "a#{a.id}: #{a.value} to '#{a.question.title}'"}, 115 | :input_html => { :size => 20, :multiple => true} 116 | ) 117 | 118 | else 119 | 120 | questions = qwester_rule_set.questions | Question.all 121 | questions.collect! do |question| 122 | style = 'border:#CCCCCC 1px solid;' 123 | html = [content_tag('td', question.title, :style => style)] 124 | answers = question.answers.collect do |answer| 125 | answer_style = style 126 | answer_style += 'background-color:#005C1F;color:white;font-weight:bold;' if qwester_rule_set.answers.include?(answer) 127 | content_tag('td', "a#{answer.id} #{answer.value}".html_safe, :style => answer_style).html_safe 128 | end 129 | html << answers.join(" ").html_safe 130 | content_tag('tr', html.join(" ").html_safe) 131 | end 132 | table_class = ["selection"] 133 | table_class << 'associated_questions' if qwester_rule_set.id.present? 134 | content_tag 'li', content_tag('table', questions.join("\n").html_safe, :class => table_class.join(' ')) 135 | 136 | end 137 | 138 | end 139 | 140 | f.actions 141 | end 142 | 143 | controller do 144 | def permitted_params 145 | params.permit( 146 | qwester_rule_set: [ 147 | :title, :description, :url, :link_text, :presentation, :rule, 148 | {answer_ids: []} 149 | ] 150 | ) 151 | end 152 | end unless Qwester.rails_three? 153 | 154 | show do 155 | div do 156 | para sanitize(qwester_rule_set.description) 157 | end if qwester_rule_set.description.present? 158 | 159 | div do 160 | h3 'Target url' 161 | para link_to qwester_rule_set.url 162 | para qwester_rule_set.link_text? ? qwester_rule_set.link_text : 'No link text specified' 163 | end 164 | 165 | div do 166 | h3 'Presentation' 167 | if qwester_rule_set.presentation? 168 | para "The presentations of questionnaires should change to: #{qwester_rule_set.presentation}" 169 | else 170 | para "The presentations of questionnaires should be unaffected by this rule" 171 | end 172 | end 173 | 174 | div do 175 | h3 'The rule' 176 | para qwester_rule_set.rule 177 | end 178 | 179 | begin 180 | if qwester_rule_set.matching_answer_sets.present? 181 | div do 182 | h3 'Sample matching answer sets' 183 | if qwester_rule_set.matching_answer_sets.length 184 | para "There are at least #{qwester_rule_set.matching_answer_sets.length} combinations of answers that would pass this test." 185 | end 186 | para 'The following combinations of answers would pass' 187 | qwester_rule_set.matching_answer_sets.each do |answer_set| 188 | ul :style => 'border:#CCCCCC 1px solid;padding:5px;list-style:none;' do 189 | answer_set.each do |answer_id| 190 | next unless Answer.exists?(answer_id) 191 | answer = Answer.find(answer_id) 192 | question_summary = [answer.value, answer.question.title].join(' : ') 193 | li "(a#{answer_id}) #{question_summary}" 194 | end 195 | end 196 | end 197 | end 198 | else 199 | div do 200 | h3 'Matching answer sets' 201 | para 'Answers will pass unless they contain a blocking answer set' 202 | end 203 | end 204 | 205 | if qwester_rule_set.blocking_answer_sets.present? 206 | div do 207 | h3 'Sample blocking answer sets' 208 | para 'The following combinations of answers would not pass' 209 | qwester_rule_set.blocking_answer_sets.each do |answer_set| 210 | ul :style => 'border:#CCCCCC 1px solid;padding:5px;list-style:none;' do 211 | answer_set.each do |answer_id| 212 | next unless Answer.exists?(answer_id) 213 | answer = Answer.find(answer_id) 214 | question_summary = [answer.value, answer.question.title].join(' : ') 215 | li "(a#{answer_id}) #{question_summary}" 216 | end 217 | end 218 | end 219 | end 220 | else 221 | div do 222 | h3 'Blocking answer sets' 223 | para 'Answers will only pass if they contain a matching answer set' 224 | end 225 | end 226 | rescue ArrayLogic::UnableToDetermineCombinationsError => e 227 | h3 'Sample combinations' 228 | para e.message 229 | end 230 | 231 | 232 | end 233 | 234 | end if defined?(ActiveAdmin) 235 | 236 | end -------------------------------------------------------------------------------- /lib/qwester.rb: -------------------------------------------------------------------------------- 1 | require "qwester/engine" 2 | require 'acts_as_list' 3 | require 'random_string' 4 | require_relative 'rails/actionpack/lib/action_controller/base' 5 | 6 | module Qwester 7 | 8 | def self.active_admin_load_path 9 | File.expand_path("active_admin/admin", File.dirname(__FILE__)) 10 | end 11 | 12 | def self.active_admin_menu 13 | if @active_admin_menu == 'none' 14 | return nil 15 | elsif @active_admin_menu 16 | @active_admin_menu 17 | else 18 | 'Qwester' 19 | end 20 | end 21 | 22 | def self.active_admin_menu=(menu) 23 | @active_admin_menu = menu 24 | end 25 | 26 | def self.session_key 27 | @session_key || :qwester_answer_store 28 | end 29 | 30 | def self.session_key=(key) 31 | @session_key = key 32 | end 33 | 34 | def self.rails_version 35 | @rails_version ||= Rails.version.split('.').first if defined? Rails 36 | end 37 | 38 | def self.rails_three? 39 | rails_version == '3' 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /lib/qwester/engine.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | class Engine < ::Rails::Engine 3 | isolate_namespace Qwester 4 | 5 | initializer 'qwester.action_controller' do |app| 6 | ActiveSupport.on_load :action_controller do 7 | 8 | helper Qwester::QuestionnairesHelper 9 | 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/qwester/version.rb: -------------------------------------------------------------------------------- 1 | module Qwester 2 | VERSION = "0.5.0" 3 | end 4 | 5 | # History 6 | # ======= 7 | # 0.5.0 - Removes Paperclip as a dependency of this engine. 8 | # Please note that the ActiveAdmin styling in the test/dummy app no longer loads. 9 | # This shouldn't alter the behaviour of this engine, as ActiveAdmin is given here 10 | # as an example. 11 | # 12 | # 0.4.0 - Updates version of paperclip used to avoid recent vulnerability 13 | # 14 | # 0.3.5 - Fixes typo in README (yes ... the same one!) 15 | # 16 | # 0.3.4 - Fixes typo in test/dummy/config/routes.rb 17 | # 18 | # 0.3.3 - Applies labels to checkbox and radio button texts within standard 19 | # questionnaire forms. 20 | # 21 | # 0.3.2 - Fixes issues where rule_set rule references answers that do not exist. 22 | # 23 | # 0.3.1 - Adds facility to mark as questionnaire as 'must complete' 24 | # When set, all questions must be answered before a questionnaire can be 25 | # successfully submitted. 26 | # 27 | # 0.3.0 - Allows qwester to be hosted in a rails 4 app 28 | # 29 | # 0.2.2 - Removes require statements not needed in production 30 | # Some gems needed to be required to run the dummy app, but these may have 31 | # caused some javascript and/or asset pipeline errors in production apps. 32 | # 33 | # 0.2.1 - Allows questionnaires to be positioned within a presentation 34 | # 35 | # 0.2.0 - Adds weighting to answers 36 | # Weighting can be used to give some answers greater weight in comparisons. 37 | # Weighting can be aliased. 38 | # 39 | # 0.1.4 - Bug fix update 40 | # Rescues exception in admin_rules_sets#show due to rule including a function 41 | # 42 | # 0.1.3 - Update to use version of Array Logic that supports functions. 43 | # Allows RuleSet#rule to be defined using sum, average or count functions 44 | # 45 | # 0.1.2 - Modification to admin pages 46 | # Changes CKeditor to use default style. 47 | # 48 | # 0.1.1 - Makes presentation object available to controller and views 49 | # @presentation used to store current Presentation 50 | # 51 | # 0.1.0 - Add presentations as a way of controlling the display of questionnaires 52 | # Allows admin to define groups of questionnaires as presentation views 53 | # and rule sets that will display a presentation when triggered. In that way 54 | # the list of questionnaires being displayed can change as questionnaires are 55 | # submitted. 56 | # 57 | # 0.0.9 - Maintenance update 58 | # Removes cope_index from migrations. 59 | # Ensures answer_store#session_id is unique 60 | # 61 | # 0.0.8 - Add facility to preserve answer stores 62 | # This allows snaps shots to be taken, and redundant answer stores to be 63 | # removed during routine maintenance. 64 | # 65 | # 0.0.7 - Improves deployment process 66 | # Fixes a bug in one of the migrations and updates the README 67 | # 68 | # 0.0.6 - Bug fix update 69 | # Fixes issue where error raised if user submits a questionnaire without 70 | # selecting and answer. 71 | # 72 | # 0.0.5 - Bug fix update 73 | # Fixes issue where active admin rule set form did not include link text field. 74 | # 75 | -------------------------------------------------------------------------------- /lib/rails/actionpack/lib/action_controller/base.rb: -------------------------------------------------------------------------------- 1 | module ActionController 2 | class Base < Metal 3 | 4 | protected 5 | def update_qwester_answer_store 6 | if params_includes_answers? 7 | get_qwester_answer_store(true) 8 | ActiveRecord::Base.transaction do 9 | add_answers_to_qwester_answer_store 10 | add_questionnaire_to_qwester_answer_store 11 | ensure_complete_all_questionnaires_completed 12 | end 13 | end 14 | end 15 | 16 | def get_qwester_answer_store(create_new = false) 17 | if session[Qwester.session_key] 18 | current_qwester_answer_store 19 | elsif create_new 20 | new_qwester_answer_store 21 | end 22 | end 23 | helper_method :get_qwester_answer_store 24 | 25 | def current_qwester_answer_store 26 | @qwester_answer_store = Qwester::AnswerStore.find_by_session_id(session[Qwester.session_key]) 27 | end 28 | 29 | def new_qwester_answer_store 30 | set_qwester_answer_store Qwester::AnswerStore.create 31 | end 32 | 33 | def set_qwester_answer_store(answer_store) 34 | @qwester_answer_store = answer_store 35 | session[Qwester.session_key] = @qwester_answer_store.session_id 36 | @qwester_answer_store 37 | end 38 | 39 | def match_rule_sets 40 | if get_qwester_answer_store 41 | @qwester_rule_sets = Qwester::RuleSet.matching(@qwester_answer_store.answers) 42 | get_presentation_from_rule_sets 43 | return @qwester_rule_sets 44 | end 45 | end 46 | alias_method :matching_rule_sets, :match_rule_sets 47 | 48 | def current_questionnaires 49 | match_rule_sets 50 | presentation_questionnaires || default_presentation_questionnaires || Qwester::Questionnaire.all 51 | end 52 | 53 | def presentation_questionnaires 54 | @presentation = Qwester::Presentation.find_by_name(session[:presentations].last) if session[:presentations] 55 | @presentation.questionnaires if @presentation 56 | end 57 | 58 | def default_presentation_questionnaires 59 | @presentation = Qwester::Presentation.find_by_default(true) 60 | @presentation.questionnaires if @presentation 61 | end 62 | 63 | def get_presentation_from_rule_sets 64 | @qwester_rule_sets.clone.each do |rule_set| 65 | next unless rule_set.presentation? 66 | add_presentation_to_session rule_set.presentation 67 | @qwester_rule_sets.delete(rule_set) 68 | end 69 | end 70 | 71 | def add_presentation_to_session(presentation) 72 | session_presentations = session[:presentations] || [] 73 | unless session_presentations.include? presentation 74 | session_presentations << presentation 75 | session[:presentations] = session_presentations 76 | end 77 | end 78 | 79 | def add_answers_to_qwester_answer_store 80 | answers = params[:question_id].values.collect do |question_values| 81 | question_values[:answer_ids].collect{|id| Qwester::Answer.find(id)} 82 | end 83 | answers.flatten! 84 | remove_answers_to_questions_answered_with(answers) if answers.present? 85 | @qwester_answer_store.answers = (@qwester_answer_store.answers | answers) 86 | end 87 | 88 | def params_includes_answers? 89 | params[:question_id].kind_of?(Hash) and params[:question_id].values.present? 90 | end 91 | 92 | def remove_answers_to_questions_answered_with(answers) 93 | question_ids = answers.collect(&:question_id).uniq 94 | answers_to_delete = @qwester_answer_store.answers.collect {|a| a if question_ids.include? a.question_id.to_i}.compact 95 | @qwester_answer_store.answers.delete(answers_to_delete) 96 | end 97 | 98 | def add_questionnaire_to_qwester_answer_store 99 | @qwester_answer_store.questionnaires << @questionnaire 100 | end 101 | 102 | def ensure_complete_all_questionnaires_completed 103 | if @questionnaire.must_complete? and !@qwester_answer_store.completed_questionnaires.include?(@questionnaire) 104 | @questionnaire.errors.add(:base, "All questions must be answered in this questionnaire") 105 | end 106 | raise ActiveRecord::Rollback unless @questionnaire.errors.empty? 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/random_string.rb: -------------------------------------------------------------------------------- 1 | class RandomString < String 2 | def initialize(length = 40) 3 | super(new_random_string(length)) 4 | end 5 | 6 | 7 | private 8 | def new_random_string(length = 40) 9 | string = "" 10 | string += random_string until string.length >= length 11 | return string[0, length] 12 | end 13 | 14 | def random_string 15 | string_pairs = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ).scan(/.{2}/) 16 | random_strings = string_pairs.collect{|s| s[0,1].crypt(s)} 17 | random_string = random_strings.join 18 | return random_string.gsub(/[^A-Za-z0-9]/, "") 19 | end 20 | end -------------------------------------------------------------------------------- /lib/tasks/qwester_tasks.rake: -------------------------------------------------------------------------------- 1 | namespace :qwester do 2 | 3 | # Usage: rake data:reset_questionnaire_positions RAILS_ENV=production 4 | desc "Goes through each of the acts_as_list objects and resets the positions based on order they were added to the database" 5 | task :reset_questionnaire_positions => :environment do 6 | 7 | Qwester::Questionnaire.all.each do |questionnaire| 8 | first_id = questionnaire.questionnaires_questions.minimum(:id) 9 | if first_id # nil if questionnaire has no questions 10 | sql = "UPDATE qwester_questionnaires_questions SET position = (1 + id - #{first_id}) WHERE questionnaire_id = #{questionnaire.id};" 11 | ActiveRecord::Base.connection.execute sql 12 | end 13 | end 14 | 15 | puts "Positions reset" 16 | end 17 | 18 | desc "Removes unpreserved answer stores" 19 | task :destroy_unpreserved_answer_stores => :environment do 20 | before = Qwester::AnswerStore.count 21 | Qwester::AnswerStore.destroy_unpreserved 22 | after = Qwester::AnswerStore.count 23 | puts "#{before - after} answer stores removed, with #{after} remaining." 24 | end 25 | 26 | desc "Reset positions of questionnaires within presentations" 27 | task :reset_presentation_questionnaires_positions => :environment do 28 | Qwester::Presentation.all.each do |presentation| 29 | presentation.presentation_questionnaires.each_with_index do |presentation_questionnaire, index| 30 | presentation_questionnaire.update_attribute(:position, index + 1) 31 | end 32 | puts "Presentation '#{presentation.name}' questionnaires postitioned #{presentation.presentation_questionnaires.collect(&:position)}" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /qwester.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require "qwester/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = "qwester" 9 | s.version = Qwester::VERSION 10 | s.authors = ["Rob Nichols"] 11 | s.email = ["rob@undervale.co.uk"] 12 | s.homepage = "https://github.com/reggieb/qwester" 13 | s.summary = "Questionnaire engine with configurable questions and answers" 14 | s.description = "Questionnaires have many questions. Questions have many answers. Answers match Rule Sets. Qwester manages these objects and the relationships that join them." 15 | s.license = "MIT-LICENSE" 16 | s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"] 17 | s.test_files = Dir["test/**/*"] 18 | 19 | s.add_dependency "rails", '~> 3.2' 20 | s.add_dependency "array_logic" # logic engine used in rule sets 21 | s.add_dependency 'acts_as_list' 22 | 23 | s.add_development_dependency "dibber" # Used for seeding in test/dummy 24 | s.add_development_dependency "sqlite3" 25 | s.add_development_dependency 'activeadmin' 26 | s.add_development_dependency 'sass-rails' 27 | s.add_development_dependency 'uglifier' 28 | s.add_development_dependency 'jquery-ui-rails' 29 | s.add_development_dependency 'jquery-rails' 30 | s.add_development_dependency 'inherited_resources' 31 | s.add_development_dependency 'devise' 32 | s.add_development_dependency 'test-unit' 33 | end 34 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | ENGINE_ROOT = File.expand_path('../..', __FILE__) 5 | ENGINE_PATH = File.expand_path('../../lib/qwester/engine', __FILE__) 6 | 7 | require 'rails/all' 8 | require 'rails/engine/commands' 9 | -------------------------------------------------------------------------------- /test/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == Welcome to Rails 2 | 3 | Rails is a web-application framework that includes everything needed to create 4 | database-backed web applications according to the Model-View-Control pattern. 5 | 6 | This pattern splits the view (also called the presentation) into "dumb" 7 | templates that are primarily responsible for inserting pre-built data in between 8 | HTML tags. The model contains the "smart" domain objects (such as Account, 9 | Product, Person, Post) that holds all the business logic and knows how to 10 | persist themselves to a database. The controller handles the incoming requests 11 | (such as Save New Account, Update Product, Show Post) by manipulating the model 12 | and directing data to the view. 13 | 14 | In Rails, the model is handled by what's called an object-relational mapping 15 | layer entitled Active Record. This layer allows you to present the data from 16 | database rows as objects and embellish these data objects with business logic 17 | methods. You can read more about Active Record in 18 | link:files/vendor/rails/activerecord/README.html. 19 | 20 | The controller and view are handled by the Action Pack, which handles both 21 | layers by its two parts: Action View and Action Controller. These two layers 22 | are bundled in a single package due to their heavy interdependence. This is 23 | unlike the relationship between the Active Record and Action Pack that is much 24 | more separate. Each of these packages can be used independently outside of 25 | Rails. You can read more about Action Pack in 26 | link:files/vendor/rails/actionpack/README.html. 27 | 28 | 29 | == Getting Started 30 | 31 | 1. At the command prompt, create a new Rails application: 32 | rails new myapp (where myapp is the application name) 33 | 34 | 2. Change directory to myapp and start the web server: 35 | cd myapp; rails server (run with --help for options) 36 | 37 | 3. Go to http://localhost:3000/ and you'll see: 38 | "Welcome aboard: You're riding Ruby on Rails!" 39 | 40 | 4. Follow the guidelines to start developing your application. You can find 41 | the following resources handy: 42 | 43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html 44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/ 45 | 46 | 47 | == Debugging Rails 48 | 49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that 50 | will help you debug it and get it back on the rails. 51 | 52 | First area to check is the application log files. Have "tail -f" commands 53 | running on the server.log and development.log. Rails will automatically display 54 | debugging and runtime information to these files. Debugging info will also be 55 | shown in the browser on requests from 127.0.0.1. 56 | 57 | You can also log your own messages directly into the log file from your code 58 | using the Ruby logger class from inside your controllers. Example: 59 | 60 | class WeblogController < ActionController::Base 61 | def destroy 62 | @weblog = Weblog.find(params[:id]) 63 | @weblog.destroy 64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") 65 | end 66 | end 67 | 68 | The result will be a message in your log file along the lines of: 69 | 70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! 71 | 72 | More information on how to use the logger is at http://www.ruby-doc.org/core/ 73 | 74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are 75 | several books available online as well: 76 | 77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) 78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) 79 | 80 | These two books will bring you up to speed on the Ruby language and also on 81 | programming in general. 82 | 83 | 84 | == Debugger 85 | 86 | Debugger support is available through the debugger command when you start your 87 | Mongrel or WEBrick server with --debugger. This means that you can break out of 88 | execution at any point in the code, investigate and change the model, and then, 89 | resume execution! You need to install ruby-debug to run the server in debugging 90 | mode. With gems, use sudo gem install ruby-debug. Example: 91 | 92 | class WeblogController < ActionController::Base 93 | def index 94 | @posts = Post.all 95 | debugger 96 | end 97 | end 98 | 99 | So the controller will accept the action, run the first line, then present you 100 | with a IRB prompt in the server window. Here you can do things like: 101 | 102 | >> @posts.inspect 103 | => "[#nil, "body"=>nil, "id"=>"1"}>, 105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" 107 | >> @posts.first.title = "hello from a debugger" 108 | => "hello from a debugger" 109 | 110 | ...and even better, you can examine how your runtime objects actually work: 111 | 112 | >> f = @posts.first 113 | => #nil, "body"=>nil, "id"=>"1"}> 114 | >> f. 115 | Display all 152 possibilities? (y or n) 116 | 117 | Finally, when you're ready to resume execution, you can enter "cont". 118 | 119 | 120 | == Console 121 | 122 | The console is a Ruby shell, which allows you to interact with your 123 | application's domain model. Here you'll have all parts of the application 124 | configured, just like it is when the application is running. You can inspect 125 | domain models, change values, and save to the database. Starting the script 126 | without arguments will launch it in the development environment. 127 | 128 | To start the console, run rails console from the application 129 | directory. 130 | 131 | Options: 132 | 133 | * Passing the -s, --sandbox argument will rollback any modifications 134 | made to the database. 135 | * Passing an environment name as an argument will load the corresponding 136 | environment. Example: rails console production. 137 | 138 | To reload your controllers and models after launching the console run 139 | reload! 140 | 141 | More information about irb can be found at: 142 | link:http://www.rubycentral.org/pickaxe/irb.html 143 | 144 | 145 | == dbconsole 146 | 147 | You can go to the command line of your database directly through rails 148 | dbconsole. You would be connected to the database with the credentials 149 | defined in database.yml. Starting the script without arguments will connect you 150 | to the development database. Passing an argument will connect you to a different 151 | database, like rails dbconsole production. Currently works for MySQL, 152 | PostgreSQL and SQLite 3. 153 | 154 | == Description of Contents 155 | 156 | The default directory structure of a generated Ruby on Rails application: 157 | 158 | |-- app 159 | | |-- assets 160 | | |-- images 161 | | |-- javascripts 162 | | `-- stylesheets 163 | | |-- controllers 164 | | |-- helpers 165 | | |-- mailers 166 | | |-- models 167 | | `-- views 168 | | `-- layouts 169 | |-- config 170 | | |-- environments 171 | | |-- initializers 172 | | `-- locales 173 | |-- db 174 | |-- doc 175 | |-- lib 176 | | `-- tasks 177 | |-- log 178 | |-- public 179 | |-- script 180 | |-- test 181 | | |-- fixtures 182 | | |-- functional 183 | | |-- integration 184 | | |-- performance 185 | | `-- unit 186 | |-- tmp 187 | | |-- cache 188 | | |-- pids 189 | | |-- sessions 190 | | `-- sockets 191 | `-- vendor 192 | |-- assets 193 | `-- stylesheets 194 | `-- plugins 195 | 196 | app 197 | Holds all the code that's specific to this particular application. 198 | 199 | app/assets 200 | Contains subdirectories for images, stylesheets, and JavaScript files. 201 | 202 | app/controllers 203 | Holds controllers that should be named like weblogs_controller.rb for 204 | automated URL mapping. All controllers should descend from 205 | ApplicationController which itself descends from ActionController::Base. 206 | 207 | app/models 208 | Holds models that should be named like post.rb. Models descend from 209 | ActiveRecord::Base by default. 210 | 211 | app/views 212 | Holds the template files for the view that should be named like 213 | weblogs/index.html.erb for the WeblogsController#index action. All views use 214 | eRuby syntax by default. 215 | 216 | app/views/layouts 217 | Holds the template files for layouts to be used with views. This models the 218 | common header/footer method of wrapping views. In your views, define a layout 219 | using the layout :default and create a file named default.html.erb. 220 | Inside default.html.erb, call <% yield %> to render the view using this 221 | layout. 222 | 223 | app/helpers 224 | Holds view helpers that should be named like weblogs_helper.rb. These are 225 | generated for you automatically when using generators for controllers. 226 | Helpers can be used to wrap functionality for your views into methods. 227 | 228 | config 229 | Configuration files for the Rails environment, the routing map, the database, 230 | and other dependencies. 231 | 232 | db 233 | Contains the database schema in schema.rb. db/migrate contains all the 234 | sequence of Migrations for your schema. 235 | 236 | doc 237 | This directory is where your application documentation will be stored when 238 | generated using rake doc:app 239 | 240 | lib 241 | Application specific libraries. Basically, any kind of custom code that 242 | doesn't belong under controllers, models, or helpers. This directory is in 243 | the load path. 244 | 245 | public 246 | The directory available for the web server. Also contains the dispatchers and the 247 | default HTML files. This should be set as the DOCUMENT_ROOT of your web 248 | server. 249 | 250 | script 251 | Helper scripts for automation and generation. 252 | 253 | test 254 | Unit and functional tests along with fixtures. When using the rails generate 255 | command, template test files will be generated for you and placed in this 256 | directory. 257 | 258 | vendor 259 | External libraries that the application depends on. Also includes the plugins 260 | subdirectory. If the app has frozen rails, those gems also go here, under 261 | vendor/rails/. This directory is in the load path. 262 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Dummy::Application.load_tasks 8 | -------------------------------------------------------------------------------- /test/dummy/app/admin/admin_user.rb: -------------------------------------------------------------------------------- 1 | require_relative '../models/admin_user' 2 | ActiveAdmin.register AdminUser do 3 | index do 4 | column :email 5 | column :current_sign_in_at 6 | column :last_sign_in_at 7 | column :sign_in_count 8 | default_actions 9 | end 10 | 11 | filter :email 12 | 13 | form do |f| 14 | f.inputs "Admin Details" do 15 | f.input :email 16 | f.input :password 17 | f.input :password_confirmation 18 | end 19 | f.actions 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/admin/dashboard.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register_page "Dashboard" do 2 | 3 | menu :priority => 1, :label => proc{ I18n.t("active_admin.dashboard") } 4 | 5 | content :title => proc{ I18n.t("active_admin.dashboard") } do 6 | div :class => "blank_slate_container", :id => "dashboard_default_message" do 7 | span :class => "blank_slate" do 8 | span I18n.t("active_admin.dashboard_welcome.welcome") 9 | small I18n.t("active_admin.dashboard_welcome.call_to_action") 10 | end 11 | end 12 | 13 | # Here is an example of a simple dashboard with columns and panels. 14 | # 15 | # columns do 16 | # column do 17 | # panel "Recent Posts" do 18 | # ul do 19 | # Post.recent(5).map do |post| 20 | # li link_to(post.title, admin_post_path(post)) 21 | # end 22 | # end 23 | # end 24 | # end 25 | 26 | # column do 27 | # panel "Info" do 28 | # para "Welcome to ActiveAdmin." 29 | # end 30 | # end 31 | # end 32 | end # content 33 | end if defined?(ActiveAdmin) 34 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/active_admin.js: -------------------------------------------------------------------------------- 1 | //= require active_admin/application 2 | 3 | $(function() { 4 | if (!!$.prototype.tooltip) { 5 | $( '#questions' ).tooltip(); 6 | } 7 | 8 | $(".associated_questions :checkbox").change(function(event) { 9 | 10 | var action = event.target.checked ? 'add' : 'delete'; 11 | 12 | $.ajax({ 13 | type: "PUT", 14 | url: 'answers/' + event.target.value + '/' + action, 15 | 16 | success: function () { 17 | //console.log('Successfully retrieved ' + name); 18 | }, 19 | error: function () { 20 | //console.log('Error retrieving ' + name); 21 | } 22 | }) 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/questionnaires.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/active_admin.css.scss: -------------------------------------------------------------------------------- 1 | // SASS variable overrides must be declared before loading up Active Admin's styles. 2 | // 3 | // To view the variables that Active Admin provides, take a look at 4 | // `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the 5 | // Active Admin source. 6 | // 7 | // For example, to change the sidebar width: 8 | // $sidebar-width: 242px; 9 | 10 | // Overriding any non-variable SASS must be done after the fact. 11 | // For example, to change the default status-tag color: 12 | // 13 | // body.active_admin { 14 | // .status_tag { background: #6090DB; } 15 | // } 16 | // 17 | // Notice that Active Admin CSS rules are nested within a 18 | // 'body.active_admin' selector to prevent conflicts with 19 | // other pages in the app. It is best to wrap your changes in a 20 | // namespace so they are properly recognized. You have options 21 | // if you e.g. want different styles for different namespaces: 22 | // 23 | // .active_admin applies to any Active Admin namespace 24 | // .admin_namespace applies to the admin namespace (eg: /admin) 25 | // .other_namespace applies to a custom namespace named other (eg: /other) 26 | 27 | body.active_admin { 28 | 29 | a.no_decoration { 30 | text-decoration: none; 31 | color: #323537; 32 | } 33 | 34 | form { 35 | 36 | table.selection { 37 | width: inherit; 38 | tr:nth-child(odd) { 39 | background-color: #d9e4ec; 40 | } 41 | 42 | .options input { 43 | margin-right: 1em; 44 | } 45 | } 46 | } 47 | 48 | .rule_set h2{ 49 | margin-top: 1em; 50 | } 51 | 52 | table.sortable_list { 53 | width: inherit; 54 | margin-left: 20px; 55 | } 56 | 57 | ul.matching_answers { 58 | border: black 1px solid; 59 | padding: 10px; 60 | 61 | li { 62 | list-style: none; 63 | } 64 | } 65 | 66 | .rule_example { 67 | margin: 10px 20px 30px; 68 | } 69 | 70 | div.warn { 71 | border: red solid 2px; 72 | padding: 0; 73 | margin: 10px; 74 | 75 | h3 { 76 | background-color: red; 77 | margin: 0; 78 | padding: 5px 10px; 79 | color: white; 80 | } 81 | 82 | p { 83 | padding: 5px 10px; 84 | font-size: 1.2em; 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/questionnaires.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the questionnaires controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | .questionnaire { 6 | display: inline-block; 7 | } 8 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/test/dummy/app/mailers/.gitkeep -------------------------------------------------------------------------------- /test/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/test/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /test/dummy/app/models/admin_user.rb: -------------------------------------------------------------------------------- 1 | #require 'active_admin' 2 | class AdminUser < ActiveRecord::Base 3 | # Include default devise modules. Others available are: 4 | # :token_authenticatable, :confirmable, 5 | # :lockable, :timeoutable and :omniauthable 6 | devise :database_authenticatable, 7 | :recoverable, :rememberable, :trackable, :validatable 8 | 9 | # Setup accessible (or protected) attributes for your model 10 | attr_accessible :email, :password, :password_confirmation, :remember_me 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag "application", :media => "all" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Dummy::Application 5 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | require "inherited_resources" 3 | require 'devise' 4 | 5 | require 'rails/all' 6 | 7 | Bundler.require 8 | require "qwester" 9 | 10 | module Dummy 11 | class Application < Rails::Application 12 | # Settings in config/environments/* take precedence over those specified here. 13 | # Application configuration should go into files in config/initializers 14 | # -- all .rb files in that directory are automatically loaded. 15 | 16 | # Custom directories with classes and modules you want to be autoloadable. 17 | # config.autoload_paths += %W(#{config.root}/extras) 18 | 19 | # Only load the plugins named here, in the order given (default is alphabetical). 20 | # :all can be used as a placeholder for all plugins not explicitly named. 21 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 22 | 23 | # Activate observers that should always be running. 24 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 25 | 26 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 27 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 28 | # config.time_zone = 'Central Time (US & Canada)' 29 | 30 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 31 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 32 | # config.i18n.default_locale = :de 33 | 34 | # Configure the default encoding used in templates for Ruby 1.9. 35 | config.encoding = "utf-8" 36 | 37 | # Configure sensitive parameters which will be filtered from the log file. 38 | config.filter_parameters += [:password] 39 | 40 | # Enable escaping HTML in JSON. 41 | config.active_support.escape_html_entities_in_json = true 42 | 43 | # Use SQL instead of Active Record's schema dumper when creating the database. 44 | # This is necessary if your schema can't be completely dumped by the schema dumper, 45 | # like if you have constraints or database-specific column types 46 | # config.active_record.schema_format = :sql 47 | 48 | # Enforce whitelist mode for mass assignment. 49 | # This will create an empty whitelist of attributes available for mass-assignment for all models 50 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 51 | # parameters by using an attr_accessible or attr_protected declaration. 52 | config.active_record.whitelist_attributes = true 53 | 54 | # Enable the asset pipeline 55 | config.assets.enabled = true 56 | 57 | # Version of your assets, change this if you want to expire all your assets 58 | config.assets.version = '1.0' 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | if File.exist?(gemfile) 5 | ENV['BUNDLE_GEMFILE'] = gemfile 6 | require 'bundler' 7 | Bundler.setup 8 | end 9 | 10 | $:.unshift File.expand_path('../../../../lib', __FILE__) -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Load elements needed for active admin to work in dummy 5 | 6 | require 'action_controller' 7 | require 'active_support/log_subscriber' 8 | require 'inherited_resources' 9 | require 'devise' 10 | 11 | # Initialize the rails application 12 | Dummy::Application.initialize! 13 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Raise exception on mass assignment protection for Active Record models 26 | config.active_record.mass_assignment_sanitizer = :strict 27 | 28 | # Log the query plan for queries taking more than this (works 29 | # with SQLite, MySQL, and PostgreSQL) 30 | config.active_record.auto_explain_threshold_in_seconds = 0.5 31 | 32 | # Do not compress assets 33 | config.assets.compress = false 34 | 35 | # Expands the lines which load the assets 36 | config.assets.debug = true 37 | end 38 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to nil and saved in location specified by config.assets.prefix 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | end 68 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Raise exception on mass assignment protection for Active Record models 33 | config.active_record.mass_assignment_sanitizer = :strict 34 | 35 | # Print deprecation notices to the stderr 36 | config.active_support.deprecation = :stderr 37 | end 38 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/active_admin.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin' 2 | ActiveAdmin.setup do |config| 3 | 4 | # == Site Title 5 | # 6 | # Set the title that is displayed on the main layout 7 | # for each of the active admin pages. 8 | # 9 | config.site_title = "Dummy" 10 | 11 | # Set the link url for the title. For example, to take 12 | # users to your main site. Defaults to no link. 13 | # 14 | # config.site_title_link = "/" 15 | 16 | # Set an optional image to be displayed for the header 17 | # instead of a string (overrides :site_title) 18 | # 19 | # Note: Recommended image height is 21px to properly fit in the header 20 | # 21 | # config.site_title_image = "/images/logo.png" 22 | 23 | # == Default Namespace 24 | # 25 | # Set the default namespace each administration resource 26 | # will be added to. 27 | # 28 | # eg: 29 | # config.default_namespace = :hello_world 30 | # 31 | # This will create resources in the HelloWorld module and 32 | # will namespace routes to /hello_world/* 33 | # 34 | # To set no namespace by default, use: 35 | # config.default_namespace = false 36 | # 37 | # Default: 38 | # config.default_namespace = :admin 39 | # 40 | # You can customize the settings for each namespace by using 41 | # a namespace block. For example, to change the site title 42 | # within a namespace: 43 | # 44 | # config.namespace :admin do |admin| 45 | # admin.site_title = "Custom Admin Title" 46 | # end 47 | # 48 | # This will ONLY change the title for the admin section. Other 49 | # namespaces will continue to use the main "site_title" configuration. 50 | 51 | # == User Authentication 52 | # 53 | # Active Admin will automatically call an authentication 54 | # method in a before filter of all controller actions to 55 | # ensure that there is a currently logged in admin user. 56 | # 57 | # This setting changes the method which Active Admin calls 58 | # within the controller. 59 | config.authentication_method = :authenticate_admin_user! 60 | 61 | 62 | # == Current User 63 | # 64 | # Active Admin will associate actions with the current 65 | # user performing them. 66 | # 67 | # This setting changes the method which Active Admin calls 68 | # to return the currently logged in user. 69 | config.current_user_method = :current_admin_user 70 | 71 | 72 | # == Logging Out 73 | # 74 | # Active Admin displays a logout link on each screen. These 75 | # settings configure the location and method used for the link. 76 | # 77 | # This setting changes the path where the link points to. If it's 78 | # a string, the strings is used as the path. If it's a Symbol, we 79 | # will call the method to return the path. 80 | # 81 | # Default: 82 | config.logout_link_path = :destroy_admin_user_session_path 83 | 84 | # This setting changes the http method used when rendering the 85 | # link. For example :get, :delete, :put, etc.. 86 | # 87 | # Default: 88 | # config.logout_link_method = :get 89 | 90 | # == Root 91 | # 92 | # Set the action to call for the root path. You can set different 93 | # roots for each namespace. 94 | # 95 | # Default: 96 | # config.root_to = 'dashboard#index' 97 | 98 | # == Admin Comments 99 | # 100 | # Admin comments allow you to add comments to any model for admin use. 101 | # Admin comments are enabled by default. 102 | # 103 | # Default: 104 | # config.allow_comments = true 105 | # 106 | # You can turn them on and off for any given namespace by using a 107 | # namespace config block. 108 | # 109 | # Eg: 110 | # config.namespace :without_comments do |without_comments| 111 | # without_comments.allow_comments = false 112 | # end 113 | 114 | 115 | # == Batch Actions 116 | # 117 | # Enable and disable Batch Actions 118 | # 119 | config.batch_actions = true 120 | 121 | 122 | # == Controller Filters 123 | # 124 | # You can add before, after and around filters to all of your 125 | # Active Admin resources and pages from here. 126 | # 127 | # config.before_filter :do_something_awesome 128 | 129 | 130 | # == Register Stylesheets & Javascripts 131 | # 132 | # We recommend using the built in Active Admin layout and loading 133 | # up your own stylesheets / javascripts to customize the look 134 | # and feel. 135 | # 136 | # To load a stylesheet: 137 | # config.register_stylesheet 'my_stylesheet.css' 138 | 139 | # You can provide an options hash for more control, which is passed along to stylesheet_link_tag(): 140 | # config.register_stylesheet 'my_print_stylesheet.css', :media => :print 141 | # 142 | # To load a javascript file: 143 | # config.register_javascript 'my_javascript.js' 144 | # Load application.js to remove jQuery conflicts (so jquery is only loaded in application.js): 145 | current_javascripts = config.javascripts.clone 146 | config.clear_javascripts! 147 | config.register_javascript 'application.js' 148 | current_javascripts.reverse_each{|j| config.register_javascript j} 149 | 150 | # == CSV options 151 | # 152 | # Set the CSV builder separator (default is ",") 153 | # config.csv_column_separator = ',' 154 | # 155 | # Set the CSV builder options (default is {}) 156 | # config.csv_options = {} 157 | 158 | config.load_paths << Qwester.active_admin_load_path 159 | 160 | end 161 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. 2 | # Many of these configuration options can be set straight in your model. 3 | Devise.setup do |config| 4 | 5 | config.secret_key = '778bf64a3ff719bae4179047012814a0ab280525f3b37e8bc9aba3b2928b2e110588950a4231070581e2155495a5bff3f252d5ce51a9dbcb25410cb76d356b57' 6 | 7 | # ==> Mailer Configuration 8 | # Configure the e-mail address which will be shown in Devise::Mailer, 9 | # note that it will be overwritten if you use your own mailer class with default "from" parameter. 10 | config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" 11 | 12 | # Configure the class responsible to send e-mails. 13 | # config.mailer = "Devise::Mailer" 14 | 15 | # ==> ORM configuration 16 | # Load and configure the ORM. Supports :active_record (default) and 17 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 18 | # available as additional gems. 19 | require 'devise/orm/active_record' 20 | 21 | # ==> Configuration for any authentication mechanism 22 | # Configure which keys are used when authenticating a user. The default is 23 | # just :email. You can configure it to use [:username, :subdomain], so for 24 | # authenticating a user, both parameters are required. Remember that those 25 | # parameters are used only when authenticating and not when retrieving from 26 | # session. If you need permissions, you should implement that in a before filter. 27 | # You can also supply a hash where the value is a boolean determining whether 28 | # or not authentication should be aborted when the value is not present. 29 | # config.authentication_keys = [ :email ] 30 | 31 | # Configure parameters from the request object used for authentication. Each entry 32 | # given should be a request method and it will automatically be passed to the 33 | # find_for_authentication method and considered in your model lookup. For instance, 34 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 35 | # The same considerations mentioned for authentication_keys also apply to request_keys. 36 | # config.request_keys = [] 37 | 38 | # Configure which authentication keys should be case-insensitive. 39 | # These keys will be downcased upon creating or modifying a user and when used 40 | # to authenticate or find a user. Default is :email. 41 | config.case_insensitive_keys = [ :email ] 42 | 43 | # Configure which authentication keys should have whitespace stripped. 44 | # These keys will have whitespace before and after removed upon creating or 45 | # modifying a user and when used to authenticate or find a user. Default is :email. 46 | config.strip_whitespace_keys = [ :email ] 47 | 48 | # Tell if authentication through request.params is enabled. True by default. 49 | # It can be set to an array that will enable params authentication only for the 50 | # given strategies, for example, `config.params_authenticatable = [:database]` will 51 | # enable it only for database (email + password) authentication. 52 | # config.params_authenticatable = true 53 | 54 | # Tell if authentication through HTTP Basic Auth is enabled. False by default. 55 | # It can be set to an array that will enable http authentication only for the 56 | # given strategies, for example, `config.http_authenticatable = [:token]` will 57 | # enable it only for token authentication. 58 | # config.http_authenticatable = false 59 | 60 | # If http headers should be returned for AJAX requests. True by default. 61 | # config.http_authenticatable_on_xhr = true 62 | 63 | # The realm used in Http Basic Authentication. "Application" by default. 64 | # config.http_authentication_realm = "Application" 65 | 66 | # It will change confirmation, password recovery and other workflows 67 | # to behave the same regardless if the e-mail provided was right or wrong. 68 | # Does not affect registerable. 69 | # config.paranoid = true 70 | 71 | # By default Devise will store the user in session. You can skip storage for 72 | # :http_auth and :token_auth by adding those symbols to the array below. 73 | # Notice that if you are skipping storage for all authentication paths, you 74 | # may want to disable generating routes to Devise's sessions controller by 75 | # passing :skip => :sessions to `devise_for` in your config/routes.rb 76 | config.skip_session_storage = [:http_auth] 77 | 78 | # ==> Configuration for :database_authenticatable 79 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If 80 | # using other encryptors, it sets how many times you want the password re-encrypted. 81 | # 82 | # Limiting the stretches to just one in testing will increase the performance of 83 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 84 | # a value less than 10 in other environments. 85 | config.stretches = Rails.env.test? ? 1 : 10 86 | 87 | # Setup a pepper to generate the encrypted password. 88 | # config.pepper = "097e57758dd9c4386f41c5209b8af01637d4d0141e750b616397f7f2a6e966b8b27aab567081c13314369ad78d562fdd783181a7fb2f839b19382dc5833b4235" 89 | 90 | # ==> Configuration for :confirmable 91 | # A period that the user is allowed to access the website even without 92 | # confirming his account. For instance, if set to 2.days, the user will be 93 | # able to access the website for two days without confirming his account, 94 | # access will be blocked just in the third day. Default is 0.days, meaning 95 | # the user cannot access the website without confirming his account. 96 | # config.allow_unconfirmed_access_for = 2.days 97 | 98 | # A period that the user is allowed to confirm their account before their 99 | # token becomes invalid. For example, if set to 3.days, the user can confirm 100 | # their account within 3 days after the mail was sent, but on the fourth day 101 | # their account can't be confirmed with the token any more. 102 | # Default is nil, meaning there is no restriction on how long a user can take 103 | # before confirming their account. 104 | # config.confirm_within = 3.days 105 | 106 | # If true, requires any email changes to be confirmed (exactly the same way as 107 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 108 | # db field (see migrations). Until confirmed new email is stored in 109 | # unconfirmed email column, and copied to email column on successful confirmation. 110 | config.reconfirmable = true 111 | 112 | # Defines which key will be used when confirming an account 113 | # config.confirmation_keys = [ :email ] 114 | 115 | # ==> Configuration for :rememberable 116 | # The time the user will be remembered without asking for credentials again. 117 | # config.remember_for = 2.weeks 118 | 119 | # If true, extends the user's remember period when remembered via cookie. 120 | # config.extend_remember_period = false 121 | 122 | # Options to be passed to the created cookie. For instance, you can set 123 | # :secure => true in order to force SSL only cookies. 124 | # config.rememberable_options = {} 125 | 126 | # ==> Configuration for :validatable 127 | # Range for password length. Default is 8..128. 128 | config.password_length = 8..128 129 | 130 | # Email regex used to validate email formats. It simply asserts that 131 | # an one (and only one) @ exists in the given string. This is mainly 132 | # to give user feedback and not to assert the e-mail validity. 133 | # config.email_regexp = /\A[^@]+@[^@]+\z/ 134 | 135 | # ==> Configuration for :timeoutable 136 | # The time you want to timeout the user session without activity. After this 137 | # time the user will be asked for credentials again. Default is 30 minutes. 138 | # config.timeout_in = 30.minutes 139 | 140 | # If true, expires auth token on session timeout. 141 | # config.expire_auth_token_on_timeout = false 142 | 143 | # ==> Configuration for :lockable 144 | # Defines which strategy will be used to lock an account. 145 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 146 | # :none = No lock strategy. You should handle locking by yourself. 147 | # config.lock_strategy = :failed_attempts 148 | 149 | # Defines which key will be used when locking and unlocking an account 150 | # config.unlock_keys = [ :email ] 151 | 152 | # Defines which strategy will be used to unlock an account. 153 | # :email = Sends an unlock link to the user email 154 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 155 | # :both = Enables both strategies 156 | # :none = No unlock strategy. You should handle unlocking by yourself. 157 | # config.unlock_strategy = :both 158 | 159 | # Number of authentication tries before locking an account if lock_strategy 160 | # is failed attempts. 161 | # config.maximum_attempts = 20 162 | 163 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 164 | # config.unlock_in = 1.hour 165 | 166 | # ==> Configuration for :recoverable 167 | # 168 | # Defines which key will be used when recovering the password for an account 169 | # config.reset_password_keys = [ :email ] 170 | 171 | # Time interval you can reset your password with a reset password key. 172 | # Don't put a too small interval or your users won't have the time to 173 | # change their passwords. 174 | config.reset_password_within = 6.hours 175 | 176 | # ==> Configuration for :encryptable 177 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use 178 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, 179 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) 180 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy 181 | # REST_AUTH_SITE_KEY to pepper) 182 | # config.encryptor = :sha512 183 | 184 | # ==> Configuration for :token_authenticatable 185 | # Defines name of the authentication token params key 186 | # config.token_authentication_key = :auth_token 187 | 188 | # ==> Scopes configuration 189 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 190 | # "users/sessions/new". It's turned off by default because it's slower if you 191 | # are using only default views. 192 | # config.scoped_views = false 193 | 194 | # Configure the default scope given to Warden. By default it's the first 195 | # devise role declared in your routes (usually :user). 196 | # config.default_scope = :user 197 | 198 | # Set this configuration to false if you want /users/sign_out to sign out 199 | # only the current scope. By default, Devise signs out all scopes. 200 | # config.sign_out_all_scopes = true 201 | 202 | # ==> Navigation configuration 203 | # Lists the formats that should be treated as navigational. Formats like 204 | # :html, should redirect to the sign in page when the user does not have 205 | # access, but formats like :xml or :json, should return 401. 206 | # 207 | # If you have any extra navigational formats, like :iphone or :mobile, you 208 | # should add them to the navigational formats lists. 209 | # 210 | # The "*/*" below is required to match Internet Explorer requests. 211 | # config.navigational_formats = ["*/*", :html] 212 | 213 | # The default HTTP method used to sign out a resource. Default is :delete. 214 | config.sign_out_via = :delete 215 | 216 | # ==> OmniAuth 217 | # Add a new OmniAuth provider. Check the wiki for more information on setting 218 | # up on your models and hooks. 219 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' 220 | 221 | # ==> Warden configuration 222 | # If you want to use other strategies, that are not supported by Devise, or 223 | # change the failure app, you can configure them inside the config.warden block. 224 | # 225 | # config.warden do |manager| 226 | # manager.intercept_401 = false 227 | # manager.default_strategies(:scope => :user).unshift :some_external_strategy 228 | # end 229 | 230 | # ==> Mountable engine configurations 231 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 232 | # is mountable, there are some extra configurations to be taken into account. 233 | # The following options are available, assuming the engine is mounted as: 234 | # 235 | # mount MyEngine, at: "/my_engine" 236 | # 237 | # The router that invoked `devise_for`, in the example above, would be: 238 | # config.router_name = :my_engine 239 | # 240 | # When using omniauth, Devise cannot automatically set Omniauth path, 241 | # so you need to do it manually. For the users scope, it would be: 242 | # config.omniauth_path_prefix = "/my_engine/users/auth" 243 | end if defined?(Devise) 244 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/qwester.rb: -------------------------------------------------------------------------------- 1 | 2 | Qwester.active_admin_menu = 'Q and A' 3 | 4 | Qwester::Answer.weighting_alias = :score -------------------------------------------------------------------------------- /test/dummy/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Dummy::Application.config.secret_token = '589061f57c9df78def70559f67cb5186b8c8831efdca6fcc6bb3ce5382bb201ccf33d41e686f44036893ea13df3f5871251d467bb2f1064e85f1dc9db33d89e7' 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Dummy::Application.config.session_store(:cookie_store, key: '_dummy_session') 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Dummy::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters(format: [:json]) 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | errors: 5 | messages: 6 | expired: "has expired, please request a new one" 7 | not_found: "not found" 8 | already_confirmed: "was already confirmed, please try signing in" 9 | not_locked: "was not locked" 10 | not_saved: 11 | one: "1 error prohibited this %{resource} from being saved:" 12 | other: "%{count} errors prohibited this %{resource} from being saved:" 13 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 14 | 15 | devise: 16 | failure: 17 | already_authenticated: 'You are already signed in.' 18 | unauthenticated: 'You need to sign in or sign up before continuing.' 19 | unconfirmed: 'You have to confirm your account before continuing.' 20 | locked: 'Your account is locked.' 21 | not_found_in_database: 'Invalid email or password.' 22 | invalid: 'Invalid email or password.' 23 | invalid_token: 'Invalid authentication token.' 24 | timeout: 'Your session expired, please sign in again to continue.' 25 | inactive: 'Your account was not activated yet.' 26 | sessions: 27 | signed_in: 'Signed in successfully.' 28 | signed_out: 'Signed out successfully.' 29 | passwords: 30 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' 31 | updated: 'Your password was changed successfully. You are now signed in.' 32 | updated_not_active: 'Your password was changed successfully.' 33 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 34 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 35 | confirmations: 36 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' 37 | send_paranoid_instructions: 'If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes.' 38 | confirmed: 'Your account was successfully confirmed. You are now signed in.' 39 | registrations: 40 | signed_up: 'Welcome! You have signed up successfully.' 41 | signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.' 42 | signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.' 43 | signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.' 44 | updated: 'You updated your account successfully.' 45 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address." 46 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' 47 | unlocks: 48 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' 49 | unlocked: 'Your account has been unlocked successfully. Please sign in to continue.' 50 | send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.' 51 | omniauth_callbacks: 52 | success: 'Successfully authenticated from %{kind} account.' 53 | failure: 'Could not authenticate you from %{kind} because "%{reason}".' 54 | mailer: 55 | confirmation_instructions: 56 | subject: 'Confirmation instructions' 57 | reset_password_instructions: 58 | subject: 'Reset password instructions' 59 | unlock_instructions: 60 | subject: 'Unlock Instructions' 61 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | root :to => 'qwester/questionnaires#index' 4 | 5 | require 'inherited_resources' 6 | if defined?(ActiveAdmin) 7 | ActiveAdmin.routes(self) 8 | devise_for :admin_users, ActiveAdmin::Devise.config 9 | end 10 | 11 | mount Qwester::Engine => "/questionnaires" 12 | 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130110115938_devise_create_admin_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateAdminUsers < ActiveRecord::Migration 2 | 3 | def change 4 | create_table(:admin_users) do |t| 5 | ## Database authenticatable 6 | t.string :email, :null => false, :default => "" 7 | t.string :encrypted_password, :null => false, :default => "" 8 | 9 | ## Recoverable 10 | t.string :reset_password_token 11 | t.datetime :reset_password_sent_at 12 | 13 | ## Rememberable 14 | t.datetime :remember_created_at 15 | 16 | ## Trackable 17 | t.integer :sign_in_count, :default => 0 18 | t.datetime :current_sign_in_at 19 | t.datetime :last_sign_in_at 20 | t.string :current_sign_in_ip 21 | t.string :last_sign_in_ip 22 | 23 | ## Confirmable 24 | # t.string :confirmation_token 25 | # t.datetime :confirmed_at 26 | # t.datetime :confirmation_sent_at 27 | # t.string :unconfirmed_email # Only if using reconfirmable 28 | 29 | ## Lockable 30 | # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts 31 | # t.string :unlock_token # Only if unlock strategy is :email or :both 32 | # t.datetime :locked_at 33 | 34 | ## Token authenticatable 35 | # t.string :authentication_token 36 | 37 | 38 | t.timestamps 39 | end 40 | 41 | add_index :admin_users, :email, :unique => true 42 | add_index :admin_users, :reset_password_token, :unique => true 43 | # add_index :admin_users, :confirmation_token, :unique => true 44 | # add_index :admin_users, :unlock_token, :unique => true 45 | # add_index :admin_users, :authentication_token, :unique => true 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130110115940_create_admin_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateAdminComments < ActiveRecord::Migration 2 | def change 3 | create_table :active_admin_comments do |t| 4 | t.string :resource_id, :null => false 5 | t.string :resource_type, :null => false 6 | t.references :author, :polymorphic => true 7 | t.string :namespace 8 | 9 | t.text :body 10 | t.timestamps 11 | end 12 | add_index :active_admin_comments, [:namespace] 13 | add_index :active_admin_comments, [:author_type, :author_id] 14 | end 15 | 16 | 17 | end 18 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103645_create_questions.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121008151526) 2 | class CreateQuestions < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_questions do |t| 5 | t.string :title 6 | t.text :description 7 | t.integer :parent_id 8 | t.integer :lft 9 | t.integer :rgt 10 | t.integer :depth 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103646_create_answers.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121008152106) 2 | class CreateAnswers < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_answers do |t| 5 | t.integer :question_id 6 | t.boolean :value 7 | t.timestamps 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103647_create_rule_sets.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121008153758) 2 | class CreateRuleSets < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_rule_sets do |t| 5 | t.string :title 6 | t.text :description 7 | t.string :url 8 | t.timestamps 9 | end 10 | 11 | create_table :qwester_answers_rule_sets, :id => false do |t| 12 | t.integer :answer_id 13 | t.integer :rule_set_id 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103648_create_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121009080219) 2 | class CreateQuestionnaires < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_questionnaires do |t| 5 | t.string :title 6 | t.text :description 7 | t.timestamps 8 | end 9 | 10 | create_table :qwester_questionnaires_questions, :id => false do |t| 11 | t.integer :questionnaire_id 12 | t.integer :question_id 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103649_create_answer_stores.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121009095600) 2 | class CreateAnswerStores < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_answer_stores do |t| 5 | t.string :session_id 6 | t.timestamps 7 | end 8 | 9 | create_table :qwester_answer_stores_answers, :id => false do |t| 10 | t.integer :answer_id 11 | t.integer :answer_store_id 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103650_add_questionnaire_id_to_answers.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121009160807) 2 | class AddQuestionnaireIdToAnswers < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_answers, :questionnaire_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103651_change_answers_value_to_string.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121009161206) 2 | class ChangeAnswersValueToString < ActiveRecord::Migration 3 | def up 4 | change_column(:qwester_answers, :value, :string) 5 | end 6 | 7 | def down 8 | change_column(:qwester_answers, :value, :boolean) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103652_add_button_image_to_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121019111051) 2 | require 'paperclip' 3 | ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Paperclip::Schema::Statements 4 | class AddButtonImageToQuestionnaires < ActiveRecord::Migration 5 | def change 6 | add_attachment :qwester_questionnaires, :button_image 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103653_create_ckeditor_assets.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121114084347) 2 | class CreateCkeditorAssets < ActiveRecord::Migration 3 | def self.up 4 | create_table :qwester_ckeditor_assets do |t| 5 | t.string :data_file_name, :null => false 6 | t.string :data_content_type 7 | t.integer :data_file_size 8 | 9 | t.integer :assetable_id 10 | t.string :assetable_type, :limit => 30 11 | t.string :type, :limit => 30 12 | 13 | # Uncomment it to save images dimensions, if your need it 14 | t.integer :width 15 | t.integer :height 16 | 17 | t.timestamps 18 | end 19 | 20 | add_index "qwester_ckeditor_assets", ["assetable_type", "type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable_type" 21 | add_index "qwester_ckeditor_assets", ["assetable_type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable" 22 | end 23 | 24 | def self.down 25 | drop_table :qwester_ckeditor_assets 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103654_add_ref_to_questions.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121115114408) 2 | class AddRefToQuestions < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_questions, :ref, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103655_add_ids_to_questionnaires_questions.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121120092108) 2 | class AddIdsToQuestionnairesQuestions < ActiveRecord::Migration 3 | def up 4 | add_column qq_table, :id, :primary_key 5 | add_column qq_table, :position, :integer 6 | add_column qq_table, :created_at, :datetime 7 | add_column qq_table, :updated_at, :datetime 8 | 9 | execute "UPDATE #{qq_table} SET created_at = '#{Time.now.to_s(:db)}', updated_at = '#{Time.now.to_s(:db)}';" 10 | 11 | puts "==============================================" 12 | puts "Now run 'rake qwester:reset_positions RAILS_ENV=#{Rails.env}' to set the positions" 13 | puts "==============================================" 14 | end 15 | 16 | def down 17 | remove_column qq_table, :id 18 | remove_column qq_table, :position 19 | remove_column qq_table, :created_at 20 | remove_column qq_table, :updated_at 21 | end 22 | 23 | def qq_table 24 | :qwester_questionnaires_questions 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103656_add_position_to_answers.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121122130930) 2 | class AddPositionToAnswers < ActiveRecord::Migration 3 | def change 4 | # Adds unless clause, because original version of migration added another field 5 | # and file name was changed when this field was removed. This can cause this 6 | # migration to appear a second time if rake qwester:install:migrations is 7 | # run again in an app created before the change was made. 8 | unless column_exists?(:qwester_answers, :position) 9 | add_column :qwester_answers, :position, :integer 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103657_remove_questionnaire_from_answer.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121122141628) 2 | class RemoveQuestionnaireFromAnswer < ActiveRecord::Migration 3 | def up 4 | remove_column :qwester_answers, :questionnaire_id 5 | end 6 | 7 | def down 8 | add_column :qwester_answers, :questionnaire_id, :integer 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103658_add_rule_to_rule_sets.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121126152146) 2 | class AddRuleToRuleSets < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_rule_sets, :rule, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103659_add_multi_answer_to_questions.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121203150555) 2 | class AddMultiAnswerToQuestions < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_questions, :multi_answer, :boolean 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103660_create_answer_stores_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121204082213) 2 | class CreateAnswerStoresQuestionnaires < ActiveRecord::Migration 3 | def change 4 | 5 | create_table :qwester_answer_stores_questionnaires, :id => false do |t| 6 | t.integer :questionnaire_id 7 | t.integer :answer_store_id 8 | end 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103661_add_link_text_to_rule_sets.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20121204100803) 2 | class AddLinkTextToRuleSets < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_rule_sets, :link_text, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130314103662_add_preserved_to_answer_stores.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130306114331) 2 | class AddPreservedToAnswerStores < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_answer_stores, :preserved, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130315113346_create_qwester_presentations.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130315112847) 2 | class CreateQwesterPresentations < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_presentations do |t| 5 | t.string :name 6 | t.string :title 7 | t.text :description 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130315113347_create_qwester_presentation_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130315113027) 2 | class CreateQwesterPresentationQuestionnaires < ActiveRecord::Migration 3 | def change 4 | create_table :qwester_presentation_questionnaires do |t| 5 | t.integer :questionnaire_id 6 | t.integer :presentation_id 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130402085423_add_presentation_to_rule_sets.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130318102537) 2 | class AddPresentationToRuleSets < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_rule_sets, :presentation, :string 5 | add_column :qwester_presentations, :default, :boolean 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130402085424_add_weighting_to_qwester_answers.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130402074240) 2 | class AddWeightingToQwesterAnswers < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_answers, :weighting, :float, :default => 0 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20130502082731_add_position_to_qwester_presentation_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20130502082540) 2 | class AddPositionToQwesterPresentationQuestionnaires < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_presentation_questionnaires, :position, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20131119142050_add_must_complete_to_qwester_questionnaires.qwester.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from qwester (originally 20131119142009) 2 | class AddMustCompleteToQwesterQuestionnaires < ActiveRecord::Migration 3 | def change 4 | add_column :qwester_questionnaires, :must_complete, :boolean 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20131119142050) do 15 | 16 | create_table "active_admin_comments", :force => true do |t| 17 | t.string "resource_id", :null => false 18 | t.string "resource_type", :null => false 19 | t.integer "author_id" 20 | t.string "author_type" 21 | t.string "namespace" 22 | t.text "body" 23 | t.datetime "created_at", :null => false 24 | t.datetime "updated_at", :null => false 25 | end 26 | 27 | add_index "active_admin_comments", ["author_type", "author_id"], :name => "index_active_admin_comments_on_author_type_and_author_id" 28 | add_index "active_admin_comments", ["namespace"], :name => "index_active_admin_comments_on_namespace" 29 | 30 | create_table "admin_users", :force => true do |t| 31 | t.string "email", :default => "", :null => false 32 | t.string "encrypted_password", :default => "", :null => false 33 | t.string "reset_password_token" 34 | t.datetime "reset_password_sent_at" 35 | t.datetime "remember_created_at" 36 | t.integer "sign_in_count", :default => 0 37 | t.datetime "current_sign_in_at" 38 | t.datetime "last_sign_in_at" 39 | t.string "current_sign_in_ip" 40 | t.string "last_sign_in_ip" 41 | t.datetime "created_at", :null => false 42 | t.datetime "updated_at", :null => false 43 | end 44 | 45 | add_index "admin_users", ["email"], :name => "index_admin_users_on_email", :unique => true 46 | add_index "admin_users", ["reset_password_token"], :name => "index_admin_users_on_reset_password_token", :unique => true 47 | 48 | create_table "qwester_answer_stores", :force => true do |t| 49 | t.string "session_id" 50 | t.datetime "created_at", :null => false 51 | t.datetime "updated_at", :null => false 52 | t.datetime "preserved" 53 | end 54 | 55 | create_table "qwester_answer_stores_answers", :id => false, :force => true do |t| 56 | t.integer "answer_id" 57 | t.integer "answer_store_id" 58 | end 59 | 60 | create_table "qwester_answer_stores_questionnaires", :id => false, :force => true do |t| 61 | t.integer "questionnaire_id" 62 | t.integer "answer_store_id" 63 | end 64 | 65 | create_table "qwester_answers", :force => true do |t| 66 | t.integer "question_id" 67 | t.string "value" 68 | t.datetime "created_at", :null => false 69 | t.datetime "updated_at", :null => false 70 | t.integer "position" 71 | t.float "weighting", :default => 0.0 72 | end 73 | 74 | create_table "qwester_answers_rule_sets", :id => false, :force => true do |t| 75 | t.integer "answer_id" 76 | t.integer "rule_set_id" 77 | end 78 | 79 | create_table "qwester_ckeditor_assets", :force => true do |t| 80 | t.string "data_file_name", :null => false 81 | t.string "data_content_type" 82 | t.integer "data_file_size" 83 | t.integer "assetable_id" 84 | t.string "assetable_type", :limit => 30 85 | t.string "type", :limit => 30 86 | t.integer "width" 87 | t.integer "height" 88 | t.datetime "created_at", :null => false 89 | t.datetime "updated_at", :null => false 90 | end 91 | 92 | add_index "qwester_ckeditor_assets", ["assetable_type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable" 93 | add_index "qwester_ckeditor_assets", ["assetable_type", "type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable_type" 94 | 95 | create_table "qwester_presentation_questionnaires", :force => true do |t| 96 | t.integer "questionnaire_id" 97 | t.integer "presentation_id" 98 | t.datetime "created_at", :null => false 99 | t.datetime "updated_at", :null => false 100 | t.integer "position" 101 | end 102 | 103 | create_table "qwester_presentations", :force => true do |t| 104 | t.string "name" 105 | t.string "title" 106 | t.text "description" 107 | t.datetime "created_at", :null => false 108 | t.datetime "updated_at", :null => false 109 | t.boolean "default" 110 | end 111 | 112 | create_table "qwester_questionnaires", :force => true do |t| 113 | t.string "title" 114 | t.text "description" 115 | t.datetime "created_at", :null => false 116 | t.datetime "updated_at", :null => false 117 | t.string "button_image_file_name" 118 | t.string "button_image_content_type" 119 | t.integer "button_image_file_size" 120 | t.datetime "button_image_updated_at" 121 | t.boolean "must_complete" 122 | end 123 | 124 | create_table "qwester_questionnaires_questions", :force => true do |t| 125 | t.integer "questionnaire_id" 126 | t.integer "question_id" 127 | t.integer "position" 128 | t.datetime "created_at" 129 | t.datetime "updated_at" 130 | end 131 | 132 | create_table "qwester_questions", :force => true do |t| 133 | t.string "title" 134 | t.text "description" 135 | t.integer "parent_id" 136 | t.integer "lft" 137 | t.integer "rgt" 138 | t.integer "depth" 139 | t.datetime "created_at", :null => false 140 | t.datetime "updated_at", :null => false 141 | t.string "ref" 142 | t.boolean "multi_answer" 143 | end 144 | 145 | create_table "qwester_rule_sets", :force => true do |t| 146 | t.string "title" 147 | t.text "description" 148 | t.string "url" 149 | t.datetime "created_at", :null => false 150 | t.datetime "updated_at", :null => false 151 | t.text "rule" 152 | t.string "link_text" 153 | t.string "presentation" 154 | end 155 | 156 | end 157 | -------------------------------------------------------------------------------- /test/dummy/db/seeds.rb: -------------------------------------------------------------------------------- 1 | require 'dibber' 2 | Seeder = Dibber::Seeder 3 | 4 | Seeder.seeds_path = "#{Rails.root}/db/seeds" 5 | 6 | ############################## 7 | Seeder.monitor AdminUser 8 | admin_email = 'admin@warwickshire.gov.uk' 9 | password = 'changeme' 10 | AdminUser.create!( 11 | :email => admin_email, 12 | :password => password, 13 | :password_confirmation => password 14 | ) unless AdminUser.exists?(:email => admin_email) 15 | 16 | ############################## 17 | Seeder.monitor Qwester::Questionnaire 18 | Seeder.monitor Qwester::Question 19 | Seeder.monitor Qwester::Answer 20 | Seeder.monitor Qwester::RuleSet 21 | Seeder.process_log.start('first questionnaire questions', 'Qwester::Questionnaire.count > 0 ? Qwester::Questionnaire.first.questions.length : 0') 22 | 23 | if Qwester::Questionnaire.count > 1 24 | puts "**** Bypassing questionnaire, question, answer and rule set population" 25 | puts "**** as there are already #{Qwester::Questionnaire.count} questionnaires in the system" 26 | else 27 | Seeder.objects_from('questions.yml').each do |holder, data| 28 | questionnaire = Qwester::Questionnaire.find_or_create_by_title(data['title']) 29 | 30 | if data['questions'] 31 | data['questions'].values.each do |question_data| 32 | question = Qwester::Question.find_or_create_by_title(question_data['question']) 33 | question.create_standard_answers 34 | answer = question.answers.find_or_create_by_value('Yes') 35 | rule_set = Qwester::RuleSet.find_or_initialize_by_title(question_data['title']) 36 | rule_set.url = question_data['url'] 37 | rule_set.answers = [answer] 38 | rule_set.save 39 | questionnaire.questions << question 40 | end 41 | end 42 | end 43 | end 44 | 45 | 46 | ############################## 47 | puts Seeder.report -------------------------------------------------------------------------------- /test/dummy/db/seeds/questions.yml: -------------------------------------------------------------------------------- 1 | Q1: 2 | title: Blue 3 | questions: 4 | a: 5 | title: Other blue favourites 6 | question: Is blue your favourite colour? 7 | url: https://www.google.co.uk/search?q=favourite+blue 8 | 9 | b: 10 | title: 11 | question: Is the sky blue? 12 | url: https://www.google.co.uk/search?q=sky+blue 13 | 14 | 15 | Q2: 16 | title: Qwester 17 | questions: 18 | a: 19 | title: As you like qwester 20 | question: Do you like qwester? 21 | url: https://github.com/reggieb/qwester 22 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/test/dummy/lib/assets/.gitkeep -------------------------------------------------------------------------------- /test/dummy/log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/test/dummy/log/.gitkeep -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reggieb/qwester/458690e814c83405edb32404eb45b06dc5fc8f59/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/dummy/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/admin_users.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | email: 'admin@user.com' 4 | encrypted_password: <%= AdminUser.new.send(:password_digest, 'password') %> 5 | 6 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/answer_stores.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | session_id: <%= RandomString.new(15) %> 4 | 5 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/answers.yml: -------------------------------------------------------------------------------- 1 | 2 | one: 3 | id: 1 4 | value: <%= Qwester::Answer.standard_values[0] %> 5 | question_id: 1 6 | 7 | two: 8 | id: 2 9 | value: <%= Qwester::Answer.standard_values[1] %> 10 | question_id: 1 11 | 12 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/presentation_questionnaires.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | questionnaire_id: 1 4 | presentation_id: 1 5 | position: 1 6 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/presentations.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | id: 1 5 | name: Start 6 | title: The starting point 7 | description: This is the first set of questionnaires 8 | 9 | two: 10 | id: 2 11 | name: Main 12 | title: Main set of questionnaires 13 | description: This is another set of questionnaires 14 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/questionnaires.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | title: Questions about stuff 4 | 5 | two: 6 | id: 2 7 | title: More questions 8 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/questionnaires_questions.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | question_id: 1 4 | questionnaire_id: 1 5 | position: 1 6 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/questions.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | title: Is this true? 4 | 5 | two: 6 | id: 2 7 | title: Is this a question? 8 | 9 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/qwester/rule_sets.yml: -------------------------------------------------------------------------------- 1 | 2 | one: 3 | id: 1 4 | url: http://undervale.co.uk 5 | title: Test Rule Set 6 | -------------------------------------------------------------------------------- /test/dummy/test/functional/qwester/questionnaires_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Qwester 4 | class QuestionnairesControllerTest < ActionController::TestCase 5 | def setup 6 | @questionnaire = Questionnaire.find(1) 7 | @question = Question.find(1) 8 | @answer = @question.answers.find(1) 9 | @presentation = Presentation.find(1) 10 | end 11 | 12 | def test_setup 13 | assert_equal(2, @question.answers.count) 14 | assert_not_equal(@presentation.questionnaires, Questionnaire.all) 15 | end 16 | 17 | def test_index 18 | get :index, :use_route => :qwester 19 | assert_response :success 20 | assert_equal(Questionnaire.all, assigns('questionnaires')) 21 | end 22 | 23 | def test_presentation_not_assigned_when_not_used 24 | test_index 25 | assert_nil(assigns('presentation')) 26 | end 27 | 28 | def test_index_with_presentation 29 | get :index, {:use_route => :qwester}, :presentations => [@presentation.name] 30 | assert_response :success 31 | assert_equal([@presentation.name], session[:presentations]) 32 | assert_equal(@presentation.questionnaires, assigns('questionnaires')) 33 | assert_equal(@presentation, assigns('presentation')) 34 | end 35 | 36 | def test_index_with_presentation_and_history_of_other_presentation 37 | names = [other_presentation.name, @presentation.name] 38 | get :index, {:use_route => :qwester}, :presentations => names 39 | assert_response :success 40 | assert_equal(names, session[:presentations]) 41 | assert_equal(@presentation.questionnaires, assigns('questionnaires')) 42 | end 43 | 44 | def test_index_with_other_presentation_and_history_of_presentation 45 | other = other_presentation 46 | names = [@presentation.name, other.name] 47 | get :index, {:use_route => :qwester}, :presentations => names 48 | assert_response :success 49 | assert_equal(names, session[:presentations]) 50 | assert_equal(other.questionnaires, assigns('questionnaires')) 51 | end 52 | 53 | def test_index_with_default_presentation 54 | @presentation.update_attribute :default, true 55 | get :index, {:use_route => :qwester} 56 | assert_response :success 57 | assert_equal(@presentation.questionnaires, assigns('questionnaires')) 58 | assert_equal(nil, session[:presentations]) 59 | assert_equal(@presentation, assigns('presentation')) 60 | end 61 | 62 | def test_show 63 | get :show, :id => @questionnaire.id, :use_route => :qwester 64 | assert_response :success 65 | assert_equal(@questionnaire, assigns('questionnaire')) 66 | end 67 | 68 | 69 | def test_update 70 | put( 71 | :update, 72 | :id => @questionnaire.id, 73 | :question_id => { 74 | @question.id.to_s => { 75 | :answer_ids => [@answer.id.to_s] 76 | } 77 | }, 78 | :use_route => :qwester 79 | ) 80 | answer_store = AnswerStore.last 81 | assert_equal(@question, @answer.question) 82 | assert_equal(answer_store.session_id, session[Qwester.session_key]) 83 | assert_response :redirect 84 | end 85 | 86 | def test_update_with_no_answers 87 | put( 88 | :update, 89 | :id => @questionnaire.id, 90 | :use_route => :qwester 91 | ) 92 | assert_equal(nil, session[Qwester.session_key]) 93 | assert_response :redirect 94 | end 95 | 96 | def test_update_with_must_complete_and_all_questions_answered 97 | @questionnaire.update_attribute :must_complete, true 98 | test_update 99 | end 100 | 101 | def test_update_with_must_complete_and_no_questions_answered 102 | @questionnaire.update_attribute :must_complete, true 103 | test_update_with_no_answers 104 | end 105 | 106 | def test_update_with_must_complete_and_some_answers_completed 107 | @questionnaire.update_attribute :must_complete, true 108 | @questionnaire.questions << Question.find(2) 109 | put( 110 | :update, 111 | :id => @questionnaire.id, 112 | :question_id => { 113 | @question.id.to_s => { 114 | :answer_ids => [@answer.id.to_s] 115 | } 116 | }, 117 | :use_route => :qwester 118 | ) 119 | assert_response :success 120 | assert assigns('questionnaire').errors.present?, "@questionnaire should include errors" 121 | assert !assigns('qwester_answer_store').questionnaires.include?(@questionnaire), "@questionnaire should not be stored" 122 | end 123 | 124 | def test_answer_store_created_when_questionnaire_first_submitted 125 | assert_difference 'AnswerStore.count' do 126 | test_update 127 | end 128 | end 129 | 130 | def test_update_add_multiple_answers 131 | other_question = Question.find(2) 132 | other_question.create_standard_answers 133 | other_answer = other_question.answers.last 134 | assert_difference 'AnswerStore.count' do 135 | put( 136 | :update, 137 | :id => @questionnaire.id, 138 | :question_id => { 139 | @question.id.to_s => { 140 | :answer_ids => [@answer.id.to_s] 141 | }, 142 | other_question.id.to_s => { 143 | :answer_ids => [other_answer.id.to_s] 144 | } 145 | }, 146 | :use_route => :qwester 147 | ) 148 | end 149 | answer_store = AnswerStore.last 150 | 151 | assert_equal([@answer, other_answer], answer_store.answers) 152 | end 153 | 154 | def test_update_add_multiple_answers_for_same_question 155 | answer, other_answer = @question.answers[0..1] 156 | assert_not_equal(answer, other_answer) 157 | assert_difference 'AnswerStore.count', 1 do 158 | put( 159 | :update, 160 | :id => @questionnaire.id, 161 | :question_id => { 162 | @question.id.to_s => { 163 | :answer_ids => [answer.id.to_s, other_answer.id.to_s] 164 | } 165 | }, 166 | :use_route => :qwester 167 | ) 168 | end 169 | answer_store = AnswerStore.last 170 | 171 | assert_equal([answer, other_answer], answer_store.answers) 172 | end 173 | 174 | def test_rule_set_match_after_update 175 | test_update 176 | rule_set = RuleSet.first 177 | rule_set.answers << AnswerStore.last.answers.first 178 | rule_set.save 179 | get :index, :use_route => :qwester 180 | assert_equal([rule_set], assigns['qwester_rule_sets']) 181 | end 182 | 183 | def test_reset 184 | test_update 185 | assert_no_difference 'Answer.count' do 186 | get :reset, :use_route => :qwester 187 | assert_response :redirect 188 | assert_equal([], AnswerStore.last.answers) 189 | assert_equal([], AnswerStore.last.questionnaires) 190 | end 191 | end 192 | 193 | def test_update_adds_questionnaire_to_answer_store 194 | test_update 195 | assert_equal([@questionnaire], AnswerStore.last.questionnaires) 196 | end 197 | 198 | def test_previous_answers_to_question_removed_on_update 199 | assert_difference 'AnswerStore.count', 1 do 200 | test_update 201 | end 202 | @answer = @question.answers.find(2) 203 | assert_no_difference 'AnswerStore.count' do 204 | test_update 205 | end 206 | end 207 | 208 | def test_transition_from_default_to_final_via_submission_of_questionnaire 209 | @target_presentation = Presentation.find(2) 210 | @target_presentation.questionnaires = [Questionnaire.find(2)] 211 | @target_presentation.save 212 | @rule_set = RuleSet.find(1) 213 | @rule_set.answers = [@answer] 214 | @rule_set.presentation = @target_presentation.name 215 | @rule_set.save 216 | @presentation.update_attribute :default, true 217 | get :index, :use_route => :qwester 218 | assert_equal(@presentation.questionnaires, assigns[:questionnaires]) 219 | put( 220 | :update, 221 | :id => @questionnaire.id, 222 | :question_id => { 223 | @question.id.to_s => { 224 | :answer_ids => [@answer.id.to_s] 225 | } 226 | }, 227 | :use_route => :qwester 228 | ) 229 | assert_response :redirect 230 | get :index, :use_route => :qwester 231 | assert_equal(@target_presentation.questionnaires, assigns[:questionnaires]) 232 | end 233 | 234 | private 235 | def other_presentation 236 | presentation = Presentation.find(2) 237 | presentation.questionnaires << Questionnaire.find(2) 238 | return presentation 239 | end 240 | end 241 | 242 | end 243 | -------------------------------------------------------------------------------- /test/dummy/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | 3 | puts "!!!! Running tests from /test/dummy only runs the tests in /test/dummy/test, and ignores tests in /test/unit !!!!" 4 | puts "run 'rake test' at the project root to run all tests." -------------------------------------------------------------------------------- /test/dummy/test/unit/admin_user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AdminUserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @admin_user = AdminUser.find(1) 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/test/unit/helpers/questionnaires_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class QuestionnairesHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/integration/navigation_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class NavigationTest < ActionDispatch::IntegrationTest 4 | fixtures :all 5 | 6 | # test "the truth" do 7 | # assert true 8 | # end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require File.expand_path("../dummy/config/environment.rb", __FILE__) 5 | require "rails/test_help" 6 | 7 | Rails.backtrace_cleaner.remove_silencers! 8 | 9 | puts "fixture path = " + ActiveSupport::TestCase.fixture_path 10 | 11 | class ActiveSupport::TestCase 12 | fixtures :all 13 | end 14 | -------------------------------------------------------------------------------- /test/unit/qwester/answer_store_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | module Qwester 3 | 4 | class AnswerStoreTest < ActiveSupport::TestCase 5 | 6 | def setup 7 | @answer_store = AnswerStore.find(1) 8 | @answer_store.update_attribute(:updated_at, 2.days.ago) 9 | @answer = Answer.find(1) 10 | @other_answer = Answer.find(2) 11 | @questionnaire = Questionnaire.find(1) 12 | end 13 | 14 | def test_answers_empty 15 | assert_equal([], @answer_store.answers) 16 | end 17 | 18 | def test_answer_store_accepts_objects 19 | @answer_store.answers << @answer 20 | @answer_store.questionnaires << @questionnaire 21 | assert_equal([@questionnaire], @answer_store.questionnaires) 22 | assert_equal([@answer], @answer_store.answers) 23 | end 24 | 25 | def test_reset 26 | test_answer_store_accepts_objects 27 | @answer_store.reset 28 | assert_equal([], @answer_store.questionnaires) 29 | assert_equal([], @answer_store.answers) 30 | end 31 | 32 | def test_session_id_generated_on_creation 33 | answer_store = AnswerStore.create 34 | assert_match /^\w*$/, answer_store.session_id 35 | assert_equal 15, answer_store.session_id.length 36 | end 37 | 38 | def test_preserve 39 | assert_difference 'AnswerStore.count', 1 do 40 | @preserved = @answer_store.preserve 41 | end 42 | assert(@preserved.preserved?) 43 | assert(!@answer_store.preserved?) 44 | end 45 | 46 | def test_preserved_is_last_created 47 | test_preserve 48 | assert_equal(AnswerStore.last, @preserved) 49 | end 50 | 51 | def test_preserved_answer_store_has_new_session_id 52 | test_preserve 53 | assert_not_equal(@answer_store.session_id, @preserved.session_id) 54 | end 55 | 56 | def test_preserved_has_same_objects_as_original 57 | test_answer_store_accepts_objects 58 | test_preserve 59 | assert_equal(@answer_store.answers, @preserved.answers) 60 | assert_equal(@answer_store.questionnaires, @preserved.questionnaires) 61 | end 62 | 63 | def test_changes_to_original_do_not_affect_preserved 64 | test_preserve 65 | assert_equal([], @preserved.answers) 66 | assert_equal([], @preserved.questionnaires) 67 | test_answer_store_accepts_objects 68 | @preserved.reload 69 | assert_equal([], @preserved.answers) 70 | assert_equal([], @preserved.questionnaires) 71 | end 72 | 73 | def test_destroy_unpreserved 74 | assert_not_equal(0, AnswerStore.count) 75 | AnswerStore.destroy_unpreserved 76 | assert_equal(0, AnswerStore.count) 77 | end 78 | 79 | def test_destroy_unpreserved_does_not_remove_preserved 80 | count = AnswerStore.count 81 | test_preserve 82 | assert_difference 'AnswerStore.count', - count do 83 | AnswerStore.destroy_unpreserved 84 | end 85 | assert_equal(@preserved, AnswerStore.first) 86 | end 87 | 88 | def test_destroy_unpreserved_does_not_destroy_answers 89 | test_answer_store_accepts_objects 90 | assert_no_difference 'Answer.count' do 91 | AnswerStore.destroy_unpreserved 92 | end 93 | end 94 | 95 | def test_destroy_unpreserved_does_not_destroy_questionnaires 96 | test_answer_store_accepts_objects 97 | assert_no_difference 'Questionnaire.count' do 98 | AnswerStore.destroy_unpreserved 99 | end 100 | end 101 | 102 | def test_destroy_unpreserved_removes_entries_from_answers_join_table 103 | test_answer_store_accepts_objects 104 | assert_on_destroy_unpreserved_join_entries_removed_for 'answers' 105 | end 106 | 107 | def test_destroy_unpreserved_removes_entries_from_questionnaires_join_table 108 | test_answer_store_accepts_objects 109 | assert_on_destroy_unpreserved_join_entries_removed_for 'questionnaires' 110 | end 111 | 112 | def test_destroy_unpreserved_does_not_remove_recent_answer_stores 113 | answer_store = AnswerStore.create 114 | AnswerStore.destroy_unpreserved 115 | assert_equal([answer_store], AnswerStore.all) 116 | end 117 | 118 | def test_restore_preserved 119 | test_preserve 120 | assert_difference 'AnswerStore.count' do 121 | @restored = @preserved.restore 122 | end 123 | assert_equal(false, @restored.preserved?) 124 | assert_not_equal(@preserved.session_id, @restored.session_id) 125 | assert_equal(@preserved.answers, @restored.answers) 126 | assert_equal(@preserved.questionnaires, @restored.questionnaires) 127 | end 128 | 129 | def test_get_session_ids 130 | test_preserve # to populate database with extra answer_stores 131 | assert_equal(AnswerStore.all.collect(&:session_id).sort, @answer_store.send(:get_session_ids).sort) 132 | end 133 | 134 | def test_completed_questionnaires 135 | assert_equal [], @answer_store.completed_questionnaires 136 | @answer_store.answers << @questionnaire.questions.first.answers.first 137 | @answer_store.questionnaires << @questionnaire 138 | assert_equal [@questionnaire], @answer_store.completed_questionnaires 139 | end 140 | 141 | def test_completed_questionnaires_with_second_question 142 | first_question = @questionnaire.questions.first 143 | second_question = Question.find(2) 144 | second_question.build_standard_answers 145 | second_question.save 146 | @questionnaire.questions = [first_question, second_question] 147 | @answer_store.answers << first_question.answers.first 148 | @answer_store.questionnaires << @questionnaire 149 | assert_equal [], @answer_store.completed_questionnaires 150 | @answer_store.answers << second_question.answers.first 151 | assert_equal [@questionnaire], @answer_store.completed_questionnaires 152 | end 153 | 154 | private 155 | def assert_on_destroy_unpreserved_join_entries_removed_for(table) 156 | join_table = @answer_store.association(table).join_table.name 157 | sql = "SELECT count(*) FROM #{join_table}" 158 | original_joins = AnswerStore.connection.select_value(sql) 159 | AnswerStore.destroy_unpreserved 160 | joins = AnswerStore.connection.select_value(sql) 161 | assert_equal(original_joins - 1, joins) 162 | end 163 | 164 | end 165 | end -------------------------------------------------------------------------------- /test/unit/qwester/answer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Qwester 4 | 5 | class AnswerTest < ActiveSupport::TestCase 6 | def setup 7 | @answer = Answer.find(1) 8 | end 9 | 10 | def test_find_first_or_create 11 | answer = Answer.find_first_or_create( 12 | :value => @answer.value, 13 | :question_id => @answer.question_id 14 | ) 15 | assert_equal(@answer, answer) 16 | end 17 | 18 | def test_find_first_or_create_when_answer_does_not_exist 19 | new_value = 'something' 20 | assert_difference 'Answer.count' do 21 | answer = Answer.find_first_or_create( 22 | :value => 'something', 23 | :question_id => Question.first.id 24 | ) 25 | end 26 | assert_equal(new_value, Answer.last.value) 27 | end 28 | 29 | def test_destroy 30 | rule_set = RuleSet.first 31 | answer_store = AnswerStore.first 32 | @answer.rule_sets = [rule_set] 33 | @answer.answer_stores = [answer_store] 34 | assert_difference 'Answer.count', -1 do 35 | assert_difference 'rule_set.answers.count', -1 do 36 | assert_difference 'answer_store.answers.count', -1 do 37 | @answer.destroy 38 | end 39 | end 40 | end 41 | end 42 | 43 | def test_rule_label 44 | assert_equal("a#{@answer.id}", @answer.rule_label) 45 | end 46 | 47 | def test_set_weighting_alias 48 | name = :new_alias_method 49 | assert(!Answer.instance_methods.include?(name.to_sym), "Answer methods should not include #{name} at start of test") 50 | Answer.weighting_alias = name 51 | assert_equal(name, Answer.weighting_alias) 52 | assert(Answer.instance_methods.include?(name.to_sym), "Set weighting alias should define instance method #{name} ") 53 | weighting = 4 54 | @answer.weighting = weighting 55 | assert_equal(weighting, @answer.send(name)) 56 | remove_weighting_alias 57 | end 58 | 59 | def test_remove_weighting_alias 60 | Answer.weighting_alias = :something 61 | remove_weighting_alias 62 | end 63 | 64 | def test_setting_weighting_alias_to_nil_removes_existing_alias 65 | name = :new_alias_method 66 | Answer.weighting_alias = name 67 | Answer.weighting_alias = nil 68 | assert(!Answer.instance_methods.include?(name), "Answer methods should not include #{name}") 69 | assert_nil(Answer.weighting_alias) 70 | end 71 | 72 | def test_weighting_alias_name 73 | assert_equal('weighting', Answer.weighting_alias_name) 74 | name = 'something_weighty' 75 | Answer.weighting_alias = name 76 | assert_equal(name, Answer.weighting_alias_name) 77 | end 78 | 79 | private 80 | def remove_weighting_alias 81 | name = Answer.weighting_alias.to_sym 82 | Answer.remove_weighting_alias 83 | assert(!Answer.instance_methods.include?(name), "Answer methods should not include #{name}") 84 | Answer.class_eval "@weighting_alias = nil" 85 | assert_nil(Answer.weighting_alias) 86 | end 87 | 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /test/unit/qwester/presentation_questionnaire_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Qwester 4 | class PresentationQuestionnaireTest < ActiveSupport::TestCase 5 | 6 | def setup 7 | @presentation_questionnaire = PresentationQuestionnaire.find(1) 8 | @questionnaire = Questionnaire.find(2) 9 | @presentation = @presentation_questionnaire.presentation 10 | @presentation.questionnaires << @questionnaire 11 | @default_order = [@presentation_questionnaire.questionnaire, @questionnaire] 12 | end 13 | 14 | def test_setup 15 | assert_kind_of(Questionnaire, @presentation_questionnaire.questionnaire) 16 | assert_not_equal(@questionnaire, @presentation_questionnaire.questionnaire) 17 | assert_equal(@default_order, @presentation.questionnaires) 18 | end 19 | 20 | def test_acts_as_list_method_last 21 | assert_equal(true, @presentation.last?(@questionnaire)) 22 | assert_equal(false, @presentation.last?(@presentation_questionnaire.questionnaire)) 23 | end 24 | 25 | def test_acts_as_list_method_move_to_top 26 | @presentation.move_to_top(@questionnaire) 27 | assert_equal(@default_order.reverse, @presentation.questionnaires) 28 | end 29 | 30 | def test_position 31 | assert_equal(@presentation_questionnaire.position, @presentation.position(@presentation_questionnaire.questionnaire)) 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/unit/qwester/presentation_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module Qwester 4 | class PresentationTest < ActiveSupport::TestCase 5 | 6 | def setup 7 | @questionnaire = Questionnaire.find(1) 8 | @presentation = Presentation.find(1) 9 | end 10 | 11 | def test_questionnaires 12 | assert(@presentation.questionnaires.include? @questionnaire) 13 | end 14 | 15 | def test_empty_questionnaires 16 | assert_equal([], empty_presentation.questionnaires) 17 | end 18 | 19 | def test_valid_names 20 | valid = %w{this that foo_bar HeyHo this1 that2 123} 21 | valid.each do |name| 22 | @presentation.name = name 23 | assert(@presentation.save, "#{name} should be a valid presentation name #{@presentation.errors.full_messages}") 24 | end 25 | end 26 | 27 | def test_invalid_names 28 | invalid = ['This and that', 'foo bar', '$this', '@that'] 29 | invalid.each do |name| 30 | @presentation.name = name 31 | assert(!@presentation.save, "'#{name}' should be an invalid presentation name") 32 | end 33 | end 34 | 35 | def test_title_created_from_name 36 | @presentation.name = "foo_bar" 37 | @presentation.title = nil 38 | @presentation.save 39 | assert_equal('Foo bar', @presentation.title) 40 | end 41 | 42 | def test_set_as_default 43 | assert !@presentation.default?, "Presentation should not be default at start" 44 | @presentation.update_attribute(:default, true) 45 | assert @presentation.default?, "Presentation should be default" 46 | assert !empty_presentation.reload.default?, "Empty presentation should not be default" 47 | end 48 | 49 | def test_set_as_default_when_other_presentation_default 50 | test_set_as_default 51 | empty_presentation.update_attribute(:default, true) 52 | assert !@presentation.reload.default?, "Presentation should not be default" 53 | assert empty_presentation.default?, "Empty presentation should be default" 54 | end 55 | 56 | # Overriding this method, so want to make sure default behaviour still works 57 | # Tests for methods over-riding this are in other test, e.g. questionnaires_question_test.rb 58 | def test_method_missing 59 | assert_raise NoMethodError do 60 | @questionnaire.no_such_method 61 | end 62 | end 63 | 64 | def empty_presentation 65 | @empty_presentation ||= Presentation.find(2) 66 | end 67 | 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/unit/qwester/question_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | module Qwester 3 | 4 | class QuestionTest < ActiveSupport::TestCase 5 | 6 | def setup 7 | @question = Question.find(2) 8 | end 9 | 10 | def test_setup 11 | assert_equal(0, @question.answers.count) 12 | end 13 | 14 | def test_build_standard_answers 15 | assert_no_difference 'Answer.count' do 16 | @question.build_standard_answers 17 | assert_question_has_standard_answers 18 | end 19 | end 20 | 21 | def test_create_standard_answers 22 | assert_difference 'Answer.count', Answer.standard_values.length do 23 | @question.create_standard_answers 24 | assert_question_has_standard_answers 25 | end 26 | end 27 | 28 | def test_destroy 29 | test_create_standard_answers 30 | assert_difference 'Question.count', -1 do 31 | assert_difference 'Answer.count', -(Answer.standard_values.length) do 32 | @question.destroy 33 | end 34 | end 35 | end 36 | 37 | private 38 | def assert_question_has_standard_answers 39 | assert_equal(Answer.standard_values, @question.answers.collect(&:value)) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/unit/qwester/questionnaire_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | module Qwester 3 | 4 | class QuestionnaireTest < ActiveSupport::TestCase 5 | def setup 6 | @questionnaire = Questionnaire.find(1) 7 | end 8 | 9 | # Overriding this method, so want to make sure default behaviour still works 10 | # Tests for methods over-riding this are in other test, e.g. questionnaires_question_test.rb 11 | def test_method_missing 12 | assert_raise NoMethodError do 13 | @questionnaire.no_such_method 14 | end 15 | end 16 | 17 | def test_habtm_joins_to_answer_store 18 | @questionnaire.answer_stores << AnswerStore.first 19 | end 20 | 21 | def test_presentation 22 | assert_equal([Presentation.find(1)], @questionnaire.presentations) 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/unit/qwester/questionnaires_question_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | module Qwester 3 | 4 | class QuestionnairesQuestionTest < ActiveSupport::TestCase 5 | def setup 6 | @questionnaires_question = QuestionnairesQuestion.find(1) 7 | @questionnaire = @questionnaires_question.questionnaire 8 | @question = Question.find(2) 9 | end 10 | 11 | def test_setup 12 | assert_not_equal(@questionnaires_question.question, @question) 13 | end 14 | 15 | def test_position 16 | @questionnaire.questions << @question 17 | assert_equal(1, @questionnaires_question.position) 18 | assert_equal(2, QuestionnairesQuestion.last.position) 19 | end 20 | 21 | def test_move_question_up 22 | test_position 23 | @questionnaire.move_higher(@question) 24 | assert_equal(2, @questionnaires_question.reload.position) 25 | assert_equal(1, QuestionnairesQuestion.last.position) 26 | end 27 | 28 | def test_second_questionnaire_not_affected_by_positioning_in_first 29 | questionnaire = Questionnaire.find(2) 30 | question = Question.find(1) 31 | questionnaire.questions << question 32 | questionnaire.questions << @question 33 | assert_not_equal(@questionnaire, questionnaire, "should be working with two questionnaires") 34 | assert_equal([question, @question], questionnaire.questions) 35 | assert_equal([question], @questionnaire.questions) 36 | test_move_question_up 37 | assert_equal([question, @question], questionnaire.reload.questions) 38 | assert_equal([@question, question], @questionnaire.reload.questions) 39 | end 40 | 41 | def test_adding_question_again_does_not_create_duplicates 42 | @questionnaire.questions << @question 43 | assert_no_difference '@questionnaire.questions.count' do 44 | @questionnaire.reload.questions << @question 45 | end 46 | end 47 | 48 | def test_removing_quesition 49 | test_adding_question_again_does_not_create_duplicates 50 | @questionnaire.questions.delete(@question) 51 | assert_equal(1, @questionnaire.questions.count) 52 | end 53 | 54 | def test_removing_question_deletes_questionnaires_question_but_does_not_delete_question 55 | assert_no_difference 'Question.count' do 56 | assert_difference 'QuestionnairesQuestion.count', -1 do 57 | @questionnaire.questions.delete(@questionnaire.questions.first) 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /test/unit/qwester/rule_set_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | module Qwester 3 | 4 | class RuleSetTest < ActiveSupport::TestCase 5 | def setup 6 | @rule_set = RuleSet.find(1) 7 | @answer = Answer.find(1) 8 | @other_answer = Answer.find(2) 9 | @rule_set.answers << @answer 10 | end 11 | 12 | def test_match 13 | assert(@rule_set.match(@rule_set.answers), "should match if rule_set.answers equal answers passed in") 14 | end 15 | 16 | def test_match_when_additional_answers_passed_in 17 | assert(@rule_set.match([@rule_set.answers, @other_answer].flatten), "should match as long as answers passed in include ruleset.answers") 18 | end 19 | 20 | def test_match_failure 21 | assert_no_rule_set_match([@other_answer]) 22 | end 23 | 24 | def test_partial_match 25 | @rule_set.answers << @other_answer 26 | assert(@rule_set.match([@other_answer]), "should match as long as answers includes @other_answer") 27 | end 28 | 29 | def test_match_when_no_answers_passed_in 30 | assert_no_rule_set_match([]) 31 | end 32 | 33 | def test_match_when_nil_passed_in 34 | assert_no_rule_set_match(nil) 35 | end 36 | 37 | def test_matches 38 | create_more_rule_sets 39 | rule_sets = RuleSet.matching([@answer]) 40 | assert_equal(2, rule_sets.length) 41 | assert(rule_sets.include?(@rule_set), "RuleSet @rule_set should be included") 42 | assert(rule_sets.include?(@rule_set_one), "RuleSet one should be included") 43 | assert(!rule_sets.include?(@rule_set_two), "RuleSet two should not be included") 44 | end 45 | 46 | def test_matchs_with_where 47 | create_more_rule_sets 48 | rule_sets = RuleSet.where(:title => @rule_set.title).matching([@answer]) 49 | assert_equal([@rule_set], rule_sets) 50 | end 51 | 52 | def test_custom_rule 53 | create_custom_rule_set('a1 and a2') 54 | assert(@rule_set.match([@answer, @other_answer]), "Should match as both answers present") 55 | assert(!@rule_set.match([@answer]), "Should not match as other answer missing") 56 | end 57 | 58 | def test_custom_rule_or 59 | create_custom_rule_set('a1 or a2') 60 | assert(@rule_set.match([@answer, @other_answer]), "Should match as both answers present") 61 | assert(@rule_set.match([@answer]), "Should match as answer present") 62 | end 63 | 64 | def test_custom_rule_in 65 | create_custom_rule_set('2 in a1 a2') 66 | assert(@rule_set.match([@answer, @other_answer]), "Should match as both answers present") 67 | assert(!@rule_set.match([@answer]), "Should not match as only one answer present") 68 | end 69 | 70 | def test_matching_answer_sets 71 | create_custom_rule_set('a1 and a2') 72 | assert_equal([[1, 2]], @rule_set.matching_answer_sets) 73 | end 74 | 75 | def test_blocking_answer_sets 76 | create_custom_rule_set('a1 and a2') 77 | assert_equal([[1], [2]], @rule_set.blocking_answer_sets) 78 | end 79 | 80 | def test_default_rules_created_from_answers_on_create 81 | rule_set = RuleSet.create(:title => 'One', :answers => [@answer], :url => 'http://undervale.co.uk') 82 | assert_equal(rule_set.default_rule, rule_set.rule) 83 | rule_set = RuleSet.create(:title => 'Two', :answers => [@answer, @other_answer], :url => 'http://undervale.co.uk') 84 | assert_equal(rule_set.default_rule, rule_set.rule) 85 | end 86 | 87 | def test_default_rule 88 | assert_equal(@answer.rule_label, @rule_set.default_rule) 89 | @rule_set.answers << @other_answer 90 | expected = "#{@answer.rule_label} #{RuleSet::DEFAULT_RULE_JOIN} #{@other_answer.rule_label}" 91 | assert_equal(expected, @rule_set.default_rule) 92 | end 93 | 94 | def test_answers_populated_by_rules 95 | test_custom_rule 96 | assert_equal([@answer, @other_answer], @rule_set.answers) 97 | end 98 | 99 | def test_answers_shrink_when_answer_removed_from_rule 100 | test_answers_populated_by_rules 101 | @rule_set.reload.rule = 'a1' 102 | @rule_set.save 103 | assert_equal([@answer], @rule_set.answers) 104 | end 105 | 106 | def test_answers_expand_when_answer_added_to_rule 107 | @rule_set.rule = 'a1 and a2' 108 | @rule_set.save 109 | assert_equal([@answer, @other_answer], @rule_set.answers) 110 | end 111 | 112 | def test_rule_validation 113 | @rule_set.rule = 'invalid' 114 | @rule_set.save 115 | assert(@rule_set.errors[:rule].present?, "there should be an error against the rule field") 116 | end 117 | 118 | def test_rule_has_url 119 | @rule_set.url = nil 120 | assert(@rule_set.invalid?, "RuleSet should be invalid if no url") 121 | assert(@rule_set.errors[:url], "Error should be on url field") 122 | end 123 | 124 | def test_rule_valid_without_url_if_presentation_set 125 | @rule_set.presentation = 'something' 126 | @rule_set.url = nil 127 | assert(@rule_set.valid?, "RuleSet should be valid") 128 | end 129 | 130 | def test_rule_set_invalid_if_answer_in_rule_does_not_exist 131 | nonexistent_answer_id = Qwester::Answer.select('id').max.id + 1 132 | @rule_set.rule = "a#{nonexistent_answer_id}" 133 | assert @rule_set.invalid?, "Rules set should be invalid" 134 | assert @rule_set.errors[:rule], 'Error should be on rule' 135 | end 136 | 137 | private 138 | def assert_no_rule_set_match(answers = nil) 139 | assert(!@rule_set.match(answers), "should not return true if no match found") 140 | end 141 | 142 | def create_more_rule_sets 143 | @rule_set_one = RuleSet.create(:title => 'One', :answers => [@answer], :url => 'http://undervale.co.uk') 144 | @rule_set_two = RuleSet.create(:title => 'Two', :answers => [@other_answer], :url => 'http://undervale.com') 145 | end 146 | 147 | def create_custom_rule_set(rule) 148 | @rule_set = RuleSet.create(:title => 'Custom', :rule => rule, :url => 'http://undervale.co.uk') 149 | end 150 | 151 | end 152 | 153 | end 154 | -------------------------------------------------------------------------------- /test/unit/qwester_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class QwesterTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | Qwester.active_admin_menu = nil 7 | Qwester.session_key = nil 8 | end 9 | 10 | def test_qwester_set_up_as_a_module 11 | assert_kind_of Module, Qwester 12 | end 13 | 14 | def test_active_admin_load_path 15 | expected = "#{Qwester::Engine.root}/lib/active_admin/admin" 16 | assert_equal(expected, Qwester.active_admin_load_path) 17 | end 18 | 19 | def test_active_admin_menu 20 | assert_equal('Qwester', Qwester.active_admin_menu) 21 | end 22 | 23 | def test_setting_active_admin_menu 24 | text = 'Foo' 25 | Qwester.active_admin_menu = text 26 | assert_equal(text, Qwester.active_admin_menu) 27 | end 28 | 29 | def test_active_admin_menu_none 30 | Qwester.active_admin_menu = 'none' 31 | assert_nil(Qwester.active_admin_menu) 32 | end 33 | 34 | def test_session_key 35 | assert_equal(:qwester_answer_store, Qwester.session_key) 36 | end 37 | 38 | def test_setting_session_key 39 | key = :something_else 40 | Qwester.session_key = key 41 | assert_equal(key, Qwester.session_key) 42 | end 43 | 44 | end 45 | --------------------------------------------------------------------------------