breakpointer-surveyor 0.2.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 (198) hide show
  1. data/.gitignore +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +20 -0
  4. data/Rakefile +57 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/answers_controller.rb +87 -0
  7. data/app/controllers/dependencies_controller.rb +87 -0
  8. data/app/controllers/dependency_conditions_controller.rb +87 -0
  9. data/app/controllers/questions_controller.rb +85 -0
  10. data/app/controllers/sections_controller.rb +87 -0
  11. data/app/controllers/surveying_controller.rb +139 -0
  12. data/app/controllers/surveys_controller.rb +87 -0
  13. data/app/helpers/answers_helper.rb +2 -0
  14. data/app/helpers/application_helper.rb +3 -0
  15. data/app/helpers/questions_helper.rb +2 -0
  16. data/app/helpers/sections_helper.rb +2 -0
  17. data/app/helpers/survey_form_builder.rb +38 -0
  18. data/app/helpers/survey_importer_helper.rb +2 -0
  19. data/app/helpers/surveying_helper.rb +91 -0
  20. data/app/helpers/surveys_helper.rb +2 -0
  21. data/app/models/answer.rb +18 -0
  22. data/app/models/dependency.rb +43 -0
  23. data/app/models/dependency_condition.rb +76 -0
  24. data/app/models/question.rb +44 -0
  25. data/app/models/question_group.rb +5 -0
  26. data/app/models/response.rb +58 -0
  27. data/app/models/response_set.rb +174 -0
  28. data/app/models/survey.rb +54 -0
  29. data/app/models/survey_section.rb +19 -0
  30. data/app/models/user.rb +5 -0
  31. data/app/views/answer_display_types/_any_answer.html.haml +3 -0
  32. data/app/views/answer_display_types/_any_other_and_string.html.haml +5 -0
  33. data/app/views/answer_display_types/_any_string.html.haml +4 -0
  34. data/app/views/answer_display_types/_date.html.haml +3 -0
  35. data/app/views/answer_display_types/_datetime.html.haml +2 -0
  36. data/app/views/answer_display_types/_default.html.haml +1 -0
  37. data/app/views/answer_display_types/_float.html.haml +5 -0
  38. data/app/views/answer_display_types/_grid_any_answer.html.haml +2 -0
  39. data/app/views/answer_display_types/_grid_default.html.haml +2 -0
  40. data/app/views/answer_display_types/_grid_float.html.haml +2 -0
  41. data/app/views/answer_display_types/_grid_integer.html.haml +2 -0
  42. data/app/views/answer_display_types/_grid_one_answer.html.haml +2 -0
  43. data/app/views/answer_display_types/_grid_string.html.haml +2 -0
  44. data/app/views/answer_display_types/_integer.html.haml +6 -0
  45. data/app/views/answer_display_types/_one_answer.html.haml +3 -0
  46. data/app/views/answer_display_types/_one_string.html.haml +6 -0
  47. data/app/views/answer_display_types/_repeater_integer.html.haml +6 -0
  48. data/app/views/answer_display_types/_repeater_string.html.haml +7 -0
  49. data/app/views/answer_display_types/_string.html.haml +7 -0
  50. data/app/views/answer_display_types/_text.html.haml +6 -0
  51. data/app/views/answer_display_types/_time.html.haml +2 -0
  52. data/app/views/layouts/surveys.html.erb +17 -0
  53. data/app/views/question_display_types/_default.html.haml +12 -0
  54. data/app/views/question_display_types/_dropdown.html.haml +12 -0
  55. data/app/views/question_display_types/_grid_default.html.haml +14 -0
  56. data/app/views/question_display_types/_grid_dropdown.html.haml +15 -0
  57. data/app/views/question_display_types/_group_default.html.haml +8 -0
  58. data/app/views/question_display_types/_group_dropdown.html.haml +6 -0
  59. data/app/views/question_display_types/_image.html.haml +2 -0
  60. data/app/views/question_display_types/_inline.html.haml +12 -0
  61. data/app/views/question_display_types/_label.html.haml +4 -0
  62. data/app/views/question_display_types/_repeater_default.html.haml +10 -0
  63. data/app/views/question_display_types/_repeater_dropdown.html.haml +8 -0
  64. data/app/views/question_display_types/_slider.html.haml +17 -0
  65. data/app/views/question_group_display_types/_default.html.haml +13 -0
  66. data/app/views/question_group_display_types/_grid.html.haml +24 -0
  67. data/app/views/question_group_display_types/_repeater.html.haml +20 -0
  68. data/app/views/surveying/edit.html.haml +38 -0
  69. data/app/views/surveying/finish.html.haml +1 -0
  70. data/app/views/surveying/index.html.erb +19 -0
  71. data/app/views/surveying/new.html.haml +17 -0
  72. data/app/views/surveying/show.html.haml +8 -0
  73. data/config/routes.rb +10 -0
  74. data/generators/surveyor/surveyor_generator.rb +38 -0
  75. data/generators/surveyor/templates/README +9 -0
  76. data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_leftright.gif +0 -0
  77. data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_updown.gif +0 -0
  78. data/generators/surveyor/templates/assets/images/222222_11x11_icon_close.gif +0 -0
  79. data/generators/surveyor/templates/assets/images/222222_11x11_icon_doc.gif +0 -0
  80. data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_closed.gif +0 -0
  81. data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_open.gif +0 -0
  82. data/generators/surveyor/templates/assets/images/222222_11x11_icon_minus.gif +0 -0
  83. data/generators/surveyor/templates/assets/images/222222_11x11_icon_plus.gif +0 -0
  84. data/generators/surveyor/templates/assets/images/222222_11x11_icon_resize_se.gif +0 -0
  85. data/generators/surveyor/templates/assets/images/222222_35x9_colorpicker_indicator.gif.gif +0 -0
  86. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_down.gif +0 -0
  87. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_left.gif +0 -0
  88. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_right.gif +0 -0
  89. data/generators/surveyor/templates/assets/images/222222_7x7_arrow_up.gif +0 -0
  90. data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_leftright.gif +0 -0
  91. data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_updown.gif +0 -0
  92. data/generators/surveyor/templates/assets/images/454545_11x11_icon_close.gif +0 -0
  93. data/generators/surveyor/templates/assets/images/454545_11x11_icon_doc.gif +0 -0
  94. data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_closed.gif +0 -0
  95. data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_open.gif +0 -0
  96. data/generators/surveyor/templates/assets/images/454545_11x11_icon_minus.gif +0 -0
  97. data/generators/surveyor/templates/assets/images/454545_11x11_icon_plus.gif +0 -0
  98. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_down.gif +0 -0
  99. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_left.gif +0 -0
  100. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_right.gif +0 -0
  101. data/generators/surveyor/templates/assets/images/454545_7x7_arrow_up.gif +0 -0
  102. data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_leftright.gif +0 -0
  103. data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_updown.gif +0 -0
  104. data/generators/surveyor/templates/assets/images/888888_11x11_icon_close.gif +0 -0
  105. data/generators/surveyor/templates/assets/images/888888_11x11_icon_doc.gif +0 -0
  106. data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_closed.gif +0 -0
  107. data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_open.gif +0 -0
  108. data/generators/surveyor/templates/assets/images/888888_11x11_icon_minus.gif +0 -0
  109. data/generators/surveyor/templates/assets/images/888888_11x11_icon_plus.gif +0 -0
  110. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_down.gif +0 -0
  111. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_left.gif +0 -0
  112. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_right.gif +0 -0
  113. data/generators/surveyor/templates/assets/images/888888_7x7_arrow_up.gif +0 -0
  114. data/generators/surveyor/templates/assets/images/dadada_40x100_textures_02_glass_75.png +0 -0
  115. data/generators/surveyor/templates/assets/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
  116. data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_01_flat_0.png +0 -0
  117. data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_02_glass_65.png +0 -0
  118. data/generators/surveyor/templates/assets/javascripts/accessibleUISlider.jQuery.js +201 -0
  119. data/generators/surveyor/templates/assets/javascripts/jquery-1.2.6.js +3549 -0
  120. data/generators/surveyor/templates/assets/javascripts/jquery-ui-personalized-1.5.3.js +7616 -0
  121. data/generators/surveyor/templates/assets/javascripts/jquery.form.js +637 -0
  122. data/generators/surveyor/templates/assets/javascripts/surveyor.js +35 -0
  123. data/generators/surveyor/templates/assets/stylesheets/jquery-ui-slider-additions.css +71 -0
  124. data/generators/surveyor/templates/assets/stylesheets/reset.css +46 -0
  125. data/generators/surveyor/templates/assets/stylesheets/sass/surveyor.sass +321 -0
  126. data/generators/surveyor/templates/assets/stylesheets/surveyor.css +245 -0
  127. data/generators/surveyor/templates/assets/stylesheets/ui.theme.css +851 -0
  128. data/generators/surveyor/templates/migrate/create_answers.rb +46 -0
  129. data/generators/surveyor/templates/migrate/create_dependencies.rb +21 -0
  130. data/generators/surveyor/templates/migrate/create_dependency_conditions.rb +29 -0
  131. data/generators/surveyor/templates/migrate/create_question_groups.rb +18 -0
  132. data/generators/surveyor/templates/migrate/create_questions.rb +33 -0
  133. data/generators/surveyor/templates/migrate/create_response_sets.rb +22 -0
  134. data/generators/surveyor/templates/migrate/create_responses.rb +33 -0
  135. data/generators/surveyor/templates/migrate/create_survey_sections.rb +25 -0
  136. data/generators/surveyor/templates/migrate/create_surveys.rb +25 -0
  137. data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +201 -0
  138. data/init.rb +1 -0
  139. data/install.rb +1 -0
  140. data/lib/tasks/surveyor_tasks.rake +29 -0
  141. data/lib/tiny_code.rb +58 -0
  142. data/lib/user_manager.rb +9 -0
  143. data/lib/xml_formatter.rb +12 -0
  144. data/script/surveyor/answer.rb +84 -0
  145. data/script/surveyor/columnizer.rb +36 -0
  146. data/script/surveyor/dependency.rb +43 -0
  147. data/script/surveyor/dependency_condition.rb +74 -0
  148. data/script/surveyor/dslparse.rb +66 -0
  149. data/script/surveyor/question.rb +76 -0
  150. data/script/surveyor/question_group.rb +33 -0
  151. data/script/surveyor/specs/answer_spec.rb +66 -0
  152. data/script/surveyor/specs/question_dependency_spec.rb +46 -0
  153. data/script/surveyor/specs/question_group_spec.rb +9 -0
  154. data/script/surveyor/specs/question_spec.rb +111 -0
  155. data/script/surveyor/specs/section_spec.rb +58 -0
  156. data/script/surveyor/survey.rb +108 -0
  157. data/script/surveyor/survey_section.rb +153 -0
  158. data/script/surveyor/whr_dsl.tmproj +244 -0
  159. data/spec/controllers/answers_controller_spec.rb +64 -0
  160. data/spec/controllers/dependencies_controller_spec.rb +63 -0
  161. data/spec/controllers/dependency_conditions_controller_spec.rb +64 -0
  162. data/spec/controllers/questions_controller_spec.rb +64 -0
  163. data/spec/controllers/sections_controller_spec.rb +64 -0
  164. data/spec/controllers/surveying_controller_spec.rb +328 -0
  165. data/spec/controllers/surveying_routing_spec.rb +45 -0
  166. data/spec/controllers/surveys_controller_spec.rb +64 -0
  167. data/spec/fixtures/answers.yml +9 -0
  168. data/spec/fixtures/dependencies.yml +7 -0
  169. data/spec/fixtures/dependency_conditions.yml +27 -0
  170. data/spec/fixtures/question_groups.yml +7 -0
  171. data/spec/fixtures/questions.yml +15 -0
  172. data/spec/fixtures/response_sets.yml +13 -0
  173. data/spec/fixtures/responses.yml +9 -0
  174. data/spec/fixtures/survey_sections.yml +11 -0
  175. data/spec/fixtures/surveys.yml +9 -0
  176. data/spec/fixtures/users.yml +7 -0
  177. data/spec/helpers/survey_importer_helper_spec.rb +11 -0
  178. data/spec/helpers/surveying_helper_spec.rb +11 -0
  179. data/spec/models/answer_spec.rb +62 -0
  180. data/spec/models/dependency_condition_spec.rb +347 -0
  181. data/spec/models/dependency_spec.rb +82 -0
  182. data/spec/models/question_group_spec.rb +11 -0
  183. data/spec/models/question_spec.rb +70 -0
  184. data/spec/models/response_set_spec.rb +172 -0
  185. data/spec/models/response_spec.rb +94 -0
  186. data/spec/models/survey_section_spec.rb +34 -0
  187. data/spec/models/survey_spec.rb +72 -0
  188. data/spec/models/user_spec.rb +11 -0
  189. data/spec/rcov.opts +2 -0
  190. data/spec/spec.opts +4 -0
  191. data/spec/spec_helper.rb +72 -0
  192. data/spec/views/app/edit.html.erb_spec.rb +20 -0
  193. data/spec/views/app/index.html.erb_spec.rb +19 -0
  194. data/spec/views/app/new.html.erb_spec.rb +21 -0
  195. data/spec/views/app/show.html.erb_spec.rb +17 -0
  196. data/surveyor.gemspec +258 -0
  197. data/uninstall.rb +1 -0
  198. metadata +286 -0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ # Init hook code here
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), "../../script/surveyor/dslparse")
2
+ require 'active_record/fixtures'
3
+
4
+ namespace :surveyor do
5
+
6
+ desc "generate and load survey fixtures from survey file (shortcut for rake surveyor:generate and rake surveyor:load)"
7
+ task :bootstrap => [:generate, :load]
8
+
9
+ desc "generate survey fixtures from survey file"
10
+ task :generate => :environment do
11
+ raise "usage: file name required e.g. 'FILE=surveys/kitchen_sink_survey.rb'" if ENV["FILE"].blank?
12
+ DSLParse.parse(File.join(RAILS_ROOT, ENV["FILE"]))
13
+ end
14
+
15
+ desc "load survey fixtures"
16
+ task :load => :environment do
17
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
18
+
19
+ fixture_dir = File.join(RAILS_ROOT, "surveys", "fixtures")
20
+ fixtures = Dir.glob("#{fixture_dir}/*.yml")
21
+ raise "No fixtures found." if fixtures.empty?
22
+
23
+ fixtures.each do |file_name|
24
+ puts "Loading #{file_name}..."
25
+ Fixtures.create_fixtures(File.join(fixture_dir, file_name))
26
+ end
27
+ end
28
+
29
+ end
data/lib/tiny_code.rb ADDED
@@ -0,0 +1,58 @@
1
+ module TinyCode
2
+
3
+ module ClassMethods
4
+
5
+ RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "abcdefghijklmnopqrstuvwxyz"
6
+
7
+ def make_tiny_code
8
+ random_string(10)
9
+ end
10
+
11
+ def random_string(len)
12
+ rand_max = RAND_CHARS.size
13
+ ret = ""
14
+ len.times{ ret << RAND_CHARS[rand(rand_max)] }
15
+ ret
16
+ end
17
+
18
+
19
+ def self.to_normalized_string(text)
20
+ words_to_omit = ["a", "to", "the", "of", "has", "have", "it", "is", "in", "on", "or", "but", "when", "be"]
21
+
22
+ # strip all the html tags from the text data
23
+ col_text = text.gsub(/(<[^>]*>)|\n|\t/s, ' ')
24
+
25
+ # Removing capitalization
26
+ col_text.downcase!
27
+ # Removing potential problem characters
28
+ col_text.gsub!(/\"|\'/, '')
29
+ # Removing text inside parens
30
+ col_text.gsub!(/\(.*?\)/,'')
31
+
32
+ # Removing all other non-word characters
33
+ col_text.gsub!(/\W/, ' ')
34
+
35
+ column_words = []
36
+ words = col_text.split(' ')
37
+ words.each do |word|
38
+ if !words_to_omit.include?(word)
39
+ column_words << word
40
+ end
41
+ end
42
+
43
+ #reducing the word list to limit column length
44
+ if column_words.length > 5
45
+ column_words.slice!(0, column_words.length - 5)
46
+ end
47
+
48
+ #re-assemble the string
49
+ column_words.join("_")
50
+ end
51
+
52
+ end
53
+
54
+ def self.included(klass)
55
+ klass.extend(ClassMethods)
56
+ end
57
+
58
+ end
@@ -0,0 +1,9 @@
1
+ module UserManager
2
+
3
+ def current_user
4
+ @current_user= User.new(:login => "foobaz")
5
+ @current_user.id = 123
6
+ @current_user
7
+ end
8
+
9
+ end
@@ -0,0 +1,12 @@
1
+ module XmlFormatter
2
+ def to_xml(options = {})
3
+ options[:indent] ||= 2
4
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
5
+ xml.instruct! unless options[:skip_instruct]
6
+ xml.tag!(self.class.name.downcase.to_sym, self.attributes) do
7
+ self.class.reflect_on_all_associations.to_a.each do |assoc|
8
+ xml.tag!(assoc.name)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/columnizer'
2
+
3
+ class Answer
4
+ include Columnizer
5
+
6
+ # Context, Content, Display, Reference
7
+ attr_accessor :id, :question_id, :parser
8
+ attr_accessor :text, :short_text, :help_text, :numerical_value
9
+ attr_accessor :response_class, :display_order, :is_exclusive, :is_a_disabler, :hide_label
10
+ attr_accessor :reference_identifier, :data_export_identifier
11
+
12
+ def initialize(question, args, options)
13
+ self.parser = question ? question.parser : nil
14
+ self.id = parser ? parser.new_answer_id : nil
15
+ self.question_id = question ? question.id : nil
16
+
17
+ #self.text is set in args
18
+ args_options = parse_args_options(args)
19
+ self.default_options(args_options[:text]).merge(options).merge(args_options).each{|key,value| self.instance_variable_set("@#{key}", value)}
20
+ end
21
+
22
+ def default_options(text)
23
+ { :short_text => text,
24
+ :data_export_identifier => Columnizer.to_normalized_column(text),
25
+ :is_exclusive => false,
26
+ :is_a_disabler => false,
27
+ :hide_label => false,
28
+ :response_class => "answer"
29
+ }
30
+ end
31
+
32
+ def parse_args_options(args)
33
+ a0, a1, a2 = args
34
+
35
+ # Hash
36
+ if a0.is_a?(Hash)
37
+ {:text => "Answer"}.merge(a0)
38
+
39
+ # String, Hash || String, Symbol, Hash
40
+ elsif a0.is_a?(String)
41
+ a1.is_a?(Symbol) ? {:text => a0, :response_class => a1.to_s}.merge(a2 || {}) : {:text => a0}.merge(a1 || {})
42
+
43
+ # Symbol, Hash || Symbol, Symbol, Hash
44
+ elsif a0.is_a?(Symbol)
45
+ shortcuts = case a0
46
+ when :other
47
+ {:text => "Other"}
48
+ when :other_and_string
49
+ {:text => "Other", :response_class => "string"}
50
+ when :none, :omit #a disabler erases and disables all other answer options (text, checkbox, dropdowns, etc). Unchecking the omit box is the only way to enable other options (except in the case limit => :one)
51
+ {:text => a0.to_s.humanize, :is_exclusive => true} # "omit" is no longer a response class... it's treated as any other answer type
52
+ when :integer, :date, :time, :datetime, :text, :datetime, :string
53
+ {:text => a0.to_s.humanize, :response_class => a0.to_s, :hide_label => true}
54
+ else
55
+ {:text => a0.to_s.humanize}
56
+ end
57
+ a1.is_a?(Symbol) ? shortcuts.merge({:response_class => a1.to_s}).merge(a2 || {}) : shortcuts.merge(a1 || {})
58
+ else
59
+ {:text => "Answer"}
60
+ end
61
+ end
62
+
63
+ def to_yml
64
+ out =[ %(#{@data_export_identifier}_#{@id}:) ]
65
+ out << %( id: #{@id})
66
+ out << %( question_id: #{@question_id})
67
+ out << %( text: "#{@text}")
68
+ out << %( short_text: "#{@short_text}")
69
+ out << %( help_text: "#{@help_text}")
70
+ out << %( weight: #{@weight})
71
+ out << %( response_class: "#{@response_class}")
72
+ out << %( display_order: #{display_order} )
73
+ out << %( is_exclusive: #{@is_exclusive})
74
+ out << %( hide_label: #{@hide_label})
75
+ out << %( reference_identifier: #{@reference_identifier})
76
+ out << %( data_export_identifier: "#{@data_export_identifier}")
77
+ (out << nil ).join("\r\n")
78
+ end
79
+
80
+ def to_file
81
+ File.open(self.parser.answers_yml, File::CREAT|File::APPEND|File::WRONLY){ |f| f << to_yml }
82
+ end
83
+
84
+ end
@@ -0,0 +1,36 @@
1
+ module Columnizer
2
+
3
+ def self.to_normalized_column(text)
4
+ words_to_omit = ["a", "to", "the", "of", "has", "have", "it", "is", "in", "on", "or", "but", "when", "be"]
5
+
6
+ # strip all the html tags from the text data
7
+ col_text = text.gsub(/(<[^>]*>)|\n|\t/s, ' ')
8
+
9
+ # Removing capitalization
10
+ col_text.downcase!
11
+ # Removing potential problem characters
12
+ col_text.gsub!(/\"|\'/, '')
13
+ # Removing text inside parens
14
+ col_text.gsub!(/\(.*?\)/,'')
15
+
16
+ # Removing all other non-word characters
17
+ col_text.gsub!(/\W/, ' ')
18
+
19
+ column_words = []
20
+ words = col_text.split(' ')
21
+ words.each do |word|
22
+ if !words_to_omit.include?(word)
23
+ column_words << word
24
+ end
25
+ end
26
+
27
+ #reducing the word list to limit column length
28
+ if column_words.length > 5
29
+ column_words.slice!(0, column_words.length - 5)
30
+ end
31
+
32
+ #re-assemble the string
33
+ column_words.join("_")
34
+ end
35
+
36
+ end
@@ -0,0 +1,43 @@
1
+ class Dependency
2
+
3
+ # Context, Conditional, Children
4
+ attr_accessor :id, :question_id, :parser
5
+ attr_accessor :rule
6
+ attr_accessor :dependency_conditions
7
+
8
+ # id, question, and rule required
9
+ def initialize(question, args, options)
10
+ self.parser = question.parser
11
+ self.id = parser.new_dependency_id
12
+ self.question_id = question.id
13
+ self.rule = (args[0] || {})[:rule]
14
+ self.dependency_conditions = []
15
+ self.default_options().merge(options).merge(args[1] || {}).each{|key,value| self.instance_variable_set("@#{key}", value)}
16
+ end
17
+
18
+ def default_options()
19
+ {}
20
+ end
21
+
22
+ # Injecting the id of the current dependency object into the dependency_condition on assignment
23
+ def add_dependency_condition(dc_obj)
24
+ dc_obj.dependency_id = self.id
25
+ self.dependency_conditions << dc_obj
26
+ end
27
+
28
+ def to_yml
29
+ out =[ %(#{question_id}_#{@id}:) ]
30
+ out << %( id: #{@id})
31
+ out << %( question_id: #{@question_id})
32
+ out << %( rule: "#{@rule}")
33
+ (out << nil ).join("\r\n")
34
+ end
35
+
36
+ def to_file
37
+ File.open(self.parser.dependencies_yml, File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
38
+ self.dependency_conditions.compact.map(&:to_file)
39
+ end
40
+
41
+
42
+
43
+ end
@@ -0,0 +1,74 @@
1
+ class DependencyCondition
2
+
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
8
+
9
+ # id, dependency, and question_id required
10
+ def initialize(dependency, args, options)
11
+ self.parser = dependency.parser
12
+ self.id = parser.new_dependency_condition_id
13
+ self.dependency_id = dependency.id
14
+ self.rule_key = options[:reference_identifier]
15
+
16
+ args_options = parse_args_options(args)
17
+ self.default_options().merge(options).merge(args_options).each{|key,value| self.instance_variable_set("@#{key}", value)}
18
+
19
+ end
20
+
21
+ def default_options()
22
+ { :operator => "==" }
23
+ end
24
+
25
+ def parse_args_options(args)
26
+ a0, a1, a2 = args
27
+ options = {:question_reference => a0.to_s.gsub("q_", ""), :operator => a1}
28
+ a2.is_a?(Hash) ? options.merge(a2) : options.merge({:answer_reference => a2.to_s.gsub("a_", "")})
29
+ end
30
+
31
+ def to_yml
32
+ out =[ %(#{@question_id}_#{@dependency_id}_#{id}:) ]
33
+ out << %( id: #{@id})
34
+ out << %( dependency_id: #{@dependency_id})
35
+ out << %( rule_key: "#{@rule_key}")
36
+ out << %( question_id: #{@question_id})
37
+ out << %( operator: "#{@operator}")
38
+ out << %( answer_id: #{@answer_id})
39
+ out << %( datetime_value: #{@date_value})
40
+ out << %( integer_value: #{@integer_value})
41
+ out << %( float_value: #{@float_value})
42
+ out << %( unit: "#{@unit}")
43
+ out << %( text_value: "#{@text_value}")
44
+ out << %( string_value: "#{@string_value}")
45
+ out << %( response_other: "#{@response_other}")
46
+ (out << nil ).join("\r\n")
47
+ end
48
+
49
+ def reconcile_dependencies
50
+ # Looking up references to questions and answers for linking the dependency objects
51
+ puts "Looking up references for question: #{@question_reference} in Survey: #{Survey.current_survey.title}"
52
+ ref_question = Survey.current_survey.find_question_by_reference(@question_reference) # Argh. I can't think of a better way to get a hold of this reference here...
53
+ if ref_question
54
+ @question_id = ref_question.id
55
+ ref_answer = ref_question.find_answer_by_reference(@answer_reference)
56
+ if ref_answer
57
+ @answer_id = ref_answer.id
58
+ else
59
+ raise "Could not find referenced answer #{@answer_reference}"
60
+ end
61
+
62
+ else
63
+ raise "Could not find referenced question #{@question_reference}"
64
+ end
65
+ end
66
+
67
+ def to_file
68
+ # Reconciling the references used in the dsl to actual object ids
69
+ #puts "Reconcile of dependency:"
70
+ # reconcile_dependencies
71
+ File.open(self.parser.dependency_conditions_yml, File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
72
+ end
73
+
74
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/survey'
2
+
3
+ class DSLParse
4
+ # Children, Counters, Files
5
+ attr_accessor :surveys
6
+ attr_accessor :last_survey_id, :last_survey_section_id, :last_question_id, :last_answer_id
7
+ attr_accessor :surveys_yml, :survey_sections_yml, :question_groups_yml, :questions_yml, :answers_yml, :dependencies_yml, :dependency_conditions_yml
8
+
9
+ # no more "ARRRGH, EVIL GLOBALS!!!"
10
+ def initialize
11
+ self.surveys = []
12
+ self.define_counter_methods(%w(survey survey_section question_group question answer dependency dependency_condition))
13
+ self.initialize_fixtures(%w(surveys survey_sections question_groups questions answers dependencies dependency_conditions), File.join(RAILS_ROOT, "surveys", "fixtures"))
14
+ end
15
+
16
+ # new_survey_id, new_survey_section_id, etc.
17
+ def define_counter_methods(names)
18
+ names.each do |name|
19
+ self.instance_variable_set("@last_#{name}_id", 0)
20
+ # self.class.send is a hack - define_method is private
21
+ self.class.send(:define_method, "new_#{name}_id") do
22
+ self.instance_variable_set("@last_#{name}_id", self.instance_variable_get("@last_#{name}_id") + 1)
23
+ end
24
+ end
25
+ end
26
+
27
+ # surveys_yml, survey_sections_yml, etc.
28
+ def initialize_fixtures(names, path)
29
+ names.each do |name|
30
+ file = self.instance_variable_set("@#{name}_yml", "#{path}/#{name}.yml")
31
+ File.truncate(file, 0) if File.exist?(file)
32
+ end
33
+ end
34
+
35
+ def self.parse(file_name)
36
+ puts "--- Parsing '#{file_name}' ---"
37
+ parser = DSLParse.new
38
+ parser.instance_eval(File.read(file_name), file_name)
39
+ parser.to_files
40
+ puts "--- End of parsing ---"
41
+ end
42
+
43
+ def survey(title, &block)
44
+ puts "Survey: #{title}"
45
+ if block_given?
46
+ new_survey = Survey.new(self.new_survey_id, self, title)
47
+ new_survey.instance_eval(&block)
48
+ new_survey.reconcile_dependencies
49
+ add_survey(new_survey)
50
+ puts "Survey added"
51
+ else
52
+ puts "ERROR: A survey cannot be empty!"
53
+ end
54
+ end
55
+
56
+
57
+
58
+ def add_survey(survey)
59
+ self.surveys << survey
60
+ end
61
+
62
+ def to_files
63
+ self.surveys.compact.map(&:to_file)
64
+ end
65
+
66
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/columnizer'
2
+
3
+ class Question
4
+ include Columnizer
5
+
6
+ # Context, Content, Display, Reference, Children
7
+ attr_accessor :id, :survey_section_id, :parser
8
+ attr_accessor :text, :short_text, :help_text
9
+ attr_accessor :pick, :display_type, :display_order, :question_group_id, :is_mandatory
10
+ attr_accessor :reference_identifier, :data_export_identifier
11
+ attr_accessor :answers, :dependency
12
+
13
+ def initialize(section, args, options)
14
+ self.parser = section.parser
15
+ self.id = parser.new_question_id
16
+ self.display_order = self.id
17
+ self.survey_section_id = section.id
18
+ self.text = args[0]
19
+ self.answers = []
20
+ self.dependency = nil
21
+ self.default_options(text).merge(options).merge(args[1] || {}).each{|key,value| self.instance_variable_set("@#{key}", value)}
22
+ end
23
+
24
+ def default_options(text)
25
+ { :short_text => text,
26
+ :pick => :none,
27
+ :display_type => :default,
28
+ :is_mandatory => true,
29
+ :data_export_identifier => Columnizer.to_normalized_column(text)
30
+ }
31
+ end
32
+
33
+ # Injecting the id of this question object into the dependency object on assignment
34
+ def dependency=(dep)
35
+ unless dep.nil?
36
+ dep.question_id = self.id
37
+ end
38
+ @dependency = dep
39
+ end
40
+
41
+ def find_answer_by_reference(ref_id)
42
+ answer = nil
43
+ count = 0
44
+ puts "Looking up answer with ref: #{ref_id}"
45
+ while answer.nil? and count < self.answers.size
46
+ answer = self.answers[count] if self.answers[count].reference_identifier == ref_id
47
+ count += 1
48
+ end
49
+ puts " found answer: '#{answer.text}' (id:#{answer.id})" unless answer.nil?
50
+ answer
51
+ end
52
+
53
+ def to_yml
54
+ out =[ %(#{@data_export_identifier}_#{@id}:) ]
55
+ out << %( id: #{@id})
56
+ out << %( survey_section_id: #{@survey_section_id})
57
+ out << %( text: "#{@text}")
58
+ out << %( short_text: "#{@short_text}")
59
+ out << %( help_text: "#{@help_text}")
60
+ out << %( pick: #{pick})
61
+ out << %( display_type: #{display_type})
62
+ out << %( display_order: #{display_order})
63
+ out << %( question_group_id: #{question_group_id})
64
+ out << %( is_mandatory: #{@is_mandatory})
65
+ out << %( reference_identifier: #{@reference_identifier})
66
+ out << %( data_export_identifier: "#{@data_export_identifier}")
67
+ (out << nil ).join("\r\n")
68
+ end
69
+
70
+ def to_file
71
+ File.open(self.parser.questions_yml, File::CREAT|File::APPEND|File::WRONLY){ |f| f << to_yml }
72
+ self.answers.compact.map(&:to_file)
73
+ if self.dependency then self.dependency.to_file end
74
+ end
75
+
76
+ end