surveyor 0.14.5 → 0.15.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.
Files changed (49) hide show
  1. data/README.md +12 -4
  2. data/VERSION +1 -1
  3. data/features/step_definitions/surveyor_steps.rb +64 -4
  4. data/features/surveyor.feature +144 -11
  5. data/lib/surveyor.rb +0 -4
  6. data/lib/surveyor/acts_as_response.rb +13 -29
  7. data/lib/surveyor/common.rb +2 -2
  8. data/lib/surveyor/models/answer_methods.rb +14 -1
  9. data/lib/surveyor/models/dependency_condition_methods.rb +4 -3
  10. data/lib/surveyor/models/question_methods.rb +11 -1
  11. data/lib/surveyor/models/response_methods.rb +2 -1
  12. data/lib/surveyor/models/survey_methods.rb +10 -2
  13. data/lib/surveyor/models/survey_section_methods.rb +11 -1
  14. data/lib/surveyor/models/validation_condition_methods.rb +3 -2
  15. data/lib/surveyor/models/validation_methods.rb +2 -2
  16. data/lib/surveyor/parser.rb +274 -0
  17. data/lib/tasks/surveyor_tasks.rake +25 -25
  18. data/spec/factories.rb +1 -1
  19. data/spec/lib/common_spec.rb +22 -0
  20. data/spec/lib/parser_spec.rb +30 -0
  21. data/spec/models/answer_spec.rb +6 -5
  22. data/spec/models/dependency_condition_spec.rb +7 -6
  23. data/spec/models/question_spec.rb +12 -10
  24. data/spec/models/survey_section_spec.rb +3 -2
  25. data/spec/models/survey_spec.rb +11 -0
  26. data/spec/models/validation_condition_spec.rb +5 -5
  27. data/spec/models/validation_spec.rb +5 -4
  28. data/surveyor.gemspec +7 -23
  29. metadata +10 -26
  30. data/lib/fixtures_extensions.rb +0 -6
  31. data/script/surveyor/answer.rb +0 -54
  32. data/script/surveyor/base.rb +0 -75
  33. data/script/surveyor/dependency.rb +0 -13
  34. data/script/surveyor/dependency_condition.rb +0 -40
  35. data/script/surveyor/parser.rb +0 -223
  36. data/script/surveyor/question.rb +0 -59
  37. data/script/surveyor/question_group.rb +0 -26
  38. data/script/surveyor/specs/answer_spec.rb +0 -29
  39. data/script/surveyor/specs/question_spec.rb +0 -63
  40. data/script/surveyor/specs/spec_helper.rb +0 -7
  41. data/script/surveyor/specs/survey_section_spec.rb +0 -23
  42. data/script/surveyor/specs/validation_condition_spec.rb +0 -20
  43. data/script/surveyor/specs/validation_spec.rb +0 -20
  44. data/script/surveyor/survey.rb +0 -34
  45. data/script/surveyor/survey_section.rb +0 -21
  46. data/script/surveyor/validation.rb +0 -21
  47. data/script/surveyor/validation_condition.rb +0 -21
  48. data/script/surveyor/whr_dsl.tmproj +0 -244
  49. data/spec/lib/surveyor_spec.rb +0 -44
data/README.md CHANGED
@@ -84,10 +84,6 @@ Try out the "kitchen sink" survey:
84
84
 
85
85
  rake surveyor FILE=surveys/kitchen_sink_survey.rb
86
86
 
87
- The rake surveyor task overwrites previous surveys by default, but can append instead:
88
-
89
- rake surveyor FILE=surveys/kitchen_sink_survey.rb APPEND=true
90
-
91
87
  The rake tasks above generate surveys in our custom survey DSL (which is a great format for end users and stakeholders to use).
92
88
  After you have run them start up your app and go to:
93
89
 
@@ -114,6 +110,18 @@ To work on the plugin code (for enhancements, and bug fixes, etc...) fork this g
114
110
 
115
111
  # Changes
116
112
 
113
+ 0.15.0
114
+
115
+ * prevent duplicate survey titles by appending incrementing numbers
116
+ * rake task to remove a survey. closes #64
117
+ * cleanup of old parsing strategy
118
+ * features and specs and new parser. closes #62
119
+ * first test driven work on parser
120
+ * moving parser and common specs so they run automatically. fixing some spec errors
121
+ * first shot a surveyor parser. some parts untested, but coded to determine style. references #62
122
+ * refactoring counters
123
+ * fixing failing specs. fixes acts\_as\_response issues
124
+
117
125
  0.14.5
118
126
 
119
127
  * use modules to include model methods. re-closes #77
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.5
1
+ 0.15.0
@@ -1,7 +1,67 @@
1
- Given /^I am a banana$/ do
2
- @me = "banana"
1
+ Given /^I parse$/ do |string|
2
+ Surveyor::Parser.parse(string)
3
3
  end
4
4
 
5
- Then /^I should be a banana$/ do
6
- @me.should == "banana"
5
+ Then /^there should be (\d+) survey(?:s?) with:$/ do |x, table|
6
+ Survey.count.should == x.to_i
7
+ table.hashes.each do |hash|
8
+ Survey.find(:first, :conditions => hash).should_not be_nil
9
+ end
10
+ end
11
+
12
+ Then /^there should be (\d+) question groups with:$/ do |x, table|
13
+ QuestionGroup.count.should == x.to_i
14
+ table.hashes.each do |hash|
15
+ QuestionGroup.find(:first, :conditions => hash).should_not be_nil
16
+ end
17
+ end
18
+
19
+ Then /^there should be (\d+) question(?:s?) with:$/ do |x, table|
20
+ Question.count.should == x.to_i
21
+ table.hashes.each do |hash|
22
+ hash["reference_identifier"] = nil if hash["reference_identifier"] == "nil"
23
+ hash["custom_class"] = nil if hash["custom_class"] == "nil"
24
+ Question.find(:first, :conditions => hash).should_not be_nil
25
+ end
26
+ end
27
+
28
+ Then /^there should be (\d+) answer(?:s?) with:$/ do |x, table|
29
+ Answer.count.should == x.to_i
30
+ table.hashes.each do |hash|
31
+ hash["reference_identifier"] = nil if hash["reference_identifier"] == "nil"
32
+ Answer.find(:first, :conditions => hash).should_not be_nil
33
+ end
34
+ end
35
+
36
+ Then /^there should be (\d+) dependency with:$/ do |x, table|
37
+ Dependency.count.should == x.to_i
38
+ table.hashes.each do |hash|
39
+ Dependency.find(:first, :conditions => hash).should_not be_nil
40
+ end
41
+ end
42
+
43
+ Then /^there should be (\d+) resolved dependency_condition(?:s?) with:$/ do |x, table|
44
+ DependencyCondition.count.should == x.to_i
45
+ table.hashes.each do |hash|
46
+ d = DependencyCondition.find(:first, :conditions => hash)
47
+ d.should_not be_nil
48
+ d.question.should_not be_nil
49
+ d.answer.should_not be_nil
50
+ end
51
+ end
52
+
53
+
54
+ Then /^there should be (\d+) validation(?:s?) with:$/ do |x, table|
55
+ Validation.count.should == x.to_i
56
+ table.hashes.each do |hash|
57
+ Validation.find(:first, :conditions => hash).should_not be_nil
58
+ end
59
+ end
60
+
61
+ Then /^there should be (\d+) validation_condition(?:s?) with:$/ do |x, table|
62
+ ValidationCondition.count.should == x.to_i
63
+ table.hashes.each do |hash|
64
+ hash["integer_value"] = nil if hash["integer_value"] == "nil"
65
+ ValidationCondition.find(:first, :conditions => hash).should_not be_nil
66
+ end
7
67
  end
@@ -1,11 +1,144 @@
1
- Feature: Logging in
2
- As an anonymous user with an account
3
- I want to log in to my account
4
- So that I can be myself
5
-
6
- #
7
- # Log in: get form
8
- #
9
- Scenario: Anonymous user can get a login form.
10
- Given I am a banana
11
- Then I should be a banana
1
+ Feature: Survey creation
2
+ As a
3
+ I want to write out the survey in the DSL
4
+ So that I can give it to survey participants
5
+
6
+ Scenario: Basic questions
7
+ Given I parse
8
+ """
9
+ survey "Simple survey" do
10
+ section "Basic questions" do
11
+ label "These questions are examples of the basic supported input types"
12
+
13
+ question_1 "What is your favorite color?", :pick => :one
14
+ answer "red"
15
+ answer "blue"
16
+ answer "green"
17
+ answer :other
18
+
19
+ q_2b "Choose the colors you don't like", :pick => :any
20
+ a_1 "orange"
21
+ a_2 "purple"
22
+ a_3 "brown"
23
+ a :omit
24
+ end
25
+ end
26
+ """
27
+ Then there should be 1 survey with:
28
+ | title |
29
+ | Simple survey |
30
+ And there should be 3 questions with:
31
+ | reference_identifier | text | pick | display_type |
32
+ | nil | These questions are examples of the basic supported input types | none | label |
33
+ | 1 | What is your favorite color? | one | default |
34
+ | 2b | Choose the colors you don't like | any | default |
35
+ And there should be 8 answers with:
36
+ | reference_identifier | text | response_class |
37
+ | nil | red | answer |
38
+ | nil | blue | answer |
39
+ | nil | green | answer |
40
+ | nil | Other | answer |
41
+ | 1 | orange | answer |
42
+ | 2 | purple | answer |
43
+ | 3 | brown | answer |
44
+ | nil | Omit | answer |
45
+
46
+ Scenario: More complex questions
47
+ Given I parse
48
+ """
49
+ survey "Complex survey" do
50
+ section "Complicated questions" do
51
+ grid "Tell us how you feel today" do
52
+ a "-2"
53
+ a "-1"
54
+ a "0"
55
+ a "1"
56
+ a "2"
57
+ q "down|up" , :pick => :one
58
+ q "sad|happy", :pick => :one
59
+ q "limp|perky", :pick => :one
60
+ end
61
+
62
+ q "Choose your favorite utensils and enter frequency of use (daily, weekly, monthly, etc...)", :pick => :any
63
+ a "spoon", :string
64
+ a "fork", :string
65
+ a "knife", :string
66
+ a :other, :string
67
+
68
+ repeater "Tell us about the cars you own" do
69
+ q "Make", :pick => :one, :display_type => :dropdown
70
+ a "Toyota"
71
+ a "Ford"
72
+ a "GMChevy"
73
+ a "Ferrari"
74
+ a "Tesla"
75
+ a "Honda"
76
+ a "Other weak brand"
77
+ q "Model"
78
+ a :string
79
+ q "Year"
80
+ a :string
81
+ end
82
+ end
83
+ end
84
+ """
85
+ Then there should be 1 survey with:
86
+ | title |
87
+ | Complex survey |
88
+ And there should be 2 question groups with:
89
+ | text | display_type |
90
+ | Tell us how you feel today | grid |
91
+ | Tell us about the cars you own | repeater |
92
+ And there should be 7 questions with:
93
+ | text | pick | display_type |
94
+ | Make | one | dropdown |
95
+ And there should be 28 answers with:
96
+ | text | response_class |
97
+ | -2 | answer |
98
+ | Other | string |
99
+
100
+ Scenario: Dependencies and validations
101
+ Given I parse
102
+ """
103
+ survey "Dependency and validation survey" do
104
+ section "Conditionals" do
105
+ q_montypython3 "What... is your name? (e.g. It is 'Arthur', King of the Britons)"
106
+ a_1 :string
107
+
108
+ q_montypython4 "What... is your quest? (e.g. To seek the Holy Grail)"
109
+ a_1 :string
110
+ dependency :rule => "A"
111
+ condition_A :q_montypython3, "==", {:string_value => "It is 'Arthur', King of the Britons", :answer_reference => "1"}
112
+
113
+ q "How many pets do you own?"
114
+ a :integer
115
+ validation :rule => "A"
116
+ condition_A ">=", :integer_value => 0
117
+
118
+ q "What is your address?", :custom_class => 'address'
119
+ a :text, :custom_class => 'mapper'
120
+ validation :rule => "AC"
121
+ vcondition_AC "=~", :regexp => /[0-9a-zA-z\. #]/
122
+ end
123
+ end
124
+ """
125
+ Then there should be 1 survey with:
126
+ | title |
127
+ | Dependency and validation survey |
128
+ And there should be 4 questions with:
129
+ | text | pick | display_type | custom_class |
130
+ | What... is your name? (e.g. It is 'Arthur', King of the Britons) | none | default | nil |
131
+ | What is your address? | none | default | address |
132
+ And there should be 1 dependency with:
133
+ | rule |
134
+ | A |
135
+ And there should be 1 resolved dependency_condition with:
136
+ | rule_key |
137
+ | A |
138
+ And there should be 2 validations with:
139
+ | rule |
140
+ | A |
141
+ | AC |
142
+ And there should be 2 validation_conditions with:
143
+ | rule_key | integer_value |
144
+ | A | 0 |
data/lib/surveyor.rb CHANGED
@@ -1,6 +1,2 @@
1
1
  require 'surveyor/common'
2
2
  require 'surveyor/acts_as_response'
3
-
4
- #
5
- # Dir.glob(File.join(File.dirname(__FILE__),'..','app','models','*.rb')).each{|f| require f}
6
- # Dir.glob(File.join(File.dirname(__FILE__),'..','app','helpers','*.rb')).each{|f| require f}
@@ -1,33 +1,17 @@
1
- require 'active_record'
2
-
3
1
  module Surveyor
4
- module Response
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
9
- module ClassMethods
10
- def acts_as_response
11
- include Surveyor::Response::InstanceMethods
12
- end
13
- end
14
-
15
- module InstanceMethods
16
- # Returns the response as a particular response_class type
17
- def as(type_symbol)
18
- return case type_symbol.to_sym
19
- when :string, :text, :integer, :float, :datetime
20
- self.send("#{type_symbol}_value".to_sym)
21
- when :date
22
- self.datetime_value.nil? ? nil : self.datetime_value.to_date
23
- when :time
24
- self.datetime_value.nil? ? nil : self.datetime_value.to_time
25
- else # :answer_id
26
- self.answer_id
27
- end
2
+ module ActsAsResponse
3
+ # Returns the response as a particular response_class type
4
+ def as(type_symbol)
5
+ return case type_symbol.to_sym
6
+ when :string, :text, :integer, :float, :datetime
7
+ self.send("#{type_symbol}_value".to_sym)
8
+ when :date
9
+ self.datetime_value.nil? ? nil : self.datetime_value.to_date
10
+ when :time
11
+ self.datetime_value.nil? ? nil : self.datetime_value.to_time
12
+ else # :answer_id
13
+ self.answer_id
28
14
  end
29
15
  end
30
16
  end
31
- end
32
-
33
- ActiveRecord::Base.send(:include, Surveyor::Response)
17
+ end
@@ -1,7 +1,7 @@
1
1
  module Surveyor
2
2
  class Common
3
3
  RAND_CHARS = [('a'..'z'), ('A'..'Z'), (0..9)].map{|r| r.to_a}.flatten.to_s
4
- OPERATORS = %w(== != < > <= >=)
4
+ OPERATORS = %w(== != < > <= >= =~)
5
5
 
6
6
  class << self
7
7
  def make_tiny_code(len = 10)
@@ -14,7 +14,7 @@ module Surveyor
14
14
 
15
15
  def to_normalized_string(text)
16
16
  words_to_omit = %w(a be but has have in is it of on or the to when)
17
- col_text = text.gsub(/(<[^>]*>)|\n|\t/s, ' ') # Remove html tags
17
+ col_text = text.to_s.gsub(/(<[^>]*>)|\n|\t/s, ' ') # Remove html tags
18
18
  col_text.downcase! # Remove capitalization
19
19
  col_text.gsub!(/\"|\'/, '') # Remove potential problem characters
20
20
  col_text.gsub!(/\(.*?\)/,'') # Remove text inside parens
@@ -12,10 +12,23 @@ module Surveyor
12
12
 
13
13
  # Validations
14
14
  base.send :validates_presence_of, :text
15
- base.send :validates_numericality_of, :question_id, :allow_nil => false, :only_integer => true
15
+ # this causes issues with building and saving
16
+ # base.send :validates_numericality_of, :question_id, :allow_nil => false, :only_integer => true
16
17
  end
17
18
 
18
19
  # Instance Methods
20
+ def initialize(*args)
21
+ super(*args)
22
+ default_args
23
+ end
24
+
25
+ def default_args
26
+ self.display_order ||= self.question ? self.question.answers.count : 0
27
+ # self.is_exclusive ||= false
28
+ # self.hide_label ||= false
29
+ # self.response_class ||= "answer"
30
+ end
31
+
19
32
  def renderer(q = question)
20
33
  r = [q.pick.to_s, self.response_class].compact.map(&:downcase).join("_")
21
34
  r.blank? ? :default : r.to_sym
@@ -9,12 +9,13 @@ module Surveyor
9
9
  base.send :belongs_to, :question
10
10
 
11
11
  # Validations
12
- base.send :validates_numericality_of, :dependency_id, :question_id, :answer_id
13
12
  base.send :validates_presence_of, :operator, :rule_key
14
13
  base.send :validates_inclusion_of, :operator, :in => Surveyor::Common::OPERATORS
15
14
  base.send :validates_uniqueness_of, :rule_key, :scope => :dependency_id
16
-
17
- base.send :acts_as_response # includes "as" instance method
15
+ # this causes issues with building and saving
16
+ # base.send :validates_numericality_of, :question_id, :answer_id, :dependency_id
17
+
18
+ base.send :include, Surveyor::ActsAsResponse # includes "as" instance method
18
19
 
19
20
  # Class methods
20
21
  base.instance_eval do
@@ -12,7 +12,9 @@ module Surveyor
12
12
  base.send :default_scope, :order => "display_order ASC"
13
13
 
14
14
  # Validations
15
- base.send :validates_presence_of, :text, :survey_section_id, :display_order
15
+ base.send :validates_presence_of, :text, :display_order
16
+ # this causes issues with building and saving
17
+ #, :survey_section_id
16
18
  base.send :validates_inclusion_of, :is_mandatory, :in => [true, false]
17
19
  end
18
20
 
@@ -26,6 +28,14 @@ module Surveyor
26
28
  self.is_mandatory ||= true
27
29
  self.display_type ||= "default"
28
30
  self.pick ||= "none"
31
+ self.display_order ||= self.survey_section ? self.survey_section.questions.count : 0
32
+ end
33
+
34
+ def pick=(val)
35
+ write_attribute(:pick, val.nil? ? nil : val.to_s)
36
+ end
37
+ def display_type=(val)
38
+ write_attribute(:display_type, val.nil? ? nil : val.to_s)
29
39
  end
30
40
 
31
41
  def mandatory?
@@ -10,7 +10,8 @@ module Surveyor
10
10
  # Validations
11
11
  base.send :validates_presence_of, :response_set_id, :question_id, :answer_id
12
12
 
13
- base.send :acts_as_response # includes "as" instance method
13
+ base.send :include, Surveyor::ActsAsResponse # includes "as" instance method
14
+
14
15
  end
15
16
 
16
17
  # Instance Methods
@@ -34,8 +34,16 @@ module Surveyor
34
34
  end
35
35
 
36
36
  def title=(value)
37
- self.access_code = Surveyor::Common.to_normalized_string(value)
38
- super
37
+ adjusted_value = value
38
+ while Survey.find_by_access_code(Survey.to_normalized_string(adjusted_value))
39
+ i ||= 0
40
+ i += 1
41
+ adjusted_value = "#{value} #{i.to_s}"
42
+ end
43
+ self.access_code = Survey.to_normalized_string(adjusted_value)
44
+ super(adjusted_value)
45
+ # self.access_code = Survey.to_normalized_string(value)
46
+ # super
39
47
  end
40
48
 
41
49
  def active?
@@ -11,10 +11,20 @@ module Surveyor
11
11
  base.send :named_scope, :with_includes, { :include => {:questions => [:answers, :question_group, {:dependency => :dependency_conditions}]}}
12
12
 
13
13
  # Validations
14
- base.send :validates_presence_of, :title, :survey, :display_order
14
+ base.send :validates_presence_of, :title, :display_order
15
+ # this causes issues with building and saving
16
+ #, :survey
15
17
  end
16
18
 
17
19
  # Instance Methods
20
+ def initialize(*args)
21
+ super(*args)
22
+ default_args
23
+ end
24
+
25
+ def default_args
26
+ self.display_order ||= self.survey ? self.survey.sections.count : 0
27
+ end
18
28
 
19
29
  end
20
30
  end