surveyor 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.9.2
@@ -11,7 +11,7 @@ namespace :surveyor do
11
11
  raise "USAGE: file name required e.g. 'FILE=surveys/kitchen_sink_survey.rb'" if ENV["FILE"].blank?
12
12
  fixture_dir = File.join(RAILS_ROOT, "surveys", "fixtures")
13
13
  mkdir fixture_dir unless File.exists?(fixture_dir)
14
- SurveyParser.parse(File.join(RAILS_ROOT, ENV["FILE"]))
14
+ SurveyParser::Parser.parse(File.join(RAILS_ROOT, ENV["FILE"]))
15
15
  end
16
16
 
17
17
  desc "load survey fixtures"
@@ -1,52 +1,54 @@
1
- class Answer < SurveyParser::Base
2
- # Context, Content, Reference, Display
3
- attr_accessor :id, :parser, :question_id
4
- attr_accessor :text, :short_text, :help_text, :weight, :response_class
5
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
6
- attr_accessor :display_order, :is_exclusive, :hide_label, :display_length, :custom_class, :custom_renderer
7
- attr_accessor :validation
1
+ module SurveyParser
2
+ class Answer < SurveyParser::Base
3
+ # Context, Content, Reference, Display
4
+ attr_accessor :id, :parser, :question_id
5
+ attr_accessor :text, :short_text, :help_text, :weight, :response_class
6
+ attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
7
+ attr_accessor :display_order, :is_exclusive, :hide_label, :display_length, :custom_class, :custom_renderer
8
+ attr_accessor :validation
8
9
 
9
- def default_options
10
- { :is_exclusive => false,
11
- :hide_label => false,
12
- :response_class => "answer"
13
- }
14
- end
10
+ def default_options
11
+ { :is_exclusive => false,
12
+ :hide_label => false,
13
+ :response_class => "answer"
14
+ }
15
+ end
15
16
 
16
- def parse_args(args)
17
- case args[0]
18
- when Hash # Hash
19
- text_args(args[0][:text]).merge(args[0])
20
- when String # (String, Hash) or (String, Symbol, Hash)
21
- text_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
22
- when Symbol # (Symbol, Hash) or (Symbol, Symbol, Hash)
23
- symbol_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
24
- else
25
- text_args(nil)
17
+ def parse_args(args)
18
+ case args[0]
19
+ when Hash # Hash
20
+ text_args(args[0][:text]).merge(args[0])
21
+ when String # (String, Hash) or (String, Symbol, Hash)
22
+ text_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
23
+ when Symbol # (Symbol, Hash) or (Symbol, Symbol, Hash)
24
+ symbol_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
25
+ else
26
+ text_args(nil)
27
+ end
26
28
  end
27
- end
28
29
 
29
- def text_args(text = "Answer")
30
- {:text => text.to_s, :short_text => text, :data_export_identifier => Surveyor.to_normalized_string(text)}
31
- end
32
- def hash_from(arg)
33
- arg.is_a?(Symbol) ? {:response_class => arg.to_s} : arg.is_a?(Hash) ? arg : {}
34
- end
35
- def symbol_args(arg)
36
- case arg
37
- when :other
38
- text_args("Other")
39
- when :other_and_string
40
- text_args("Other").merge({:response_class => "string"})
41
- when :none, :omit # is_exclusive erases and disables other checkboxes and input elements
42
- text_args(arg.to_s.humanize).merge({:is_exclusive => true})
43
- when :integer, :date, :time, :datetime, :text, :datetime, :string
44
- text_args(arg.to_s.humanize).merge({:response_class => arg.to_s, :hide_label => true})
30
+ def text_args(text = "Answer")
31
+ {:text => text.to_s, :short_text => text, :data_export_identifier => Surveyor.to_normalized_string(text)}
32
+ end
33
+ def hash_from(arg)
34
+ arg.is_a?(Symbol) ? {:response_class => arg.to_s} : arg.is_a?(Hash) ? arg : {}
35
+ end
36
+ def symbol_args(arg)
37
+ case arg
38
+ when :other
39
+ text_args("Other")
40
+ when :other_and_string
41
+ text_args("Other").merge({:response_class => "string"})
42
+ when :none, :omit # is_exclusive erases and disables other checkboxes and input elements
43
+ text_args(arg.to_s.humanize).merge({:is_exclusive => true})
44
+ when :integer, :date, :time, :datetime, :text, :datetime, :string
45
+ text_args(arg.to_s.humanize).merge({:response_class => arg.to_s, :hide_label => true})
46
+ end
47
+ end
48
+ def to_file
49
+ super
50
+ if self.validation then self.validation.to_file end
45
51
  end
46
- end
47
- def to_file
48
- super
49
- if self.validation then self.validation.to_file end
50
- end
51
52
 
53
+ end
52
54
  end
@@ -1,4 +1,4 @@
1
- class SurveyParser
1
+ module SurveyParser
2
2
  class Base
3
3
 
4
4
  # Class level instance variable, because class variable are shared with subclasses
@@ -22,17 +22,17 @@ class SurveyParser
22
22
  # Instance methods
23
23
  def initialize(obj, args, opts)
24
24
  # inherit the parser from parent (obj)
25
- self.parser = (obj.nil? ? nil : obj.class == SurveyParser ? obj : obj.parser)
25
+ self.parser = (obj.nil? ? nil : obj.class == SurveyParser::Parser ? obj : obj.parser)
26
26
  # get a new id from the parser
27
- self.id = parser.nil? ? nil : parser.send("new_#{self.class.name.underscore}_id")
27
+ self.id = parser.nil? ? nil : parser.send("new_#{self.class.name.demodulize.underscore}_id")
28
28
  # set [parent]_id to obj.id, if we have that attribute
29
- self.send("#{obj.class.name.underscore}_id=", obj.nil? ? nil : obj.id) if self.respond_to?("#{obj.class.name.underscore}_id=")
29
+ self.send("#{obj.class.name.demodulize.underscore}_id=", obj.nil? ? nil : obj.id) if self.respond_to?("#{obj.class.name.demodulize.underscore}_id=")
30
30
  # initialize descendant models
31
31
  self.class.children.each{|model| self.send("#{model}=", [])}
32
32
  # combine default options, parsed opts, parsed args, and initialize instance variables
33
33
  self.default_options.merge(parse_opts(opts)).merge(parse_args(args)).each{|k,v| self.send("#{k.to_s}=", v)}
34
34
  # print to the log
35
- print "#{self.class.name.gsub(/[a-z]/, "")[-1,1]}#{self.id} "
35
+ print "#{self.class.name.demodulize.gsub(/[a-z]/, "")[-1,1]}#{self.id} "
36
36
  end
37
37
  def default_options
38
38
  {}
@@ -54,7 +54,7 @@ class SurveyParser
54
54
  (out << nil ).join("\r\n")
55
55
  end
56
56
  def to_file
57
- File.open(self.parser.send("#{self.class.name.underscore.pluralize}_yml"), File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
57
+ File.open(self.parser.send("#{self.class.name.demodulize.underscore.pluralize}_yml"), File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
58
58
  self.class.children.each{|model| self.send(model).compact.map(&:to_file)}
59
59
  end
60
60
  end
@@ -1,11 +1,13 @@
1
- class Dependency < SurveyParser::Base
2
- # Context, Conditional, Children
3
- attr_accessor :id, :question_id, :question_group_id, :parser
4
- attr_accessor :rule
5
- has_children :dependency_conditions
1
+ module SurveyParser
2
+ class Dependency < SurveyParser::Base
3
+ # Context, Conditional, Children
4
+ attr_accessor :id, :question_id, :question_group_id, :parser
5
+ attr_accessor :rule
6
+ has_children :dependency_conditions
6
7
 
7
- def parse_opts(opts)
8
- {} # toss the method name and reference identifier by default
9
- end
8
+ def parse_opts(opts)
9
+ {} # toss the method name and reference identifier by default
10
+ end
10
11
 
12
+ end
11
13
  end
@@ -1,38 +1,40 @@
1
- class DependencyCondition < SurveyParser::Base
1
+ module SurveyParser
2
+ class DependencyCondition < SurveyParser::Base
2
3
 
3
- # Context, Conditional, Value
4
- attr_accessor :id, :dependency_id, :rule_key, :parser
5
- attr_accessor :question_id, :operator
6
- attr_accessor :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other
7
- attr_accessor :question_reference, :answer_reference
4
+ # Context, Conditional, Value
5
+ attr_accessor :id, :dependency_id, :rule_key, :parser
6
+ attr_accessor :question_id, :operator
7
+ attr_accessor :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other
8
+ attr_accessor :question_reference, :answer_reference
8
9
 
9
- def default_options
10
- { :operator => "==" }
11
- end
12
- def parse_args(args)
13
- a0, a1, a2 = args
14
- {:question_reference => a0.to_s.gsub("q_", ""), :operator => a1}.merge(a2.is_a?(Hash) ? a2 : {:answer_reference => a2.to_s.gsub("a_", "")})
15
- end
16
- def parse_opts(opts)
17
- {:rule_key => opts[:reference_identifier]}
18
- end
10
+ def default_options
11
+ { :operator => "==" }
12
+ end
13
+ def parse_args(args)
14
+ a0, a1, a2 = args
15
+ {:question_reference => a0.to_s.gsub("q_", ""), :operator => a1}.merge(a2.is_a?(Hash) ? a2 : {:answer_reference => a2.to_s.gsub("a_", "")})
16
+ end
17
+ def parse_opts(opts)
18
+ {:rule_key => opts[:reference_identifier]}
19
+ end
19
20
 
20
- def reconcile_dependencies
21
- # Looking up references to questions and answers for linking the dependency objects
22
- print "Lookup Q ref #{@question_reference}:"
23
- if (ref_question = parser.current_survey.find_question_by_reference(@question_reference))
24
- print " found Q#{ref_question.id} "
25
- @question_id = ref_question.id
26
- print "Lookup A ref #{@answer_reference}"
27
- if (ref_answer = ref_question.find_answer_by_reference(@answer_reference))
28
- print " found A#{ref_answer.id} "
29
- @answer_id = ref_answer.id
21
+ def reconcile_dependencies
22
+ # Looking up references to questions and answers for linking the dependency objects
23
+ print "Lookup Q ref #{@question_reference}:"
24
+ if (ref_question = parser.current_survey.find_question_by_reference(@question_reference))
25
+ print " found Q#{ref_question.id} "
26
+ @question_id = ref_question.id
27
+ print "Lookup A ref #{@answer_reference}"
28
+ if (ref_answer = ref_question.find_answer_by_reference(@answer_reference))
29
+ print " found A#{ref_answer.id} "
30
+ @answer_id = ref_answer.id
31
+ else
32
+ raise "Could not find referenced answer #{@answer_reference}"
33
+ end
30
34
  else
31
- raise "Could not find referenced answer #{@answer_reference}"
35
+ raise "Could not find referenced question #{@question_reference}"
32
36
  end
33
- else
34
- raise "Could not find referenced question #{@question_reference}"
35
37
  end
36
- end
37
38
 
39
+ end
38
40
  end
@@ -0,0 +1,187 @@
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 :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.surveys = []
36
+ self.grid_answers = []
37
+ initialize_counters(@@models)
38
+ initialize_fixtures(@@models.map(&:pluralize), File.join(RAILS_ROOT, "surveys", "fixtures"))
39
+ end
40
+
41
+ # @last_survey_id, @last_survey_section_id, etc.
42
+ def initialize_counters(names)
43
+ names.each{|name| instance_variable_set("@last_#{name}_id", 0)}
44
+ end
45
+
46
+ # @surveys_yml, @survey_sections_yml, etc.
47
+ def initialize_fixtures(names, path)
48
+ names.each {|name| file = instance_variable_set("@#{name}_yml", "#{path}/#{name}.yml"); File.truncate(file, 0) if File.exist?(file) }
49
+ end
50
+
51
+ # This method_missing does all the heavy lifting for the DSL
52
+ def method_missing(missing_method, *args, &block)
53
+ method_name, reference_identifier = missing_method.to_s.split("_", 2)
54
+ opts = {:method_name => method_name, :reference_identifier => reference_identifier}
55
+ case method_name
56
+ when "survey"
57
+ self.current_survey = Survey.new(self, args, opts)
58
+ evaluate_the "survey", &block
59
+
60
+ when "section"
61
+ self.current_survey_section = SurveySection.new(self.current_survey, args, opts.merge({:display_order => current_survey.survey_sections.size + 1}))
62
+ evaluate_the "survey_section", &block
63
+
64
+ when "group", "g", "grid", "repeater"
65
+ self.current_question_group = QuestionGroup.new(self.current_survey_section, args, opts)
66
+ evaluate_the "question_group", &block
67
+
68
+ when "question", "q", "label", "image"
69
+ drop_the &block
70
+ self.current_question = Question.new(self.current_survey_section, args, opts.merge(:question_group_id => current_question_group ? current_question_group.id : nil))
71
+ add_grid_answers if in_a_grid?
72
+
73
+ when "dependency", "d"
74
+ drop_the &block
75
+ self.current_dependency = Dependency.new(self.current_question_group || current_question, args, opts)
76
+
77
+ when "condition", "c"
78
+ drop_the &block
79
+ raise "Error: No current dependency or validation for this condition" if self.current_dependency.nil? && self.current_validation.nil?
80
+ if self.current_dependency.nil?
81
+ self.current_validation.validation_conditions << ValidationCondition.new(self.current_validation, args, opts)
82
+ else
83
+ self.current_dependency.dependency_conditions << DependencyCondition.new(self.current_dependency, args, opts)
84
+ end
85
+
86
+ when "answer", "a"
87
+ drop_the &block
88
+ if in_a_grid?
89
+ self.grid_answers << Answer.new(nil, args, opts.merge(:display_order => grid_answers.size + 1))
90
+ else
91
+ raise "Error: No current question" if self.current_question.nil?
92
+ self.current_answer = Answer.new(self.current_question, args, opts.merge(:display_order => current_question.answers.size + 1))
93
+ end
94
+
95
+ when "validation", "v"
96
+ drop_the &block
97
+ self.current_validation = Validation.new(self.current_answer, args, opts)
98
+
99
+ else
100
+ raise " ERROR: '#{missing_method}' not valid method"
101
+
102
+ end
103
+ end
104
+
105
+ def drop_the(&block)
106
+ raise "Error, I'm dropping the block like it's hot" if block_given?
107
+ end
108
+
109
+ def evaluate_the(model, &block)
110
+ raise "Error: A #{model.humanize} cannot be empty" unless block_given?
111
+ self.instance_eval(&block)
112
+ self.send("clear_current", model)
113
+ end
114
+
115
+ def clear_current(model)
116
+ # puts "clear_current #{model}"
117
+ case model
118
+ when "survey"
119
+ self.current_survey.reconcile_dependencies unless current_survey.nil?
120
+ when "question_group"
121
+ self.grid_answers = []
122
+ clear_current("question")
123
+ when "question"
124
+ @current_dependency = nil
125
+ when "answer"
126
+ @current_validation = nil
127
+ end
128
+ instance_variable_set("@current_#{model}", nil)
129
+ "SurveyParser::#{model.classify}".constantize.send(:children).each{|m| clear_current(m.to_s.singularize)}
130
+ end
131
+
132
+ def current_survey=(s)
133
+ clear_current "survey"
134
+ self.surveys << s
135
+ @current_survey = s
136
+ end
137
+ def current_survey_section=(s)
138
+ clear_current "survey_section"
139
+ self.current_survey.survey_sections << s
140
+ @current_survey_section = s
141
+ end
142
+ def current_question_group=(g)
143
+ clear_current "question_group"
144
+ self.current_survey_section.question_groups << g
145
+ @current_question_group = g
146
+ end
147
+ def current_question=(q)
148
+ clear_current "question"
149
+ self.current_survey_section.questions << q
150
+ @current_question = q
151
+ end
152
+ def current_dependency=(d)
153
+ raise "Error: No question or question group" unless (dependent = self.current_question_group || self.current_question)
154
+ dependent.dependency = d
155
+ @current_dependency = d
156
+ end
157
+ def current_answer=(a)
158
+ raise "Error: No current question" if self.current_question.nil?
159
+ self.current_question.answers << a
160
+ @current_answer = a
161
+ end
162
+ def current_validation=(v)
163
+ clear_current "validation"
164
+ self.current_answer.validation = v
165
+ @current_validation = v
166
+ end
167
+
168
+ def in_a_grid?
169
+ self.current_question_group and self.current_question_group.display_type == "grid"
170
+ end
171
+
172
+ def add_grid_answers
173
+ self.grid_answers.each do |grid_answer|
174
+ my_answer = grid_answer.dup
175
+ my_answer.id = new_answer_id
176
+ my_answer.question_id = self.current_question.id
177
+ my_answer.parser = self
178
+ self.current_answer = my_answer
179
+ end
180
+ end
181
+
182
+ def to_files
183
+ self.surveys.compact.map(&:to_file)
184
+ end
185
+
186
+ end
187
+ end
@@ -1,34 +1,36 @@
1
- class Question < SurveyParser::Base
2
- # Context, Content, Reference, Display, Children
3
- attr_accessor :id, :parser, :survey_section_id, :question_group_id
4
- attr_accessor :text, :short_text, :help_text, :pick
5
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier
6
- attr_accessor :display_order, :display_type, :is_mandatory, :display_width, :custom_class, :custom_renderer
7
- attr_accessor :dependency
8
- has_children :answers
1
+ module SurveyParser
2
+ class Question < SurveyParser::Base
3
+ # Context, Content, Reference, Display, Children
4
+ attr_accessor :id, :parser, :survey_section_id, :question_group_id
5
+ attr_accessor :text, :short_text, :help_text, :pick
6
+ attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier
7
+ attr_accessor :display_order, :display_type, :is_mandatory, :display_width, :custom_class, :custom_renderer
8
+ attr_accessor :dependency
9
+ has_children :answers
9
10
 
10
- def default_options
11
- { :pick => :none,
12
- :display_type => :default,
13
- :is_mandatory => true,
14
- :display_order => self.id
15
- }
16
- end
17
- def parse_opts(opts)
18
- (name = opts.delete(:method_name)) =~ /label|image/ ? opts.merge(:display_type => name) : opts
19
- end
20
- def parse_args(args)
21
- text = args[0] || "Question"
22
- {:text => text, :short_text => text, :data_export_identifier => Surveyor.to_normalized_string(text)}.merge(args[1] || {})
23
- end
11
+ def default_options
12
+ { :pick => :none,
13
+ :display_type => :default,
14
+ :is_mandatory => true,
15
+ :display_order => self.id
16
+ }
17
+ end
18
+ def parse_opts(opts)
19
+ (name = opts.delete(:method_name)) =~ /label|image/ ? opts.merge(:display_type => name) : opts
20
+ end
21
+ def parse_args(args)
22
+ text = args[0] || "Question"
23
+ {:text => text, :short_text => text, :data_export_identifier => Surveyor.to_normalized_string(text)}.merge(args[1] || {})
24
+ end
24
25
 
25
- def find_answer_by_reference(ref_id)
26
- self.answers.detect{|a| a.reference_identifier == ref_id}
27
- end
26
+ def find_answer_by_reference(ref_id)
27
+ self.answers.detect{|a| a.reference_identifier == ref_id}
28
+ end
28
29
 
29
- def to_file
30
- super
31
- if self.dependency then self.dependency.to_file end
32
- end
30
+ def to_file
31
+ super
32
+ if self.dependency then self.dependency.to_file end
33
+ end
33
34
 
35
+ end
34
36
  end
@@ -1,24 +1,26 @@
1
- class QuestionGroup < SurveyParser::Base
2
- # Context, Content, Display, Children
3
- attr_accessor :id, :parser
4
- attr_accessor :text, :help_text
5
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
6
- attr_accessor :display_type, :custom_class, :custom_renderer
7
- attr_accessor :dependency
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
8
9
 
9
- def default_options
10
- {:display_type => "default"}
11
- end
12
- def parse_args(args)
13
- {:text => args[0] || "Question Group"}.merge(args[1] || {})
14
- end
15
- def parse_opts(opts)
16
- (name = opts.delete(:method_name)) =~ /grid|repeater/ ? opts.merge(:display_type => name) : opts
17
- end
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
18
19
 
19
- def to_file
20
- super
21
- if self.dependency then self.dependency.to_file end
22
- end
20
+ def to_file
21
+ super
22
+ if self.dependency then self.dependency.to_file end
23
+ end
23
24
 
25
+ end
24
26
  end
@@ -1,13 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'answer'
3
3
 
4
- describe Answer, " when first created" do
4
+ describe SurveyParser::Answer, " when first created" do
5
5
 
6
6
  before(:each) do
7
- question = mock("Question", :id => 2, :parser => mock("Parser", :new_answer_id => 1))
8
- question.stub!(:class => Question)
7
+ question = mock("Question", :id => 2, :parser => mock("SurveyParser::Parser", :new_answer_id => 1))
8
+ question.stub!(:class => SurveyParser::Question)
9
9
  options = {:help_text => "Never or rarely ever", :reference_identifier => "b3a_1"}
10
- @ans = Answer.new(question, ["No / Rarely"], options)
10
+ @ans = SurveyParser::Answer.new(question, ["No / Rarely"], options)
11
11
  end
12
12
 
13
13
  it "should set inititalized variables to those passed in" do
@@ -1,13 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'question'
3
3
 
4
- describe Question, " when first created" do
4
+ describe SurveyParser::Question, " when first created" do
5
5
  before do
6
- section = mock("SurveySection", :id => 2, :parser => mock("Parser", :new_question_id => 1))
7
- section.stub!(:class => SurveySection)
6
+ section = mock("SurveyParser::SurveySection", :id => 2, :parser => mock("SurveyParser::Parser", :new_question_id => 1))
7
+ section.stub!(:class => SurveyParser::SurveySection)
8
8
  args = {:help_text => "Please give a rough estimate", :reference_identifier => "B3"}
9
9
  options = {}
10
- @ques = Question.new(section, ["In the past 12 months how many times have you been to a doctor?", args], options)
10
+ @ques = SurveyParser::Question.new(section, ["In the past 12 months how many times have you been to a doctor?", args], options)
11
11
  end
12
12
 
13
13
  it "should set initialization parameters properly" do
@@ -39,15 +39,15 @@ describe Question, " when first created" do
39
39
 
40
40
  end
41
41
 
42
- describe Question, "when it contains data" do
42
+ describe SurveyParser::Question, "when it contains data" do
43
43
  before(:each) do
44
- section = mock("SurveySection", :id => 2, :parser => mock("Parser", :new_question_id => 1))
44
+ section = mock("SurveyParser::SurveySection", :id => 2, :parser => mock("SurveyParser::Parser", :new_question_id => 1))
45
45
  args = {:help_text => "Please give a rough estimate", :reference_identifier => "B3"}
46
46
  options = {}
47
- @ques = Question.new(section, ["In the past 12 months how many times have you been to a doctor?", args], options)
48
- @ques.answers << mock("Answer", :reference_identifier => "1", :text => "foo")
49
- @ques.answers << mock("Answer", :reference_identifier => "2", :text => "foo")
50
- @ques.answers << mock("Answer", :reference_identifier => "3", :text => "foo")
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
51
  end
52
52
 
53
53
  it "should have added the test answers correctly" do
@@ -1,5 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
2
2
  require File.expand_path(File.dirname(__FILE__) + "/../../../lib/surveyor")
3
+ require File.expand_path(File.dirname(__FILE__) + "/../parser")
3
4
  require File.expand_path(File.dirname(__FILE__) + "/../base")
4
5
 
5
6
  Spec::Runner.configure do |config|
@@ -1,10 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'survey_section'
3
3
 
4
- describe SurveySection, "when first created" do
4
+ describe SurveyParser::SurveySection, "when first created" do
5
5
 
6
6
  before(:each) do
7
- @section = SurveySection.new(mock("Survey", :id => 2, :parser => mock("Parser", :new_survey_section_id => 1)), ["Demographics"], {})
7
+ @section = SurveyParser::SurveySection.new(mock("SurveyParser::Survey", :id => 2, :parser => mock("SurveyParser::Parser", :new_survey_section_id => 1)), ["Demographics"], {})
8
8
  end
9
9
 
10
10
  it "should generate a data export identifier" do
@@ -12,9 +12,9 @@ describe SurveySection, "when first created" do
12
12
  end
13
13
 
14
14
  it "should find a question by reference" do
15
- @section.questions << mock("Question", :reference_identifier => "1", :text => "foo")
16
- @section.questions << mock("Question", :reference_identifier => "2", :text => "foo")
17
- @section.questions << mock("Question", :reference_identifier => "3", :text => "foo")
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
18
 
19
19
  q = @section.find_question_by_reference("2")
20
20
  q.should_not be_nil
@@ -1,13 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'validation_condition'
3
3
 
4
- describe ValidationCondition, " when first created" do
4
+ describe SurveyParser::ValidationCondition, " when first created" do
5
5
  before do
6
- validation = mock("Validation", :id => 29, :parser => mock("Parser", :new_validation_condition_id => 21))
7
- validation.stub!(:class => Validation)
6
+ validation = mock("SurveyParser::Validation", :id => 29, :parser => mock("SurveyParser::Parser", :new_validation_condition_id => 21))
7
+ validation.stub!(:class => SurveyParser::Validation)
8
8
  args = [">=", {:integer_value => 0}]
9
9
  options = {}
10
- @validation_condition = ValidationCondition.new(validation, args, options)
10
+ @validation_condition = SurveyParser::ValidationCondition.new(validation, args, options)
11
11
  end
12
12
 
13
13
  it "should set initialization parameters properly" do
@@ -1,13 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'validation'
3
3
 
4
- describe Validation, " when first created" do
4
+ describe SurveyParser::Validation, " when first created" do
5
5
  before do
6
- answer = mock("Answer", :id => 2, :parser => mock("Parser", :new_validation_id => 1))
7
- answer.stub!(:class => Answer)
6
+ answer = mock("SurveyParser::Answer", :id => 2, :parser => mock("SurveyParser::Parser", :new_validation_id => 1))
7
+ answer.stub!(:class => SurveyParser::Answer)
8
8
  args = [{:rule => "C", :message => "Please select a number between 0 and 120"}]
9
9
  options = {}
10
- @validation = Validation.new(answer, args, options)
10
+ @validation = SurveyParser::Validation.new(answer, args, options)
11
11
  end
12
12
 
13
13
  it "should set initialization parameters properly" do
@@ -1,32 +1,34 @@
1
- class Survey < SurveyParser::Base
2
- # Context, Content, Reference, Expiry, Display
3
- attr_accessor :id, :parser
4
- attr_accessor :title, :description
5
- attr_accessor :access_code, :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
6
- attr_accessor :active_at, :inactive_at
7
- attr_accessor :css_url, :custom_class
8
- has_children :survey_sections
1
+ module SurveyParser
2
+ class Survey < SurveyParser::Base
3
+ # Context, Content, Reference, Expiry, Display
4
+ attr_accessor :id, :parser
5
+ attr_accessor :title, :description
6
+ attr_accessor :access_code, :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
7
+ attr_accessor :active_at, :inactive_at
8
+ attr_accessor :css_url, :custom_class
9
+ has_children :survey_sections
9
10
 
10
- def parse_args(args)
11
- title = args[0]
12
- {:title => title, :access_code => Surveyor.to_normalized_string(title)}.merge(args[1] || {})
13
- end
11
+ def parse_args(args)
12
+ title = args[0]
13
+ {:title => title, :access_code => Surveyor.to_normalized_string(title)}.merge(args[1] || {})
14
+ end
14
15
 
15
- def find_question_by_reference(ref_id)
16
- found = nil
17
- survey_sections.detect{|s| found = s.find_question_by_reference(ref_id)}
18
- return found
19
- end
16
+ def find_question_by_reference(ref_id)
17
+ found = nil
18
+ survey_sections.detect{|s| found = s.find_question_by_reference(ref_id)}
19
+ return found
20
+ end
20
21
 
21
- def reconcile_dependencies
22
- survey_sections.each do |section|
23
- section.questions.each do |question|
24
- question.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless question.dependency.nil?
25
- end
26
- section.question_groups.each do |group|
27
- group.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless group.dependency.nil?
28
- end
29
- end
30
- end
22
+ def reconcile_dependencies
23
+ survey_sections.each do |section|
24
+ section.questions.each do |question|
25
+ question.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless question.dependency.nil?
26
+ end
27
+ section.question_groups.each do |group|
28
+ group.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless group.dependency.nil?
29
+ end
30
+ end
31
+ end
31
32
 
33
+ end
32
34
  end
@@ -1,19 +1,21 @@
1
- class SurveySection < SurveyParser::Base
2
- # Context, Content, Display, Reference, Children, Placeholders
3
- attr_accessor :id, :parser, :survey_id
4
- attr_accessor :title, :description
5
- attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
6
- attr_accessor :display_order, :custom_class
7
- has_children :question_groups, :questions
1
+ module SurveyParser
2
+ class SurveySection < SurveyParser::Base
3
+ # Context, Content, Display, Reference, Children, Placeholders
4
+ attr_accessor :id, :parser, :survey_id
5
+ attr_accessor :title, :description
6
+ attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
7
+ attr_accessor :display_order, :custom_class
8
+ has_children :question_groups, :questions
8
9
 
9
- def parse_args(args)
10
- title = args[0]
11
- {:title => title, :data_export_identifier => Surveyor.to_normalized_string(title)}.merge(args[1] || {})
12
- end
10
+ def parse_args(args)
11
+ title = args[0]
12
+ {:title => title, :data_export_identifier => Surveyor.to_normalized_string(title)}.merge(args[1] || {})
13
+ end
13
14
 
14
- # Used to find questions for dependency linking
15
- def find_question_by_reference(ref_id)
16
- self.questions.detect{|q| q.reference_identifier == ref_id}
17
- end
15
+ # Used to find questions for dependency linking
16
+ def find_question_by_reference(ref_id)
17
+ self.questions.detect{|q| q.reference_identifier == ref_id}
18
+ end
18
19
 
20
+ end
19
21
  end
@@ -1,19 +1,21 @@
1
- class Validation < SurveyParser::Base
1
+ module SurveyParser
2
+ class Validation < SurveyParser::Base
2
3
 
3
- # Context, Conditional, Children
4
- attr_accessor :id, :answer_id, :parser
5
- attr_accessor :rule, :message
6
- has_children :validation_conditions
4
+ # Context, Conditional, Children
5
+ attr_accessor :id, :answer_id, :parser
6
+ attr_accessor :rule, :message
7
+ has_children :validation_conditions
7
8
 
8
9
 
9
- def default_options
10
- {:rule => "A"}
11
- end
12
- def parse_args(args)
13
- args[0] || {}
14
- end
15
- def parse_opts(opts)
16
- {} # toss the method name and reference identifier by default
17
- end
10
+ def default_options
11
+ {:rule => "A"}
12
+ end
13
+ def parse_args(args)
14
+ args[0] || {}
15
+ end
16
+ def parse_opts(opts)
17
+ {} # toss the method name and reference identifier by default
18
+ end
18
19
 
20
+ end
19
21
  end
@@ -1,19 +1,21 @@
1
- class ValidationCondition < SurveyParser::Base
2
- # Context, Conditional, Value, Reference
3
- attr_accessor :id, :validation_id, :rule_key, :parser
4
- attr_accessor :operator
5
- attr_accessor :question_id, :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other, :regexp
6
- attr_accessor :question_reference, :answer_reference
1
+ module SurveyParser
2
+ class ValidationCondition < SurveyParser::Base
3
+ # Context, Conditional, Value, Reference
4
+ attr_accessor :id, :validation_id, :rule_key, :parser
5
+ attr_accessor :operator
6
+ attr_accessor :question_id, :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other, :regexp
7
+ attr_accessor :question_reference, :answer_reference
7
8
 
8
- def default_options
9
- { :operator => "==" }
10
- end
11
- def parse_args(args)
12
- a0, a1 = args
13
- {:operator => a0}.merge(a1 || {})
14
- end
15
- def parse_opts(opts)
16
- {:rule_key => opts[:reference_identifier]}
17
- end
9
+ def default_options
10
+ { :operator => "==" }
11
+ end
12
+ def parse_args(args)
13
+ a0, a1 = args
14
+ {:operator => a0}.merge(a1 || {})
15
+ end
16
+ def parse_opts(opts)
17
+ {:rule_key => opts[:reference_identifier]}
18
+ end
18
19
 
20
+ end
19
21
  end
data/spec/factories.rb CHANGED
@@ -98,7 +98,7 @@ end
98
98
  Factory.define :response_set do |r|
99
99
  r.user_id {}
100
100
  r.association :survey # r.survey_id {}
101
- r.access_code {ResponseSet.make_tiny_code}
101
+ r.access_code {Surveyor.make_tiny_code}
102
102
  r.started_at {Time.now}
103
103
  r.completed_at {}
104
104
  end
data/surveyor.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{surveyor}
8
- s.version = "0.9.1"
8
+ s.version = "0.9.2"
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"]
@@ -137,6 +137,7 @@ Gem::Specification.new do |s|
137
137
  "script/surveyor/base.rb",
138
138
  "script/surveyor/dependency.rb",
139
139
  "script/surveyor/dependency_condition.rb",
140
+ "script/surveyor/parser.rb",
140
141
  "script/surveyor/question.rb",
141
142
  "script/surveyor/question_group.rb",
142
143
  "script/surveyor/specs/answer_spec.rb",
@@ -146,7 +147,6 @@ Gem::Specification.new do |s|
146
147
  "script/surveyor/specs/validation_condition_spec.rb",
147
148
  "script/surveyor/specs/validation_spec.rb",
148
149
  "script/surveyor/survey.rb",
149
- "script/surveyor/survey_parser.rb",
150
150
  "script/surveyor/survey_section.rb",
151
151
  "script/surveyor/validation.rb",
152
152
  "script/surveyor/validation_condition.rb",
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.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Chamberlain
@@ -154,6 +154,7 @@ files:
154
154
  - script/surveyor/base.rb
155
155
  - script/surveyor/dependency.rb
156
156
  - script/surveyor/dependency_condition.rb
157
+ - script/surveyor/parser.rb
157
158
  - script/surveyor/question.rb
158
159
  - script/surveyor/question_group.rb
159
160
  - script/surveyor/specs/answer_spec.rb
@@ -163,7 +164,6 @@ files:
163
164
  - script/surveyor/specs/validation_condition_spec.rb
164
165
  - script/surveyor/specs/validation_spec.rb
165
166
  - script/surveyor/survey.rb
166
- - script/surveyor/survey_parser.rb
167
167
  - script/surveyor/survey_section.rb
168
168
  - script/surveyor/validation.rb
169
169
  - script/surveyor/validation_condition.rb
@@ -1,186 +0,0 @@
1
- require 'activesupport' # for pluralize, humanize in ActiveSupport::CoreExtensions::String::Inflections
2
-
3
- class SurveyParser
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 :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.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.surveys = []
36
- self.grid_answers = []
37
- initialize_counters(@@models)
38
- initialize_fixtures(@@models.map(&:pluralize), File.join(RAILS_ROOT, "surveys", "fixtures"))
39
- end
40
-
41
- # @last_survey_id, @last_survey_section_id, etc.
42
- def initialize_counters(names)
43
- names.each{|name| instance_variable_set("@last_#{name}_id", 0)}
44
- end
45
-
46
- # @surveys_yml, @survey_sections_yml, etc.
47
- def initialize_fixtures(names, path)
48
- names.each {|name| file = instance_variable_set("@#{name}_yml", "#{path}/#{name}.yml"); File.truncate(file, 0) if File.exist?(file) }
49
- end
50
-
51
- # This method_missing does all the heavy lifting for the DSL
52
- def method_missing(missing_method, *args, &block)
53
- method_name, reference_identifier = missing_method.to_s.split("_", 2)
54
- opts = {:method_name => method_name, :reference_identifier => reference_identifier}
55
- case method_name
56
- when "survey"
57
- self.current_survey = Survey.new(self, args, opts)
58
- evaluate_the "survey", &block
59
-
60
- when "section"
61
- self.current_survey_section = SurveySection.new(self.current_survey, args, opts.merge({:display_order => current_survey.survey_sections.size + 1}))
62
- evaluate_the "survey_section", &block
63
-
64
- when "group", "g", "grid", "repeater"
65
- self.current_question_group = QuestionGroup.new(self.current_survey_section, args, opts)
66
- evaluate_the "question_group", &block
67
-
68
- when "question", "q", "label", "image"
69
- drop_the &block
70
- self.current_question = Question.new(self.current_survey_section, args, opts.merge(:question_group_id => current_question_group ? current_question_group.id : nil))
71
- add_grid_answers if in_a_grid?
72
-
73
- when "dependency", "d"
74
- drop_the &block
75
- self.current_dependency = Dependency.new(self.current_question_group || current_question, args, opts)
76
-
77
- when "condition", "c"
78
- drop_the &block
79
- raise "Error: No current dependency or validation for this condition" if self.current_dependency.nil? && self.current_validation.nil?
80
- if self.current_dependency.nil?
81
- self.current_validation.validation_conditions << ValidationCondition.new(self.current_validation, args, opts)
82
- else
83
- self.current_dependency.dependency_conditions << DependencyCondition.new(self.current_dependency, args, opts)
84
- end
85
-
86
- when "answer", "a"
87
- drop_the &block
88
- if in_a_grid?
89
- self.grid_answers << Answer.new(nil, args, opts.merge(:display_order => grid_answers.size + 1))
90
- else
91
- raise "Error: No current question" if self.current_question.nil?
92
- self.current_answer = Answer.new(self.current_question, args, opts.merge(:display_order => current_question.answers.size + 1))
93
- end
94
-
95
- when "validation", "v"
96
- drop_the &block
97
- self.current_validation = Validation.new(self.current_answer, args, opts)
98
-
99
- else
100
- raise " ERROR: '#{missing_method}' not valid method"
101
-
102
- end
103
- end
104
-
105
- def drop_the(&block)
106
- raise "Error, I'm dropping the block like it's hot" if block_given?
107
- end
108
-
109
- def evaluate_the(model, &block)
110
- raise "Error: A #{model.humanize} cannot be empty" unless block_given?
111
- self.instance_eval(&block)
112
- self.send("clear_current", model)
113
- end
114
-
115
- def clear_current(model)
116
- # puts "clear_current #{model}"
117
- case model
118
- when "survey"
119
- self.current_survey.reconcile_dependencies unless current_survey.nil?
120
- when "question_group"
121
- self.grid_answers = []
122
- clear_current("question")
123
- when "question"
124
- @current_dependency = nil
125
- when "answer"
126
- @current_validation = nil
127
- end
128
- instance_variable_set("@current_#{model}", nil)
129
- model.classify.constantize.send(:children).each{|m| clear_current(m.to_s.singularize)}
130
- end
131
-
132
- def current_survey=(s)
133
- clear_current "survey"
134
- self.surveys << s
135
- @current_survey = s
136
- end
137
- def current_survey_section=(s)
138
- clear_current "survey_section"
139
- self.current_survey.survey_sections << s
140
- @current_survey_section = s
141
- end
142
- def current_question_group=(g)
143
- clear_current "question_group"
144
- self.current_survey_section.question_groups << g
145
- @current_question_group = g
146
- end
147
- def current_question=(q)
148
- clear_current "question"
149
- self.current_survey_section.questions << q
150
- @current_question = q
151
- end
152
- def current_dependency=(d)
153
- raise "Error: No question or question group" unless (dependent = self.current_question_group || self.current_question)
154
- dependent.dependency = d
155
- @current_dependency = d
156
- end
157
- def current_answer=(a)
158
- raise "Error: No current question" if self.current_question.nil?
159
- self.current_question.answers << a
160
- @current_answer = a
161
- end
162
- def current_validation=(v)
163
- clear_current "validation"
164
- self.current_answer.validation = v
165
- @current_validation = v
166
- end
167
-
168
- def in_a_grid?
169
- self.current_question_group and self.current_question_group.display_type == "grid"
170
- end
171
-
172
- def add_grid_answers
173
- self.grid_answers.each do |grid_answer|
174
- my_answer = grid_answer.dup
175
- my_answer.id = new_answer_id
176
- my_answer.question_id = self.current_question.id
177
- my_answer.parser = self
178
- self.current_answer = my_answer
179
- end
180
- end
181
-
182
- def to_files
183
- self.surveys.compact.map(&:to_file)
184
- end
185
-
186
- end