wycats-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 (58) hide show
  1. data/CHANGELOG +136 -2
  2. data/CONTRIBUTORS +6 -0
  3. data/PUBLIC_CHANGELOG +15 -0
  4. data/Rakefile +12 -14
  5. data/lib/merb-core.rb +82 -43
  6. data/lib/merb-core/bootloader.rb +268 -60
  7. data/lib/merb-core/config.rb +119 -34
  8. data/lib/merb-core/controller/abstract_controller.rb +58 -18
  9. data/lib/merb-core/controller/exceptions.rb +2 -15
  10. data/lib/merb-core/controller/merb_controller.rb +28 -1
  11. data/lib/merb-core/controller/mime.rb +4 -0
  12. data/lib/merb-core/controller/mixins/controller.rb +14 -17
  13. data/lib/merb-core/controller/mixins/render.rb +23 -28
  14. data/lib/merb-core/controller/mixins/responder.rb +0 -1
  15. data/lib/merb-core/controller/template.rb +44 -20
  16. data/lib/merb-core/core_ext/kernel.rb +8 -3
  17. data/lib/merb-core/dispatch/default_exception/default_exception.rb +1 -1
  18. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +3 -1
  19. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +71 -67
  20. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +6 -2
  21. data/lib/merb-core/dispatch/dispatcher.rb +5 -9
  22. data/lib/merb-core/dispatch/request.rb +46 -57
  23. data/lib/merb-core/dispatch/router.rb +83 -6
  24. data/lib/merb-core/dispatch/router/behavior.rb +87 -27
  25. data/lib/merb-core/dispatch/router/resources.rb +281 -167
  26. data/lib/merb-core/dispatch/router/route.rb +141 -27
  27. data/lib/merb-core/logger.rb +213 -202
  28. data/lib/merb-core/rack.rb +3 -1
  29. data/lib/merb-core/rack/adapter.rb +7 -4
  30. data/lib/merb-core/rack/adapter/ebb.rb +12 -13
  31. data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -15
  32. data/lib/merb-core/rack/adapter/irb.rb +3 -2
  33. data/lib/merb-core/rack/adapter/mongrel.rb +22 -15
  34. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +4 -16
  35. data/lib/merb-core/rack/adapter/thin.rb +21 -22
  36. data/lib/merb-core/rack/adapter/thin_turbo.rb +4 -11
  37. data/lib/merb-core/rack/adapter/webrick.rb +54 -18
  38. data/lib/merb-core/rack/handler/mongrel.rb +12 -13
  39. data/lib/merb-core/rack/middleware/csrf.rb +1 -1
  40. data/lib/merb-core/server.rb +135 -98
  41. data/lib/merb-core/tasks/gem_management.rb +50 -12
  42. data/lib/merb-core/tasks/merb.rb +1 -0
  43. data/lib/merb-core/tasks/merb_rake_helper.rb +9 -38
  44. data/lib/merb-core/tasks/stats.rake +2 -2
  45. data/lib/merb-core/test.rb +9 -3
  46. data/lib/merb-core/test/helpers.rb +1 -0
  47. data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -2
  48. data/lib/merb-core/test/helpers/request_helper.rb +40 -372
  49. data/lib/merb-core/test/helpers/route_helper.rb +15 -7
  50. data/lib/merb-core/test/matchers.rb +1 -0
  51. data/lib/merb-core/test/matchers/controller_matchers.rb +4 -247
  52. data/lib/merb-core/test/matchers/view_matchers.rb +22 -4
  53. data/lib/merb-core/test/run_specs.rb +117 -25
  54. data/lib/merb-core/version.rb +1 -1
  55. metadata +1 -1
  56. data/lib/merb-core/vendor/facets.rb +0 -2
  57. data/lib/merb-core/vendor/facets/dictionary.rb +0 -433
  58. data/lib/merb-core/vendor/facets/inflect.rb +0 -342
@@ -1,191 +1,305 @@
1
1
  module Merb
2
2
  class Router
3
- class Behavior
4
- module Resources
5
-
6
- # Behavior#+resources+ is a route helper for defining a collection of
7
- # RESTful resources. It yields to a block for child routes.
8
- #
9
- # ==== Parameters
10
- # name<String, Symbol>:: The name of the resources
11
- # options<Hash>::
12
- # Ovverides and parameters to be associated with the route
13
- #
14
- # ==== Options (options)
15
- # :namespace<~to_s>: The namespace for this route.
16
- # :name_prefix<~to_s>:
17
- # A prefix for the named routes. If a namespace is passed and there
18
- # isn't a name prefix, the namespace will become the prefix.
19
- # :controller<~to_s>: The controller for this route
20
- # :collection<~to_s>: Special settings for the collections routes
21
- # :member<Hash>:
22
- # Special settings and resources related to a specific member of this
23
- # resource.
24
- # :keys<Array>:
25
- # A list of the keys to be used instead of :id with the resource in the order of the url.
26
- #
27
- # ==== Block parameters
28
- # next_level<Behavior>:: The child behavior.
29
- #
30
- # ==== Returns
31
- # Array::
32
- # Routes which will define the specified RESTful collection of resources
33
- #
34
- # ==== Examples
35
- #
36
- # r.resources :posts # will result in the typical RESTful CRUD
37
- # # lists resources
38
- # # GET /posts/?(\.:format)? :action => "index"
39
- # # GET /posts/index(\.:format)? :action => "index"
40
- #
41
- # # shows new resource form
42
- # # GET /posts/new :action => "new"
43
- #
44
- # # creates resource
45
- # # POST /posts/?(\.:format)?, :action => "create"
46
- #
47
- # # shows resource
48
- # # GET /posts/:id(\.:format)? :action => "show"
49
- #
50
- # # shows edit form
51
- # # GET /posts/:id/edit :action => "edit"
52
- #
53
- # # updates resource
54
- # # PUT /posts/:id(\.:format)? :action => "update"
55
- #
56
- # # shows deletion confirmation page
57
- # # GET /posts/:id/delete :action => "delete"
58
- #
59
- # # destroys resources
60
- # # DELETE /posts/:id(\.:format)? :action => "destroy"
61
- #
62
- # # Nesting resources
63
- # r.resources :posts do |posts|
64
- # posts.resources :comments
65
- # end
66
- #---
67
- # @public
68
- def resources(name, options = {}, &block)
69
- singular = name.to_s.singularize
70
- keys = options.delete(:keys) || [:id]
71
- params = { :controller => options.delete(:controller) || name.to_s }
72
- collection = options.delete(:collection) || {}
73
- member = { :edit => :get, :delete => :get }.merge(options.delete(:member) || {})
3
+
4
+ module Resources
5
+ # Behavior#+resources+ is a route helper for defining a collection of
6
+ # RESTful resources. It yields to a block for child routes.
7
+ #
8
+ # ==== Parameters
9
+ # name<String, Symbol>:: The name of the resources
10
+ # options<Hash>::
11
+ # Ovverides and parameters to be associated with the route
12
+ #
13
+ # ==== Options (options)
14
+ # :namespace<~to_s>: The namespace for this route.
15
+ # :name_prefix<~to_s>:
16
+ # A prefix for the named routes. If a namespace is passed and there
17
+ # isn't a name prefix, the namespace will become the prefix.
18
+ # :controller<~to_s>: The controller for this route
19
+ # :collection<~to_s>: Special settings for the collections routes
20
+ # :member<Hash>:
21
+ # Special settings and resources related to a specific member of this
22
+ # resource.
23
+ # :identify<Symbol|Array>: The method(s) that should be called on the object
24
+ # before inserting it into an URL.
25
+ # :keys<Array>:
26
+ # A list of the keys to be used instead of :id with the resource in the order of the url.
27
+ # :singular<Symbol>
28
+ #
29
+ # ==== Block parameters
30
+ # next_level<Behavior>:: The child behavior.
31
+ #
32
+ # ==== Returns
33
+ # Array::
34
+ # Routes which will define the specified RESTful collection of resources
35
+ #
36
+ # ==== Examples
37
+ #
38
+ # r.resources :posts # will result in the typical RESTful CRUD
39
+ # # lists resources
40
+ # # GET /posts/?(\.:format)? :action => "index"
41
+ # # GET /posts/index(\.:format)? :action => "index"
42
+ #
43
+ # # shows new resource form
44
+ # # GET /posts/new :action => "new"
45
+ #
46
+ # # creates resource
47
+ # # POST /posts/?(\.:format)?, :action => "create"
48
+ #
49
+ # # shows resource
50
+ # # GET /posts/:id(\.:format)? :action => "show"
51
+ #
52
+ # # shows edit form
53
+ # # GET /posts/:id/edit :action => "edit"
54
+ #
55
+ # # updates resource
56
+ # # PUT /posts/:id(\.:format)? :action => "update"
57
+ #
58
+ # # shows deletion confirmation page
59
+ # # GET /posts/:id/delete :action => "delete"
60
+ #
61
+ # # destroys resources
62
+ # # DELETE /posts/:id(\.:format)? :action => "destroy"
63
+ #
64
+ # # Nesting resources
65
+ # r.resources :posts do |posts|
66
+ # posts.resources :comments
67
+ # end
68
+ #---
69
+ # @public
70
+ def resources(name, *args, &block)
71
+ name = name.to_s
72
+ options = extract_options_from_args!(args) || {}
73
+ singular = options[:singular] ? options[:singular].to_s : Extlib::Inflection.singularize(name)
74
+ klass_name = args.first ? args.first.to_s : Extlib::Inflection.classify(singular)
75
+ klass = Object.full_const_get(klass_name) rescue nil
76
+ keys = options.delete(:keys) || options.delete(:key)
77
+ params = { :controller => options.delete(:controller) || name }
78
+ collection = options.delete(:collection) || {}
79
+ member = { :edit => :get, :delete => :get }.merge(options.delete(:member) || {})
80
+
81
+ # Use the identifier for the class as a default
82
+ if klass
83
+ keys ||= options[:identify]
84
+ keys ||= @identifiers[klass]
85
+ elsif options[:identify]
86
+ raise Error, "The constant #{klass_name} does not exist, please specify the constant for this resource"
87
+ end
88
+
89
+ keys = [ keys || :id ].flatten
90
+
74
91
 
75
- # Try pulling :namespace out of options for backwards compatibility
76
- options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
77
- options[:controller_prefix] ||= options.delete(:namespace)
92
+ # Try pulling :namespace out of options for backwards compatibility
93
+ options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
94
+ options[:resource_prefix] ||= nil # Don't use a resource_prefix if not needed
95
+ options[:controller_prefix] ||= options.delete(:namespace)
78
96
 
79
- self.namespace(name, options).to(params) do |resource|
80
- root_keys = keys.map { |k| ":#{k}" }.join("/")
81
- # => index
82
- resource.match("(/index)(.:format)", :method => :get).to(:action => "index").name(name)
83
- # => create
84
- resource.match("(.:format)", :method => :post).to(:action => "create")
85
- # => new
86
- resource.match("/new(.:format)", :method => :get).to(:action => "new").name(:new, singular)
97
+ context = options[:identify]
98
+ context = klass && options[:identify] ? identify(klass => options.delete(:identify)) : self
99
+ context.namespace(name, options).to(params) do |resource|
100
+ root_keys = keys.map { |k| ":#{k}" }.join("/")
101
+
102
+ # => index
103
+ resource.match("(/index)(.:format)", :method => :get).to(:action => "index").
104
+ name(name).register_resource(name)
105
+
106
+ # => create
107
+ resource.match("(.:format)", :method => :post).to(:action => "create")
108
+
109
+ # => new
110
+ resource.match("/new(.:format)", :method => :get).to(:action => "new").
111
+ name("new", singular).register_resource(name, "new")
87
112
 
88
- # => user defined collection routes
89
- collection.each_pair do |action, method|
90
- resource.match("/#{action}(.:format)", :method => method).to(:action => "#{action}").name(action, name)
91
- end
113
+ # => user defined collection routes
114
+ collection.each_pair do |action, method|
115
+ action = action.to_s
116
+ resource.match("/#{action}(.:format)", :method => method).to(:action => "#{action}").
117
+ name(action, name).register_resource(name, action)
118
+ end
92
119
 
93
- # => show
94
- resource.match("/#{root_keys}(.:format)", :method => :get).to(:action => "show").name(singular)
120
+ # => show
121
+ resource.match("/#{root_keys}(.:format)", :method => :get).to(:action => "show").
122
+ name(singular).register_resource(klass_name)
95
123
 
96
- # => user defined member routes
97
- member.each_pair do |action, method|
98
- resource.match("/#{root_keys}/#{action}(.:format)", :method => method).to(:action => "#{action}").name(action, singular)
99
- end
124
+ # => user defined member routes
125
+ member.each_pair do |action, method|
126
+ action = action.to_s
127
+ resource.match("/#{root_keys}/#{action}(.:format)", :method => method).
128
+ to(:action => "#{action}").name(action, singular).register_resource(klass_name, action)
129
+ end
100
130
 
101
- # => update
102
- resource.match("/#{root_keys}(.:format)", :method => :put).to(:action => "update")
103
- # => destroy
104
- resource.match("/#{root_keys}(.:format)", :method => :delete).to(:action => "destroy")
131
+ # => update
132
+ resource.match("/#{root_keys}(.:format)", :method => :put).
133
+ to(:action => "update")
134
+
135
+ # => destroy
136
+ resource.match("/#{root_keys}(.:format)", :method => :delete).
137
+ to(:action => "destroy")
105
138
 
106
- if block_given?
107
- nested_keys = keys.map { |k| k.to_s == "id" ? ":#{singular}_id" : ":#{k}" }.join("/")
108
- resource.options(:name_prefix => singular).match("/#{nested_keys}", &block)
139
+ if block_given?
140
+ nested_keys = keys.map do |k|
141
+ k.to_s == "id" ? ":#{singular}_id" : ":#{k}"
142
+ end.join("/")
143
+
144
+ # Procs for building the extra collection/member resource routes
145
+ placeholder = Router.resource_routes[ [@options[:resource_prefix], klass_name].flatten.compact ]
146
+ builders = {}
147
+
148
+ builders[:collection] = lambda do |action, to, method|
149
+ resource.before(placeholder).match("/#{action}(.:format)", :method => method).
150
+ to(:action => to).name(action, name).register_resource(name, action)
109
151
  end
110
-
152
+
153
+ builders[:member] = lambda do |action, to, method|
154
+ resource.match("/#{root_keys}/#{action}(.:format)", :method => method).
155
+ to(:action => to).name(action, singular).register_resource(klass_name, action)
156
+ end
157
+
158
+ resource.options(:name_prefix => singular, :resource_prefix => klass_name).
159
+ match("/#{nested_keys}").resource_block(builders, &block)
111
160
  end
112
- end
161
+ end # namespace
162
+ end # resources
113
163
 
114
- # Behavior#+resource+ is a route helper for defining a singular RESTful
115
- # resource. It yields to a block for child routes.
116
- #
117
- # ==== Parameters
118
- # name<String, Symbol>:: The name of the resource.
119
- # options<Hash>::
120
- # Overides and parameters to be associated with the route.
121
- #
122
- # ==== Options (options)
123
- # :namespace<~to_s>: The namespace for this route.
124
- # :name_prefix<~to_s>:
125
- # A prefix for the named routes. If a namespace is passed and there
126
- # isn't a name prefix, the namespace will become the prefix.
127
- # :controller<~to_s>: The controller for this route
128
- #
129
- # ==== Block parameters
130
- # next_level<Behavior>:: The child behavior.
131
- #
132
- # ==== Returns
133
- # Array:: Routes which define a RESTful single resource.
134
- #
135
- # ==== Examples
136
- #
137
- # r.resource :account # will result in the typical RESTful CRUD
138
- # # shows new resource form
139
- # # GET /account/new :action => "new"
140
- #
141
- # # creates resource
142
- # # POST /account/?(\.:format)?, :action => "create"
143
- #
144
- # # shows resource
145
- # # GET /account/(\.:format)? :action => "show"
146
- #
147
- # # shows edit form
148
- # # GET /account//edit :action => "edit"
149
- #
150
- # # updates resource
151
- # # PUT /account/(\.:format)? :action => "update"
152
- #
153
- # # shows deletion confirmation page
154
- # # GET /account//delete :action => "delete"
155
- #
156
- # # destroys resources
157
- # # DELETE /account/(\.:format)? :action => "destroy"
158
- #
159
- # You can optionally pass :namespace and :controller to refine the routing
160
- # or pass a block to nest resources.
161
- #
162
- # r.resource :account, :namespace => "admin" do |account|
163
- # account.resources :preferences, :controller => "settings"
164
- # end
165
- # ---
166
- # @public
167
- def resource(name, options = {}, &block)
168
- params = { :controller => options.delete(:controller) || name.to_s.pluralize }
164
+ # Behavior#+resource+ is a route helper for defining a singular RESTful
165
+ # resource. It yields to a block for child routes.
166
+ #
167
+ # ==== Parameters
168
+ # name<String, Symbol>:: The name of the resource.
169
+ # options<Hash>::
170
+ # Overides and parameters to be associated with the route.
171
+ #
172
+ # ==== Options (options)
173
+ # :namespace<~to_s>: The namespace for this route.
174
+ # :name_prefix<~to_s>:
175
+ # A prefix for the named routes. If a namespace is passed and there
176
+ # isn't a name prefix, the namespace will become the prefix.
177
+ # :controller<~to_s>: The controller for this route
178
+ #
179
+ # ==== Block parameters
180
+ # next_level<Behavior>:: The child behavior.
181
+ #
182
+ # ==== Returns
183
+ # Array:: Routes which define a RESTful single resource.
184
+ #
185
+ # ==== Examples
186
+ #
187
+ # r.resource :account # will result in the typical RESTful CRUD
188
+ # # shows new resource form
189
+ # # GET /account/new :action => "new"
190
+ #
191
+ # # creates resource
192
+ # # POST /account/?(\.:format)?, :action => "create"
193
+ #
194
+ # # shows resource
195
+ # # GET /account/(\.:format)? :action => "show"
196
+ #
197
+ # # shows edit form
198
+ # # GET /account//edit :action => "edit"
199
+ #
200
+ # # updates resource
201
+ # # PUT /account/(\.:format)? :action => "update"
202
+ #
203
+ # # shows deletion confirmation page
204
+ # # GET /account//delete :action => "delete"
205
+ #
206
+ # # destroys resources
207
+ # # DELETE /account/(\.:format)? :action => "destroy"
208
+ #
209
+ # You can optionally pass :namespace and :controller to refine the routing
210
+ # or pass a block to nest resources.
211
+ #
212
+ # r.resource :account, :namespace => "admin" do |account|
213
+ # account.resources :preferences, :controller => "settings"
214
+ # end
215
+ # ---
216
+ # @public
217
+ def resource(name, *args, &block)
218
+ name = name.to_s
219
+ options = extract_options_from_args!(args) || {}
220
+ params = { :controller => options.delete(:controller) || name.pluralize }
221
+ member = { :new => :get, :edit => :get, :delete => :get }.merge(options.delete(:member) || {})
169
222
 
170
- options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
171
- options[:controller_prefix] ||= options.delete(:namespace)
223
+ options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
224
+ options[:resource_prefix] ||= nil # Don't use a resource_prefix if not needed
225
+ options[:controller_prefix] ||= options.delete(:namespace)
172
226
 
173
- self.namespace(name, options).to(params) do |resource|
174
- resource.match("(.:format)", :method => :get ).to(:action => "show" ).name(name)
175
- resource.match("(.:format)", :method => :post ).to(:action => "create" )
176
- resource.match("(.:format)", :method => :put ).to(:action => "update" )
177
- resource.match("(.:format)", :method => :delete).to(:action => "destroy")
178
- resource.match("/new(.:format)", :method => :get ).to(:action => "new" ).name(:new, name)
179
- resource.match("/edit(.:format)", :method => :get ).to(:action => "edit" ).name(:edit, name)
180
- resource.match("/delete(.:format)", :method => :get ).to(:action => "delete" ).name(:delete, name)
227
+ self.namespace(name, options).to(params) do |resource|
228
+ # => show
229
+ resource.match("(.:format)", :method => :get).to(:action => "show").
230
+ name(name).register_resource(name)
231
+
232
+ # => create
233
+ resource.match("(.:format)", :method => :post).to(:action => "create")
234
+
235
+ # => update
236
+ resource.match("(.:format)", :method => :put).to(:action => "update")
237
+
238
+ # => destroy
239
+ resource.match("(.:format)", :method => :delete).to(:action => "destroy")
240
+
241
+ member.each_pair do |action, method|
242
+ action = action.to_s
243
+ resource.match("/#{action}(.:format)", :method => method).to(:action => action).
244
+ name(action, name).register_resource(name, action)
245
+ end
181
246
 
182
- resource.options(:name_prefix => name, &block) if block_given?
247
+ if block_given?
248
+ builders = {}
249
+
250
+ builders[:member] = lambda do |action, to, method|
251
+ resource.match("/#{action}(.:format)", :method => method).to(:action => to).
252
+ name(action, name).register_resource(name, action)
253
+ end
254
+
255
+ resource.options(:name_prefix => name, :resource_prefix => name).
256
+ resource_block(builders, &block)
183
257
  end
184
258
  end
259
+ end
260
+
261
+ protected
262
+
263
+ def register_resource(*key)
264
+ key = [@options[:resource_prefix], key].flatten.compact
265
+ @route.resource = key
266
+ self
267
+ end
185
268
 
269
+ def resource_block(builders, &block)
270
+ behavior = ResourceBehavior.new(builders, @proxy, @conditions, @params, @defaults, @identifiers, @options, @blocks)
271
+ with_behavior_context(behavior, &block)
186
272
  end
187
273
 
274
+ end # Resources
275
+
276
+ class Behavior
188
277
  include Resources
189
278
  end
279
+
280
+ # Adding the collection and member methods to behavior
281
+ class ResourceBehavior < Behavior #:nodoc:
282
+
283
+ def initialize(builders, *args)
284
+ super(*args)
285
+ @collection = builders[:collection]
286
+ @member = builders[:member]
287
+ end
288
+
289
+ def collection(action, options = {})
290
+ action = action.to_s
291
+ method = options[:method]
292
+ to = options[:to] || action
293
+ @collection[action, to, method]
294
+ end
295
+
296
+ def member(action, options = {})
297
+ action = action.to_s
298
+ method = options[:method]
299
+ to = options[:to] || action
300
+ @member[action, to, method]
301
+ end
302
+
303
+ end
190
304
  end
191
305
  end