cornucopia 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +16 -0
  6. data/LICENSE.TXT +22 -0
  7. data/README.md +341 -0
  8. data/Rakefile +18 -0
  9. data/cornucopia.gemspec +39 -0
  10. data/lib/cornucopia.rb +18 -0
  11. data/lib/cornucopia/capybara/finder_diagnostics.rb +536 -0
  12. data/lib/cornucopia/capybara/finder_extensions.rb +89 -0
  13. data/lib/cornucopia/capybara/install_finder_extensions.rb +105 -0
  14. data/lib/cornucopia/capybara/install_matcher_extensions.rb +39 -0
  15. data/lib/cornucopia/capybara/matcher_extensions.rb +83 -0
  16. data/lib/cornucopia/capybara/page_diagnostics.rb +228 -0
  17. data/lib/cornucopia/cucumber_hooks.rb +38 -0
  18. data/lib/cornucopia/factory_girl/dynamic_association.rb +14 -0
  19. data/lib/cornucopia/rspec_hooks.rb +37 -0
  20. data/lib/cornucopia/site_prism/element_extensions.rb +273 -0
  21. data/lib/cornucopia/site_prism/install_element_extensions.rb +23 -0
  22. data/lib/cornucopia/site_prism/page_application.rb +126 -0
  23. data/lib/cornucopia/source_files/collapse.gif +0 -0
  24. data/lib/cornucopia/source_files/cornucopia.css +162 -0
  25. data/lib/cornucopia/source_files/expand.gif +0 -0
  26. data/lib/cornucopia/source_files/index_base.html +10 -0
  27. data/lib/cornucopia/source_files/index_contents.html +2 -0
  28. data/lib/cornucopia/source_files/more_info.js +87 -0
  29. data/lib/cornucopia/source_files/report_base.html +10 -0
  30. data/lib/cornucopia/source_files/report_contents.html +3 -0
  31. data/lib/cornucopia/spinach_hooks.rb +51 -0
  32. data/lib/cornucopia/util/configuration.rb +493 -0
  33. data/lib/cornucopia/util/configured_report.rb +520 -0
  34. data/lib/cornucopia/util/file_asset.rb +46 -0
  35. data/lib/cornucopia/util/generic_settings.rb +37 -0
  36. data/lib/cornucopia/util/log_capture.rb +97 -0
  37. data/lib/cornucopia/util/pretty_formatter.rb +580 -0
  38. data/lib/cornucopia/util/report_builder.rb +474 -0
  39. data/lib/cornucopia/util/report_formatters.rb +11 -0
  40. data/lib/cornucopia/util/report_table.rb +195 -0
  41. data/lib/cornucopia/version.rb +3 -0
  42. data/lib/tasks/cornucopia_tasks.rake +4 -0
  43. data/spec/dummy/README.rdoc +28 -0
  44. data/spec/dummy/Rakefile +6 -0
  45. data/spec/dummy/app/assets/images/.keep +0 -0
  46. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  47. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  49. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/mailers/.keep +0 -0
  52. data/spec/dummy/app/models/.keep +0 -0
  53. data/spec/dummy/app/models/concerns/.keep +0 -0
  54. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  55. data/spec/dummy/bin/bundle +3 -0
  56. data/spec/dummy/bin/rails +4 -0
  57. data/spec/dummy/bin/rake +4 -0
  58. data/spec/dummy/config.ru +4 -0
  59. data/spec/dummy/config/application.rb +27 -0
  60. data/spec/dummy/config/boot.rb +5 -0
  61. data/spec/dummy/config/cucumber.yml +8 -0
  62. data/spec/dummy/config/database.yml +37 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +29 -0
  65. data/spec/dummy/config/environments/production.rb +80 -0
  66. data/spec/dummy/config/environments/test.rb +36 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  69. data/spec/dummy/config/initializers/inflections.rb +16 -0
  70. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  71. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  72. data/spec/dummy/config/initializers/session_store.rb +3 -0
  73. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/spec/dummy/config/locales/en.yml +23 -0
  75. data/spec/dummy/config/routes.rb +56 -0
  76. data/spec/dummy/db/schema.rb +16 -0
  77. data/spec/dummy/features/support/env.rb +66 -0
  78. data/spec/dummy/lib/assets/.keep +0 -0
  79. data/spec/dummy/lib/tasks/cucumber.rake +65 -0
  80. data/spec/dummy/public/404.html +58 -0
  81. data/spec/dummy/public/422.html +58 -0
  82. data/spec/dummy/public/500.html +57 -0
  83. data/spec/dummy/public/favicon.ico +0 -0
  84. data/spec/dummy/script/cucumber +10 -0
  85. data/spec/fixtures/sample_page.html +150 -0
  86. data/spec/lib/capybara/finder_diagnostics_spec.rb +517 -0
  87. data/spec/lib/capybara/finder_extensions_spec.rb +328 -0
  88. data/spec/lib/capybara/page_diagnostics_spec.rb +277 -0
  89. data/spec/lib/site_prism/element_extensions_spec.rb +290 -0
  90. data/spec/lib/site_prism/page_application_spec.rb +81 -0
  91. data/spec/lib/util/configuration_spec.rb +254 -0
  92. data/spec/lib/util/configured_report_spec.rb +1058 -0
  93. data/spec/lib/util/file_asset_spec.rb +86 -0
  94. data/spec/lib/util/generic_settings_spec.rb +48 -0
  95. data/spec/lib/util/log_capture_spec.rb +151 -0
  96. data/spec/lib/util/pretty_formatter_spec.rb +694 -0
  97. data/spec/lib/util/report_builder_spec.rb +983 -0
  98. data/spec/lib/util/report_formatters_spec.rb +13 -0
  99. data/spec/lib/util/report_table_exception_spec.rb +21 -0
  100. data/spec/lib/util/report_table_spec.rb +319 -0
  101. data/spec/pages/cornucopia_report_app.rb +10 -0
  102. data/spec/pages/google/email_page.rb +22 -0
  103. data/spec/pages/google/login_page.rb +25 -0
  104. data/spec/rails_helper.rb +43 -0
  105. data/spec/sample_report.rb +45 -0
  106. data/spec/spec_helper.rb +81 -0
  107. metadata +410 -0
@@ -0,0 +1,46 @@
1
+ module Cornucopia
2
+ module Util
3
+ class FileAsset
4
+ class << self
5
+ def asset(asset_name)
6
+ @@asset_list ||= {}
7
+ @@asset_list[asset_name.to_sym] = FileAsset.new(asset_name) unless @@asset_list[asset_name.to_sym]
8
+ @@asset_list[asset_name.to_sym]
9
+ end
10
+ end
11
+
12
+ def initialize(asset_name)
13
+ @asset_name = asset_name
14
+ end
15
+
16
+ def body=(asset_body)
17
+ @asset_body = asset_body
18
+ end
19
+
20
+ def source_file=(source_file_name)
21
+ # We read the file into memory in case the file moves or is temporary.
22
+ @asset_body = File.read(source_file_name)
23
+ end
24
+
25
+ def add_file(output_location)
26
+ unless (File.exists?(output_location))
27
+ create_file(output_location)
28
+ end
29
+ end
30
+
31
+ def create_file(output_location)
32
+ if @asset_body
33
+ File.open(output_location, "w+") do |write_file|
34
+ write_file << @asset_body
35
+ end
36
+ else
37
+ FileUtils.cp path, output_location
38
+ end
39
+ end
40
+
41
+ def path
42
+ File.absolute_path(File.join(File.dirname(__FILE__), "../source_files/", @asset_name))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ module Cornucopia
2
+ module Util
3
+ # This is a stupid little settings class
4
+ # Basicaly, anything you send to it is put into a hash or returned from a hash.
5
+ class GenericSettings
6
+ def initialize
7
+ @settings_hash = {}
8
+ end
9
+
10
+ def method_missing(method_sym, *arguments, &block)
11
+ if self.respond_to?("super__#{method_sym}".to_sym)
12
+ super
13
+ else
14
+ if method_sym.to_s[-1] == "="
15
+ raise "wrong number of arguments (#{arguments.length} for 1)" if !arguments || arguments.length != 1
16
+ raise "block not accepted" if block
17
+
18
+ @settings_hash[method_sym.to_s[0..-2].to_sym] = arguments[0]
19
+ else
20
+ raise "too many arguments (#{arguments.length} for 0)" if arguments && arguments.length > 0
21
+ raise "block not accepted" if block
22
+ @settings_hash[method_sym]
23
+ end
24
+ end
25
+ end
26
+
27
+ def respond_to?(method_sym, include_private = false)
28
+ method_name = method_sym.to_s
29
+ if method_name[0.."super__".length - 1] == "super__"
30
+ super(method_sym["super__".length..-1].to_sym, include_private)
31
+ else
32
+ true
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,97 @@
1
+ require ::File.expand_path('configuration', File.dirname(__FILE__))
2
+ require ::File.expand_path('report_builder', File.dirname(__FILE__))
3
+
4
+ module Cornucopia
5
+ module Util
6
+ class LogCapture
7
+ class << self
8
+ TAIL_BUF_LENGTH = 1 << 16
9
+
10
+ # This function will capture the logs and output them to the report
11
+ def capture_logs(report_table)
12
+ if report_table
13
+ if Object.const_defined?("Rails")
14
+ log_folder = Rails.root.to_s
15
+ if (log_folder =~ /\/features\/?$/ || log_folder =~ /\/spec\/?$/)
16
+ log_folder = File.join(log_folder, "../")
17
+ end
18
+
19
+ default_log_file = "log/#{Rails.env.to_s}.log"
20
+
21
+ output_log_file(report_table, File.join(log_folder, default_log_file))
22
+ else
23
+ log_folder = FileUtils.pwd
24
+ end
25
+
26
+ Cornucopia::Util::Configuration.user_log_files.each do |relative_log_file, options|
27
+ output_log_file(report_table, File.join(log_folder, relative_log_file), options)
28
+ end
29
+ else
30
+ Cornucopia::Util::ReportBuilder.current_report.within_section("Log Dump:") do |report|
31
+ report.within_table do |new_report_table|
32
+ Cornucopia::Util::LogCapture.capture_logs new_report_table
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def highlight_log_output(log_text)
39
+ output_text = Cornucopia::Util::ReportBuilder.format_code_refs(log_text)
40
+ output_text = output_text.gsub(/^(Completed [^23].*)$/, "<span class=\"completed-error\">\\1<\/span>")
41
+ output_text = output_text.gsub(/^(Completed [23].*)$/, "<span class=\"completed-other\">\\1<\/span>")
42
+
43
+ output_text.html_safe
44
+ end
45
+
46
+ # A cheap and sleazy tail function, but it should work...
47
+ def output_log_file(report_table, log_file_name, options = {})
48
+ if File.exists?(log_file_name)
49
+ output_file = false
50
+
51
+ options.reverse_merge!({ num_lines: Cornucopia::Util::Configuration.num_lines })
52
+
53
+ num_lines = options[:num_lines] || Cornucopia::Util::Configuration.num_lines
54
+ num_lines = Cornucopia::Util::Configuration.num_lines if num_lines <= 0
55
+ log_buffer = ""
56
+ file_size = File.size(log_file_name)
57
+
58
+ File.open(log_file_name) do |log_file|
59
+ seek_len = [file_size, TAIL_BUF_LENGTH].min
60
+ log_file.seek -seek_len, IO::SEEK_END
61
+
62
+ while (log_buffer.count("\n") <= num_lines)
63
+ log_buffer = log_file.read(seek_len) + log_buffer
64
+
65
+ file_size -= seek_len
66
+ seek_len = [file_size, TAIL_BUF_LENGTH].min
67
+
68
+ break if seek_len <= 0
69
+
70
+ log_file.seek -seek_len - TAIL_BUF_LENGTH, IO::SEEK_CUR
71
+ end
72
+ end
73
+
74
+ if log_buffer
75
+ log_buffer = log_buffer.split("\n")
76
+ if (log_buffer.length > num_lines)
77
+ log_buffer = log_buffer[-num_lines..-1]
78
+ end
79
+
80
+ report_table.write_stats File.basename(log_file_name),
81
+ Cornucopia::Util::PrettyFormatter.format_string(
82
+ Cornucopia::Util::LogCapture.highlight_log_output(
83
+ "log_file - #{log_file_name}:#{file_size}\n#{log_buffer.join("\n")}"
84
+ )
85
+ ),
86
+ do_not_pretty_print: true
87
+
88
+ output_file = true
89
+ end
90
+
91
+ output_file
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,580 @@
1
+ module Cornucopia
2
+ module Util
3
+ class PrettyFormatter
4
+ class << self
5
+ def format_string(unknown_string)
6
+ PrettyFormatter.new(unknown_string).pretty_print
7
+ end
8
+ end
9
+
10
+ def initialize(unknown_string)
11
+ @unknown_string = unknown_string
12
+ @print_html_safe = @unknown_string.html_safe?
13
+ end
14
+
15
+ def pretty_print
16
+ search_pos = 0
17
+
18
+ do_pretty_print = @unknown_string =~ /Parameters:[ \t]*\{.*?\}/
19
+ formatted_class_pos = class_pos = -1
20
+ while !do_pretty_print && class_pos && class_pos == formatted_class_pos
21
+ search_pos += formatted_class_pos + 1
22
+
23
+ if @print_html_safe
24
+ class_pos = @unknown_string[search_pos..-1] =~ /\#\&lt;[^ \t\n]+/
25
+ formatted_class_pos = (@unknown_string[search_pos..-1] =~ /\#\&lt;[^ \t\n]+[ \t]*\n/)
26
+ else
27
+ class_pos = @unknown_string[search_pos..-1] =~ /\#\<[^ \t\n]+/
28
+ formatted_class_pos = (@unknown_string[search_pos..-1] =~ /\#\<[^ \t\n]+[ \t]*\n/)
29
+ end
30
+
31
+ do_pretty_print ||= (class_pos && class_pos != formatted_class_pos)
32
+ end
33
+
34
+ if do_pretty_print
35
+ @indent_level = 0
36
+ @current_state = :unknown
37
+ @state_stack = [:unknown]
38
+ @current_pos = 0
39
+ @start_pos = 0
40
+ @unknown_string_len = @unknown_string.length
41
+ @formatted_string = ""
42
+
43
+ while @current_pos < @unknown_string_len
44
+ case @current_state
45
+ when :unknown
46
+ search_pos = @current_pos
47
+ params_pos = @unknown_string[search_pos..-1] =~ /Parameters:[ \t]*\{.*?\}/
48
+ params_pos += search_pos if params_pos
49
+ formatted_class_pos = class_pos = -1
50
+ while (class_pos && class_pos == formatted_class_pos)
51
+ search_pos += formatted_class_pos + 1
52
+
53
+ if @print_html_safe
54
+ class_pos = @unknown_string[search_pos..-1] =~ /\#\&lt;[^ \t\n]+/
55
+ formatted_class_pos = (@unknown_string[search_pos..-1] =~ /\#\&lt;[^ \t\n]+[ \t]*\n/)
56
+ else
57
+ class_pos = @unknown_string[search_pos..-1] =~ /\#\<[^ \t\n]+/
58
+ formatted_class_pos = (@unknown_string[search_pos..-1] =~ /\#\<[^ \t\n]+[ \t]*\n/)
59
+ end
60
+ end
61
+ class_pos += search_pos if class_pos
62
+ formatted_class_pos += search_pos if formatted_class_pos
63
+
64
+ if class_pos &&
65
+ class_pos != formatted_class_pos &&
66
+ (!params_pos || class_pos < params_pos)
67
+ @current_pos = class_pos
68
+
69
+ if @start_pos < @current_pos || @start_pos > 0
70
+ output_line(@unknown_string[@start_pos..@current_pos - 1])
71
+ else
72
+ @indent_level -= 1
73
+ @state_stack << :bottom_level
74
+ end
75
+
76
+ start_class
77
+ else
78
+ if params_pos
79
+ @current_pos = params_pos
80
+ @state_stack << :parameters
81
+
82
+ if @start_pos < @current_pos || @start_pos > 0
83
+ output_line(@unknown_string[@start_pos..@current_pos - 1])
84
+ else
85
+ @state_stack << :bottom_level
86
+ @indent_level -= 1
87
+ end
88
+
89
+ @formatted_string.rstrip!
90
+ @state_stack << :parameters_name
91
+ @current_state = :value
92
+ @start_pos = @current_pos
93
+ @indent_level += 1
94
+ else
95
+ output_end
96
+ end
97
+ end
98
+
99
+ when :value_end
100
+ case @state_stack[-1]
101
+ when :unknown
102
+ @current_state = :unknown
103
+
104
+ when :parameters
105
+ if @current_pos + 1 < @unknown_string_len
106
+ @formatted_string << "\n"
107
+ end
108
+ @indent_level -= 1
109
+ @state_stack.pop
110
+ @current_state = :unknown
111
+
112
+ when :parameters_name
113
+ group_value_end :parameters_start, "}"
114
+
115
+ when :class_name
116
+ group_value_end :variable,
117
+ ">",
118
+ end_append_text: @indent_level == 0 ? "\n" : "",
119
+ append_text: " ",
120
+ group_end_same_line: true,
121
+ add_value: true
122
+ @indent_level += 1
123
+
124
+ when :variable
125
+ if (position_matches_string(@current_pos - 1, ":") &&
126
+ ((!@print_html_safe &&
127
+ 0 == (@unknown_string[@current_pos..-1] =~ /^[ \t\r\n]*({.*?}|\#<.*?>|\[.*?\]|\".*?\"|[^\n,\"]+)[ \t\r\n]*[,\>]/)) ||
128
+ (@print_html_safe &&
129
+ 0 == (@unknown_string[@current_pos..-1] =~ /^[ \t\r\n]*({.*?}|\#&lt\;.*?&gt\;|\[.*?\]|&quot\;.*?&quot\;|[^\n,\"]+)[ \t\r\n]*(,|&gt\;)/))
130
+ ))
131
+ @state_stack.pop
132
+ @state_stack << :group_implied
133
+ @state_stack << :hash_key_symbol_post
134
+ elsif (!position_matches_string(@current_pos - 1, ":") &&
135
+ ((!@print_html_safe &&
136
+ 0 == (@unknown_string[@current_pos..-1] =~ /^[ \t\r\n]*=>[ \t\r\n]*({.*?}|\#<.*?>|\[.*?\]|\".*?\"|[^\n,\"]+)[ \t\r\n]*[,\>]/)) ||
137
+ (@print_html_safe &&
138
+ 0 == (@unknown_string[@current_pos..-1] =~ /^[ \t\r\n]*=&gt\;[ \t\r\n]*({.*?}|\#&lt\;.*?&gt\;|\[.*?\]|&quot\;.*?&quot\;|[^\n,\"&]+)[ \t\r\n]*(,|&gt\;)/))
139
+ ))
140
+ @state_stack.pop
141
+ @state_stack << :group_implied
142
+ @state_stack << :hash_key_string
143
+ else
144
+ group_value_end :variable_equal,
145
+ ">",
146
+ append_text: " ",
147
+ group_end_same_line: !@variable_set,
148
+ end_append_text: @indent_level == 0 ? "\n" : ""
149
+ end
150
+
151
+ when :variable_value
152
+ group_value_end :variable_comma, ">",
153
+ same_line: true,
154
+ end_append_text: @indent_level == 0 ? "\n" : ""
155
+
156
+ when :hash_key_symbol_post
157
+ group_value_end :hash_value,
158
+ @state_stack[-2] == :group_implied ? ">" : "}",
159
+ append_text: " ",
160
+ add_value: true
161
+
162
+ when :hash_key, :hash_key_symbol, :hash_key_string
163
+ group_value_end :hash_rocket,
164
+ @state_stack[-2] == :group_implied ? ">" : "}",
165
+ append_text: " "
166
+
167
+ when :hash_value
168
+ group_value_end :hash_comma,
169
+ @state_stack[-1] == :group_implied ? ">" : "}",
170
+ same_line: true
171
+
172
+ when :array
173
+ group_value_end :array_comma, "]"
174
+ end
175
+
176
+ @current_pos -= 1
177
+
178
+ when :hash_rocket
179
+ if @unknown_string[@current_pos] == ","
180
+ group_separator ",",
181
+ :hash_value,
182
+ @state_stack[-2] == :group_implied ? ">" : "}",
183
+ append_text: " "
184
+ else
185
+ group_separator "=>",
186
+ :hash_value,
187
+ @state_stack[-2] == :group_implied ? ">" : "}",
188
+ append_text: " "
189
+ end
190
+
191
+ when :variable_equal
192
+ @variable_set = true
193
+ if @unknown_string[@current_pos] == ","
194
+ group_separator ",",
195
+ :variable_value, ">",
196
+ end_append_text: @indent_level == 0 ? "\n" : ""
197
+ else
198
+ group_separator "=",
199
+ :variable_value, ">",
200
+ end_append_text: @indent_level == 0 ? "\n" : "",
201
+ append_text: " "
202
+ end
203
+
204
+ when :variable_comma
205
+ group_separator ",",
206
+ :variable, ">",
207
+ end_append_text: @indent_level == 0 ? "\n" : ""
208
+
209
+ when :array_comma
210
+ group_separator ",", :array, "]"
211
+
212
+ when :hash_comma
213
+ group_separator ",",
214
+ :hash_key,
215
+ @state_stack[-1] == :group_implied ? ">" : "}"
216
+ if @state_stack[-1] == :group_implied
217
+ @state_stack.pop
218
+ end
219
+
220
+ when :parameters_start
221
+ start_grouping :hash_key
222
+
223
+ when :whitespace
224
+ case @unknown_string[@current_pos]
225
+ when " ", "\r", "\n", "\t"
226
+ @start_pos = @current_pos + 1
227
+
228
+ else
229
+ @start_pos = @current_pos # probably redundant, but better safe than sorry...
230
+ @current_pos -= 1
231
+ @current_state = @state_stack.pop
232
+ end
233
+
234
+ when :string
235
+ if position_matches_string(@current_pos, "\\")
236
+ @current_pos += 1
237
+ elsif position_matches_string(@current_pos, "\"")
238
+ @current_pos += translate_value("\"").length
239
+ value_end
240
+ end
241
+
242
+ when :value
243
+ case @unknown_string[@current_pos]
244
+ when "{"
245
+ if @start_pos == @current_pos
246
+ start_grouping :hash_key
247
+ else
248
+ output_end
249
+ end
250
+
251
+ when "["
252
+ if @start_pos == @current_pos
253
+ start_grouping :array
254
+ else
255
+ output_end
256
+ end
257
+
258
+ when "\""
259
+ if position_matches_string(@current_pos, "\"")
260
+ start_quote
261
+ end
262
+
263
+ when "#"
264
+ if @start_pos == @current_pos &&
265
+ @current_pos < @unknown_string_len - 2 &&
266
+ position_matches_string(@current_pos + 1, "<")
267
+ start_class
268
+ end
269
+
270
+ when "="
271
+ case @state_stack[-1]
272
+ when :variable,
273
+ :hash_key,
274
+ :hash_key_string,
275
+ :hash_key_symbol,
276
+ :hash_key_symbol_post
277
+ value_end
278
+ end
279
+
280
+ when ":"
281
+ case @state_stack[-1]
282
+ when :hash_key
283
+ if @current_pos == @start_pos
284
+ @state_stack.pop
285
+ @state_stack << :hash_key_symbol
286
+ else
287
+ @state_stack.pop
288
+ @state_stack << :hash_key_symbol_post
289
+
290
+ @current_pos += 1
291
+ value_end
292
+ end
293
+
294
+ when :parameters_name
295
+ @current_pos += 1
296
+ value_end
297
+
298
+ when :variable
299
+ unless @current_pos == @start_pos
300
+ @current_pos += 1
301
+ value_end
302
+ end
303
+ end
304
+
305
+ when "&", ">"
306
+ if position_matches_string(@current_pos, ">")
307
+ case @state_stack[-1]
308
+ when :class_name,
309
+ :variable,
310
+ :variable_equal,
311
+ :variable_value,
312
+ :variable_comma
313
+ value_end
314
+ when :hash_key,
315
+ :hash_key_symbol_post,
316
+ :hash_key_symbol,
317
+ :hash_key_string,
318
+ :hash_rocket,
319
+ :hash_value,
320
+ :hash_comma
321
+ if @state_stack[-2] == :group_implied
322
+ value_end
323
+ end
324
+ end
325
+ elsif position_matches_string(@current_pos, "\"")
326
+ start_quote
327
+ end
328
+
329
+ when "}"
330
+ case @state_stack[-1]
331
+ when :hash_key,
332
+ :hash_key_symbol_post,
333
+ :hash_key_symbol,
334
+ :hash_key_string,
335
+ :hash_rocket,
336
+ :hash_value,
337
+ :hash_comma
338
+ value_end
339
+ end
340
+
341
+ when "]"
342
+ case @state_stack[-1]
343
+ when :array,
344
+ :array_comma
345
+ value_end
346
+ end
347
+
348
+ when ","
349
+ case @state_stack[-1]
350
+ when :hash_value,
351
+ :array,
352
+ :variable_value,
353
+ #unexpected, probably error states...
354
+ :variable,
355
+ :variable_equal,
356
+ :hash_key,
357
+ :hash_key_string,
358
+ :hash_key_symbol,
359
+ :hash_key_symbol_post,
360
+ :hash_rocket,
361
+ :hash_comma,
362
+ :variable_comma,
363
+ :array_comma
364
+ value_end
365
+ end
366
+
367
+ when " ", "\r", "\n", "\t"
368
+ case @state_stack[-1]
369
+ when :variable_value,
370
+ :array,
371
+ :hash_value
372
+ @current_pos += 1
373
+ @current_pos -= 1
374
+
375
+ else
376
+ value_end
377
+ end
378
+ end
379
+ end
380
+
381
+ @current_pos += 1
382
+ end
383
+
384
+ if @start_pos < @current_pos
385
+ output_end
386
+ end
387
+
388
+ @formatted_string.rstrip!
389
+ @formatted_string = @formatted_string.html_safe if @print_html_safe
390
+ @formatted_string
391
+ else
392
+ @unknown_string
393
+ end
394
+ end
395
+
396
+ def start_quote
397
+ if @start_pos == @current_pos
398
+ if @state_stack[-1] == :hash_key && @start_pos == @current_pos
399
+ @state_stack.pop
400
+ @state_stack << :hash_key_string
401
+ end
402
+
403
+ @current_state = :string
404
+ @current_pos += translate_value("\"").length - 1
405
+ else
406
+ # This is almost certainly an error. End it now...
407
+ output_end
408
+ end
409
+ end
410
+
411
+ def group_value_end(next_state, group_end_char, options = {})
412
+ @state_stack.pop
413
+
414
+ if position_matches_string @current_pos, group_end_char
415
+ output_line(@unknown_string[@start_pos..@current_pos - 1], options.merge(strip_output: true))
416
+ @start_pos = @current_pos
417
+
418
+ safe_end = translate_value(group_end_char)
419
+ end_grouping safe_end, options
420
+ @current_pos += 1
421
+ else
422
+ output_line(@unknown_string[@start_pos..@current_pos - 1], options.merge(strip_output: true))
423
+
424
+ if options[:append_text]
425
+ @formatted_string << options[:append_text]
426
+ end
427
+
428
+ @state_stack << next_state
429
+ @state_stack << :value if options[:add_value]
430
+ @start_pos = @current_pos
431
+ @current_state = :whitespace
432
+ end
433
+ end
434
+
435
+ def value_end
436
+ @current_state = :value_end
437
+ @current_pos -= 1
438
+ end
439
+
440
+ def group_separator(separator_value, next_state, group_end_char, options = {})
441
+ if position_matches_string(@current_pos, separator_value)
442
+ safe_separator = translate_value(separator_value)
443
+
444
+ @formatted_string << @unknown_string[@start_pos..(@current_pos + safe_separator.length - 1)]
445
+ @formatted_string.rstrip!
446
+
447
+ if options[:append_text]
448
+ @formatted_string << options[:append_text]
449
+ end
450
+
451
+ @start_pos = @current_pos + safe_separator.length
452
+ @current_pos += safe_separator.length - 1
453
+ @state_stack << next_state
454
+ @state_stack << :value
455
+ @current_state = :whitespace
456
+
457
+ elsif position_matches_string(@current_pos, group_end_char)
458
+ end_grouping translate_value(group_end_char), options
459
+
460
+ else
461
+ # Some kind of error, exit out
462
+ output_end
463
+ end
464
+ end
465
+
466
+ def end_grouping(safe_end, options = {})
467
+ @indent_level -= 1
468
+
469
+ if options[:group_end_same_line]
470
+ @formatted_string.strip!
471
+ @formatted_string << @unknown_string[@start_pos..@current_pos + safe_end.length - 1]
472
+ else
473
+ output_line(@unknown_string[@start_pos..@current_pos + safe_end.length - 1])
474
+
475
+ if options[:end_append_text]
476
+ @formatted_string << options[:end_append_text]
477
+ end
478
+ end
479
+
480
+ @indent_level -= 1
481
+ if @state_stack[-1] == :bottom_level ||
482
+ (@state_stack[-1] == :group_implied && @state_stack[-2] == :bottom_level)
483
+ if @state_stack[-1] == :bottom_level
484
+ @state_stack.pop
485
+ else
486
+ @state_stack.pop
487
+ @state_stack.pop
488
+ @state_stack << :group_implied
489
+ end
490
+
491
+ @indent_level += 1
492
+ end
493
+
494
+ @current_pos += safe_end.length - 1
495
+ @start_pos = @current_pos + 1
496
+ @current_state = :value_end
497
+ end
498
+
499
+ def start_grouping group_type
500
+ if @state_stack[-1] == :array
501
+ @state_stack << :bottom_level
502
+ else
503
+ @indent_level += 1
504
+ end
505
+
506
+ output_line(@unknown_string[@start_pos..@current_pos])
507
+ @indent_level += 1
508
+ @start_pos = @current_pos + 1
509
+ @state_stack << group_type
510
+ @state_stack << :value
511
+ @current_state = :whitespace
512
+ end
513
+
514
+ def start_class
515
+ if @state_stack[-1] == :array
516
+ @state_stack << :bottom_level
517
+ @indent_level -= 1
518
+ end
519
+ @indent_level += 1
520
+
521
+ @start_pos = @current_pos
522
+
523
+ @variable_set = false
524
+ @state_stack << :class_name
525
+
526
+ @current_pos += 1
527
+ @current_state = :value
528
+
529
+ @formatted_string.rstrip!
530
+ end
531
+
532
+ def output_end
533
+ unless @start_pos >= @unknown_string_len
534
+ @formatted_string << "\n" unless @formatted_string[-1] == "\n"
535
+ @formatted_string << @unknown_string[@start_pos..-1]
536
+ end
537
+
538
+ @current_pos = @unknown_string_len
539
+ @start_pos = @unknown_string_len
540
+ end
541
+
542
+ def translate_value(value)
543
+ safe_string = value
544
+
545
+ if @print_html_safe
546
+ safe_string = "".html_safe
547
+ safe_string << value
548
+ end
549
+
550
+ safe_string
551
+ end
552
+
553
+ def position_matches_string(test_position, test_string)
554
+ safe_string = translate_value(test_string)
555
+
556
+ test_position <= @unknown_string_len - safe_string.length &&
557
+ @unknown_string[test_position..(test_position + safe_string.length - 1)] == safe_string
558
+ end
559
+
560
+ def output_line(text, options = {})
561
+ unless text.blank?
562
+ unless options[:same_line]
563
+ if (@formatted_string[-1] == " ")
564
+ @formatted_string.rstrip!
565
+ end
566
+
567
+ @formatted_string << "\n" unless @formatted_string.length == 0 || @formatted_string[-1] == "\n"
568
+ @formatted_string << " " * (2 * @indent_level)
569
+ end
570
+
571
+ @formatted_string << text
572
+
573
+ if options[:strip_output]
574
+ @formatted_string.rstrip!
575
+ end
576
+ end
577
+ end
578
+ end
579
+ end
580
+ end