merb 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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