surveyor 0.6.10 → 0.7.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.
- data/README.md +13 -2
- data/VERSION +1 -1
- data/app/controllers/surveyor_controller.rb +1 -1
- data/app/models/dependency.rb +21 -6
- data/app/models/question.rb +3 -7
- data/app/models/question_group.rb +13 -1
- data/app/models/response_set.rb +1 -1
- data/app/models/survey_section.rb +1 -1
- data/app/views/partials/_question_group.html.haml +2 -1
- data/generators/surveyor/templates/migrate/create_answers.rb +1 -1
- data/generators/surveyor/templates/migrate/create_dependencies.rb +1 -0
- data/generators/surveyor/templates/migrate/create_question_groups.rb +1 -1
- data/generators/surveyor/templates/migrate/create_questions.rb +1 -1
- data/generators/surveyor/templates/migrate/create_survey_sections.rb +1 -1
- data/generators/surveyor/templates/migrate/create_surveys.rb +1 -1
- data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -67
- data/script/surveyor/answer.rb +7 -21
- data/script/surveyor/dependency.rb +11 -6
- data/script/surveyor/dependency_condition.rb +8 -19
- data/script/surveyor/question.rb +6 -19
- data/script/surveyor/question_group.rb +8 -13
- data/script/surveyor/survey.rb +5 -13
- data/script/surveyor/survey_section.rb +12 -24
- data/surveyor.gemspec +2 -2
- metadata +2 -2
data/README.md
CHANGED
@@ -7,12 +7,12 @@ Surveyor is a rails (gem) plugin, that brings surveys to your rails app. Before
|
|
7
7
|
As a plugin:
|
8
8
|
|
9
9
|
sudo gem install haml
|
10
|
-
script/plugin install git://github.com/breakpointer/surveyor.git -r 'tag v0.
|
10
|
+
script/plugin install git://github.com/breakpointer/surveyor.git -r 'tag v0.7.0'
|
11
11
|
|
12
12
|
Or as a gem plugin:
|
13
13
|
|
14
14
|
# in environment.rb
|
15
|
-
config.gem "surveyor", :version => '>=0.
|
15
|
+
config.gem "surveyor", :version => '>=0.7.0', :source => 'http://gemcutter.org'
|
16
16
|
|
17
17
|
sudo gem install gemcutter
|
18
18
|
gem tumble
|
@@ -103,4 +103,15 @@ The <code>surveyor\_includes</code> helper just calls <code>surveyor\_stylsheets
|
|
103
103
|
|
104
104
|
Surveyor depends on Rails 2.3 and the SASS style sheet language, part of HAML (http://haml.hamptoncatlin.com/download)
|
105
105
|
|
106
|
+
# Changes
|
107
|
+
|
108
|
+
0.7.0
|
109
|
+
|
110
|
+
* new kitchen sink survey with better documentation of DSL
|
111
|
+
* migration misspelling
|
112
|
+
* fixing ordering, dependency conditions evaluation, and changing named scopes for now
|
113
|
+
* DRYing up surveyor DSL models
|
114
|
+
* working on adding dependencies for question groups
|
115
|
+
|
116
|
+
|
106
117
|
Copyright (c) 2008-2009 Brian Chamberlain and Mark Yoon, released under the MIT license
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
@@ -40,7 +40,7 @@ class SurveyorController < ApplicationController
|
|
40
40
|
@survey = Survey.with_sections.find_by_id(@response_set.survey_id)
|
41
41
|
@sections = @survey.sections
|
42
42
|
@section = params[:section] ? @sections.with_includes.find(section_id_from(params[:section])) || @sections.with_includes.first : @sections.with_includes.first
|
43
|
-
@questions = @section.questions
|
43
|
+
@questions = @section.questions
|
44
44
|
else
|
45
45
|
flash[:notice] = "Unable to find your responses to the survey"
|
46
46
|
redirect_to(available_surveys_path)
|
data/app/models/dependency.rb
CHANGED
@@ -1,19 +1,30 @@
|
|
1
1
|
class Dependency < ActiveRecord::Base
|
2
2
|
# Associations
|
3
3
|
belongs_to :question
|
4
|
+
belongs_to :question_group
|
4
5
|
has_many :dependency_conditions
|
5
6
|
|
6
7
|
# Scopes
|
7
|
-
named_scope :depending_on_questions, lambda {|question_ids| {:
|
8
|
+
named_scope :depending_on_questions, lambda {|question_ids| {:joins => :dependency_conditions, :conditions => {:dependency_conditions => {:question_id => question_ids}} }}
|
8
9
|
|
9
10
|
# Validations
|
10
11
|
validates_presence_of :rule
|
11
|
-
validates_format_of :rule, :with => /^(?:and|or|\)|\(|\
|
12
|
-
validates_numericality_of :question_id
|
12
|
+
validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/ #TODO properly formed parenthesis etc.
|
13
|
+
# validates_numericality_of :question_id
|
13
14
|
|
14
15
|
# Attribute aliases
|
15
16
|
alias_attribute :dependent_question_id, :question_id
|
16
|
-
|
17
|
+
|
18
|
+
def question_group_id=(i)
|
19
|
+
write_attribute(:question_id, nil)
|
20
|
+
write_attribute(:question_group_id, i)
|
21
|
+
end
|
22
|
+
|
23
|
+
def question_id=(i)
|
24
|
+
write_attribute(:question_group_id, nil)
|
25
|
+
write_attribute(:question_id, i)
|
26
|
+
end
|
27
|
+
|
17
28
|
# Is the method that determines if this dependency has been met within
|
18
29
|
# the provided response set
|
19
30
|
def met?(response_set)
|
@@ -29,6 +40,7 @@ class Dependency < ActiveRecord::Base
|
|
29
40
|
# calling keyed_condition_pairs will return {:A => true, :B => false}
|
30
41
|
def keyed_conditions(response_set)
|
31
42
|
keyed_pairs = {}
|
43
|
+
# logger.debug dependency_conditions.inspect
|
32
44
|
self.dependency_conditions.each do |dc|
|
33
45
|
keyed_pairs.merge!(dc.to_evaluation_hash(response_set))
|
34
46
|
end
|
@@ -38,8 +50,11 @@ class Dependency < ActiveRecord::Base
|
|
38
50
|
# Does the substiution and evaluation of the dependency rule with the keyed pairs
|
39
51
|
def rule_evaluation(keyed_pairs)
|
40
52
|
# subtitute into rule for evaluation
|
41
|
-
rgx = Regexp.new(self.dependency_conditions.map{|dc| dc.rule_key}.join("|")) # Making a regexp to only look for the keys used in the child conditions
|
42
|
-
#logger.debug
|
53
|
+
rgx = Regexp.new(self.dependency_conditions.map{|dc| ["a","o"].include?(dc.rule_key) ? "#{dc.rule_key}(?!nd|r)" : dc.rule_key}.join("|")) # Making a regexp to only look for the keys used in the child conditions
|
54
|
+
# logger.debug "rule: #{self.rule.inspect}"
|
55
|
+
# logger.debug "rexp: #{rgx.inspect}"
|
56
|
+
# logger.debug "keyp: #{keyed_pairs.inspect}"
|
57
|
+
# logger.debug "subd: #{self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}}"
|
43
58
|
eval(self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}) # returns the evaluation of the rule and the conditions
|
44
59
|
end
|
45
60
|
|
data/app/models/question.rb
CHANGED
@@ -3,12 +3,11 @@ class Question < ActiveRecord::Base
|
|
3
3
|
# Associations
|
4
4
|
belongs_to :survey_section
|
5
5
|
belongs_to :question_group
|
6
|
-
has_many :answers # it might not always have answers
|
6
|
+
has_many :answers, :order => "display_order ASC" # it might not always have answers
|
7
7
|
has_one :dependency
|
8
8
|
|
9
9
|
# Scopes
|
10
10
|
default_scope :order => "display_order ASC"
|
11
|
-
named_scope :in_order, {:order => "display_order ASC"}
|
12
11
|
|
13
12
|
# Validations
|
14
13
|
validates_presence_of :text, :survey_section_id, :display_order
|
@@ -22,8 +21,8 @@ class Question < ActiveRecord::Base
|
|
22
21
|
|
23
22
|
def default_args
|
24
23
|
self.is_mandatory ||= true
|
25
|
-
self.display_type
|
26
|
-
self.pick
|
24
|
+
self.display_type ||= "default"
|
25
|
+
self.pick ||= "none"
|
27
26
|
end
|
28
27
|
|
29
28
|
def mandatory?
|
@@ -39,9 +38,6 @@ class Question < ActiveRecord::Base
|
|
39
38
|
def dep_class(response_set)
|
40
39
|
dependent? ? triggered?(response_set) ? "dependent" : "hidden dependent" : nil
|
41
40
|
end
|
42
|
-
def dependency_satisfied?(response_set)
|
43
|
-
self.has_dependency? and self.dependency.met?(response_set)
|
44
|
-
end
|
45
41
|
|
46
42
|
def part_of_group?
|
47
43
|
!self.question_group.nil?
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class QuestionGroup < ActiveRecord::Base
|
2
2
|
|
3
3
|
has_many :questions
|
4
|
+
has_one :dependency
|
4
5
|
|
5
6
|
# Instance Methods
|
6
7
|
def initialize(*args)
|
@@ -9,10 +10,21 @@ class QuestionGroup < ActiveRecord::Base
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def default_args
|
12
|
-
self.display_type
|
13
|
+
self.display_type ||= "inline"
|
13
14
|
end
|
14
15
|
|
15
16
|
def renderer
|
16
17
|
display_type.blank? ? :default : display_type.to_sym
|
17
18
|
end
|
19
|
+
|
20
|
+
def dependent?
|
21
|
+
self.dependency != nil
|
22
|
+
end
|
23
|
+
def triggered?(response_set)
|
24
|
+
dependent? ? self.dependency.met?(response_set) : true
|
25
|
+
end
|
26
|
+
def dep_class(response_set)
|
27
|
+
dependent? ? triggered?(response_set) ? "dependent" : "hidden dependent" : nil
|
28
|
+
end
|
29
|
+
|
18
30
|
end
|
data/app/models/response_set.rb
CHANGED
@@ -105,7 +105,7 @@ class ResponseSet < ActiveRecord::Base
|
|
105
105
|
|
106
106
|
def all_dependencies
|
107
107
|
arr = dependencies.partition{|d| d.met?(self) }
|
108
|
-
{:show => arr[0].map{|d| "question_#{d.question_id}"}, :hide => arr[1].map{|d| "question_#{d.question_id}"}}
|
108
|
+
{:show => arr[0].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group#{d.question_group_id}"}, :hide => arr[1].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group#{d.question_group_id}"}}
|
109
109
|
end
|
110
110
|
|
111
111
|
protected
|
@@ -1,7 +1,8 @@
|
|
1
1
|
- renderer = question_group.renderer
|
2
|
+
- dep_class = question_group.dep_class(response_set)
|
2
3
|
- locals = {:question_group => question_group, :response_set => response_set, :hide_label => true}
|
3
4
|
|
4
|
-
- div_for question_group do
|
5
|
+
- div_for question_group, :class => dep_class do
|
5
6
|
.head
|
6
7
|
.number= next_number
|
7
8
|
%span.text= question_group.text
|
@@ -15,7 +15,7 @@ class CreateAnswers < ActiveRecord::Migration
|
|
15
15
|
t.string :reference_identifier # from paper
|
16
16
|
t.string :data_export_identifier # data export
|
17
17
|
t.string :common_namespace # maping to a common vocab
|
18
|
-
t.string :
|
18
|
+
t.string :common_identifier # maping to a common vocab
|
19
19
|
|
20
20
|
# Display
|
21
21
|
t.integer :display_order
|
@@ -9,7 +9,7 @@ class CreateQuestionGroups < ActiveRecord::Migration
|
|
9
9
|
t.string :reference_identifier # from paper
|
10
10
|
t.string :data_export_identifier # data export
|
11
11
|
t.string :common_namespace # maping to a common vocab
|
12
|
-
t.string :
|
12
|
+
t.string :common_identifier # maping to a common vocab
|
13
13
|
|
14
14
|
# Display
|
15
15
|
t.string :display_type
|
@@ -15,7 +15,7 @@ class CreateQuestions < ActiveRecord::Migration
|
|
15
15
|
t.string :reference_identifier # from paper
|
16
16
|
t.string :data_export_identifier # data export
|
17
17
|
t.string :common_namespace # maping to a common vocab
|
18
|
-
t.string :
|
18
|
+
t.string :common_identifier # maping to a common vocab
|
19
19
|
|
20
20
|
# Display
|
21
21
|
t.integer :display_order
|
@@ -12,7 +12,7 @@ class CreateSurveySections < ActiveRecord::Migration
|
|
12
12
|
t.string :reference_identifier # from paper
|
13
13
|
t.string :data_export_identifier # data export
|
14
14
|
t.string :common_namespace # maping to a common vocab
|
15
|
-
t.string :
|
15
|
+
t.string :common_identifier # maping to a common vocab
|
16
16
|
|
17
17
|
# Display
|
18
18
|
t.integer :display_order
|
@@ -10,7 +10,7 @@ class CreateSurveys < ActiveRecord::Migration
|
|
10
10
|
t.string :reference_identifier # from paper
|
11
11
|
t.string :data_export_identifier # data export
|
12
12
|
t.string :common_namespace # maping to a common vocab
|
13
|
-
t.string :
|
13
|
+
t.string :common_identifier # maping to a common vocab
|
14
14
|
|
15
15
|
# Expiry
|
16
16
|
t.datetime :active_at
|
@@ -1,34 +1,29 @@
|
|
1
1
|
survey "“Kitchen Sink” survey" do
|
2
2
|
|
3
3
|
section "Basic questions" do
|
4
|
-
|
5
|
-
# exclusive
|
6
|
-
# name people you live with (choose all that apply)
|
7
|
-
# husband, friend, partner, siblings, other, alone, choose not to answer
|
8
|
-
# alone unchecks/erases, and disables all the others
|
9
|
-
# omit (choose not to answer) is exclusive as well
|
10
|
-
|
11
|
-
# a label is a question that accepts no answers
|
12
|
-
# reference_identifier => label
|
4
|
+
# A label is a question that accepts no answers
|
13
5
|
label "These questions are examples of the basic supported input types"
|
14
6
|
|
15
|
-
|
16
|
-
# a basic question,
|
17
|
-
# reference_identifier => 1
|
7
|
+
# A basic question with radio buttons
|
18
8
|
question_1 "What is your favorite color?", :pick => :one
|
19
9
|
answer "red"
|
20
10
|
answer "blue"
|
21
11
|
answer "green"
|
22
12
|
answer "yellow"
|
23
|
-
answer :other
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
13
|
+
answer :other
|
14
|
+
|
15
|
+
# A basic question with checkboxes
|
16
|
+
# "question" and "answer" may be abbreviated as "q" and "a"
|
17
|
+
q_2 "Choose the colors you don't like", :pick => :any
|
18
|
+
a_1 "red"
|
19
|
+
a_2 "blue"
|
20
|
+
a_3 "green"
|
21
|
+
a_4 "yellow"
|
22
|
+
a :omit
|
23
|
+
|
24
|
+
# A dependent question, with conditions and rule to logically join them
|
25
|
+
# the question's reference identifier is "2a", and the answer's reference_identifier is "1"
|
26
|
+
# question reference identifiers used in conditions need to be unique for the lookups to work
|
32
27
|
q_2a "Please explain why you don't like this color?"
|
33
28
|
a_1 "explanation", :text
|
34
29
|
dependency :rule => "A or B or C or D"
|
@@ -37,67 +32,54 @@ survey "“Kitchen Sink” survey" do
|
|
37
32
|
condition_C :q_2, "==", :a_3
|
38
33
|
condition_D :q_2, "==", :a_4
|
39
34
|
|
40
|
-
#
|
41
|
-
# questions and answers don't need to have numbers. Only if they are referenced in a dependency condition
|
35
|
+
# When :pick isn't specified, the default is :none (no checkbox or radio button)
|
42
36
|
q "What is your name?"
|
43
37
|
a :string
|
44
38
|
|
45
|
-
|
39
|
+
# Surveys, sections, questions, groups, and answers all take the following reference arguments
|
40
|
+
# :reference_identifier # usually from paper
|
41
|
+
# :data_export_identifier # data export
|
42
|
+
# :common_namespace # maping to a common vocab
|
43
|
+
# :common_identitier # maping to a common vocab
|
44
|
+
q "Get me started on an improv sketch", :reference_identifier => "improv_start", :data_export_identifier => "i_s", :common_namespace => "sketch comedy questions", :common_identifer => "get me started"
|
46
45
|
a "who", :string
|
47
46
|
a "what", :string
|
48
47
|
a "where", :string
|
49
|
-
|
50
|
-
|
48
|
+
|
49
|
+
# Various types of respones can be accepted
|
50
|
+
q "How many pets do you own?"
|
51
51
|
a :integer
|
52
52
|
|
53
|
-
q "What is your address?"
|
53
|
+
q "What is your address?"
|
54
54
|
a :text
|
55
|
-
#
|
56
|
-
# q "Pick your favorite date"
|
57
|
-
# a :date
|
58
|
-
#
|
59
|
-
# q "Pick your favorite time"
|
60
|
-
# a :time
|
61
55
|
|
62
56
|
q "Pick your favorite date AND time"
|
63
57
|
a :datetime
|
64
58
|
|
65
|
-
q "What time
|
59
|
+
q "What time do you usually take a lunch break?"
|
66
60
|
a :time
|
67
61
|
|
68
|
-
q "
|
62
|
+
q "When would you like to meet for dinner?"
|
69
63
|
a :date
|
70
|
-
|
64
|
+
|
65
|
+
# Sliders deprecate to select boxes when javascript is off
|
66
|
+
# Valid Ruby code may be used to shorted repetitive tasks
|
71
67
|
q "Adjust the slider to reflect your level of awesomeness", :pick => :one, :display_type => :slider
|
72
|
-
(1..10).to_a.each{
|
68
|
+
(1..10).to_a.each{|num| a num.to_s}
|
73
69
|
|
74
70
|
q "How much do you like Ruby?", :pick => :one, :display_type => :slider
|
75
71
|
["not at all", "a little", "some", "a lot", "a ton"].each{|level| a level}
|
76
|
-
|
77
|
-
# range "When did you start working at NUBIC?", :range_type => :date do
|
78
|
-
# q "From"
|
79
|
-
# a :date
|
80
|
-
#
|
81
|
-
# q "To"
|
82
|
-
# a :date
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# range "What times are you awake?", :range_type => :integer do
|
86
|
-
# q "From"
|
87
|
-
# a :integer
|
88
|
-
#
|
89
|
-
# q "To"
|
90
|
-
# a :integer
|
91
|
-
# end
|
92
72
|
|
73
|
+
# The "|" pipe is used to place labels before or after the input elements
|
93
74
|
q "How much money do you want?"
|
94
|
-
a "$|USD", :float
|
75
|
+
a "$|USD", :float
|
95
76
|
|
77
|
+
# Questions may be grouped
|
96
78
|
group "How much oil do you use per day?", :display_type => :inline do
|
97
79
|
q "Quantity"
|
98
80
|
a "Quantity", :float
|
99
81
|
|
100
|
-
q "Unit", :pick => :one, :display_type => :dropdown
|
82
|
+
q "Unit", :pick => :one, :display_type => :dropdown
|
101
83
|
a "Barrels"
|
102
84
|
a "Gallons"
|
103
85
|
a "Quarts"
|
@@ -124,7 +106,8 @@ survey "“Kitchen Sink” survey" do
|
|
124
106
|
"St. Clair","Stark", "Stephenson","Tazewell","Union",
|
125
107
|
"Vermilion","Wabash","Warren","Washington","Wayne",
|
126
108
|
"White","Whiteside","Will","Williamson","Winnebago","Woodford"].each{ |county| a county}
|
127
|
-
|
109
|
+
|
110
|
+
# When an is_exclusive answer is checked, it unchecks all other options and disables them (using Javascript)
|
128
111
|
q "Choose your favorite meats", :display_type => :inline, :pick => :any
|
129
112
|
a "Chicken"
|
130
113
|
a "Pork"
|
@@ -141,7 +124,8 @@ survey "“Kitchen Sink” survey" do
|
|
141
124
|
end
|
142
125
|
|
143
126
|
section "Complicated questions" do
|
144
|
-
|
127
|
+
|
128
|
+
# Grids are useful for repeated questions with the same set of answers, which are specified before the questions
|
145
129
|
grid "Tell us how often do you cover these each day" do
|
146
130
|
a "1"
|
147
131
|
a "2"
|
@@ -150,33 +134,35 @@ survey "“Kitchen Sink” survey" do
|
|
150
134
|
q "Knees", :pick => :one
|
151
135
|
q "Toes", :pick => :one
|
152
136
|
end
|
153
|
-
|
137
|
+
|
138
|
+
# "grid" is a shortcut for group with :display_type => :grid
|
139
|
+
# The answers will be repeated every 10 rows, but long grids aren't recommended as they are generally tedious
|
154
140
|
grid "Tell us how you feel today day" do
|
155
141
|
a "-2"
|
156
142
|
a "-1"
|
157
143
|
a "0"
|
158
144
|
a "1"
|
159
145
|
a "2"
|
160
|
-
a :omit
|
161
146
|
q "down|up" , :pick => :one
|
162
147
|
q "sad|happy", :pick => :one
|
163
148
|
q "limp|perky", :pick => :one
|
164
149
|
end
|
165
150
|
|
166
151
|
q "Please rank the following foods based on how much you like them"
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
152
|
+
a "|pizza", :integer
|
153
|
+
a "|salad", :integer
|
154
|
+
a "|sushi", :integer
|
155
|
+
a "|ice cream", :integer
|
156
|
+
a "|breakfast ceral", :integer
|
172
157
|
|
173
|
-
|
158
|
+
# :other, :string allows someone to specify both the "other" and some other information
|
174
159
|
q "Choose your favorite utensils and enter frequency of use (daily, weekly, monthly, etc...)", :pick => :any
|
175
160
|
a "spoon", :string
|
176
161
|
a "fork", :string
|
177
162
|
a "knife", :string
|
178
163
|
a :other, :string
|
179
|
-
|
164
|
+
|
165
|
+
# Repeaters allow multiple responses to a question or set of questions
|
180
166
|
repeater "Tell us about the cars you own" do
|
181
167
|
q "Make", :pick => :one, :display_type => :dropdown
|
182
168
|
a "Toyota"
|
@@ -191,10 +177,11 @@ survey "“Kitchen Sink” survey" do
|
|
191
177
|
q "Year"
|
192
178
|
a :string
|
193
179
|
end
|
180
|
+
|
194
181
|
repeater "Tell us the name and age of your siblings" do
|
195
182
|
q "Sibling"
|
196
183
|
a "Name", :string
|
197
|
-
a "Age", :integer
|
184
|
+
a "Age|years", :integer
|
198
185
|
end
|
199
186
|
|
200
187
|
end
|
data/script/surveyor/answer.rb
CHANGED
@@ -23,7 +23,6 @@ class Answer
|
|
23
23
|
{ :short_text => text,
|
24
24
|
:data_export_identifier => Columnizer.to_normalized_column(text),
|
25
25
|
:is_exclusive => false,
|
26
|
-
:is_a_disabler => false,
|
27
26
|
:hide_label => false,
|
28
27
|
:response_class => "answer"
|
29
28
|
}
|
@@ -36,11 +35,11 @@ class Answer
|
|
36
35
|
if a0.is_a?(Hash)
|
37
36
|
{:text => "Answer"}.merge(a0)
|
38
37
|
|
39
|
-
# String, Hash
|
38
|
+
# (String, Hash) or (String, Symbol, Hash)
|
40
39
|
elsif a0.is_a?(String)
|
41
40
|
a1.is_a?(Symbol) ? {:text => a0, :response_class => a1.to_s}.merge(a2 || {}) : {:text => a0}.merge(a1 || {})
|
42
41
|
|
43
|
-
# Symbol, Hash
|
42
|
+
# (Symbol, Hash) or (Symbol, Symbol, Hash)
|
44
43
|
elsif a0.is_a?(Symbol)
|
45
44
|
shortcuts = case a0
|
46
45
|
when :other
|
@@ -60,25 +59,12 @@ class Answer
|
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
62
|
+
def yml_attrs
|
63
|
+
instance_variables.sort - ["@parser"]
|
64
|
+
end
|
63
65
|
def to_yml
|
64
|
-
out =[ %(#{@data_export_identifier}_#{@id}:) ]
|
65
|
-
out <<
|
66
|
-
out << %( question_id: #{@question_id})
|
67
|
-
out << %( text: "#{@text}")
|
68
|
-
out << %( short_text: "#{@short_text}")
|
69
|
-
out << %( help_text: "#{@help_text}")
|
70
|
-
out << %( weight: #{@weight})
|
71
|
-
out << %( response_class: "#{@response_class}")
|
72
|
-
out << %( reference_identifier: "#{@reference_identifier}")
|
73
|
-
out << %( data_export_identifier: "#{@data_export_identifier}")
|
74
|
-
out << %( common_namespace: "#{@common_namespace}")
|
75
|
-
out << %( common_identitier: "#{@common_identitier}")
|
76
|
-
out << %( display_order: #{@display_order} )
|
77
|
-
out << %( is_exclusive: #{@is_exclusive})
|
78
|
-
out << %( hide_label: #{@hide_label})
|
79
|
-
out << %( display_length: #{@display_length} )
|
80
|
-
out << %( custom_class: "#{@custom_class}")
|
81
|
-
out << %( custom_renderer: "#{@custom_renderer}")
|
66
|
+
out = [ %(#{@data_export_identifier}_#{@id}:) ]
|
67
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
82
68
|
(out << nil ).join("\r\n")
|
83
69
|
end
|
84
70
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Dependency
|
2
2
|
|
3
3
|
# Context, Conditional, Children
|
4
|
-
attr_accessor :id, :question_id, :parser
|
4
|
+
attr_accessor :id, :question_id, :question_group_id, :parser
|
5
5
|
attr_accessor :rule
|
6
6
|
attr_accessor :dependency_conditions
|
7
7
|
|
@@ -9,7 +9,11 @@ class Dependency
|
|
9
9
|
def initialize(question, args, options)
|
10
10
|
self.parser = question.parser
|
11
11
|
self.id = parser.new_dependency_id
|
12
|
-
|
12
|
+
if question.class == QuestionGroup
|
13
|
+
self.question_group_id = question.id
|
14
|
+
else
|
15
|
+
self.question_id = question.id
|
16
|
+
end
|
13
17
|
self.rule = (args[0] || {})[:rule]
|
14
18
|
self.dependency_conditions = []
|
15
19
|
self.default_options().merge(options).merge(args[1] || {}).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
@@ -25,11 +29,12 @@ class Dependency
|
|
25
29
|
self.dependency_conditions << dc_obj
|
26
30
|
end
|
27
31
|
|
32
|
+
def yml_attrs
|
33
|
+
instance_variables.sort - ["@parser", "@dependency_conditions", "@reference_identifier"]
|
34
|
+
end
|
28
35
|
def to_yml
|
29
|
-
out =[ %(#{
|
30
|
-
out <<
|
31
|
-
out << %( question_id: #{@question_id})
|
32
|
-
out << %( rule: "#{@rule}")
|
36
|
+
out = [ %(#{@data_export_identifier}_#{@id}:) ]
|
37
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
33
38
|
(out << nil ).join("\r\n")
|
34
39
|
end
|
35
40
|
|
@@ -11,11 +11,8 @@ class DependencyCondition
|
|
11
11
|
self.parser = dependency.parser
|
12
12
|
self.id = parser.new_dependency_condition_id
|
13
13
|
self.dependency_id = dependency.id
|
14
|
-
self.rule_key = options[:reference_identifier]
|
15
|
-
|
16
14
|
args_options = parse_args_options(args)
|
17
15
|
self.default_options().merge(options).merge(args_options).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
18
|
-
|
19
16
|
end
|
20
17
|
|
21
18
|
def default_options()
|
@@ -28,29 +25,21 @@ class DependencyCondition
|
|
28
25
|
a2.is_a?(Hash) ? options.merge(a2) : options.merge({:answer_reference => a2.to_s.gsub("a_", "")})
|
29
26
|
end
|
30
27
|
|
28
|
+
def yml_attrs
|
29
|
+
instance_variables.sort - ["@parser", "@question_reference", "@answer_reference", "@reference_identifier"]
|
30
|
+
end
|
31
31
|
def to_yml
|
32
|
-
out =[ %(#{@
|
33
|
-
out <<
|
34
|
-
out << %( dependency_id: #{@dependency_id})
|
35
|
-
out << %( rule_key: "#{@rule_key}")
|
36
|
-
out << %( question_id: #{@question_id})
|
37
|
-
out << %( operator: "#{@operator}")
|
38
|
-
out << %( answer_id: #{@answer_id})
|
39
|
-
out << %( datetime_value: #{@date_value})
|
40
|
-
out << %( integer_value: #{@integer_value})
|
41
|
-
out << %( float_value: #{@float_value})
|
42
|
-
out << %( unit: "#{@unit}")
|
43
|
-
out << %( text_value: "#{@text_value}")
|
44
|
-
out << %( string_value: "#{@string_value}")
|
45
|
-
out << %( response_other: "#{@response_other}")
|
32
|
+
out = [ %(#{@data_export_identifier}_#{@id}:) ]
|
33
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
46
34
|
(out << nil ).join("\r\n")
|
47
35
|
end
|
48
36
|
|
49
37
|
def reconcile_dependencies
|
50
38
|
# Looking up references to questions and answers for linking the dependency objects
|
51
|
-
puts "Looking up references for question: #{@question_reference}
|
52
|
-
ref_question = Survey.current_survey.find_question_by_reference(@question_reference) # Argh. I can't think of a better way to get a hold of this reference here...
|
39
|
+
puts "Looking up references for question: #{@question_reference}"
|
40
|
+
ref_question = Survey.current_survey.find_question_by_reference(@question_reference) # TODO change this. Argh. I can't think of a better way to get a hold of this reference here...
|
53
41
|
if ref_question
|
42
|
+
puts " found question: #{ref_question.data_export_identifier} (id:#{ref_question.id})"
|
54
43
|
@question_id = ref_question.id
|
55
44
|
ref_answer = ref_question.find_answer_by_reference(@answer_reference)
|
56
45
|
if ref_answer
|
data/script/surveyor/question.rb
CHANGED
@@ -49,26 +49,13 @@ class Question
|
|
49
49
|
puts " found answer: '#{answer.text}' (id:#{answer.id})" unless answer.nil?
|
50
50
|
answer
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
|
+
def yml_attrs
|
54
|
+
instance_variables.sort - ["@parser", "@answers", "@dependency"]
|
55
|
+
end
|
53
56
|
def to_yml
|
54
|
-
out =[ %(#{@data_export_identifier}_#{@id}:) ]
|
55
|
-
out <<
|
56
|
-
out << %( survey_section_id: #{@survey_section_id})
|
57
|
-
out << %( question_group_id: #{@question_group_id})
|
58
|
-
out << %( text: "#{@text}")
|
59
|
-
out << %( short_text: "#{@short_text}")
|
60
|
-
out << %( help_text: "#{@help_text}")
|
61
|
-
out << %( pick: "#{pick}")
|
62
|
-
out << %( reference_identifier: "#{@reference_identifier}")
|
63
|
-
out << %( data_export_identifier: "#{@data_export_identifier}")
|
64
|
-
out << %( common_namespace: "#{@common_namespace}")
|
65
|
-
out << %( common_identitier: "#{@common_identitier}")
|
66
|
-
out << %( display_order: #{@display_order})
|
67
|
-
out << %( display_type: "#{@display_type}")
|
68
|
-
out << %( is_mandatory: #{@is_mandatory})
|
69
|
-
out << %( display_width: #{@display_width})
|
70
|
-
out << %( custom_class: "#{@custom_class}")
|
71
|
-
out << %( custom_renderer: "#{@custom_renderer}")
|
57
|
+
out = [ %(#{@data_export_identifier}_#{@id}:) ]
|
58
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
72
59
|
(out << nil ).join("\r\n")
|
73
60
|
end
|
74
61
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
class QuestionGroup
|
2
2
|
|
3
|
-
# Context, Content, Display
|
4
|
-
attr_accessor :id, :
|
3
|
+
# Context, Content, Display, Children
|
4
|
+
attr_accessor :id, :parser
|
5
5
|
attr_accessor :text, :help_text
|
6
6
|
attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
|
7
7
|
attr_accessor :display_type, :custom_class, :custom_renderer
|
8
|
+
attr_accessor :dependency
|
8
9
|
|
9
10
|
# id, section and text required
|
10
11
|
def initialize(section, args, options)
|
@@ -18,18 +19,12 @@ class QuestionGroup
|
|
18
19
|
{:display_type => "default"}
|
19
20
|
end
|
20
21
|
|
22
|
+
def yml_attrs
|
23
|
+
instance_variables.sort - ["@parser", "@dependency"]
|
24
|
+
end
|
21
25
|
def to_yml
|
22
|
-
out =[ %(#{@id}:) ]
|
23
|
-
out <<
|
24
|
-
out << %( text: "#{@text}")
|
25
|
-
out << %( help_text: "#{@help_text}")
|
26
|
-
out << %( reference_identifier: "#{@reference_identifier}")
|
27
|
-
out << %( data_export_identifier: "#{@data_export_identifier}")
|
28
|
-
out << %( common_namespace: "#{@common_namespace}")
|
29
|
-
out << %( common_identitier: "#{@common_identitier}")
|
30
|
-
out << %( display_type: "#{@display_type}")
|
31
|
-
out << %( custom_class: "#{@custom_class}")
|
32
|
-
out << %( custom_renderer: "#{@custom_renderer}")
|
26
|
+
out = [ %(#{@id}:) ]
|
27
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
33
28
|
(out << nil ).join("\r\n")
|
34
29
|
end
|
35
30
|
|
data/script/surveyor/survey.rb
CHANGED
@@ -86,20 +86,12 @@ class Survey
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
def yml_attrs
|
90
|
+
instance_variables.sort - ["@parser", "@survey_sections"]
|
91
|
+
end
|
89
92
|
def to_yml
|
90
|
-
out =[ %(survey_#{@id}:) ]
|
91
|
-
out <<
|
92
|
-
out << %( title: "#{@title}")
|
93
|
-
out << %( description: "#{@description}")
|
94
|
-
out << %( access_code: "#{@access_code}")
|
95
|
-
out << %( reference_identifier: "#{@reference_identifier}")
|
96
|
-
out << %( data_export_identifier: "#{@data_export_identifier}")
|
97
|
-
out << %( common_namespace: "#{@common_namespace}")
|
98
|
-
out << %( common_identitier: "#{@common_identitier}")
|
99
|
-
out << %( active_at: #{@active_at})
|
100
|
-
out << %( inactive_at: #{@inactive_at})
|
101
|
-
out << %( css_url: "#{@css_url}")
|
102
|
-
out << %( custom_class: "#{@custom_class}")
|
93
|
+
out = [ %(survey_#{@id}:) ]
|
94
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
103
95
|
(out << nil ).join("\r\n")
|
104
96
|
end
|
105
97
|
|
@@ -41,13 +41,14 @@ class SurveySection
|
|
41
41
|
# This method_missing magic does all the heavy lifting for the DSL
|
42
42
|
def method_missing(missing_method, *args, &block)
|
43
43
|
method_name, reference_identifier = missing_method.to_s.split("_")
|
44
|
-
|
44
|
+
|
45
45
|
case method_name
|
46
46
|
|
47
47
|
when "group", "g", "grid", "repeater"
|
48
48
|
puts " Group: #{reference_identifier}"
|
49
49
|
raise "Error: A question group cannot be empty" unless block_given?
|
50
50
|
|
51
|
+
clear_current_question
|
51
52
|
options = {:reference_identifier => reference_identifier, :display_type => (method_name =~ /grid|repeater/)? method_name : nil }
|
52
53
|
self.question_groups << (self.current_question_group = QuestionGroup.new(self, args, options))
|
53
54
|
self.instance_eval(&block)
|
@@ -67,17 +68,17 @@ class SurveySection
|
|
67
68
|
when "dependency", "d"
|
68
69
|
puts " Dependency: #{reference_identifier}"
|
69
70
|
raise "Error: I'm dropping the block like it's hot" if block_given?
|
70
|
-
raise "Error: " unless self.current_question
|
71
|
+
raise "Error: No question or question group" unless (d = self.current_question_group || self.current_question)
|
71
72
|
|
72
|
-
options = {:reference_identifier => reference_identifier}
|
73
|
-
|
73
|
+
options = {}# {:reference_identifier => reference_identifier}
|
74
|
+
d.dependency = (self.current_dependency = Dependency.new(d, args, options))
|
74
75
|
|
75
76
|
when "condition", "c"
|
76
77
|
puts " Condition: #{reference_identifier}"
|
77
78
|
raise "Error, I'm dropping the block like it's hot" if block_given?
|
78
79
|
raise "Error: No current dependency" unless self.current_dependency
|
79
80
|
|
80
|
-
options = {:
|
81
|
+
options = {:rule_key => reference_identifier}
|
81
82
|
self.current_dependency.add_dependency_condition DependencyCondition.new(self, args, options)
|
82
83
|
|
83
84
|
when "answer", "a"
|
@@ -121,28 +122,15 @@ class SurveySection
|
|
121
122
|
|
122
123
|
# Used to find questions for dependency linking
|
123
124
|
def find_question_by_reference(ref_id)
|
124
|
-
|
125
|
-
count = 0
|
126
|
-
while question.nil? and count < questions.size
|
127
|
-
question = self.questions[count] if self.questions[count].reference_identifier == ref_id
|
128
|
-
count +=1
|
129
|
-
end
|
130
|
-
question
|
125
|
+
self.questions.detect{|q| q.reference_identifier == ref_id}
|
131
126
|
end
|
132
127
|
|
128
|
+
def yml_attrs
|
129
|
+
instance_variables.sort - ["@parser", "@question_groups", "@questions", "@grid_answers", "@current_question_group", "@current_question", "@current_dependency"]
|
130
|
+
end
|
133
131
|
def to_yml
|
134
|
-
out =[ %(#{@data_export_identifier}_#{@id}:) ]
|
135
|
-
out <<
|
136
|
-
out << %( survey_id: #{@survey_id})
|
137
|
-
out << %( title: "#{@title}")
|
138
|
-
out << %( description: "#{@description}")
|
139
|
-
out << %( reference_identifier: "#{@reference_identifier}")
|
140
|
-
out << %( data_export_identifier: "#{@data_export_identifier}")
|
141
|
-
out << %( common_namespace: "#{@common_namespace}")
|
142
|
-
out << %( common_identitier: "#{@common_identitier}")
|
143
|
-
out << %( display_order: #{@display_order})
|
144
|
-
out << %( custom_class: "#{@custom_class}")
|
145
|
-
|
132
|
+
out = [ %(#{@data_export_identifier}_#{@id}:) ]
|
133
|
+
yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
|
146
134
|
(out << nil ).join("\r\n")
|
147
135
|
end
|
148
136
|
|
data/surveyor.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{surveyor}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brian Chamberlain", "Mark Yoon"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-20}
|
13
13
|
s.email = %q{yoon@northwestern.edu}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.md"
|
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: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Chamberlain
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-10-
|
13
|
+
date: 2009-10-20 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|