viewcumber 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/Rakefile +51 -0
  4. data/VERSION +1 -0
  5. data/lib/viewcumber.rb +651 -0
  6. metadata +10 -5
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "viewcumber"
8
+ gem.summary = %Q{ Cucumber formatter for easily viewing each step of your scenarios }
9
+ gem.homepage = "http://github.com/versapay/viewcumber"
10
+ gem.authors = ["gregbell", "pcreux", "samuelreh"]
11
+ gem.add_dependency "cucumber", ">=0.8.5"
12
+ gem.add_dependency "capybara", ">=0.3"
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/test_*.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+ task :test => :check_dependencies
40
+
41
+ task :default => :test
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "viewcumber #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,651 @@
1
+ require 'cucumber'
2
+ require 'capybara'
3
+
4
+ require 'erb'
5
+ require 'cucumber/formatter/ordered_xml_markup'
6
+ require 'cucumber/formatter/duration'
7
+ require 'cucumber/formatter/io'
8
+ require 'FileUtils'
9
+
10
+ if respond_to? :AfterStep
11
+ AfterStep do |scenario|
12
+ begin
13
+ if Capybara.page.driver.respond_to? :html
14
+ Viewcumber.last_step_html = Viewcumber.rewrite_css_and_image_references(Capybara.page.driver.html.to_s)
15
+ end
16
+ rescue Exception => e
17
+ p e
18
+ end
19
+ end
20
+ end
21
+
22
+ class Viewcumber
23
+ include ERB::Util # for the #h method
24
+ include Cucumber::Formatter::Duration
25
+ include Cucumber::Formatter::Io
26
+
27
+ class << self
28
+ attr_accessor :last_step_html
29
+ end
30
+
31
+ def initialize(step_mother, path_or_io, options)
32
+ FileUtils.mkdir "viewcumber/" unless File.directory? "viewcumber/"
33
+ @io = File.open("viewcumber/" + "index.html", 'w+')
34
+ @step_mother = step_mother
35
+ @options = options
36
+ @buffer = {}
37
+ @builder = create_builder(@io)
38
+ @feature_number = 0
39
+ @scenario_number = 0
40
+ @step_number = 0
41
+ @header_red = nil
42
+ end
43
+
44
+ def embed(file, mime_type)
45
+ case(mime_type)
46
+ when /^image\/(png|gif|jpg|jpeg)/
47
+ embed_image(file)
48
+ end
49
+ end
50
+
51
+ def embed_image(file)
52
+ id = file.hash
53
+ @builder.span(:class => 'embed') do |pre|
54
+ pre << %{<a href="" onclick="img=document.getElementById('#{id}'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false">Screenshot</a><br>&nbsp;
55
+ <img id="#{id}" style="display: none" src="#{file}"/>}
56
+ end
57
+ end
58
+
59
+
60
+ def before_features(features)
61
+ @step_count = get_step_count(features)
62
+ copy_assets
63
+
64
+ # <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
65
+ @builder.declare!(
66
+ :DOCTYPE,
67
+ :html,
68
+ :PUBLIC,
69
+ '-//W3C//DTD XHTML 1.0 Strict//EN',
70
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
71
+ )
72
+
73
+ @builder << '<html xmlns ="http://www.w3.org/1999/xhtml">'
74
+ @builder.head do
75
+ @builder.meta(:content => 'text/html;charset=utf-8')
76
+ @builder.title 'Cucumber'
77
+ inline_css
78
+ inline_js
79
+ @builder << <<-HTML
80
+ <link href='http://fonts.googleapis.com/css?family=Droid+Serif' rel='stylesheet' type='text/css'>
81
+ <link rel="stylesheet" type="text/css" href="main.css" />
82
+ <script type="text/javascript" src="jquery-min.js"></script>
83
+ <script type="text/javascript" src="main.js"></script>
84
+ HTML
85
+ end
86
+ @builder << '<body>'
87
+ @builder << "<!-- Step count #{@step_count}-->"
88
+ @builder << <<-HTML
89
+ <div id="navigator">
90
+ <a href="#" id="prev-btn" onclick="Cucumber.previous(); return false;">&larr;</a>
91
+ <a href="#" id="next-btn" onclick="Cucumber.next(); return false;">&rarr;</a>
92
+ <p id="step-description">
93
+ <span id="step-title"></span>
94
+ (<a href="#" onclick="Cucumber.changeScenario(); return false">Change</a>)
95
+ </p>
96
+ <p id="step-name"></p>
97
+ </div>
98
+ HTML
99
+ @builder << '<div class="cucumber">'
100
+ @builder << "<ul>"
101
+ end
102
+
103
+ def after_features(features)
104
+ @builder << '</li>'
105
+ @builder << '</ul>'
106
+ @builder << '</div>'
107
+ @builder << '<iframe name="viewport" id="viewport"></iframe>'
108
+ @builder << '</body>'
109
+ @builder << '</html>'
110
+ end
111
+
112
+ def before_feature(feature)
113
+ @exceptions = []
114
+ @builder << '<li class="feature">'
115
+ end
116
+
117
+ def after_feature(feature)
118
+ @builder << '</ul>'
119
+ @builder << '</li> <!--class="feature" -->'
120
+ end
121
+
122
+ def before_comment(comment)
123
+ @builder << '<pre class="comment">'
124
+ end
125
+
126
+ def after_comment(comment)
127
+ @builder << '</pre>'
128
+ end
129
+
130
+ def comment_line(comment_line)
131
+ @builder.text!(comment_line)
132
+ @builder.br
133
+ end
134
+
135
+ def after_tags(tags)
136
+ @tag_spacer = nil
137
+ end
138
+
139
+ def tag_name(tag_name)
140
+ @builder.text!(@tag_spacer) if @tag_spacer
141
+ @tag_spacer = ' '
142
+ @builder.span(tag_name, :class => 'tag')
143
+ end
144
+
145
+ def feature_name(keyword, name)
146
+ lines = name.split(/\r?\n/)
147
+ return if lines.empty?
148
+ @builder.h2 do |h2|
149
+ #@builder.span(keyword + ': ' + lines[0], :class => 'val')
150
+ @builder.span(lines[0], :class => 'val')
151
+ end
152
+ @builder.p(:class => 'narrative') do
153
+ lines[1..-1].each do |line|
154
+ @builder.text!(line.strip)
155
+ @builder.br
156
+ end
157
+ end
158
+ @builder << '<ul>'
159
+ end
160
+
161
+ def before_background(background)
162
+ @in_background = true
163
+ @builder << '<li class="background">'
164
+ end
165
+
166
+ def after_background(background)
167
+ @in_background = nil
168
+ @builder << "</li>\n"
169
+ end
170
+
171
+ def background_name(keyword, name, file_colon_line, source_indent)
172
+ @listing_background = true
173
+ @builder.h3 do |h3|
174
+ @builder.span(keyword, :class => 'keyword')
175
+ @builder.text!(' ')
176
+ @builder.span(name, :class => 'val')
177
+ end
178
+ end
179
+
180
+ def before_feature_element(feature_element)
181
+ @scenario_number+=1
182
+ @scenario_red = false
183
+ css_class = {
184
+ Cucumber::Ast::Scenario => 'scenario',
185
+ Cucumber::Ast::ScenarioOutline => 'scenario outline'
186
+ }[feature_element.class]
187
+ @builder << "<li class='#{css_class}'>"
188
+ end
189
+
190
+ def after_feature_element(feature_element)
191
+ @builder << "</li>\n"
192
+ @open_step_list = true
193
+ end
194
+
195
+ def scenario_name(keyword, name, file_colon_line, source_indent)
196
+ @listing_background = false
197
+ @builder.h3(:id => "scenario_#{@scenario_number}") do
198
+ @builder.span(name, :class => 'val')
199
+ end
200
+ end
201
+
202
+ def before_outline_table(outline_table)
203
+ @outline_row = 0
204
+ @builder << '<table>'
205
+ end
206
+
207
+ def after_outline_table(outline_table)
208
+ @builder << '</table>'
209
+ @outline_row = nil
210
+ end
211
+
212
+ def before_examples(examples)
213
+ @builder << '<li class="examples">'
214
+ end
215
+
216
+ def after_examples(examples)
217
+ @builder << "</li>\n"
218
+ end
219
+
220
+ def examples_name(keyword, name)
221
+ @builder.h4 do
222
+ @builder.span(keyword, :class => 'keyword')
223
+ @builder.text!(' ')
224
+ @builder.span(name, :class => 'val')
225
+ end
226
+ end
227
+
228
+ def before_steps(steps)
229
+ @builder << '<ol>'
230
+ end
231
+
232
+ def after_steps(steps)
233
+ @builder << '</ol>'
234
+ end
235
+
236
+ def before_step(step)
237
+ @step_id = step.dom_id
238
+ @step_number += 1
239
+ @step = step
240
+ @file_name = @step.file_colon_line.gsub(/:|\//,'-') + ".html"
241
+ end
242
+
243
+ def after_step(step)
244
+ File.open("viewcumber/" + @file_name, 'w+') do |f|
245
+ f << self.class.last_step_html
246
+ end
247
+ end
248
+
249
+ def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
250
+ @step_match = step_match
251
+ @hide_this_step = false
252
+ if exception
253
+ if @exceptions.include?(exception)
254
+ @hide_this_step = true
255
+ return
256
+ end
257
+ @exceptions << exception
258
+ end
259
+ if status != :failed && @in_background ^ background
260
+ @hide_this_step = true
261
+ return
262
+ end
263
+ @status = status
264
+ return if @hide_this_step
265
+ set_scenario_color(status)
266
+ @builder << "<li id='#{@step_id}' class='step #{status}'>"
267
+ end
268
+
269
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
270
+ return if @hide_this_step
271
+ # print snippet for undefined steps
272
+ if status == :undefined
273
+ step_multiline_class = @step.multiline_arg ? @step.multiline_arg.class : nil
274
+ @builder.pre do |pre|
275
+ pre << @step_mother.snippet_text(@step.actual_keyword,step_match.instance_variable_get("@name") || '',step_multiline_class)
276
+ end
277
+ end
278
+ @builder << "</li>\n"
279
+ end
280
+
281
+ def step_name(keyword, step_match, status, source_indent, background)
282
+ @step_matches ||= []
283
+ background_in_scenario = background && !@listing_background
284
+ @skip_step = @step_matches.index(step_match) || background_in_scenario
285
+ @step_matches << step_match
286
+
287
+ unless @skip_step
288
+ build_step(keyword, step_match, status)
289
+ end
290
+ end
291
+
292
+ def exception(exception, status)
293
+ build_exception_detail(exception)
294
+ end
295
+
296
+ def extra_failure_content(file_colon_line)
297
+ @snippet_extractor ||= SnippetExtractor.new
298
+ "<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
299
+ end
300
+
301
+ def before_multiline_arg(multiline_arg)
302
+ return if @hide_this_step || @skip_step
303
+ if Ast::Table === multiline_arg
304
+ @builder << '<table>'
305
+ end
306
+ end
307
+
308
+ def after_multiline_arg(multiline_arg)
309
+ return if @hide_this_step || @skip_step
310
+ if Ast::Table === multiline_arg
311
+ @builder << '</table>'
312
+ end
313
+ end
314
+
315
+ def py_string(string)
316
+ return if @hide_this_step
317
+ @builder.pre(:class => 'val') do |pre|
318
+ @builder << string.gsub("\n", '&#x000A;')
319
+ end
320
+ end
321
+
322
+
323
+ def before_table_row(table_row)
324
+ @row_id = table_row.dom_id
325
+ @col_index = 0
326
+ return if @hide_this_step
327
+ @builder << "<tr class='step' id='#{@row_id}'>"
328
+ end
329
+
330
+ def after_table_row(table_row)
331
+ return if @hide_this_step
332
+ @builder << "</tr>\n"
333
+ if table_row.exception
334
+ @builder.tr do
335
+ @builder.td(:colspan => @col_index.to_s, :class => 'failed') do
336
+ @builder.pre do |pre|
337
+ pre << format_exception(table_row.exception)
338
+ end
339
+ end
340
+ end
341
+ set_scenario_color_failed
342
+ end
343
+ if @outline_row
344
+ @outline_row += 1
345
+ end
346
+ @step_number += 1
347
+ end
348
+
349
+ def table_cell_value(value, status)
350
+ return if @hide_this_step
351
+
352
+ @cell_type = @outline_row == 0 ? :th : :td
353
+ attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
354
+ attributes[:class] += " #{status}" if status
355
+ build_cell(@cell_type, value, attributes)
356
+ set_scenario_color(status)
357
+ @col_index += 1
358
+ end
359
+
360
+ def announce(announcement)
361
+ @builder.pre(announcement, :class => 'announcement')
362
+ end
363
+
364
+ def self.rewrite_css_and_image_references(response_html) # :nodoc:
365
+ return response_html unless Capybara.asset_root
366
+ directories = Dir.new(Capybara.asset_root).entries.inject([]) do |list, name|
367
+ list << name if File.directory?(name) and not name.to_s =~ /^\./
368
+ list
369
+ end
370
+ response_html.gsub(/("|')\/(#{directories.join('|')})/, '\1public/\2')
371
+ end
372
+
373
+ protected
374
+
375
+ def build_exception_detail(exception)
376
+ backtrace = Array.new
377
+ @builder.div(:class => 'message') do
378
+ message = exception.message
379
+ if defined?(RAILS_ROOT) && message.include?('Exception caught')
380
+ matches = message.match(/Showing <i>(.+)<\/i>(?:.+)#(\d+)/)
381
+ backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"]
382
+ message = message.match(/<code>([^(\/)]+)<\//m)[1]
383
+ end
384
+ @builder.pre do
385
+ @builder.text!(message)
386
+ end
387
+ end
388
+ @builder.div(:class => 'backtrace') do
389
+ @builder.pre do
390
+ backtrace = exception.backtrace
391
+ backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
392
+ @builder << backtrace_line(backtrace.join("\n"))
393
+ end
394
+ end
395
+ extra = extra_failure_content(backtrace)
396
+ @builder << extra unless extra == ""
397
+ end
398
+
399
+ def set_scenario_color(status)
400
+ if status == :undefined
401
+ set_scenario_color_pending
402
+ end
403
+ if status == :failed
404
+ set_scenario_color_failed
405
+ end
406
+ end
407
+
408
+ def set_scenario_color_failed
409
+ @builder.script do
410
+ @builder.text!("makeRed('cucumber-header');") unless @header_red
411
+ @header_red = true
412
+ @builder.text!("makeRed('scenario_#{@scenario_number}');") unless @scenario_red
413
+ @scenario_red = true
414
+ end
415
+ end
416
+
417
+ def set_scenario_color_pending
418
+ @builder.script do
419
+ @builder.text!("makeYellow('cucumber-header');") unless @header_red
420
+ @builder.text!("makeYellow('scenario_#{@scenario_number}');") unless @scenario_red
421
+ end
422
+ end
423
+
424
+ def get_step_count(features)
425
+ count = 0
426
+ features = features.instance_variable_get("@features")
427
+ features.each do |feature|
428
+ #get background steps
429
+ if feature.instance_variable_get("@background")
430
+ background = feature.instance_variable_get("@background")
431
+ background.init
432
+ background_steps = background.instance_variable_get("@steps").instance_variable_get("@steps")
433
+ count += background_steps.size
434
+ end
435
+ #get scenarios
436
+ feature.instance_variable_get("@feature_elements").each do |scenario|
437
+ scenario.init
438
+ #get steps
439
+ steps = scenario.instance_variable_get("@steps").instance_variable_get("@steps")
440
+ count += steps.size
441
+
442
+ #get example table
443
+ examples = scenario.instance_variable_get("@examples_array")
444
+ unless examples.nil?
445
+ examples.each do |example|
446
+ example_matrix = example.instance_variable_get("@outline_table").instance_variable_get("@cell_matrix")
447
+ count += example_matrix.size
448
+ end
449
+ end
450
+
451
+ #get multiline step tables
452
+ steps.each do |step|
453
+ multi_arg = step.instance_variable_get("@multiline_arg")
454
+ next if multi_arg.nil?
455
+ matrix = multi_arg.instance_variable_get("@cell_matrix")
456
+ count += matrix.size unless matrix.nil?
457
+ end
458
+ end
459
+ end
460
+ return count
461
+ end
462
+
463
+ def build_step(keyword, step_match, status)
464
+ step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
465
+ @builder.div(:class => 'step_name') do |div|
466
+ @builder << "<a target=\"viewport\" class=\"step-link\" href=\"#{@file_name}\">"
467
+ @builder.span(keyword, :class => 'keyword')
468
+ @builder.span(:class => 'val') do |name|
469
+ name << h(step_name).gsub(/&lt;span class=&quot;(.*?)&quot;&gt;/, '<span class="\1">').gsub(/&lt;\/span&gt;/, '</span>')
470
+ end
471
+ @builder << "</a>"
472
+ end
473
+ end
474
+
475
+ def build_cell(cell_type, value, attributes)
476
+ @builder.__send__(cell_type, attributes) do
477
+ @builder.div do
478
+ @builder.span(value,:class => 'step param')
479
+ end
480
+ end
481
+ end
482
+
483
+ def inline_css
484
+ @builder.style(:type => 'text/css') do
485
+ # Inline CSS here
486
+ #@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
487
+ end
488
+ end
489
+
490
+ def inline_js
491
+ @builder.script(:type => 'text/javascript') do
492
+ # Inline JQuery here
493
+ #@builder << inline_jquery
494
+ #@builder << inline_js_content
495
+ end
496
+ end
497
+
498
+ def inline_jquery
499
+ File.read(File.dirname(__FILE__) + '/jquery-min.js')
500
+ end
501
+
502
+ def inline_js_content
503
+ <<-EOF
504
+
505
+ SCENARIOS = "h3[id^='scenario_']";
506
+
507
+ $(document).ready(function() {
508
+ $(SCENARIOS).css('cursor', 'pointer');
509
+ $(SCENARIOS).click(function() {
510
+ $(this).siblings().toggle(250);
511
+ });
512
+
513
+ $("#collapser").css('cursor', 'pointer');
514
+ $("#collapser").click(function() {
515
+ $(SCENARIOS).siblings().hide();
516
+ });
517
+
518
+ $("#expander").css('cursor', 'pointer');
519
+ $("#expander").click(function() {
520
+ $(SCENARIOS).siblings().show();
521
+ });
522
+ })
523
+
524
+ function moveProgressBar(percentDone) {
525
+ $("cucumber-header").css('width', percentDone +"%");
526
+ }
527
+ function makeRed(element_id) {
528
+ $('#'+element_id).css('background', '#C40D0D');
529
+ $('#'+element_id).css('color', '#FFFFFF');
530
+ }
531
+ function makeYellow(element_id) {
532
+ $('#'+element_id).css('background', '#FAF834');
533
+ $('#'+element_id).css('color', '#000000');
534
+ }
535
+
536
+ EOF
537
+ end
538
+
539
+ def move_progress
540
+ @builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
541
+ end
542
+
543
+ def percent_done
544
+ result = 100.0
545
+ if @step_count != 0
546
+ result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
547
+ end
548
+ result
549
+ end
550
+
551
+ def format_exception(exception)
552
+ (["#{exception.message}"] + exception.backtrace).join("\n")
553
+ end
554
+
555
+ def backtrace_line(line)
556
+ line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*)/) do
557
+ if ENV['TM_PROJECT_DIRECTORY']
558
+ "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
559
+ else
560
+ line
561
+ end
562
+ end
563
+ end
564
+
565
+ def print_stats(features)
566
+ @builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
567
+ @builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
568
+ end
569
+
570
+ def print_stat_string(features)
571
+ string = String.new
572
+ string << dump_count(@step_mother.scenarios.length, "scenario")
573
+ scenario_count = print_status_counts{|status| @step_mother.scenarios(status)}
574
+ string << scenario_count if scenario_count
575
+ string << "<br />"
576
+ string << dump_count(@step_mother.steps.length, "step")
577
+ step_count = print_status_counts{|status| @step_mother.steps(status)}
578
+ string << step_count if step_count
579
+ end
580
+
581
+ def print_status_counts
582
+ counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
583
+ elements = yield status
584
+ elements.any? ? "#{elements.length} #{status.to_s}" : nil
585
+ end.compact
586
+ return " (#{counts.join(', ')})" if counts.any?
587
+ end
588
+
589
+ def dump_count(count, what, state=nil)
590
+ [count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
591
+ end
592
+
593
+ def create_builder(io)
594
+ Cucumber::Formatter::OrderedXmlMarkup.new(:target => io, :indent => 0)
595
+ end
596
+
597
+ def copy_assets
598
+ FileUtils.mkdir "viewcumber" unless File.directory? "viewcumber"
599
+ assets_dir = File.dirname(__FILE__) + "/assets"
600
+ File.copy assets_dir + "/viewcumber.css", "viewcumber/main.css"
601
+ File.copy assets_dir + "/jquery-min.js", "viewcumber/jquery-min.js"
602
+ File.copy assets_dir + "/viewcumber.js", "viewcumber/main.js"
603
+ File.copy assets_dir + "/viewless.html", "viewcumber/viewless.html"
604
+ FileUtils.cp_r "public", "viewcumber/public"
605
+ end
606
+
607
+ class SnippetExtractor #:nodoc:
608
+ class NullConverter; def convert(code, pre); code; end; end #:nodoc:
609
+ begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
610
+
611
+ def snippet(error)
612
+ raw_code, line = snippet_for(error[0])
613
+ highlighted = @@converter.convert(raw_code, false)
614
+ highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
615
+ post_process(highlighted, line)
616
+ end
617
+
618
+ def snippet_for(error_line)
619
+ if error_line =~ /(.*):(\d+)/
620
+ file = $1
621
+ line = $2.to_i
622
+ [lines_around(file, line), line]
623
+ else
624
+ ["# Couldn't get snippet for #{error_line}", 1]
625
+ end
626
+ end
627
+
628
+ def lines_around(file, line)
629
+ if File.file?(file)
630
+ lines = File.open(file).read.split("\n")
631
+ min = [0, line-3].max
632
+ max = [line+1, lines.length-1].min
633
+ selected_lines = []
634
+ selected_lines.join("\n")
635
+ lines[min..max].join("\n")
636
+ else
637
+ "# Couldn't get snippet for #{file}"
638
+ end
639
+ end
640
+
641
+ def post_process(highlighted, offending_line)
642
+ new_lines = []
643
+ highlighted.split("\n").each_with_index do |line, i|
644
+ new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
645
+ new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
646
+ new_lines << new_line
647
+ end
648
+ new_lines.join("\n")
649
+ end
650
+ end
651
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: viewcumber
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - gregbell
@@ -61,12 +61,17 @@ extra_rdoc_files:
61
61
  - LICENSE
62
62
  - README.rdoc
63
63
  files:
64
+ - .document
65
+ - .gitignore
66
+ - LICENSE
67
+ - README.rdoc
68
+ - Rakefile
69
+ - VERSION
64
70
  - lib/assets/jquery-min.js
65
71
  - lib/assets/viewcumber.css
66
72
  - lib/assets/viewcumber.js
67
73
  - lib/assets/viewless.html
68
- - LICENSE
69
- - README.rdoc
74
+ - lib/viewcumber.rb
70
75
  - test/helper.rb
71
76
  - test/test_viewcumber.rb
72
77
  has_rdoc: true