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
@@ -1,12 +1,35 @@
1
1
  class Exception
2
+ # Returns the action_name that will be invoked on your Exceptions controller when this
3
+ # exception is raised. Override this method to force a different action to be invoked.
4
+ #
5
+ # ==== Returns
6
+ # String:: The name of the action in the Exceptions controller which will get invoked
7
+ # when this exception is raised during a request.
8
+ #
9
+ # @api public
10
+ # @overridable
2
11
  def action_name() self.class.action_name end
3
12
 
13
+
14
+ # ==== Returns
15
+ # Boolean:: Whether or not this exception is the same as another.
16
+ #
17
+ # @api public
4
18
  def same?(other)
5
19
  self.class == other.class &&
6
20
  self.message == other.message &&
7
21
  self.backtrace == other.backtrace
8
22
  end
9
23
 
24
+ # Returns the action_name that will be invoked on your Exceptions controller when an instance
25
+ # is raised during a request.
26
+ #
27
+ # ==== Returns
28
+ # String:: The name of the action in the Exceptions controller which will get invoked
29
+ # when an instance of this Exception sub/class is raised by an action.
30
+ #
31
+ # @api public
32
+ # @overridable
10
33
  def self.action_name
11
34
  if self == Exception
12
35
  return nil unless Object.const_defined?(:Exceptions) &&
@@ -17,6 +40,14 @@ class Exception
17
40
  Exceptions.method_defined?(name) ? name : superclass.action_name
18
41
  end
19
42
 
43
+ # The status that will be sent in the response when an instance is
44
+ # raised during a request. Override this to send a different status.
45
+ #
46
+ # ==== Returns
47
+ # Integer:: The status code to send in the response. Defaults to 500.
48
+ #
49
+ # @api public
50
+ # @overridable
20
51
  def self.status
21
52
  500
22
53
  end
@@ -81,13 +112,13 @@ module Merb
81
112
  # MySpecialMailer.deliver(
82
113
  # "team@cowboys.com",
83
114
  # "Exception occured at #{Time.now}",
84
- # params[:exception])
115
+ # self.request.exceptions.first)
85
116
  # render 'Something is wrong, but the team is on it!'
86
117
  # end
87
118
  #
88
- # Note: The special param[:exception] is available in all Exception actions
89
- # and contains the ControllerException that was raised (this is handy if
90
- # you want to display the associated message or display more detailed info)
119
+ # Note: The special method +exceptions+ is available on Merb::Request instances
120
+ # and contains the exceptions that was raised (this is handy if
121
+ # you want to display the associated message or display more detailed info).
91
122
  #
92
123
  #
93
124
  # ==== Extending ControllerExceptions
@@ -135,6 +166,8 @@ module Merb
135
166
  #
136
167
  # ==== Returns
137
168
  # Fixnum:: The status code of this exception.
169
+ #
170
+ # @api public
138
171
  def status
139
172
  const_get(:STATUS) rescue 0
140
173
  end
@@ -147,6 +180,11 @@ module Merb
147
180
  #
148
181
  # ==== Parameters
149
182
  # num<~to_i>:: The status code
183
+ #
184
+ # ==== Returns
185
+ # (Integer, nil):: The status set on this exception, or nil if a status was already set.
186
+ #
187
+ # @api private
150
188
  def status=(num)
151
189
  unless self.status?
152
190
  register_status_code(self, num)
@@ -157,7 +195,9 @@ module Merb
157
195
  # See if a status-code has been defined (on self explicitly).
158
196
  #
159
197
  # ==== Returns
160
- # Boolean:: Whether the a status code has been set
198
+ # Boolean:: Whether a status code has been set
199
+ #
200
+ # @api private
161
201
  def status?
162
202
  self.const_defined?(:STATUS)
163
203
  end
@@ -172,6 +212,8 @@ module Merb
172
212
  #
173
213
  # subclass<Merb::ControllerExceptions::Base>::
174
214
  # The Exception class that is inheriting from Merb::ControllerExceptions::Base
215
+ #
216
+ # @api public
175
217
  def inherited(subclass)
176
218
  # don't set the constant yet - any class methods will be called after self.inherited
177
219
  # unless self.status = ... is set explicitly, the status code will be inherited
@@ -184,6 +226,8 @@ module Merb
184
226
  #
185
227
  # ==== Parameters
186
228
  # num<~to_i>:: The status code
229
+ #
230
+ # @api privaate
187
231
  def register_status_code(klass, code)
188
232
  name = self.to_s.split('::').last.snake_case
189
233
  STATUS_CODES[name.to_sym] = code.to_i
@@ -299,4 +343,4 @@ module Merb
299
343
  "#{(e.backtrace or []).join("\n")}"
300
344
  end
301
345
 
302
- end
346
+ end
@@ -15,104 +15,104 @@ class Merb::Controller < Merb::AbstractController
15
15
  include Merb::AuthenticationMixin
16
16
  include Merb::ConditionalGetMixin
17
17
 
18
- class << self
19
-
20
- # ==== Parameters
21
- # klass<Merb::Controller>::
22
- # The Merb::Controller inheriting from the base class.
23
- def inherited(klass)
24
- _subclasses << klass.to_s
25
- super
26
- klass._template_root = Merb.dir_for(:view) unless self._template_root
27
- end
28
-
29
- # Hide each of the given methods from being callable as actions.
30
- #
31
- # ==== Parameters
32
- # *names<~to-s>:: Actions that should be added to the list.
33
- #
34
- # ==== Returns
35
- # Array[String]::
36
- # An array of actions that should not be possible to dispatch to.
37
- #
38
- #---
39
- # @public
40
- def hide_action(*names)
41
- self._hidden_actions = self._hidden_actions | names.map { |n| n.to_s }
42
- end
18
+ # ==== Parameters
19
+ # klass<Merb::Controller>::
20
+ # The Merb::Controller inheriting from the base class.
21
+ #
22
+ # @api private
23
+ def self.inherited(klass)
24
+ _subclasses << klass.to_s
25
+ super
26
+ klass._template_root = Merb.dir_for(:view) unless self._template_root
27
+ end
43
28
 
44
- # Makes each of the given methods being callable as actions. You can use
45
- # this to make methods included from modules callable as actions.
46
- #
47
- # ==== Parameters
48
- # *names<~to-s>:: Actions that should be added to the list.
49
- #
50
- # ==== Returns
51
- # Array[String]::
52
- # An array of actions that should be dispatched to even if they would not
53
- # otherwise be.
54
- #
55
- # ==== Example
56
- # module Foo
57
- # def self.included(base)
58
- # base.show_action(:foo)
59
- # end
60
- #
61
- # def foo
62
- # # some actiony stuff
63
- # end
64
- #
65
- # def foo_helper
66
- # # this should not be an action
67
- # end
68
- # end
69
- #
70
- #---
71
- # @public
72
- def show_action(*names)
73
- self._shown_actions = self._shown_actions | names.map {|n| n.to_s}
74
- end
29
+ # Hide each of the given methods from being callable as actions.
30
+ #
31
+ # ==== Parameters
32
+ # *names<~to-s>:: Actions that should be added to the list.
33
+ #
34
+ # ==== Returns
35
+ # Array[String]::
36
+ # An array of actions that should not be possible to dispatch to.
37
+ #
38
+ # @api public
39
+ def self.hide_action(*names)
40
+ self._hidden_actions = self._hidden_actions | names.map { |n| n.to_s }
41
+ end
75
42
 
76
- # The list of actions that are callable, after taking defaults,
77
- # _hidden_actions and _shown_actions into consideration. It is calculated
78
- # once, the first time an action is dispatched for this controller.
79
- #
80
- # ==== Returns
81
- # SimpleSet[String]:: A set of actions that should be callable.
82
- def callable_actions
83
- @callable_actions ||= Extlib::SimpleSet.new(_callable_methods)
84
- end
43
+ # Makes each of the given methods being callable as actions. You can use
44
+ # this to make methods included from modules callable as actions.
45
+ #
46
+ # ==== Parameters
47
+ # *names<~to-s>:: Actions that should be added to the list.
48
+ #
49
+ # ==== Returns
50
+ # Array[String]::
51
+ # An array of actions that should be dispatched to even if they would not
52
+ # otherwise be.
53
+ #
54
+ # ==== Example
55
+ # module Foo
56
+ # def self.included(base)
57
+ # base.show_action(:foo)
58
+ # end
59
+ #
60
+ # def foo
61
+ # # some actiony stuff
62
+ # end
63
+ #
64
+ # def foo_helper
65
+ # # this should not be an action
66
+ # end
67
+ # end
68
+ #
69
+ # @api public
70
+ def self.show_action(*names)
71
+ self._shown_actions = self._shown_actions | names.map {|n| n.to_s}
72
+ end
85
73
 
86
- # This is a stub method so plugins can implement param filtering if they want.
87
- #
88
- # ==== Parameters
89
- # params<Hash{Symbol => String}>:: A list of params
90
- #
91
- # ==== Returns
92
- # Hash{Symbol => String}:: A new list of params, filtered as desired
93
- #---
94
- # @semipublic
95
- def _filter_params(params)
96
- params
97
- end
74
+ # The list of actions that are callable, after taking defaults,
75
+ # _hidden_actions and _shown_actions into consideration. It is calculated
76
+ # once, the first time an action is dispatched for this controller.
77
+ #
78
+ # ==== Returns
79
+ # SimpleSet[String]:: A set of actions that should be callable.
80
+ #
81
+ # @api public
82
+ def self.callable_actions
83
+ @callable_actions ||= Extlib::SimpleSet.new(_callable_methods)
84
+ end
98
85
 
99
- private
86
+ # This is a stub method so plugins can implement param filtering if they want.
87
+ #
88
+ # ==== Parameters
89
+ # params<Hash{Symbol => String}>:: A list of params
90
+ #
91
+ # ==== Returns
92
+ # Hash{Symbol => String}:: A new list of params, filtered as desired
93
+ #
94
+ # @api plugin
95
+ # @overridable
96
+ def self._filter_params(params)
97
+ params
98
+ end
100
99
 
101
- # All methods that are callable as actions.
102
- #
103
- # ==== Returns
104
- # Array:: A list of method names that are also actions
105
- def _callable_methods
106
- callables = []
107
- klass = self
108
- begin
109
- callables << (klass.public_instance_methods(false) + klass._shown_actions) - klass._hidden_actions
110
- klass = klass.superclass
111
- end until klass == Merb::AbstractController || klass == Object
112
- callables.flatten.reject{|action| action =~ /^_.*/}
113
- end
100
+ # All methods that are callable as actions.
101
+ #
102
+ # ==== Returns
103
+ # Array:: A list of method names that are also actions
104
+ #
105
+ # @api private
106
+ def self._callable_methods
107
+ callables = []
108
+ klass = self
109
+ begin
110
+ callables << (klass.public_instance_methods(false) + klass._shown_actions) - klass._hidden_actions
111
+ klass = klass.superclass
112
+ end until klass == Merb::AbstractController || klass == Object
113
+ callables.flatten.reject{|action| action =~ /^_.*/}
114
+ end
114
115
 
115
- end # class << self
116
116
 
117
117
  # The location to look for a template for a particular controller, context,
118
118
  # and mime-type. This is overridden from AbstractController, which defines a
@@ -124,14 +124,14 @@ class Merb::Controller < Merb::AbstractController
124
124
  # The mime-type of the template that will be rendered. Defaults to nil.
125
125
  # controller<~to_s>::
126
126
  # The name of the controller that will be rendered. Defaults to
127
- # controller_name.
127
+ # controller_name. This will be "layout" for rendering a layout.
128
128
  #
129
129
  # ==== Notes
130
130
  # By default, this renders ":controller/:action.:type". To change this,
131
131
  # override it in your application class or in individual controllers.
132
132
  #
133
- #---
134
- # @public
133
+ # @api public
134
+ # @overridable
135
135
  def _template_location(context, type, controller)
136
136
  _conditionally_append_extension(controller ? "#{controller}/#{context}" : "#{context}", type)
137
137
  end
@@ -148,7 +148,7 @@ class Merb::Controller < Merb::AbstractController
148
148
  # type<~to_s>::
149
149
  # The mime-type of the template that will be rendered. Defaults to nil.
150
150
  #
151
- # @public
151
+ # @api public
152
152
  def _absolute_template_location(template, type)
153
153
  _conditionally_append_extension(template, type)
154
154
  end
@@ -164,8 +164,9 @@ class Merb::Controller < Merb::AbstractController
164
164
  # headers<Hash{header => value}>::
165
165
  # A hash of headers to start the controller with. These headers can be
166
166
  # overridden later by the #headers method.
167
- #---
168
- # @semipublic
167
+ #
168
+ # @api plugin
169
+ # @overridable
169
170
  def initialize(request, status=200, headers={'Content-Type' => 'text/html; charset=utf-8'})
170
171
  super()
171
172
  @request, @_status, @headers = request, status, headers
@@ -181,8 +182,8 @@ class Merb::Controller < Merb::AbstractController
181
182
  #
182
183
  # ==== Raises
183
184
  # ActionNotFound:: The requested action was not found in class.
184
- #---
185
- # @semipublic
185
+ #
186
+ # @api plugin
186
187
  def _dispatch(action=:index)
187
188
  Merb.logger.info("Params: #{self.class._filter_params(request.params).inspect}")
188
189
  start = Time.now
@@ -197,6 +198,10 @@ class Merb::Controller < Merb::AbstractController
197
198
 
198
199
  attr_reader :request, :headers
199
200
 
201
+ # ==== Returns
202
+ # Fixnum:: The response status code
203
+ #
204
+ # @api public
200
205
  def status
201
206
  @_status
202
207
  end
@@ -205,6 +210,8 @@ class Merb::Controller < Merb::AbstractController
205
210
  #
206
211
  # ==== Parameters
207
212
  # s<Fixnum, Symbol>:: A status-code or named http-status
213
+ #
214
+ # @api public
208
215
  def status=(s)
209
216
  if s.is_a?(Symbol) && STATUS_CODES.key?(s)
210
217
  @_status = STATUS_CODES[s]
@@ -217,27 +224,112 @@ class Merb::Controller < Merb::AbstractController
217
224
 
218
225
  # ==== Returns
219
226
  # Hash:: The parameters from the request object
227
+ #
228
+ # @api public
220
229
  def params() request.params end
221
230
 
222
- # ==== Parameters
223
- # name<~to_sym, Hash>:: The name of the URL to generate.
224
- # rparams<Hash>:: Parameters for the route generation.
231
+ # There are three possible ways to use this method. First, if you have a named route,
232
+ # you can specify the route as the first parameter as a symbol and any paramters in a
233
+ # hash. Second, you can generate the default route by just passing the params hash,
234
+ # just passing the params hash. Finally, you can use the anonymous parameters. This
235
+ # allows you to specify the parameters to a named route in the order they appear in the
236
+ # router.
237
+ #
238
+ # ==== Parameters(Named Route)
239
+ # name<Symbol>::
240
+ # The name of the route.
241
+ # args<Hash>::
242
+ # Parameters for the route generation.
243
+ #
244
+ # ==== Parameters(Default Route)
245
+ # args<Hash>::
246
+ # Parameters for the route generation. This route will use the default route.
247
+ #
248
+ # ==== Parameters(Anonymous Parameters)
249
+ # name<Symbol>::
250
+ # The name of the route.
251
+ # args<Array>::
252
+ # An array of anonymous parameters to generate the route
253
+ # with. These parameters are assigned to the route parameters
254
+ # in the order that they are passed.
225
255
  #
226
256
  # ==== Returns
227
257
  # String:: The generated URL.
228
258
  #
229
- # ==== Alternatives
230
- # If a hash is used as the first argument, a default route will be
231
- # generated based on it and rparams.
232
- # ====
233
- # TODO: Update this documentation
259
+ # ==== Examples
260
+ # Named Route
261
+ #
262
+ # Merb::Router.prepare do
263
+ # match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")
264
+ # end
265
+ #
266
+ # url(:articles, :title => "new_article")
267
+ #
268
+ # Default Route
269
+ #
270
+ # Merb::Router.prepare do
271
+ # default_routes
272
+ # end
273
+ #
274
+ # url(:controller => "articles", :action => "new")
275
+ #
276
+ # Anonymous Paramters
277
+ #
278
+ # Merb::Router.prepare do
279
+ # match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")
280
+ # end
281
+ #
282
+ # url(:articles, 2008, 10, "test_article")
283
+ #
284
+ # @api public
234
285
  def url(name, *args)
235
286
  args << params
236
287
  Merb::Router.url(name, *args)
237
288
  end
289
+
290
+ # Generates a URL for a single or nested resource.
291
+ #
292
+ # ==== Parameters
293
+ # resources<Symbol,Object>:: The resources for which the URL
294
+ # should be generated. These resources should be specified
295
+ # in the router.rb file using #resources and #resource.
296
+ #
297
+ # options<Hash>:: Any extra parameters that are needed to
298
+ # generate the URL.
299
+ #
300
+ # ==== Returns
301
+ # String:: The generated URL.
302
+ #
303
+ # ==== Examples
304
+ #
305
+ # Merb::Router.prepare do
306
+ # resources :users do
307
+ # resources :comments
308
+ # end
309
+ # end
310
+ #
311
+ # resource(:users) # => /users
312
+ # resource(@user) # => /users/10
313
+ # resource(@user, :comments) # => /users/10/comments
314
+ # resource(@user, @comment) # => /users/10/comments/15
315
+ # resource(:users, :new) # => /users/new
316
+ # resource(:@user, :edit) # => /users/10/edit
317
+ #
318
+ # @api public
319
+ def resource(*args)
320
+ args << params
321
+ Merb::Router.resource(*args)
322
+ end
323
+
238
324
 
239
325
  alias_method :relative_url, :url
240
326
 
327
+ # Returns the absolute url including the passed protocol and host.
328
+ #
329
+ # This uses the same arguments as the url method, with added requirements
330
+ # of protocol and host options.
331
+ #
332
+ # @api public
241
333
  def absolute_url(*args)
242
334
  options = extract_options_from_args!(args) || {}
243
335
  options[:protocol] ||= request.protocol
@@ -251,6 +343,8 @@ class Merb::Controller < Merb::AbstractController
251
343
  # ==== Returns
252
344
  # Array[Integer, Hash, String]::
253
345
  # The controller's status code, headers, and body
346
+ #
347
+ # @api private
254
348
  def rack_response
255
349
  [status, headers, Merb::Rack::StreamWrapper.new(body)]
256
350
  end
@@ -258,6 +352,8 @@ class Merb::Controller < Merb::AbstractController
258
352
  # Sets a controller to be "abstract"
259
353
  # This controller will not be able to be routed to
260
354
  # and is used for super classing only
355
+ #
356
+ # @api public
261
357
  def self.abstract!
262
358
  @_abstract = true
263
359
  end
@@ -267,6 +363,8 @@ class Merb::Controller < Merb::AbstractController
267
363
  # === Returns
268
364
  # Boolean
269
365
  # true if the controller has been set as abstract
366
+ #
367
+ # @api public
270
368
  def self.abstract?
271
369
  !!@_abstract
272
370
  end
@@ -277,6 +375,15 @@ class Merb::Controller < Merb::AbstractController
277
375
  private
278
376
 
279
377
  # If not already added, add the proper mime extension to the template path.
378
+ #
379
+ # ==== Parameters
380
+ #
381
+ # template<~to_s> ::
382
+ # The template path to append the mime type to.
383
+ # type<~to_s> ::
384
+ # The extension to append to the template path conditionally
385
+ #
386
+ # @api private
280
387
  def _conditionally_append_extension(template, type)
281
388
  type && !template.match(/\.#{type.to_s.escape_regexp}$/) ? "#{template}.#{type}" : template
282
389
  end