pendragon 0.3.0

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