waves-edge 2009.03.10.13.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/bin/waves +30 -0
  2. data/doc/HISTORY +1 -0
  3. data/doc/LICENSE +22 -0
  4. data/doc/README +1 -0
  5. data/doc/VERSION +1 -0
  6. data/lib/caches/file.rb +48 -0
  7. data/lib/caches/memcached.rb +40 -0
  8. data/lib/caches/simple.rb +25 -0
  9. data/lib/caches/synchronized.rb +25 -0
  10. data/lib/commands/console.rb +35 -0
  11. data/lib/commands/generate.rb +52 -0
  12. data/lib/commands/help.rb +5 -0
  13. data/lib/commands/server.rb +68 -0
  14. data/lib/dispatchers/base.rb +68 -0
  15. data/lib/dispatchers/default.rb +25 -0
  16. data/lib/ext/float.rb +13 -0
  17. data/lib/ext/hash.rb +31 -0
  18. data/lib/ext/integer.rb +27 -0
  19. data/lib/ext/kernel.rb +20 -0
  20. data/lib/ext/module.rb +20 -0
  21. data/lib/ext/object.rb +33 -0
  22. data/lib/ext/string.rb +20 -0
  23. data/lib/ext/symbol.rb +11 -0
  24. data/lib/ext/tempfile.rb +5 -0
  25. data/lib/foundations/classic.rb +59 -0
  26. data/lib/foundations/compact.rb +52 -0
  27. data/lib/helpers/basic.rb +11 -0
  28. data/lib/helpers/doc_type.rb +34 -0
  29. data/lib/helpers/extended.rb +21 -0
  30. data/lib/helpers/form.rb +42 -0
  31. data/lib/helpers/formatting.rb +30 -0
  32. data/lib/helpers/layouts.rb +37 -0
  33. data/lib/helpers/model.rb +37 -0
  34. data/lib/helpers/view.rb +22 -0
  35. data/lib/layers/inflect/english.rb +35 -0
  36. data/lib/layers/mvc.rb +41 -0
  37. data/lib/layers/mvc/controllers.rb +41 -0
  38. data/lib/layers/mvc/extensions.rb +52 -0
  39. data/lib/layers/orm/migration.rb +79 -0
  40. data/lib/layers/orm/providers/active_record.rb +84 -0
  41. data/lib/layers/orm/providers/active_record/migrations/empty.rb.erb +9 -0
  42. data/lib/layers/orm/providers/active_record/tasks/generate.rb +28 -0
  43. data/lib/layers/orm/providers/active_record/tasks/schema.rb +22 -0
  44. data/lib/layers/orm/providers/data_mapper.rb +37 -0
  45. data/lib/layers/orm/providers/filebase.rb +25 -0
  46. data/lib/layers/orm/providers/sequel.rb +87 -0
  47. data/lib/layers/orm/providers/sequel/migrations/empty.rb.erb +9 -0
  48. data/lib/layers/orm/providers/sequel/tasks/generate.rb +30 -0
  49. data/lib/layers/orm/providers/sequel/tasks/schema.rb +16 -0
  50. data/lib/layers/renderers/erubis.rb +52 -0
  51. data/lib/layers/renderers/haml.rb +67 -0
  52. data/lib/layers/renderers/markaby.rb +41 -0
  53. data/lib/matchers/accept.rb +21 -0
  54. data/lib/matchers/base.rb +30 -0
  55. data/lib/matchers/content_type.rb +17 -0
  56. data/lib/matchers/path.rb +67 -0
  57. data/lib/matchers/query.rb +21 -0
  58. data/lib/matchers/request.rb +27 -0
  59. data/lib/matchers/resource.rb +19 -0
  60. data/lib/matchers/traits.rb +19 -0
  61. data/lib/matchers/uri.rb +20 -0
  62. data/lib/renderers/mixin.rb +13 -0
  63. data/lib/resources/mixin.rb +136 -0
  64. data/lib/resources/paths.rb +132 -0
  65. data/lib/runtime/configuration.rb +100 -0
  66. data/lib/runtime/console.rb +23 -0
  67. data/lib/runtime/logger.rb +35 -0
  68. data/lib/runtime/mime_types.rb +536 -0
  69. data/lib/runtime/mocks.rb +14 -0
  70. data/lib/runtime/monitor.rb +32 -0
  71. data/lib/runtime/request.rb +152 -0
  72. data/lib/runtime/response.rb +43 -0
  73. data/lib/runtime/response_mixin.rb +54 -0
  74. data/lib/runtime/runtime.rb +69 -0
  75. data/lib/runtime/server.rb +20 -0
  76. data/lib/runtime/session.rb +27 -0
  77. data/lib/runtime/worker.rb +86 -0
  78. data/lib/servers/base.rb +42 -0
  79. data/lib/servers/mongrel.rb +13 -0
  80. data/lib/servers/webrick.rb +13 -0
  81. data/lib/tasks/gem.rb +32 -0
  82. data/lib/tasks/generate.rb +85 -0
  83. data/lib/views/errors.rb +49 -0
  84. data/lib/views/mixin.rb +64 -0
  85. data/lib/waves.rb +63 -0
  86. data/samples/blog/Rakefile +25 -0
  87. data/samples/blog/configurations/default.rb +11 -0
  88. data/samples/blog/configurations/development.rb +29 -0
  89. data/samples/blog/configurations/production.rb +26 -0
  90. data/samples/blog/models/comment.rb +23 -0
  91. data/samples/blog/models/entry.rb +31 -0
  92. data/samples/blog/public/css/site.css +13 -0
  93. data/samples/blog/public/javascript/jquery-1.2.6.min.js +32 -0
  94. data/samples/blog/public/javascript/site.js +13 -0
  95. data/samples/blog/resources/entry.rb +39 -0
  96. data/samples/blog/resources/map.rb +9 -0
  97. data/samples/blog/schema/migrations/001_initial_schema.rb +17 -0
  98. data/samples/blog/schema/migrations/002_add_comments.rb +18 -0
  99. data/samples/blog/schema/migrations/templates/empty.rb.erb +9 -0
  100. data/samples/blog/startup.rb +8 -0
  101. data/samples/blog/templates/comment/add.mab +12 -0
  102. data/samples/blog/templates/comment/list.mab +6 -0
  103. data/samples/blog/templates/entry/edit.mab +14 -0
  104. data/samples/blog/templates/entry/list.mab +16 -0
  105. data/samples/blog/templates/entry/show.mab +18 -0
  106. data/samples/blog/templates/entry/summary.mab +9 -0
  107. data/samples/blog/templates/errors/not_found_404.mab +7 -0
  108. data/samples/blog/templates/errors/server_error_500.mab +2 -0
  109. data/samples/blog/templates/layouts/default.mab +19 -0
  110. data/samples/blog/templates/waves/status.mab +85 -0
  111. data/templates/classic/Rakefile +90 -0
  112. data/templates/classic/configurations/default.rb.erb +9 -0
  113. data/templates/classic/configurations/development.rb.erb +26 -0
  114. data/templates/classic/configurations/production.rb.erb +29 -0
  115. data/templates/classic/controllers/.gitignore +0 -0
  116. data/templates/classic/helpers/.gitignore +0 -0
  117. data/templates/classic/lib/tasks/.gitignore +0 -0
  118. data/templates/classic/models/.gitignore +0 -0
  119. data/templates/classic/public/css/.gitignore +0 -0
  120. data/templates/classic/public/flash/.gitignore +0 -0
  121. data/templates/classic/public/images/.gitignore +0 -0
  122. data/templates/classic/public/javascript/.gitignore +0 -0
  123. data/templates/classic/resources/.gitignore +0 -0
  124. data/templates/classic/resources/map.rb.erb +8 -0
  125. data/templates/classic/schema/migrations/.gitignore +0 -0
  126. data/templates/classic/startup.rb.erb +11 -0
  127. data/templates/classic/templates/errors/not_found_404.mab +7 -0
  128. data/templates/classic/templates/errors/server_error_500.mab +7 -0
  129. data/templates/classic/templates/layouts/default.mab +14 -0
  130. data/templates/classic/tmp/sessions/.gitignore +0 -0
  131. data/templates/classic/views/.gitignore +0 -0
  132. data/templates/compact/startup.rb.erb +11 -0
  133. data/test/ext/object.rb +55 -0
  134. data/test/ext/shortcuts.rb +73 -0
  135. data/test/helpers.rb +17 -0
  136. data/test/match/accept.rb +34 -0
  137. data/test/match/methods.rb +22 -0
  138. data/test/match/params.rb +33 -0
  139. data/test/match/path.rb +106 -0
  140. data/test/match/query.rb +40 -0
  141. data/test/process/request.rb +75 -0
  142. data/test/process/resource.rb +53 -0
  143. data/test/resources/path.rb +166 -0
  144. data/test/runtime/configurations.rb +19 -0
  145. data/test/runtime/request.rb +63 -0
  146. data/test/runtime/response.rb +55 -0
  147. data/test/views/views.rb +34 -0
  148. metadata +394 -0
@@ -0,0 +1,9 @@
1
+ module <%= @name %>
2
+ module Configurations
3
+ class Default < Waves::Configurations::Default
4
+
5
+ resource <%= @name %>::Resources::Map
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module <%= @name %>
2
+
3
+ module Configurations
4
+
5
+ class Development < Default
6
+
7
+ database :adapter => 'sqlite', :database => '<%= @name.downcase %>'
8
+
9
+ reloadable [ <%= @name %> ]
10
+ log :level => :debug
11
+ host '127.0.0.1'
12
+ port 3000
13
+ dependencies []
14
+
15
+ application do
16
+ use ::Rack::ShowExceptions
17
+ use ::Rack::Static, :urls => [ '/css/', '/javascript/', '/favicon.ico' ], :root => 'public'
18
+ run ::Waves::Dispatchers::Default.new
19
+ end
20
+
21
+ server Waves::Servers::Mongrel
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,29 @@
1
+ module <%= @name %>
2
+
3
+ module Configurations
4
+
5
+ class Production < Default
6
+
7
+ database :host => 'localhost', :adapter => 'mysql', :database => '<%= @name.downcase %>',
8
+ :user => 'root', :password => ''
9
+
10
+ reloadable []
11
+
12
+ log :level => :error,
13
+ :output => ( :log / "waves.#{$$}" ),
14
+ :rotation => :weekly
15
+
16
+ host '0.0.0.0'
17
+
18
+ port 80
19
+
20
+ application do
21
+ use ::Rack::Static, :urls => [ '/css/', '/javascript/', '/favicon.ico' ], :root => 'public'
22
+ run ::Waves::Dispatchers::Default.new
23
+ end
24
+
25
+ server Waves::Servers::Mongrel
26
+
27
+ end
28
+ end
29
+ end
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ module <%= @name %>
2
+ module Resources
3
+ class Map
4
+ include Waves::Resources::Mixin
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'foundations/classic'
2
+ <% if @orm %>
3
+ require 'layers/orm/providers/<%= @orm %>'
4
+ <% end %>
5
+
6
+ module <%= @name %>
7
+ include Waves::Foundations::Classic
8
+ <% if @orm %>
9
+ include Waves::Layers::ORM::<%= @orm.camel_case %>
10
+ <% end %>
11
+ end
@@ -0,0 +1,7 @@
1
+ style <<-STYLE
2
+ body { background: #933; padding: 20px; font-family: verdana, sans-serif; }
3
+ h1 { font-size: 60px; font-weight: bold; margin: 0px; }
4
+ p { font-size: 24px; margin: 0px; }
5
+ STYLE
6
+ h1 '404'
7
+ p { 'That URL does not exist on this server.' }
@@ -0,0 +1,7 @@
1
+ style <<-STYLE
2
+ body { background: #c93; padding: 20px; font-family: verdana, sans-serif; }
3
+ h1 { font-size: 60px; font-weight: bold; margin: 0px; }
4
+ p { font-size: 24px; margin: 0px; }
5
+ STYLE
6
+ h1 '500'
7
+ p %q( Internal server error. Sorry, but your request could not be processed. )
@@ -0,0 +1,14 @@
1
+ doctype :html4_strict
2
+
3
+ html do
4
+
5
+ head do
6
+ title @title
7
+ end
8
+
9
+ body do
10
+ layout_content
11
+ end
12
+
13
+ end
14
+
File without changes
@@ -0,0 +1,11 @@
1
+ require 'foundations/compact'
2
+ <% if @orm %>
3
+ require 'layers/orm/<%= @orm %>'
4
+ <% end %>
5
+
6
+ module <%= @name %>
7
+ include Waves::Foundations::Compact
8
+ <% if @orm %>
9
+ include Waves::Layers::ORM::<%= @orm.camel_case %>
10
+ <% end %>
11
+ end
@@ -0,0 +1,55 @@
1
+ require "#{File.dirname(__FILE__)}/../helpers.rb"
2
+
3
+ describe "Object#cache_method_missing" do
4
+
5
+ before do
6
+ class A; end
7
+ end
8
+
9
+ after do
10
+ Object.instance_eval { remove_const(:A) if const_defined?(:A) }
11
+ end
12
+
13
+ it "defines the missing method" do
14
+ A.module_eval do
15
+ def method_missing(name, *args)
16
+ cache_method_missing name, "'hi'", *args
17
+ end
18
+ end
19
+ A.new.bar
20
+ A.new.should.respond_to :bar
21
+ end
22
+
23
+ it "passes along the args" do
24
+ A.module_eval do
25
+ def method_missing(name, *args, &block)
26
+ cache_method_missing name, "args.join('-')", *args
27
+ end
28
+ end
29
+
30
+ A.new.bar(1, 2, 3).should == "1-2-3"
31
+ end
32
+
33
+ it "passes along the block" do
34
+ A.module_eval do
35
+ def method_missing(name, *args, &block)
36
+ cache_method_missing name, "block.call", *args, &block
37
+ end
38
+ end
39
+
40
+ A.new.bar { 'bye' }.should == 'bye'
41
+ end
42
+
43
+ end
44
+
45
+ describe "Object#instance_exec" do
46
+
47
+ before do
48
+ @block = lambda { |guy| "Howdy, #{guy}!" }
49
+ end
50
+
51
+ it "works like instance_eval, but it takes args and gives them to the block" do
52
+ instance_exec( "Steve", &@block ).should == "Howdy, Steve!"
53
+ end
54
+
55
+ end
@@ -0,0 +1,73 @@
1
+ require "#{File.dirname(__FILE__)}/../helpers.rb"
2
+
3
+ describe "Waves::Ext::String" do
4
+ it "defines / as syntactic sugar for File.join" do
5
+ ( "lib" / "utilities" ).should == File.join( "lib", "utilities" )
6
+ ( "lib" / :utilities ).should == File.join( "lib", "utilities" )
7
+ ( "lib" / 3 ).should == File.join( "lib", "3" )
8
+ end
9
+ end
10
+
11
+ describe "A monkeypatch to Symbol" do
12
+
13
+ it "defines / as syntactic sugar for File.join" do
14
+ ( :lib / :utilities ).should == File.join( "lib", "utilities")
15
+ end
16
+
17
+ end
18
+
19
+ describe "Waves::Ext::Hash" do
20
+
21
+ it "adds a non-destructive method for converting all hash keys to strings" do
22
+ h = { :a => 1, 'b' => 2, 3 => 3}
23
+ h.stringify_keys.should == { 'a' => 1, 'b' => 2, '3' => 3}
24
+ h.should == { :a => 1, 'b' => 2, 3 => 3}
25
+ end
26
+
27
+ it "adds a destructive method for converting hash keys to symbols" do
28
+ h = { "two" => 2, :three => 3}
29
+ h.symbolize_keys!
30
+ h.should == { :two => 2, :three => 3 }
31
+ end
32
+
33
+ end
34
+
35
+ describe "Waves::Ext::Integer" do
36
+
37
+ it "has an absolutely pedantic amount of helpers" do
38
+ 1.kilobytes.should == "1_024".to_i
39
+ 1.megabytes.should == "1_048_576".to_i
40
+ 1.gigabytes.should == "1_073_741_824".to_i
41
+ 1.terabytes.should == "1_099_511_627_776".to_i
42
+ 1.petabytes.should == "1_125_899_906_842_624".to_i
43
+ 1.exabytes.should == "1_152_921_504_606_846_976".to_i
44
+ 1.zettabytes.should == "1_180_591_620_717_411_303_424".to_i
45
+ 1.yottabytes.should == "1_208_925_819_614_629_174_706_176".to_i
46
+ end
47
+
48
+ end
49
+
50
+ describe "Waves::Ext::Module" do
51
+
52
+ before do
53
+ module Eenie; module Meenie; module Miney; end; end ; end
54
+ end
55
+
56
+ it "defines a basename method" do
57
+ Eenie::Meenie.basename.should == "Meenie"
58
+ Eenie::Meenie::Miney.basename.should == "Miney"
59
+ end
60
+
61
+ it "defines [] for easy access to namespaced constants" do
62
+ Eenie[:Meenie].should == Eenie::Meenie
63
+ end
64
+
65
+ it "defines a method for obtaining the outermost constant name" do
66
+ Eenie::Meenie::Miney.rootname.should == "Eenie"
67
+ end
68
+
69
+ it "defines a method for obtaining the outermost constant" do
70
+ Eenie::Meenie::Miney.root.should == Eenie
71
+ end
72
+
73
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'; %w{ bacon facon }.each { |dep| require dep }
2
+
3
+ # Framework lib goes to the front of the loadpath
4
+ $:.unshift('lib')
5
+ require 'waves'
6
+ require 'runtime/mocks'
7
+
8
+ Waves::Runtime.instance = Waves::Runtime.new
9
+
10
+ Bacon::Context.module_eval do
11
+ include Waves::Mocks
12
+ alias_method :specification, :describe
13
+ alias_method :feature, :it
14
+ end
15
+
16
+ Bacon.extend Bacon::TestUnitOutput
17
+ Bacon.summary_on_exit
@@ -0,0 +1,34 @@
1
+ require "#{File.dirname(__FILE__)}/../../test/helpers.rb"
2
+ require 'foundations/compact'
3
+
4
+ describe "Matching The Accepts Header" do
5
+
6
+ before do
7
+ Test = Module.new { include Waves::Foundations::Compact }
8
+ Waves << Test
9
+ end
10
+
11
+ after do
12
+ Waves.applications.clear
13
+ Object.instance_eval { remove_const(:Test) if const_defined?(:Test) }
14
+ end
15
+
16
+ feature "Match an implied accept header using the file extension" do
17
+ Test::Resources::Map.on( :get, true, :accept => :javascript ) {}
18
+ get("/foo.js").status.should == 200
19
+ get("/foo").status.should == 404
20
+ end
21
+
22
+ feature "Match against an array of options" do
23
+ Test::Resources::Map.on( :get, true, :accept => [ :javascript, :css ] ) {}
24
+ get("/foo.js").status.should == 200
25
+ get("/foo").status.should == 404
26
+ end
27
+
28
+ feature "Match against a Mime type (rather than subtype)" do
29
+ Test::Resources::Map.on( :get, true, :accept => :image ) {}
30
+ get("/foo.png").status.should == 200
31
+ get("/foo").status.should == 404
32
+ end
33
+
34
+ end
@@ -0,0 +1,22 @@
1
+ require "#{File.dirname(__FILE__)}/../../test/helpers.rb"
2
+ require 'foundations/compact'
3
+
4
+ describe "Matching Request Methods" do
5
+
6
+ before do
7
+ Test = Module.new { include Waves::Foundations::Compact }
8
+ Test::Resources::Map.module_eval {
9
+ %w( get put post delete head ).each { |m| on( m ) { m } } }
10
+ Waves << Test
11
+ end
12
+
13
+ after do
14
+ Waves.applications.clear
15
+ Object.instance_eval { remove_const(:Test) if const_defined?(:Test) }
16
+ end
17
+
18
+ %w( get put post delete head ).each do |m|
19
+ feature( "Match the '#{m}' method" ) { send( m, '/' ).body.should == m }
20
+ end
21
+
22
+ end
@@ -0,0 +1,33 @@
1
+ require "#{File.dirname(__FILE__)}/../../test/helpers.rb"
2
+ require 'foundations/compact'
3
+
4
+ describe "A resource that has matched a request" do
5
+
6
+ before do
7
+ Test = Module.new { include Waves::Foundations::Compact }
8
+ Waves << Test
9
+
10
+ @request = Waves::Request.new( env('http://localhost/baz?bar=7', :method => 'GET' ) )
11
+ Test::Resources::Map.on( :get, [ :foo ] ) {}
12
+ @resource = Test::Resources::Map.new( @request )
13
+ @resource.get
14
+ end
15
+
16
+ after do
17
+ Waves.applications.clear
18
+ Object.instance_eval { remove_const(:Test) if const_defined?(:Test) }
19
+ end
20
+
21
+ it "makes parameters captured from the path available as #captured" do
22
+ @resource.captured.to_h.should == { 'foo' => 'baz' }
23
+ end
24
+
25
+ it "makes http query parameters available as #query" do
26
+ @resource.query.to_h.should == { 'bar' => '7' }
27
+ end
28
+
29
+ it "provides access to all derived parameters using #params" do
30
+ @resource.params.to_h.should == { 'foo' => 'baz', 'bar' => '7' }
31
+ end
32
+
33
+ end
@@ -0,0 +1,106 @@
1
+ require "#{File.dirname(__FILE__)}/../../test/helpers.rb"
2
+ require "foundations/compact"
3
+
4
+ describe "Matching Request URIs" do
5
+
6
+ before do
7
+ Test = Module.new { include Waves::Foundations::Compact }
8
+ Waves << Test
9
+ end
10
+
11
+ after do
12
+ Waves.applications.clear
13
+ Object.instance_eval { remove_const(:Test) if const_defined?(:Test) }
14
+ end
15
+
16
+ feature "By default, we match an arbitrary path." do
17
+ Test::Resources::Map.on( :get ) {}
18
+ get("/foobar").status.should == 200
19
+ get("/foo/bar").status.should == 200
20
+ end
21
+
22
+ feature "A path of true matches an arbitrary path." do
23
+ Test::Resources::Map.on( :get, true ) { request.path }
24
+ get("/foobar").body.should == '/foobar'
25
+ get("/foo/bar").body.should == '/foo/bar'
26
+ end
27
+
28
+ feature "An empty path matches root." do
29
+ Test::Resources::Map.on( :get, [] ) { request.path }
30
+ get("/").body.should == '/'
31
+ end
32
+
33
+ feature "A string path component matches that string." do
34
+ Test::Resources::Map.on( :get, [ 'foo' ] ) { request.path }
35
+ get("/foo").body.should == '/foo'
36
+ get("/bar").status.should == 404
37
+ end
38
+
39
+ feature "A symbol path component captures that component using the symbol as a key." do
40
+ Test::Resources::Map.on( :get, [ :foo ] ) { captured[:foo] }
41
+ get("/foo").body.should == 'foo'
42
+ get("/bar").body.should == 'bar'
43
+ end
44
+
45
+ feature "A regexp path component matches the any component that matches the regexp." do
46
+ Test::Resources::Map.on( :get, [ /\d+/ ] ) { request.path }
47
+ get("/1234").body.should == '/1234'
48
+ get("/foo").status.should == 404
49
+ end
50
+
51
+ feature "A path component of true matches the remaining path." do
52
+ Test::Resources::Map.on( :get, [ 'foo', true ] ) { request.path }
53
+ get("/foo/bar/baz").body.should == '/foo/bar/baz'
54
+ get("/foo").status.should == 404
55
+ end
56
+
57
+ [["one or more", 1..-1, false, true, true],
58
+ ["zero or more", 0..-1, true, true, true],
59
+ ["one to N (N=2)", 1..2, false, true, true, false],
60
+ ["one to N (N=3)", 1..3, false, true, true, true, false]
61
+ ].each do |name, range, *tests|
62
+ feature "A Range path component can match #{name} components" do
63
+ Test::Resources::Map.on(:get, ['foo', range]) { request.path }
64
+ tests.size.times do |i|
65
+ get("/foo" * ( i + 1 ) ).status.should == (tests[i] ? 200 : 404)
66
+ end
67
+ end
68
+ end
69
+
70
+ feature "A path component can use a hash with a value of true to capture the remaining path." do
71
+ Test::Resources::Map.on( :get, [ 'foo', { :rest => true } ] ) { captured[:rest] * ' ' }
72
+ get("/foo/bar/baz").body.should == 'bar baz'
73
+ end
74
+
75
+ [["one or more", 1..-1, false, 'foo', 'foo foo'],
76
+ ["zero or more", 0..-1, '', 'foo', 'foo foo'],
77
+ ["one to N (N=2)", 1..2, false, 'foo', 'foo foo', false],
78
+ ["one to N (N=3)", 0..3, '', 'foo', 'foo foo', 'foo foo foo', false]
79
+ ].each do |name, range, *tests|
80
+ feature "A Range path component can match #{name} components" do
81
+ Test::Resources::Map.on(:get, ['foo', { :rest => range }]) { captured.rest * ' ' }
82
+ tests.size.times do |i|
83
+ response = get("/foo" * ( i + 1 ) )
84
+ tests[i] ? response.body.should == tests[i] : response.status.should == 404
85
+ end
86
+ end
87
+ end
88
+
89
+ feature "A path component can use a hash with a string value to provide a default." do
90
+ Test::Resources::Map.on( :get, [ 'foo', { :bar => 'bar' } ] ) { captured[:bar] }
91
+ get("/foo/baz").body.should == 'baz'
92
+ get("/foo").body.should == 'bar'
93
+ end
94
+
95
+ feature "A path component can use a hash with a regexp value to match and capture." do
96
+ Test::Resources::Map.on( :get, [ 'foo', { :bar => /\d+/ } ] ) { captured[:bar] }
97
+ get("/foo/123").body.should == '123'
98
+ get("/foo").status.should == 404
99
+ end
100
+
101
+ it "Path components match against unescaped values" do
102
+ Test::Resources::Map.on(:get, [ 'polar bear']) { 'bang' }
103
+ get("/polar%20bear").body.should == 'bang'
104
+ end
105
+
106
+ end