merb-core 0.9.8 → 0.9.9

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 (57) hide show
  1. data/CONTRIBUTORS +33 -0
  2. data/README +7 -3
  3. data/Rakefile +3 -3
  4. data/lib/merb-core.rb +165 -94
  5. data/lib/merb-core/bootloader.rb +469 -100
  6. data/lib/merb-core/config.rb +79 -3
  7. data/lib/merb-core/constants.rb +24 -2
  8. data/lib/merb-core/controller/abstract_controller.rb +172 -67
  9. data/lib/merb-core/controller/exceptions.rb +50 -6
  10. data/lib/merb-core/controller/merb_controller.rb +215 -108
  11. data/lib/merb-core/controller/mime.rb +36 -12
  12. data/lib/merb-core/controller/mixins/authentication.rb +52 -7
  13. data/lib/merb-core/controller/mixins/conditional_get.rb +14 -0
  14. data/lib/merb-core/controller/mixins/controller.rb +90 -58
  15. data/lib/merb-core/controller/mixins/render.rb +34 -10
  16. data/lib/merb-core/controller/mixins/responder.rb +40 -16
  17. data/lib/merb-core/controller/template.rb +37 -16
  18. data/lib/merb-core/core_ext/hash.rb +9 -0
  19. data/lib/merb-core/core_ext/kernel.rb +92 -41
  20. data/lib/merb-core/dispatch/dispatcher.rb +29 -45
  21. data/lib/merb-core/dispatch/request.rb +186 -82
  22. data/lib/merb-core/dispatch/router.rb +141 -53
  23. data/lib/merb-core/dispatch/router/behavior.rb +296 -139
  24. data/lib/merb-core/dispatch/router/resources.rb +51 -19
  25. data/lib/merb-core/dispatch/router/route.rb +76 -23
  26. data/lib/merb-core/dispatch/session.rb +80 -36
  27. data/lib/merb-core/dispatch/session/container.rb +31 -15
  28. data/lib/merb-core/dispatch/session/cookie.rb +51 -22
  29. data/lib/merb-core/dispatch/session/memcached.rb +10 -6
  30. data/lib/merb-core/dispatch/session/memory.rb +17 -5
  31. data/lib/merb-core/dispatch/session/store_container.rb +21 -9
  32. data/lib/merb-core/dispatch/worker.rb +16 -2
  33. data/lib/merb-core/gem_ext/erubis.rb +4 -0
  34. data/lib/merb-core/plugins.rb +13 -0
  35. data/lib/merb-core/rack.rb +1 -0
  36. data/lib/merb-core/rack/adapter.rb +1 -0
  37. data/lib/merb-core/rack/adapter/abstract.rb +95 -17
  38. data/lib/merb-core/rack/adapter/irb.rb +50 -5
  39. data/lib/merb-core/rack/application.rb +27 -5
  40. data/lib/merb-core/rack/handler/mongrel.rb +6 -6
  41. data/lib/merb-core/rack/helpers.rb +33 -0
  42. data/lib/merb-core/rack/middleware/conditional_get.rb +1 -1
  43. data/lib/merb-core/rack/middleware/path_prefix.rb +3 -3
  44. data/lib/merb-core/rack/middleware/static.rb +11 -7
  45. data/lib/merb-core/server.rb +134 -69
  46. data/lib/merb-core/tasks/gem_management.rb +153 -80
  47. data/lib/merb-core/tasks/merb_rake_helper.rb +12 -4
  48. data/lib/merb-core/tasks/stats.rake +1 -1
  49. data/lib/merb-core/test/helpers/mock_request_helper.rb +29 -22
  50. data/lib/merb-core/test/helpers/request_helper.rb +1 -1
  51. data/lib/merb-core/test/helpers/route_helper.rb +50 -4
  52. data/lib/merb-core/test/matchers/request_matchers.rb +2 -36
  53. data/lib/merb-core/test/matchers/view_matchers.rb +32 -22
  54. data/lib/merb-core/test/run_specs.rb +6 -5
  55. data/lib/merb-core/test/test_ext/rspec.rb +6 -19
  56. data/lib/merb-core/version.rb +1 -1
  57. metadata +5 -4
@@ -6,19 +6,19 @@ require 'merb-core/dispatch/router/route'
6
6
  module Merb
7
7
  # Router stores route definitions and finds the first
8
8
  # route that matches the incoming request URL.
9
- #
9
+ #
10
10
  # Then information from route is used by dispatcher to
11
11
  # call action on the controller.
12
- #
12
+ #
13
13
  # ==== Routes compilation.
14
- #
14
+ #
15
15
  # The most interesting method of Router (and heart of
16
16
  # route matching machinery) is match method generated
17
17
  # on the fly from routes definitions. It is called routes
18
18
  # compilation. Generated match method body contains
19
19
  # one if/elsif statement that picks the first matching route
20
20
  # definition and sets values to named parameters of the route.
21
- #
21
+ #
22
22
  # Compilation is synchronized by mutex.
23
23
  class Router
24
24
  @routes = []
@@ -26,14 +26,14 @@ module Merb
26
26
  @resource_routes = {}
27
27
  @compiler_mutex = Mutex.new
28
28
  @root_behavior = Behavior.new.defaults(:action => "index")
29
-
29
+
30
30
  # Raised when route lookup fails.
31
31
  class RouteNotFound < StandardError; end;
32
32
  # Raised when parameters given to generation
33
33
  # method do not match route parameters.
34
34
  class GenerationError < StandardError; end;
35
35
  class NotCompiledError < StandardError; end;
36
-
36
+
37
37
  class << self
38
38
  # @private
39
39
  attr_accessor :routes, :named_routes, :resource_routes, :root_behavior
@@ -41,19 +41,20 @@ module Merb
41
41
  # Creates a route building context and evaluates the block in it. A
42
42
  # copy of +root_behavior+ (and instance of Behavior) is copied as
43
43
  # the context.
44
- #
44
+ #
45
45
  # ==== Parameters
46
46
  # first<Array>::
47
47
  # An array containing routes that should be prepended to the routes
48
48
  # defined in the block.
49
- #
50
49
  # last<Array>::
51
50
  # An array containing routes that should be appended to the routes
52
51
  # defined in the block.
53
- #
52
+ #
54
53
  # ==== Returns
55
54
  # Merb::Router::
56
55
  # Returns self to allow chaining of methods.
56
+ #
57
+ # @api public
57
58
  def prepare(first = [], last = [], &block)
58
59
  @routes = []
59
60
  root_behavior._with_proxy(&block)
@@ -63,17 +64,23 @@ module Merb
63
64
  end
64
65
 
65
66
  # Appends route in the block to routing table.
67
+ #
68
+ # @api public
66
69
  def append(&block)
67
70
  prepare(routes, [], &block)
68
71
  end
69
-
72
+
70
73
  # Prepends routes in the block to routing table.
74
+ #
75
+ # @api public
71
76
  def prepend(&block)
72
77
  prepare([], routes, &block)
73
78
  end
74
79
 
75
80
  # Clears the routing table. Route generation and request matching
76
81
  # won't work anymore until a new routing table is built.
82
+ #
83
+ # @api private
77
84
  def reset!
78
85
  class << self
79
86
  alias_method :match, :match_before_compilation
@@ -84,18 +91,16 @@ module Merb
84
91
  # Finds route matching URI of the request and returns a tuple of
85
92
  # [route index, route params]. This method is called by the
86
93
  # dispatcher and isn't as useful in applications.
87
- #
94
+ #
88
95
  # ==== Parameters
89
96
  # request<Merb::Request>:: request to match.
90
- #
97
+ #
91
98
  # ==== Returns
92
- # <Array(Integer, Hash)::
93
- # Two-tuple: route index and route parameters. Route
94
- # parameters are :controller, :action and all the named
95
- # segments of the route.
96
- #
97
- # ---
98
- # @private
99
+ # Array[Integer, Hash]::
100
+ # Two-tuple: route index and route parameters. Route parameters
101
+ # are :controller, :action and all the named segments of the route.
102
+ #
103
+ # @api private
99
104
  def route_for(request) #:nodoc:
100
105
  index, params = match(request)
101
106
  route = routes[index] if index
@@ -105,43 +110,85 @@ module Merb
105
110
  end
106
111
  [route, params]
107
112
  end
108
-
109
- # Just a placeholder for the compiled match method
113
+
114
+ # A placeholder for the compiled match method.
115
+ #
116
+ # ==== Notes
117
+ # This method is aliased as +match+ but this method gets overridden with
118
+ # the actual +match+ method (generated from the routes definitions) after
119
+ # being compiled. This method is only ever called before routes are
120
+ # compiled.
121
+ #
122
+ # ==== Raises
123
+ # NotCompiledError:: routes have not been compiled yet.
124
+ #
125
+ # @api private
110
126
  def match_before_compilation(request) #:nodoc:
111
127
  raise NotCompiledError, "The routes have not been compiled yet"
112
128
  end
113
-
129
+
114
130
  alias_method :match, :match_before_compilation
115
131
 
116
- # Generates a URL from the params
132
+ # There are three possible ways to use this method. First, if you have a named route,
133
+ # you can specify the route as the first parameter as a symbol and any paramters in a
134
+ # hash. Second, you can generate the default route by just passing the params hash,
135
+ # just passing the params hash. Finally, you can use the anonymous parameters. This
136
+ # allows you to specify the parameters to a named route in the order they appear in the
137
+ # router.
117
138
  #
118
- # ==== Parameters
119
- # name<Symbol>::
120
- # The name of the route to generate
139
+ # ==== Parameters(Named Route)
140
+ # name<Symbol>::
141
+ # The name of the route.
142
+ # args<Hash>::
143
+ # Parameters for the route generation.
121
144
  #
122
- # anonymous_params<Object>::
145
+ # ==== Parameters(Default Route)
146
+ # args<Hash>::
147
+ # Parameters for the route generation. This route will use the default route.
148
+ #
149
+ # ==== Parameters(Anonymous Parameters)
150
+ # name<Symbol>::
151
+ # The name of the route.
152
+ # args<Array>::
123
153
  # An array of anonymous parameters to generate the route
124
154
  # with. These parameters are assigned to the route parameters
125
155
  # in the order that they are passed.
126
156
  #
127
- # params<Hash>::
128
- # Named parameters to generate the route with.
129
- #
130
- # defaults<Hash>::
131
- # A hash of default parameters to generate the route with.
132
- # This is usually the request parameters. If there are any
133
- # required params that are missing to generate the route,
134
- # they are pulled from this hash.
135
157
  # ==== Returns
136
- # String:: The generated URL
137
- # ---
138
- # @private
158
+ # String:: The generated URL.
159
+ #
160
+ # ==== Examples
161
+ # Named Route
162
+ #
163
+ # Merb::Router.prepare do
164
+ # match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")
165
+ # end
166
+ #
167
+ # url(:articles, :title => "new_article")
168
+ #
169
+ # Default Route
170
+ #
171
+ # Merb::Router.prepare do
172
+ # default_routes
173
+ # end
174
+ #
175
+ # url(:controller => "articles", :action => "new")
176
+ #
177
+ # Anonymous Paramters
178
+ #
179
+ # Merb::Router.prepare do
180
+ # match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")
181
+ # end
182
+ #
183
+ # url(:articles, 2008, 10, "test_article")
184
+ #
185
+ # @api private
139
186
  def url(name, *args)
140
187
  unless name.is_a?(Symbol)
141
188
  args.unshift(name)
142
189
  name = :default
143
190
  end
144
-
191
+
145
192
  unless route = Merb::Router.named_routes[name]
146
193
  raise Merb::Router::GenerationError, "Named route not found: #{name}"
147
194
  end
@@ -152,25 +199,26 @@ module Merb
152
199
  end
153
200
 
154
201
  # Generates a URL from the resource(s)
155
- #
202
+ #
156
203
  # ==== Parameters
157
204
  # resources<Symbol,Object>::
158
205
  # The identifiers for the resource route to generate. These
159
206
  # can either be symbols or objects. Symbols denote resource
160
207
  # collection routes and objects denote the members.
161
- #
208
+ #
162
209
  # params<Hash>::
163
210
  # Any extra parameters needed to generate the route.
211
+ #
164
212
  # ==== Returns
165
213
  # String:: The generated URL
166
- # ---
167
- # @private
214
+ #
215
+ # @api private
168
216
  def resource(*args)
169
217
  defaults = args.pop
170
218
  options = extract_options_from_args!(args) || {}
171
219
  key = []
172
220
  params = []
173
-
221
+
174
222
  args.each do |arg|
175
223
  if arg.is_a?(Symbol) || arg.is_a?(String)
176
224
  key << arg.to_s
@@ -179,19 +227,56 @@ module Merb
179
227
  params << arg
180
228
  end
181
229
  end
182
-
230
+
183
231
  params << options
184
-
232
+
185
233
  unless route = Merb::Router.resource_routes[key]
186
234
  raise Merb::Router::GenerationError, "Resource route not found: #{args.inspect}"
187
235
  end
188
-
236
+
189
237
  route.generate(params, defaults)
190
238
  end
239
+
240
+ # Add functionality to the router. This can be in the form of
241
+ # including a new module or directly defining new methods.
242
+ #
243
+ # ==== Parameters
244
+ # &block<Block>::
245
+ # A block of code used to extend the route builder with. This
246
+ # can be including a module or directly defining some new methods
247
+ # that should be available to building routes.
248
+ #
249
+ # ==== Returns
250
+ # nil
251
+ #
252
+ # ==== Example
253
+ # Merb::Router.extensions do
254
+ # def domain(name, domain, options={}, &block)
255
+ # match(:domain => domain).namespace(name, :path => nil, &block)
256
+ # end
257
+ # end
258
+ #
259
+ # In this case, a method 'domain' will be available to the route builder
260
+ # which will create namespaces around domains instead of path prefixes.
261
+ #
262
+ # This can then be used as follows.
263
+ #
264
+ # Merb::Router.prepare do
265
+ # domain(:admin, "my-admin.com") do
266
+ # # ... routes come here ...
267
+ # end
268
+ # end
269
+ #
270
+ # @api public
271
+ def extensions(&block)
272
+ Router::Behavior.class_eval(&block)
273
+ end
191
274
 
192
275
  private
193
276
 
194
- # Defines method with a switch statement that does routes recognition.
277
+ # Compiles the routes and creates the +match+ method.
278
+ #
279
+ # @api private
195
280
  def compile
196
281
  if routes.any?
197
282
  eval(compiled_statement, binding, "Generated Code for Router", 1)
@@ -199,18 +284,21 @@ module Merb
199
284
  reset!
200
285
  end
201
286
  end
202
-
203
- # Generates method that does route recognition with a switch statement.
287
+
288
+ # Generates the method for evaluation defining a +match+ method to match
289
+ # a request with the defined routes.
290
+ #
291
+ # @api private
204
292
  def compiled_statement
205
293
  @compiler_mutex.synchronize do
206
294
  condition_keys, if_statements = Set.new, ""
207
-
295
+
208
296
  routes.each_with_index do |route, i|
209
297
  route.freeze
210
298
  route.conditions.keys.each { |key| condition_keys << key }
211
299
  if_statements << route.compiled_statement(i == 0)
212
300
  end
213
-
301
+
214
302
  statement = "def match(request)\n"
215
303
  statement << condition_keys.inject("") do |cached, key|
216
304
  cached << " cached_#{key} = request.#{key}.to_s\n"
@@ -222,7 +310,7 @@ module Merb
222
310
  statement << "end"
223
311
  end
224
312
  end
225
-
313
+
226
314
  end # class << self
227
315
  end
228
316
  end
@@ -3,31 +3,51 @@ module Merb
3
3
  class Router
4
4
 
5
5
  class Behavior
6
-
7
- class Error < StandardError; end;
6
+
7
+ class Error < StandardError; end
8
8
 
9
9
  # Proxy catches any methods and proxies them to the current behavior.
10
10
  # This allows building routes without constantly having to catching the
11
11
  # yielded behavior object
12
- # ---
13
- # @private
14
- class Proxy #:nodoc:
12
+ #
13
+ # @api private
14
+ class Proxy
15
+
15
16
  # Undefine as many methods as possible so that everything can be proxied
16
17
  # along to the behavior
17
18
  instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get instance_eval].include?(m) }
18
19
 
20
+ # @api private
19
21
  def initialize
20
22
  @behaviors = []
21
23
  end
22
24
 
25
+ # Puts a behavior on the bottom of the stack.
26
+ #
27
+ # ==== Notes
28
+ # The behaviors keep track of nested scopes.
29
+ #
30
+ # @api private
23
31
  def push(behavior)
24
32
  @behaviors.push(behavior)
25
33
  end
26
34
 
35
+ # Removes the top-most behavior.
36
+ #
37
+ # ==== Notes
38
+ # This occurs at the end of a nested scope (namespace, etc).
39
+ #
40
+ # @api private
27
41
  def pop
28
42
  @behaviors.pop
29
43
  end
30
44
 
45
+ # Tests whether the top-most behavior responds to the arguments.
46
+ #
47
+ # ==== Notes
48
+ # Behaviors contain the actual functionality of the proxy.
49
+ #
50
+ # @api private
31
51
  def respond_to?(*args)
32
52
  super || @behaviors.last.respond_to?(*args)
33
53
  end
@@ -35,7 +55,7 @@ module Merb
35
55
  # Rake does some stuff with methods in the global namespace, so if I don't
36
56
  # explicitly define the Behavior methods to proxy here (specifically namespace)
37
57
  # Rake's methods take precedence.
38
- # ---
58
+ #
39
59
  # Removing the following:
40
60
  # name full_name fixatable redirect
41
61
  %w(
@@ -49,25 +69,86 @@ module Merb
49
69
  }
50
70
  end
51
71
 
52
- # --- These methods are to be used in defer_to blocks
72
+ # == These methods are to be used in defer_to blocks
53
73
 
54
- # Generates a URL from the passed arguments. This method is for use
55
- # inside of defer_to
74
+ # There are three possible ways to use this method. First, if you have a named route,
75
+ # you can specify the route as the first parameter as a symbol and any paramters in a
76
+ # hash. Second, you can generate the default route by just passing the params hash,
77
+ # just passing the params hash. Finally, you can use the anonymous parameters. This
78
+ # allows you to specify the parameters to a named route in the order they appear in the
79
+ # router.
80
+ #
81
+ # ==== Parameters(Named Route)
82
+ # name<Symbol>::
83
+ # The name of the route.
84
+ # args<Hash>::
85
+ # Parameters for the route generation.
86
+ #
87
+ # ==== Parameters(Default Route)
88
+ # args<Hash>::
89
+ # Parameters for the route generation. This route will use the default route.
90
+ #
91
+ # ==== Parameters(Anonymous Parameters)
92
+ # name<Symbol>::
93
+ # The name of the route.
94
+ # args<Array>::
95
+ # An array of anonymous parameters to generate the route
96
+ # with. These parameters are assigned to the route parameters
97
+ # in the order that they are passed.
98
+ #
99
+ # ==== Returns
100
+ # String:: The generated URL.
101
+ #
102
+ # ==== Examples
103
+ # Named Route
104
+ #
105
+ # Merb::Router.prepare do
106
+ # match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")
107
+ # end
108
+ #
109
+ # url(:articles, :title => "new_article")
110
+ #
111
+ # Default Route
112
+ #
113
+ # Merb::Router.prepare do
114
+ # default_routes
115
+ # end
116
+ #
117
+ # url(:controller => "articles", :action => "new")
118
+ #
119
+ # Anonymous Paramters
120
+ #
121
+ # Merb::Router.prepare do
122
+ # match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")
123
+ # end
124
+ #
125
+ # url(:articles, 2008, 10, "test_article")
126
+ #
127
+ # @api public
56
128
  def url(name, *args)
57
129
  args << {}
58
130
  Merb::Router.url(name, *args)
59
131
  end
60
132
 
133
+ # Generates a Rack redirection response.
134
+ #
135
+ # ==== Notes
136
+ # Refer to Merb::Rack::Helpers.redirect for documentation.
137
+ #
138
+ # @api public
61
139
  def redirect(url, opts = {})
62
- [url, opts[:permanent] ? 301 : 302]
140
+ Merb::Rack::Helpers.redirect(url, opts)
63
141
  end
64
142
 
65
- def route(params)
66
- params
67
- end
143
+ private
68
144
 
69
- private
70
-
145
+ # Proxies the method calls to the behavior.
146
+ #
147
+ # ==== Notes
148
+ # Please refer to:
149
+ # http://ruby-doc.org/core/classes/Kernel.html#M005951
150
+ #
151
+ # @api private
71
152
  def method_missing(method, *args, &block)
72
153
  behavior = @behaviors.last
73
154
 
@@ -78,14 +159,14 @@ module Merb
78
159
  end
79
160
  end
80
161
  end
81
-
162
+
82
163
  # Behavior objects are used for the Route building DSL. Each object keeps
83
164
  # track of the current definitions for the level at which it is defined.
84
165
  # Each time a method is called on a Behavior object that accepts a block,
85
166
  # a new instance of the Behavior class is created.
86
- #
167
+ #
87
168
  # ==== Parameters
88
- #
169
+ #
89
170
  # proxy<Proxy>::
90
171
  # This is the object initialized by Merb::Router.prepare that tracks the
91
172
  # current Behavior object stack so that Behavior methods can be called
@@ -100,11 +181,11 @@ module Merb
100
181
  # The initial route options. See #options.
101
182
  # blocks<Array>::
102
183
  # The stack of deferred routing blocks for the route
103
- #
184
+ #
104
185
  # ==== Returns
105
186
  # Behavior:: The initialized Behavior object
106
- #---
107
- # @private
187
+ #
188
+ # @api private
108
189
  def initialize(proxy = nil, conditions = {}, params = {}, defaults = {}, identifiers = {}, options = {}, blocks = []) #:nodoc:
109
190
  @proxy = proxy
110
191
  @conditions = conditions
@@ -113,130 +194,129 @@ module Merb
113
194
  @identifiers = identifiers
114
195
  @options = options
115
196
  @blocks = blocks
116
-
197
+
117
198
  stringify_condition_values
118
199
  end
119
-
200
+
120
201
  # Defines the +conditions+ that are required to match a Request. Each
121
202
  # +condition+ is applied to a method of the Request object. Conditions
122
203
  # can also be applied to segments of the +path+.
123
- #
204
+ #
124
205
  # If #match is passed a block, it will create a new route scope with
125
206
  # the conditions passed to it and yield to the block such that all
126
207
  # routes that are defined in the block have the conditions applied
127
208
  # to them.
128
- #
209
+ #
129
210
  # ==== Parameters
130
- #
211
+ #
131
212
  # path<String, Regexp>::
132
213
  # The pattern against which Merb::Request path is matched.
133
- #
214
+ #
134
215
  # When +path+ is a String, any substring that is wrapped in parenthesis
135
216
  # is considered optional and any segment that begins with a colon, ex.:
136
217
  # ":login", defines both a capture and a named param. Extra conditions
137
218
  # can then be applied each named param individually.
138
- #
219
+ #
139
220
  # When +path+ is a Regexp, the pattern is left untouched and the
140
221
  # Merb::Request path is matched against it as is.
141
222
  #
142
223
  # +path+ is optional.
143
- #
224
+ #
144
225
  # conditions<Hash>::
145
226
  # Additional conditions that the request must meet in order to match.
146
227
  # The keys must be the names of previously defined path segments or
147
228
  # be methods that the Merb::Request instance will respond to. The
148
229
  # value is the string or regexp that matched the returned value.
149
230
  # Conditions are inherited by child routes.
150
- #
231
+ #
151
232
  # &block::
152
233
  # All routes defined in the block will be scoped to the conditions
153
234
  # defined by the #match method.
154
- #
235
+ #
155
236
  # ==== Block parameters
156
237
  # r<Behavior>:: +optional+ - The match behavior object.
157
- #
238
+ #
158
239
  # ==== Returns
159
240
  # Behavior::
160
241
  # A new instance of Behavior with the specified path and conditions.
161
- #
242
+ #
162
243
  # +Tip+: When nesting always make sure the most inner sub-match registers
163
- # a Route and doesn't just returns new Behaviors.
164
- #
244
+ # a Route and doesn't just return new Behaviors.
245
+ #
165
246
  # ==== Examples
166
- #
247
+ #
167
248
  # # registers /foo/bar to controller => "foo", :action => "bar"
168
249
  # # and /foo/baz to controller => "foo", :action => "baz"
169
250
  # match("/foo") do
170
251
  # match("/bar").to(:controller => "foo", :action => "bar")
171
252
  # match("/baz").to(:controller => "foo", :action => "caz")
172
253
  # end
173
- #
254
+ #
174
255
  # # Checks the format of the segments against the specified Regexp
175
256
  # match("/:string/:number", :string => /[a-z]+/, :number => /\d+/).
176
257
  # to(:controller => "string_or_numbers")
177
- #
258
+ #
178
259
  # # Equivalent to the default_route
179
260
  # match("/:controller(/:action(:id))(.:format)").register
180
- #
261
+ #
181
262
  # #match only if the browser string contains MSIE or Gecko
182
263
  # match("/foo", :user_agent => /(MSIE|Gecko)/ )
183
264
  # .to(:controller => 'foo', :action => 'popular')
184
- #
265
+ #
185
266
  # # Route GET and POST requests to different actions (see also #resources)
186
267
  # r.match('/foo', :method => :get).to(:action => 'show')
187
268
  # r.match('/foo', :method => :post).to(:action => 'create')
188
- #
269
+ #
189
270
  # # match also takes regular expressions
190
- #
271
+ #
191
272
  # r.match(%r[/account/([a-z]{4,6})]).to(:controller => "account",
192
273
  # :action => "show", :id => "[1]")
193
- #
274
+ #
194
275
  # r.match(%r{/?(en|es|fr|be|nl)?}).to(:language => "[1]") do
195
276
  # match("/guides/:action/:id").to(:controller => "tour_guides")
196
277
  # end
197
- #---
198
- # @public
278
+ #
279
+ # @api public
199
280
  def match(path = {}, conditions = {}, &block)
200
281
  path, conditions = path[:path], path if path.is_a?(Hash)
201
-
202
- raise Error, "The route has already been committed. Further conditions cannot be specified" if @route
203
282
 
283
+ raise Error, "The route has already been committed. Further conditions cannot be specified" if @route
204
284
 
205
285
  conditions.delete_if { |k, v| v.nil? }
206
286
  conditions[:path] = merge_paths(path)
207
-
287
+
208
288
  behavior = Behavior.new(@proxy, @conditions.merge(conditions), @params, @defaults, @identifiers, @options, @blocks)
209
289
  with_behavior_context(behavior, &block)
210
290
  end
211
291
 
212
292
  # Creates a Route from one or more Behavior objects, unless a +block+ is
213
293
  # passed in.
214
- #
294
+ #
215
295
  # ==== Parameters
216
296
  # params<Hash>:: The parameters the route maps to.
217
- #
297
+ #
218
298
  # &block::
219
299
  # All routes defined in the block will be scoped to the params
220
300
  # defined by the #to method.
221
- #
301
+ #
222
302
  # ==== Block parameters
223
303
  # r<Behavior>:: +optional+ - The to behavior object.
224
- #
304
+ #
225
305
  # ==== Returns
226
306
  # Route:: It registers a new route and returns it.
227
- #
307
+ #
228
308
  # ==== Examples
229
309
  # match('/:controller/:id).to(:action => 'show')
230
- #
310
+ #
231
311
  # to(:controller => 'simple') do
232
312
  # match('/test').to(:action => 'index')
233
313
  # match('/other').to(:action => 'other')
234
314
  # end
235
- #---
236
- # @public
315
+ #
316
+ # @api public
237
317
  def to(params = {}, &block)
238
318
  raise Error, "The route has already been committed. Further params cannot be specified" if @route
239
-
319
+
240
320
  behavior = Behavior.new(@proxy, @conditions, @params.merge(params), @defaults, @identifiers, @options, @blocks)
241
321
 
242
322
  if block_given?
@@ -247,7 +327,8 @@ module Merb
247
327
  end
248
328
 
249
329
  # Equivalent of #to. Allows for some nicer syntax when scoping blocks
250
- # --- Ex:
330
+ #
331
+ # ==== Examples
251
332
  # Merb::Router.prepare do
252
333
  # with(:controller => "users") do
253
334
  # match("/signup").to(:action => "signup")
@@ -255,32 +336,32 @@ module Merb
255
336
  # match("/logout").to(:action => "logout")
256
337
  # end
257
338
  # end
258
- alias_method :with, :to
339
+ alias :with :to
259
340
 
260
341
  # Equivalent of #to. Allows for nicer syntax when registering routes with no params
261
- # --- Ex:
342
+ #
343
+ # ==== Examples
262
344
  # Merb::Router.prepare do
263
345
  # match("/:controller(/:action(/:id))(.:format)").register
264
346
  # end
265
- #
266
- alias_method :register, :to
347
+ alias :register :to
267
348
 
268
349
  # Sets default values for route parameters. If no value for the key
269
350
  # can be extracted from the request, then the value provided here
270
351
  # will be used.
271
- #
352
+ #
272
353
  # ==== Parameters
273
354
  # defaults<Hash>::
274
355
  # The default values for named segments.
275
- #
356
+ #
276
357
  # &block::
277
358
  # All routes defined in the block will be scoped to the defaults defined
278
359
  # by the #default method.
279
- #
360
+ #
280
361
  # ==== Block parameters
281
362
  # r<Behavior>:: +optional+ - The defaults behavior object.
282
- # ---
283
- # @public
363
+ #
364
+ # @api public
284
365
  def default(defaults = {}, &block)
285
366
  behavior = Behavior.new(@proxy, @conditions, @params, @defaults.merge(defaults), @identifiers, @options, @blocks)
286
367
  with_behavior_context(behavior, &block)
@@ -289,34 +370,34 @@ module Merb
289
370
  alias_method :defaults, :default
290
371
 
291
372
  # Allows the fine tuning of certain router options.
292
- #
373
+ #
293
374
  # ==== Parameters
294
375
  # options<Hash>::
295
376
  # The options to set for all routes defined in the scope. The currently
296
377
  # supported options are:
297
378
  # * :controller_prefix - The module that the controller is included in.
298
379
  # * :name_prefix - The prefix added to all routes named with #name
299
- #
380
+ #
300
381
  # &block::
301
382
  # All routes defined in the block will be scoped to the options defined
302
383
  # by the #options method.
303
- #
384
+ #
304
385
  # ==== Block parameters
305
386
  # r<Behavior>:: The options behavior object. This is optional
306
- #
387
+ #
307
388
  # ==== Examples
308
389
  # # If :group is not matched in the path, it will be "registered" instead
309
390
  # # of nil.
310
391
  # match("/users(/:group)").default(:group => "registered")
311
- # ---
312
- # @public
392
+ #
393
+ # @api public
313
394
  def options(opts = {}, &block)
314
395
  options = @options.dup
315
-
396
+
316
397
  opts.each_pair do |key, value|
317
398
  options[key] = (options[key] || []) + [value.freeze] if value
318
399
  end
319
-
400
+
320
401
  behavior = Behavior.new(@proxy, @conditions, @params, @defaults, @identifiers, options, @blocks)
321
402
  with_behavior_context(behavior, &block)
322
403
  end
@@ -325,44 +406,47 @@ module Merb
325
406
 
326
407
  # Creates a namespace for a route. This way you can have logical
327
408
  # separation to your routes.
328
- #
409
+ #
329
410
  # ==== Parameters
330
411
  # name_or_path<String, Symbol>::
331
412
  # The name or path of the namespace.
332
- #
413
+ #
333
414
  # options<Hash>::
334
- # Optional hash, set :path if you want to override what appears on the url
335
- #
415
+ # Optional hash (see below)
416
+ #
336
417
  # &block::
337
418
  # All routes defined in the block will be scoped to the namespace defined
338
419
  # by the #namespace method.
339
- #
420
+ #
421
+ # ==== Options (opts)
422
+ # :path<String>:: match against this url
423
+ #
340
424
  # ==== Block parameters
341
425
  # r<Behavior>:: The namespace behavior object. This is optional
342
- #
426
+ #
343
427
  # ==== Examples
344
428
  # namespace :admin do
345
429
  # resources :accounts
346
430
  # resource :email
347
431
  # end
348
- #
432
+ #
349
433
  # # /super_admin/accounts
350
434
  # namespace(:admin, :path=>"super_admin") do
351
435
  # resources :accounts
352
436
  # end
353
- # ---
354
- # @public
437
+ #
438
+ # @api public
355
439
  def namespace(name_or_path, opts = {}, &block)
356
440
  name = name_or_path.to_s # We don't want this modified ever
357
441
  path = opts.has_key?(:path) ? opts[:path] : name
358
-
442
+
359
443
  raise Error, "The route has already been committed. Further options cannot be specified" if @route
360
-
444
+
361
445
  # option keys could be nil
362
446
  opts[:controller_prefix] = name unless opts.has_key?(:controller_prefix)
363
447
  opts[:name_prefix] = name unless opts.has_key?(:name_prefix)
364
448
  opts[:resource_prefix] = opts[:name_prefix] unless opts.has_key?(:resource_prefix)
365
-
449
+
366
450
  behavior = self
367
451
  behavior = behavior.match("/#{path}") unless path.nil? || path.empty?
368
452
  behavior.options(opts, &block)
@@ -372,22 +456,22 @@ module Merb
372
456
  # insertion into a route. This is useful when using models and want a
373
457
  # specific method to be called on it (For example, for ActiveRecord::Base
374
458
  # it would be #to_param).
375
- #
459
+ #
376
460
  # The default method called on objects is #to_s.
377
- #
461
+ #
378
462
  # ==== Paramters
379
463
  # identifiers<Hash>::
380
464
  # The keys are Classes and the values are the method that instances of the specified
381
465
  # class should have called on.
382
- #
466
+ #
383
467
  # &block::
384
468
  # All routes defined in the block will be call the specified methods during
385
469
  # generation.
386
- #
470
+ #
387
471
  # ==== Block parameters
388
472
  # r<Behavior>:: The identify behavior object. This is optional
389
- # ---
390
- # @public
473
+ #
474
+ # @api public
391
475
  def identify(identifiers = {}, &block)
392
476
  identifiers = if Hash === identifiers
393
477
  @identifiers.merge(identifiers)
@@ -402,23 +486,23 @@ module Merb
402
486
  # Creates the most common routes /:controller/:action/:id.format when
403
487
  # called with no arguments. You can pass a hash or a block to add parameters
404
488
  # or override the default behavior.
405
- #
489
+ #
406
490
  # ==== Parameters
407
491
  # params<Hash>::
408
492
  # This optional hash can be used to augment the default settings
409
- #
493
+ #
410
494
  # &block::
411
495
  # When passing a block a new behavior is yielded and more refinement is
412
496
  # possible.
413
- #
497
+ #
414
498
  # ==== Returns
415
499
  # Route:: the default route
416
- #
500
+ #
417
501
  # ==== Examples
418
- #
502
+ #
419
503
  # # Passing an extra parameter "mode" to all matches
420
504
  # r.default_routes :mode => "default"
421
- #
505
+ #
422
506
  # # specifying exceptions within a block
423
507
  # r.default_routes do |nr|
424
508
  # nr.defer_to do |request, params|
@@ -426,31 +510,31 @@ module Merb
426
510
  # :action => "new") if request.env["REQUEST_URI"] =~ /\/private\//
427
511
  # end
428
512
  # end
429
- #---
430
- # @public
513
+ #
514
+ # @api public
431
515
  def default_routes(params = {}, &block)
432
516
  match("/:controller(/:action(/:id))(.:format)").to(params, &block).name(:default)
433
517
  end
434
518
 
435
519
  # Takes a block and stores it for deferred conditional routes. The block
436
520
  # takes the +request+ object and the +params+ hash as parameters.
437
- #
521
+ #
438
522
  # ==== Parameters
439
523
  # params<Hash>:: Parameters and conditions associated with this behavior.
440
524
  # &conditional_block::
441
525
  # A block with the conditions to be met for the behavior to take
442
526
  # effect.
443
- #
527
+ #
444
528
  # ==== Returns
445
529
  # Route :: The default route.
446
- #
530
+ #
447
531
  # ==== Examples
448
532
  # defer_to do |request, params|
449
533
  # params.merge :controller => 'here',
450
534
  # :action => 'there' if request.xhr?
451
535
  # end
452
- #---
453
- # @public
536
+ #
537
+ # @api public
454
538
  def defer_to(params = {}, &block)
455
539
  defer(block).to(params)
456
540
  end
@@ -458,27 +542,39 @@ module Merb
458
542
  # Takes a Proc as a parameter and applies it as a deferred proc for all the
459
543
  # routes defined in the block. This is mostly interesting for plugin
460
544
  # developers.
545
+ #
546
+ # ==== Examples
547
+ # defered_block = proc do |r, p|
548
+ # p.merge :controller => 'api/comments' if request.xhr?
549
+ # end
550
+ # defer(defered_block) do
551
+ # resources :comments
552
+ # end
553
+ #
554
+ # @api public
461
555
  def defer(deferred_block, &block)
462
556
  blocks = @blocks + [CachedProc.new(deferred_block)]
463
557
  behavior = Behavior.new(@proxy, @conditions, @params, @defaults, @identifiers, @options, blocks)
464
558
  with_behavior_context(behavior, &block)
465
559
  end
466
560
 
467
- # Names this route in Router. Name must be a Symbol.
468
- #
561
+ # Registers the route as a named route with the name given.
562
+ #
469
563
  # ==== Parameters
470
- # symbol<Symbol>:: The name of the route.
471
- #
564
+ # symbol<Symbol>:: the name of the route.
565
+ #
472
566
  # ==== Raises
473
567
  # ArgumentError:: symbol is not a Symbol.
568
+ #
569
+ # @api public
474
570
  def name(prefix, name = nil)
475
571
  unless name
476
572
  name, prefix = prefix, nil
477
573
  end
478
-
574
+
479
575
  full_name([prefix, @options[:name_prefix], name].flatten.compact.join('_'))
480
576
  end
481
-
577
+
482
578
  # Names this route in Router. Name must be a Symbol. The current
483
579
  # name_prefix is ignored.
484
580
  #
@@ -487,6 +583,8 @@ module Merb
487
583
  #
488
584
  # ==== Raises
489
585
  # ArgumentError:: symbol is not a Symbol.
586
+ #
587
+ # @api private
490
588
  def full_name(name)
491
589
  if @route
492
590
  @route.name = name
@@ -496,27 +594,34 @@ module Merb
496
594
  end
497
595
  end
498
596
 
597
+ # Specifies that a route can be fixatable.
598
+ #
499
599
  # ==== Parameters
500
600
  # enabled<Boolean>:: True enables fixation on the route.
601
+ #
602
+ # @api public
501
603
  def fixatable(enable = true)
502
604
  @route.fixation = enable
503
605
  self
504
606
  end
505
-
506
- # Sets the current route as a redirect.
507
- #
607
+
608
+ # Redirects the current route.
609
+ #
508
610
  # ==== Parameters
509
- # path<String::
510
- # The path to redirect to
511
- #
611
+ # path<String>:: The path to redirect to.
612
+ #
512
613
  # options<Hash>::
513
- # Options for the redirect
514
- # The supported options are:
515
- # * :permanent: Whether or not the redirect should be permanent.
516
- # The default value is false.
614
+ # Options (see below)
615
+ #
616
+ # ==== Options (opts)
617
+ # :permanent<Boolean>::
618
+ # Whether or not the redirect should be permanent.
619
+ # The default value is false.
620
+ #
621
+ # @api public
517
622
  def redirect(url, opts = {})
518
623
  raise Error, "The route has already been committed." if @route
519
-
624
+
520
625
  status = opts[:permanent] ? 301 : 302
521
626
  @route = Route.new(@conditions, {:url => url.freeze, :status => status.freeze}, @blocks, :redirects => true)
522
627
  @route.register
@@ -529,6 +634,8 @@ module Merb
529
634
  # it doesn't affect how/which routes are added.
530
635
  #
531
636
  # &block:: A context in which routes are generated.
637
+ #
638
+ # @api public
532
639
  def capture(&block)
533
640
  captured_routes = {}
534
641
  name_prefix = [@options[:name_prefix]].flatten.compact.map { |p| "#{p}_"}
@@ -545,27 +652,42 @@ module Merb
545
652
  captured_routes
546
653
  end
547
654
 
548
- # So that Router can have a default route
549
- # ---
550
- # @private
551
- def _with_proxy(&block) #:nodoc:
655
+ # Proxy routes with the default behaviors.
656
+ #
657
+ # ==== Parameters
658
+ # &block:: defines routes within the provided context.
659
+ #
660
+ # @api private
661
+ def _with_proxy(&block)
552
662
  proxy = Proxy.new
553
663
  proxy.push Behavior.new(proxy, @conditions, @params, @defaults, @identifiers, @options, @blocks)
554
664
  proxy.instance_eval(&block)
555
665
  proxy
556
666
  end
557
667
 
558
- protected
559
-
668
+ protected
669
+
670
+ # Returns the current route.
671
+ #
672
+ # ==== Returns
673
+ # Route:: the route.
674
+ #
675
+ # @api private
560
676
  def _route
561
677
  @route
562
678
  end
563
679
 
564
- def to_route # :nodoc:
680
+ # Turns a route definition into a Route object.
681
+ #
682
+ # ==== Returns
683
+ # Route:: the route generated.
684
+ #
685
+ # @api private
686
+ def to_route
565
687
  raise Error, "The route has already been committed." if @route
566
-
688
+
567
689
  controller = @params[:controller]
568
-
690
+
569
691
  if prefixes = @options[:controller_prefix]
570
692
  controller ||= ":controller"
571
693
 
@@ -594,12 +716,22 @@ module Merb
594
716
 
595
717
  # Allows to insert the route at a certain spot in the list of routes
596
718
  # instead of appending to the list.
597
- def before(route, &block) #:nodoc:
719
+ #
720
+ # ==== Params
721
+ # route<Route>:: the route to insert before.
722
+ # &block:: the route definition to insert.
723
+ #
724
+ # @api plugin
725
+ def before(route, &block)
598
726
  options(:before => route, &block)
599
727
  end
600
-
601
- private
602
-
728
+
729
+ private
730
+
731
+ # Takes @conditions and turns values into strings (except for Regexp and
732
+ # Array values).
733
+ #
734
+ # @api private
603
735
  def stringify_condition_values # :nodoc:
604
736
  @conditions.each do |key, value|
605
737
  unless value.nil? || Regexp === value || Array === value
@@ -607,7 +739,18 @@ module Merb
607
739
  end
608
740
  end
609
741
  end
610
-
742
+
743
+ # Creates a new context with a given behavior for the route definition in
744
+ # the block.
745
+ #
746
+ # ==== Params
747
+ # behavior<Behavior>:: the behavior to proxy the calls in the block.
748
+ # &block:: the routing definitions to be nested within the behavior.
749
+ #
750
+ # ==== Returns
751
+ # Behavior:: the behavior wrapping.
752
+ #
753
+ # @api private
611
754
  def with_behavior_context(behavior, &block) # :nodoc:
612
755
  if block_given?
613
756
  @proxy.push(behavior)
@@ -616,11 +759,25 @@ module Merb
616
759
  end
617
760
  behavior
618
761
  end
619
-
762
+
763
+ # Merges the path elements together into an array to be joined for
764
+ # generating named routes.
765
+ #
766
+ # ==== Parameters
767
+ # path<String>:: the path to merge at the end.
768
+ #
769
+ # ==== Returns
770
+ # Array:: array of path elements.
771
+ #
772
+ # ==== Notes
773
+ # An array of ['a', 'b'] (the 'a' namespace with the 'b' action) will
774
+ # produce a name of :a_b.
775
+ #
776
+ # @api private
620
777
  def merge_paths(path) # :nodoc:
621
778
  [@conditions[:path], path.freeze].flatten.compact
622
779
  end
623
-
780
+
624
781
  end
625
782
  end
626
783
  end