nanoc 3.4.3 → 3.5.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/CONTRIBUTING.md +25 -0
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +22 -13
  4. data/NEWS.md +27 -0
  5. data/README.md +3 -1
  6. data/lib/nanoc.rb +2 -2
  7. data/lib/nanoc/base/compilation/compiler_dsl.rb +19 -0
  8. data/lib/nanoc/base/core_ext/array.rb +18 -8
  9. data/lib/nanoc/base/core_ext/hash.rb +18 -8
  10. data/lib/nanoc/base/core_ext/pathname.rb +2 -8
  11. data/lib/nanoc/base/plugin_registry.rb +57 -19
  12. data/lib/nanoc/base/result_data/item_rep.rb +3 -15
  13. data/lib/nanoc/base/source_data/item.rb +1 -1
  14. data/lib/nanoc/base/source_data/layout.rb +1 -1
  15. data/lib/nanoc/base/source_data/site.rb +15 -19
  16. data/lib/nanoc/cli.rb +28 -23
  17. data/lib/nanoc/cli/command_runner.rb +4 -0
  18. data/lib/nanoc/cli/commands/autocompile.rb +15 -6
  19. data/lib/nanoc/cli/commands/check.rb +47 -0
  20. data/lib/nanoc/cli/commands/compile.rb +271 -195
  21. data/lib/nanoc/cli/commands/create-site.rb +5 -5
  22. data/lib/nanoc/cli/commands/deploy.rb +16 -4
  23. data/lib/nanoc/cli/commands/prune.rb +3 -3
  24. data/lib/nanoc/cli/commands/show-data.rb +73 -58
  25. data/lib/nanoc/cli/commands/show-rules.rb +1 -1
  26. data/lib/nanoc/cli/commands/validate-css.rb +2 -3
  27. data/lib/nanoc/cli/commands/validate-html.rb +2 -3
  28. data/lib/nanoc/cli/commands/validate-links.rb +5 -11
  29. data/lib/nanoc/cli/commands/view.rb +1 -1
  30. data/lib/nanoc/cli/commands/watch.rb +38 -20
  31. data/lib/nanoc/cli/error_handler.rb +122 -122
  32. data/lib/nanoc/data_sources.rb +2 -0
  33. data/lib/nanoc/data_sources/filesystem_unified.rb +1 -1
  34. data/lib/nanoc/data_sources/filesystem_verbose.rb +1 -1
  35. data/lib/nanoc/data_sources/static.rb +60 -0
  36. data/lib/nanoc/extra.rb +2 -0
  37. data/lib/nanoc/extra/checking.rb +16 -0
  38. data/lib/nanoc/extra/checking/check.rb +33 -0
  39. data/lib/nanoc/extra/checking/checks.rb +19 -0
  40. data/lib/nanoc/extra/checking/checks/css.rb +23 -0
  41. data/lib/nanoc/extra/checking/checks/external_links.rb +149 -0
  42. data/lib/nanoc/extra/checking/checks/html.rb +24 -0
  43. data/lib/nanoc/extra/checking/checks/internal_links.rb +57 -0
  44. data/lib/nanoc/extra/checking/checks/stale.rb +23 -0
  45. data/lib/nanoc/extra/checking/dsl.rb +31 -0
  46. data/lib/nanoc/extra/checking/issue.rb +19 -0
  47. data/lib/nanoc/extra/checking/runner.rb +130 -0
  48. data/lib/nanoc/extra/link_collector.rb +57 -0
  49. data/lib/nanoc/extra/pruner.rb +1 -1
  50. data/lib/nanoc/extra/validators/links.rb +5 -262
  51. data/lib/nanoc/extra/validators/w3c.rb +8 -76
  52. data/lib/nanoc/filters/colorize_syntax.rb +1 -1
  53. data/lib/nanoc/filters/handlebars.rb +2 -2
  54. data/lib/nanoc/filters/less.rb +1 -1
  55. data/lib/nanoc/filters/redcarpet.rb +1 -1
  56. data/lib/nanoc/filters/rubypants.rb +1 -1
  57. data/lib/nanoc/filters/slim.rb +1 -1
  58. data/lib/nanoc/helpers/blogging.rb +163 -104
  59. data/lib/nanoc/helpers/xml_sitemap.rb +9 -3
  60. data/tasks/doc.rake +2 -1
  61. data/test/base/core_ext/array_spec.rb +4 -4
  62. data/test/base/core_ext/hash_spec.rb +7 -7
  63. data/test/base/core_ext/pathname_spec.rb +12 -2
  64. data/test/base/test_compiler_dsl.rb +24 -0
  65. data/test/base/test_item_rep.rb +58 -26
  66. data/test/cli/commands/test_watch.rb +0 -8
  67. data/test/data_sources/test_static.rb +66 -0
  68. data/test/extra/checking/checks/test_css.rb +40 -0
  69. data/test/extra/checking/checks/test_external_links.rb +76 -0
  70. data/test/extra/checking/checks/test_html.rb +40 -0
  71. data/test/extra/checking/checks/test_internal_links.rb +46 -0
  72. data/test/extra/checking/checks/test_stale.rb +49 -0
  73. data/test/extra/checking/test_check.rb +16 -0
  74. data/test/extra/checking/test_dsl.rb +20 -0
  75. data/test/extra/checking/test_runner.rb +15 -0
  76. data/test/extra/test_link_collector.rb +93 -0
  77. data/test/extra/validators/test_links.rb +0 -64
  78. data/test/extra/validators/test_w3c.rb +20 -26
  79. data/test/filters/test_colorize_syntax.rb +15 -0
  80. data/test/filters/test_less.rb +14 -0
  81. data/test/filters/test_pandoc.rb +5 -1
  82. data/test/helpers/test_blogging.rb +52 -8
  83. data/test/helpers/test_xml_sitemap.rb +68 -0
  84. data/test/test_gem.rb +1 -1
  85. metadata +31 -6
@@ -35,12 +35,16 @@ module Nanoc::Helpers
35
35
  #
36
36
  # @option params [Array] :items A list of items to include in the sitemap
37
37
  #
38
+ # @option params [Proc] :rep_select A proc to filter reps through. If the
39
+ # proc returns true, the rep will be included; otherwise, it will not.
40
+ #
38
41
  # @return [String] The XML sitemap
39
42
  def xml_sitemap(params={})
40
43
  require 'builder'
41
44
 
42
45
  # Extract parameters
43
- items = params.fetch(:items) { @items.reject { |i| i[:is_hidden] } }
46
+ items = params.fetch(:items) { @items.reject { |i| i[:is_hidden] } }
47
+ select_proc = params.fetch(:rep_select, nil)
44
48
 
45
49
  # Create builder
46
50
  buffer = ''
@@ -55,8 +59,10 @@ module Nanoc::Helpers
55
59
  xml.instruct!
56
60
  xml.urlset(:xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9') do
57
61
  # Add item
58
- items.each do |item|
59
- item.reps.reject { |r| r.raw_path.nil? }.each do |rep|
62
+ items.sort_by { |i| i.identifier }.each do |item|
63
+ reps = item.reps.reject { |r| r.raw_path.nil? }
64
+ reps.reject! { |r| !select_proc[r] } if select_proc
65
+ reps.sort_by { |r| r.name.to_s }.each do |rep|
60
66
  xml.url do
61
67
  xml.loc @site.config[:base_url] + rep.path
62
68
  xml.lastmod item[:mtime].to_iso8601_date unless item[:mtime].nil?
@@ -11,6 +11,7 @@ YARD::Rake::YardocTask.new(:doc) do |yard|
11
11
  '--readme', 'README.md',
12
12
  '--files', 'NEWS.md,LICENSE',
13
13
  '--output-dir', 'doc/yardoc',
14
- '--template-path', 'doc/yardoc_templates'
14
+ '--template-path', 'doc/yardoc_templates',
15
+ '--load', 'doc/yardoc_handlers/identifier.rb'
15
16
  ]
16
17
  end
@@ -1,21 +1,21 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe 'Array#symbolize_keys' do
3
+ describe 'Array#symbolize_keys_recursively' do
4
4
 
5
5
  it 'should convert keys to symbols' do
6
6
  array_old = [ :abc, 'xyz', { 'foo' => 'bar', :baz => :qux } ]
7
7
  array_new = [ :abc, 'xyz', { :foo => 'bar', :baz => :qux } ]
8
- array_old.symbolize_keys.must_equal array_new
8
+ array_old.symbolize_keys_recursively.must_equal array_new
9
9
  end
10
10
 
11
11
  end
12
12
 
13
- describe 'Array#stringify_keys' do
13
+ describe 'Array#stringify_keys_recursively' do
14
14
 
15
15
  it 'should convert keys to strings' do
16
16
  array_old = [ :abc, 'xyz', { :foo => 'bar', 'baz' => :qux } ]
17
17
  array_new = [ :abc, 'xyz', { 'foo' => 'bar', 'baz' => :qux } ]
18
- array_old.stringify_keys.must_equal array_new
18
+ array_old.stringify_keys_recursively.must_equal array_new
19
19
  end
20
20
 
21
21
  end
@@ -1,39 +1,39 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe 'Hash#symbolize_keys' do
3
+ describe 'Hash#symbolize_keys_recursively' do
4
4
 
5
5
  it 'should convert keys to symbols' do
6
6
  hash_old = { 'foo' => 'bar' }
7
7
  hash_new = { :foo => 'bar' }
8
- hash_old.symbolize_keys.must_equal hash_new
8
+ hash_old.symbolize_keys_recursively.must_equal hash_new
9
9
  end
10
10
 
11
11
  end
12
12
 
13
- describe 'Hash#stringify_keys' do
13
+ describe 'Hash#stringify_keys_recursively' do
14
14
 
15
15
  it 'should leave strings as strings' do
16
16
  hash_old = { 'foo' => 'bar' }
17
17
  hash_new = { 'foo' => 'bar' }
18
- hash_old.stringify_keys.must_equal hash_new
18
+ hash_old.stringify_keys_recursively.must_equal hash_new
19
19
  end
20
20
 
21
21
  it 'should convert symbols to strings' do
22
22
  hash_old = { :foo => 'bar' }
23
23
  hash_new = { 'foo' => 'bar' }
24
- hash_old.stringify_keys.must_equal hash_new
24
+ hash_old.stringify_keys_recursively.must_equal hash_new
25
25
  end
26
26
 
27
27
  it 'should convert integers to strings' do
28
28
  hash_old = { 123 => 'bar' }
29
29
  hash_new = { '123' => 'bar' }
30
- hash_old.stringify_keys.must_equal hash_new
30
+ hash_old.stringify_keys_recursively.must_equal hash_new
31
31
  end
32
32
 
33
33
  it 'should convert nil to an empty string' do
34
34
  hash_old = { nil => 'bar' }
35
35
  hash_new = { '' => 'bar' }
36
- hash_old.stringify_keys.must_equal hash_new
36
+ hash_old.stringify_keys_recursively.must_equal hash_new
37
37
  end
38
38
 
39
39
  end
@@ -4,10 +4,15 @@ describe 'Pathname#checksum' do
4
4
 
5
5
  it 'should work on empty files' do
6
6
  begin
7
+ # Create file
7
8
  FileUtils.mkdir_p('tmp')
8
9
  File.open('tmp/myfile', 'w') { |io| io.write('') }
10
+ now = Time.now
11
+ File.utime(now, now, 'tmp/myfile')
12
+
13
+ # Create checksum
9
14
  pathname = Pathname.new('tmp/myfile')
10
- pathname.checksum.must_equal 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
15
+ pathname.checksum.must_equal '0-' + now.to_s
11
16
  ensure
12
17
  FileUtils.rm_rf('tmp')
13
18
  end
@@ -15,10 +20,15 @@ describe 'Pathname#checksum' do
15
20
 
16
21
  it 'should work on all files' do
17
22
  begin
23
+ # Create file
18
24
  FileUtils.mkdir_p('tmp')
19
25
  File.open('tmp/myfile', 'w') { |io| io.write('abc') }
26
+ now = Time.now
27
+ File.utime(now, now, 'tmp/myfile')
28
+
29
+ # Create checksum
20
30
  pathname = Pathname.new('tmp/myfile')
21
- pathname.checksum.must_equal 'a9993e364706816aba3e25717850c26c9cd0d89d'
31
+ pathname.checksum.must_equal '3-' + now.to_s
22
32
  ensure
23
33
  FileUtils.rm_rf('tmp')
24
34
  end
@@ -16,6 +16,30 @@ class Nanoc::CompilerDSLTest < MiniTest::Unit::TestCase
16
16
  # TODO implement
17
17
  end
18
18
 
19
+ def test_include_rules
20
+ # Create site
21
+ Nanoc::CLI.run %w( create_site with_bonus_rules )
22
+ FileUtils.cd('with_bonus_rules') do
23
+ # Create rep
24
+ item = Nanoc::Item.new('foo', { :extension => 'bar' }, '/foo/')
25
+ rep = Nanoc::ItemRep.new(item, :default)
26
+
27
+ # Create a bonus rules file
28
+ File.open('more_rules.rb', 'w') { |io| io.write "passthrough '/foo/'" }
29
+
30
+ # Create other necessary stuff
31
+ site = Nanoc::Site.new('.')
32
+ site.items << item
33
+ dsl = site.compiler.rules_collection.dsl
34
+
35
+ # Include rules
36
+ dsl.include_rules 'more_rules'
37
+
38
+ # Check that the rule made it into the collection
39
+ refute_nil site.compiler.rules_collection.routing_rule_for(rep)
40
+ end
41
+ end
42
+
19
43
  def test_passthrough
20
44
  with_site do
21
45
  # Create rules
@@ -221,32 +221,6 @@ class Nanoc::ItemRepTest < MiniTest::Unit::TestCase
221
221
  assert_equal a_long_time_ago.to_s, File.mtime(item_rep.raw_path).to_s
222
222
  end
223
223
 
224
- def test_write_should_not_touch_identical_binary_files
225
- # Create temporary source file
226
- File.open('blahblah', 'w') { |io| io.write("Blah blah…") }
227
- full_file_path = File.expand_path('blahblah')
228
-
229
- # Mock item
230
- item = Nanoc::Item.new(
231
- full_file_path, {}, '/',
232
- :binary => true
233
- )
234
-
235
- # Create rep
236
- item_rep = Nanoc::ItemRep.new(item, :foo)
237
- item_rep.raw_path = 'foo/bar/baz/quux'
238
-
239
- # Write once
240
- item_rep.write
241
- a_long_time_ago = Time.now-1_000_000
242
- File.utime(a_long_time_ago, a_long_time_ago, item_rep.raw_path)
243
-
244
- # Write again
245
- assert_equal a_long_time_ago.to_s, File.mtime(item_rep.raw_path).to_s
246
- item_rep.write
247
- assert_equal a_long_time_ago.to_s, File.mtime(item_rep.raw_path).to_s
248
- end
249
-
250
224
  def test_write
251
225
  # Mock item
252
226
  item = Nanoc::Item.new(
@@ -536,6 +510,64 @@ class Nanoc::ItemRepTest < MiniTest::Unit::TestCase
536
510
  end
537
511
  end
538
512
 
513
+ def test_write_should_calculate_is_modified_correctly_for_binary_items_new
514
+ # Mock item
515
+ FileUtils.mkdir_p('content')
516
+ File.open('content/meow.dat', 'w') { |io| io.write('asdf') }
517
+ item = Nanoc::Item.new(
518
+ "content/meow.dat", {}, '/',
519
+ :binary => true
520
+ )
521
+
522
+ # Create rep
523
+ item_rep = Nanoc::ItemRep.new(item, :foo)
524
+ FileUtils.mkdir_p('tmp')
525
+ File.open('tmp/woof.dat', 'w') { |io| io.write('fdsa') }
526
+ item_rep.instance_eval { @temporary_filenames[:last] = 'tmp/woof.dat' }
527
+ item_rep.raw_path = 'output/woof.dat'
528
+
529
+ # Write
530
+ notified = false
531
+ Nanoc::NotificationCenter.on(:rep_written, self) do |rep, raw_path, is_created, is_modified|
532
+ notified = true
533
+ assert is_created
534
+ assert is_modified
535
+ end
536
+ item_rep.write
537
+ assert notified
538
+ Nanoc::NotificationCenter.remove(:rep_written, self)
539
+ end
540
+
541
+ def test_write_should_calculate_is_modified_correctly_for_binary_items_existing
542
+ # Mock item
543
+ FileUtils.mkdir_p('content')
544
+ File.open('content/meow.dat', 'w') { |io| io.write('asdf') }
545
+ item = Nanoc::Item.new(
546
+ "content/meow.dat", {}, '/',
547
+ :binary => true
548
+ )
549
+
550
+ # Create rep
551
+ item_rep = Nanoc::ItemRep.new(item, :foo)
552
+ FileUtils.mkdir_p('tmp')
553
+ File.open('tmp/woof.dat', 'w') { |io| io.write('fdsa') }
554
+ FileUtils.mkdir_p('output')
555
+ File.open('output/woof.dat', 'w') { |io| io.write('fdsa but different') }
556
+ item_rep.instance_eval { @temporary_filenames[:last] = 'tmp/woof.dat' }
557
+ item_rep.raw_path = 'output/woof.dat'
558
+
559
+ # Write
560
+ notified = false
561
+ Nanoc::NotificationCenter.on(:rep_written, self) do |rep, raw_path, is_created, is_modified|
562
+ notified = true
563
+ refute is_created
564
+ assert is_modified
565
+ end
566
+ item_rep.write
567
+ assert notified
568
+ Nanoc::NotificationCenter.remove(:rep_written, self)
569
+ end
570
+
539
571
  private
540
572
 
541
573
  def create_binary_item
@@ -4,14 +4,6 @@ class Nanoc::CLI::Commands::WatchTest < MiniTest::Unit::TestCase
4
4
 
5
5
  include Nanoc::TestHelpers
6
6
 
7
- def setup
8
- super
9
- @@warned ||= begin
10
- STDERR.puts "\n(fssm deprecation warning can be ignored; master branch uses guard/listen)"
11
- true
12
- end
13
- end
14
-
15
7
  def test_run
16
8
  with_site do |s|
17
9
  watch_thread = Thread.new do
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ class Nanoc::DataSources::StaticTest < MiniTest::Unit::TestCase
4
+
5
+ include Nanoc::TestHelpers
6
+
7
+ def new_data_source(params=nil)
8
+ # Mock site
9
+ site = Nanoc::Site.new({})
10
+
11
+ # Create data source
12
+ data_source = Nanoc::DataSources::Static.new(site, nil, nil, params)
13
+
14
+ # Done
15
+ data_source
16
+ end
17
+
18
+ def test_items
19
+ # Create data source
20
+ data_source = new_data_source(:prefix => 'foo')
21
+
22
+ # Create sample files
23
+ FileUtils.mkdir_p('foo')
24
+ FileUtils.mkdir_p('foo/a/b')
25
+ File.open('foo/bar.png', 'w') { |io| io.write("random binary data") }
26
+ File.open('foo/b.c.css', 'w') { |io| io.write("more binary data") }
27
+ File.open('foo/a/b/c.gif', 'w') { |io| io.write("yet more binary data") }
28
+
29
+ # Get expected and actual output
30
+ expected_out = [
31
+ Nanoc::Item.new(
32
+ 'foo/bar.png',
33
+ { :extension => 'png', :filename => 'foo/bar.png' },
34
+ '/bar.png/',
35
+ :binary => true,
36
+ :mtime => File.mtime('foo/bar.png'),
37
+ :checksum => Pathname.new('foo/bar.png').checksum
38
+ ),
39
+ Nanoc::Item.new(
40
+ 'foo/b.c.css',
41
+ { :extension => 'css', :filename => 'foo/b.c.css' },
42
+ '/b.c.css/',
43
+ :binary => true,
44
+ :mtime => File.mtime('foo/b.c.css'),
45
+ :checksum => Pathname.new('foo/b.c.css').checksum
46
+ ),
47
+ Nanoc::Item.new(
48
+ 'foo/a/b/c.gif',
49
+ { :extension => 'gif', :filename => 'foo/a/b/c.gif' },
50
+ '/a/b/c.gif/',
51
+ :binary => true,
52
+ :mtime => File.mtime('foo/a/b/c.gif'),
53
+ :checksum => Pathname.new('foo/a/b/c.gif').checksum
54
+ )
55
+ ].sort_by { |i| i.identifier }
56
+
57
+ actual_out = data_source.send(:items).sort_by { |i| i.identifier }
58
+
59
+ (0..expected_out.size-1).each do |i|
60
+ assert_equal expected_out[i].raw_content, actual_out[i].raw_content, 'content must match'
61
+ assert_equal expected_out[i].identifier, actual_out[i].identifier, 'identifier must match'
62
+ assert_equal expected_out[i].mtime, actual_out[i].mtime, 'mtime must match'
63
+ assert_equal expected_out[i].raw_filename, actual_out[i].raw_filename, 'file paths must match'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ class Nanoc::Extra::Checking::Checks::CSSTest < MiniTest::Unit::TestCase
4
+
5
+ include Nanoc::TestHelpers
6
+
7
+ def test_run_ok
8
+ with_site do |site|
9
+ # Create files
10
+ FileUtils.mkdir_p('output')
11
+ File.open('output/blah.html', 'w') { |io| io.write('<h1>Hi!</h1>') }
12
+ File.open('output/style.css', 'w') { |io| io.write('h1 { color: red; }') }
13
+
14
+ # Run check
15
+ check = Nanoc::Extra::Checking::Checks::CSS.new(site)
16
+ check.run
17
+
18
+ # Check
19
+ assert check.issues.empty?
20
+ end
21
+ end
22
+
23
+ def test_run_error
24
+ with_site do |site|
25
+ # Create files
26
+ FileUtils.mkdir_p('output')
27
+ File.open('output/blah.html', 'w') { |io| io.write('<h1>Hi!</h1>') }
28
+ File.open('output/style.css', 'w') { |io| io.write('h1 { coxlor: rxed; }') }
29
+
30
+ # Run check
31
+ check = Nanoc::Extra::Checking::Checks::CSS.new(site)
32
+ check.run
33
+
34
+ # Check
35
+ refute check.issues.empty?
36
+ end
37
+ end
38
+
39
+ end
40
+
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ class Nanoc::Extra::Checking::Checks::ExternalLinksTest < MiniTest::Unit::TestCase
4
+
5
+ include Nanoc::TestHelpers
6
+
7
+ def setup
8
+ super
9
+ require 'timeout'
10
+ end
11
+
12
+ def test_run
13
+ with_site do |site|
14
+ # Create files
15
+ FileUtils.mkdir_p('output')
16
+ FileUtils.mkdir_p('output/stuff')
17
+ File.open('output/foo.txt', 'w') { |io| io.write('<a href="http://example.com/404">broken</a>') }
18
+ File.open('output/bar.html', 'w') { |io| io.write('<a href="http://example.com/">not broken</a>') }
19
+
20
+ # Create check
21
+ check = Nanoc::Extra::Checking::Checks::InternalLinks.new(site)
22
+ check.run
23
+
24
+ # Test
25
+ assert check.issues.empty?
26
+ end
27
+ end
28
+
29
+ def test_valid?
30
+ with_site do |site|
31
+ # Create files
32
+ FileUtils.mkdir_p('output')
33
+ FileUtils.mkdir_p('output/stuff')
34
+ File.open('output/origin', 'w') { |io| io.write('hi') }
35
+ File.open('output/foo', 'w') { |io| io.write('hi') }
36
+ File.open('output/stuff/blah', 'w') { |io| io.write('hi') }
37
+
38
+ # Create check
39
+ check = Nanoc::Extra::Checking::Checks::ExternalLinks.new(site)
40
+
41
+ # Test
42
+ self.run_server_while do
43
+ assert ok?(check, 'http://127.0.0.1:9204/200')
44
+ assert ok?(check, 'foo://example.com/')
45
+ refute ok?(check, 'http://127.0.0.1:9204">')
46
+ end
47
+ end
48
+ end
49
+
50
+ def ok?(check, url)
51
+ Timeout.timeout(3) do
52
+ check.validate(url).nil?
53
+ end
54
+ end
55
+
56
+ def run_server_while
57
+ @app = lambda { |env| [ env['REQUEST_PATH'][1..-1].to_i, {}, [ '... Useless body ...' ] ] }
58
+ @server = nil
59
+
60
+ @thread = Thread.new do
61
+ Rack::Handler::WEBrick.run(@app, :Host => @host='127.0.0.1', :Port => @port=9204) do |server|
62
+ @server = server
63
+ end
64
+ end
65
+
66
+ Timeout::timeout(5) do
67
+ Thread.pass until @server
68
+ end
69
+
70
+ yield
71
+
72
+ @server.stop
73
+ @thread.kill
74
+ end
75
+
76
+ end