cucumber 0.6.2 → 0.6.3
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.
- 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
|