surveyor 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +19 -0
- data/features/step_definitions/parser_steps.rb +4 -2
- data/features/surveyor_dependencies.feature +33 -0
- data/features/surveyor_parser.feature +24 -0
- data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +15 -13
- data/lib/surveyor/models/dependency_condition_methods.rb +2 -2
- data/lib/surveyor/models/question_methods.rb +1 -1
- data/lib/surveyor/parser.rb +6 -2
- data/lib/surveyor/version.rb +1 -1
- data/spec/lib/tasks_spec.rake +26 -0
- data/spec/models/dependency_condition_spec.rb +18 -0
- data/spec/models/question_spec.rb +16 -16
- metadata +4 -2
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
History for Surveyor
|
2
2
|
====================
|
3
3
|
|
4
|
+
1.1.0
|
5
|
+
-----
|
6
|
+
|
7
|
+
### Features
|
8
|
+
|
9
|
+
- Breaking change: Question#is_mandatory => false by default. For those who found it useful to have
|
10
|
+
all questions mandatory, the parser accepts `:default_mandatory => true` as an argument to the survey.
|
11
|
+
|
12
|
+
### Fixes
|
13
|
+
|
14
|
+
- fixing and documenting count operators on dependency conditions
|
15
|
+
|
16
|
+
### Infrastructure
|
17
|
+
|
18
|
+
- basic spec for the surveyor task
|
19
|
+
|
4
20
|
1.0.1
|
5
21
|
------
|
6
22
|
|
@@ -10,12 +26,15 @@ History for Surveyor
|
|
10
26
|
now exclude the question or question group from the DOM. These display types are
|
11
27
|
used to inject data (responses) into surveys. Note, custom_class => "hidden" doesn't
|
12
28
|
have any effect until a custom css rule is created by the end user. (#197)
|
29
|
+
|
13
30
|
- more readable parser and more strict parser method aliases (#278)
|
14
31
|
|
15
32
|
### Fixes
|
16
33
|
|
17
34
|
- Replaced deprecated ActiveRecord::Errors#each_full with ActiveRecord::Errors#full_messages. (#363)
|
35
|
+
|
18
36
|
- fixing dependency condition evaluation where #Response.*_value is nil. (#297)
|
37
|
+
|
19
38
|
- fixing grid answers leak, introduced in 5baa7ac3. thanks @jsurrett (#375, #377)
|
20
39
|
|
21
40
|
1.0.0
|
@@ -20,7 +20,7 @@ Given /^the questions?$/ do |q_string|
|
|
20
20
|
SURVEY
|
21
21
|
end
|
22
22
|
|
23
|
-
Given /^I parse redcap file "(
|
23
|
+
Given /^I parse redcap file "(.*)"$/ do |name|
|
24
24
|
Surveyor::RedcapParser.parse File.read(File.join(Rails.root, '..', 'features', 'support', name)), name
|
25
25
|
end
|
26
26
|
|
@@ -50,7 +50,9 @@ Then /^there should be (\d+) question(?:s?) with:$/ do |x, table|
|
|
50
50
|
table.hashes.each do |hash|
|
51
51
|
hash["reference_identifier"] = nil if hash["reference_identifier"] == "nil"
|
52
52
|
hash["custom_class"] = nil if hash["custom_class"] == "nil"
|
53
|
-
|
53
|
+
hash["is_mandatory"] = (hash["is_mandatory"] == "true" ? true : (hash["is_mandatory"] == "false" ? false : hash["is_mandatory"]))
|
54
|
+
result = Question.find(:first, :conditions => hash)
|
55
|
+
result.should_not be_nil
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
@@ -328,3 +328,36 @@ Feature: Survey dependencies
|
|
328
328
|
Then the question "How much does it cost to run your non-passive heating solutions?" should be hidden
|
329
329
|
And I choose "Oven"
|
330
330
|
Then the question "How much does it cost to run your non-passive heating solutions?" should be triggered
|
331
|
+
|
332
|
+
@javascript
|
333
|
+
Scenario: Count== dependencies
|
334
|
+
Given I parse
|
335
|
+
"""
|
336
|
+
survey "Counting" do
|
337
|
+
section "First" do
|
338
|
+
q_counts "How many times do you count a day", :pick => :any
|
339
|
+
a_1 "Once for me"
|
340
|
+
a_2 "Once for you"
|
341
|
+
a_3 "Once for everyone"
|
342
|
+
|
343
|
+
label "Good!"
|
344
|
+
dependency :rule => "A"
|
345
|
+
condition_A :q_counts, "count==1"
|
346
|
+
|
347
|
+
label "Twice as good!"
|
348
|
+
dependency :rule => "A"
|
349
|
+
condition_A :q_counts, "count==2"
|
350
|
+
end
|
351
|
+
end
|
352
|
+
"""
|
353
|
+
When I go to the surveys page
|
354
|
+
And I start the "Counting" survey
|
355
|
+
Then I should see "How many times do you count a day"
|
356
|
+
And the element "#q_2" should be hidden
|
357
|
+
And the element "#q_3" should be hidden
|
358
|
+
When I check "Once for me"
|
359
|
+
Then the element "#q_2" should not be hidden
|
360
|
+
And the element "#q_3" should be hidden
|
361
|
+
When I check "Once for you"
|
362
|
+
Then the element "#q_3" should not be hidden
|
363
|
+
And the element "#q_2" should be hidden
|
@@ -449,3 +449,27 @@ Feature: Survey parser
|
|
449
449
|
| sad | 0 |
|
450
450
|
| indifferent | 1 |
|
451
451
|
| happy | 2 |
|
452
|
+
|
453
|
+
Scenario: Parsing mandatory questions
|
454
|
+
Given I parse
|
455
|
+
"""
|
456
|
+
survey "Chores", :default_mandatory => true do
|
457
|
+
section "Morning" do
|
458
|
+
q "Did you take out the trash", :pick => :one
|
459
|
+
a "Yes"
|
460
|
+
a "No"
|
461
|
+
|
462
|
+
q "Did you do the laundry", :pick => :one
|
463
|
+
a "Yes"
|
464
|
+
a "No"
|
465
|
+
|
466
|
+
q "Optional comments", :is_mandatory => false
|
467
|
+
a :string
|
468
|
+
end
|
469
|
+
end
|
470
|
+
"""
|
471
|
+
And there should be 3 questions with:
|
472
|
+
| text | is_mandatory |
|
473
|
+
| Did you take out the trash | true |
|
474
|
+
| Did you do the laundry | true |
|
475
|
+
| Optional comments | false |
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
2
|
+
# Question#is_mandatory is now false by default. The default_mandatory option allows you to set
|
3
|
+
# is_mandatory for all questions in a survey.
|
4
|
+
survey "Kitchen Sink survey", :default_mandatory => false do
|
3
5
|
|
4
6
|
section "Basic questions" do
|
5
7
|
# A label is a question that accepts no answers
|
6
8
|
label "These questions are examples of the basic supported input types"
|
7
9
|
|
8
10
|
# A basic question with radio buttons
|
9
|
-
|
11
|
+
question "What is your favorite color?", :pick => :one
|
10
12
|
answer "red"
|
11
13
|
answer "blue"
|
12
14
|
answer "green"
|
@@ -14,7 +16,10 @@ survey "Kitchen Sink survey" do
|
|
14
16
|
answer :other
|
15
17
|
|
16
18
|
# A basic question with checkboxes
|
17
|
-
# "question" and "answer" may be abbreviated as "q" and "a"
|
19
|
+
# The "question" and "answer" methods may be abbreviated as "q" and "a".
|
20
|
+
# Append a reference identifier (a short string used for dependencies and validations) using the "_" underscore.
|
21
|
+
# Question reference identifiers must be unique within a survey.
|
22
|
+
# Answer reference identifiers must be unique within a question
|
18
23
|
q_2 "Choose the colors you don't like", :pick => :any
|
19
24
|
a_1 "red"
|
20
25
|
a_2 "blue"
|
@@ -23,8 +28,7 @@ survey "Kitchen Sink survey" do
|
|
23
28
|
a :omit
|
24
29
|
|
25
30
|
# A dependent question, with conditions and rule to logically join them
|
26
|
-
# the
|
27
|
-
# question reference identifiers used in conditions need to be unique on a survey for the lookups to work
|
31
|
+
# If the conditions, logically joined into the rule, are met, then the question will be shown.
|
28
32
|
q_2a "Please explain why you don't like this color?"
|
29
33
|
a_1 "explanation", :text, :help_text => "Please give an explanation for each color you don't like"
|
30
34
|
dependency :rule => "A or B or C or D"
|
@@ -33,10 +37,8 @@ survey "Kitchen Sink survey" do
|
|
33
37
|
condition_C :q_2, "==", :a_3
|
34
38
|
condition_D :q_2, "==", :a_4
|
35
39
|
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# It understands conditions of the form count> count< count>= count<=
|
39
|
-
# count!=
|
40
|
+
# The count operator checks how many responses exist for the referenced question.
|
41
|
+
# It understands conditions of the form: count== count!= count> count< count>= count<=
|
40
42
|
q_2b "Please explain why you dislike so many colors?"
|
41
43
|
a_1 "explanation", :text
|
42
44
|
dependency :rule => "Z"
|
@@ -81,10 +83,10 @@ survey "Kitchen Sink survey" do
|
|
81
83
|
condition_A :q_cooling_1, "!=", :a_4
|
82
84
|
|
83
85
|
# Surveys, sections, questions, groups, and answers all take the following reference arguments
|
84
|
-
# :reference_identifier # usually from paper
|
85
|
-
# :data_export_identifier # data export
|
86
|
-
# :common_namespace # maping to a common
|
87
|
-
# :common_identifier # maping to a common
|
86
|
+
# :reference_identifier # used for parsing references, usually derived from the paper version
|
87
|
+
# :data_export_identifier # used for data export
|
88
|
+
# :common_namespace # maping to a common vocabulary - the namespace
|
89
|
+
# :common_identifier # maping to a common vocabulary - the identifier within the namespace
|
88
90
|
q "Get me started on an improv sketch", :reference_identifier => "improv_start", :data_export_identifier => "i_s", :common_namespace => "sketch comedy questions", :common_identifier => "get me started"
|
89
91
|
a "who", :string
|
90
92
|
a "what", :string
|
@@ -37,8 +37,8 @@ module Surveyor
|
|
37
37
|
def to_hash(response_set)
|
38
38
|
# all responses to associated question
|
39
39
|
responses = question.blank? ? [] : response_set.responses.where("responses.answer_id in (?)", question.answer_ids).all
|
40
|
-
if self.operator.match /^count(
|
41
|
-
op, i = self.operator.scan(/^count(
|
40
|
+
if self.operator.match /^count(>|>=|<|<=|==|!=)\d+$/
|
41
|
+
op, i = self.operator.scan(/^count(>|>=|<|<=|==|!=)(\d+)$/).flatten
|
42
42
|
# logger.warn({rule_key.to_sym => responses.count.send(op, i.to_i)})
|
43
43
|
return {rule_key.to_sym => (op == "!=" ? !responses.count.send("==", i.to_i) : responses.count.send(op, i.to_i))}
|
44
44
|
elsif operator == "!=" and (responses.blank? or responses.none?{|r| r.answer.id == self.answer.id})
|
data/lib/surveyor/parser.rb
CHANGED
@@ -152,9 +152,11 @@ module SurveyorParserSurveyMethods
|
|
152
152
|
|
153
153
|
# build and set context
|
154
154
|
title = args[0]
|
155
|
+
args[1] ||= {}
|
156
|
+
context[:default_mandatory] = args[1].delete(:default_mandatory) || false
|
155
157
|
self.attributes = ({
|
156
158
|
:title => title,
|
157
|
-
:reference_identifier => reference_identifier }.merge(args[1]
|
159
|
+
:reference_identifier => reference_identifier }.merge(args[1]))
|
158
160
|
context[:survey] = self
|
159
161
|
end
|
160
162
|
def clear(context)
|
@@ -165,7 +167,8 @@ module SurveyorParserSurveyMethods
|
|
165
167
|
:bad_references => [],
|
166
168
|
:duplicate_references => [],
|
167
169
|
:dependency_conditions => [],
|
168
|
-
:questions_with_correct_answers => {}
|
170
|
+
:questions_with_correct_answers => {},
|
171
|
+
:default_mandatory => false
|
169
172
|
})
|
170
173
|
end
|
171
174
|
end
|
@@ -239,6 +242,7 @@ module SurveyorParserQuestionMethods
|
|
239
242
|
self.attributes = ({
|
240
243
|
:question_group => context[:question_group],
|
241
244
|
:reference_identifier => reference_identifier,
|
245
|
+
:is_mandatory => context[:default_mandatory],
|
242
246
|
:text => text,
|
243
247
|
:display_type => (original_method =~ /label|image/ ? original_method : "default"),
|
244
248
|
:display_order => context[:survey_section].questions.size }.merge(hash_args))
|
data/lib/surveyor/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require "rake"
|
3
|
+
|
4
|
+
describe "app rake tasks" do
|
5
|
+
before(:all) do # do this only once before all tests
|
6
|
+
@rake = Rake::Application.new
|
7
|
+
Rake.application = @rake
|
8
|
+
Rake.application.rake_require "lib/tasks/surveyor_tasks"
|
9
|
+
Rake::Task.define_task(:environment)
|
10
|
+
end
|
11
|
+
after do # after every test
|
12
|
+
@rake[@task_name].reenable if @task_name
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have 'environment' as a prereq" do
|
16
|
+
@task_name = "surveyor:parse"
|
17
|
+
@rake[@task_name].prerequisites.should include("environment")
|
18
|
+
end
|
19
|
+
it "should should trace" do
|
20
|
+
ENV["FILE"]="surveys/kitchen_sink_survey.rb"
|
21
|
+
@task_name = "surveyor:parse"
|
22
|
+
@rake.options.trace = true
|
23
|
+
Surveyor::Parser.should_receive(:parse).with(File.read(File.join(Rails.root, ENV["FILE"])), {:trace => true})
|
24
|
+
@rake[@task_name].invoke
|
25
|
+
end
|
26
|
+
end
|
@@ -58,6 +58,24 @@ describe DependencyCondition do
|
|
58
58
|
@dependency_condition.operator = "#"
|
59
59
|
@dependency_condition.should have(1).error_on(:operator)
|
60
60
|
end
|
61
|
+
it "should have a properly formed count operator" do
|
62
|
+
%w(count>1 count<1 count>=1 count<=1 count==1 count!=1).each do |o|
|
63
|
+
@dependency_condition.operator = o
|
64
|
+
@dependency_condition.should have(0).errors_on(:operator)
|
65
|
+
end
|
66
|
+
%w(count> count< count>= count<= count== count!=).each do |o|
|
67
|
+
@dependency_condition.operator = o
|
68
|
+
@dependency_condition.should have(1).errors_on(:operator)
|
69
|
+
end
|
70
|
+
%w(count=1 count><1 count<>1 count!1 count!!1 count=>1 count=<1).each do |o|
|
71
|
+
@dependency_condition.operator = o
|
72
|
+
@dependency_condition.should have(1).errors_on(:operator)
|
73
|
+
end
|
74
|
+
%w(count= count>< count<> count! count!! count=> count=< count> count< count>= count<= count== count!=).each do |o|
|
75
|
+
@dependency_condition.operator = o
|
76
|
+
@dependency_condition.should have(1).errors_on(:operator)
|
77
|
+
end
|
78
|
+
end
|
61
79
|
|
62
80
|
it "should protect timestamps" do
|
63
81
|
saved_attrs = @dependency_condition.attributes
|
@@ -3,24 +3,24 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe Question, "when creating a new question" do
|
4
4
|
before(:each) do
|
5
5
|
@ss = mock_model(SurveySection)
|
6
|
-
@question = Question.new(:text => "What is your favorite color?", :survey_section => @ss, :is_mandatory =>
|
6
|
+
@question = Question.new(:text => "What is your favorite color?", :survey_section => @ss, :is_mandatory => false, :display_order => 1)
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should be invalid without text" do
|
10
10
|
@question.text = nil
|
11
11
|
@question.should have(1).error_on(:text)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
it "should have a parent survey section" do
|
15
15
|
# this causes issues with building and saving
|
16
16
|
# @question.survey_section = nil
|
17
17
|
# @question.should have(1).error_on(:survey_section_id)
|
18
18
|
end
|
19
|
-
|
20
|
-
it "should be mandatory by default" do
|
21
|
-
@question.mandatory?.should
|
19
|
+
|
20
|
+
it "should not be mandatory by default" do
|
21
|
+
@question.mandatory?.should be_false
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it "should convert pick attribute to string" do
|
25
25
|
@question.pick.should == "none"
|
26
26
|
@question.pick = :one
|
@@ -28,7 +28,7 @@ describe Question, "when creating a new question" do
|
|
28
28
|
@question.pick = nil
|
29
29
|
@question.pick.should == nil
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
it "should split the text" do
|
33
33
|
@question.split_text.should == "What is your favorite color?"
|
34
34
|
@question.split_text(:pre).should == "What is your favorite color?"
|
@@ -38,11 +38,11 @@ describe Question, "when creating a new question" do
|
|
38
38
|
@question.split_text(:pre).should == "before"
|
39
39
|
@question.split_text(:post).should == "after|extra"
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
it "should have an api_id" do
|
43
43
|
@question.api_id.length.should == 36
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
it "should protect api_id, timestamps" do
|
47
47
|
saved_attrs = @question.attributes
|
48
48
|
if defined? ActiveModel::MassAssignmentSecurity::Error
|
@@ -63,11 +63,11 @@ describe Question, "that has answers" do
|
|
63
63
|
Factory(:answer, :question => @question, :display_order => 1, :text => "red")
|
64
64
|
Factory(:answer, :question => @question, :display_order => 2, :text => "green")
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
it "should have answers" do
|
68
68
|
@question.answers.should have(3).answers
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
it "should retrieve those answers in display_order" do
|
72
72
|
@question.answers.map(&:display_order).should == [1,2,3]
|
73
73
|
end
|
@@ -79,7 +79,7 @@ describe Question, "that has answers" do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
describe Question, "when interacting with an instance" do
|
82
|
-
|
82
|
+
|
83
83
|
before(:each) do
|
84
84
|
@question = Factory(:question)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ describe Question, "when interacting with an instance" do
|
|
88
88
|
@question.display_type = nil
|
89
89
|
@question.renderer.should == :default
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
it "should let you know if it is part of a group" do
|
93
93
|
@question.question_group = Factory(:question_group)
|
94
94
|
@question.solo?.should be_false
|
@@ -97,7 +97,7 @@ describe Question, "when interacting with an instance" do
|
|
97
97
|
@question.solo?.should be_true
|
98
98
|
@question.part_of_group?.should be_false
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
require 'mustache'
|
102
102
|
class FakeMustacheContext < ::Mustache
|
103
103
|
def site
|
@@ -111,7 +111,7 @@ describe Question, "when interacting with an instance" do
|
|
111
111
|
@question.text = "You are in {{site}}"
|
112
112
|
@question.render_question_text(FakeMustacheContext).should == "You are in Northwestern"
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
end
|
116
116
|
|
117
117
|
describe Question, "with dependencies" do
|
@@ -131,5 +131,5 @@ describe Question, "with dependencies" do
|
|
131
131
|
@question.destroy
|
132
132
|
Dependency.find_by_id(dep_id).should be_nil
|
133
133
|
end
|
134
|
-
|
134
|
+
|
135
135
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surveyor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-11-02 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -521,6 +521,7 @@ files:
|
|
521
521
|
- spec/lib/parser_spec.rb
|
522
522
|
- spec/lib/rake_kitchen_sink.rb
|
523
523
|
- spec/lib/redcap_parser_spec.rb
|
524
|
+
- spec/lib/tasks_spec.rake
|
524
525
|
- spec/lib/unparser_spec.rb
|
525
526
|
- spec/models/answer_spec.rb
|
526
527
|
- spec/models/dependency_condition_spec.rb
|
@@ -596,6 +597,7 @@ test_files:
|
|
596
597
|
- spec/lib/parser_spec.rb
|
597
598
|
- spec/lib/rake_kitchen_sink.rb
|
598
599
|
- spec/lib/redcap_parser_spec.rb
|
600
|
+
- spec/lib/tasks_spec.rake
|
599
601
|
- spec/lib/unparser_spec.rb
|
600
602
|
- spec/models/answer_spec.rb
|
601
603
|
- spec/models/dependency_condition_spec.rb
|