sinatra-contrib 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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