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.
- data/LICENSE +20 -0
- data/README.md +135 -0
- data/Rakefile +75 -0
- data/ideas.md +29 -0
- data/lib/sinatra/capture.rb +42 -0
- data/lib/sinatra/config_file.rb +151 -0
- data/lib/sinatra/content_for.rb +111 -0
- data/lib/sinatra/contrib.rb +39 -0
- data/lib/sinatra/contrib/all.rb +2 -0
- data/lib/sinatra/contrib/setup.rb +53 -0
- data/lib/sinatra/contrib/version.rb +45 -0
- data/lib/sinatra/cookies.rb +331 -0
- data/lib/sinatra/decompile.rb +113 -0
- data/lib/sinatra/engine_tracking.rb +96 -0
- data/lib/sinatra/extension.rb +95 -0
- data/lib/sinatra/json.rb +134 -0
- data/lib/sinatra/link_header.rb +132 -0
- data/lib/sinatra/multi_route.rb +81 -0
- data/lib/sinatra/namespace.rb +282 -0
- data/lib/sinatra/reloader.rb +384 -0
- data/lib/sinatra/respond_with.rb +245 -0
- data/lib/sinatra/streaming.rb +267 -0
- data/lib/sinatra/test_helpers.rb +87 -0
- data/sinatra-contrib.gemspec +125 -0
- data/spec/capture_spec.rb +80 -0
- data/spec/config_file/key_value.yml +6 -0
- data/spec/config_file/missing_env.yml +4 -0
- data/spec/config_file/with_envs.yml +7 -0
- data/spec/config_file/with_nested_envs.yml +11 -0
- data/spec/config_file_spec.rb +44 -0
- data/spec/content_for/different_key.erb +1 -0
- data/spec/content_for/different_key.erubis +1 -0
- data/spec/content_for/different_key.haml +2 -0
- data/spec/content_for/different_key.slim +2 -0
- data/spec/content_for/layout.erb +1 -0
- data/spec/content_for/layout.erubis +1 -0
- data/spec/content_for/layout.haml +1 -0
- data/spec/content_for/layout.slim +1 -0
- data/spec/content_for/multiple_blocks.erb +4 -0
- data/spec/content_for/multiple_blocks.erubis +4 -0
- data/spec/content_for/multiple_blocks.haml +8 -0
- data/spec/content_for/multiple_blocks.slim +8 -0
- data/spec/content_for/multiple_yields.erb +3 -0
- data/spec/content_for/multiple_yields.erubis +3 -0
- data/spec/content_for/multiple_yields.haml +3 -0
- data/spec/content_for/multiple_yields.slim +3 -0
- data/spec/content_for/passes_values.erb +1 -0
- data/spec/content_for/passes_values.erubis +1 -0
- data/spec/content_for/passes_values.haml +1 -0
- data/spec/content_for/passes_values.slim +1 -0
- data/spec/content_for/same_key.erb +1 -0
- data/spec/content_for/same_key.erubis +1 -0
- data/spec/content_for/same_key.haml +2 -0
- data/spec/content_for/same_key.slim +2 -0
- data/spec/content_for/takes_values.erb +1 -0
- data/spec/content_for/takes_values.erubis +1 -0
- data/spec/content_for/takes_values.haml +3 -0
- data/spec/content_for/takes_values.slim +3 -0
- data/spec/content_for_spec.rb +201 -0
- data/spec/cookies_spec.rb +782 -0
- data/spec/decompile_spec.rb +44 -0
- data/spec/extension_spec.rb +33 -0
- data/spec/json_spec.rb +115 -0
- data/spec/link_header_spec.rb +100 -0
- data/spec/multi_route_spec.rb +45 -0
- data/spec/namespace/foo.erb +1 -0
- data/spec/namespace/nested/foo.erb +1 -0
- data/spec/namespace_spec.rb +623 -0
- data/spec/okjson.rb +581 -0
- data/spec/reloader/app.rb.erb +40 -0
- data/spec/reloader_spec.rb +441 -0
- data/spec/respond_with/bar.erb +1 -0
- data/spec/respond_with/bar.json.erb +1 -0
- data/spec/respond_with/foo.html.erb +1 -0
- data/spec/respond_with/not_html.sass +2 -0
- data/spec/respond_with_spec.rb +289 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/streaming_spec.rb +436 -0
- 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
|