surveyor 1.0.1 → 1.1.0

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