sinatra-contrib 1.3.0

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 (79) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +135 -0
  3. data/Rakefile +75 -0
  4. data/ideas.md +29 -0
  5. data/lib/sinatra/capture.rb +42 -0
  6. data/lib/sinatra/config_file.rb +151 -0
  7. data/lib/sinatra/content_for.rb +111 -0
  8. data/lib/sinatra/contrib.rb +39 -0
  9. data/lib/sinatra/contrib/all.rb +2 -0
  10. data/lib/sinatra/contrib/setup.rb +53 -0
  11. data/lib/sinatra/contrib/version.rb +45 -0
  12. data/lib/sinatra/cookies.rb +331 -0
  13. data/lib/sinatra/decompile.rb +113 -0
  14. data/lib/sinatra/engine_tracking.rb +96 -0
  15. data/lib/sinatra/extension.rb +95 -0
  16. data/lib/sinatra/json.rb +134 -0
  17. data/lib/sinatra/link_header.rb +132 -0
  18. data/lib/sinatra/multi_route.rb +81 -0
  19. data/lib/sinatra/namespace.rb +282 -0
  20. data/lib/sinatra/reloader.rb +384 -0
  21. data/lib/sinatra/respond_with.rb +245 -0
  22. data/lib/sinatra/streaming.rb +267 -0
  23. data/lib/sinatra/test_helpers.rb +87 -0
  24. data/sinatra-contrib.gemspec +125 -0
  25. data/spec/capture_spec.rb +80 -0
  26. data/spec/config_file/key_value.yml +6 -0
  27. data/spec/config_file/missing_env.yml +4 -0
  28. data/spec/config_file/with_envs.yml +7 -0
  29. data/spec/config_file/with_nested_envs.yml +11 -0
  30. data/spec/config_file_spec.rb +44 -0
  31. data/spec/content_for/different_key.erb +1 -0
  32. data/spec/content_for/different_key.erubis +1 -0
  33. data/spec/content_for/different_key.haml +2 -0
  34. data/spec/content_for/different_key.slim +2 -0
  35. data/spec/content_for/layout.erb +1 -0
  36. data/spec/content_for/layout.erubis +1 -0
  37. data/spec/content_for/layout.haml +1 -0
  38. data/spec/content_for/layout.slim +1 -0
  39. data/spec/content_for/multiple_blocks.erb +4 -0
  40. data/spec/content_for/multiple_blocks.erubis +4 -0
  41. data/spec/content_for/multiple_blocks.haml +8 -0
  42. data/spec/content_for/multiple_blocks.slim +8 -0
  43. data/spec/content_for/multiple_yields.erb +3 -0
  44. data/spec/content_for/multiple_yields.erubis +3 -0
  45. data/spec/content_for/multiple_yields.haml +3 -0
  46. data/spec/content_for/multiple_yields.slim +3 -0
  47. data/spec/content_for/passes_values.erb +1 -0
  48. data/spec/content_for/passes_values.erubis +1 -0
  49. data/spec/content_for/passes_values.haml +1 -0
  50. data/spec/content_for/passes_values.slim +1 -0
  51. data/spec/content_for/same_key.erb +1 -0
  52. data/spec/content_for/same_key.erubis +1 -0
  53. data/spec/content_for/same_key.haml +2 -0
  54. data/spec/content_for/same_key.slim +2 -0
  55. data/spec/content_for/takes_values.erb +1 -0
  56. data/spec/content_for/takes_values.erubis +1 -0
  57. data/spec/content_for/takes_values.haml +3 -0
  58. data/spec/content_for/takes_values.slim +3 -0
  59. data/spec/content_for_spec.rb +201 -0
  60. data/spec/cookies_spec.rb +782 -0
  61. data/spec/decompile_spec.rb +44 -0
  62. data/spec/extension_spec.rb +33 -0
  63. data/spec/json_spec.rb +115 -0
  64. data/spec/link_header_spec.rb +100 -0
  65. data/spec/multi_route_spec.rb +45 -0
  66. data/spec/namespace/foo.erb +1 -0
  67. data/spec/namespace/nested/foo.erb +1 -0
  68. data/spec/namespace_spec.rb +623 -0
  69. data/spec/okjson.rb +581 -0
  70. data/spec/reloader/app.rb.erb +40 -0
  71. data/spec/reloader_spec.rb +441 -0
  72. data/spec/respond_with/bar.erb +1 -0
  73. data/spec/respond_with/bar.json.erb +1 -0
  74. data/spec/respond_with/foo.html.erb +1 -0
  75. data/spec/respond_with/not_html.sass +2 -0
  76. data/spec/respond_with_spec.rb +289 -0
  77. data/spec/spec_helper.rb +6 -0
  78. data/spec/streaming_spec.rb +436 -0
  79. metadata +256 -0
@@ -0,0 +1,44 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ RSpec::Matchers.define :decompile do |path|
5
+ match do |app|
6
+ @compiled, @keys = app.send :compile, path
7
+ @decompiled = app.decompile(@compiled, @keys)
8
+ @decompiled.should == path
9
+ end
10
+
11
+ failure_message_for_should do |app|
12
+ values = [app, @compiled, @keys, path, @decompiled].map(&:inspect)
13
+ "expected %s to decompile %s with %s to %s, but was %s" % values
14
+ end
15
+ end
16
+
17
+ describe Sinatra::Decompile do
18
+ subject { Sinatra::Application }
19
+ it { should decompile("") }
20
+ it { should decompile("/") }
21
+ it { should decompile("/?") }
22
+ it { should decompile("/foo") }
23
+ it { should decompile("/:name") }
24
+ it { should decompile("/:name?") }
25
+ it { should decompile("/:foo/:bar") }
26
+ it { should decompile("/page/:id/edit") }
27
+ it { should decompile("/hello/*") }
28
+ it { should decompile("/*/foo/*") }
29
+ it { should decompile("*") }
30
+ it { should decompile(":name.:format") }
31
+ it { should decompile("a b") }
32
+ it { should decompile("a+b") }
33
+ it { should decompile(/./) }
34
+ it { should decompile(/f(oo)/) }
35
+ it { should decompile(/ba+r/) }
36
+
37
+ it 'just returns strings' do
38
+ subject.decompile('/foo').should == '/foo'
39
+ end
40
+
41
+ it 'just decompile simple regexps without keys' do
42
+ subject.decompile(%r{/foo}).should == '/foo'
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Sinatra::Extension do
5
+ module ExampleExtension
6
+ extend Sinatra::Extension
7
+
8
+ set :foo, :bar
9
+ settings.set :bar, :blah
10
+
11
+ configure :test, :production do
12
+ set :reload_stuff, false
13
+ end
14
+
15
+ configure :development do
16
+ set :reload_stuff, true
17
+ end
18
+
19
+ get '/' do
20
+ "from extension, yay"
21
+ end
22
+ end
23
+
24
+ before { mock_app { register ExampleExtension }}
25
+
26
+ it('allows using set') { settings.foo.should == :bar }
27
+ it('implements configure') { settings.reload_stuff.should be_false }
28
+
29
+ it 'allows defing routes' do
30
+ get('/').should be_ok
31
+ body.should == "from extension, yay"
32
+ end
33
+ end
data/spec/json_spec.rb ADDED
@@ -0,0 +1,115 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+ require_relative 'okjson'
4
+
5
+ shared_examples_for "a json encoder" do |lib, const|
6
+ before do
7
+ begin
8
+ require lib if lib
9
+ @encoder = eval(const)
10
+ rescue LoadError
11
+ pending "unable to load #{lib}"
12
+ end
13
+ end
14
+
15
+ it "allows setting :encoder to #{const}" do
16
+ enc = @encoder
17
+ mock_app { get('/') { json({'foo' => 'bar'}, :encoder => enc) }}
18
+ results_in 'foo' => 'bar'
19
+ end
20
+
21
+ it "allows setting settings.json_encoder to #{const}" do
22
+ enc = @encoder
23
+ mock_app do
24
+ set :json_encoder, enc
25
+ get('/') { json 'foo' => 'bar' }
26
+ end
27
+ results_in 'foo' => 'bar'
28
+ end
29
+ end
30
+
31
+ describe Sinatra::JSON do
32
+ def mock_app(&block)
33
+ super do
34
+ helpers Sinatra::JSON
35
+ class_eval(&block)
36
+ end
37
+ end
38
+
39
+ def results_in(obj)
40
+ OkJson.decode(get('/').body).should == obj
41
+ end
42
+
43
+ it "encodes objects to json out of the box" do
44
+ mock_app { get('/') { json :foo => [1, 'bar'] } }
45
+ results_in 'foo' => [1, 'bar']
46
+ end
47
+
48
+ it "sets the content type to 'application/json'" do
49
+ mock_app { get('/') { json({}) } }
50
+ get('/')["Content-Type"].should include("application/json")
51
+ end
52
+
53
+ it "allows overriding content type with :content_type" do
54
+ mock_app { get('/') { json({}, :content_type => "foo/bar") } }
55
+ get('/')["Content-Type"].should == "foo/bar"
56
+ end
57
+
58
+ it "accepts shorthands for :content_type" do
59
+ mock_app { get('/') { json({}, :content_type => :js) } }
60
+ get('/')["Content-Type"].should == "application/javascript;charset=utf-8"
61
+ end
62
+
63
+ it 'calls generate on :encoder if available' do
64
+ enc = Object.new
65
+ def enc.generate(obj) obj.inspect end
66
+ mock_app { get('/') { json(42, :encoder => enc) }}
67
+ get('/').body.should == '42'
68
+ end
69
+
70
+ it 'calls encode on :encoder if available' do
71
+ enc = Object.new
72
+ def enc.encode(obj) obj.inspect end
73
+ mock_app { get('/') { json(42, :encoder => enc) }}
74
+ get('/').body.should == '42'
75
+ end
76
+
77
+ it 'sends :encoder as method call if it is a Symbol' do
78
+ mock_app { get('/') { json(42, :encoder => :inspect) }}
79
+ get('/').body.should == '42'
80
+ end
81
+
82
+ it 'calls generate on settings.json_encoder if available' do
83
+ enc = Object.new
84
+ def enc.generate(obj) obj.inspect end
85
+ mock_app do
86
+ set :json_encoder, enc
87
+ get('/') { json 42 }
88
+ end
89
+ get('/').body.should == '42'
90
+ end
91
+
92
+ it 'calls encode on settings.json_encode if available' do
93
+ enc = Object.new
94
+ def enc.encode(obj) obj.inspect end
95
+ mock_app do
96
+ set :json_encoder, enc
97
+ get('/') { json 42 }
98
+ end
99
+ get('/').body.should == '42'
100
+ end
101
+
102
+ it 'sends settings.json_encode as method call if it is a Symbol' do
103
+ mock_app do
104
+ set :json_encoder, :inspect
105
+ get('/') { json 42 }
106
+ end
107
+ get('/').body.should == '42'
108
+ end
109
+
110
+ describe('Yajl') { it_should_behave_like "a json encoder", "yajl", "Yajl::Encoder" }
111
+ describe('JSON') { it_should_behave_like "a json encoder", "json", "::JSON" }
112
+ describe('OkJson') { it_should_behave_like "a json encoder", nil, "OkJson" }
113
+ describe('to_json') { it_should_behave_like "a json encoder", "json", ":to_json" }
114
+ describe('without') { it_should_behave_like "a json encoder", nil, "Sinatra::JSON" }
115
+ end
@@ -0,0 +1,100 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Sinatra::LinkHeader do
5
+ before do
6
+ mock_app do
7
+ helpers Sinatra::LinkHeader
8
+ before('/') { link 'something', :rel => 'from-filter', :foo => :bar }
9
+
10
+ get '/' do
11
+ link :something, 'booyah'
12
+ end
13
+
14
+ get '/style' do
15
+ stylesheet '/style.css'
16
+ end
17
+
18
+ get '/prefetch' do
19
+ prefetch '/foo'
20
+ end
21
+
22
+ get '/link_headers' do
23
+ response['Link'] = "<foo> ;bar=\"baz\""
24
+ stylesheet '/style.css'
25
+ prefetch '/foo'
26
+ link_headers
27
+ end
28
+ end
29
+ end
30
+
31
+ describe :link do
32
+ it "sets link headers" do
33
+ get '/'
34
+ headers['Link'].lines.should include('<booyah>; rel="something"')
35
+ end
36
+
37
+ it "returns link html tags" do
38
+ get '/'
39
+ body.should == '<link href="booyah" rel="something" />'
40
+ end
41
+
42
+ it "takes an options hash" do
43
+ get '/'
44
+ elements = ["<something>", "foo=\"bar\"", "rel=\"from-filter\""]
45
+ headers['Link'].lines.first.strip.split('; ').sort.should == elements
46
+ end
47
+ end
48
+
49
+ describe :stylesheet do
50
+ it 'sets link headers' do
51
+ get '/style'
52
+ headers['Link'].should match(%r{^</style\.css>;})
53
+ end
54
+
55
+ it 'sets type to text/css' do
56
+ get '/style'
57
+ headers['Link'].should include('type="text/css"')
58
+ end
59
+
60
+ it 'sets rel to stylesheet' do
61
+ get '/style'
62
+ headers['Link'].should include('rel="stylesheet"')
63
+ end
64
+
65
+ it 'returns html tag' do
66
+ get '/style'
67
+ body.should match(%r{^<link href="/style\.css"})
68
+ end
69
+ end
70
+
71
+ describe :prefetch do
72
+ it 'sets link headers' do
73
+ get '/prefetch'
74
+ headers['Link'].should match(%r{^</foo>;})
75
+ end
76
+
77
+ it 'sets rel to prefetch' do
78
+ get '/prefetch'
79
+ headers['Link'].should include('rel="prefetch"')
80
+ end
81
+
82
+ it 'returns html tag' do
83
+ get '/prefetch'
84
+ body.should == '<link href="/foo" rel="prefetch" />'
85
+ end
86
+ end
87
+
88
+ describe :link_headers do
89
+ it 'generates html for all link headers' do
90
+ get '/link_headers'
91
+ body.should include('<link href="/foo" rel="prefetch" />')
92
+ body.should include('<link href="/style.css" ')
93
+ end
94
+
95
+ it "respects Link headers not generated on its own" do
96
+ get '/link_headers'
97
+ body.should include('<link href="foo" bar="baz" />')
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,45 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Sinatra::MultiRoute do
5
+ before do
6
+ count = 0
7
+ mock_app do
8
+ set(:some_condition) { |_| count += 1 }
9
+ register Sinatra::MultiRoute
10
+ get('/') { 'normal' }
11
+ get('/foo', '/bar', :some_condition => true) { 'paths' }
12
+ route('PUT', 'POST', '/') { 'verb' }
13
+ route(:get, '/baz') { 'symbol as verb' }
14
+ end
15
+ @count = count
16
+ end
17
+
18
+ it 'does still allow normal routing' do
19
+ get('/').should be_ok
20
+ body.should be == 'normal'
21
+ end
22
+
23
+ it 'supports multpile routes' do
24
+ get('/foo').should be_ok
25
+ body.should be == 'paths'
26
+ get('/bar').should be_ok
27
+ body.should be == 'paths'
28
+ end
29
+
30
+ it 'triggers conditions' do
31
+ @count.should be == 4
32
+ end
33
+
34
+ it 'supports multpile verbs' do
35
+ post('/').should be_ok
36
+ body.should be == 'verb'
37
+ put('/').should be_ok
38
+ body.should be == 'verb'
39
+ end
40
+
41
+ it 'takes symbols as verbs' do
42
+ get('/baz').should be_ok
43
+ body.should be == 'symbol as verb'
44
+ end
45
+ end
@@ -0,0 +1 @@
1
+ hi
@@ -0,0 +1 @@
1
+ ho
@@ -0,0 +1,623 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Sinatra::Namespace do
5
+ verbs = [:get, :head, :post, :put, :delete, :options]
6
+ verbs << :patch if Sinatra::VERSION >= '1.3'
7
+
8
+ def mock_app(&block)
9
+ super do
10
+ register Sinatra::Namespace
11
+ class_eval(&block)
12
+ end
13
+ end
14
+
15
+ def namespace(*args, &block)
16
+ mock_app { namespace(*args, &block) }
17
+ end
18
+
19
+ verbs.each do |verb|
20
+ describe "HTTP #{verb.to_s.upcase}" do
21
+ describe 'pattern generation' do
22
+ it "should add routes including prefix to the base app" do
23
+ namespace("/foo") { send(verb, "/bar") { "baz" }}
24
+ send(verb, "/foo/bar").should be_ok
25
+ body.should == "baz" unless verb == :head
26
+ send(verb, "/foo/baz").should_not be_ok
27
+ end
28
+
29
+ it "should allows adding routes with no path" do
30
+ namespace("/foo") { send(verb) { "bar" } }
31
+ send(verb, "/foo").should be_ok
32
+ body.should == "bar" unless verb == :head
33
+ end
34
+
35
+ it "allows unsing regular expressions" do
36
+ namespace("/foo") { send(verb, /\/\d\d/) { "bar" }}
37
+ send(verb, "/foo/12").should be_ok
38
+ body.should == "bar" unless verb == :head
39
+ send(verb, "/foo/123").should_not be_ok
40
+ end
41
+
42
+ it "allows using regular expressions for the prefix" do
43
+ namespace(/\/\d\d/) { send(verb, /\/\d\d/) { "foo" }}
44
+ send(verb, "/23/12").should be_ok
45
+ body.should == "foo" unless verb == :head
46
+ send(verb, "/123/12").should_not be_ok
47
+ end
48
+
49
+ it "sets params correctly from namespace" do
50
+ namespace("/:foo") { send(verb, "/bar") { params[:foo] }}
51
+ send(verb, "/foo/bar").should be_ok
52
+ body.should == "foo" unless verb == :head
53
+ send(verb, "/foo/baz").should_not be_ok
54
+ send(verb, "/fox/bar").should be_ok
55
+ body.should == "fox" unless verb == :head
56
+ end
57
+
58
+ it "sets params correctly from route" do
59
+ namespace("/foo") { send(verb, "/:bar") { params[:bar] }}
60
+ send(verb, "/foo/bar").should be_ok
61
+ body.should == "bar" unless verb == :head
62
+ send(verb, "/foo/baz").should be_ok
63
+ body.should == "baz" unless verb == :head
64
+ end
65
+
66
+ it "allows splats to be combined from namespace and route" do
67
+ namespace("/*") { send(verb, "/*") { params[:splat].join " - " }}
68
+ send(verb, '/foo/bar').should be_ok
69
+ body.should == "foo - bar" unless verb == :head
70
+ end
71
+
72
+ it "sets params correctly from namespace if simple regexp is used for route" do
73
+ namespace("/:foo") { send(verb, %r{/bar}) { params[:foo] }}
74
+ send(verb, "/foo/bar").should be_ok
75
+ body.should == "foo" unless verb == :head
76
+ send(verb, "/foo/baz").should_not be_ok
77
+ send(verb, "/fox/bar").should be_ok
78
+ body.should == "fox" unless verb == :head
79
+ end
80
+
81
+ it "sets params correctly from route if simple regexp is used for namespace" do
82
+ namespace(%r{/foo}) { send(verb, "/:bar") { params[:bar] }}
83
+ send(verb, "/foo/bar").should be_ok
84
+ body.should == "bar" unless verb == :head
85
+ send(verb, "/foo/baz").should be_ok
86
+ body.should == "baz" unless verb == :head
87
+ end
88
+
89
+ it 'allows defining routes without a pattern' do
90
+ namespace(%r{/foo}) { send(verb) { 'bar' } }
91
+ send(verb, '/foo').should be_ok
92
+ body.should == 'bar' unless verb == :head
93
+ end
94
+ end
95
+
96
+ describe 'conditions' do
97
+ it 'allows using conditions for namespaces' do
98
+ mock_app do
99
+ namespace(:host_name => 'example.com') { send(verb) { 'yes' }}
100
+ send(verb, '/') { 'no' }
101
+ end
102
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.com')
103
+ last_response.should be_ok
104
+ body.should == 'yes' unless verb == :head
105
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.org')
106
+ last_response.should be_ok
107
+ body.should == 'no' unless verb == :head
108
+ end
109
+
110
+ it 'allows using conditions for before filters' do
111
+ namespace '/foo' do
112
+ before(:host_name => 'example.com') { @yes = "yes" }
113
+ send(verb) { @yes || "no" }
114
+ end
115
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com')
116
+ last_response.should be_ok
117
+ body.should == 'yes' unless verb == :head
118
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org')
119
+ last_response.should be_ok
120
+ body.should == 'no' unless verb == :head
121
+ end
122
+
123
+ it 'allows using conditions for after filters' do
124
+ ran = false
125
+ namespace '/foo' do
126
+ before(:host_name => 'example.com') { ran = true }
127
+ send(verb) { "ok" }
128
+ end
129
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org')
130
+ ran.should be_false
131
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com')
132
+ ran.should be_true
133
+ end
134
+
135
+ it 'allows using conditions for routes' do
136
+ namespace '/foo' do
137
+ send(verb, :host_name => 'example.com') { "ok" }
138
+ end
139
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com').should be_ok
140
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org').should_not be_ok
141
+ end
142
+
143
+ it 'allows using conditions for before filters and the namespace' do
144
+ ran = false
145
+ namespace '/', :provides => :txt do
146
+ before(:host_name => 'example.com') { ran = true }
147
+ send(verb) { "ok" }
148
+ end
149
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
150
+ ran.should be_false
151
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
152
+ ran.should be_false
153
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
154
+ ran.should be_true
155
+ end
156
+
157
+ it 'allows using conditions for routes and the namespace' do
158
+ namespace '/foo', :host_name => 'example.com' do
159
+ send(verb, :provides => :txt) { "ok" }
160
+ end
161
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
162
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
163
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
164
+ end
165
+
166
+ it 'allows combining conditions with a prefix for namespaces' do
167
+ namespace '/', :host_name => 'example.com' do
168
+ send(verb) { "ok" }
169
+ end
170
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.com').should be_ok
171
+ send(verb, '/', {}, 'HTTP_HOST' => 'example.org').should_not be_ok
172
+ end
173
+
174
+ it 'allows combining conditions with a prefix for before filters' do
175
+ ran = false
176
+ namespace :provides => :txt do
177
+ before('/foo', :host_name => 'example.com') { ran = true }
178
+ send(verb, '/*') { "ok" }
179
+ end
180
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
181
+ ran.should be_false
182
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
183
+ ran.should be_false
184
+ send(verb, '/bar', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
185
+ ran.should be_false
186
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
187
+ ran.should be_true
188
+ end
189
+
190
+ it 'allows combining conditions with a prefix for after filters' do
191
+ ran = false
192
+ namespace :provides => :txt do
193
+ after('/foo', :host_name => 'example.com') { ran = true }
194
+ send(verb, '/*') { "ok" }
195
+ end
196
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
197
+ ran.should be_false
198
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
199
+ ran.should be_false
200
+ send(verb, '/bar', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
201
+ ran.should be_false
202
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
203
+ ran.should be_true
204
+ end
205
+
206
+ it 'allows combining conditions with a prefix for routes' do
207
+ namespace :host_name => 'example.com' do
208
+ send(verb, '/foo', :provides => :txt) { "ok" }
209
+ end
210
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
211
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
212
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
213
+ end
214
+
215
+ it 'allows combining conditions with a prefix for filters and the namespace' do
216
+ ran = false
217
+ namespace '/f', :provides => :txt do
218
+ before('oo', :host_name => 'example.com') { ran = true }
219
+ send(verb, '/*') { "ok" }
220
+ end
221
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
222
+ ran.should be_false
223
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
224
+ ran.should be_false
225
+ send(verb, '/far', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
226
+ ran.should be_false
227
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
228
+ ran.should be_true
229
+ end
230
+
231
+ it 'allows combining conditions with a prefix for routes and the namespace' do
232
+ namespace '/f', :host_name => 'example.com' do
233
+ send(verb, 'oo', :provides => :txt) { "ok" }
234
+ end
235
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
236
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
237
+ send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
238
+ end
239
+ end
240
+
241
+ describe 'filters' do
242
+ it 'should trigger before filters for namespaces' do
243
+ ran = false
244
+ namespace('/foo') { before { ran = true }}
245
+ send(verb, '/foo')
246
+ ran.should be_true
247
+ end
248
+
249
+ it 'should trigger after filters for namespaces' do
250
+ ran = false
251
+ namespace('/foo') { after { ran = true }}
252
+ send(verb, '/foo')
253
+ ran.should be_true
254
+ end
255
+
256
+ it 'should not trigger before filter for different namespaces' do
257
+ ran = false
258
+ namespace('/foo') { before { ran = true }}
259
+ send(verb, '/fox')
260
+ ran.should be_false
261
+ end
262
+
263
+ it 'should not trigger after filter for different namespaces' do
264
+ ran = false
265
+ namespace('/foo') { after { ran = true }}
266
+ send(verb, '/fox')
267
+ ran.should be_false
268
+ end
269
+ end
270
+
271
+ describe 'helpers' do
272
+ it "allows defining helpers with the helpers method" do
273
+ namespace '/foo' do
274
+ helpers do
275
+ def magic
276
+ 42
277
+ end
278
+ end
279
+
280
+ send verb, '/bar' do
281
+ magic.to_s
282
+ end
283
+ end
284
+
285
+ send(verb, '/foo/bar').should be_ok
286
+ body.should == '42' unless verb == :head
287
+ end
288
+
289
+ it "allows defining helpers without the helpers method" do
290
+ namespace '/foo' do
291
+ def magic
292
+ 42
293
+ end
294
+
295
+ send verb, '/bar' do
296
+ magic.to_s
297
+ end
298
+ end
299
+
300
+ send(verb, '/foo/bar').should be_ok
301
+ body.should == '42' unless verb == :head
302
+ end
303
+
304
+ it "allows using helper mixins with the helpers method" do
305
+ mixin = Module.new do
306
+ def magic
307
+ 42
308
+ end
309
+ end
310
+
311
+ namespace '/foo' do
312
+ helpers mixin
313
+ send verb, '/bar' do
314
+ magic.to_s
315
+ end
316
+ end
317
+
318
+ send(verb, '/foo/bar').should be_ok
319
+ body.should == '42' unless verb == :head
320
+ end
321
+
322
+ it "makes helpers defined inside a namespace not available to routes outside that namespace" do
323
+ mock_app do
324
+ namespace '/foo' do
325
+ def magic
326
+ 42
327
+ end
328
+
329
+ send verb, '/bar' do
330
+ magic.to_s
331
+ end
332
+ end
333
+
334
+ send verb, '/' do
335
+ magic.to_s
336
+ end
337
+ end
338
+
339
+ proc { send verb, '/' }.should raise_error(NameError)
340
+ end
341
+
342
+ it "makes helper mixins used inside a namespace not available to routes outside that namespace" do
343
+ mixin = Module.new do
344
+ def magic
345
+ 42
346
+ end
347
+ end
348
+
349
+ mock_app do
350
+ namespace '/foo' do
351
+ helpers mixin
352
+ send verb, '/bar' do
353
+ magic.to_s
354
+ end
355
+ end
356
+
357
+ send verb, '/' do
358
+ magic.to_s
359
+ end
360
+ end
361
+
362
+ proc { send verb, '/' }.should raise_error(NameError)
363
+ end
364
+
365
+ it "allows accessing helpers defined outside the namespace" do
366
+ mock_app do
367
+ helpers do
368
+ def magic
369
+ 42
370
+ end
371
+ end
372
+
373
+ namespace '/foo' do
374
+ send verb, '/bar' do
375
+ magic.to_s
376
+ end
377
+ end
378
+ end
379
+
380
+ send(verb, '/foo/bar').should be_ok
381
+ body.should == '42' unless verb == :head
382
+ end
383
+
384
+ it "allows calling super in helpers overwritten inside a namespace" do
385
+ mock_app do
386
+ helpers do
387
+ def magic
388
+ 42
389
+ end
390
+ end
391
+
392
+ namespace '/foo' do
393
+ def magic
394
+ super - 19
395
+ end
396
+
397
+ send verb, '/bar' do
398
+ magic.to_s
399
+ end
400
+ end
401
+ end
402
+
403
+ send(verb, '/foo/bar').should be_ok
404
+ body.should == '23' unless verb == :head
405
+ end
406
+ end
407
+
408
+ describe 'nesting' do
409
+ it 'routes to nested namespaces' do
410
+ namespace '/foo' do
411
+ namespace '/bar' do
412
+ send(verb, '/baz') { 'OKAY!!11!'}
413
+ end
414
+ end
415
+
416
+ send(verb, '/foo/bar/baz').should be_ok
417
+ body.should == 'OKAY!!11!' unless verb == :head
418
+ end
419
+
420
+ it 'exposes helpers to nested namespaces' do
421
+ namespace '/foo' do
422
+ helpers do
423
+ def magic
424
+ 42
425
+ end
426
+ end
427
+
428
+ namespace '/bar' do
429
+ send verb, '/baz' do
430
+ magic.to_s
431
+ end
432
+ end
433
+ end
434
+
435
+ send(verb, '/foo/bar/baz').should be_ok
436
+ body.should == '42' unless verb == :head
437
+ end
438
+
439
+ it 'does not use helpers of nested namespaces outside that namespace' do
440
+ namespace '/foo' do
441
+ namespace '/bar' do
442
+ def magic
443
+ 42
444
+ end
445
+
446
+ send verb, '/baz' do
447
+ magic.to_s
448
+ end
449
+ end
450
+
451
+ send verb do
452
+ magic.to_s
453
+ end
454
+ end
455
+
456
+ proc { send verb, '/foo' }.should raise_error(NameError)
457
+ end
458
+
459
+ it 'sets params correctly' do
460
+ namespace('/:a') { namespace('/:b') { send(verb) { params[:a] }}}
461
+ send(verb, '/foo/bar').should be_ok
462
+ body.should == 'foo' unless verb == :head
463
+ end
464
+ end
465
+
466
+ describe 'error handlers' do
467
+ it "should allow custom error handlers with not found" do
468
+ namespace('/de') do
469
+ not_found { 'nicht gefunden' }
470
+ end
471
+ send(verb, '/foo').status.should == 404
472
+ last_response.body.should_not == 'nicht gefunden' unless verb == :head
473
+ get('/en/foo').status.should == 404
474
+ last_response.body.should_not == 'nicht gefunden' unless verb == :head
475
+ get('/de/foo').status.should == 404
476
+ last_response.body.should == 'nicht gefunden' unless verb == :head
477
+ end
478
+
479
+ it "should allow custom error handlers with error" do
480
+ namespace('/de') do
481
+ error(404) { 'nicht gefunden' }
482
+ end
483
+ send(verb, '/foo').status.should == 404
484
+ last_response.body.should_not == 'nicht gefunden' unless verb == :head
485
+ get('/en/foo').status.should == 404
486
+ last_response.body.should_not == 'nicht gefunden' unless verb == :head
487
+ get('/de/foo').status.should == 404
488
+ last_response.body.should == 'nicht gefunden' unless verb == :head
489
+ end
490
+ end
491
+
492
+ describe 'templates' do
493
+ it "allows using templates from the base" do
494
+ mock_app do
495
+ template(:foo) { 'hi' }
496
+ send(verb, '/') { erb :foo }
497
+ namespace '/foo' do
498
+ send(verb) { erb :foo }
499
+ end
500
+ end
501
+
502
+ if verb != :head
503
+ send(verb, '/').body.should == "hi"
504
+ send(verb, '/foo').body.should == "hi"
505
+ end
506
+ end
507
+
508
+ it "allows to define nested templates" do
509
+ mock_app do
510
+ template(:foo) { 'hi' }
511
+ send(verb, '/') { erb :foo }
512
+ namespace '/foo' do
513
+ template(:foo) { 'ho' }
514
+ send(verb) { erb :foo }
515
+ end
516
+ end
517
+
518
+ if verb != :head
519
+ send(verb, '/').body.should == "hi"
520
+ send(verb, '/foo').body.should == "ho"
521
+ end
522
+ end
523
+
524
+ it "allows to define nested layouts" do
525
+ mock_app do
526
+ layout { 'Hello <%= yield %>!' }
527
+ template(:foo) { 'World' }
528
+ send(verb, '/') { erb :foo }
529
+ namespace '/foo' do
530
+ layout { 'Hi <%= yield %>!' }
531
+ send(verb) { erb :foo }
532
+ end
533
+ end
534
+
535
+ if verb != :head
536
+ send(verb, '/').body.should == "Hello World!"
537
+ send(verb, '/foo').body.should == "Hi World!"
538
+ end
539
+ end
540
+
541
+ it "allows using templates from the base" do
542
+ mock_app do
543
+ layout { "he said: <%= yield %>" }
544
+ template(:foo) { 'hi' }
545
+ send(verb, '/') { erb :foo }
546
+ namespace '/foo' do
547
+ template(:foo) { 'ho' }
548
+ send(verb) { erb :foo }
549
+ end
550
+ end
551
+
552
+ if verb != :head
553
+ send(verb, '/').body.should == "he said: hi"
554
+ send(verb, '/foo').body.should == "he said: ho"
555
+ end
556
+ end
557
+
558
+ it "allows setting a different views directory" do
559
+ mock_app do
560
+ set :views, File.expand_path('../namespace', __FILE__)
561
+ send(verb, '/') { erb :foo }
562
+ namespace('/foo') do
563
+ set :views, File.expand_path('../namespace/nested', __FILE__)
564
+ send(verb) { erb :foo }
565
+ end
566
+ end
567
+
568
+ if verb != :head
569
+ send(verb, '/').body.should == "hi\n"
570
+ send(verb, '/foo').body.should == "ho\n"
571
+ end
572
+ end
573
+ end
574
+
575
+ describe 'extensions' do
576
+ it 'allows read access to settings' do
577
+ value = nil
578
+ mock_app do
579
+ set :foo, 42
580
+ namespace '/foo' do
581
+ value = foo
582
+ end
583
+ end
584
+ value.should == 42
585
+ end
586
+
587
+ it 'allows registering extensions for a namespace only' do
588
+ a = b = nil
589
+ extension = Module.new { define_method(:views) { "CUSTOM!!!" } }
590
+ mock_app do
591
+ namespace '/' do
592
+ register extension
593
+ a = views
594
+ end
595
+ b = views
596
+ end
597
+ a.should == 'CUSTOM!!!'
598
+ b.should_not == 'CUSTOM!!!'
599
+ end
600
+
601
+ it 'triggers route_added hook' do
602
+ route = nil
603
+ extension = Module.new
604
+ extension.singleton_class.class_eval do
605
+ define_method(:route_added) { |*r| route = r }
606
+ end
607
+ mock_app do
608
+ namespace '/f' do
609
+ register extension
610
+ get('oo') { }
611
+ end
612
+ get('/bar') { }
613
+ end
614
+ route[1].should == '/foo'
615
+ end
616
+
617
+ it 'prevents changing app global settings' do
618
+ proc { namespace('/') { set :foo, :bar }}.should raise_error
619
+ end
620
+ end
621
+ end
622
+ end
623
+ end