viewcumber 0.0.1 → 0.0.2

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 (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