surveyor 0.22.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +83 -0
  4. data/Gemfile.rails_version +7 -1
  5. data/README.md +114 -16
  6. data/Rakefile +15 -9
  7. data/app/inputs/quiet_input.rb +5 -0
  8. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  9. data/app/inputs/surveyor_radio_input.rb +18 -0
  10. data/app/views/partials/_answer.html.haml +4 -4
  11. data/app/views/partials/_question.html.haml +7 -7
  12. data/app/views/partials/_question_group.html.haml +9 -8
  13. data/app/views/surveyor/export.json.rabl +28 -25
  14. data/app/views/surveyor/new.html.haml +8 -5
  15. data/app/views/surveyor/show.json.rabl +3 -2
  16. data/ci-exec.sh +13 -7
  17. data/cucumber.yml +3 -3
  18. data/doc/REPRESENTATIONS.md +34 -0
  19. data/doc/api_id_schema.json +7 -0
  20. data/doc/response_set_schema.json +54 -0
  21. data/doc/surveyor question combinations.png +0 -0
  22. data/doc/surveyor_timestamp_schema.json +9 -0
  23. data/features/ajax_submissions.feature +140 -0
  24. data/features/export_to_json.feature +182 -34
  25. data/features/no_duplicates.feature +110 -0
  26. data/features/show_survey.feature +1 -1
  27. data/features/step_definitions/parser_steps.rb +25 -2
  28. data/features/step_definitions/surveyor_steps.rb +145 -20
  29. data/features/step_definitions/ui_steps.rb +3 -0
  30. data/features/support/database_cleaner.rb +16 -0
  31. data/features/support/env.rb +21 -17
  32. data/features/support/simultaneous_ajax.rb +101 -0
  33. data/features/support/single_quit_selenium_driver.rb +23 -0
  34. data/features/support/slow_updates.rb +18 -0
  35. data/features/surveyor.feature +174 -44
  36. data/features/surveyor_dependencies.feature +80 -39
  37. data/features/surveyor_parser.feature +114 -20
  38. data/features/z_redcap_parser.feature +0 -1
  39. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/next.gif +0 -0
  40. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/prev.gif +0 -0
  41. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/lib/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ef8c08_256x240.png +0 -0
  48. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ffffff_256x240.png +0 -0
  49. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui-timepicker-addon.js +23 -23
  50. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui.js +125 -125
  51. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  52. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.surveyor.js +52 -57
  53. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.tools.min.js +7 -7
  54. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/dateinput.css +13 -13
  55. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui-timepicker-addon.css +0 -0
  56. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui.custom.css +17 -17
  57. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/reset.css +1 -1
  58. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/results.css +0 -0
  59. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  60. data/lib/{generators/surveyor/templates/public/stylesheets/sass → assets/stylesheets}/surveyor.sass +15 -7
  61. data/lib/generators/surveyor/custom_generator.rb +3 -2
  62. data/lib/generators/surveyor/install_generator.rb +59 -17
  63. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +5 -0
  64. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  65. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +2 -1
  66. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +1 -0
  67. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +1 -0
  68. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +1 -0
  69. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +1 -0
  70. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +1 -0
  71. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +1 -0
  72. data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +1 -0
  73. data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +1 -0
  74. data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +1 -0
  75. data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +1 -0
  76. data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +1 -0
  77. data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +1 -0
  78. data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +1 -0
  79. data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
  80. data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +3 -2
  81. data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
  82. data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
  83. data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +6 -5
  84. data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +2 -1
  85. data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +2 -1
  86. data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +5 -4
  87. data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +4 -3
  88. data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +1 -0
  89. data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +10 -9
  90. data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +5 -4
  91. data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +4 -3
  92. data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +5 -4
  93. data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +3 -2
  94. data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
  95. data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
  96. data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
  97. data/lib/generators/surveyor/templates/surveys/date_survey.rb +1 -0
  98. data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -24
  99. data/lib/generators/surveyor/templates/surveys/quiz.rb +1 -0
  100. data/lib/generators/surveyor/templates/{public/stylesheets/sass → vendor/assets/stylesheets}/custom.sass +1 -1
  101. data/lib/surveyor/common.rb +16 -31
  102. data/lib/surveyor/engine.rb +2 -4
  103. data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
  104. data/lib/surveyor/helpers/formtastic_custom_input.rb +17 -0
  105. data/lib/surveyor/helpers/surveyor_helper_methods.rb +10 -8
  106. data/lib/surveyor/models/answer_methods.rb +3 -0
  107. data/lib/surveyor/models/dependency_condition_methods.rb +27 -28
  108. data/lib/surveyor/models/dependency_methods.rb +3 -0
  109. data/lib/surveyor/models/question_group_methods.rb +3 -0
  110. data/lib/surveyor/models/question_methods.rb +10 -7
  111. data/lib/surveyor/models/response_methods.rb +16 -0
  112. data/lib/surveyor/models/response_set_methods.rb +71 -64
  113. data/lib/surveyor/models/survey_methods.rb +19 -28
  114. data/lib/surveyor/models/survey_section_methods.rb +3 -0
  115. data/lib/surveyor/models/validation_condition_methods.rb +4 -2
  116. data/lib/surveyor/models/validation_methods.rb +3 -0
  117. data/lib/surveyor/parser.rb +198 -148
  118. data/lib/surveyor/redcap_parser.rb +120 -80
  119. data/lib/surveyor/surveyor_controller_methods.rb +86 -37
  120. data/lib/surveyor/version.rb +2 -2
  121. data/lib/surveyor.rb +5 -6
  122. data/lib/tasks/surveyor_tasks.rake +19 -7
  123. data/spec/controllers/surveyor_controller_spec.rb +166 -92
  124. data/spec/factories.rb +33 -32
  125. data/spec/helpers/formtastic_custom_input_spec.rb +16 -0
  126. data/spec/lib/common_spec.rb +0 -39
  127. data/spec/lib/redcap_parser_spec.rb +24 -24
  128. data/spec/models/answer_spec.rb +12 -0
  129. data/spec/models/dependency_condition_spec.rb +279 -323
  130. data/spec/models/dependency_spec.rb +10 -0
  131. data/spec/models/question_group_spec.rb +12 -0
  132. data/spec/models/question_spec.rb +12 -0
  133. data/spec/models/response_set_spec.rb +189 -139
  134. data/spec/models/response_spec.rb +60 -0
  135. data/spec/models/survey_section_spec.rb +9 -0
  136. data/spec/models/survey_spec.rb +72 -9
  137. data/spec/models/validation_condition_spec.rb +9 -1
  138. data/spec/models/validation_spec.rb +10 -0
  139. data/spec/spec_helper.rb +25 -6
  140. data/surveyor.gemspec +5 -4
  141. metadata +332 -291
  142. data/features/step_definitions/common_steps.rb +0 -3
  143. data/lib/formtastic/surveyor_builder.rb +0 -82
  144. data/lib/generators/surveyor/templates/public/javascripts/surveyor/jquery.blockUI.js +0 -499
@@ -1,8 +1,9 @@
1
1
  %w(survey survey_section question_group question dependency dependency_condition answer validation validation_condition).each {|model| require model }
2
2
  module Surveyor
3
+ class ParserError < StandardError; end
3
4
  class Parser
4
5
  class << self; attr_accessor :options end
5
-
6
+
6
7
  # Attributes
7
8
  attr_accessor :context
8
9
 
@@ -17,7 +18,7 @@ module Surveyor
17
18
  self.options ||= {}
18
19
  print str if self.options[:trace] == true
19
20
  end
20
-
21
+
21
22
  # Instance methods
22
23
  def initialize
23
24
  self.context = {}
@@ -30,27 +31,31 @@ module Surveyor
30
31
  def method_missing(missing_method, *args, &block)
31
32
  method_name, reference_identifier = missing_method.to_s.split("_", 2)
32
33
  type = full(method_name)
33
-
34
+
34
35
  Surveyor::Parser.rake_trace reference_identifier.blank? ? "#{type} " : "#{type}_#{reference_identifier} "
35
-
36
+
36
37
  # check for blocks
37
- raise "Error: A #{type.humanize} cannot be empty" if block_models.include?(type) && !block_given?
38
- raise "Error: Dropping the #{type.humanize} block like it's hot!" if !block_models.include?(type) && block_given?
39
-
38
+ raise Surveyor::ParserError, "Error: A #{type.humanize} cannot be empty" if block_models.include?(type) && !block_given?
39
+ raise Surveyor::ParserError, "Error: Dropping the #{type.humanize} block like it's hot!" if !block_models.include?(type) && block_given?
40
+
40
41
  # parse and build
41
- type.classify.constantize.parse_and_build(context, args, method_name, reference_identifier)
42
-
42
+ type.classify.constantize.new.extend("SurveyorParser#{type.classify}Methods".constantize).parse_and_build(context, args, method_name, reference_identifier)
43
+
43
44
  # evaluate and clear context for block models
44
45
  if block_models.include?(type)
45
- self.instance_eval(&block)
46
+ self.instance_eval(&block)
46
47
  if type == 'survey'
48
+ resolve_dependency_condition_references
49
+ resolve_question_correct_answers
50
+ report_lost_and_duplicate_references
47
51
  Surveyor::Parser.rake_trace "\n"
48
- Surveyor::Parser.rake_trace context[type.to_sym].save ? "saved. " : " not saved! #{context[type.to_sym].errors.each_full{|x| x }.join(", ")} "
52
+ Surveyor::Parser.rake_trace context[:survey].save ? "saved. " : " not saved! #{context[type.to_sym].errors.each_full{|x| x }.join(", ")} "
53
+ else
54
+ context[type.to_sym].clear(context)
49
55
  end
50
- context[type.to_sym].clear(context) unless type == 'survey'
51
56
  end
52
57
  end
53
-
58
+
54
59
  # Private methods
55
60
  private
56
61
 
@@ -71,228 +76,273 @@ module Surveyor
71
76
  def block_models
72
77
  %w(survey survey_section question_group)
73
78
  end
79
+ def report_lost_and_duplicate_references
80
+ raise Surveyor::ParserError, "Bad references: #{self.context[:bad_references].join("; ")}" unless self.context[:bad_references].empty?
81
+ raise Surveyor::ParserError, "Duplicate references: #{self.context[:duplicate_references].join("; ")}" unless self.context[:duplicate_references].empty?
82
+ end
83
+ def resolve_question_correct_answers
84
+ self.context[:questions_with_correct_answers].each do |question_reference_idenitifer, correct_answer_reference|
85
+ # Looking up references for quiz answers
86
+ if self.context[:answer_references][question_reference_idenitifer] &&
87
+ (a = self.context[:answer_references][question_reference_idenitifer][correct_answer_reference]) &&
88
+ a.save
89
+ self.context[:question_references][question_reference_idenitifer].correct_answer_id = a.id
90
+ else
91
+ self.context[:bad_references].push "q_#{question_reference_idenitifer}.correct => a_#{correct_answer_reference}"
92
+ end
93
+ end
94
+ end
95
+ def resolve_dependency_condition_references
96
+ self.context[:dependency_conditions].each do |dc|
97
+ # Looking up references to questions and answers for linking the dependency objects
98
+ self.context[:bad_references].push "q_#{dc.question_reference}" unless (dc.question = self.context[:question_references][dc.question_reference])
99
+ self.context[:answer_references][dc.question_reference] ||= {}
100
+ self.context[:bad_references].push "q_#{dc.question_reference}, a_#{dc.answer_reference}" if !dc.answer_reference.blank? and (dc.answer = self.context[:answer_references][dc.question_reference][dc.answer_reference]).nil?
101
+ end
102
+ end
74
103
  end
75
104
  end
76
105
 
77
106
  # Surveyor models with extra parsing methods
78
- class Survey < ActiveRecord::Base
79
- # block
80
-
81
- def self.parse_and_build(context, args, original_method, reference_identifier)
107
+
108
+ # Survey model
109
+ module SurveyorParserSurveyMethods
110
+ def parse_and_build(context, args, original_method, reference_identifier)
82
111
  # clear context
83
- context.delete_if{|k,v| true }
84
- context[:question_references] = {}
85
- context[:answer_references] = {}
86
-
112
+ clear(context)
113
+
87
114
  # build and set context
88
115
  title = args[0]
89
- context[:survey] = new({ :title => title,
90
- :reference_identifier => reference_identifier }.merge(args[1] || {}))
116
+ self.attributes = ({
117
+ :title => title,
118
+ :reference_identifier => reference_identifier }.merge(args[1] || {}))
119
+ context[:survey] = self
91
120
  end
92
121
  def clear(context)
93
- context.delete_if{|k,v| true }
94
- context[:question_references] = {}
95
- context[:answer_references] = {}
122
+ context.delete_if{|k,v| true}
123
+ context.merge!({
124
+ :question_references => {},
125
+ :answer_references => {},
126
+ :bad_references => [],
127
+ :duplicate_references => [],
128
+ :dependency_conditions => [],
129
+ :questions_with_correct_answers => {} })
96
130
  end
97
131
  end
98
- class SurveySection < ActiveRecord::Base
99
- # block
100
-
101
- def self.parse_and_build(context, args, original_method, reference_identifier)
132
+
133
+ # SurveySection model
134
+ module SurveyorParserSurveySectionMethods
135
+ def parse_and_build(context, args, original_method, reference_identifier)
102
136
  # clear context
103
- context.delete_if{|k,v| !%w(survey question_references answer_references).map(&:to_sym).include?(k)}
104
-
137
+ clear(context)
138
+
105
139
  # build and set context
106
140
  title = args[0]
107
- context[:survey_section] = context[:survey].sections.build({ :title => title,
108
- :display_order => context[:survey].sections.size }.merge(args[1] || {}))
141
+ self.attributes = ({
142
+ :title => title,
143
+ :display_order => context[:survey].sections.size }.merge(args[1] || {}))
144
+ context[:survey].sections << context[:survey_section] = self
109
145
  end
110
146
  def clear(context)
111
- context.delete_if{|k,v| !%w(survey question_references answer_references).map(&:to_sym).include?(k)}
147
+ [ :survey_section,
148
+ :question_group,
149
+ :question,
150
+ :dependency,
151
+ :dependency_condition,
152
+ :answer,
153
+ :validation,
154
+ :validation_condition ].each{|k| context.delete k}
112
155
  end
113
156
  end
114
- class QuestionGroup < ActiveRecord::Base
115
- # block
116
-
117
- def self.parse_and_build(context, args, original_method, reference_identifier)
157
+
158
+ # QuestionGroup model
159
+ module SurveyorParserQuestionGroupMethods
160
+ def parse_and_build(context, args, original_method, reference_identifier)
118
161
  # clear context
119
- context.delete_if{|k,v| !%w(survey survey_section question_references answer_references).map(&:to_sym).include?(k)}
120
-
121
- # build and set context
122
- context[:question_group] = context[:question_group] = new({ :text => args[0] || "Question Group",
123
- :display_type => (original_method =~ /grid|repeater/ ? original_method : "default")}.merge(args[1] || {}))
162
+ clear(context)
124
163
 
164
+ # build and set context
165
+ self.attributes = ({
166
+ :text => args[0] || "Question Group",
167
+ :display_type => (original_method =~ /grid|repeater/ ? original_method : "default")}.merge(args[1] || {}))
168
+ context[:question_group] = self
125
169
  end
126
170
  def clear(context)
127
- context.delete_if{|k,v| !%w(survey survey_section question_references answer_references).map(&:to_sym).include?(k)}
171
+ [ :question_group,
172
+ :question,
173
+ :dependency,
174
+ :dependency_condition,
175
+ :answer,
176
+ :validation,
177
+ :validation_condition ].each{|k| context.delete k}
128
178
  end
129
179
  end
130
- class Question < ActiveRecord::Base
131
- # nonblock
132
-
133
- # attributes
134
- attr_accessor :correct, :context_reference
135
- before_save :resolve_correct_answers
136
-
137
- def self.parse_and_build(context, args, original_method, reference_identifier)
180
+
181
+ # Question model
182
+ module SurveyorParserQuestionMethods
183
+ def parse_and_build(context, args, original_method, reference_identifier)
138
184
  # clear context
139
- context.delete_if{|k,v| %w(question dependency dependency_condition answer validation validation_condition).map(&:to_sym).include? k}
140
-
185
+ [ :question,
186
+ :dependency,
187
+ :dependency_condition,
188
+ :answer,
189
+ :validation,
190
+ :validation_condition ].each{|k| context.delete k}
191
+
141
192
  # build and set context
142
193
  text = args[0] || "Question"
143
- context[:question] = context[:survey_section].questions.build({
144
- :context_reference => context,
194
+ hash_args = args[1] || {}
195
+ correct = hash_args.delete :correct
196
+ self.attributes = ({
145
197
  :question_group => context[:question_group],
146
198
  :reference_identifier => reference_identifier,
147
199
  :text => text,
148
200
  :display_type => (original_method =~ /label|image/ ? original_method : "default"),
149
- :display_order => context[:survey_section].questions.size }.merge(args[1] || {}))
150
-
201
+ :display_order => context[:survey_section].questions.size }.merge(hash_args))
202
+ context[:survey_section].questions << context[:question] = self
203
+
204
+ # keep reference for correct answers
205
+ context[:questions_with_correct_answers][self.reference_identifier] = correct unless self.reference_identifier.blank? or correct.blank?
206
+
151
207
  # keep reference for dependencies
152
- context[:question_references][reference_identifier] = context[:question] unless reference_identifier.blank?
153
-
208
+ unless self.reference_identifier.blank?
209
+ context[:duplicate_references].push "q_#{self.reference_identifier}" if context[:question_references].has_key?(self.reference_identifier)
210
+ context[:question_references][self.reference_identifier] = context[:question]
211
+ end
212
+
154
213
  # add grid answers
155
214
  if context[:question_group] && context[:question_group].display_type == "grid"
156
215
  (context[:grid_answers] || []).each do |grid_answer|
157
- a = context[:question].answers.build(grid_answer.attributes)
158
- context[:answer_references][reference_identifier] ||= {} unless reference_identifier.blank?
159
- context[:answer_references][reference_identifier][grid_answer.reference_identifier] = a unless reference_identifier.blank? or grid_answer.reference_identifier.blank?
216
+ a = context[:question].answers.build(grid_answer.attributes.reject{|k,v| %w(id api_id created_at updated_at).include?(k)})
217
+ context[:answer_references][self.reference_identifier] ||= {} unless self.reference_identifier.blank?
218
+ context[:answer_references][self.reference_identifier][grid_answer.reference_identifier] = a unless self.reference_identifier.blank? or grid_answer.reference_identifier.blank?
160
219
  end
161
220
  end
162
221
  end
163
-
164
- def resolve_correct_answers
165
- unless correct.blank? or reference_identifier.blank? or context_reference.blank?
166
- # Looking up references for quiz answers
167
- context_reference[:answer_references][reference_identifier] ||= {}
168
- Surveyor::Parser.rake_trace( (self.correct_answer = context_reference[:answer_references][reference_identifier][correct]) ? "found correct answer:#{correct} " : "lost! correct answer:#{correct} ")
169
- end
170
- end
171
222
  end
172
- class Dependency < ActiveRecord::Base
173
- # nonblock
174
-
175
- def self.parse_and_build(context, args, original_method, reference_identifier)
223
+
224
+ # Dependency model
225
+ module SurveyorParserDependencyMethods
226
+ def parse_and_build(context, args, original_method, reference_identifier)
176
227
  # clear context
177
- context.delete_if{|k,v| %w(dependency dependency_condition).map(&:to_sym).include? k}
178
-
228
+ [ :dependency,
229
+ :dependency_condition ].each{|k| context.delete k}
230
+
179
231
  # build and set context
232
+ self.attributes = (args[0] || {})
180
233
  if context[:question]
181
- context[:dependency] = context[:question].build_dependency(args[0] || {})
234
+ context[:dependency] = context[:question].dependency = self
182
235
  elsif context[:question_group]
183
- context[:dependency] = context[:question_group].build_dependency(args[0] || {})
236
+ context[:dependency] = context[:question_group].dependency = self
184
237
  end
185
238
  end
186
239
  end
187
- class DependencyCondition < ActiveRecord::Base
188
- # nonblock
189
-
190
- attr_accessor :question_reference, :answer_reference, :context_reference
191
- before_save :resolve_references
192
-
193
- def self.parse_and_build(context, args, original_method, reference_identifier)
240
+
241
+ # DependencyCondition model
242
+ module SurveyorParserDependencyConditionMethods
243
+ DependencyCondition.instance_eval do
244
+ attr_accessor :question_reference, :answer_reference
245
+ attr_accessible :question_reference, :answer_reference
246
+ end
247
+ def parse_and_build(context, args, original_method, reference_identifier)
194
248
  # clear context
195
- context.delete_if{|k,v| k == :dependency_condition}
196
-
249
+ context.delete :dependency_condition
250
+
197
251
  # build and set context
198
252
  a0, a1, a2 = args
199
- context[:dependency_condition] = context[:dependency].
200
- dependency_conditions.build(
201
- {
202
- :context_reference => context,
203
- :operator => a1 || "==",
204
- :question_reference => a0.to_s.gsub(/^q_/, ""),
205
- :rule_key => reference_identifier
206
- }.merge(
207
- a2.is_a?(Hash) ? a2 : { :answer_reference =>
208
- a2.to_s.gsub(/^a_/, "") }
209
- )
210
- )
211
- end
212
-
213
- def resolve_references
214
- if context_reference
215
- # Looking up references to questions and answers for linking the dependency objects
216
- Surveyor::Parser.rake_trace( (self.question = context_reference[:question_references][question_reference]) ? "found question:#{question_reference} " : "lost! question:#{question_reference} ")
217
- context_reference[:answer_references][question_reference] ||= {}
218
- Surveyor::Parser.rake_trace( (self.answer = context_reference[:answer_references][question_reference][answer_reference]) ? "found answer:#{answer_reference} " : "lost! answer:#{answer_reference} ")
219
- end
253
+ self.attributes = ({
254
+ :operator => a1 || "==",
255
+ :question_reference => a0.to_s.gsub(/^q_/, ""),
256
+ :rule_key => reference_identifier
257
+ }.merge( a2.is_a?(Hash) ? a2 : { :answer_reference => a2.to_s.gsub(/^a_/, "") }))
258
+ context[:dependency].dependency_conditions << context[:dependency_condition] = self
259
+ context[:dependency_conditions] << self
220
260
  end
221
261
  end
222
262
 
223
- class Answer < ActiveRecord::Base
224
- # nonblock
225
-
226
- def self.parse_and_build(context, args, original_method, reference_identifier)
263
+ # Answer model
264
+ module SurveyorParserAnswerMethods
265
+ def parse_and_build(context, args, original_method, reference_identifier)
227
266
  # clear context
228
- context.delete_if{|k,v| %w(answer validation validation_condition reference_identifier).map(&:to_sym).include? k}
267
+ [ :answer,
268
+ :validation,
269
+ :validation_condition ].each{|k| context.delete k}
270
+ attrs = { :reference_identifier => reference_identifier }.merge(parse_args(args))
229
271
 
230
- attrs = { :reference_identifier => reference_identifier }.merge(self.parse_args(args))
231
-
232
272
  # add answers to grid
233
273
  if context[:question_group] && context[:question_group].display_type == "grid"
234
- context[:grid_answers] ||= []
235
- context[:answer] = new({:display_order => [:grid_answers].size}.merge(attrs))
236
- context[:grid_answers] << context[:answer]
274
+ context[:grid_answers] ||= []
275
+ self.attributes = ({:display_order => [:grid_answers].size}.merge(attrs))
276
+ context[:grid_answers] << context[:answer] = self
237
277
  else
238
- context[:answer] = context[:question].answers.build({:display_order => context[:question].answers.size}.merge(attrs))
239
- context[:answer_references][context[:question].reference_identifier] ||= {} unless context[:question].reference_identifier.blank?
240
- context[:answer_references][context[:question].reference_identifier][reference_identifier] = context[:answer] unless reference_identifier.blank? or context[:question].reference_identifier.blank?
278
+ self.attributes = ({:display_order => context[:question].answers.size}.merge(attrs))
279
+ context[:question].answers << context[:answer] = self
280
+ # keep reference for dependencies
281
+ unless context[:question].reference_identifier.blank? or reference_identifier.blank?
282
+ context[:answer_references][context[:question].reference_identifier] ||= {}
283
+ context[:duplicate_references].push "q_#{context[:question].reference_identifier}, a_#{reference_identifier}" if context[:answer_references][context[:question].reference_identifier].has_key?(reference_identifier)
284
+ context[:answer_references][context[:question].reference_identifier][reference_identifier] = context[:answer]
285
+ end
241
286
  end
242
287
  end
243
- def self.parse_args(args)
288
+ def parse_args(args)
244
289
  case args[0]
245
290
  when Hash # Hash
246
- self.text_args(args[0][:text]).merge(args[0])
291
+ text_args(args[0][:text]).merge(args[0])
247
292
  when String # (String, Hash) or (String, Symbol, Hash)
248
- self.text_args(args[0]).merge(self.hash_from args[1]).merge(args[2] || {})
293
+ text_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
249
294
  when Symbol # (Symbol, Hash) or (Symbol, Symbol, Hash)
250
- self.symbol_args(args[0]).merge(self.hash_from args[1]).merge(args[2] || {})
295
+ symbol_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
251
296
  else
252
- self.text_args(nil)
297
+ text_args(nil)
253
298
  end
254
299
  end
255
- def self.text_args(text = "Answer")
300
+ def text_args(text = "Answer")
256
301
  {:text => text.to_s}
257
302
  end
258
- def self.hash_from(arg)
303
+ def hash_from(arg)
259
304
  arg.is_a?(Symbol) ? {:response_class => arg.to_s} : arg.is_a?(Hash) ? arg : {}
260
305
  end
261
- def self.symbol_args(arg)
306
+ def symbol_args(arg)
262
307
  case arg
263
308
  when :other
264
- self.text_args("Other")
309
+ text_args("Other")
265
310
  when :other_and_string
266
- self.text_args("Other").merge({:response_class => "string"})
311
+ text_args("Other").merge({:response_class => "string"})
267
312
  when :none, :omit # is_exclusive erases and disables other checkboxes and input elements
268
- self.text_args(arg.to_s.humanize).merge({:is_exclusive => true})
313
+ text_args(arg.to_s.humanize).merge({:is_exclusive => true})
269
314
  when :integer, :float, :date, :time, :datetime, :text, :datetime, :string
270
- self.text_args(arg.to_s.humanize).merge({:response_class => arg.to_s, :display_type => "hidden_label"})
315
+ text_args(arg.to_s.humanize).merge({:response_class => arg.to_s, :display_type => "hidden_label"})
271
316
  end
272
317
  end
273
318
  end
274
- class Validation < ActiveRecord::Base
275
- # nonblock
276
-
277
- def self.parse_and_build(context, args, original_method, reference_identifier)
319
+
320
+ # Validation model
321
+ module SurveyorParserValidationMethods
322
+ def parse_and_build(context, args, original_method, reference_identifier)
278
323
  # clear context
324
+ [ :validation,
325
+ :validation_condition ].each{|k| context.delete k}
326
+
279
327
  context.delete_if{|k,v| %w(validation validation_condition).map(&:to_sym).include? k}
280
328
 
281
329
  # build and set context
282
- context[:validation] = context[:answer].validations.build({:rule => "A"}.merge(args[0] || {}))
330
+ self.attributes = ({:rule => "A"}.merge(args[0] || {}))
331
+ context[:answer].validations << context[:validation] = self
283
332
  end
284
333
  end
285
- class ValidationCondition < ActiveRecord::Base
286
- # nonblock
287
-
288
- def self.parse_and_build(context, args, original_method, reference_identifier)
334
+
335
+ # ValidationCondition model
336
+ module SurveyorParserValidationConditionMethods
337
+ def parse_and_build(context, args, original_method, reference_identifier)
289
338
  # clear context
290
- context.delete_if{|k,v| k == :validation_condition}
339
+ context.delete :validation_condition
291
340
 
292
341
  # build and set context
293
342
  a0, a1 = args
294
- context[:validation_condition] = context[:validation].validation_conditions.build({
295
- :operator => a0 || "==",
296
- :rule_key => reference_identifier}.merge(a1 || {}))
343
+ self.attributes = ({
344
+ :operator => a0 || "==",
345
+ :rule_key => reference_identifier}.merge(a1 || {}))
346
+ context[:validation].validation_conditions << context[:validation_condition] = self
297
347
  end
298
348
  end