pendragon 0.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.
@@ -0,0 +1,60 @@
1
+ module Pendragon
2
+ class Route
3
+
4
+ ##
5
+ # The accessors are useful to access from Pendragon::Router
6
+ attr_accessor :block, :capture, :router, :options, :verb, :order
7
+
8
+ ##
9
+ # For compile option
10
+ attr_accessor :index
11
+
12
+ ##
13
+ # Constructs a new instance of Pendragon::Route
14
+ def initialize(path, verb, options = {}, &block)
15
+ @block = block if block_given?
16
+ @path, @verb, @options = path, verb, options
17
+ @capture = {}
18
+ @order = 0
19
+ end
20
+
21
+ def matcher
22
+ @matcher ||= Matcher.new(@path, :capture => @capture,
23
+ :default_values => options[:default_values])
24
+ end
25
+
26
+ def arity
27
+ block.arity
28
+ end
29
+
30
+ def call(*args)
31
+ @block.call(*args)
32
+ end
33
+
34
+ def match(pattern)
35
+ matcher.match(pattern)
36
+ end
37
+
38
+ def name
39
+ @options[:name]
40
+ end
41
+
42
+ def name=(value)
43
+ warn "[DEPRECATION] 'name=' is depreacted. Please use 'options[:name]=' instead"
44
+ @options[:name] = value
45
+ end
46
+
47
+ def to(&block)
48
+ @block = block if block_given?
49
+ @order = router.current
50
+ router.increment_order!
51
+ end
52
+
53
+ def path(*args)
54
+ return @path if args.empty?
55
+ params = args[0]
56
+ params.delete(:captures)
57
+ matcher.expand(params) if matcher.mustermann?
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,188 @@
1
+ require 'pendragon/route'
2
+ require 'pendragon/matcher'
3
+ require 'pendragon/error_handler'
4
+ require 'pendragon/compile_helpers'
5
+ require 'pendragon/configuration'
6
+ require 'rack'
7
+
8
+ module Pendragon
9
+ class Router
10
+
11
+ # The accessors are useful to access from Pendragon::Route
12
+ attr_accessor :current, :routes
13
+
14
+ # Constructs a new instance of Pendragon::Router
15
+ # Possible to pass the block
16
+ #
17
+ # @example with a block
18
+ # app = Pendragon::Router.new do
19
+ # get("/"){ "hello!" }
20
+ # post("/"){ "hello post!" }
21
+ # end
22
+ #
23
+ # @example with base style
24
+ # app = Pendragon::Router.new
25
+ # app.get("/"){ "hello!" }
26
+ # app.post("/"){ "hello post!" }
27
+ def initialize(&block)
28
+ reset!
29
+ instance_eval(&block) if block_given?
30
+ end
31
+
32
+ # Finds the routes if request method is valid
33
+ # @return the Rack style response
34
+ def call(env)
35
+ request = Rack::Request.new(env)
36
+ raise BadRequest unless valid_verb?(request.request_method)
37
+ prepare! unless prepared?
38
+ route, params = recognize(request).first
39
+ body = route.arity != 0 ? route.call(params) : route.call
40
+ [200, {'Content-Type' => 'text/html;charset=utf-8'}, Array(body)]
41
+ rescue BadRequest, NotFound, MethodNotAllowed
42
+ $!.call
43
+ end
44
+
45
+ # Provides some methods intuitive than #add
46
+ # Basic usage is the same as #add
47
+ # @see Pendragon::Router#add
48
+ def get(path, options = {}, &block); add :get, path, options, &block end
49
+ def post(path, options = {}, &block); add :post, path, options, &block end
50
+ def delete(path, options = {}, &block); add :delete, path, options, &block end
51
+ def put(path, options = {}, &block); add :put, path, options, &block end
52
+ def head(path, options = {}, &block); add :head, path, options, &block end
53
+
54
+ # Adds a new route to router
55
+ # @return [Pendragon::Route]
56
+ def add(verb, path, options = {}, &block)
57
+ routes << (route = Route.new(path, verb, options, &block))
58
+ route.router = self
59
+ route
60
+ end
61
+
62
+ # Resets the router's instance variables
63
+ def reset!
64
+ @routes = []
65
+ @current = 0
66
+ @prepared = nil
67
+ end
68
+
69
+ # Prepares the router for route's priority
70
+ # This method is executed only once in the initial load
71
+ def prepare!
72
+ @prepared = true
73
+ @routes.sort_by!(&:order) unless current.zero?
74
+ if Pendragon.configuration.enable_compiler?
75
+ class << self
76
+ include CompileHelpers
77
+ alias_method :old_recognize, :recognize
78
+ alias_method :recognize, :recognize_by_compiling_regexp
79
+ end
80
+ compile!
81
+ end
82
+ end
83
+
84
+ # @return [Boolean] the router is already prepared?
85
+ def prepared?
86
+ !!@prepared
87
+ end
88
+
89
+ # Increments for the integrity of priorities
90
+ def increment_order!
91
+ @current += 1
92
+ end
93
+
94
+ # Recognizes the route by request
95
+ # @param request [Rack::Request]
96
+ # @return [Array]
97
+ def recognize(request)
98
+ path_info, verb, request_params = parse_request(request)
99
+ scan(path_info, verb) do |route|
100
+ params, match_data = {}, route.match(path_info)
101
+ if match_data.names.empty?
102
+ params[:captures] = match_data.captures
103
+ else
104
+ params.merge!(match_data.names.inject({}){|result, name|
105
+ result[name.to_sym] = match_data[name] ? Rack::Utils.unescape(match_data[name]) : nil
106
+ result
107
+ }).merge!(request_params){|key, self_val, new_val| self_val || new_val }
108
+ end
109
+ [route, params]
110
+ end
111
+ end
112
+
113
+ # Recognizes a given path
114
+ # @param path_info [String]
115
+ # @return [Array]
116
+ def recognize_path(path_info)
117
+ route, params = recognize(Rack::MockRequest.env_for(path_info)).first
118
+ [route.options[:name], params]
119
+ end
120
+
121
+ # Returns a expanded path matched with the conditions as arguments
122
+ # @return [String, Regexp]
123
+ # @example
124
+ # router = Pendragon.new
125
+ # index = router.get("/:id", :name => :index){}
126
+ # router.path(:index, :id => 1) #=> "/1"
127
+ # router.path(:index, :id => 2, :foo => "bar") #=> "/1?foo=bar"
128
+ def path(name, *args)
129
+ params = args.delete_at(args.last.is_a?(Hash) ? -1 : 0) || {}
130
+ saved_args = args.dup
131
+ @routes.each do |route|
132
+ next unless route.options[:name] == name
133
+ matcher = route.matcher
134
+ if !args.empty? and matcher.mustermann?
135
+ matcher_names = matcher.names
136
+ params_for_expand = Hash[matcher_names.map{|matcher_name|
137
+ [matcher_name.to_sym, (params[matcher_name.to_sym] || args.shift)]}]
138
+ params_for_expand.merge!(Hash[params.select{|k, v| !matcher_names.include?(name.to_sym) }])
139
+ args = saved_args.dup
140
+ else
141
+ params_for_expand = params.dup
142
+ end
143
+ return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path
144
+ end
145
+ raise InvalidRouteException
146
+ end
147
+
148
+ private
149
+
150
+ # @!visibility private
151
+ def valid_verb?(verb)
152
+ Pendragon::HTTP_VERBS.include?(verb.downcase.to_sym)
153
+ end
154
+
155
+ # @!visibility private
156
+ def scan(pattern, verb)
157
+ raise NotFound if (selected_routes = routes.select{|route| route.match(pattern) }).empty?
158
+
159
+ result = selected_routes.map do |route|
160
+ next unless verb == route.verb
161
+ yield route
162
+ end.compact
163
+
164
+ if result.empty?
165
+ raise MethodNotAllowed.new(selected_routes.map(&:verb))
166
+ else
167
+ result
168
+ end
169
+ end
170
+
171
+ # @!visibility private
172
+ def parse_request(request)
173
+ if request.is_a?(Hash)
174
+ [request['PATH_INFO'], request['REQUEST_METHOD'].downcase.to_sym, {}]
175
+ else
176
+ [request.path_info, request.request_method.downcase.to_sym, parse_request_params(request.params)]
177
+ end
178
+ end
179
+
180
+ # @!visibility private
181
+ def parse_request_params(params)
182
+ params.inject({}) do |result, entry|
183
+ result[entry[0].to_sym] = entry[1]
184
+ result
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Pendragon
3
+ VERSION = '0.3.0'
4
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path("../lib/pendragon/version", __FILE__)
2
+
3
+ Gem::Specification.new "pendragon", Pendragon::VERSION do |s|
4
+ s.description = "Provides an HTTP router for use in Rack and Padrino."
5
+ s.summary = s.description
6
+ s.authors = ["namusyaka"]
7
+ s.email = "namusyaka@gmail.com"
8
+ s.homepage = "https://github.com/namusyaka/pendragon"
9
+ s.files = `git ls-files`.split("\n") - %w(.gitignore)
10
+ s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
11
+ s.license = "MIT"
12
+
13
+ s.add_dependency "rack", ">= 1.3.0"
14
+ s.add_dependency "mustermann", "= 0.2.0"
15
+ s.add_development_dependency "rake", ">= 0.8.7"
16
+ s.add_development_dependency "rack-test", ">= 0.5.0"
17
+ s.add_development_dependency "mocha", ">= 0.10.0"
18
+ s.add_development_dependency "haml"
19
+ s.add_development_dependency "padrino-core", "= 0.12.0.rc3"
20
+ end
@@ -0,0 +1,5 @@
1
+ require 'pendragon'
2
+
3
+ Pendragon.configure do |config|
4
+ config.enable_compiler = true
5
+ end
@@ -0,0 +1,87 @@
1
+ require 'bundler/setup'
2
+ ENV['PADRINO_ENV'] = 'test'
3
+ PADRINO_ROOT = File.dirname(__FILE__) unless defined?(PADRINO_ROOT)
4
+ require File.expand_path('../../lib/pendragon', __FILE__)
5
+
6
+ require 'minitest/unit'
7
+ require 'minitest/autorun'
8
+ require 'minitest/spec'
9
+ require 'mocha/setup'
10
+ require 'padrino-core'
11
+ require 'rack'
12
+ require 'rack/test'
13
+
14
+ begin
15
+ require 'ruby-debug'
16
+ rescue LoadError; end
17
+
18
+ class Sinatra::Base
19
+ include MiniTest::Assertions
20
+ end
21
+
22
+ class MiniTest::Spec
23
+ include Rack::Test::Methods
24
+
25
+ def pendragon
26
+ @app = Pendragon.new
27
+ end
28
+
29
+ def enable_compiler?
30
+ Pendragon.configuration.enable_compiler?
31
+ end
32
+
33
+ def mock_app(base = nil, &block)
34
+ @app = Sinatra.new(base || ::Padrino::Application, &block)
35
+ end
36
+
37
+ def app
38
+ Rack::Lint.new(@app)
39
+ end
40
+
41
+ def method_missing(name, *args, &block)
42
+ if response && response.respond_to?(name)
43
+ response.send(name, *args, &block)
44
+ else
45
+ super(name, *args, &block)
46
+ end
47
+ rescue Rack::Test::Error # no response yet
48
+ super(name, *args, &block)
49
+ end
50
+ alias response last_response
51
+
52
+ class << self
53
+ alias :setup :before unless defined?(Rails)
54
+ alias :teardown :after unless defined?(Rails)
55
+ alias :should :it
56
+ alias :context :describe
57
+ def should_eventually(desc)
58
+ it("should eventually #{desc}") { skip("Should eventually #{desc}") }
59
+ end
60
+ end
61
+ alias :assert_no_match :refute_match
62
+ alias :assert_not_nil :refute_nil
63
+ alias :assert_not_equal :refute_equal
64
+ end
65
+
66
+
67
+ class ColoredIO
68
+ def initialize(io)
69
+ @io = io
70
+ end
71
+
72
+ def print(o)
73
+ case o
74
+ when "." then @io.send(:print, o.colorize(:green))
75
+ when "E" then @io.send(:print, o.colorize(:red))
76
+ when "F" then @io.send(:print, o.colorize(:yellow))
77
+ when "S" then @io.send(:print, o.colorize(:magenta))
78
+ else @io.send(:print, o)
79
+ end
80
+ end
81
+
82
+ def puts(*o)
83
+ super
84
+ end
85
+ end
86
+
87
+ MiniTest::Unit.output = ColoredIO.new($stdout)
@@ -0,0 +1,1942 @@
1
+ require File.expand_path('../../lib/pendragon/padrino', __FILE__)
2
+ $:.unshift(File.dirname(__FILE__))
3
+ require 'helper'
4
+
5
+ class FooError < RuntimeError; end
6
+
7
+ describe "Pendragon::Padrino" do
8
+ setup do
9
+ Padrino::Application.send(:register, Padrino::Rendering)
10
+ Padrino::Application.send(:register, Pendragon::Padrino)
11
+ Padrino::Rendering::DEFAULT_RENDERING_OPTIONS[:strict_format] = false
12
+ end
13
+
14
+ should "serve static files with simple cache control" do
15
+ mock_app do
16
+ set :static_cache_control, :public
17
+ set :public_folder, File.dirname(__FILE__)
18
+ end
19
+ get "/#{File.basename(__FILE__)}"
20
+ assert headers.has_key?('Cache-Control')
21
+ assert_equal headers['Cache-Control'], 'public'
22
+ end # static simple
23
+
24
+ should "serve static files with cache control and max_age" do
25
+ mock_app do
26
+ set :static_cache_control, [:public, :must_revalidate, {:max_age => 300}]
27
+ set :public_folder, File.dirname(__FILE__)
28
+ end
29
+ get "/#{File.basename(__FILE__)}"
30
+ assert headers.has_key?('Cache-Control')
31
+ assert_equal headers['Cache-Control'], 'public, must-revalidate, max-age=300'
32
+ end # static max_age
33
+
34
+ should 'ignore trailing delimiters for basic route' do
35
+ mock_app do
36
+ get("/foo"){ "okey" }
37
+ get(:test) { "tester" }
38
+ end
39
+ get "/foo"
40
+ assert_equal "okey", body
41
+ get "/foo/"
42
+ assert_equal "okey", body
43
+ get "/test"
44
+ assert_equal "tester", body
45
+ get "/test/"
46
+ assert_equal "tester", body
47
+ end
48
+
49
+ should 'fail with unrecognized route exception when not found' do
50
+ mock_app do
51
+ get(:index){ "okey" }
52
+ end
53
+ get @app.url_for(:index)
54
+ assert_equal "okey", body
55
+ assert_raises(Padrino::Routing::UnrecognizedException) {
56
+ get @app.url_for(:fake)
57
+ }
58
+ end
59
+
60
+ should 'accept regexp routes' do
61
+ mock_app do
62
+ get(%r./fob|/baz.) { "regexp" }
63
+ get("/foo") { "str" }
64
+ get %r./([0-9]+)/. do |num|
65
+ "Your lucky number: #{num} #{params[:captures].first}"
66
+ end
67
+ get %r./page/([0-9]+)|/. do |num|
68
+ "My lucky number: #{num} #{params[:captures].first}"
69
+ end
70
+ end
71
+ get "/foo"
72
+ assert_equal "str", body
73
+ get "/fob"
74
+ assert_equal "regexp", body
75
+ get "/baz"
76
+ assert_equal "regexp", body
77
+ get "/321/"
78
+ assert_equal "Your lucky number: 321 321", body
79
+ get "/page/99"
80
+ assert_equal "My lucky number: 99 99", body
81
+ end
82
+
83
+ should 'accept regexp routes with generate with :generate_with' do
84
+ mock_app do
85
+ get(%r{/fob|/baz}, :name => :foo, :generate_with => '/fob') { "regexp" }
86
+ end
87
+ assert_equal "/fob", @app.url(:foo)
88
+ end
89
+
90
+ should "parse routes with question marks" do
91
+ mock_app do
92
+ get("/foo/?"){ "okey" }
93
+ post('/unauthenticated/?') { "no access" }
94
+ end
95
+ get "/foo"
96
+ assert_equal "okey", body
97
+ get "/foo/"
98
+ assert_equal "okey", body
99
+ post "/unauthenticated"
100
+ assert_equal "no access", body
101
+ post "/unauthenticated/"
102
+ assert_equal "no access", body
103
+ end
104
+
105
+ should 'parse routes that are encoded' do
106
+ mock_app do
107
+ get('/щч') { 'success!' }
108
+ end
109
+ get(URI.escape('/щч'))
110
+ assert_equal 'success!', body
111
+ end
112
+
113
+ should 'parse routes that include encoded slash' do
114
+ mock_app do
115
+ get('/:drive_alias/:path', :path => /.*/){
116
+ "Show #{params[:drive_alias]} and #{params[:path]}"
117
+ }
118
+ end
119
+ get("/drive%2Ffoo/some/path")
120
+ assert_equal "Show drive/foo and some/path", body
121
+ end
122
+
123
+ should 'parse route that contains encoded param.' do
124
+ mock_app do
125
+ get('/foo/:name'){ params[:name] }
126
+ end
127
+ get(URI.escape('/foo/あいうえお'))
128
+ assert_equal 'あいうえお', body
129
+ end
130
+
131
+ should 'encode params using UTF-8' do
132
+ mock_app do
133
+ get('/:foo') { params[:foo].encoding.name }
134
+ end
135
+ get '/bar'
136
+ assert_equal 'UTF-8', body
137
+ end
138
+
139
+ should 'match correctly similar paths' do
140
+ mock_app do
141
+ get("/my/:foo_id"){ params[:foo_id] }
142
+ get("/my/:bar_id/bar"){ params[:bar_id] }
143
+ end
144
+ get "/my/1"
145
+ assert_equal "1", body
146
+ get "/my/2/bar"
147
+ assert_equal "2", body
148
+ end
149
+
150
+ should "match user agents" do
151
+ skip if enable_compiler?
152
+ app = mock_app do
153
+ get("/main", :agent => /IE/){ "hello IE" }
154
+ get("/main"){ "hello" }
155
+ end
156
+ get "/main"
157
+ assert_equal "hello", body
158
+ get "/main", {}, {'HTTP_USER_AGENT' => 'This is IE'}
159
+ assert_equal "hello IE", body
160
+ end
161
+
162
+ should "use regex for parts of a route" do
163
+ app = mock_app do
164
+ get("/main/:id", :id => /\d+/){ "hello #{params[:id]}" }
165
+ end
166
+ get "/main/123"
167
+ assert_equal "hello 123", body
168
+ get "/main/asd"
169
+ assert_equal 404, status
170
+ end
171
+
172
+ should "parse params when use regex for parts of a route" do
173
+ mock_app do
174
+ post :index, :with => [:foo, :bar], :bar => /.+/ do
175
+ "show #{params[:foo]}"
176
+ end
177
+
178
+ get :index, :map => '/mystuff/:a_id/boing/:boing_id' do
179
+ "show #{params[:a_id]} and #{params[:boing_id]}"
180
+ end
181
+ end
182
+ get "/mystuff/5/boing/2"
183
+ assert_equal "show 5 and 2", body
184
+ end
185
+
186
+ should "not generate overlapping head urls" do
187
+ app = mock_app do
188
+ get("/main"){ "hello" }
189
+ post("/main"){ "hello" }
190
+ end
191
+ assert_equal 3, app.routes.size, "should generate GET, HEAD and PUT"
192
+ assert_equal "GET", app.routes[0].request_methods.first
193
+ assert_equal "HEAD", app.routes[1].request_methods.first
194
+ assert_equal "POST", app.routes[2].request_methods.first
195
+ end
196
+
197
+ should 'generate basic urls' do
198
+ mock_app do
199
+ get(:foo){ "/foo" }
200
+ get(:foo, :with => :id){ |id| "/foo/#{id}" }
201
+ get([:foo, :id]){ |id| "/foo/#{id}" }
202
+ get(:hash, :with => :id){ url(:hash, :id => 1) }
203
+ get([:hash, :id]){ url(:hash, :id => 1) }
204
+ get(:array, :with => :id){ url(:array, 23) }
205
+ get([:array, :id]){ url(:array, 23) }
206
+ get(:hash_with_extra, :with => :id){ url(:hash_with_extra, :id => 1, :query => 'string') }
207
+ get([:hash_with_extra, :id]){ url(:hash_with_extra, :id => 1, :query => 'string') }
208
+ get(:array_with_extra, :with => :id){ url(:array_with_extra, 23, :query => 'string') }
209
+ get([:array_with_extra, :id]){ url(:array_with_extra, 23, :query => 'string') }
210
+ get("/old-bar/:id"){ params[:id] }
211
+ post(:mix, :map => "/mix-bar/:id"){ params[:id] }
212
+ get(:mix, :map => "/mix-bar/:id"){ params[:id] }
213
+ end
214
+ get "/foo"
215
+ assert_equal "/foo", body
216
+ get "/foo/123"
217
+ assert_equal "/foo/123", body
218
+ get "/hash/2"
219
+ assert_equal "/hash/1", body
220
+ get "/array/23"
221
+ assert_equal "/array/23", body
222
+ get "/hash_with_extra/1"
223
+ assert_equal "/hash_with_extra/1?query=string", body
224
+ get "/array_with_extra/23"
225
+ assert_equal "/array_with_extra/23?query=string", body
226
+ get "/old-bar/3"
227
+ assert_equal "3", body
228
+ post "/mix-bar/4"
229
+ assert_equal "4", body
230
+ get "/mix-bar/4"
231
+ assert_equal "4", body
232
+ end
233
+
234
+ should 'generate url with format' do
235
+ mock_app do
236
+ get(:a, :provides => :any){ url(:a, :format => :json) }
237
+ get(:b, :provides => :js){ url(:b, :format => :js) }
238
+ get(:c, :provides => [:js, :json]){ url(:c, :format => :json) }
239
+ get(:d, :provides => [:html, :js]){ url(:d, :format => :js, :foo => :bar) }
240
+ end
241
+ get "/a.js"
242
+ assert_equal "/a.json", body
243
+ get "/b.js"
244
+ assert_equal "/b.js", body
245
+ get "/b.ru"
246
+ assert_equal 404, status
247
+ get "/c.js"
248
+ assert_equal "/c.json", body
249
+ get "/c.json"
250
+ assert_equal "/c.json", body
251
+ get "/c.ru"
252
+ assert_equal 404, status
253
+ get "/d"
254
+ assert_equal "/d.js?foo=bar", body
255
+ get "/d.js"
256
+ assert_equal "/d.js?foo=bar", body
257
+ get "/e.xml"
258
+ assert_equal 404, status
259
+ end
260
+
261
+ should 'generate absolute urls' do
262
+ mock_app do
263
+ get(:hash, :with => :id){ absolute_url(:hash, :id => 1) }
264
+ end
265
+ get "/hash/2"
266
+ assert_equal "http://example.org/hash/1", body
267
+ get "https://example.org/hash/2"
268
+ assert_equal "https://example.org/hash/1", body
269
+ end
270
+
271
+ should 'generate proper absolute urls for mounted apps' do
272
+ class Test < Padrino::Application
273
+ get :foo do
274
+ absolute_url(:foo, :id => 1)
275
+ end
276
+ end
277
+ Padrino.mount("Test").to("/test")
278
+ @app = Padrino.application
279
+ get('/test/foo')
280
+ assert_equal 'http://example.org/test/foo?id=1', body
281
+ end
282
+
283
+ should 'allow regex url with format' do
284
+ mock_app do
285
+ get(/.*/, :provides => :any) { "regexp" }
286
+ end
287
+ get "/anything"
288
+ assert_equal "regexp", body
289
+ end
290
+
291
+ should 'use padrino url method' do
292
+ mock_app do
293
+ end
294
+
295
+ assert_equal @app.method(:url).owner, Pendragon::Padrino::ClassMethods
296
+ end
297
+
298
+ should 'work correctly with sinatra redirects' do
299
+ mock_app do
300
+ get(:index) { redirect url(:index) }
301
+ get(:google) { redirect "http://google.com" }
302
+ get("/foo") { redirect "/bar" }
303
+ get("/bar") { "Bar" }
304
+ end
305
+
306
+ get "/"
307
+ assert_equal "http://example.org/", headers['Location']
308
+ get "/google"
309
+ assert_equal "http://google.com", headers['Location']
310
+ get "/foo"
311
+ assert_equal "http://example.org/bar", headers['Location']
312
+ end
313
+
314
+ should "return 406 on Accept-Headers it does not provide" do
315
+ mock_app do
316
+ get(:a, :provides => [:html, :js]){ content_type }
317
+ end
318
+
319
+ get "/a", {}, {"HTTP_ACCEPT" => "application/yaml"}
320
+ assert_equal 406, status
321
+ end
322
+
323
+ should "return 406 on file extensions it does not provide and flag is set" do
324
+ mock_app do
325
+ enable :treat_format_as_accept
326
+ get(:a, :provides => [:html, :js]){ content_type }
327
+ end
328
+
329
+ get "/a.xml", {}, {}
330
+ assert_equal 406, status
331
+ end
332
+
333
+ should "return 404 on file extensions it does not provide and flag is not set" do
334
+ mock_app do
335
+ get(:a, :provides => [:html, :js]){ content_type }
336
+ end
337
+
338
+ get "/a.xml", {}, {}
339
+ assert_equal 404, status
340
+ end
341
+
342
+ should "not set content_type to :html if Accept */* and html not in provides" do
343
+ mock_app do
344
+ get("/foo", :provides => [:json, :xml]) { content_type.to_s }
345
+ end
346
+
347
+ get '/foo', {}, { 'HTTP_ACCEPT' => '*/*;q=0.5' }
348
+ assert_equal 'json', body
349
+ end
350
+
351
+ should "set content_type to :json if Accept contains */*" do
352
+ mock_app do
353
+ get("/foo", :provides => [:json]) { content_type.to_s }
354
+ end
355
+
356
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' }
357
+ assert_equal 'json', body
358
+ end
359
+
360
+ should 'set and get content_type' do
361
+ mock_app do
362
+ get("/foo"){ content_type(:json); content_type.to_s }
363
+ end
364
+ get "/foo"
365
+ assert_equal 'application/json', content_type
366
+ assert_equal 'json', body
367
+ end
368
+
369
+ should "send the appropriate number of params" do
370
+ mock_app do
371
+ get('/id/:user_id', :provides => [:json]) { |user_id, format| user_id}
372
+ end
373
+ get '/id/5.json'
374
+ assert_equal '5', body
375
+ end
376
+
377
+ should "allow .'s in param values" do
378
+ #skip
379
+ mock_app do
380
+ get('/id/:email', :provides => [:json]) { |email, format| [email, format] * '/' }
381
+ end
382
+ get '/id/foo@bar.com.json'
383
+ assert_equal 'foo@bar.com/json', body
384
+ end
385
+
386
+ should "set correct content_type for Accept not equal to */* even if */* also provided" do
387
+ mock_app do
388
+ get("/foo", :provides => [:html, :js, :xml]) { content_type.to_s }
389
+ end
390
+
391
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript, */*;q=0.5' }
392
+ assert_equal 'js', body
393
+ end
394
+
395
+ should "return the first content type in provides if accept header is empty" do
396
+ mock_app do
397
+ get(:a, :provides => [:js]){ content_type.to_s }
398
+ end
399
+
400
+ get "/a", {}, {}
401
+ assert_equal "js", body
402
+ end
403
+
404
+ should "not default to HTML if HTML is not provided and no type is given" do
405
+ mock_app do
406
+ get(:a, :provides => [:js]){ content_type }
407
+ end
408
+
409
+ get "/a", {}, {}
410
+ assert_equal "application/javascript;charset=utf-8", content_type
411
+ end
412
+
413
+ should "not match routes if url_format and http_accept is provided but not included" do
414
+ mock_app do
415
+ get(:a, :provides => [:js, :html]){ content_type }
416
+ end
417
+
418
+ get "/a.xml", {}, {"HTTP_ACCEPT" => "text/html"}
419
+ assert_equal 404, status
420
+ end
421
+
422
+ should "generate routes for format simple" do
423
+ mock_app do
424
+ get(:foo, :provides => [:html, :rss]) { render :haml, "Test" }
425
+ end
426
+ get "/foo"
427
+ assert_equal "Test\n", body
428
+ get "/foo.rss"
429
+ assert_equal "Test\n", body
430
+ end
431
+
432
+ should "should inject the controller name into the request" do
433
+ mock_app do
434
+ controller :posts do
435
+ get(:index) { request.controller }
436
+ controller :mini do
437
+ get(:index) { request.controller }
438
+ end
439
+ end
440
+ end
441
+ get "/posts"
442
+ assert_equal "posts", body
443
+ get "/mini"
444
+ assert_equal "mini", body
445
+ end
446
+
447
+ should "should inject the action name into the request" do
448
+ mock_app do
449
+ controller :posts do
450
+ get('/omnomnom(/:id)?') { request.action.inspect }
451
+ controller :mini do
452
+ get([:a, :b, :c]) { request.action.inspect }
453
+ end
454
+ end
455
+ end
456
+ get "/posts/omnomnom"
457
+ assert_equal "\"/omnomnom(/:id)?\"", body
458
+ get "/mini/a/b/c"
459
+ assert_equal ":a", body
460
+ end
461
+
462
+ should "support not_found" do
463
+ mock_app do
464
+ not_found { 'whatever' }
465
+
466
+ get :index, :map => "/" do
467
+ 'index'
468
+ end
469
+ end
470
+ get '/wrong'
471
+ assert_equal 404, status
472
+ assert_equal 'whatever', body
473
+ get '/'
474
+ assert_equal 'index', body
475
+ assert_equal 200, status
476
+ end
477
+
478
+ should "should inject the route into the request" do
479
+ mock_app do
480
+ controller :posts do
481
+ get(:index) { request.route_obj.name.to_s }
482
+ end
483
+ end
484
+ get "/posts"
485
+ assert_equal "posts index", body
486
+ end
487
+
488
+ should "preserve the format if you set it manually" do
489
+ mock_app do
490
+ before do
491
+ params[:format] = "json"
492
+ end
493
+
494
+ get "test", :provides => [:html, :json] do
495
+ content_type.inspect
496
+ end
497
+ end
498
+ get "/test"
499
+ assert_equal ":json", body
500
+ get "/test.html"
501
+ assert_equal ":json", body
502
+ get "/test.php"
503
+ assert_equal ":json", body
504
+ end
505
+
506
+ should "correctly accept '.' in the route" do
507
+ mock_app do
508
+ get "test.php", :provides => [:html, :json] do
509
+ content_type.inspect
510
+ end
511
+ end
512
+ get "/test.php"
513
+ assert_equal ":html", body
514
+ get "/test.php.json"
515
+ assert_equal ":json", body
516
+ end
517
+
518
+ should "correctly accept priority of format" do
519
+ mock_app do
520
+ get "test.php", :provides => [:html, :json, :xml] do
521
+ content_type.inspect
522
+ end
523
+ end
524
+
525
+ get "/test.php"
526
+ assert_equal ":html", body
527
+ get "/test.php", {}, { 'HTTP_ACCEPT' => 'application/xml' }
528
+ assert_equal ":xml", body
529
+ get "/test.php?format=json", { 'HTTP_ACCEPT' => 'application/xml' }
530
+ assert_equal ":json", body
531
+ get "/test.php.json?format=html", { 'HTTP_ACCEPT' => 'application/xml' }
532
+ assert_equal ":json", body
533
+ end
534
+
535
+ should "generate routes for format with controller" do
536
+ mock_app do
537
+ controller :posts do
538
+ get(:index, :provides => [:html, :rss, :atom, :js]) { render :haml, "Index.#{content_type}" }
539
+ get(:show, :with => :id, :provides => [:html, :rss, :atom]) { render :haml, "Show.#{content_type}" }
540
+ end
541
+ end
542
+ get "/posts"
543
+ assert_equal "Index.html\n", body
544
+ get "/posts.rss"
545
+ assert_equal "Index.rss\n", body
546
+ get "/posts.atom"
547
+ assert_equal "Index.atom\n", body
548
+ get "/posts.js"
549
+ assert_equal "Index.js\n", body
550
+ get "/posts/show/5"
551
+ assert_equal "Show.html\n", body
552
+ get "/posts/show/5.rss"
553
+ assert_equal "Show.rss\n", body
554
+ get "/posts/show/10.atom"
555
+ assert_equal "Show.atom\n", body
556
+ end
557
+
558
+ should 'map routes' do
559
+ mock_app do
560
+ get(:bar){ "bar" }
561
+ end
562
+ get "/bar"
563
+ assert_equal "bar", body
564
+ assert_equal "/bar", @app.url(:bar)
565
+ end
566
+
567
+ should 'remove index from path' do
568
+ mock_app do
569
+ get(:index){ "index" }
570
+ get("/accounts/index"){ "accounts" }
571
+ end
572
+ get "/"
573
+ assert_equal "index", body
574
+ assert_equal "/", @app.url(:index)
575
+ get "/accounts/index"
576
+ assert_equal "accounts", body
577
+ end
578
+
579
+ should 'remove index from path with params' do
580
+ mock_app do
581
+ get(:index, :with => :name){ "index with #{params[:name]}" }
582
+ end
583
+ get "/bobby"
584
+ assert_equal "index with bobby", body
585
+ assert_equal "/john", @app.url(:index, :name => "john")
586
+ end
587
+
588
+ should 'parse named params' do
589
+ mock_app do
590
+ get(:print, :with => :id){ "Im #{params[:id]}" }
591
+ end
592
+ get "/print/9"
593
+ assert_equal "Im 9", body
594
+ assert_equal "/print/9", @app.url(:print, :id => 9)
595
+ end
596
+
597
+ should '405 on wrong request_method' do
598
+ skip if enable_compiler?
599
+ mock_app do
600
+ post('/bar'){ "bar" }
601
+ end
602
+ get "/bar"
603
+ assert_equal 405, status
604
+ end
605
+
606
+ should 'respond to' do
607
+ mock_app do
608
+ get(:a, :provides => :js){ "js" }
609
+ get(:b, :provides => :any){ "any" }
610
+ get(:c, :provides => [:js, :json]){ "js,json" }
611
+ get(:d, :provides => [:html, :js]){ "html,js"}
612
+ end
613
+ get "/a"
614
+ assert_equal 200, status
615
+ assert_equal "js", body
616
+ get "/a.js"
617
+ assert_equal "js", body
618
+ get "/b"
619
+ assert_equal "any", body
620
+ # TODO randomly fails in minitest :(
621
+ # assert_raises(RuntimeError) { get "/b.foo" }
622
+ get "/c"
623
+ assert_equal 200, status
624
+ assert_equal "js,json", body
625
+ get "/c.js"
626
+ assert_equal "js,json", body
627
+ get "/c.json"
628
+ assert_equal "js,json", body
629
+ get "/d"
630
+ assert_equal "html,js", body
631
+ get "/d.js"
632
+ assert_equal "html,js", body
633
+ end
634
+
635
+ should 'respond_to and set content_type' do
636
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
637
+ mock_app do
638
+ get :a, :provides => :any do
639
+ case content_type
640
+ when :js then "js"
641
+ when :json then "json"
642
+ when :foo then "foo"
643
+ when :html then "html"
644
+ end
645
+ end
646
+ end
647
+ get "/a.js"
648
+ assert_equal "js", body
649
+ assert_equal 'application/javascript;charset=utf-8', response["Content-Type"]
650
+ get "/a.json"
651
+ assert_equal "json", body
652
+ assert_equal 'application/json;charset=utf-8', response["Content-Type"]
653
+ get "/a.foo"
654
+ assert_equal "foo", body
655
+ assert_equal 'application/foo;charset=utf-8', response["Content-Type"]
656
+ get "/a"
657
+ assert_equal "html", body
658
+ assert_equal 'text/html;charset=utf-8', response["Content-Type"]
659
+ end
660
+
661
+ should 'use controllers' do
662
+ mock_app do
663
+ controller "/admin" do
664
+ get("/"){ "index" }
665
+ get("/show/:id"){ "show #{params[:id]}" }
666
+ end
667
+ end
668
+ get "/admin"
669
+ assert_equal "index", body
670
+ get "/admin/show/1"
671
+ assert_equal "show 1", body
672
+ end
673
+
674
+ should 'use named controllers' do
675
+ mock_app do
676
+ controller :admin do
677
+ get(:index, :with => :id){ params[:id] }
678
+ get(:show, :with => :id){ "show #{params[:id]}" }
679
+ end
680
+ controllers :foo, :bar do
681
+ get(:index){ "foo_bar_index" }
682
+ end
683
+ end
684
+ get "/admin/1"
685
+ assert_equal "1", body
686
+ get "/admin/show/1"
687
+ assert_equal "show 1", body
688
+ assert_equal "/admin/1", @app.url(:admin, :index, :id => 1)
689
+ assert_equal "/admin/show/1", @app.url(:admin, :show, :id => 1)
690
+ get "/foo/bar"
691
+ assert_equal "foo_bar_index", body
692
+ end
693
+
694
+ should 'use map and with' do
695
+ mock_app do
696
+ get :index, :map => '/bugs', :with => :id do
697
+ params[:id]
698
+ end
699
+ end
700
+ get '/bugs/4'
701
+ assert_equal '4', body
702
+ assert_equal "/bugs/4", @app.url(:index, :id => 4)
703
+ end
704
+
705
+ should "ignore trailing delimiters within a named controller" do
706
+ mock_app do
707
+ controller :posts do
708
+ get(:index, :provides => [:html, :js]){ "index" }
709
+ get(:new) { "new" }
710
+ get(:show, :with => :id){ "show #{params[:id]}" }
711
+ end
712
+ end
713
+ get "/posts"
714
+ assert_equal "index", body
715
+ get "/posts/"
716
+ assert_equal "index", body
717
+ get "/posts.js"
718
+ assert_equal "index", body
719
+ get "/posts.js/"
720
+ assert_equal "index", body
721
+ get "/posts/new"
722
+ assert_equal "new", body
723
+ get "/posts/new/"
724
+ assert_equal "new", body
725
+ end
726
+
727
+ should "ignore trailing delimiters within a named controller for unnamed actions" do
728
+ mock_app do
729
+ controller :accounts do
730
+ get("/") { "account_index" }
731
+ get("/new") { "new" }
732
+ end
733
+ controller :votes do
734
+ get("/") { "vote_index" }
735
+ end
736
+ end
737
+ get "/accounts"
738
+ assert_equal "account_index", body
739
+ get "/accounts/"
740
+ assert_equal "account_index", body
741
+ get "/accounts/new"
742
+ assert_equal "new", body
743
+ get "/accounts/new/"
744
+ assert_equal "new", body
745
+ get "/votes"
746
+ assert_equal "vote_index", body
747
+ get "/votes/"
748
+ assert_equal "vote_index", body
749
+ end
750
+
751
+ should 'use named controllers with array routes' do
752
+ mock_app do
753
+ controller :admin do
754
+ get(:index){ "index" }
755
+ get(:show, :with => :id){ "show #{params[:id]}" }
756
+ end
757
+ controllers :foo, :bar do
758
+ get(:index){ "foo_bar_index" }
759
+ end
760
+ end
761
+ get "/admin"
762
+ assert_equal "index", body
763
+ get "/admin/show/1"
764
+ assert_equal "show 1", body
765
+ assert_equal "/admin", @app.url(:admin, :index)
766
+ assert_equal "/admin/show/1", @app.url(:admin, :show, :id => 1)
767
+ get "/foo/bar"
768
+ assert_equal "foo_bar_index", body
769
+ end
770
+
771
+ should "support a reindex action and remove index inside controller" do
772
+ mock_app do
773
+ controller :posts do
774
+ get(:index){ "index" }
775
+ get(:reindex){ "reindex" }
776
+ end
777
+ end
778
+ get "/posts"
779
+ assert_equal "index", body
780
+ get "/posts/reindex"
781
+ assert_equal "/posts/reindex", @app.url(:posts, :reindex)
782
+ assert_equal "reindex", body
783
+ end
784
+
785
+ should 'use uri_root' do
786
+ mock_app do
787
+ get(:foo){ "foo" }
788
+ end
789
+ @app.uri_root = '/'
790
+ assert_equal "/foo", @app.url(:foo)
791
+ @app.uri_root = '/testing'
792
+ assert_equal "/testing/foo", @app.url(:foo)
793
+ @app.uri_root = '/testing/'
794
+ assert_equal "/testing/foo", @app.url(:foo)
795
+ @app.uri_root = 'testing/bar///'
796
+ assert_equal "/testing/bar/foo", @app.url(:foo)
797
+ end
798
+
799
+ should 'use uri_root with controllers' do
800
+ mock_app do
801
+ controller :foo do
802
+ get(:bar){ "bar" }
803
+ end
804
+ end
805
+ @app.uri_root = '/testing'
806
+ assert_equal "/testing/foo/bar", @app.url(:foo, :bar)
807
+ end
808
+
809
+ should 'use RACK_BASE_URI' do
810
+ mock_app do
811
+ get(:foo){ "foo" }
812
+ end
813
+ # Wish there was a side-effect free way to test this...
814
+ ENV['RACK_BASE_URI'] = '/'
815
+ assert_equal "/foo", @app.url(:foo)
816
+ ENV['RACK_BASE_URI'] = '/testing'
817
+ assert_equal "/testing/foo", @app.url(:foo)
818
+ ENV['RACK_BASE_URI'] = nil
819
+ end
820
+
821
+ should 'reset routes' do
822
+ mock_app do
823
+ get("/"){ "foo" }
824
+ reset_router!
825
+ end
826
+ get "/"
827
+ assert_equal 404, status
828
+ end
829
+
830
+ should "match params and format" do
831
+ app = mock_app do
832
+ get '/:id', :provides => [:json, :html] do |id, _|
833
+ id
834
+ end
835
+
836
+ get 'format/:id', :provides => [:json, :html] do |id, format|
837
+ format
838
+ end
839
+ end
840
+
841
+ get '/123.html'
842
+ assert_equal '123', body
843
+
844
+ get 'format/123.html'
845
+ assert_equal 'html', body
846
+ end
847
+
848
+
849
+ should 'respect priorities' do
850
+ skip if enable_compiler?
851
+ route_order = []
852
+ mock_app do
853
+ get(:index, :priority => :normal) { route_order << :normal; pass }
854
+ get(:index, :priority => :low) { route_order << :low; "hello" }
855
+ get(:index, :priority => :high) { route_order << :high; pass }
856
+ end
857
+ get '/'
858
+ assert_equal [:high, :normal, :low], route_order
859
+ assert_equal "hello", body
860
+ end
861
+
862
+ should 'catch all after controllers' do
863
+ mock_app do
864
+ get(:index, :with => :slug, :priority => :low) { "catch all" }
865
+ controllers :contact do
866
+ get(:index) { "contact"}
867
+ end
868
+ end
869
+ get "/contact"
870
+ assert_equal "contact", body
871
+ get "/foo"
872
+ assert_equal "catch all", body
873
+ end
874
+
875
+ should 'allow optionals' do
876
+ mock_app do
877
+ get(:show, :map => "/stories/:type(/:category)?") do
878
+ "#{params[:type]}/#{params[:category]}"
879
+ end
880
+ end
881
+ get "/stories/foo"
882
+ assert_equal "foo/", body
883
+ get "/stories/foo/bar"
884
+ assert_equal "foo/bar", body
885
+ end
886
+
887
+ should 'apply maps' do
888
+ mock_app do
889
+ controllers :admin do
890
+ get(:index, :map => "/"){ "index" }
891
+ get(:show, :with => :id, :map => "/show"){ "show #{params[:id]}" }
892
+ get(:edit, :map => "/edit/:id/product"){ "edit #{params[:id]}" }
893
+ get(:wacky, :map => "/wacky-:id-:product_id"){ "wacky #{params[:id]}-#{params[:product_id]}" }
894
+ end
895
+ end
896
+ get "/"
897
+ assert_equal "index", body
898
+ get @app.url(:admin, :index)
899
+ assert_equal "index", body
900
+ get "/show/1"
901
+ assert_equal "show 1", body
902
+ get "/edit/1/product"
903
+ assert_equal "edit 1", body
904
+ get "/wacky-1-2"
905
+ assert_equal "wacky 1-2", body
906
+ end
907
+
908
+ should 'apply maps when given path is kind of hash' do
909
+ mock_app do
910
+ controllers :admin do
911
+ get(:foobar, "/foo/bar"){ "foobar" }
912
+ end
913
+ end
914
+ get "/foo/bar"
915
+ assert_equal "foobar", body
916
+ end
917
+
918
+ should "apply parent to route" do
919
+ mock_app do
920
+ controllers :project do
921
+ get(:index, :parent => :user) { "index #{params[:user_id]}" }
922
+ get(:index, :parent => [:user, :section]) { "index #{params[:user_id]} #{params[:section_id]}" }
923
+ get(:edit, :with => :id, :parent => :user) { "edit #{params[:id]} #{params[:user_id]}"}
924
+ get(:show, :with => :id, :parent => [:user, :product]) { "show #{params[:id]} #{params[:user_id]} #{params[:product_id]}"}
925
+ end
926
+ end
927
+ get "/user/1/project"
928
+ assert_equal "index 1", body
929
+ get "/user/1/section/3/project"
930
+ assert_equal "index 1 3", body
931
+ get "/user/1/project/edit/2"
932
+ assert_equal "edit 2 1", body
933
+ get "/user/1/product/2/project/show/3"
934
+ assert_equal "show 3 1 2", body
935
+ end
936
+
937
+ should "respect parent precedence: controllers parents go before route parents" do
938
+ mock_app do
939
+ controllers :project do
940
+ get(:index, :parent => :user) { "index #{params[:user_id]}" }
941
+ end
942
+
943
+ controllers :bar, :parent => :foo do
944
+ get(:index) { "index on foo #{params[:foo_id]} @ bar" }
945
+ get(:index, :parent => :baz) { "index on foo #{params[:foo_id]} @ baz #{params[:baz_id]} @ bar" }
946
+ end
947
+ end
948
+
949
+ get "/user/1/project"
950
+ assert_equal "index 1", body
951
+ get "/foo/1/bar"
952
+ assert_equal "index on foo 1 @ bar", body
953
+ get "/foo/1/baz/2/bar"
954
+ assert_equal "index on foo 1 @ baz 2 @ bar", body
955
+ end
956
+
957
+ should "keep a reference to the parent on the route" do
958
+ mock_app do
959
+ controllers :project do
960
+ get(:index, :parent => :user) { "index #{params[:user_id]}" }
961
+ get(:index, :parent => [:user, :section]) { "index #{params[:user_id]} #{params[:section_id]}" }
962
+ get(:edit, :with => :id, :parent => :user) { "edit #{params[:id]} #{params[:user_id]}"}
963
+ get(:show, :with => :id, :parent => [:user, :product]) { "show #{params[:id]} #{params[:user_id]} #{params[:product_id]}"}
964
+ end
965
+
966
+ controllers :bar, :parent => :foo do
967
+ get(:index) { "index on foo/bar" }
968
+ get(:index, :parent => :baz) { "index on foo/baz/bar" }
969
+ end
970
+ end
971
+
972
+ # get "/user/1/project"
973
+ assert_equal :user, @app.routes[0].parent
974
+ # get "/user/1/section/3/project"
975
+ assert_equal [:user, :section], @app.routes[2].parent
976
+ # get "/user/1/project/edit/2"
977
+ assert_equal :user, @app.routes[4].parent
978
+ # get "/user/1/product/2/project/show/3"
979
+ assert_equal [:user, :product], @app.routes[6].parent
980
+ # get "/foo/1/bar"
981
+ assert_equal :foo, @app.routes[8].parent
982
+ # get "/foo/1/baz/2/bar"
983
+ assert_equal [:foo, :baz], @app.routes[10].parent
984
+ end
985
+
986
+ should "apply parent to controller" do
987
+ mock_app do
988
+ controller :project, :parent => :user do
989
+ get(:index) { "index #{params[:user_id]}"}
990
+ get(:edit, :with => :id, :parent => :user) { "edit #{params[:id]} #{params[:user_id]}"}
991
+ get(:show, :with => :id, :parent => :product) { "show #{params[:id]} #{params[:user_id]} #{params[:product_id]}"}
992
+ end
993
+ end
994
+
995
+ user_project_url = "/user/1/project"
996
+ get user_project_url
997
+ assert_equal "index 1", body
998
+ assert_equal user_project_url, @app.url(:project, :index, :user_id => 1)
999
+
1000
+ user_project_edit_url = "/user/1/project/edit/2"
1001
+ get user_project_edit_url
1002
+ assert_equal "edit 2 1", body
1003
+ assert_equal user_project_edit_url, @app.url(:project, :edit, :user_id => 1, :id => 2)
1004
+
1005
+ user_product_project_url = "/user/1/product/2/project/show/3"
1006
+ get user_product_project_url
1007
+ assert_equal "show 3 1 2", body
1008
+ assert_equal user_product_project_url, @app.url(:project, :show, :user_id => 1, :product_id => 2, :id => 3)
1009
+ end
1010
+
1011
+ should "apply parent with shallowing to controller" do
1012
+ mock_app do
1013
+ controller :project do
1014
+ parent :user
1015
+ parent :shop, :optional => true
1016
+ get(:index) { "index #{params[:user_id]} #{params[:shop_id]}" }
1017
+ get(:edit, :with => :id) { "edit #{params[:id]} #{params[:user_id]} #{params[:shop_id]}" }
1018
+ get(:show, :with => :id, :parent => :product) { "show #{params[:id]} #{params[:user_id]} #{params[:product_id]} #{params[:shop_id]}" }
1019
+ end
1020
+ end
1021
+
1022
+ assert_equal "/user/1/project", @app.url(:project, :index, :user_id => 1, :shop_id => nil)
1023
+ assert_equal "/user/1/shop/23/project", @app.url(:project, :index, :user_id => 1, :shop_id => 23)
1024
+
1025
+ user_project_url = "/user/1/project"
1026
+ get user_project_url
1027
+ assert_equal "index 1 ", body
1028
+ assert_equal user_project_url, @app.url(:project, :index, :user_id => 1)
1029
+
1030
+ user_project_edit_url = "/user/1/project/edit/2"
1031
+ get user_project_edit_url
1032
+ assert_equal "edit 2 1 ", body
1033
+ assert_equal user_project_edit_url, @app.url(:project, :edit, :user_id => 1, :id => 2)
1034
+
1035
+ user_product_project_url = "/user/1/product/2/project/show/3"
1036
+ get user_product_project_url
1037
+ assert_equal "show 3 1 2 ", body
1038
+ assert_equal user_product_project_url, @app.url(:project, :show, :user_id => 1, :product_id => 2, :id => 3)
1039
+
1040
+ user_project_url = "/user/1/shop/1/project"
1041
+ get user_project_url
1042
+ assert_equal "index 1 1", body
1043
+ assert_equal user_project_url, @app.url(:project, :index, :user_id => 1, :shop_id => 1)
1044
+
1045
+ user_project_edit_url = "/user/1/shop/1/project/edit/2"
1046
+ get user_project_edit_url
1047
+ assert_equal "edit 2 1 1", body
1048
+ assert_equal user_project_edit_url, @app.url(:project, :edit, :user_id => 1, :id => 2, :shop_id => 1)
1049
+
1050
+ user_product_project_url = "/user/1/shop/1/product/2/project/show/3"
1051
+ get user_product_project_url
1052
+ assert_equal "show 3 1 2 1", body
1053
+ assert_equal user_product_project_url, @app.url(:project, :show, :user_id => 1, :product_id => 2, :id => 3, :shop_id => 1)
1054
+ end
1055
+
1056
+ should "respect map in parents with shallowing" do
1057
+ mock_app do
1058
+ controller :project do
1059
+ parent :shop, :map => "/foo/bar"
1060
+ get(:index) { "index #{params[:shop_id]}" }
1061
+ end
1062
+ end
1063
+
1064
+ shop_project_url = "/foo/bar/1/project"
1065
+ get shop_project_url
1066
+ assert_equal "index 1", body
1067
+ assert_equal shop_project_url, @app.url(:project, :index, :shop_id => 1)
1068
+ end
1069
+
1070
+ should "use default values" do
1071
+ mock_app do
1072
+ controller :lang => :it do
1073
+ get(:index, :map => "/:lang") { "lang is #{params[:lang]}" }
1074
+ end
1075
+ # This is only for be sure that default values
1076
+ # work only for the given controller
1077
+ get(:foo, :map => "/foo") {}
1078
+ end
1079
+ assert_equal "/it", @app.url(:index)
1080
+ assert_equal "/foo", @app.url(:foo)
1081
+ get "/en"
1082
+ assert_equal "lang is en", body
1083
+ end
1084
+
1085
+ should "transitions to the next matching route on pass" do
1086
+ skip if enable_compiler?
1087
+ mock_app do
1088
+ get '/:foo' do
1089
+ pass
1090
+ 'Hello Foo'
1091
+ end
1092
+ get '/:bar' do
1093
+ 'Hello World'
1094
+ end
1095
+ end
1096
+
1097
+ get '/za'
1098
+ assert_equal 'Hello World', body
1099
+ end
1100
+
1101
+ should "filters by accept header" do
1102
+ mock_app do
1103
+ get '/foo', :provides => [:xml, :js] do
1104
+ request.env['HTTP_ACCEPT']
1105
+ end
1106
+ end
1107
+
1108
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
1109
+ assert ok?
1110
+ assert_equal 'application/xml', body
1111
+ assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
1112
+
1113
+ get '/foo.xml'
1114
+ assert ok?
1115
+ assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
1116
+
1117
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
1118
+ assert ok?
1119
+ assert_equal 'application/javascript', body
1120
+ assert_equal 'application/javascript;charset=utf-8', response.headers['Content-Type']
1121
+
1122
+ get '/foo.js'
1123
+ assert ok?
1124
+ assert_equal 'application/javascript;charset=utf-8', response.headers['Content-Type']
1125
+
1126
+ get '/foo', {}, { "HTTP_ACCEPT" => 'text/html' }
1127
+ assert_equal 406, status
1128
+ end
1129
+
1130
+ should "does not allow global provides" do
1131
+ mock_app do
1132
+ provides :xml
1133
+
1134
+ get("/foo"){ "Foo in #{content_type.inspect}" }
1135
+ get("/bar"){ "Bar in #{content_type.inspect}" }
1136
+ end
1137
+
1138
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
1139
+ assert_equal 'Foo in :xml', body
1140
+ get '/foo'
1141
+ assert_equal 'Foo in :xml', body
1142
+
1143
+ get '/bar', {}, { 'HTTP_ACCEPT' => 'application/xml' }
1144
+ assert_equal 'Bar in nil', body
1145
+ end
1146
+
1147
+ should "does not allow global provides in controller" do
1148
+ mock_app do
1149
+ controller :base do
1150
+ provides :xml
1151
+
1152
+ get(:foo, "/foo"){ "Foo in #{content_type.inspect}" }
1153
+ get(:bar, "/bar"){ "Bar in #{content_type.inspect}" }
1154
+ end
1155
+ end
1156
+
1157
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
1158
+ assert_equal 'Foo in :xml', body
1159
+ get '/foo'
1160
+ assert_equal 'Foo in :xml', body
1161
+
1162
+ get '/bar', {}, { 'HTTP_ACCEPT' => 'application/xml' }
1163
+ assert_equal 'Bar in nil', body
1164
+ end
1165
+
1166
+ should "map non named routes in controllers" do
1167
+ mock_app do
1168
+ controller :base do
1169
+ get("/foo") { "ok" }
1170
+ get("/bar") { "ok" }
1171
+ end
1172
+ end
1173
+
1174
+ get "/base/foo"
1175
+ assert ok?
1176
+ get "/base/bar"
1177
+ assert ok?
1178
+ end
1179
+
1180
+ should "set content_type to :html for both empty Accept as well as Accept text/html" do
1181
+ mock_app do
1182
+ provides :html
1183
+
1184
+ get("/foo"){ content_type.to_s }
1185
+ end
1186
+
1187
+ get '/foo', {}, {}
1188
+ assert_equal 'html', body
1189
+
1190
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html' }
1191
+ assert_equal 'html', body
1192
+ end
1193
+
1194
+ should "set content_type to :html if Accept */*" do
1195
+ mock_app do
1196
+ get("/foo", :provides => [:html, :js]) { content_type.to_s }
1197
+ end
1198
+ get '/foo', {}, {}
1199
+ assert_equal 'html', body
1200
+
1201
+ get '/foo', {}, { 'HTTP_ACCEPT' => '*/*;q=0.5' }
1202
+ assert_equal 'html', body
1203
+ end
1204
+
1205
+ should "set content_type to :js if Accept includes both application/javascript and */*;q=0.5" do
1206
+ mock_app do
1207
+ get("/foo", :provides => [:html, :js]) { content_type.to_s }
1208
+ end
1209
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript, */*;q=0.5' }
1210
+ assert_equal 'js', body
1211
+ end
1212
+
1213
+ should "set content_type to :html if Accept */* and provides of :any" do
1214
+ mock_app do
1215
+ get("/foo", :provides => :any) { content_type.to_s }
1216
+ end
1217
+
1218
+ get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' }
1219
+ assert_equal 'html', body
1220
+ end
1221
+
1222
+ should "set content_type to :js if Accept includes both application/javascript, */*;q=0.5 and provides of :any" do
1223
+ mock_app do
1224
+ get("/foo", :provides => :any) { content_type.to_s }
1225
+ end
1226
+
1227
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript, */*;q=0.5' }
1228
+ assert_equal 'js', body
1229
+ end
1230
+
1231
+ should 'allows custom route-conditions to be set via route options and halt' do
1232
+ protector = Module.new do
1233
+ def protect(*args)
1234
+ condition {
1235
+ unless authorize(params["user"], params["password"])
1236
+ halt 403, "go away"
1237
+ end
1238
+ }
1239
+ end
1240
+ end
1241
+
1242
+ mock_app do
1243
+ register protector
1244
+
1245
+ helpers do
1246
+ def authorize(username, password)
1247
+ username == "foo" && password == "bar"
1248
+ end
1249
+ end
1250
+
1251
+ get "/", :protect => true do
1252
+ "hey"
1253
+ end
1254
+ end
1255
+
1256
+ get "/"
1257
+ assert forbidden?
1258
+ assert_equal "go away", body
1259
+
1260
+ get "/", :user => "foo", :password => "bar"
1261
+ assert ok?
1262
+ assert_equal "hey", body
1263
+ end
1264
+
1265
+ should 'allows custom route-conditions to be set via route options using two routes' do
1266
+ skip if enable_compiler?
1267
+ protector = Module.new do
1268
+ def protect(*args)
1269
+ condition { authorize(params["user"], params["password"]) }
1270
+ end
1271
+ end
1272
+
1273
+ mock_app do
1274
+ register protector
1275
+
1276
+ helpers do
1277
+ def authorize(username, password)
1278
+ username == "foo" && password == "bar"
1279
+ end
1280
+ end
1281
+
1282
+ get "/", :protect => true do
1283
+ "hey"
1284
+ end
1285
+
1286
+ get "/" do
1287
+ "go away"
1288
+ end
1289
+ end
1290
+
1291
+ get "/"
1292
+ assert_equal "go away", body
1293
+
1294
+ get "/", :user => "foo", :password => "bar"
1295
+ assert ok?
1296
+ assert_equal "hey", body
1297
+ end
1298
+
1299
+ should "allow concise routing" do
1300
+ mock_app do
1301
+ get :index, ":id" do
1302
+ params[:id]
1303
+ end
1304
+
1305
+ get :map, "route/:id" do
1306
+ params[:id]
1307
+ end
1308
+ end
1309
+
1310
+ get "/123"
1311
+ assert_equal "123", body
1312
+
1313
+ get "/route/123"
1314
+ assert_equal "123", body
1315
+ end
1316
+
1317
+ should "support halting with 404 and message" do
1318
+ mock_app do
1319
+ controller do
1320
+ get :index do
1321
+ halt 404, "not found"
1322
+ end
1323
+ end
1324
+ end
1325
+
1326
+ get "/"
1327
+ assert_equal 404, status
1328
+ assert_equal "not found", body
1329
+ end
1330
+
1331
+ should "allow passing & halting in before filters" do
1332
+ skip if enable_compiler?
1333
+ mock_app do
1334
+ controller do
1335
+ before { env['QUERY_STRING'] == 'secret' or pass }
1336
+ get :index do
1337
+ "secret index"
1338
+ end
1339
+ end
1340
+
1341
+ controller do
1342
+ before { env['QUERY_STRING'] == 'halt' and halt 401, 'go away!' }
1343
+ get :index do
1344
+ "index"
1345
+ end
1346
+ end
1347
+ end
1348
+
1349
+ get "/?secret"
1350
+ assert_equal "secret index", body
1351
+
1352
+ get "/?halt"
1353
+ assert_equal "go away!", body
1354
+ assert_equal 401, status
1355
+
1356
+ get "/"
1357
+ assert_equal "index", body
1358
+ end
1359
+
1360
+ should 'scope filters in the given controller' do
1361
+ mock_app do
1362
+ before { @global = 'global' }
1363
+ after { @global = nil }
1364
+
1365
+ controller :foo do
1366
+ before { @foo = :foo }
1367
+ after { @foo = nil }
1368
+ get("/") { [@foo, @bar, @global].compact.join(" ") }
1369
+ end
1370
+
1371
+ get("/") { [@foo, @bar, @global].compact.join(" ") }
1372
+
1373
+ controller :bar do
1374
+ before { @bar = :bar }
1375
+ after { @bar = nil }
1376
+ get("/") { [@foo, @bar, @global].compact.join(" ") }
1377
+ end
1378
+ end
1379
+
1380
+ get "/bar"
1381
+ assert_equal "bar global", body
1382
+
1383
+ get "/foo"
1384
+ assert_equal "foo global", body
1385
+
1386
+ get "/"
1387
+ assert_equal "global", body
1388
+ end
1389
+
1390
+ should 'works with optionals params' do
1391
+ mock_app do
1392
+ get("/foo(/:bar)?") { params[:bar] }
1393
+ end
1394
+
1395
+ get "/foo/bar"
1396
+ assert_equal "bar", body
1397
+
1398
+ get "/foo"
1399
+ assert_equal "", body
1400
+ end
1401
+
1402
+ should 'work with multiple dashed params' do
1403
+ mock_app do
1404
+ get "/route/:foo/:bar/:baz", :provides => :html do
1405
+ "#{params[:foo]};#{params[:bar]};#{params[:baz]}"
1406
+ end
1407
+ end
1408
+
1409
+ get "/route/foo/bar/baz"
1410
+ assert_equal 'foo;bar;baz', body
1411
+
1412
+ get "/route/foo/bar-whatever/baz"
1413
+ assert_equal 'foo;bar-whatever;baz', body
1414
+ end
1415
+
1416
+ should 'work with arbitrary params' do
1417
+ mock_app do
1418
+ get(:testing) { params[:foo] }
1419
+ end
1420
+
1421
+ url = @app.url(:testing, :foo => 'bar')
1422
+ assert_equal "/testing?foo=bar", url
1423
+ get url
1424
+ assert_equal "bar", body
1425
+ end
1426
+
1427
+ should 'ignore nil params' do
1428
+ mock_app do
1429
+ get(:testing, :provides => [:html, :json]) do
1430
+ end
1431
+ end
1432
+ assert_equal '/testing.html', @app.url(:testing, :format => :html)
1433
+ assert_equal '/testing', @app.url(:testing, :format => nil)
1434
+ end
1435
+
1436
+ should 'be able to access params in a before filter' do
1437
+ username_from_before_filter = nil
1438
+
1439
+ mock_app do
1440
+ before do
1441
+ username_from_before_filter = params[:username]
1442
+ end
1443
+
1444
+ get :users, :with => :username do
1445
+ end
1446
+ end
1447
+ get '/users/josh'
1448
+ assert_equal 'josh', username_from_before_filter
1449
+ end
1450
+
1451
+ should "be able to access params normally when a before filter is specified" do
1452
+ mock_app do
1453
+ before { }
1454
+ get :index do
1455
+ params.inspect
1456
+ end
1457
+ end
1458
+ get '/?test=what'
1459
+ assert_equal '{"test"=>"what"}', body
1460
+ end
1461
+
1462
+ should 'work with controller and arbitrary params' do
1463
+ mock_app do
1464
+ get(:testing) { params[:foo] }
1465
+ controller :test1 do
1466
+ get(:url1) { params[:foo] }
1467
+ get(:url2, :provides => [:html, :json]) { params[:foo] }
1468
+ end
1469
+ end
1470
+
1471
+ url = @app.url(:test1, :url1, :foo => 'bar1')
1472
+ assert_equal "/test1/url1?foo=bar1", url
1473
+ get url
1474
+ assert_equal "bar1", body
1475
+
1476
+ url = @app.url(:test1, :url2, :foo => 'bar2')
1477
+ assert_equal "/test1/url2?foo=bar2", url
1478
+ get url
1479
+ assert_equal "bar2", body
1480
+ end
1481
+
1482
+ should "parse two routes with the same path but different http verbs" do
1483
+ mock_app do
1484
+ get(:index) { "This is the get index" }
1485
+ post(:index) { "This is the post index" }
1486
+ end
1487
+ get "/"
1488
+ assert_equal "This is the get index", body
1489
+ post "/"
1490
+ assert_equal "This is the post index", body
1491
+ end
1492
+
1493
+ should "use optionals params" do
1494
+ mock_app do
1495
+ get(:index, :map => "/:foo(/:bar)?") { "#{params[:foo]}-#{params[:bar]}" }
1496
+ end
1497
+ get "/foo"
1498
+ assert_equal "foo-", body
1499
+ get "/foo/bar"
1500
+ assert_equal "foo-bar", body
1501
+ end
1502
+
1503
+ should "parse two routes with the same path but different http verbs and provides" do
1504
+ mock_app do
1505
+ get(:index, :provides => [:html, :json]) { "This is the get index.#{content_type}" }
1506
+ post(:index, :provides => [:html, :json]) { "This is the post index.#{content_type}" }
1507
+ end
1508
+ get "/"
1509
+ assert_equal "This is the get index.html", body
1510
+ post "/"
1511
+ assert_equal "This is the post index.html", body
1512
+ get "/.json"
1513
+ assert_equal "This is the get index.json", body
1514
+ get "/.js"
1515
+ assert_equal 404, status
1516
+ post "/.json"
1517
+ assert_equal "This is the post index.json", body
1518
+ post "/.js"
1519
+ assert_equal 404, status
1520
+ end
1521
+
1522
+ should "allow controller level mapping" do
1523
+ mock_app do
1524
+ controller :map => "controller-:id" do
1525
+ get(:url3) { "#{params[:id]}" }
1526
+ get(:url4, :map => 'test-:id2') { "#{params[:id]}, #{params[:id2]}" }
1527
+ end
1528
+ end
1529
+
1530
+ url = @app.url(:url3, :id => 1)
1531
+ assert_equal "/controller-1/url3", url
1532
+ get url
1533
+ assert_equal "1", body
1534
+
1535
+ url = @app.url(:url4, 1, 2)
1536
+ assert_equal "/controller-1/test-2", url
1537
+ get url
1538
+ assert_equal "1, 2", body
1539
+ end
1540
+
1541
+ should "replace name of named controller with mapping path" do
1542
+ mock_app do
1543
+ controller :ugly, :map => "/pretty/:id" do
1544
+ get(:url3) { "#{params[:id]}" }
1545
+ get(:url4, :map => 'test-:id2') { "#{params[:id]}, #{params[:id2]}" }
1546
+ end
1547
+ controller :voldemort, :map => "" do
1548
+ get(:url5) { "okay" }
1549
+ end
1550
+ end
1551
+
1552
+ url = @app.url(:ugly, :url3, :id => 1)
1553
+ assert_equal "/pretty/1/url3", url
1554
+ get url
1555
+ assert_equal "1", body
1556
+
1557
+ url = @app.url(:ugly, :url4, 3, 5)
1558
+ assert_equal "/pretty/3/test-5", url
1559
+ get url
1560
+ assert_equal "3, 5", body
1561
+
1562
+ url = @app.url(:voldemort, :url5)
1563
+ assert_equal "/url5", url
1564
+ get url
1565
+ assert_equal 'okay', body
1566
+ end
1567
+
1568
+ should 'use absolute and relative maps' do
1569
+ mock_app do
1570
+ controller :one do
1571
+ parent :three
1572
+ get :index, :map => 'one' do; end
1573
+ get :index2, :map => '/one' do; end
1574
+ end
1575
+
1576
+ controller :two, :map => 'two' do
1577
+ parent :three
1578
+ get :index, :map => 'two' do; end
1579
+ get :index2, :map => '/two', :with => :id do; end
1580
+ end
1581
+ end
1582
+ assert_equal "/three/three_id/one", @app.url(:one, :index, 'three_id')
1583
+ assert_equal "/one", @app.url(:one, :index2)
1584
+ assert_equal "/two/three/three_id/two", @app.url(:two, :index, 'three_id')
1585
+ assert_equal "/two/four_id", @app.url(:two, :index2, 'four_id')
1586
+ end
1587
+
1588
+ should "work with params and parent options" do
1589
+ mock_app do
1590
+ controller :test2, :parent => :parent1, :parent1_id => 1 do
1591
+ get(:url3) { params[:foo] }
1592
+ get(:url4, :with => :with1) { params[:foo] }
1593
+ get(:url5, :with => :with2, :provides => [:html]) { params[:foo] }
1594
+ end
1595
+ end
1596
+
1597
+ url = @app.url(:test2, :url3, :foo => 'bar3')
1598
+ assert_equal "/parent1/1/test2/url3?foo=bar3", url
1599
+ get url
1600
+ assert_equal "bar3", body
1601
+
1602
+ url = @app.url(:test2, :url4, :with1 => 'awith1', :foo => 'bar4')
1603
+ assert_equal "/parent1/1/test2/url4/awith1?foo=bar4", url
1604
+ get url
1605
+ assert_equal "bar4", body
1606
+
1607
+ url = @app.url(:test2, :url5, :with2 => 'awith1', :foo => 'bar5')
1608
+ assert_equal "/parent1/1/test2/url5/awith1?foo=bar5", url
1609
+ get url
1610
+ assert_equal "bar5", body
1611
+ end
1612
+
1613
+ should "parse params without explicit provides for every matching route" do
1614
+ mock_app do
1615
+ get(:index, :map => "/foos/:bar") { "get bar = #{params[:bar]}" }
1616
+ post :create, :map => "/foos/:bar", :provides => [:html, :js] do
1617
+ "post bar = #{params[:bar]}"
1618
+ end
1619
+ end
1620
+
1621
+ get "/foos/hello"
1622
+ assert_equal "get bar = hello", body
1623
+ post "/foos/hello"
1624
+ assert_equal "post bar = hello", body
1625
+ post "/foos/hello.js"
1626
+ assert_equal "post bar = hello", body
1627
+ end
1628
+
1629
+ should "properly route to first foo with two similar routes" do
1630
+ mock_app do
1631
+ controllers do
1632
+ get('/foo/') { "this is foo" }
1633
+ get(:show, :map => "/foo/:bar/:id") { "/foo/#{params[:bar]}/#{params[:id]}" }
1634
+ end
1635
+ end
1636
+ get "/foo"
1637
+ assert_equal "this is foo", body
1638
+ get "/foo/"
1639
+ assert_equal "this is foo", body
1640
+ get '/foo/5/10'
1641
+ assert_equal "/foo/5/10", body
1642
+ end
1643
+
1644
+ should "index routes should be optional when nested" do
1645
+ mock_app do
1646
+ controller '/users', :provides => [:json] do
1647
+ get '/' do
1648
+ "foo"
1649
+ end
1650
+ end
1651
+ end
1652
+ get "/users.json"
1653
+ assert_equal "foo", body
1654
+ end
1655
+
1656
+ should "use provides as conditional" do
1657
+ mock_app do
1658
+ provides :json
1659
+ get "/" do
1660
+ "foo"
1661
+ end
1662
+ end
1663
+ get "/.json"
1664
+ assert_equal "foo", body
1665
+ end
1666
+
1667
+ should_eventually "reset provides for routes that didn't use it" do
1668
+ mock_app do
1669
+ get('/foo', :provides => :js){}
1670
+ get('/bar'){}
1671
+ end
1672
+ get '/foo'
1673
+ assert ok?
1674
+ get '/foo.js'
1675
+ assert ok?
1676
+ get '/bar'
1677
+ assert ok?
1678
+ get '/bar.js'
1679
+ assert_equal 404, status
1680
+ end
1681
+
1682
+ should "pass controller conditions to each route" do
1683
+ counter = 0
1684
+
1685
+ mock_app do
1686
+ self.class.send(:define_method, :increment!) do |*args|
1687
+ condition { counter += 1 }
1688
+ end
1689
+
1690
+ controller :posts, :conditions => {:increment! => true} do
1691
+ get("/foo") { "foo" }
1692
+ get("/bar") { "bar" }
1693
+ end
1694
+
1695
+ end
1696
+
1697
+ get "/posts/foo"
1698
+ get "/posts/bar"
1699
+ assert_equal 2, counter
1700
+ end
1701
+
1702
+ should "allow controller conditions to be overridden" do
1703
+ counter = 0
1704
+
1705
+ mock_app do
1706
+ self.class.send(:define_method, :increment!) do |increment|
1707
+ condition { counter += 1 } if increment
1708
+ end
1709
+
1710
+ controller :posts, :conditions => {:increment! => true} do
1711
+ get("/foo") { "foo" }
1712
+ get("/bar", :increment! => false) { "bar" }
1713
+ end
1714
+
1715
+ end
1716
+
1717
+ get "/posts/foo"
1718
+ get "/posts/bar"
1719
+ assert_equal 1, counter
1720
+ end
1721
+
1722
+ should "parse params with class level provides" do
1723
+ mock_app do
1724
+ controllers :posts, :provides => [:html, :js] do
1725
+ post(:create, :map => "/foo/:bar/:baz/:id") {
1726
+ "POST CREATE #{params[:bar]} - #{params[:baz]} - #{params[:id]}"
1727
+ }
1728
+ end
1729
+ controllers :topics, :provides => [:js, :html] do
1730
+ get(:show, :map => "/foo/:bar/:baz/:id") { render "topics/show" }
1731
+ post(:create, :map => "/foo/:bar/:baz") { "TOPICS CREATE #{params[:bar]} - #{params[:baz]}" }
1732
+ end
1733
+ end
1734
+ post "/foo/bar/baz.js"
1735
+ assert_equal "TOPICS CREATE bar - baz", body, "should parse params with explicit .js"
1736
+ post @app.url(:topics, :create, :format => :js, :bar => 'bar', :baz => 'baz')
1737
+ assert_equal "TOPICS CREATE bar - baz", body, "should parse params from generated url"
1738
+ post "/foo/bar/baz/5.js"
1739
+ assert_equal "POST CREATE bar - baz - 5", body
1740
+ post @app.url(:posts, :create, :format => :js, :bar => 'bar', :baz => 'baz', :id => 5)
1741
+ assert_equal "POST CREATE bar - baz - 5", body
1742
+ end
1743
+
1744
+ should "parse params properly with inline provides" do
1745
+ mock_app do
1746
+ controllers :posts do
1747
+ post(:create, :map => "/foo/:bar/:baz/:id", :provides => [:html, :js]) {
1748
+ "POST CREATE #{params[:bar]} - #{params[:baz]} - #{params[:id]}"
1749
+ }
1750
+ end
1751
+ controllers :topics do
1752
+ get(:show, :map => "/foo/:bar/:baz/:id", :provides => [:html, :js]) { render "topics/show" }
1753
+ post(:create, :map => "/foo/:bar/:baz", :provides => [:html, :js]) { "TOPICS CREATE #{params[:bar]} - #{params[:baz]}" }
1754
+ end
1755
+ end
1756
+ post @app.url(:topics, :create, :format => :js, :bar => 'bar', :baz => 'baz')
1757
+ assert_equal "TOPICS CREATE bar - baz", body, "should properly post to topics create action"
1758
+ post @app.url(:posts, :create, :format => :js, :bar => 'bar', :baz => 'baz', :id => 5)
1759
+ assert_equal "POST CREATE bar - baz - 5", body, "should properly post to create action"
1760
+ end
1761
+
1762
+ should "have overideable format" do
1763
+ ::Rack::Mime::MIME_TYPES[".other"] = "text/html"
1764
+ mock_app do
1765
+ before do
1766
+ params[:format] ||= :other
1767
+ end
1768
+ get("/format_test", :provides => [:html, :other]){ content_type.to_s }
1769
+ end
1770
+ get "/format_test"
1771
+ assert_equal "other", body
1772
+ ::Rack::Mime::MIME_TYPES.delete('.other')
1773
+ end
1774
+
1775
+ should 'invokes handlers registered with ::error when raised' do
1776
+ mock_app do
1777
+ set :raise_errors, false
1778
+ error(FooError) { 'Foo!' }
1779
+ get '/' do
1780
+ raise FooError
1781
+ end
1782
+ end
1783
+ get '/'
1784
+ assert_equal 500, status
1785
+ assert_equal 'Foo!', body
1786
+ end
1787
+
1788
+ should 'have MethodOverride middleware' do
1789
+ mock_app do
1790
+ put('/') { 'okay' }
1791
+ end
1792
+ assert @app.method_override?
1793
+ post '/', {'_method'=>'PUT'}, {}
1794
+ assert_equal 200, status
1795
+ assert_equal 'okay', body
1796
+ end
1797
+
1798
+ should 'return value from params' do
1799
+ mock_app do
1800
+ get("/foo/:bar"){ raise "'bar' should be a string" unless params[:bar].kind_of? String}
1801
+ end
1802
+ get "/foo/50"
1803
+ assert ok?
1804
+ end
1805
+
1806
+ should 'have MethodOverride middleware with more options' do
1807
+ mock_app do
1808
+ put('/hi', :provides => [:json]) { 'hi' }
1809
+ end
1810
+ post '/hi', {'_method'=>'PUT'}
1811
+ assert_equal 200, status
1812
+ assert_equal 'hi', body
1813
+ post '/hi.json', {'_method'=>'PUT'}
1814
+ assert_equal 200, status
1815
+ assert_equal 'hi', body
1816
+ unless enable_compiler?
1817
+ post '/hi.json'
1818
+ assert_equal 405, status
1819
+ end
1820
+ end
1821
+
1822
+ should 'parse nested params' do
1823
+ mock_app do
1824
+ get(:index) { "%s %s" % [params[:account][:name], params[:account][:surname]] }
1825
+ end
1826
+ get "/?account[name]=foo&account[surname]=bar"
1827
+ assert_equal 'foo bar', body
1828
+ get @app.url(:index, "account[name]" => "foo", "account[surname]" => "bar")
1829
+ assert_equal 'foo bar', body
1830
+ end
1831
+
1832
+ should 'render sinatra NotFound page' do
1833
+ mock_app { set :environment, :development }
1834
+ get "/"
1835
+ assert_equal 404, status
1836
+ assert_match %r{(Sinatra doesn&rsquo;t know this ditty.|<h1>Not Found</h1>)}, body
1837
+ end
1838
+
1839
+ should 'render a custom NotFound page' do
1840
+ mock_app do
1841
+ error(Sinatra::NotFound) { "not found" }
1842
+ end
1843
+ get "/"
1844
+ assert_equal 404, status
1845
+ assert_match(/not found/, body)
1846
+ end
1847
+
1848
+ should 'render a custom 404 page using not_found' do
1849
+ mock_app do
1850
+ not_found { "custom 404 not found" }
1851
+ end
1852
+ get "/"
1853
+ assert_equal 404, status
1854
+ assert_equal "custom 404 not found", body
1855
+ end
1856
+
1857
+ should 'render a custom error page using error method' do
1858
+ skip
1859
+ mock_app do
1860
+ error(404) { "custom 404 error" }
1861
+ end
1862
+ get "/"
1863
+ assert_equal 404, status
1864
+ assert_equal "custom 404 error", body
1865
+ end
1866
+
1867
+ should 'render a custom 403 page' do
1868
+ mock_app do
1869
+ error(403) { "custom 403 not found" }
1870
+ get("/") { status 403 }
1871
+ end
1872
+ get "/"
1873
+ assert_equal 403, status
1874
+ assert_equal "custom 403 not found", body
1875
+ end
1876
+
1877
+ should 'recognize paths' do
1878
+ mock_app do
1879
+ controller :foo do
1880
+ get(:bar, :map => "/my/:id/custom-route") { }
1881
+ end
1882
+ get(:simple, :map => "/simple/:id") { }
1883
+ get(:with_format, :with => :id, :provides => :js) { }
1884
+ end
1885
+ assert_equal [:"foo bar", { :id => "fantastic" }], @app.recognize_path(@app.url(:foo, :bar, :id => :fantastic))
1886
+ assert_equal [:"foo bar", { :id => "18" }], @app.recognize_path(@app.url(:foo, :bar, :id => 18))
1887
+ assert_equal [:simple, { :id => "bar" }], @app.recognize_path(@app.url(:simple, :id => "bar"))
1888
+ assert_equal [:simple, { :id => "true" }], @app.recognize_path(@app.url(:simple, :id => true))
1889
+ assert_equal [:simple, { :id => "9" }], @app.recognize_path(@app.url(:simple, :id => 9))
1890
+ assert_equal [:with_format, { :id => "bar", :format => "js" }], @app.recognize_path(@app.url(:with_format, :id => "bar", :format => :js))
1891
+ assert_equal [:with_format, { :id => "true", :format => "js" }], @app.recognize_path(@app.url(:with_format, :id => true, :format => "js"))
1892
+ assert_equal [:with_format, { :id => "9", :format => "js" }], @app.recognize_path(@app.url(:with_format, :id => 9, :format => :js))
1893
+ end
1894
+
1895
+ should 'have current_path' do
1896
+ mock_app do
1897
+ controller :foo do
1898
+ get(:index) { current_path }
1899
+ get :bar, :map => "/paginate/:page" do
1900
+ current_path
1901
+ end
1902
+ get(:after) { current_path }
1903
+ end
1904
+ end
1905
+ get "/paginate/10"
1906
+ assert_equal "/paginate/10", body
1907
+ get "/foo/after"
1908
+ assert_equal "/foo/after", body
1909
+ get "/foo"
1910
+ assert_equal "/foo", body
1911
+ end
1912
+
1913
+ should 'accept :map and :parent' do
1914
+ mock_app do
1915
+ controller :posts do
1916
+ get :show, :parent => :users, :map => "posts/:id" do
1917
+ "#{params[:user_id]}-#{params[:id]}"
1918
+ end
1919
+ end
1920
+ end
1921
+ get '/users/123/posts/321'
1922
+ assert_equal "123-321", body
1923
+ end
1924
+
1925
+ should 'change params in current_path' do
1926
+ mock_app do
1927
+ get :index, :map => "/paginate/:page" do
1928
+ current_path(:page => 66)
1929
+ end
1930
+ end
1931
+ get @app.url(:index, :page => 10)
1932
+ assert_equal "/paginate/66", body
1933
+ end
1934
+
1935
+ should 'not route get :users, :with => :id to /users//' do
1936
+ mock_app do
1937
+ get(:users, :with => :id) { 'boo' }
1938
+ end
1939
+ get '/users//'
1940
+ assert_equal 404, status
1941
+ end
1942
+ end