merb 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/README +23 -160
  2. data/Rakefile +15 -14
  3. data/app_generators/merb/merb_generator.rb +4 -3
  4. data/app_generators/merb/templates/Rakefile +1 -6
  5. data/app_generators/merb/templates/app/mailers/views/layout/{application.erb → application.html.erb} +0 -0
  6. data/app_generators/merb/templates/app/mailers/views/layout/application.text.erb +1 -0
  7. data/app_generators/merb/templates/app/parts/views/layout/application.html.erb +1 -0
  8. data/app_generators/merb/templates/app/views/layout/application.html.erb +2 -2
  9. data/app_generators/merb/templates/config/dependencies.rb +1 -1
  10. data/app_generators/merb/templates/config/router.rb +4 -1
  11. data/app_generators/merb/templates/spec/spec_helper.rb +2 -3
  12. data/lib/autotest/merb_rspec.rb +1 -0
  13. data/lib/merb/abstract_controller.rb +31 -2
  14. data/lib/merb/controller.rb +5 -5
  15. data/lib/merb/core_ext/get_args.rb +5 -1
  16. data/lib/merb/exceptions.rb +17 -0
  17. data/lib/merb/generators/merb_app/merb_app.rb +4 -1
  18. data/lib/merb/generators/merb_plugin.rb +4 -1
  19. data/lib/merb/logger.rb +5 -1
  20. data/lib/merb/mail_controller.rb +1 -1
  21. data/lib/merb/mailer.rb +2 -2
  22. data/lib/merb/mixins/controller.rb +5 -1
  23. data/lib/merb/mixins/render.rb +57 -27
  24. data/lib/merb/part_controller.rb +1 -1
  25. data/lib/merb/request.rb +2 -2
  26. data/lib/merb/server.rb +33 -5
  27. data/lib/merb/template/erubis.rb +1 -1
  28. data/lib/merb.rb +15 -5
  29. data/merb_generators/resource/resource_generator.rb +9 -2
  30. data/spec/fixtures/config/merb.yml +18 -0
  31. data/spec/fixtures/controllers/dispatch_spec_controllers.rb +227 -0
  32. data/spec/fixtures/controllers/render_spec_controllers.rb +115 -0
  33. data/spec/fixtures/foo.rb +3 -0
  34. data/spec/fixtures/mailers/views/layout/application.html.erb +3 -0
  35. data/spec/fixtures/mailers/views/layout/application.text.erb +3 -0
  36. data/spec/fixtures/mailers/views/test_mail_controller/eighth.html.erb +1 -0
  37. data/spec/fixtures/mailers/views/test_mail_controller/eighth.text.erb +1 -0
  38. data/spec/fixtures/mailers/views/test_mail_controller/first.html.erb +1 -0
  39. data/spec/fixtures/mailers/views/test_mail_controller/first.text.erb +1 -0
  40. data/spec/fixtures/mailers/views/test_mail_controller/ninth.html.erb +1 -0
  41. data/spec/fixtures/mailers/views/test_mail_controller/ninth.text.erb +1 -0
  42. data/spec/fixtures/mailers/views/test_mail_controller/second.text.erb +1 -0
  43. data/spec/fixtures/mailers/views/test_mail_controller/third.html.erb +1 -0
  44. data/spec/fixtures/models/router_spec_models.rb +20 -0
  45. data/spec/fixtures/parts/views/layout/todo_part.html.erb +3 -0
  46. data/spec/fixtures/parts/views/layout/todo_part.xml.erb +3 -0
  47. data/spec/fixtures/parts/views/todo_part/formatted_output.html.erb +1 -0
  48. data/spec/fixtures/parts/views/todo_part/formatted_output.js.erb +1 -0
  49. data/spec/fixtures/parts/views/todo_part/formatted_output.xml.erb +1 -0
  50. data/spec/fixtures/parts/views/todo_part/list.html.erb +3 -0
  51. data/spec/fixtures/sample.txt +1 -0
  52. data/spec/fixtures/views/erubis.html.erb +1 -0
  53. data/spec/fixtures/views/examples/_erubis.html.erb +1 -0
  54. data/spec/fixtures/views/examples/_haml.html.haml +1 -0
  55. data/spec/fixtures/views/examples/_markaby.html.mab +1 -0
  56. data/spec/fixtures/views/examples/_throw_content.html.erb +6 -0
  57. data/spec/fixtures/views/examples/hello.xml.builder +1 -0
  58. data/spec/fixtures/views/examples/js.js.erb +1 -0
  59. data/spec/fixtures/views/examples/template_catch_content.html.erb +15 -0
  60. data/spec/fixtures/views/examples/template_catch_content_from_partial.html.erb +6 -0
  61. data/spec/fixtures/views/examples/template_throw_content.html.erb +10 -0
  62. data/spec/fixtures/views/exceptions/admin_access_required.html.erb +1 -0
  63. data/spec/fixtures/views/extension_template_controller/_nested_js.js.erb +1 -0
  64. data/spec/fixtures/views/extension_template_controller/_nested_xml.xml.erb +1 -0
  65. data/spec/fixtures/views/extension_template_controller/_render_partial_multiple_times.html.erb +1 -0
  66. data/spec/fixtures/views/extension_template_controller/erubis_templates.html.erb +1 -0
  67. data/spec/fixtures/views/extension_template_controller/erubis_templates.js.erb +1 -0
  68. data/spec/fixtures/views/extension_template_controller/erubis_templates.rhtml +1 -0
  69. data/spec/fixtures/views/extension_template_controller/erubis_templates.xml.erb +1 -0
  70. data/spec/fixtures/views/extension_template_controller/haml_index.html.haml +0 -0
  71. data/spec/fixtures/views/extension_template_controller/haml_templates.html.haml +1 -0
  72. data/spec/fixtures/views/extension_template_controller/haml_templates.js.haml +1 -0
  73. data/spec/fixtures/views/extension_template_controller/haml_templates.xml.haml +1 -0
  74. data/spec/fixtures/views/extension_template_controller/index.html.erb +0 -0
  75. data/spec/fixtures/views/extension_template_controller/markaby_index.html.mab +0 -0
  76. data/spec/fixtures/views/extension_template_controller/markaby_templates.html.mab +1 -0
  77. data/spec/fixtures/views/extension_template_controller/markaby_templates.js.mab +1 -0
  78. data/spec/fixtures/views/extension_template_controller/markaby_templates.xml.mab +1 -0
  79. data/spec/fixtures/views/extension_template_controller/render_multiple_partials.html.erb +4 -0
  80. data/spec/fixtures/views/extension_template_controller/render_nested_js.js.erb +1 -0
  81. data/spec/fixtures/views/extension_template_controller/render_nested_xml.xml.erb +1 -0
  82. data/spec/fixtures/views/haml.html.haml +1 -0
  83. data/spec/fixtures/views/haml.xml.haml +2 -0
  84. data/spec/fixtures/views/layout/application.html.erb +1 -0
  85. data/spec/fixtures/views/layout/application.xml.erb +1 -0
  86. data/spec/fixtures/views/layout/nested/example.html.erb +1 -0
  87. data/spec/fixtures/views/markaby.html.mab +1 -0
  88. data/spec/fixtures/views/nested/example/test.html.erb +1 -0
  89. data/spec/fixtures/views/partials/_erubis.html.erb +1 -0
  90. data/spec/fixtures/views/partials/_erubis_collection.html.erb +1 -0
  91. data/spec/fixtures/views/partials/_erubis_collection_with_locals.html.erb +1 -0
  92. data/spec/fixtures/views/partials/_erubis_new.html.erb +1 -0
  93. data/spec/fixtures/views/partials/_haml.html.haml +1 -0
  94. data/spec/fixtures/views/partials/_haml_collection.html.haml +1 -0
  95. data/spec/fixtures/views/partials/_haml_collection_with_locals.html.haml +1 -0
  96. data/spec/fixtures/views/partials/_haml_new.html.haml +1 -0
  97. data/spec/fixtures/views/partials/_markaby.html.mab +1 -0
  98. data/spec/fixtures/views/partials/_markaby_collection.html.mab +1 -0
  99. data/spec/fixtures/views/partials/_markaby_collection_with_locals.html.mab +1 -0
  100. data/spec/fixtures/views/partials/_markaby_new.html.mab +1 -0
  101. data/spec/fixtures/views/render_object_controller/render_object_with_template.html.erb +1 -0
  102. data/spec/fixtures/views/render_object_controller/render_object_with_template.js.erb +1 -0
  103. data/spec/fixtures/views/render_object_controller/render_object_with_template.xml.erb +1 -0
  104. data/spec/fixtures/views/template_views/interface__buffer_erubis.html.erb +4 -0
  105. data/spec/fixtures/views/template_views/interface__buffer_haml.html.haml +7 -0
  106. data/spec/fixtures/views/template_views/interface__buffer_markaby.html.mab +7 -0
  107. data/spec/fixtures/views/template_views/interface_capture_erubis.html.erb +15 -0
  108. data/spec/fixtures/views/template_views/interface_capture_haml.html.haml +15 -0
  109. data/spec/fixtures/views/template_views/interface_capture_markaby.html.mab +4 -0
  110. data/spec/fixtures/views/template_views/interface_concat_erubis.html.erb +12 -0
  111. data/spec/fixtures/views/template_views/interface_concat_haml.html.haml +11 -0
  112. data/spec/fixtures/views/template_views/interface_concat_markaby.html.mab +14 -0
  113. data/spec/fixtures/views/test.dir/the_template.html.erb +1 -0
  114. data/spec/merb/abstract_controller_spec.rb +37 -0
  115. data/spec/merb/caching_spec.rb +102 -0
  116. data/spec/merb/config_spec.rb +29 -0
  117. data/spec/merb/controller_filters_spec.rb +188 -0
  118. data/spec/merb/controller_spec.rb +144 -0
  119. data/spec/merb/cookie_store_spec.rb +85 -0
  120. data/spec/merb/core_ext_spec.rb +430 -0
  121. data/spec/merb/dispatch_spec.rb +514 -0
  122. data/spec/merb/fake_request_spec.rb +72 -0
  123. data/spec/merb/form_control_mixin_spec.rb +431 -0
  124. data/spec/merb/generator_spec.rb +121 -0
  125. data/spec/merb/handler_spec.rb +169 -0
  126. data/spec/merb/mail_controller_spec.rb +144 -0
  127. data/spec/merb/mailer_spec.rb +87 -0
  128. data/spec/merb/multipart_spec.rb +49 -0
  129. data/spec/merb/part_controller_spec.rb +92 -0
  130. data/spec/merb/plugins_spec.rb +80 -0
  131. data/spec/merb/render_spec.rb +378 -0
  132. data/spec/merb/request_spec.rb +243 -0
  133. data/spec/merb/responder_spec.rb +561 -0
  134. data/spec/merb/router_spec.rb +726 -0
  135. data/spec/merb/template_spec.rb +41 -0
  136. data/spec/merb/upload_handler_spec.rb +101 -0
  137. data/spec/merb/view_context_spec.rb +148 -0
  138. data/spec/spec_generator_helper.rb +19 -0
  139. data/spec/spec_helper.rb +88 -0
  140. metadata +203 -65
  141. data/lib/merb/caching/store/memcache.rb +0 -20
  142. data/script/destroy +0 -14
  143. data/script/generate +0 -14
@@ -0,0 +1,726 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'benchmark'
3
+ include Benchmark
4
+
5
+ require FIXTURES / 'models/router_spec_models'
6
+ $TESTING = true
7
+
8
+ # OpenStruct fails to return 'method' correctly, which we require for our Request object
9
+ class SimpleRequest < OpenStruct
10
+ def method() @table[:method] end
11
+ end
12
+
13
+ describe Merb::Router::CachedProc do
14
+ it "should register a regular expression" do
15
+ regexp = /t.*e.*s.*t/
16
+ cc = Merb::Router::CachedProc.new(regexp)
17
+ Merb::Router::CachedProc[cc.index].cache.should == regexp
18
+ end
19
+
20
+ it "should register a proc" do
21
+ testproc = proc { puts 'test' }
22
+ cc = Merb::Router::CachedProc.new(testproc)
23
+ Merb::Router::CachedProc[cc.index].cache.should == testproc
24
+ end
25
+
26
+ it "should return ruby code as an evaluatable string" do
27
+ testproc = proc { 'test proc' }
28
+ cc = Merb::Router::CachedProc.new(testproc)
29
+ "#{cc}".should == "CachedProc[#{cc.index}].cache"
30
+ eval("Merb::Router::#{cc}.call").should == "test proc"
31
+ end
32
+ end
33
+
34
+ describe Merb::Router do
35
+ it "should compile to an if / elsif statement" do
36
+ lambda {
37
+ Merb::Router.prepare do |r|
38
+ r.match('/:controller/:action').to_resources(:controller => '/admin/:controller')
39
+ end
40
+ Merb::Router.compiled_statement.should match(/^\s*if/m)
41
+ }.should_not raise_error
42
+ end
43
+
44
+ it "should match against requests" do
45
+ Merb::Router.prepare do |r|
46
+ r.match('/:controller/:action').to_resources(:controller => '/admin/:controller')
47
+ end
48
+ request = Merb::Test::FakeRequest.new(:request_uri => "/test/request")
49
+ result = Merb::Router.match(request, {})
50
+ end
51
+
52
+ it "should be able to prepend routes to the @@routes list" do
53
+ r1, r2 = nil, nil
54
+ Merb::Router.prepare do |r|
55
+ r1 = r.match('/:controller/:action').to(:controller => '/admin/:controller')
56
+ end
57
+ Merb::Router.prepend do |r|
58
+ r2 = r.match('/:controller/:action').to(:controller => '/admin/:controller')
59
+ end
60
+ Merb::Router.routes[0].should == r2
61
+ Merb::Router.routes[1].should == r1
62
+ end
63
+
64
+ it "should be able to append routes to the @@routes list" do
65
+ r1, r2 = nil, nil
66
+ Merb::Router.prepare do |r|
67
+ r1 = r.match('/:controller/:action').to(:controller => '/admin/:controller')
68
+ end
69
+ Merb::Router.append do |r|
70
+ r2 = r.match('/:controller/:action').to(:controller => '/admin/:controller')
71
+ end
72
+ Merb::Router.routes[0].should == r1
73
+ Merb::Router.routes[1].should == r2
74
+ end
75
+
76
+ # it "should be fast" do
77
+ # Merb::Router.prepare do |r|
78
+ # r.resource :icon
79
+ # r.resources :posts, :member => {:stats => [:get, :put]},
80
+ # :collection => {:filter => [:get]} do |post|
81
+ # post.resources :comments, :member => {:stats => [:get, :put]}
82
+ # post.resource :profile
83
+ # end
84
+ # r.resources :as do |a|
85
+ # a.resources :bs do |b|
86
+ # b.resources :cs
87
+ # end
88
+ # end
89
+ # r.match("/admin") do |admin|
90
+ # admin.resources :tags
91
+ # end
92
+ # r.default_routes
93
+ # end
94
+ # request = Merb::Test::FakeRequest.new(:request_uri => "/test/request")
95
+ #
96
+ # bm(12) do |test|
97
+ # # user system total real
98
+ # # with CachedCode 4.510000 0.050000 4.560000 ( 5.244656)
99
+ # # with in-place regexps 2.130000 0.030000 2.160000 ( 2.272443)
100
+ # test.report("with in-place regexps") do
101
+ # 20_000.times do
102
+ # Merb::Router.match(request)
103
+ # end
104
+ # end
105
+ # end
106
+ # end
107
+ end
108
+
109
+ describe Merb::Router, "when doing route matching with a big set of example routes" do
110
+ require 'set'
111
+ def should_only_have_keys(hash, *keys)
112
+ Set.new(hash.keys).should == Set.new(keys)
113
+ end
114
+
115
+ before(:all) do
116
+ Merb::Router.prepare do |r|
117
+ # A simple route match, sends "/contact" to Info#contact
118
+ # (i.e. the 'contact' method inside the 'Info' controller)
119
+ r.match("/contact").
120
+ to(:controller => "info", :action => "contact")
121
+
122
+ # Use placeholders (e.g. :book_id) in the match, and they will be passed along to params
123
+ r.match("/books/:book_id/:action").
124
+ to(:controller => "books")
125
+
126
+ # Use placeholders in the "to" results for more complicated routing, e.g. for modules
127
+ r.match("/admin/:module/:controller/:action").
128
+ to(:controller => ":module/:controller")
129
+ r.match("/admin/:module/:controller/:action/:id").
130
+ to(:controller => ":module/:controller")
131
+
132
+ # Use a 'match' block to factor out repetitive 'match' parts
133
+ r.match("/accounts") do |a|
134
+ # The following will match "/accounts/overview" and route to Accounts#overview
135
+ a.match("/overview").
136
+ to(:controller => "accounts", :action => "overview")
137
+ a.match("/:id/:action").
138
+ to(:controller => "accounts")
139
+ a.match("/:id/:action.:format").
140
+ to(:controller => "accounts")
141
+ end
142
+
143
+ # Use a 'to' block to factor out repetitive 'to' parts
144
+ r.to(:controller => "accounts") do |a|
145
+ $r = a.match("/reports").
146
+ to(:action => "reports") # maps to Accounts#reports
147
+
148
+ a.match("/slideshow/:id").
149
+ to(:action => "slideshow") # maps to Accounts#slideshow
150
+ end
151
+
152
+ # Use a regular expression as the path matcher. Note that you must specify the
153
+ # ^ (beginning of line) and $ (end of line) boundaries if you desire them.
154
+ r.match(%r{^/movies/:id/movie-[a-z][a-zA-Z\-]+$}).
155
+ to(:controller => "movies", :action => "search_engine_optimizer")
156
+
157
+ # Use square-bracket notation to replace param results with captures from the path
158
+ r.match(%r[^/movies/(\d+)-(\d+)-(\d+)$]).
159
+ to(:controller => "movies", :movie_id => "[1][2][3]", :action => "show")
160
+
161
+ # Use the second optional argument of 'match' to be more specific about the request;
162
+ # in this case, only accept the POST method for the /movies/create action
163
+ r.match("/movies/create", :method => "post").
164
+ to(:controller => "movies", :action => "create")
165
+
166
+ # Use variables from the 'match' as results sent to the controller in the params hash,
167
+ # e.g. :user_agent[1] will be replaced with either 'MSIE' or 'Gecko' in the following case:
168
+ r.match(%r[^/movies/(.+)], :user_agent => /(MSIE|Gecko)/).
169
+ to(:controller => "movies", :title => "[1]", :action => "show", :agent => ":user_agent[1]")
170
+
171
+ # The 'match' method can also be called without the path string or regexp.
172
+ # In this example, direct all insecure traffic to a Insecure#index
173
+ r.match(:protocol => "http://").
174
+ to(:controller => "insecure", :action => "index")
175
+
176
+ # Use anonymous placeholders in place of the ugly-looking pattern, /([^\/.,;?]+)/
177
+ r.match("/::/users/::").
178
+ to(:controller => "users", :action => "[2]", :id => "[1]")
179
+
180
+ # Putting it all together, and adding the requirement that we use an "admin" prefix on the
181
+ # domain (e.g. admin.mysite.com), do some interesting stuff:
182
+ r.match(:domain => /^admin\b/) do |admin|
183
+ admin.match(%r[/([A-Z]\w+)\+([A-Z]\w+)/::]).
184
+ to(:controller => "admin/users", :action => ":path[3]",
185
+ :first_name => ":path[1]", :last_name => ":path[2]")
186
+ end.to(:controller => "admin/users", :action => "default")
187
+ # Note that the last line above sends all traffic in the "admin" subdomain to the
188
+ # Admin::Users#default action if no other route is matched.
189
+
190
+ # Create a deferred route. In this case, the decision of whether or not the route
191
+ # is a match is made via the .xhr? call. Note that it's ok to put the hash in a
192
+ # conditional because if the "if" statement is false, ruby returns nil (i.e. no match).
193
+ r.match(%r[^/deferred]).defer_to do |request, params|
194
+ {:controller => "ajax", :action => "index"} if request.xhr?
195
+ end
196
+
197
+ # Use the placeholders in a the deferred route
198
+ r.match("/deferred/:action").defer_to do |request, params|
199
+ params.merge(:controller => "deferred")
200
+ end
201
+ end
202
+ end
203
+
204
+ it "should connect '/contact' to Info#contact" do
205
+ index, route = Merb::Router.match(SimpleRequest.new(:protocol => "http://", :path => '/contact'), {})
206
+ route[:controller].should == "info"
207
+ route[:action].should == "contact"
208
+ should_only_have_keys(route, :controller, :action)
209
+ end
210
+
211
+ it "should use placeholders in the match and pass them along to the params" do
212
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/books/12/show'), {})
213
+ route[:controller].should == "books"
214
+ route[:action].should == "show"
215
+ route[:book_id].should == "12"
216
+ should_only_have_keys(route, :controller, :action, :book_id)
217
+ end
218
+
219
+ it "should allow placeholders to be used in the params to construct results from matches" do
220
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/admin/accounts/users/index'), {})
221
+ route[:controller].should == "accounts/users"
222
+ route[:action].should == "index"
223
+ should_only_have_keys(route, :module, :controller, :action)
224
+
225
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/admin/payment/processors/edit/4'), {})
226
+ route[:controller].should == "payment/processors"
227
+ route[:action].should == "edit"
228
+ route[:id].should == "4"
229
+ should_only_have_keys(route, :module, :controller, :action, :id)
230
+ end
231
+
232
+ it "should allow 'match' to use a block to factor out repetitive parts, merging the path as it goes" do
233
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/accounts/overview'), {})
234
+ route[:controller].should == "accounts"
235
+ route[:action].should == "overview"
236
+ should_only_have_keys(route, :controller, :action)
237
+
238
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/accounts/12/show.xml'), {})
239
+ route[:controller].should == "accounts"
240
+ route[:action].should == "show"
241
+ route[:id].should == "12"
242
+ route[:format].should == "xml"
243
+ should_only_have_keys(route, :controller, :action, :id, :format)
244
+ end
245
+
246
+ it "should allow 'to' to use a block to factor out repetitive params" do
247
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/reports'), {})
248
+ route[:controller].should == "accounts"
249
+ route[:action].should == "reports"
250
+ should_only_have_keys(route, :controller, :action)
251
+
252
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/slideshow/2'), {})
253
+ route[:controller].should == "accounts"
254
+ route[:action].should == "slideshow"
255
+ route[:id].should == "2"
256
+ should_only_have_keys(route, :controller, :action, :id)
257
+ end
258
+
259
+ it "should be able to use a regular expression instead of a string as the path-matcher" do
260
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/movies/5/movie-an-adventure-in-wonderland'), {})
261
+ route[:controller].should == "movies"
262
+ route[:action].should == "search_engine_optimizer"
263
+ route[:id].should == "5"
264
+ should_only_have_keys(route, :controller, :action, :id)
265
+ end
266
+
267
+ it "should be able to use square bracket notation to replace param results with captures from the path" do
268
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/movies/123-1-9999'), {})
269
+ route[:controller].should == "movies"
270
+ route[:action].should == "show"
271
+ route[:movie_id].should == "12319999"
272
+ should_only_have_keys(route, :controller, :action, :movie_id)
273
+ end
274
+
275
+ it "should only allow the POST method to '/movies/create'" do
276
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/movies/create', :method => "get"), {})
277
+ route[:controller].should be_nil
278
+ route[:action].should be_nil
279
+ should_only_have_keys(route)
280
+
281
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/movies/create', :method => "post"), {})
282
+ route[:controller].should == "movies"
283
+ route[:action].should == "create"
284
+ should_only_have_keys(route, :controller, :action)
285
+ end
286
+
287
+ it "should use variables from the 'match' as a result sent to the controller in the params hash" do
288
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/movies/harry-potter-3', :user_agent => "Internet Explorer (MSIE)"), {})
289
+ route[:controller].should == "movies"
290
+ route[:action].should == "show"
291
+ route[:title].should == "harry-potter-3"
292
+ route[:agent].should == "MSIE"
293
+ should_only_have_keys(route, :controller, :action, :title, :agent)
294
+ end
295
+
296
+ it "should be able to match without the use of a path, sending all HTTP traffic to 'insecure' controller" do
297
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/visit', :protocol => "http://"), {})
298
+ route[:controller].should == "insecure"
299
+ route[:action].should == "index"
300
+ should_only_have_keys(route, :controller, :action)
301
+
302
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/3/users/show', :protocol => "http://"), {})
303
+ route[:controller].should == "insecure"
304
+ route[:action].should == "index"
305
+ should_only_have_keys(route, :controller, :action)
306
+ end
307
+
308
+ it "should use anonymous placeholders" do
309
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/5/users/show', :protocol => "https://"), {})
310
+ route[:controller].should == "users"
311
+ route[:action].should == "show"
312
+ route[:id].should == "5"
313
+ should_only_have_keys(route, :controller, :action, :id)
314
+ end
315
+
316
+ it "should send all admin.* domains to the 'admin/users' controller, and 'default' action" do
317
+ index, route = Merb::Router.match(SimpleRequest.new(:domain => "admin.mysite.com", :path => '/welcome', :protocol => "https://"), {})
318
+ route[:controller].should == "admin/users"
319
+ route[:action].should == "default"
320
+ should_only_have_keys(route, :controller, :action)
321
+
322
+ index, route = Merb::Router.match(SimpleRequest.new(:domain => "admin.another-site.com", :path => '/go/somewhere/else', :protocol => "https://"), {})
323
+ route[:controller].should == "admin/users"
324
+ route[:action].should == "default"
325
+ should_only_have_keys(route, :controller, :action)
326
+ end
327
+
328
+ it "should decipher the first-name / last-name pairs on an admin.* domain" do
329
+ index, route = Merb::Router.match(SimpleRequest.new(:domain => "admin.mysite.com", :path => '/Duane+Johnson/edit', :protocol => "https://"), {})
330
+ route[:controller].should == "admin/users"
331
+ route[:action].should == "edit"
332
+ route[:first_name].should == "Duane"
333
+ route[:last_name].should == "Johnson"
334
+ should_only_have_keys(route, :controller, :action, :first_name, :last_name)
335
+ end
336
+
337
+ it "should defer to the Ajax controller for xhr requests" do
338
+ index, route = Merb::Router.match(SimpleRequest.new(:xhr? => true, :path => '/deferred/to/somewhere', :protocol => "https://"), {})
339
+ route[:controller].should == "ajax"
340
+ route[:action].should == "index"
341
+ should_only_have_keys(route, :controller, :action)
342
+ end
343
+
344
+ it "should let a deferred block use the path's MatchData" do
345
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/deferred/elsewhere', :protocol => "https://"), {})
346
+ route[:controller].should == "deferred"
347
+ route[:action].should == "elsewhere"
348
+ should_only_have_keys(route, :controller, :action)
349
+ end
350
+ end
351
+
352
+ describe Merb::Router, "with a single resource, 'blogposts' with 'comments'" do
353
+ before(:each) do
354
+ Merb::Router.prepare do |r|
355
+ r.resources :blogposts do |bposts|
356
+ bposts.resources :comments
357
+ end
358
+ end
359
+ end
360
+
361
+ it "should match /blogposts" do
362
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts', :method => :get), {})
363
+ route[:controller].should == 'blogposts'
364
+ route[:action].should == 'index'
365
+
366
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts', :method => :post), {})
367
+ route[:controller].should == 'blogposts'
368
+ route[:action].should == 'create'
369
+ end
370
+
371
+ it "should match /blogposts/new" do
372
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/new', :method => :get), {})
373
+ route[:controller].should == 'blogposts'
374
+ route[:action].should == 'new'
375
+ end
376
+
377
+ it "should match /blogposts/1" do
378
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1', :method => :get), {})
379
+ route[:controller].should == 'blogposts'
380
+ route[:action].should == 'show'
381
+ route[:id].should == '1'
382
+
383
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1', :method => :put), {})
384
+ route[:controller].should == 'blogposts'
385
+ route[:action].should == 'update'
386
+ route[:id].should == '1'
387
+
388
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1', :method => :delete), {})
389
+ route[:controller].should == 'blogposts'
390
+ route[:action].should == 'destroy'
391
+ route[:id].should == '1'
392
+ end
393
+
394
+ it "should match /blogposts/1;edit" do
395
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1;edit', :method => :get), {})
396
+ route[:controller].should == 'blogposts'
397
+ route[:action].should == 'edit'
398
+ route[:id].should == '1'
399
+
400
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1;edit', :method => :put), {})
401
+ route[:controller].should be_nil
402
+ route[:action].should be_nil
403
+ end
404
+
405
+ it "should match /blogposts/1/edit" do
406
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1/edit', :method => :get), {})
407
+ route[:controller].should == 'blogposts'
408
+ route[:action].should == 'edit'
409
+ route[:id].should == '1'
410
+
411
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/blogposts/1/edit', :method => :put), {})
412
+ route[:controller].should be_nil
413
+ route[:action].should be_nil
414
+ end
415
+
416
+ it "should generate blogposts path" do
417
+ Merb::Router.generate(:blogposts).should == '/blogposts'
418
+ end
419
+
420
+ it "should generate blogpost path" do
421
+ Merb::Router.generate(:blogpost, {:id => 1}).should == '/blogposts/1'
422
+ b = Blogposts.new
423
+ Merb::Router.generate(:blogpost, b).should == '/blogposts/42'
424
+ Merb::Router.generate(:blogpost, :id => b).should == '/blogposts/42'
425
+ end
426
+
427
+ it "should generate new_blogpost path" do
428
+ Merb::Router.generate(:new_blogpost).should == '/blogposts/new'
429
+ end
430
+
431
+ it "should generate edit_blogpost path" do
432
+ Merb::Router.generate(:edit_blogpost, {:id => 1}).should == '/blogposts/1/edit'
433
+ end
434
+
435
+ it "should generate comments path" do
436
+ c = Comment.new
437
+ Merb::Router.generate(:comments, c).should == '/blogposts/42/comments'
438
+ end
439
+
440
+ it "should generate comment path" do
441
+ c = Comment.new
442
+ Merb::Router.generate(:comment, c).should == '/blogposts/42/comments/24'
443
+ end
444
+
445
+ end
446
+
447
+
448
+ describe Merb::Router, "with resources using name_prefix, 'oranges' and 'ape'" do
449
+ before(:each) do
450
+ Merb::Router.prepare do |r|
451
+ r.resources :oranges, :name_prefix => "florida_"
452
+ r.resource :ape, :name_prefix => "grape_"
453
+ end
454
+ end
455
+
456
+ it "should match /oranges" do
457
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/oranges', :method => :get), {})
458
+ route[:controller].should == 'oranges'
459
+ route[:action].should == 'index'
460
+ end
461
+
462
+ it "should generate florida_oranges path" do
463
+ Merb::Router.generate(:florida_oranges).should == '/oranges'
464
+ end
465
+
466
+ it "should generate florida_orange path" do
467
+ Merb::Router.generate(:florida_orange, {:id => 1}).should == '/oranges/1'
468
+ b = Blogposts.new
469
+ Merb::Router.generate(:florida_orange, b).should == '/oranges/42'
470
+ Merb::Router.generate(:florida_orange, :id => b).should == '/oranges/42'
471
+ end
472
+
473
+ it "should generate new_florida_orange path" do
474
+ Merb::Router.generate(:new_florida_orange).should == '/oranges/new'
475
+ end
476
+
477
+ it "should generate edit_florida_orange path" do
478
+ Merb::Router.generate(:edit_florida_orange, {:id => 1}).should == '/oranges/1/edit'
479
+ end
480
+
481
+ it "should match /ape" do
482
+ index, route = Merb::Router.match(SimpleRequest.new(:path => '/ape', :method => :get), {})
483
+ route[:controller].should == 'ape'
484
+ route[:action].should == 'show'
485
+ end
486
+
487
+ it "should generate grape_ape path" do
488
+ Merb::Router.generate(:grape_ape).should == '/ape'
489
+ end
490
+
491
+ it "should generate new_grape_ape path" do
492
+ Merb::Router.generate(:new_grape_ape).should == '/ape/new'
493
+ end
494
+
495
+ it "should generate edit_grape_ape path" do
496
+ Merb::Router.generate(:edit_grape_ape).should == '/ape/edit'
497
+ end
498
+ end
499
+
500
+ describe Merb::Router, "with resources using a collection action" do
501
+ before(:each) do
502
+ Merb::Router.prepare do |r|
503
+ r.resources :flowers, :collection => { :random => [:get] }
504
+ end
505
+ end
506
+
507
+ it "should generate random_flowers path" do
508
+ Merb::Router.generate(:random_flowers).should == '/flowers/random'
509
+ end
510
+ end
511
+
512
+ describe Merb::Router, "with resources using a member action" do
513
+ before(:each) do
514
+ Merb::Router.prepare do |r|
515
+ r.resources :flowers, :member => { :pick => [:get] }
516
+ end
517
+ end
518
+
519
+ it 'should generate pick_flower path' do
520
+ Merb::Router.generate(:pick_flower, { :id => 1 }).should == '/flowers/1/pick'
521
+ end
522
+ end
523
+
524
+ describe Merb::Router::Behavior do
525
+ before(:all) do
526
+ @behavior = Merb::Router::Behavior
527
+ end
528
+
529
+ it "should leave strings as strings and add ^...$ in the @conditions hash" do
530
+ @behavior.new({:path => "/one/two"}).conditions[:path].should == "^/one/two$"
531
+ end
532
+
533
+ it "should replace special characters in strings with their escaped equivalents" do
534
+ @behavior.new({:path => "test.xml"}).conditions[:path].should == "^test\\.xml$"
535
+ end
536
+
537
+ it "should convert symbols to strings and add ^...$ in the @conditions hash" do
538
+ @behavior.new({:method => :get}).conditions[:method].should == "^get$"
539
+ end
540
+
541
+ it "should convert regular expressions to strings in the @conditions hash" do
542
+ @behavior.new({:protocol => /https?/}).conditions[:protocol].should == "https?"
543
+ end
544
+
545
+ it "should deduce placeholders from the @conditions hash" do
546
+ ph = @behavior.new({:path => "/:controller/:action"}).placeholders
547
+ ph[:controller].should == [:path, 1]
548
+ ph[:action].should == [:path, 2]
549
+ end
550
+
551
+ it "should deduce placeholders from the @conditions hash, even when they contain numbers" do
552
+ ph = @behavior.new({:path => "/:part1/:part2"}).placeholders
553
+ ph[:part1].should == [:path, 1]
554
+ ph[:part2].should == [:path, 2]
555
+ end
556
+
557
+ it "should deduce placeholders within regular expressions that contain prefixed captures" do
558
+ ph = @behavior.new({:path => %r[/(\d+)/:controller/:action]}).placeholders
559
+ ph[:controller].should == [:path, 2]
560
+ ph[:action].should == [:path, 3]
561
+
562
+ ph = @behavior.new({:path => %r[/:controller/:action/(\d+)]}).placeholders
563
+ ph[:controller].should == [:path, 1]
564
+ ph[:action].should == [:path, 2]
565
+ end
566
+
567
+ it "should deduce placeholder positions in nested captures" do
568
+ ph = @behavior.new({:path => %r[(/(\d+)/:controller)/:action]}).placeholders
569
+ ph[:controller].should == [:path, 3]
570
+ ph[:action].should == [:path, 4]
571
+
572
+ ph = @behavior.new({:path => %r[/(\d+)/(:controller)/:action]}).placeholders
573
+ ph[:controller].should == [:path, 3]
574
+ ph[:action].should == [:path, 4]
575
+
576
+ ph = @behavior.new({:path => %r[/(\d+)/:controller/(:action)]}).placeholders
577
+ ph[:controller].should == [:path, 2]
578
+ ph[:action].should == [:path, 4]
579
+
580
+ ph = @behavior.new({:path => %r[(/(\d+)/(:controller/((:action))))]}).placeholders
581
+ ph[:controller].should == [:path, 4]
582
+ ph[:action].should == [:path, 7]
583
+ end
584
+
585
+ it "should replace any placeholders found within @conditions strings with segment regular expressions" do
586
+ m = @behavior.new({:path => "/:my/:place:holders/:here"}).conditions
587
+ m[:path].should == "^/([^/.,;?]+)/([^/.,;?]+)([^/.,;?]+)/([^/.,;?]+)$"
588
+ end
589
+
590
+ it "should set default values for params that came from placeholders" do
591
+ p = @behavior.new({:path => "/:my/:place:holders/:here"}).params
592
+ p[:my].should == ":my"
593
+ p[:place].should == ":place"
594
+ p[:holders].should == ":holders"
595
+ p[:here].should == ":here"
596
+ end
597
+
598
+ it "should merge params with its ancestors" do
599
+ b = @behavior.new({}, {:controller => "my_controller", :action => "index"})
600
+ c = @behavior.new({}, {:action => "show"}, b)
601
+ c.merged_params.should == {:controller => "my_controller", :action => "show"}
602
+ end
603
+
604
+ # it "should have a default action and controller for merged params" do
605
+ # a = @behavior.new
606
+ # a.merged_params.should == {:controller => "application", :action => "index"}
607
+ #
608
+ # b = @behavior.new({}, {:controller => "admin"})
609
+ # b.merged_params.should == {:controller => "admin", :action => "index"}
610
+ #
611
+ # c = @behavior.new({}, {:action => "show"})
612
+ # c.merged_params.should == {:controller => "application", :action => "show"}
613
+ # end
614
+
615
+ it "should merge conditions with its ancestors" do
616
+ b = @behavior.new({:method => "get", :protocol => "http"})
617
+ c = @behavior.new({:method => "put"}, {}, b)
618
+ c.merged_conditions.should == {:method => "^put$", :protocol => "^http$"}
619
+ end
620
+
621
+ it "should merge placeholders with its ancestors" do
622
+ b = @behavior.new({:method => "get", :protocol => ":ssl"}, {:action => ":method"})
623
+ c = @behavior.new({:method => "put"}, {:action => ":ssl"}, b)
624
+ c.merged_placeholders.should == {:ssl => [:protocol, 1]}
625
+ end
626
+
627
+ it "should add the number of path captures in the ancestors' paths to placeholders that hold a place for :path captures" do
628
+ b = @behavior.new({:path => "/:controller/:action"})
629
+ b.placeholders.should == {:controller => [:path, 1], :action => [:path, 2]}
630
+ c = @behavior.new({:path => "/:id"}, {}, b)
631
+ c.placeholders.should == {:id => [:path, 1]}
632
+
633
+ c.merged_placeholders.should == {:controller => [:path, 1], :action => [:path, 2], :id => [:path, 3]}
634
+ end
635
+
636
+ it "should merge the :path differently than other @conditions keys -- it should concatenate" do
637
+ b = @behavior.new({:method => "get", :protocol => "http"})
638
+ c = @behavior.new({:path => "/test", :method => "put"}, {}, b)
639
+ c.merged_conditions.should == {:method => "^put$", :protocol => "^http$", :path => "^/test$"}
640
+
641
+ b = @behavior.new({:path => "/test", :method => "get", :protocol => "http"})
642
+ c = @behavior.new({:method => "put"}, {}, b)
643
+ c.merged_conditions.should == {:method => "^put$", :protocol => "^http$", :path => "^/test$"}
644
+
645
+ b = @behavior.new({:path => "/admin", :method => "get", :protocol => "http"})
646
+ c = @behavior.new({:path => "/test", :method => "put"}, {}, b)
647
+ c.merged_conditions.should == {:method => "^put$", :protocol => "^http$", :path => "^/admin/test$"}
648
+ end
649
+
650
+ it "should be able to compile the @params to strings and request matches" do
651
+ b = @behavior.new({:path => "/admin/:controller/:action", :method => "get"})
652
+ cp = b.send(:compiled_params)
653
+ cp[:controller].should == "path1"
654
+ cp[:action].should == "path2"
655
+
656
+ b = @behavior.new(
657
+ {:path => "/admin/:controller/:action/:postfix", :method => "get"},
658
+ {:controller => "/admin/:controller", :action => "neat_o_:action:postfix"})
659
+ cp = b.send(:compiled_params)
660
+ cp[:controller].should == "\"/admin/\" + path1"
661
+ cp[:action].should == "\"neat_o_\" + path2 + path3"
662
+ end
663
+
664
+ it "should allow a bracketed number such as [3] to compile to path3" do
665
+ b = @behavior.new(
666
+ {:path => "/admin/:controller/:action/(.+)", :method => "get"},
667
+ {:catchall => "[3]"})
668
+ cp = b.send(:compiled_params)
669
+ cp[:catchall].should == "path3"
670
+ end
671
+
672
+ it "should allow a backslash to escape an underscore in the compiled params" do
673
+ b = @behavior.new(
674
+ {:path => "/admin/:controller/:action", :method => "get"},
675
+ {:action => "some_prefix_:action\\_other"})
676
+ cp = b.send(:compiled_params)
677
+ cp[:action].should == "\"some_prefix_\" + path2 + \"_other\""
678
+ end
679
+
680
+ it "should return a Route object containing compiled conditions and params when .to is called" do
681
+ a = @behavior.new({:path => "/admin"})
682
+ b = a.match("/:controller/:action", :method => "get")
683
+ route = b.to(:controller => "/admin/:controller")
684
+ route.conditions[:path].to_s.should == /^\/admin\/([^\/.,;?]+)\/([^\/.,;?]+)$/.to_s
685
+ route.conditions[:method].should == /^get$/
686
+ route.params.should == {:controller => "\"/admin/\" + path1", :action => "path2"}
687
+ end
688
+
689
+ it "should allow for conditional blocks using the 'defer_to' method" do
690
+ b = @behavior.new({:path => "/admin"})
691
+ route = b.defer_to { |request| {:controller => "late_bound", :action => "place"} }
692
+ route.conditional_block.should be_an_instance_of(Proc)
693
+ route.compile.should match(/block_result/m)
694
+ end
695
+ end
696
+
697
+ describe Merb::Router::Behavior, "class methods" do
698
+ before(:all) do
699
+ @b = Merb::Router::Behavior
700
+ end
701
+
702
+ it "should count opening parentheses" do
703
+ @b.count_parens_up_to(" ( )", 1).should == 1
704
+ @b.count_parens_up_to(" ( )", 50).should == 1
705
+ @b.count_parens_up_to(" (() )", 1).should == 1
706
+ @b.count_parens_up_to(" (() )", 2).should == 2
707
+ # TODO: skip escaped open parens
708
+ end
709
+
710
+ it "should concatenate strings without endcaps" do
711
+ @b.concat_without_endcaps(nil, nil).should be_nil
712
+ @b.concat_without_endcaps(nil, "^test").should == "^test"
713
+ @b.concat_without_endcaps("my$", nil).should == "my$"
714
+ @b.concat_without_endcaps("my", "test").should == "mytest"
715
+ @b.concat_without_endcaps("my$", "test").should == "mytest"
716
+ @b.concat_without_endcaps("my^", "test").should == "my^test"
717
+ @b.concat_without_endcaps("my$", "^test").should == "mytest"
718
+ @b.concat_without_endcaps("^my$", "^test$").should == "^mytest$"
719
+ end
720
+
721
+ it "should compile arrays with strings and symbols into code" do
722
+ @b.array_to_code([:var, "this string"]).should == "var + \"this string\""
723
+ @b.array_to_code(["one string"]).should == "\"one string\""
724
+ @b.array_to_code(["string", :var, :var2, "other"]).should == "\"string\" + var + var2 + \"other\""
725
+ end
726
+ end