surveyor 0.14.5 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +12 -4
- data/VERSION +1 -1
- data/features/step_definitions/surveyor_steps.rb +64 -4
- data/features/surveyor.feature +144 -11
- data/lib/surveyor.rb +0 -4
- data/lib/surveyor/acts_as_response.rb +13 -29
- data/lib/surveyor/common.rb +2 -2
- data/lib/surveyor/models/answer_methods.rb +14 -1
- data/lib/surveyor/models/dependency_condition_methods.rb +4 -3
- data/lib/surveyor/models/question_methods.rb +11 -1
- data/lib/surveyor/models/response_methods.rb +2 -1
- data/lib/surveyor/models/survey_methods.rb +10 -2
- data/lib/surveyor/models/survey_section_methods.rb +11 -1
- data/lib/surveyor/models/validation_condition_methods.rb +3 -2
- data/lib/surveyor/models/validation_methods.rb +2 -2
- data/lib/surveyor/parser.rb +274 -0
- data/lib/tasks/surveyor_tasks.rake +25 -25
- data/spec/factories.rb +1 -1
- data/spec/lib/common_spec.rb +22 -0
- data/spec/lib/parser_spec.rb +30 -0
- data/spec/models/answer_spec.rb +6 -5
- data/spec/models/dependency_condition_spec.rb +7 -6
- data/spec/models/question_spec.rb +12 -10
- data/spec/models/survey_section_spec.rb +3 -2
- data/spec/models/survey_spec.rb +11 -0
- data/spec/models/validation_condition_spec.rb +5 -5
- data/spec/models/validation_spec.rb +5 -4
- data/surveyor.gemspec +7 -23
- metadata +10 -26
- data/lib/fixtures_extensions.rb +0 -6
- data/script/surveyor/answer.rb +0 -54
- data/script/surveyor/base.rb +0 -75
- data/script/surveyor/dependency.rb +0 -13
- data/script/surveyor/dependency_condition.rb +0 -40
- data/script/surveyor/parser.rb +0 -223
- data/script/surveyor/question.rb +0 -59
- data/script/surveyor/question_group.rb +0 -26
- data/script/surveyor/specs/answer_spec.rb +0 -29
- data/script/surveyor/specs/question_spec.rb +0 -63
- data/script/surveyor/specs/spec_helper.rb +0 -7
- data/script/surveyor/specs/survey_section_spec.rb +0 -23
- data/script/surveyor/specs/validation_condition_spec.rb +0 -20
- data/script/surveyor/specs/validation_spec.rb +0 -20
- data/script/surveyor/survey.rb +0 -34
- data/script/surveyor/survey_section.rb +0 -21
- data/script/surveyor/validation.rb +0 -21
- data/script/surveyor/validation_condition.rb +0 -21
- data/script/surveyor/whr_dsl.tmproj +0 -244
- 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.
|
1
|
+
0.15.0
|
@@ -1,7 +1,67 @@
|
|
1
|
-
Given /^I
|
2
|
-
|
1
|
+
Given /^I parse$/ do |string|
|
2
|
+
Surveyor::Parser.parse(string)
|
3
3
|
end
|
4
4
|
|
5
|
-
Then /^
|
6
|
-
|
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
|
data/features/surveyor.feature
CHANGED
@@ -1,11 +1,144 @@
|
|
1
|
-
Feature:
|
2
|
-
As
|
3
|
-
I want to
|
4
|
-
So that I can
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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,33 +1,17 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
|
3
1
|
module Surveyor
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
data/lib/surveyor/common.rb
CHANGED
@@ -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
|
-
|
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 :
|
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, :
|
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 :
|
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
|
-
|
38
|
-
|
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, :
|
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
|