surveyor 0.14.5 → 0.15.0

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