merb 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/README +138 -56
  2. data/Rakefile +23 -8
  3. data/app_generators/merb/templates/Rakefile +13 -0
  4. data/app_generators/merb/templates/app/helpers/global_helper.rb +1 -1
  5. data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +12 -3
  6. data/app_generators/merb/templates/config/merb.yml +14 -1
  7. data/app_generators/merb/templates/spec/spec_helper.rb +6 -0
  8. data/app_generators/merb/templates/test/test_helper.rb +1 -0
  9. data/lib/merb.rb +27 -7
  10. data/lib/merb/abstract_controller.rb +76 -36
  11. data/lib/merb/caching/store/memcache.rb +20 -0
  12. data/lib/merb/constants.rb +2 -4
  13. data/lib/merb/controller.rb +44 -2
  14. data/lib/merb/core_ext/get_args.rb +23 -4
  15. data/lib/merb/core_ext/hash.rb +16 -11
  16. data/lib/merb/core_ext/inflections.rb +1 -1
  17. data/lib/merb/core_ext/kernel.rb +106 -26
  18. data/lib/merb/core_ext/numeric.rb +1 -1
  19. data/lib/merb/core_ext/string.rb +10 -13
  20. data/lib/merb/dispatcher.rb +2 -2
  21. data/lib/merb/exceptions.rb +3 -1
  22. data/lib/merb/logger.rb +15 -6
  23. data/lib/merb/mail_controller.rb +18 -2
  24. data/lib/merb/mailer.rb +1 -1
  25. data/lib/merb/mixins/controller.rb +64 -228
  26. data/lib/merb/mixins/erubis_capture.rb +1 -1
  27. data/lib/merb/mixins/general_controller.rb +258 -0
  28. data/lib/merb/mixins/render.rb +45 -24
  29. data/lib/merb/mixins/responder.rb +89 -18
  30. data/lib/merb/mixins/view_context.rb +32 -5
  31. data/lib/merb/mixins/web_controller.rb +8 -1
  32. data/lib/merb/mongrel_handler.rb +27 -17
  33. data/lib/merb/part_controller.rb +10 -0
  34. data/lib/merb/request.rb +34 -14
  35. data/lib/merb/router.rb +77 -45
  36. data/lib/merb/server.rb +116 -72
  37. data/lib/merb/session/cookie_store.rb +14 -22
  38. data/lib/merb/session/mem_cache_session.rb +2 -2
  39. data/lib/merb/session/memory_session.rb +12 -1
  40. data/lib/merb/template/erubis.rb +31 -0
  41. data/lib/merb/template/haml.rb +4 -14
  42. data/lib/merb/template/xml_builder.rb +1 -1
  43. data/lib/merb/test/helper.rb +90 -18
  44. data/lib/merb/test/rspec.rb +145 -74
  45. data/lib/merb/version.rb +11 -0
  46. data/lib/merb/view_context.rb +3 -6
  47. data/lib/patch +69 -0
  48. data/lib/tasks/merb.rake +1 -1
  49. data/spec/fixtures/config/environments/environment_config_test.yml +1 -0
  50. data/spec/fixtures/controllers/render_spec_controllers.rb +63 -4
  51. data/spec/fixtures/views/examples/template_throw_content_without_block.html.erb +3 -0
  52. data/spec/fixtures/views/partials/_erubis.html.erb +1 -1
  53. data/spec/merb/abstract_controller_spec.rb +1 -0
  54. data/spec/merb/controller_filters_spec.rb +68 -3
  55. data/spec/merb/controller_spec.rb +35 -68
  56. data/spec/merb/cookie_store_spec.rb +7 -20
  57. data/spec/merb/core_ext_spec.rb +35 -1
  58. data/spec/merb/dispatch_spec.rb +8 -2
  59. data/spec/merb/generator_spec.rb +12 -4
  60. data/spec/merb/mail_controller_spec.rb +33 -0
  61. data/spec/merb/part_controller_spec.rb +33 -1
  62. data/spec/merb/render_spec.rb +74 -0
  63. data/spec/merb/request_spec.rb +43 -0
  64. data/spec/merb/responder_spec.rb +1 -0
  65. data/spec/merb/router_spec.rb +118 -13
  66. data/spec/merb/server_spec.rb +19 -0
  67. data/spec/merb/view_context_spec.rb +31 -3
  68. data/spec/spec_helper.rb +8 -0
  69. data/spec/spec_helpers/url_shared_behaviour.rb +112 -0
  70. metadata +124 -87
@@ -48,7 +48,16 @@ module Merb
48
48
  # image_tag('http://test.com/foo.gif')
49
49
  # # => <img src="http://test.com/foo.gif">
50
50
  def image_tag(img, opts={})
51
- opts[:path] ||= (img =~ %r{^https?://}) ? '' : '/images/'
51
+ opts[:path] ||=
52
+ if img =~ %r{^https?://}
53
+ ''
54
+ else
55
+ if Merb::Server.config[:path_prefix]
56
+ Merb::Server.config[:path_prefix] + '/images/'
57
+ else
58
+ '/images/'
59
+ end
60
+ end
52
61
  opts[:src] ||= opts.delete(:path) + img
53
62
  %{<img #{ opts.to_xml_attributes } />}
54
63
  end
@@ -258,7 +267,9 @@ module Merb
258
267
  include_tag = ""
259
268
  scripts.each do |script|
260
269
  script = script.to_s
261
- include_tag << %Q|<script src="/javascripts/#{script=~/\.js$/ ? script : script+'.js' }" type="text/javascript">//</script>\n|
270
+ url = "/javascripts/#{script =~ /\.js$/ ? script : script + '.js'}"
271
+ url = Merb::Server.config[:path_prefix] + url if Merb::Server.config[:path_prefix]
272
+ include_tag << %Q|<script src="#{url}" type="text/javascript">//</script>\n|
262
273
  end
263
274
  include_tag
264
275
  end
@@ -287,7 +298,9 @@ module Merb
287
298
  include_tag = ""
288
299
  scripts.each do |script|
289
300
  script = script.to_s
290
- include_tag << %Q|<link href="/stylesheets/#{script=~/\.css$/ ? script : script+'.css' }" media="all" rel="Stylesheet" type="text/css"/>\n|
301
+ url = "/stylesheets/#{script =~ /\.css$/ ? script : script + '.css'}"
302
+ url = Merb::Server.config[:path_prefix] + url if Merb::Server.config[:path_prefix]
303
+ include_tag << %Q|<link href="#{url}" media="all" rel="Stylesheet" type="text/css"/>\n|
291
304
  end
292
305
  include_tag
293
306
  end
@@ -339,8 +352,22 @@ module Merb
339
352
  # You can use catch_content :header anywhere in your templates.
340
353
  #
341
354
  # <%= catch_content :header %>
342
- def throw_content(name, content = nil, &block)
343
- controller.thrown_content[name] << ( content || "" ) << capture( &block )
355
+ #
356
+ # You may find that you have trouble using thrown content inside a helper method
357
+ # There are a couple of mechanisms to get around this.
358
+ #
359
+ # 1. Pass the content in as a string instead of a block
360
+ #
361
+ # Example:
362
+ #
363
+ # throw_content(:header, "Hello World")
364
+ #
365
+ # In Haml Templates, use the
366
+ #
367
+ #
368
+ def throw_content(name, content = "", &block)
369
+ content << capture(&block) if block_given?
370
+ controller.thrown_content[name] << content
344
371
  end
345
372
 
346
373
  # Concat will concatenate text directly to the buffer of the template.
@@ -1,5 +1,8 @@
1
+ # Used by secondary actions (a PartsController or the view)
2
+ # to provide the standard objects associated with each request.
3
+
1
4
  module Merb
2
- module WebControllerMixin
5
+ module WebControllerMixin #:nodoc:
3
6
 
4
7
  def request
5
8
  @web_controller.request
@@ -24,6 +27,10 @@ module Merb
24
27
  def response
25
28
  @web_controller.response
26
29
  end
30
+
31
+ def route
32
+ request.route
33
+ end
27
34
 
28
35
  end
29
36
  end
@@ -26,8 +26,9 @@ class MerbHandler < Mongrel::HttpHandler
26
26
  # Take the name of a directory and use that as the doc root or public
27
27
  # directory of your site. This is set to the root of your merb app + '/public'
28
28
  # by default.
29
- def initialize(dir, opts = {})
29
+ def initialize(dir, mongrel_x_sendfile=true, opts = {})
30
30
  @files = Mongrel::DirHandler.new(dir,false)
31
+ @mongrel_x_sendfile = mongrel_x_sendfile
31
32
  end
32
33
 
33
34
  # process incoming http requests and do a number of things
@@ -42,12 +43,12 @@ class MerbHandler < Mongrel::HttpHandler
42
43
  # header. if you set this header to the path of a file in your controller
43
44
  # then mongrel will serve the file directly and your controller can go on
44
45
  # processing other requests.
45
- def process(request, response)
46
+ def process(request, response)
47
+ return if response.socket.closed?
48
+
46
49
  start = Time.now
47
50
  benchmarks = {}
48
51
 
49
- return if response.socket.closed?
50
-
51
52
  MERB_LOGGER.info("\nRequest: REQUEST_URI: #{
52
53
  request.params[Mongrel::Const::REQUEST_URI]} (#{Time.now.strftime("%Y-%m-%d %H:%M:%S")})")
53
54
 
@@ -107,19 +108,28 @@ class MerbHandler < Mongrel::HttpHandler
107
108
  end
108
109
 
109
110
  if sendfile
110
- benchmarks[:sendfile_time] = Time.now - start
111
- MERB_LOGGER.info("X-SENDFILE: #{sendfile}\nComplete Request took: #{
112
- benchmarks[:sendfile_time]} seconds")
113
- file_status = File.stat(sendfile)
114
- response.status = 200
115
- # Set the last modified times as well and etag for all files
116
- response.header[Mongrel::Const::LAST_MODIFIED] = file_status.mtime.httpdate
117
- # Calculated the same as apache, not sure how well the works on win32
118
- response.header[Mongrel::Const::ETAG] = Mongrel::Const::ETAG_FORMAT % [file_status.mtime.to_i, file_status.size, file_status.ino]
119
- # Send a status with out content length
120
- response.send_status(file_status.size)
121
- response.send_header
122
- response.send_file(sendfile)
111
+ if @mongrel_x_sendfile
112
+ # we want to emulate X-Sendfile header internally in mongrel
113
+ benchmarks[:sendfile_time] = Time.now - start
114
+ MERB_LOGGER.info("X-SENDFILE: #{sendfile}\nComplete Request took: #{
115
+ benchmarks[:sendfile_time]} seconds")
116
+ file_status = File.stat(sendfile)
117
+ response.status = 200
118
+ # Set the last modified times as well and etag for all files
119
+ response.header[Mongrel::Const::LAST_MODIFIED] = file_status.mtime.httpdate
120
+ # Calculated the same as apache, not sure how well the works on win32
121
+ response.header[Mongrel::Const::ETAG] = Mongrel::Const::ETAG_FORMAT % [file_status.mtime.to_i, file_status.size, file_status.ino]
122
+ # Send a status with out content length
123
+ response.send_status(file_status.size)
124
+ response.send_header
125
+ response.send_file(sendfile)
126
+ else
127
+ # we want to pass thru the X-Sendfile header so apache or whatever
128
+ # front server can handle it
129
+ response.header['X-Sendfile'] = sendfile
130
+ response.header['Content-length'] = clength || File.size(sendfile)
131
+ response.finished
132
+ end
123
133
  elsif controller.body.respond_to? :read
124
134
  response.send_status(clength)
125
135
  response.send_header
@@ -15,5 +15,15 @@ module Merb
15
15
  params[:action] = old_action
16
16
  @_body
17
17
  end
18
+
19
+ private
20
+
21
+ # This method is here to overwrite the one in the general_controller mixin
22
+ # The method ensures that when a url is generated with a hash, it contains a controller
23
+ def get_controller_for_url_generation(opts)
24
+ controller = opts[:controller] || @web_controller.params[:controller]
25
+ raise "No Controller Specified for url()" unless controller
26
+ controller
27
+ end
18
28
  end
19
29
  end
@@ -22,7 +22,11 @@ module Merb
22
22
  when :get, :head, :put, :delete
23
23
  request_method
24
24
  when :post
25
- m = body_and_query_params['_method']
25
+ if self.class.parse_multipart_params
26
+ m = body_and_query_params.merge(multipart_params)['_method']
27
+ else
28
+ m = body_and_query_params['_method']
29
+ end
26
30
  m.downcase! if m
27
31
  METHODS.include?(m) ? m.to_sym : :post
28
32
  else
@@ -65,21 +69,31 @@ module Merb
65
69
  @body_and_query_params ||= begin
66
70
  h = query_params
67
71
  h.merge!(body_params) if body_params
68
- h
72
+ h.to_mash
69
73
  end
70
74
  end
71
75
 
72
76
  def multipart_params
73
- @multipart_params ||= begin
74
- if Merb::Const::MULTIPART_REGEXP =~ content_type
75
- self.class.parse_multipart(@body, $1, content_length)
77
+ @multipart_params ||=
78
+ begin
79
+ if Merb::Const::MULTIPART_REGEXP =~ content_type
80
+ if @body.size <= 0
81
+ {}
82
+ else
83
+ self.class.parse_multipart(@body, $1, content_length)
84
+ end
85
+ else
86
+ {}
87
+ end
88
+ rescue ControllerExceptions::MultiPartParseError => e
89
+ @multipart_params = {}
90
+ raise e
76
91
  end
77
- end
78
92
  end
79
93
 
80
94
  def json_params
81
95
  @json_params ||= begin
82
- if [Merb::Const::APPLICATION_JSON, Merb::Const::TEXT_JSON].include?(content_type)
96
+ if Merb::Const::JSON_MIME_TYPE_REGEXP.match(content_type)
83
97
  JSON.parse(raw_post)
84
98
  end
85
99
  end
@@ -87,7 +101,7 @@ module Merb
87
101
 
88
102
  def xml_params
89
103
  @xml_params ||= begin
90
- if [Merb::Const::APPLICATION_XML, Merb::Const::TEXT_XML].include?(content_type)
104
+ if Merb::Const::XML_MIME_TYPE_REGEXP.match(content_type)
91
105
  Hash.from_xml(raw_post) rescue Mash.new
92
106
  end
93
107
  end
@@ -97,7 +111,7 @@ module Merb
97
111
 
98
112
  def params
99
113
  @params ||= begin
100
- h = route_params.to_mash.merge(body_and_query_params)
114
+ h = body_and_query_params.merge(route_params)
101
115
  h.merge!(multipart_params) if self.class.parse_multipart_params && multipart_params
102
116
  h.merge!(json_params) if self.class.parse_json_params && json_params
103
117
  h.merge!(xml_params) if self.class.parse_xml_params && xml_params
@@ -128,7 +142,11 @@ module Merb
128
142
  end
129
143
 
130
144
  def controller_name
131
- route_params[:controller]
145
+ if route_params[:namespace]
146
+ route_params[:namespace] + '/' + route_params[:controller]
147
+ else
148
+ route_params[:controller]
149
+ end
132
150
  end
133
151
 
134
152
  def controller_class
@@ -157,7 +175,9 @@ module Merb
157
175
 
158
176
  def raw_post
159
177
  @body.rewind
160
- @body.read
178
+ res = @body.read
179
+ @body.rewind
180
+ res
161
181
  end
162
182
 
163
183
  # Returns true if the request is an Ajax request.
@@ -334,7 +354,7 @@ module Merb
334
354
  bufsize = 16384
335
355
  content_length -= boundary_size
336
356
  status = input.read(boundary_size)
337
- raise EOFError, "bad content body" unless status == boundary + EOL
357
+ raise ControllerExceptions::MultiPartParseError, "bad content body:\n'#{status}' should == '#{boundary + EOL}'" unless status == boundary + EOL
338
358
  rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
339
359
  loop {
340
360
  head = nil
@@ -369,7 +389,7 @@ module Merb
369
389
  read_size = bufsize < content_length ? bufsize : content_length
370
390
  if( read_size > 0 )
371
391
  c = input.read(read_size)
372
- raise EOFError, "bad content body" if c.nil? || c.empty?
392
+ raise ControllerExceptions::MultiPartParseError, "bad content body" if c.nil? || c.empty?
373
393
  buf << c
374
394
  content_length -= c.size
375
395
  end
@@ -389,7 +409,7 @@ module Merb
389
409
  :filename => File.basename(filename),
390
410
  :content_type => content_type,
391
411
  :tempfile => body,
392
- :size => File.size(body)
412
+ :size => File.size(body.path)
393
413
  }
394
414
  else
395
415
  data = body
@@ -14,18 +14,18 @@ module Merb
14
14
  def append(&block)
15
15
  prepare(@@routes, [], &block)
16
16
  end
17
-
17
+
18
18
  def prepend(&block)
19
19
  prepare([], @@routes, &block)
20
20
  end
21
-
21
+
22
22
  def prepare(first = [], last = [], &block)
23
23
  @@routes = []
24
24
  yield Behavior.new({}, {:action => 'index'}) # defaults
25
25
  @@routes = first + @@routes + last
26
26
  compile
27
27
  end
28
-
28
+
29
29
  def compiled_statement
30
30
  @@compiled_statement = "lambda { |request, params|\n"
31
31
  @@compiled_statement << " cached_path = request.path\n cached_method = request.method.to_s\n "
@@ -35,17 +35,17 @@ module Merb
35
35
  @@compiled_statement << " end\n"
36
36
  @@compiled_statement << "}"
37
37
  end
38
-
38
+
39
39
  def compile
40
40
  meta_def(:match, &eval(compiled_statement))
41
41
  end
42
-
42
+
43
43
  def generate(name, params = {}, fallback = {})
44
44
  raise "Named route not found: #{name}" unless @@named_routes.has_key? name
45
- @@named_routes[name].generate(params, fallback)
45
+ @@named_routes[name].generate(params, fallback).sub(/\/$/, '').squeeze("/")
46
46
  end
47
47
  end # self
48
-
48
+
49
49
  # Cache procs for future reference in eval statement
50
50
  class CachedProc
51
51
  @@index = 0
@@ -70,7 +70,7 @@ module Merb
70
70
  def [](index) @@list[index] end
71
71
  end
72
72
  end # CachedProc
73
-
73
+
74
74
  class Route
75
75
  attr_reader :conditions, :conditional_block
76
76
  attr_reader :params, :behavior, :segments, :index, :symbol
@@ -82,19 +82,31 @@ module Merb
82
82
  @segments = segments_from_path(path)
83
83
  end
84
84
  end
85
-
85
+
86
+ def to_s
87
+ r = ""
88
+ segments.each {|s|
89
+ if s.is_a?(Symbol)
90
+ r << ":#{s}"
91
+ else
92
+ r << s
93
+ end
94
+ }
95
+ r
96
+ end
97
+
86
98
  # Registers itself in the Router.routes array
87
99
  def register
88
100
  @index = Router.routes.size
89
101
  Router.routes << self
90
102
  self
91
103
  end
92
-
104
+
93
105
  # Get the symbols out of the segments array
94
106
  def symbol_segments
95
107
  segments.select{ |s| s.is_a?(Symbol) }
96
108
  end
97
-
109
+
98
110
  # Turn a path into string and symbol segments so it can be reconstructed, as in the
99
111
  # case of a named route.
100
112
  def segments_from_path(path)
@@ -109,17 +121,17 @@ module Merb
109
121
  segments << strip[path] unless path.empty?
110
122
  segments
111
123
  end
112
-
124
+
113
125
  # Name this route
114
126
  def name(symbol = nil)
115
127
  @symbol = symbol
116
128
  Router.named_routes[@symbol] = self
117
129
  end
118
-
130
+
119
131
  def regexp?
120
132
  behavior.regexp? || behavior.send(:ancestors).any?{ |a| a.regexp? }
121
133
  end
122
-
134
+
123
135
  # Given a hash of +params+, returns a string using the stored route segments
124
136
  # for reconstruction of the URL.
125
137
  def generate(params = {}, fallback = {})
@@ -143,7 +155,7 @@ module Merb
143
155
  (value.respond_to?(:to_param) ? value.to_param : value).to_s
144
156
  end.join
145
157
  end
146
-
158
+
147
159
  def if_conditions(params_as_string)
148
160
  cond = []
149
161
  condition_string = proc do |key, value, regexp_string|
@@ -157,7 +169,7 @@ module Merb
157
169
  " (#{value.inspect} =~ #{regexp_string}" + captures + ")"
158
170
  end
159
171
  @conditions.each_pair do |key, value|
160
-
172
+
161
173
  # Note: =~ is slightly faster than .match
162
174
  cond << case key
163
175
  when :path then condition_string[key, value, "cached_path"]
@@ -194,7 +206,7 @@ module Merb
194
206
  code << " [#{@index.inspect}, {#{params_as_string}}]\n"
195
207
  end
196
208
  end
197
-
209
+
198
210
  def behavior_trace
199
211
  if @behavior
200
212
  puts @behavior.send(:ancestors).reverse.map{|a| a.inspect}.join("\n"); puts @behavior.inspect; puts
@@ -203,7 +215,7 @@ module Merb
203
215
  end
204
216
  end
205
217
  end # Route
206
-
218
+
207
219
  # The Behavior class is an interim route-building class that ties pattern-matching +conditions+ to
208
220
  # output parameters, +params+.
209
221
  class Behavior
@@ -223,7 +235,7 @@ module Merb
223
235
  def add(path, params = {})
224
236
  match(path).to(params)
225
237
  end
226
-
238
+
227
239
  # Matches a +path+ and any number of optional request methods as conditions of a route.
228
240
  # Alternatively, +path+ can be a hash of conditions, in which case +conditions+ is ignored.
229
241
  # Yields 'self' so that sub-matching may occur.
@@ -235,7 +247,7 @@ module Merb
235
247
  end
236
248
  match_without_path(conditions, &block)
237
249
  end
238
-
250
+
239
251
  def match_without_path(conditions = {})
240
252
  new_behavior = self.class.new(conditions, {}, self)
241
253
  conditions.delete :path if ['', '^$'].include?(conditions[:path])
@@ -247,7 +259,7 @@ module Merb
247
259
  @params.merge!(params)
248
260
  Route.new(compiled_conditions, compiled_params, self, &conditional_block)
249
261
  end
250
-
262
+
251
263
  # Creates a Route from one or more Behavior objects, unless a +block+ is passed in.
252
264
  # If a block is passed in, a Behavior object is yielded and further .to operations
253
265
  # may be called in the block. For example:
@@ -266,7 +278,7 @@ module Merb
266
278
  to_route(params).register
267
279
  end
268
280
  end
269
-
281
+
270
282
  # Takes a block and stores it for defered conditional routes. The block takes the
271
283
  # +request+ object and the +params+ hash as parameters and should return a hash of params.
272
284
  # For example:
@@ -281,7 +293,7 @@ module Merb
281
293
  def default_routes(params = {}, &block)
282
294
  match(%r[/:controller(/:action(/:id)?)?(\.:format)?]).to(params, &block)
283
295
  end
284
-
296
+
285
297
  def to_resources(params = {}, &block)
286
298
  many_behaviors_to(resources_behaviors, params, &block)
287
299
  end
@@ -293,34 +305,47 @@ module Merb
293
305
 
294
306
  singular = name.to_s.singularize
295
307
 
296
- route_plural_name = (options[:name_prefix] || "") + name.to_s
297
- route_singular_name = (options[:name_prefix] || "") + singular
308
+ namespace = options[:namespace] || merged_params[:namespace]
309
+
310
+ if options[:name_prefix].nil? && namespace != nil
311
+ options[:name_prefix] = "#{namespace}_"
312
+ end
298
313
 
314
+ name_prefix = options.delete(:name_prefix)
315
+
316
+
317
+ route_plural_name = "#{name_prefix}#{name}"
318
+ route_singular_name = "#{name_prefix}#{singular}"
319
+
320
+ member = options.delete(:member)
321
+ collection = options.delete(:collection)
322
+
299
323
  # Add optional member actions
300
- options[:member].each_pair do |action, methods|
324
+
325
+ member.each_pair do |action, methods|
301
326
  conditions = {:path => %r[^/:id[/;]#{action}$], :method => /^(#{[methods].flatten.join("|")})$/}
302
327
  behaviors << Behavior.new(conditions, {:action => action.to_s}, next_level)
303
328
  next_level.match("/:id/#{action}").
304
329
  to_route.name(:"#{action}_#{route_singular_name}")
305
- end if options[:member]
306
-
330
+ end if member
331
+
307
332
  # Add optional collection actions
308
- options[:collection].each_pair do |action, methods|
333
+ collection.each_pair do |action, methods|
309
334
  conditions = {:path => %r[^[/;]#{action}$], :method => /^(#{[methods].flatten.join("|")})$/}
310
335
  behaviors << Behavior.new(conditions, {:action => action.to_s}, next_level)
311
336
  next_level.match("/#{action}").to_route.name(:"#{action}_#{route_plural_name}")
312
- end if options[:collection]
337
+ end if collection
338
+
339
+ options[:controller] ||= merged_params[:controller] || name.to_s
340
+ routes = many_behaviors_to(behaviors + next_level.send(:resources_behaviors), options)
313
341
 
314
- controller = options[:controller] || merged_params[:controller] || name.to_s
315
- routes = many_behaviors_to(behaviors + next_level.send(:resources_behaviors), :controller => controller)
316
-
317
342
  # Add names to some routes
318
343
  next_level.match("").to_route.name(:"#{route_plural_name}")
319
344
  next_level.match("/:id").to_route.name(:"#{route_singular_name}")
320
345
  next_level.match("/new").to_route.name(:"new_#{route_singular_name}")
321
346
  next_level.match("/:id/edit").to_route.name(:"edit_#{route_singular_name}")
322
347
  next_level.match("/:action/:id").to_route.name(:"custom_#{route_singular_name}")
323
-
348
+
324
349
  yield next_level.match("/:#{singular}_id") if block_given?
325
350
  routes
326
351
  end
@@ -332,10 +357,17 @@ module Merb
332
357
  def resource(name, options = {})
333
358
  next_level = match("/#{name}")
334
359
 
335
- controller = options[:controller] || merged_params[:controller] || name.to_s
336
- routes = next_level.to_resource(:controller => controller)
360
+ options[:controller] ||= merged_params[:controller] || name.to_s
361
+ name_prefix = options.delete(:name_prefix)
362
+ routes = next_level.to_resource(options)
363
+
364
+ namespace = options[:namespace] || merged_params[:namespace]
365
+
366
+ if options[:name_prefix].nil? && namespace != nil
367
+ options[:name_prefix] = "#{namespace}_"
368
+ end
337
369
 
338
- route_name = (options[:name_prefix] || "") + name.to_s
370
+ route_name = "#{name_prefix}#{name}"
339
371
 
340
372
  next_level.match("").to_route.name(:"#{route_name}")
341
373
  next_level.match("/new").to_route.name(:"new_#{route_name}")
@@ -356,7 +388,7 @@ module Merb
356
388
  merged_so_far.merge(@original_conditions)
357
389
  end
358
390
  end
359
-
391
+
360
392
  def merged_conditions
361
393
  if parent.nil?
362
394
  @conditions
@@ -387,11 +419,11 @@ module Merb
387
419
  end
388
420
  placeholders
389
421
  end
390
-
422
+
391
423
  def inspect
392
424
  "[captures: #{path_captures}, conditions: #{@original_conditions.inspect}, params: #{@params.inspect}, placeholders: #{@placeholders.inspect}]"
393
425
  end
394
-
426
+
395
427
  def regexp?
396
428
  @conditions_have_regexp
397
429
  end
@@ -409,7 +441,7 @@ module Merb
409
441
  Behavior.new({:path => %r[^/:id(\.:format)?$], :method => :delete}, {:action => "destroy"}, parent)
410
442
  ]
411
443
  end
412
-
444
+
413
445
  def resource_behaviors(parent = self)
414
446
  [
415
447
  Behavior.new({:path => %r{^[;/]new$}, :method => :get}, {:action => "new"}, parent),
@@ -452,7 +484,7 @@ module Merb
452
484
  end
453
485
  @original_conditions
454
486
  end
455
-
487
+
456
488
  def deduce_placeholders
457
489
  @conditions.each_pair do |match_key, source|
458
490
  while match = SEGMENT_REGEXP.match(source)
@@ -475,13 +507,13 @@ module Merb
475
507
  list
476
508
  end
477
509
  end
478
-
510
+
479
511
  # Count the number of regexp captures in the :path condition
480
512
  def path_captures
481
513
  return 0 unless conditions[:path]
482
514
  Behavior.count_parens_up_to(conditions[:path], conditions[:path].size)
483
515
  end
484
-
516
+
485
517
  def total_previous_captures
486
518
  ancestors.map{|a| a.path_captures}.inject(0){|sum, n| sum + n}
487
519
  end
@@ -571,6 +603,6 @@ module Merb
571
603
  code
572
604
  end
573
605
  end # Behavior
574
-
606
+
575
607
  end
576
608
  end