merb-core 0.9.8 → 0.9.9

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