merb 0.4.1 → 0.4.2

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