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
@@ -1,223 +0,0 @@
1
- require 'activesupport' # for pluralize, humanize in ActiveSupport::CoreExtensions::String::Inflections
2
- module SurveyParser
3
- class Parser
4
- @@models = %w(survey survey_section question_group question answer dependency dependency_condition validation validation_condition)
5
-
6
- # Require base and all models
7
- (%w(base) + @@models).each{|m| require File.dirname(__FILE__) + "/#{m}"}
8
-
9
- # Attributes
10
- attr_accessor :salt, :surveys, :grid_answers
11
- @@models.each{|m| attr_accessor "#{m.pluralize}_yml".to_sym } # for fixtures
12
- (@@models - %w(dependency_condition validation_condition)).each {|m| attr_accessor "current_#{m}".to_sym} # for current_model caches
13
-
14
- # Class methods
15
- def self.parse(file_name)
16
- self.define_counter_methods(@@models)
17
- puts "\n--- Parsing '#{file_name}' ---"
18
- parser = SurveyParser::Parser.new
19
- parser.instance_eval(File.read(file_name))
20
- parser.to_files
21
- puts "--- End of parsing ---\n\n"
22
- end
23
-
24
- # new_survey_id, new_survey_section_id, etc.
25
- def self.define_counter_methods(names)
26
- names.each do |name|
27
- define_method("new_#{name}_id") do
28
- instance_variable_set("@last_#{name}_id", instance_variable_get("@last_#{name}_id") + 1)
29
- end
30
- end
31
- end
32
-
33
- # Instance methods
34
- def initialize
35
- self.salt = Time.now.strftime("%Y%m%d%H%M%S")
36
- self.surveys = []
37
- self.grid_answers = []
38
- initialize_counters(@@models)
39
- initialize_fixtures(@@models.map(&:pluralize), File.join(RAILS_ROOT, "surveys", "fixtures"))
40
- end
41
-
42
- # @last_survey_id, @last_survey_section_id, etc.
43
- def initialize_counters(names)
44
- names.each{|name| instance_variable_set("@last_#{name}_id", 0)}
45
- end
46
-
47
- # @surveys_yml, @survey_sections_yml, etc.
48
- def initialize_fixtures(names, path)
49
- names.each {|name| file = instance_variable_set("@#{name}_yml", "#{path}/#{name}.yml"); File.truncate(file, 0) if File.exist?(file) }
50
- end
51
-
52
- # This method_missing does all the heavy lifting for the DSL
53
- def method_missing(missing_method, *args, &block)
54
- method_name, reference_identifier = missing_method.to_s.split("_", 2)
55
- opts = {:method_name => method_name, :reference_identifier => reference_identifier}
56
- case method_name
57
- when "survey"
58
- self.current_survey = Survey.new(self, args, opts)
59
- evaluate_the "survey", &block
60
-
61
- when "section"
62
- self.current_survey_section = SurveySection.new(self.current_survey, args, opts.merge({:display_order => current_survey.survey_sections.size + 1}))
63
- evaluate_the "survey_section", &block
64
-
65
- when "group", "g", "grid", "repeater"
66
- self.current_question_group = QuestionGroup.new(self.current_survey_section, args, opts)
67
- evaluate_the "question_group", &block
68
-
69
- when "question", "q", "label", "image"
70
- drop_the &block
71
- self.current_question = Question.new(self.current_survey_section, args, opts.merge(:question_group_id => current_question_group ? current_question_group.id : nil))
72
- add_grid_answers if in_a_grid?
73
-
74
- when "dependency", "d"
75
- drop_the &block
76
- self.current_dependency = Dependency.new(self.current_question_group || current_question, args, opts)
77
-
78
- when "condition", "c"
79
- drop_the &block
80
- raise "Error: No current dependency or validation for this condition" if self.current_dependency.nil? && self.current_validation.nil?
81
- if self.current_dependency.nil?
82
- self.current_validation.validation_conditions << ValidationCondition.new(self.current_validation, args, opts)
83
- else
84
- self.current_dependency.dependency_conditions << DependencyCondition.new(self.current_dependency, args, opts)
85
- end
86
-
87
- when "answer", "a"
88
- drop_the &block
89
- if in_a_grid?
90
- self.grid_answers << Answer.new(nil, args, opts.merge(:display_order => grid_answers.size + 1))
91
- else
92
- raise "Error: No current question" if self.current_question.nil?
93
- self.current_answer = Answer.new(self.current_question, args, opts.merge(:display_order => current_question.answers.size + 1))
94
- end
95
-
96
- when "correct"
97
- drop_the &block
98
- raise "Error: No current question" if self.current_question.nil?
99
- self.current_correct_answer = self.current_question.find_current_answers(args)
100
-
101
- when "validation", "v"
102
- drop_the &block
103
- self.current_validation = Validation.new(self.current_answer, args, opts)
104
-
105
-
106
- # explicitly define a dependency condition
107
- # (not really necessary as is default)
108
- when "dependencycondition", "dcondition", "dc"
109
- drop_the &block
110
- raise "Error: No current dependency for this condition" if self.current_dependency.nil?
111
- self.current_dependency.dependency_conditions << DependencyCondition.new(self.current_dependency, args, opts)
112
-
113
- # explicitly define a validation condition
114
- # (is necessary if want dependency AND validation on
115
- # same question as dependency existance would try to
116
- # make the condition a dependency condition.)
117
- when "validationcondition", "vcondition", "vc"
118
- drop_the &block
119
- raise "Error: No current validation for this condition" if self.current_validation.nil?
120
- self.current_validation.validation_conditions << ValidationCondition.new(self.current_validation, args, opts)
121
-
122
-
123
-
124
- else
125
- raise " ERROR: '#{missing_method}' not valid method"
126
-
127
- end
128
- end
129
-
130
- def drop_the(&block)
131
- raise "Error, I'm dropping the block like it's hot" if block_given?
132
- end
133
-
134
- def evaluate_the(model, &block)
135
- raise "Error: A #{model.humanize} cannot be empty" unless block_given?
136
- self.instance_eval(&block)
137
- self.send("clear_current", model)
138
- end
139
-
140
- def clear_current(model)
141
- # puts "clear_current #{model}"
142
- case model
143
- when "survey"
144
- self.current_survey.reconcile_dependencies unless current_survey.nil?
145
- when "question_group"
146
- self.grid_answers = []
147
- clear_current("question")
148
- when "question"
149
- @current_dependency = nil
150
- when "answer"
151
- @current_validation = nil
152
- end
153
- instance_variable_set("@current_#{model}", nil)
154
- "SurveyParser::#{model.classify}".constantize.send(:children).each{|m| clear_current(m.to_s.singularize)}
155
- end
156
-
157
- def current_survey=(s)
158
- clear_current "survey"
159
- self.surveys << s
160
- @current_survey = s
161
- end
162
-
163
- def current_survey_section=(s)
164
- clear_current "survey_section"
165
- self.current_survey.survey_sections << s
166
- @current_survey_section = s
167
- end
168
-
169
- def current_question_group=(g)
170
- clear_current "question_group"
171
- self.current_survey_section.question_groups << g
172
- @current_question_group = g
173
- end
174
-
175
- def current_question=(q)
176
- clear_current "question"
177
- self.current_survey_section.questions << q
178
- @current_question = q
179
- end
180
-
181
- def current_dependency=(d)
182
- raise "Error: No question or question group" unless (dependent = self.current_question_group || self.current_question)
183
- dependent.dependency = d
184
- @current_dependency = d
185
- end
186
-
187
- def current_answer=(a)
188
- raise "Error: No current question" if self.current_question.nil?
189
- self.current_question.answers << a
190
- @current_answer = a
191
- end
192
-
193
- def current_correct_answer=(a)
194
- raise "Error: No current question" if self.current_question.nil?
195
- self.current_question.correct_answer = a
196
- end
197
-
198
- def current_validation=(v)
199
- clear_current "validation"
200
- self.current_answer.validation = v
201
- @current_validation = v
202
- end
203
-
204
- def in_a_grid?
205
- self.current_question_group and self.current_question_group.display_type == "grid"
206
- end
207
-
208
- def add_grid_answers
209
- self.grid_answers.each do |grid_answer|
210
- my_answer = grid_answer.dup
211
- my_answer.id = new_answer_id
212
- my_answer.question_id = self.current_question.id
213
- my_answer.parser = self
214
- self.current_answer = my_answer
215
- end
216
- end
217
-
218
- def to_files
219
- self.surveys.compact.map(&:to_file)
220
- end
221
-
222
- end
223
- end
@@ -1,59 +0,0 @@
1
- module SurveyParser
2
- class Question < SurveyParser::Base
3
- ANSWER_ID = /\s*\w+_/ unless defined?(ANSWER_ID)
4
-
5
- # Context, Content, Reference, Display, Children
6
- attr_accessor :id, :parser, :survey_section_id, :question_group_id, :correct_answer_id
7
- attr_accessor :text, :short_text, :help_text, :pick, :answer_id
8
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier
9
- attr_accessor :display_order, :display_type, :is_mandatory, :display_width, :custom_class, :custom_renderer
10
- attr_accessor :dependency
11
- has_children :answers
12
-
13
- def default_options
14
- { :pick => :none,
15
- :display_type => :default,
16
- :is_mandatory => true,
17
- :display_order => self.id
18
- }
19
- end
20
-
21
- def parse_opts(opts)
22
- (name = opts.delete(:method_name)) =~ /label|image/ ? opts.merge(:display_type => name) : opts
23
- end
24
-
25
- def parse_args(args)
26
- text = args[0] || "Question"
27
- {:text => text, :short_text => text, :data_export_identifier => Surveyor::Common.to_normalized_string(text)}.merge(args[1] || {})
28
- end
29
-
30
- def correct_answer=(a)
31
- self.answer_id = a.id
32
- end
33
-
34
- def find_answer_by_reference(ref_id)
35
- self.answers.detect{|a| a.reference_identifier == ref_id}
36
- end
37
-
38
- # currently, only one correct answer is allowed
39
- def find_current_answers(args)
40
- ref_ids = args[0][:answer]
41
- ids = ref_ids.to_s.split(ANSWER_ID).compact
42
- self.answers.select{|a| ids.include?(a.reference_identifier)}.first
43
- end
44
-
45
- # so we can build the correct yaml structure
46
- def property_name_map(property)
47
- names = {
48
- "answer" => "correct_answer"
49
- }
50
- return names.has_key?(property) ? names[property] : property
51
- end
52
-
53
- def to_file
54
- super
55
- if self.dependency then self.dependency.to_file end
56
- end
57
-
58
- end
59
- end
@@ -1,26 +0,0 @@
1
- module SurveyParser
2
- class QuestionGroup < SurveyParser::Base
3
- # Context, Content, Display, Children
4
- attr_accessor :id, :parser
5
- attr_accessor :text, :help_text
6
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
7
- attr_accessor :display_type, :custom_class, :custom_renderer
8
- attr_accessor :dependency
9
-
10
- def default_options
11
- {:display_type => "default"}
12
- end
13
- def parse_args(args)
14
- {:text => args[0] || "Question Group"}.merge(args[1] || {})
15
- end
16
- def parse_opts(opts)
17
- (name = opts.delete(:method_name)) =~ /grid|repeater/ ? opts.merge(:display_type => name) : opts
18
- end
19
-
20
- def to_file
21
- super
22
- if self.dependency then self.dependency.to_file end
23
- end
24
-
25
- end
26
- end
@@ -1,29 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'answer'
3
-
4
- describe SurveyParser::Answer, " when first created" do
5
-
6
- before(:each) do
7
- question = mock("Question", :id => 2, :parser => mock("SurveyParser::Parser", :new_answer_id => 1))
8
- question.stub!(:class => SurveyParser::Question)
9
- options = {:help_text => "Never or rarely ever", :reference_identifier => "b3a_1"}
10
- @ans = SurveyParser::Answer.new(question, ["No / Rarely"], options)
11
- end
12
-
13
- it "should set inititalized variables to those passed in" do
14
- @ans.id.should == 1
15
- @ans.question_id.should == 2
16
- @ans.reference_identifier.should == "b3a_1"
17
- @ans.text.should == "No / Rarely"
18
- @ans.help_text.should == "Never or rarely ever"
19
- end
20
-
21
- it "should output current state to yml" do
22
- @ans.should.respond_to?(:to_yml)
23
- end
24
-
25
- it "should create a normalized code automatically when initalized" do
26
- @ans.data_export_identifier.should eql("no_rarely")
27
- end
28
-
29
- end
@@ -1,63 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'question'
3
-
4
- describe SurveyParser::Question, " when first created" do
5
- before do
6
- section = mock("SurveyParser::SurveySection", :id => 2, :parser => mock("SurveyParser::Parser", :new_question_id => 1))
7
- section.stub!(:class => SurveyParser::SurveySection)
8
- args = {:help_text => "Please give a rough estimate", :reference_identifier => "B3"}
9
- options = {}
10
- @ques = SurveyParser::Question.new(section, ["In the past 12 months how many times have you been to a doctor?", args], options)
11
- end
12
-
13
- it "should set initialization parameters properly" do
14
- @ques.id.should == 1
15
- @ques.reference_identifier.should == "B3"
16
- @ques.survey_section_id.should == 2
17
- @ques.help_text.should == "Please give a rough estimate"
18
-
19
- #Checking the defaults
20
- @ques.pick.should == :none
21
- @ques.display_type.should == :default
22
- @ques.is_mandatory.should == true
23
- end
24
-
25
- it "should output current state to yml" do
26
- @ques.should.respond_to?(:to_yml)
27
- end
28
-
29
- it "should create a normalized code automatically when initalized" do
30
- @ques.data_export_identifier.should eql("many_times_you_been_doctor")
31
- end
32
-
33
- # We don't change titles via the DSL
34
- # it "should update the normalized code if the title is changed" do
35
- # @ques.data_export_identifier.should == "many_times_you_been_doctor"
36
- # @ques.text = "Sometimes or All the time?"
37
- # @ques.data_export_identifier.should == "sometimes_all_time"
38
- # end
39
-
40
- end
41
-
42
- describe SurveyParser::Question, "when it contains data" do
43
- before(:each) do
44
- section = mock("SurveyParser::SurveySection", :id => 2, :parser => mock("SurveyParser::Parser", :new_question_id => 1))
45
- args = {:help_text => "Please give a rough estimate", :reference_identifier => "B3"}
46
- options = {}
47
- @ques = SurveyParser::Question.new(section, ["In the past 12 months how many times have you been to a doctor?", args], options)
48
- @ques.answers << mock("SurveyParser::Answer", :reference_identifier => "1", :text => "foo")
49
- @ques.answers << mock("SurveyParser::Answer", :reference_identifier => "2", :text => "foo")
50
- @ques.answers << mock("SurveyParser::Answer", :reference_identifier => "3", :text => "foo")
51
- end
52
-
53
- it "should have added the test answers correctly" do
54
- @ques.answers.length.should eql(3)
55
- end
56
-
57
- it "should find an answer by reference" do
58
- a_to_find = @ques.find_answer_by_reference("2")
59
- a_to_find.should_not be_nil
60
- a_to_find.reference_identifier.should eql("2")
61
- end
62
-
63
- end
@@ -1,7 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
2
- require File.expand_path(File.dirname(__FILE__) + "/../../../lib/surveyor")
3
- require File.expand_path(File.dirname(__FILE__) + "/../parser")
4
- require File.expand_path(File.dirname(__FILE__) + "/../base")
5
-
6
- Spec::Runner.configure do |config|
7
- end
@@ -1,23 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'survey_section'
3
-
4
- describe SurveyParser::SurveySection, "when first created" do
5
-
6
- before(:each) do
7
- @section = SurveyParser::SurveySection.new(mock("SurveyParser::Survey", :id => 2, :parser => mock("SurveyParser::Parser", :new_survey_section_id => 1)), ["Demographics"], {})
8
- end
9
-
10
- it "should generate a data export identifier" do
11
- @section.data_export_identifier.should == "demographics"
12
- end
13
-
14
- it "should find a question by reference" do
15
- @section.questions << mock("SurveyParser::Question", :reference_identifier => "1", :text => "foo")
16
- @section.questions << mock("SurveyParser::Question", :reference_identifier => "2", :text => "foo")
17
- @section.questions << mock("SurveyParser::Question", :reference_identifier => "3", :text => "foo")
18
-
19
- q = @section.find_question_by_reference("2")
20
- q.should_not be_nil
21
- q.reference_identifier.should == "2"
22
- end
23
- end
@@ -1,20 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'validation_condition'
3
-
4
- describe SurveyParser::ValidationCondition, " when first created" do
5
- before do
6
- validation = mock("SurveyParser::Validation", :id => 29, :parser => mock("SurveyParser::Parser", :new_validation_condition_id => 21))
7
- validation.stub!(:class => SurveyParser::Validation)
8
- args = [">=", {:integer_value => 0}]
9
- options = {}
10
- @validation_condition = SurveyParser::ValidationCondition.new(validation, args, options)
11
- end
12
-
13
- it "should set initialization parameters properly" do
14
- @validation_condition.id.should == 21
15
- @validation_condition.validation_id.should == 29
16
- @validation_condition.integer_value.should == 0
17
- @validation_condition.operator.should == ">="
18
- end
19
-
20
- end