cucumber 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +19 -0
- data/README.rdoc +1 -1
- data/Rakefile +2 -5
- data/VERSION.yml +1 -1
- data/cucumber.gemspec +12 -4
- data/examples/i18n/de/features/addition.feature +1 -1
- data/examples/i18n/de/features/step_definitons/calculator_steps.rb +3 -3
- data/examples/self_test/features/step_definitions/sample_steps.rb +22 -22
- data/examples/tickets/features/half_manual.feature +11 -0
- data/examples/tickets/features/step_definitons/half_manual_steps.rb +11 -0
- data/features/announce.feature +97 -97
- data/features/background.feature +1 -4
- data/features/bug_475.feature +1 -2
- data/features/call_many_steps.feature +4 -6
- data/features/exception_in_after_block.feature +2 -3
- data/features/exception_in_after_step_block.feature +2 -3
- data/features/exception_in_before_block.feature +1 -2
- data/features/html_formatter/a.html +50 -40
- data/features/negative_tagged_hooks.feature +2 -3
- data/features/profiles.feature +2 -2
- data/features/report_called_undefined_steps.feature +2 -2
- data/features/rerun_formatter.feature +45 -0
- data/features/table_diffing.feature +1 -1
- data/features/table_mapping.feature +2 -3
- data/features/wire_protocol_table_diffing.feature +25 -0
- data/features/wire_protocol_tags.feature +47 -0
- data/lib/cucumber/ast/table.rb +2 -2
- data/lib/cucumber/cli/configuration.rb +1 -1
- data/lib/cucumber/cli/drb_client.rb +27 -16
- data/lib/cucumber/cli/profile_loader.rb +3 -2
- data/lib/cucumber/core_ext/proc.rb +1 -2
- data/lib/cucumber/formatter/console.rb +6 -4
- data/lib/cucumber/formatter/cucumber.css +10 -0
- data/lib/cucumber/formatter/cucumber.sass +10 -0
- data/lib/cucumber/formatter/html.rb +197 -186
- data/lib/cucumber/formatter/pdf.rb +24 -7
- data/lib/cucumber/formatter/rerun.rb +6 -3
- data/lib/cucumber/formatter/unicode.rb +1 -1
- data/lib/cucumber/languages.yml +7 -7
- data/lib/cucumber/parser/natural_language.rb +7 -0
- data/lib/cucumber/parser/treetop_ext.rb +1 -0
- data/lib/cucumber/rb_support/rb_world.rb +5 -0
- data/lib/cucumber/step_match.rb +4 -2
- data/lib/cucumber/step_mother.rb +53 -4
- data/lib/cucumber/wire_support/configuration.rb +11 -1
- data/lib/cucumber/wire_support/wire_language.rb +6 -3
- data/lib/cucumber/wire_support/wire_protocol.rb +2 -2
- data/lib/cucumber/wire_support/wire_protocol/requests.rb +26 -4
- data/spec/cucumber/ast/background_spec.rb +3 -3
- data/spec/cucumber/ast/feature_element_spec.rb +2 -2
- data/spec/cucumber/ast/feature_spec.rb +5 -5
- data/spec/cucumber/ast/outline_table_spec.rb +1 -1
- data/spec/cucumber/ast/py_string_spec.rb +1 -1
- data/spec/cucumber/ast/scenario_outline_spec.rb +3 -3
- data/spec/cucumber/ast/scenario_spec.rb +3 -3
- data/spec/cucumber/ast/step_collection_spec.rb +3 -3
- data/spec/cucumber/ast/step_spec.rb +1 -1
- data/spec/cucumber/ast/table_spec.rb +23 -3
- data/spec/cucumber/ast/tree_walker_spec.rb +1 -1
- data/spec/cucumber/broadcaster_spec.rb +1 -1
- data/spec/cucumber/cli/configuration_spec.rb +16 -6
- data/spec/cucumber/cli/drb_client_spec.rb +1 -1
- data/spec/cucumber/cli/main_spec.rb +13 -14
- data/spec/cucumber/cli/options_spec.rb +1 -1
- data/spec/cucumber/cli/profile_loader_spec.rb +1 -1
- data/spec/cucumber/core_ext/proc_spec.rb +1 -1
- data/spec/cucumber/formatter/ansicolor_spec.rb +1 -1
- data/spec/cucumber/formatter/color_io_spec.rb +1 -1
- data/spec/cucumber/formatter/duration_spec.rb +1 -1
- data/spec/cucumber/formatter/html_spec.rb +2 -2
- data/spec/cucumber/formatter/junit_spec.rb +2 -2
- data/spec/cucumber/formatter/progress_spec.rb +3 -3
- data/spec/cucumber/parser/feature_parser_spec.rb +5 -5
- data/spec/cucumber/parser/table_parser_spec.rb +3 -3
- data/spec/cucumber/rb_support/rb_step_definition_spec.rb +7 -7
- data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +1 -1
- data/spec/cucumber/step_match_spec.rb +6 -1
- data/spec/cucumber/step_mother_spec.rb +18 -10
- data/spec/cucumber/tag_expression_spec.rb +88 -90
- data/spec/cucumber/wire_support/configuration_spec.rb +29 -12
- data/spec/cucumber/wire_support/connection_spec.rb +1 -1
- data/spec/cucumber/wire_support/wire_exception_spec.rb +1 -1
- data/spec/cucumber/wire_support/wire_language_spec.rb +1 -1
- data/spec/cucumber/wire_support/wire_packet_spec.rb +1 -1
- data/spec/cucumber/wire_support/wire_step_definition_spec.rb +1 -1
- data/spec/cucumber/world/pending_spec.rb +5 -5
- metadata +128 -54
data/lib/cucumber/ast/table.rb
CHANGED
@@ -9,24 +9,35 @@ module Cucumber
|
|
9
9
|
class DRbClient
|
10
10
|
DEFAULT_PORT = 8990
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def run(args, error_stream, out_stream, port = nil)
|
15
|
+
port ||= ENV["CUCUMBER_DRB"] || DEFAULT_PORT
|
16
|
+
|
17
|
+
setup_support_for_io_streams_over_drb
|
18
|
+
|
19
|
+
feature_server = DRbObject.new_with_uri("druby://127.0.0.1:#{port}")
|
20
|
+
cloned_args = [] # I have no idea why this is needed, but if the regular args are sent then DRb magically transforms it into a DRb object - not an array
|
21
|
+
args.each { |arg| cloned_args << arg }
|
22
|
+
feature_server.run(cloned_args, error_stream, out_stream)
|
23
|
+
rescue DRb::DRbConnError => e
|
24
|
+
raise DRbClientError, "No DRb server is running."
|
22
25
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
|
27
|
+
private
|
28
|
+
def setup_support_for_io_streams_over_drb
|
29
|
+
# See http://redmine.ruby-lang.org/issues/show/496 as to why we specify localhost:0
|
30
|
+
begin
|
31
|
+
DRb.start_service("druby://localhost:0")
|
32
|
+
rescue SocketError
|
33
|
+
# Ruby-1.8.7 on snow leopard doesn't like localhost:0 - but just :0
|
34
|
+
# seems to work just fine
|
35
|
+
DRb.start_service("druby://:0")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
29
39
|
end
|
40
|
+
|
30
41
|
end
|
31
42
|
end
|
32
43
|
end
|
@@ -22,7 +22,8 @@ Defined profiles in cucumber.yml:
|
|
22
22
|
case(args_from_yml)
|
23
23
|
when String
|
24
24
|
raise YmlLoadError, "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/
|
25
|
-
|
25
|
+
require 'shellwords'
|
26
|
+
args_from_yml = Shellwords.shellwords(args_from_yml)
|
26
27
|
when Array
|
27
28
|
raise YmlLoadError, "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml.empty?
|
28
29
|
else
|
@@ -51,7 +52,7 @@ Defined profiles in cucumber.yml:
|
|
51
52
|
require 'erb'
|
52
53
|
require 'yaml'
|
53
54
|
begin
|
54
|
-
@cucumber_erb = ERB.new(IO.read(cucumber_file)).result
|
55
|
+
@cucumber_erb = ERB.new(IO.read(cucumber_file)).result(binding)
|
55
56
|
rescue Exception => e
|
56
57
|
raise(YmlLoadError,"cucumber.yml was found, but could not be parsed with ERB. Please refer to cucumber's documentation on correct profile usage.\n#{$!.inspect}")
|
57
58
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Proc extension to get more location info out of a proc
|
2
2
|
class Proc #:nodoc:
|
3
|
-
PROC_PATTERN = /[\d\w]+@(
|
3
|
+
PROC_PATTERN = /[\d\w]+@(.+):(\d+).*>/
|
4
4
|
|
5
5
|
def to_comment_line
|
6
6
|
"# #{file_colon_line}"
|
@@ -13,7 +13,6 @@ class Proc #:nodoc:
|
|
13
13
|
if Proc.new{}.to_s =~ PROC_PATTERN
|
14
14
|
def file_colon_line
|
15
15
|
path, line = *to_s.match(PROC_PATTERN)[1..2]
|
16
|
-
line = line.to_i - 1 if Cucumber::RUBY_1_9
|
17
16
|
path = File.expand_path(path)
|
18
17
|
pwd = Dir.pwd
|
19
18
|
path = path[pwd.length+1..-1]
|
@@ -68,7 +68,7 @@ module Cucumber
|
|
68
68
|
if !@failures.empty?
|
69
69
|
@io.puts format_string("Failing Scenarios:", :failed)
|
70
70
|
@failures.each do |failure|
|
71
|
-
profiles_string = (profiles.map{|profile| "-p #{profile}
|
71
|
+
profiles_string = profiles.empty? ? '' : (profiles.map{|profile| "-p #{profile}" }).join(' ') + ' '
|
72
72
|
@io.puts format_string("cucumber #{profiles_string}" + failure.file_colon_line, :failed) +
|
73
73
|
format_string(" # Scenario: " + failure.name, :comment)
|
74
74
|
end
|
@@ -145,9 +145,11 @@ module Cucumber
|
|
145
145
|
if @delayed_announcements
|
146
146
|
@delayed_announcements << announcement
|
147
147
|
else
|
148
|
-
@io
|
149
|
-
|
150
|
-
|
148
|
+
if @io
|
149
|
+
@io.puts
|
150
|
+
@io.puts(format_string(announcement, :tag))
|
151
|
+
@io.flush
|
152
|
+
end
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
@@ -48,6 +48,16 @@ body {
|
|
48
48
|
.cucumber .summary ul.features li, td .summary ul.features li, th .summary ul.features li {
|
49
49
|
display: inline;
|
50
50
|
}
|
51
|
+
.cucumber .step_name, td .step_name, th .step_name {
|
52
|
+
float: left;
|
53
|
+
}
|
54
|
+
.cucumber .step_file, td .step_file, th .step_file {
|
55
|
+
text-align: right;
|
56
|
+
color: #999999;
|
57
|
+
}
|
58
|
+
.cucumber .step_file a, td .step_file a, th .step_file a {
|
59
|
+
color: #999999;
|
60
|
+
}
|
51
61
|
.cucumber .tag, td .tag, th .tag {
|
52
62
|
font-weight: bold;
|
53
63
|
color: #246AC1;
|
@@ -328,124 +328,137 @@ module Cucumber
|
|
328
328
|
|
329
329
|
protected
|
330
330
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
end
|
340
|
-
@builder.pre do
|
341
|
-
@builder.text!(message)
|
342
|
-
end
|
331
|
+
def build_exception_detail(exception)
|
332
|
+
backtrace = Array.new
|
333
|
+
@builder.div(:class => 'message') do
|
334
|
+
message = exception.message
|
335
|
+
if defined?(RAILS_ROOT) && message.include?('Exception caught')
|
336
|
+
matches = message.match(/Showing <i>(.+)<\/i>(?:.+)#(\d+)/)
|
337
|
+
backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"]
|
338
|
+
message = message.match(/<code>([^(\/)]+)<\//m)[1]
|
343
339
|
end
|
344
|
-
@builder.
|
345
|
-
@builder.
|
346
|
-
backtrace = exception.backtrace
|
347
|
-
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
348
|
-
@builder << backtrace_line(backtrace.join("\n"))
|
349
|
-
end
|
340
|
+
@builder.pre do
|
341
|
+
@builder.text!(message)
|
350
342
|
end
|
351
|
-
extra = extra_failure_content(backtrace)
|
352
|
-
@builder << extra unless extra == ""
|
353
343
|
end
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
if status == :failed
|
360
|
-
set_scenario_color_failed
|
344
|
+
@builder.div(:class => 'backtrace') do
|
345
|
+
@builder.pre do
|
346
|
+
backtrace = exception.backtrace
|
347
|
+
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
348
|
+
@builder << backtrace_line(backtrace.join("\n"))
|
361
349
|
end
|
362
350
|
end
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
end
|
351
|
+
extra = extra_failure_content(backtrace)
|
352
|
+
@builder << extra unless extra == ""
|
353
|
+
end
|
354
|
+
|
355
|
+
def set_scenario_color(status)
|
356
|
+
if status == :undefined
|
357
|
+
set_scenario_color_pending
|
371
358
|
end
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
359
|
+
if status == :failed
|
360
|
+
set_scenario_color_failed
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def set_scenario_color_failed
|
365
|
+
@builder.script do
|
366
|
+
@builder.text!("makeRed('cucumber-header');") unless @header_red
|
367
|
+
@header_red = true
|
368
|
+
@builder.text!("makeRed('scenario_#{@scenario_number}');") unless @scenario_red
|
369
|
+
@scenario_red = true
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def set_scenario_color_pending
|
374
|
+
@builder.script do
|
375
|
+
@builder.text!("makeYellow('cucumber-header');") unless @header_red
|
376
|
+
@builder.text!("makeYellow('scenario_#{@scenario_number}');") unless @scenario_red
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def get_step_count(features)
|
381
|
+
count = 0
|
382
|
+
features = features.instance_variable_get("@features")
|
383
|
+
features.each do |feature|
|
384
|
+
#get background steps
|
385
|
+
if feature.instance_variable_get("@background")
|
386
|
+
background = feature.instance_variable_get("@background").instance_variable_get("@steps").instance_variable_get("@steps")
|
387
|
+
count += background.size
|
388
|
+
end
|
389
|
+
#get scenarios
|
390
|
+
feature.instance_variable_get("@feature_elements").each do |scenario|
|
391
|
+
#get steps
|
392
|
+
steps = scenario.instance_variable_get("@steps").instance_variable_get("@steps")
|
393
|
+
count += steps.size
|
394
|
+
|
395
|
+
#get example table
|
396
|
+
examples = scenario.instance_variable_get("@examples_array")
|
397
|
+
unless examples.nil?
|
398
|
+
examples.each do |example|
|
399
|
+
example_matrix = example.instance_variable_get("@outline_table").instance_variable_get("@cell_matrix")
|
400
|
+
count += example_matrix.size
|
402
401
|
end
|
402
|
+
end
|
403
403
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
end
|
404
|
+
#get multiline step tables
|
405
|
+
steps.each do |step|
|
406
|
+
multi_arg = step.instance_variable_get("@multiline_arg")
|
407
|
+
next if multi_arg.nil?
|
408
|
+
matrix = multi_arg.instance_variable_get("@cell_matrix")
|
409
|
+
count += matrix.size unless matrix.nil?
|
411
410
|
end
|
412
411
|
end
|
413
|
-
return count
|
414
412
|
end
|
413
|
+
return count
|
414
|
+
end
|
415
415
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
416
|
+
def build_step(keyword, step_match, status)
|
417
|
+
step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
|
418
|
+
@builder.div(:class => 'step_name') do |div|
|
419
|
+
@builder.span(keyword, :class => 'keyword')
|
420
|
+
@builder.text!(' ')
|
421
|
+
@builder.span(:class => 'step val') do |name|
|
422
|
+
name << h(step_name).gsub(/<span class="(.*?)">/, '<span class="\1">').gsub(/<\/span>/, '</span>')
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
step_file = step_match.file_colon_line
|
427
|
+
step_file.gsub(/^([^:]*\.rb):(\d*)/) do
|
428
|
+
if ENV['TM_PROJECT_DIRECTORY']
|
429
|
+
step_file = "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
424
430
|
end
|
425
431
|
end
|
426
|
-
|
427
|
-
|
428
|
-
@builder.
|
429
|
-
@builder
|
430
|
-
@builder.span(value,:class => 'step param')
|
431
|
-
end
|
432
|
+
|
433
|
+
@builder.div(:class => 'step_file') do |div|
|
434
|
+
@builder.span do
|
435
|
+
@builder << step_file
|
432
436
|
end
|
433
437
|
end
|
438
|
+
end
|
434
439
|
|
435
|
-
|
436
|
-
|
437
|
-
|
440
|
+
def build_cell(cell_type, value, attributes)
|
441
|
+
@builder.__send__(cell_type, attributes) do
|
442
|
+
@builder.div do
|
443
|
+
@builder.span(value,:class => 'step param')
|
438
444
|
end
|
439
445
|
end
|
446
|
+
end
|
440
447
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
end
|
448
|
+
def inline_css
|
449
|
+
@builder.style(:type => 'text/css') do
|
450
|
+
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
445
451
|
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def inline_js
|
455
|
+
@builder.script(:type => 'text/javascript') do
|
456
|
+
@builder << inline_js_content
|
457
|
+
end
|
458
|
+
end
|
446
459
|
|
447
|
-
|
448
|
-
|
460
|
+
def inline_js_content
|
461
|
+
<<-EOF
|
449
462
|
function moveProgressBar(percentDone) {
|
450
463
|
document.getElementById("cucumber-header").style.width = percentDone +"%";
|
451
464
|
}
|
@@ -459,113 +472,111 @@ module Cucumber
|
|
459
472
|
document.getElementById(element_id).style.color = '#000000';
|
460
473
|
}
|
461
474
|
EOF
|
462
|
-
|
475
|
+
end
|
463
476
|
|
464
|
-
|
465
|
-
|
466
|
-
|
477
|
+
def move_progress
|
478
|
+
@builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
|
479
|
+
end
|
467
480
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
end
|
473
|
-
result
|
481
|
+
def percent_done
|
482
|
+
result = 100.0
|
483
|
+
if @step_count != 0
|
484
|
+
result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
|
474
485
|
end
|
486
|
+
result
|
487
|
+
end
|
475
488
|
|
476
|
-
|
477
|
-
|
478
|
-
|
489
|
+
def format_exception(exception)
|
490
|
+
(["#{exception.message}"] + exception.backtrace).join("\n")
|
491
|
+
end
|
479
492
|
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
end
|
493
|
+
def backtrace_line(line)
|
494
|
+
line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*)/) do
|
495
|
+
if ENV['TM_PROJECT_DIRECTORY']
|
496
|
+
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
497
|
+
else
|
498
|
+
line
|
487
499
|
end
|
488
500
|
end
|
501
|
+
end
|
489
502
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
503
|
+
def print_stats(features)
|
504
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
|
505
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
|
506
|
+
end
|
494
507
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
508
|
+
def print_stat_string(features)
|
509
|
+
string = String.new
|
510
|
+
string << dump_count(@step_mother.scenarios.length, "scenario")
|
511
|
+
scenario_count = print_status_counts{|status| @step_mother.scenarios(status)}
|
512
|
+
string << scenario_count if scenario_count
|
513
|
+
string << "<br />"
|
514
|
+
string << dump_count(@step_mother.steps.length, "step")
|
515
|
+
step_count = print_status_counts{|status| @step_mother.steps(status)}
|
516
|
+
string << step_count if step_count
|
517
|
+
end
|
505
518
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
519
|
+
def print_status_counts
|
520
|
+
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
|
521
|
+
elements = yield status
|
522
|
+
elements.any? ? "#{elements.length} #{status.to_s}" : nil
|
523
|
+
end.compact
|
524
|
+
return " (#{counts.join(', ')})" if counts.any?
|
525
|
+
end
|
513
526
|
|
514
|
-
|
515
|
-
|
516
|
-
|
527
|
+
def dump_count(count, what, state=nil)
|
528
|
+
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
|
529
|
+
end
|
517
530
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
end
|
522
|
-
end
|
523
|
-
end
|
531
|
+
def create_builder(io)
|
532
|
+
OrderedXmlMarkup.new(:target => io, :indent => 0)
|
533
|
+
end
|
524
534
|
|
535
|
+
class SnippetExtractor #:nodoc:
|
536
|
+
class NullConverter; def convert(code, pre); code; end; end #:nodoc:
|
537
|
+
begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
|
525
538
|
|
526
|
-
|
527
|
-
|
528
|
-
|
539
|
+
def snippet(error)
|
540
|
+
raw_code, line = snippet_for(error[0])
|
541
|
+
highlighted = @@converter.convert(raw_code, false)
|
542
|
+
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
|
543
|
+
post_process(highlighted, line)
|
544
|
+
end
|
529
545
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
546
|
+
def snippet_for(error_line)
|
547
|
+
if error_line =~ /(.*):(\d+)/
|
548
|
+
file = $1
|
549
|
+
line = $2.to_i
|
550
|
+
[lines_around(file, line), line]
|
551
|
+
else
|
552
|
+
["# Couldn't get snippet for #{error_line}", 1]
|
553
|
+
end
|
554
|
+
end
|
536
555
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
556
|
+
def lines_around(file, line)
|
557
|
+
if File.file?(file)
|
558
|
+
lines = File.open(file).read.split("\n")
|
559
|
+
min = [0, line-3].max
|
560
|
+
max = [line+1, lines.length-1].min
|
561
|
+
selected_lines = []
|
562
|
+
selected_lines.join("\n")
|
563
|
+
lines[min..max].join("\n")
|
564
|
+
else
|
565
|
+
"# Couldn't get snippet for #{file}"
|
566
|
+
end
|
567
|
+
end
|
547
568
|
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
"# Couldn't get snippet for #{file}"
|
558
|
-
end
|
559
|
-
end
|
569
|
+
def post_process(highlighted, offending_line)
|
570
|
+
new_lines = []
|
571
|
+
highlighted.split("\n").each_with_index do |line, i|
|
572
|
+
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
|
573
|
+
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
574
|
+
new_lines << new_line
|
575
|
+
end
|
576
|
+
new_lines.join("\n")
|
577
|
+
end
|
560
578
|
|
561
|
-
|
562
|
-
new_lines = []
|
563
|
-
highlighted.split("\n").each_with_index do |line, i|
|
564
|
-
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
|
565
|
-
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
566
|
-
new_lines << new_line
|
579
|
+
end
|
567
580
|
end
|
568
|
-
new_lines.join("\n")
|
569
581
|
end
|
570
|
-
|
571
|
-
end
|
582
|
+
end
|