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
@@ -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