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.
@@ -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 "([^"]*)"$/ do |name|
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
- Question.find(:first, :conditions => hash).should_not be_nil
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
- survey "Kitchen Sink survey" do
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
- question_1 "What is your favorite color?", :pick => :one
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 question's reference identifier is "2a", and the answer's reference_identifier is "1"
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
- # A dependant question demonstrating the count operator. The
37
- # dependency condition checks the answer count for the referenced question.
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 vocab
87
- # :common_identifier # maping to a common vocab
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(>|>=|<|<=|=|!=)\d+$/
41
- op, i = self.operator.scan(/^count(>|>=|<|<=|=|!=)(\d+)$/).flatten
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})
@@ -39,7 +39,7 @@ module Surveyor
39
39
  end
40
40
 
41
41
  def default_args
42
- self.is_mandatory ||= true
42
+ self.is_mandatory ||= false
43
43
  self.display_type ||= "default"
44
44
  self.pick ||= "none"
45
45
  self.data_export_identifier ||= Surveyor::Common.normalize(text)
@@ -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))
@@ -1,3 +1,3 @@
1
1
  module Surveyor
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -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 => true, :display_order => 1)
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 be_true
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.1
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-10-30 00:00:00.000000000 Z
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