require 'jellyfish/test' class RegexpLookAlike class MatchData def captures ["this", "is", "a", "test"] end end def match(string) ::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/" end def keys ["one", "two", "three", "four"] end end # stolen from sinatra describe 'Sinatra routing_test.rb' do paste :jellyfish %w[get put post delete options patch head].each do |verb| would "define #{verb.upcase} request handlers with #{verb}" do app = Class.new{ include Jellyfish send verb, '/hello' do 'Hello World' end }.new status, _, body = send(verb, '/hello', app) status.should.eq 200 body .should.eq ['Hello World'] end end would '404s when no route satisfies the request' do app = Class.new{ include Jellyfish get('/foo'){} }.new status, _, _ = get('/bar', app) status.should.eq 404 end would 'allows using unicode' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedPath get("/f\u{f6}\u{f6}"){} }.new status, _, _ = get('/f%C3%B6%C3%B6', app) status.should.eq 200 end would 'handle encoded slashes correctly' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedPath get(%r{^/(.+)}){ |m| m[1] } }.new status, _, body = get('/foo%2Fbar', app) status.should.eq 200 body .should.eq ['foo/bar'] end would 'override the content-type in error handlers' do app = Class.new{ include Jellyfish get{ self.headers 'Content-Type' => 'text/plain' status, headers, body = jellyfish.app.call(env) self.status status self.body body headers_merge(headers) } }.new(Class.new{ include Jellyfish handle Jellyfish::NotFound do headers_merge 'Content-Type' => 'text/html' status 404 '

Not Found

' end }.new) status, headers, body = get('/foo', app) status .should.eq 404 headers['Content-Type'].should.eq 'text/html' body .should.eq ['

Not Found

'] end would 'match empty PATH_INFO to "/" if no route is defined for ""' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedPath get('/'){ 'worked' } }.new status, _, body = get('', app) status.should.eq 200 body .should.eq ['worked'] end would 'exposes params with indifferent hash' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedParams get %r{^/(?\w+)} do params['foo'].should.eq 'bar' params[:foo ].should.eq 'bar' 'well, alright' end }.new _, _, body = get('/bar', app) body.should.eq ['well, alright'] end would 'merges named params and query string params in params' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedParams get %r{^/(?\w+)} do params['foo'].should.eq 'bar' params['baz'].should.eq 'biz' end }.new status, _, _ = get('/bar', app, 'QUERY_STRING' => 'baz=biz') status.should.eq 200 end would 'support named captures like %r{/hello/(?[^/?#]+)}' do app = Class.new{ include Jellyfish get Regexp.new('/hello/(?[^/?#]+)') do |m| "Hello #{m['person']}" end }.new _, _, body = get('/hello/Frank', app) body.should.eq ['Hello Frank'] end would 'support optional named captures' do app = Class.new{ include Jellyfish get Regexp.new('/page(?.[^/?#]+)?') do |m| "format=#{m[:format]}" end }.new status, _, body = get('/page.html', app) status.should.eq 200 body .should.eq ['format=.html'] status, _, body = get('/page.xml', app) status.should.eq 200 body .should.eq ['format=.xml'] status, _, body = get('/page', app) status.should.eq 200 body .should.eq ['format='] end would 'not concatinate params with the same name' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedParams get(%r{^/(?\w+)}){ |m| params[:foo] } }.new _, _, body = get('/a', app, 'QUERY_STRING' => 'foo=b') body.should.eq ['a'] end would 'support basic nested params' do app = Class.new{ include Jellyfish get('/hi'){ request.params['person']['name'] } }.new status, _, body = get('/hi', app, 'QUERY_STRING' => 'person[name]=John+Doe') status.should.eq 200 body.should.eq ['John Doe'] end would "expose nested params with indifferent hash" do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedParams get '/testme' do params['bar']['foo'].should.eq 'baz' params['bar'][:foo ].should.eq 'baz' 'well, alright' end }.new _, _, body = get('/testme', app, 'QUERY_STRING' => 'bar[foo]=baz') body.should.eq ['well, alright'] end would 'preserve non-nested params' do app = Class.new{ include Jellyfish get '/foo' do request.params['article_id'] .should.eq '2' request.params['comment']['body'].should.eq 'awesome' request.params['comment[body]'] .should.eq nil 'looks good' end }.new status, _, body = get('/foo', app, 'QUERY_STRING' => 'article_id=2&comment[body]=awesome') status.should.eq 200 body .should.eq ['looks good'] end would 'match paths that include spaces encoded with %20' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedPath get('/path with spaces'){ 'looks good' } }.new status, _, body = get('/path%20with%20spaces', app) status.should.eq 200 body .should.eq ['looks good'] end would 'match paths that include spaces encoded with +' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedPath get('/path with spaces'){ 'looks good' } }.new status, _, body = get('/path+with+spaces', app) status.should.eq 200 body .should.eq ['looks good'] end would 'make regular expression captures available' do app = Class.new{ include Jellyfish get(/^\/fo(.*)\/ba(.*)/) do |m| m[1..-1].should.eq ['orooomma', 'f'] 'right on' end }.new status, _, body = get('/foorooomma/baf', app) status.should.eq 200 body .should.eq ['right on'] end would 'support regular expression look-alike routes' do app = Class.new{ include Jellyfish controller_include Jellyfish::NormalizedParams matcher = Object.new def matcher.match path %r{/(?\w+)/(?\w+)/(?\w+)/(?\w+)}.match(path) end get(matcher) do |m| [m, params].each do |q| q[:one] .should.eq 'this' q[:two] .should.eq 'is' q[:three].should.eq 'a' q[:four] .should.eq 'test' end 'right on' end }.new status, _, body = get('/this/is/a/test/', app) status.should.eq 200 body .should.eq ['right on'] end would 'raise a TypeError when pattern is not a String or Regexp' do lambda{ Class.new{ include Jellyfish; get(42){} } }. should.raise(TypeError) end would 'match routes defined in superclasses' do sup = Class.new{ include Jellyfish get('/foo'){ 'foo' } } app = Class.new(sup){ get('/bar'){ 'bar' } }.new %w[foo bar].each do |path| status, _, body = get("/#{path}", app) status.should.eq 200 body .should.eq [path] end end would 'match routes itself first then downward app' do sup = Class.new{ include Jellyfish get('/foo'){ 'foo sup' } get('/bar'){ 'bar sup' } } app = Class.new{ include Jellyfish get('/foo'){ 'foo sub' } }.new(sup.new) status, _, body = get('/foo', app) status.should.eq 200 body .should.eq ['foo sub'] status, _, body = get('/bar', app) status.should.eq 200 body .should.eq ['bar sup'] end would 'allow using call to fire another request internally' do app = Class.new{ include Jellyfish get '/foo' do status, headers, body = call(env.merge('PATH_INFO' => '/bar')) self.status status self.headers headers self.body body.map(&:upcase) end get '/bar' do 'bar' end }.new status, _, body = get('/foo', app) status.should.eq 200 body .should.eq ['BAR'] end would 'play well with other routing middleware' do middleware = Class.new{include Jellyfish} inner_app = Class.new{include Jellyfish; get('/foo'){ 'hello' } } app = Rack::Builder.app do use middleware map('/test'){ run inner_app.new } end status, _, body = get('/test/foo', app) status.should.eq 200 body .should.eq ['hello'] end end