actionpack 3.0.20 → 3.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -1,56 +1,17 @@
1
+ require 'action_dispatch/middleware/session/abstract_store'
2
+ require 'rack/session/memcache'
3
+
1
4
  module ActionDispatch
2
5
  module Session
3
- class MemCacheStore < AbstractStore
6
+ class MemCacheStore < Rack::Session::Memcache
7
+ include Compatibility
8
+ include StaleSessionCheck
9
+
4
10
  def initialize(app, options = {})
5
11
  require 'memcache'
6
-
7
- # Support old :expires option
8
12
  options[:expire_after] ||= options[:expires]
9
-
10
- super
11
-
12
- @default_options = {
13
- :namespace => 'rack:session',
14
- :memcache_server => 'localhost:11211'
15
- }.merge(@default_options)
16
-
17
- @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
18
- unless @pool.servers.any? { |s| s.alive? }
19
- raise "#{self} unable to find server during initialization."
20
- end
21
- @mutex = Mutex.new
22
-
23
13
  super
24
14
  end
25
-
26
- private
27
- def get_session(env, sid)
28
- sid ||= generate_sid
29
- begin
30
- session = @pool.get(sid) || {}
31
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
32
- session = {}
33
- end
34
- [sid, session]
35
- end
36
-
37
- def set_session(env, sid, session_data)
38
- options = env['rack.session.options']
39
- expiry = options[:expire_after] || 0
40
- @pool.set(sid, session_data, expiry)
41
- sid
42
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
43
- false
44
- end
45
-
46
- def destroy(env)
47
- if sid = current_session_id(env)
48
- @pool.delete(sid)
49
- end
50
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
51
- false
52
- end
53
-
54
15
  end
55
16
  end
56
17
  end
@@ -50,7 +50,7 @@ module ActionDispatch
50
50
  # Only this middleware cares about RoutingError. So, let's just raise
51
51
  # it here.
52
52
  if headers['X-Cascade'] == 'pass'
53
- raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
53
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
54
54
  end
55
55
  rescue Exception => exception
56
56
  raise exception if env['action_dispatch.show_exceptions'] == false
@@ -62,6 +62,7 @@ module ActionDispatch
62
62
  private
63
63
  def render_exception(env, exception)
64
64
  log_error(exception)
65
+ exception = original_exception(exception)
65
66
 
66
67
  request = Request.new(env)
67
68
  if @consider_all_requests_local || request.local?
@@ -115,7 +116,7 @@ module ActionDispatch
115
116
  end
116
117
 
117
118
  def render(status, body)
118
- [status, {'Content-Type' => 'text/html', 'Content-Length' => body.bytesize.to_s}, [body]]
119
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
119
120
  end
120
121
 
121
122
  def public_path
@@ -154,5 +155,17 @@ module ActionDispatch
154
155
  def logger
155
156
  defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
156
157
  end
158
+
159
+ def original_exception(exception)
160
+ if registered_original_exception?(exception)
161
+ exception.original_exception
162
+ else
163
+ exception
164
+ end
165
+ end
166
+
167
+ def registered_original_exception?(exception)
168
+ exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
169
+ end
157
170
  end
158
171
  end
@@ -1,17 +1,27 @@
1
1
  require "active_support/inflector/methods"
2
+ require "active_support/dependencies"
2
3
 
3
4
  module ActionDispatch
4
- class MiddlewareStack < Array
5
+ class MiddlewareStack
5
6
  class Middleware
6
- attr_reader :args, :block
7
+ attr_reader :args, :block, :name, :classcache
7
8
 
8
9
  def initialize(klass_or_name, *args, &block)
9
- @ref = ActiveSupport::Dependencies::Reference.new(klass_or_name)
10
+ @klass = nil
11
+
12
+ if klass_or_name.respond_to?(:name)
13
+ @klass = klass_or_name
14
+ @name = @klass.name
15
+ else
16
+ @name = klass_or_name.to_s
17
+ end
18
+
19
+ @classcache = ActiveSupport::Dependencies::Reference
10
20
  @args, @block = args, block
11
21
  end
12
22
 
13
23
  def klass
14
- @ref.get
24
+ @klass || classcache[@name]
15
25
  end
16
26
 
17
27
  def ==(middleware)
@@ -21,7 +31,7 @@ module ActionDispatch
21
31
  when Class
22
32
  klass == middleware
23
33
  else
24
- normalize(@ref.name) == normalize(middleware)
34
+ normalize(@name) == normalize(middleware)
25
35
  end
26
36
  end
27
37
 
@@ -40,15 +50,39 @@ module ActionDispatch
40
50
  end
41
51
  end
42
52
 
43
- def initialize(*args, &block)
44
- super(*args)
45
- block.call(self) if block_given?
53
+ include Enumerable
54
+
55
+ attr_accessor :middlewares
56
+
57
+ def initialize(*args)
58
+ @middlewares = []
59
+ yield(self) if block_given?
60
+ end
61
+
62
+ def each
63
+ @middlewares.each { |x| yield x }
64
+ end
65
+
66
+ def size
67
+ middlewares.size
68
+ end
69
+
70
+ def last
71
+ middlewares.last
72
+ end
73
+
74
+ def [](i)
75
+ middlewares[i]
76
+ end
77
+
78
+ def initialize_copy(other)
79
+ self.middlewares = other.middlewares.dup
46
80
  end
47
81
 
48
82
  def insert(index, *args, &block)
49
83
  index = assert_index(index, :before)
50
84
  middleware = self.class::Middleware.new(*args, &block)
51
- super(index, middleware)
85
+ middlewares.insert(index, middleware)
52
86
  end
53
87
 
54
88
  alias_method :insert_before, :insert
@@ -63,26 +97,25 @@ module ActionDispatch
63
97
  delete(target)
64
98
  end
65
99
 
66
- def use(*args, &block)
67
- middleware = self.class::Middleware.new(*args, &block)
68
- push(middleware)
100
+ def delete(target)
101
+ middlewares.delete target
69
102
  end
70
103
 
71
- def active
72
- ActiveSupport::Deprecation.warn "All middlewares in the chain are active since the laziness " <<
73
- "was removed from the middleware stack", caller
104
+ def use(*args, &block)
105
+ middleware = self.class::Middleware.new(*args, &block)
106
+ middlewares.push(middleware)
74
107
  end
75
108
 
76
109
  def build(app = nil, &block)
77
110
  app ||= block
78
111
  raise "MiddlewareStack#build requires an app" unless app
79
- reverse.inject(app) { |a, e| e.build(a) }
112
+ middlewares.reverse.inject(app) { |a, e| e.build(a) }
80
113
  end
81
114
 
82
115
  protected
83
116
 
84
117
  def assert_index(index, where)
85
- i = index.is_a?(Integer) ? index : self.index(index)
118
+ i = index.is_a?(Integer) ? index : middlewares.index(index)
86
119
  raise "No such middleware to insert #{where}: #{index.inspect}" unless i
87
120
  i
88
121
  end
@@ -1,44 +1,56 @@
1
1
  require 'rack/utils'
2
2
 
3
3
  module ActionDispatch
4
- class Static
5
- FILE_METHODS = %w(GET HEAD).freeze
4
+ class FileHandler
5
+ def initialize(root, cache_control)
6
+ @root = root.chomp('/')
7
+ @compiled_root = /^#{Regexp.escape(root)}/
8
+ @file_server = ::Rack::File.new(@root, cache_control)
9
+ end
6
10
 
7
- def initialize(app, root)
8
- @app = app
9
- @file_server = ::Rack::File.new(root)
11
+ def match?(path)
12
+ path = path.dup
13
+
14
+ full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
15
+ paths = "#{full_path}#{ext}"
16
+
17
+ matches = Dir[paths]
18
+ match = matches.detect { |m| File.file?(m) }
19
+ if match
20
+ match.sub!(@compiled_root, '')
21
+ match
22
+ end
10
23
  end
11
24
 
12
25
  def call(env)
13
- path = env['PATH_INFO'].chomp('/')
14
- method = env['REQUEST_METHOD']
15
-
16
- if FILE_METHODS.include?(method)
17
- if file_exist?(path)
18
- return @file_server.call(env)
19
- else
20
- cached_path = directory_exist?(path) ? "#{path}/index" : path
21
- cached_path += ::ActionController::Base.page_cache_extension
22
-
23
- if file_exist?(cached_path)
24
- env['PATH_INFO'] = cached_path
25
- return @file_server.call(env)
26
- end
27
- end
26
+ @file_server.call(env)
27
+ end
28
+
29
+ def ext
30
+ @ext ||= begin
31
+ ext = ::ActionController::Base.page_cache_extension
32
+ "{,#{ext},/index#{ext}}"
28
33
  end
34
+ end
35
+ end
29
36
 
30
- @app.call(env)
37
+ class Static
38
+ def initialize(app, path, cache_control=nil)
39
+ @app = app
40
+ @file_handler = FileHandler.new(path, cache_control)
31
41
  end
32
42
 
33
- private
34
- def file_exist?(path)
35
- full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path))
36
- File.file?(full_path) && File.readable?(full_path)
43
+ def call(env)
44
+ case env['REQUEST_METHOD']
45
+ when 'GET', 'HEAD'
46
+ path = env['PATH_INFO'].chomp('/')
47
+ if match = @file_handler.match?(path)
48
+ env["PATH_INFO"] = match
49
+ return @file_handler.call(env)
50
+ end
37
51
  end
38
52
 
39
- def directory_exist?(path)
40
- full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path))
41
- File.directory?(full_path) && File.readable?(full_path)
42
- end
53
+ @app.call(env)
54
+ end
43
55
  end
44
56
  end
@@ -14,7 +14,7 @@
14
14
 
15
15
  def debug_hash(hash)
16
16
  hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
17
- end
17
+ end unless self.class.method_defined?(:debug_hash)
18
18
  %>
19
19
 
20
20
  <h2 style="margin-top: 30px">Request</h2>
@@ -24,8 +24,8 @@
24
24
  <div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
25
25
 
26
26
  <p><a href="#" onclick="document.getElementById('env_dump').style.display='block'; return false;">Show env dump</a></p>
27
- <div id="env_dump" style="display:none"><pre><%= debug_hash @request.env %></pre></div>
27
+ <div id="env_dump" style="display:none"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
28
28
 
29
29
 
30
30
  <h2 style="margin-top: 30px">Response</h2>
31
- <p><b>Headers</b>: <pre><%=h @response ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>
31
+ <p><b>Headers</b>: <pre><%=h defined?(@response) ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>
@@ -12,14 +12,14 @@
12
12
  <div id="traces">
13
13
  <% names.each do |name| %>
14
14
  <%
15
- show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
16
- hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
15
+ show = "document.getElementById('#{name.gsub(/\s/, '-')}').style.display='block';"
16
+ hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub(/\s/, '-')}').style.display='none';"}
17
17
  %>
18
18
  <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
19
19
  <% end %>
20
20
 
21
21
  <% traces.each do |name, trace| %>
22
- <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
22
+ <div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name == "Application Trace") ? 'block' : 'none' %>;">
23
23
  <pre><code><%=h trace.join "\n" %></code></pre>
24
24
  </div>
25
25
  <% end %>
@@ -1,10 +1,10 @@
1
1
  <h1>
2
2
  <%=h @exception.class.to_s %>
3
3
  <% if @request.parameters['controller'] %>
4
- in <%=h @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
4
+ in <%=h @request.parameters['controller'].classify.pluralize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
5
5
  <% end %>
6
6
  </h1>
7
7
  <pre><%=h @exception.message %></pre>
8
8
 
9
- <%= render :file => "rescues/_trace.erb" %>
10
- <%= render :file => "rescues/_request_and_response.erb" %>
9
+ <%= render :template => "rescues/_trace" %>
10
+ <%= render :template => "rescues/_request_and_response" %>
@@ -1,11 +1,13 @@
1
- <html xmlns="http://www.w3.org/1999/xhtml">
1
+ <!DOCTYPE html>
2
+ <html lang="en">
2
3
  <head>
4
+ <meta charset="utf-8" />
3
5
  <title>Action Controller: Exception caught</title>
4
6
  <style>
5
7
  body { background-color: #fff; color: #333; }
6
8
 
7
9
  body, p, ol, ul, td {
8
- font-family: verdana, arial, helvetica, sans-serif;
10
+ font-family: helvetica, verdana, arial, sans-serif;
9
11
  font-size: 13px;
10
12
  line-height: 18px;
11
13
  }
@@ -13,9 +13,5 @@
13
13
 
14
14
  <p><%=h @exception.sub_template_message %></p>
15
15
 
16
- <% @real_exception = @exception
17
- @exception = @exception.original_exception || @exception %>
18
- <%= render :file => "rescues/_trace.erb" %>
19
- <% @exception = @real_exception %>
20
-
21
- <%= render :file => "rescues/_request_and_response.erb" %>
16
+ <%= render :template => "rescues/_trace" %>
17
+ <%= render :template => "rescues/_request_and_response" %>
@@ -8,5 +8,13 @@ module ActionDispatch
8
8
  config.action_dispatch.ip_spoofing_check = true
9
9
  config.action_dispatch.show_exceptions = true
10
10
  config.action_dispatch.best_standards_support = true
11
+ config.action_dispatch.tld_length = 1
12
+ config.action_dispatch.ignore_accept_header = false
13
+ config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true}
14
+
15
+ initializer "action_dispatch.configure" do |app|
16
+ ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
17
+ ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
18
+ end
11
19
  end
12
20
  end
@@ -56,6 +56,18 @@ module ActionDispatch
56
56
  # resources :posts, :comments
57
57
  # end
58
58
  #
59
+ # Alternately, you can add prefixes to your path without using a separate
60
+ # directory by using +scope+. +scope+ takes additional options which
61
+ # apply to all enclosed routes.
62
+ #
63
+ # scope :path => "/cpanel", :as => 'admin' do
64
+ # resources :posts, :comments
65
+ # end
66
+ #
67
+ # For more, see <tt>Routing::Mapper::Resources#resources</tt>,
68
+ # <tt>Routing::Mapper::Scoping#namespace</tt>, and
69
+ # <tt>Routing::Mapper::Scoping#scope</tt>.
70
+ #
59
71
  # == Named routes
60
72
  #
61
73
  # Routes can be named by passing an <tt>:as</tt> option,
@@ -264,10 +276,10 @@ module ActionDispatch
264
276
  # Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
265
277
  #
266
278
  module Routing
267
- autoload :DeprecatedMapper, 'action_dispatch/routing/deprecated_mapper'
268
279
  autoload :Mapper, 'action_dispatch/routing/mapper'
269
280
  autoload :Route, 'action_dispatch/routing/route'
270
281
  autoload :RouteSet, 'action_dispatch/routing/route_set'
282
+ autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
271
283
  autoload :UrlFor, 'action_dispatch/routing/url_for'
272
284
  autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
273
285
 
@@ -1,7 +1,9 @@
1
1
  require 'erb'
2
2
  require 'active_support/core_ext/hash/except'
3
3
  require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/object/inclusion'
4
5
  require 'active_support/inflector'
6
+ require 'action_dispatch/routing/redirection'
5
7
 
6
8
  module ActionDispatch
7
9
  module Routing
@@ -103,10 +105,16 @@ module ActionDispatch
103
105
  @options.reverse_merge!(:controller => /.+?/)
104
106
  end
105
107
 
108
+ # Add a constraint for wildcard route to make it non-greedy and match the
109
+ # optional format part of the route by default
110
+ if path.match(/\*([^\/]+)$/) && @options[:format] != false
111
+ @options.reverse_merge!(:"#{$1}" => /.+?/)
112
+ end
113
+
106
114
  if @options[:format] == false
107
115
  @options.delete(:format)
108
116
  path
109
- elsif path.include?(":format") || path.match(/\*[^\/]+$/)
117
+ elsif path.include?(":format") || path.end_with?('/')
110
118
  path
111
119
  else
112
120
  "#{path}(.:format)"
@@ -171,21 +179,21 @@ module ActionDispatch
171
179
  raise ArgumentError, "missing :action"
172
180
  end
173
181
 
174
- { :controller => controller, :action => action }.tap do |hash|
175
- hash.delete(:controller) if hash[:controller].blank?
176
- hash.delete(:action) if hash[:action].blank?
177
- end
182
+ hash = {}
183
+ hash[:controller] = controller unless controller.blank?
184
+ hash[:action] = action unless action.blank?
185
+ hash
178
186
  end
179
187
  end
180
188
 
181
189
  def blocks
190
+ block = @scope[:blocks] || []
191
+
182
192
  if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
183
- block = @options[:constraints]
184
- else
185
- block = nil
193
+ block << @options[:constraints]
186
194
  end
187
195
 
188
- ((@scope[:blocks] || []) + [ block ]).compact
196
+ block
189
197
  end
190
198
 
191
199
  def constraints
@@ -194,8 +202,8 @@ module ActionDispatch
194
202
 
195
203
  def request_method_condition
196
204
  if via = @options[:via]
197
- via = Array(via).map { |m| m.to_s.dasherize.upcase }
198
- { :request_method => %r[^#{via.join('|')}$] }
205
+ list = Array(via).map { |m| m.to_s.dasherize.upcase }
206
+ { :request_method => list }
199
207
  else
200
208
  { }
201
209
  end
@@ -242,15 +250,11 @@ module ActionDispatch
242
250
  end
243
251
 
244
252
  module Base
245
- def initialize(set) #:nodoc:
246
- @set = set
247
- end
248
-
249
253
  # You can specify what Rails should route "/" to with the root method:
250
254
  #
251
255
  # root :to => 'pages#main'
252
256
  #
253
- # For options, see the +match+ method's documentation, as +root+ uses it internally.
257
+ # For options, see +match+, as +root+ uses it internally.
254
258
  #
255
259
  # You should put the root route at the top of <tt>config/routes.rb</tt>,
256
260
  # because this means it will be matched first. As this is the most popular route
@@ -259,18 +263,125 @@ module ActionDispatch
259
263
  match '/', options.reverse_merge(:as => :root)
260
264
  end
261
265
 
262
- # When you set up a regular route, you supply a series of symbols that
263
- # Rails maps to parts of an incoming HTTP request.
266
+ # Matches a url pattern to one or more routes. Any symbols in a pattern
267
+ # are interpreted as url query parameters and thus available as +params+
268
+ # in an action:
269
+ #
270
+ # # sets :controller, :action and :id in params
271
+ # match ':controller/:action/:id'
272
+ #
273
+ # Two of these symbols are special, +:controller+ maps to the controller
274
+ # and +:action+ to the controller's action. A pattern can also map
275
+ # wildcard segments (globs) to params:
276
+ #
277
+ # match 'songs/*category/:title' => 'songs#show'
278
+ #
279
+ # # 'songs/rock/classic/stairway-to-heaven' sets
280
+ # # params[:category] = 'rock/classic'
281
+ # # params[:title] = 'stairway-to-heaven'
282
+ #
283
+ # When a pattern points to an internal route, the route's +:action+ and
284
+ # +:controller+ should be set in options or hash shorthand. Examples:
285
+ #
286
+ # match 'photos/:id' => 'photos#show'
287
+ # match 'photos/:id', :to => 'photos#show'
288
+ # match 'photos/:id', :controller => 'photos', :action => 'show'
289
+ #
290
+ # A pattern can also point to a +Rack+ endpoint i.e. anything that
291
+ # responds to +call+:
292
+ #
293
+ # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" }
294
+ # match 'photos/:id' => PhotoRackApp
295
+ # # Yes, controller actions are just rack endpoints
296
+ # match 'photos/:id' => PhotosController.action(:show)
297
+ #
298
+ # === Options
299
+ #
300
+ # Any options not seen here are passed on as params with the url.
301
+ #
302
+ # [:controller]
303
+ # The route's controller.
304
+ #
305
+ # [:action]
306
+ # The route's action.
307
+ #
308
+ # [:path]
309
+ # The path prefix for the routes.
310
+ #
311
+ # [:module]
312
+ # The namespace for :controller.
313
+ #
314
+ # match 'path' => 'c#a', :module => 'sekret', :controller => 'posts'
315
+ # #=> Sekret::PostsController
316
+ #
317
+ # See <tt>Scoping#namespace</tt> for its scope equivalent.
318
+ #
319
+ # [:as]
320
+ # The name used to generate routing helpers.
321
+ #
322
+ # [:via]
323
+ # Allowed HTTP verb(s) for route.
324
+ #
325
+ # match 'path' => 'c#a', :via => :get
326
+ # match 'path' => 'c#a', :via => [:get, :post]
327
+ #
328
+ # [:to]
329
+ # Points to a +Rack+ endpoint. Can be an object that responds to
330
+ # +call+ or a string representing a controller's action.
331
+ #
332
+ # match 'path', :to => 'controller#action'
333
+ # match 'path', :to => lambda { [200, {}, "Success!"] }
334
+ # match 'path', :to => RackApp
335
+ #
336
+ # [:on]
337
+ # Shorthand for wrapping routes in a specific RESTful context. Valid
338
+ # values are :member, :collection, and :new. Only use within
339
+ # <tt>resource(s)</tt> block. For example:
340
+ #
341
+ # resource :bar do
342
+ # match 'foo' => 'c#a', :on => :member, :via => [:get, :post]
343
+ # end
344
+ #
345
+ # Is equivalent to:
346
+ #
347
+ # resource :bar do
348
+ # member do
349
+ # match 'foo' => 'c#a', :via => [:get, :post]
350
+ # end
351
+ # end
352
+ #
353
+ # [:constraints]
354
+ # Constrains parameters with a hash of regular expressions or an
355
+ # object that responds to #matches?
356
+ #
357
+ # match 'path/:id', :constraints => { :id => /[A-Z]\d{5}/ }
358
+ #
359
+ # class Blacklist
360
+ # def matches?(request) request.remote_ip == '1.2.3.4' end
361
+ # end
362
+ # match 'path' => 'c#a', :constraints => Blacklist.new
363
+ #
364
+ # See <tt>Scoping#constraints</tt> for more examples with its scope
365
+ # equivalent.
366
+ #
367
+ # [:defaults]
368
+ # Sets defaults for parameters
369
+ #
370
+ # # Sets params[:format] to 'jpg' by default
371
+ # match 'path' => 'c#a', :defaults => { :format => 'jpg' }
372
+ #
373
+ # See <tt>Scoping#defaults</tt> for its scope equivalent.
264
374
  #
265
- # match ':controller/:action/:id/:user_id'
375
+ # [:anchor]
376
+ # Boolean to anchor a #match pattern. Default is true. When set to
377
+ # false, the pattern matches any request prefixed with the given path.
266
378
  #
267
- # Two of these symbols are special: :controller maps to the name of a
268
- # controller in your application, and :action maps to the name of an
269
- # action within that controller. Anything other than :controller or
270
- # :action will be available to the action as part of params.
379
+ # # Matches any request starting with 'path'
380
+ # match 'path' => 'c#a', :anchor => false
271
381
  def match(path, options=nil)
272
- mapping = Mapping.new(@set, @scope, path, options || {}).to_route
273
- @set.add_route(*mapping)
382
+ mapping = Mapping.new(@set, @scope, path, options || {})
383
+ app, conditions, requirements, defaults, as, anchor = mapping.to_route
384
+ @set.add_route(app, conditions, requirements, defaults, as, anchor)
274
385
  self
275
386
  end
276
387
 
@@ -282,6 +393,8 @@ module ActionDispatch
282
393
  #
283
394
  # mount(SomeRackApp => "some_route")
284
395
  #
396
+ # For options, see +match+, as +mount+ uses it internally.
397
+ #
285
398
  # All mounted applications come with routing helpers to access them.
286
399
  # These are named after the class specified, so for the above example
287
400
  # the helper is either +some_rack_app_path+ or +some_rack_app_url+.
@@ -302,7 +415,11 @@ module ActionDispatch
302
415
 
303
416
  raise "A rack application must be specified" unless path
304
417
 
418
+ options[:as] ||= app_name(app)
419
+
305
420
  match(path, options.merge(:to => app, :anchor => false, :format => false))
421
+
422
+ define_generate_prefix(app, options[:as])
306
423
  self
307
424
  end
308
425
 
@@ -310,11 +427,45 @@ module ActionDispatch
310
427
  @set.default_url_options = options
311
428
  end
312
429
  alias_method :default_url_options, :default_url_options=
430
+
431
+ def with_default_scope(scope, &block)
432
+ scope(scope) do
433
+ instance_exec(&block)
434
+ end
435
+ end
436
+
437
+ private
438
+ def app_name(app)
439
+ return unless app.respond_to?(:routes)
440
+
441
+ if app.respond_to?(:railtie_name)
442
+ app.railtie_name
443
+ else
444
+ class_name = app.class.is_a?(Class) ? app.name : app.class.name
445
+ ActiveSupport::Inflector.underscore(class_name).gsub("/", "_")
446
+ end
447
+ end
448
+
449
+ def define_generate_prefix(app, name)
450
+ return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
451
+
452
+ _route = @set.named_routes.routes[name.to_sym]
453
+ _routes = @set
454
+ app.routes.define_mounted_helper(name)
455
+ app.routes.class_eval do
456
+ define_method :_generate_prefix do |options|
457
+ prefix_options = options.slice(*_route.segment_keys)
458
+ # we must actually delete prefix segment keys to avoid passing them to next url_for
459
+ _route.segment_keys.each { |k| options.delete(k) }
460
+ _routes.url_helpers.send("#{name}_path", prefix_options)
461
+ end
462
+ end
463
+ end
313
464
  end
314
465
 
315
466
  module HttpHelpers
316
467
  # Define a route that only recognizes HTTP GET.
317
- # For supported arguments, see +match+.
468
+ # For supported arguments, see <tt>Base#match</tt>.
318
469
  #
319
470
  # Example:
320
471
  #
@@ -324,7 +475,7 @@ module ActionDispatch
324
475
  end
325
476
 
326
477
  # Define a route that only recognizes HTTP POST.
327
- # For supported arguments, see +match+.
478
+ # For supported arguments, see <tt>Base#match</tt>.
328
479
  #
329
480
  # Example:
330
481
  #
@@ -334,7 +485,7 @@ module ActionDispatch
334
485
  end
335
486
 
336
487
  # Define a route that only recognizes HTTP PUT.
337
- # For supported arguments, see +match+.
488
+ # For supported arguments, see <tt>Base#match</tt>.
338
489
  #
339
490
  # Example:
340
491
  #
@@ -344,7 +495,7 @@ module ActionDispatch
344
495
  end
345
496
 
346
497
  # Define a route that only recognizes HTTP PUT.
347
- # For supported arguments, see +match+.
498
+ # For supported arguments, see <tt>Base#match</tt>.
348
499
  #
349
500
  # Example:
350
501
  #
@@ -353,39 +504,6 @@ module ActionDispatch
353
504
  map_method(:delete, *args, &block)
354
505
  end
355
506
 
356
- # Redirect any path to another path:
357
- #
358
- # match "/stories" => redirect("/posts")
359
- def redirect(*args, &block)
360
- options = args.last.is_a?(Hash) ? args.pop : {}
361
-
362
- path = args.shift || block
363
- path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
364
- status = options[:status] || 301
365
-
366
- lambda do |env|
367
- req = Request.new(env)
368
-
369
- params = [req.symbolized_path_parameters]
370
- params << req if path_proc.arity > 1
371
-
372
- uri = URI.parse(path_proc.call(*params))
373
- uri.scheme ||= req.scheme
374
- uri.host ||= req.host
375
- uri.port ||= req.port unless req.standard_port?
376
-
377
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
378
-
379
- headers = {
380
- 'Location' => uri.to_s,
381
- 'Content-Type' => 'text/html',
382
- 'Content-Length' => body.length.to_s
383
- }
384
-
385
- [ status, headers, [body] ]
386
- end
387
- end
388
-
389
507
  private
390
508
  def map_method(method, *args, &block)
391
509
  options = args.extract_options!
@@ -405,58 +523,53 @@ module ActionDispatch
405
523
  # namespace "admin" do
406
524
  # resources :posts, :comments
407
525
  # end
408
- #
526
+ #
409
527
  # This will create a number of routes for each of the posts and comments
410
528
  # controller. For Admin::PostsController, Rails will create:
411
- #
412
- # GET /admin/photos
413
- # GET /admin/photos/new
414
- # POST /admin/photos
415
- # GET /admin/photos/1
416
- # GET /admin/photos/1/edit
417
- # PUT /admin/photos/1
418
- # DELETE /admin/photos/1
419
- #
420
- # If you want to route /photos (without the prefix /admin) to
529
+ #
530
+ # GET /admin/posts
531
+ # GET /admin/posts/new
532
+ # POST /admin/posts
533
+ # GET /admin/posts/1
534
+ # GET /admin/posts/1/edit
535
+ # PUT /admin/posts/1
536
+ # DELETE /admin/posts/1
537
+ #
538
+ # If you want to route /posts (without the prefix /admin) to
421
539
  # Admin::PostsController, you could use
422
- #
540
+ #
423
541
  # scope :module => "admin" do
424
- # resources :posts, :comments
542
+ # resources :posts
425
543
  # end
426
544
  #
427
545
  # or, for a single case
428
- #
546
+ #
429
547
  # resources :posts, :module => "admin"
430
- #
431
- # If you want to route /admin/photos to PostsController
548
+ #
549
+ # If you want to route /admin/posts to PostsController
432
550
  # (without the Admin:: module prefix), you could use
433
- #
551
+ #
434
552
  # scope "/admin" do
435
- # resources :posts, :comments
553
+ # resources :posts
436
554
  # end
437
555
  #
438
556
  # or, for a single case
439
- #
440
- # resources :posts, :path => "/admin/posts"
557
+ #
558
+ # resources :posts, :path => "/admin"
441
559
  #
442
560
  # In each of these cases, the named routes remain the same as if you did
443
561
  # not use scope. In the last case, the following paths map to
444
562
  # PostsController:
445
- #
446
- # GET /admin/photos
447
- # GET /admin/photos/new
448
- # POST /admin/photos
449
- # GET /admin/photos/1
450
- # GET /admin/photos/1/edit
451
- # PUT /admin/photos/1
452
- # DELETE /admin/photos/1
563
+ #
564
+ # GET /admin/posts
565
+ # GET /admin/posts/new
566
+ # POST /admin/posts
567
+ # GET /admin/posts/1
568
+ # GET /admin/posts/1/edit
569
+ # PUT /admin/posts/1
570
+ # DELETE /admin/posts/1
453
571
  module Scoping
454
- def initialize(*args) #:nodoc:
455
- @scope = {}
456
- super
457
- end
458
-
459
- # Used to scope a set of routes to particular constraints.
572
+ # Scopes a set of routes to the given default options.
460
573
  #
461
574
  # Take the following route definition as an example:
462
575
  #
@@ -468,60 +581,30 @@ module ActionDispatch
468
581
  # The difference here being that the routes generated are like /rails/projects/2,
469
582
  # rather than /accounts/rails/projects/2.
470
583
  #
471
- # === Supported options
472
- # [:module]
473
- # If you want to route /posts (without the prefix /admin) to
474
- # Admin::PostsController, you could use
475
- #
476
- # scope :module => "admin" do
477
- # resources :posts
478
- # end
479
- #
480
- # [:path]
481
- # If you want to prefix the route, you could use
482
- #
483
- # scope :path => "/admin" do
484
- # resources :posts
485
- # end
486
- #
487
- # This will prefix all of the +posts+ resource's requests with '/admin'
488
- #
489
- # [:as]
490
- # Prefixes the routing helpers in this scope with the specified label.
584
+ # === Options
491
585
  #
492
- # scope :as => "sekret" do
493
- # resources :posts
494
- # end
495
- #
496
- # Helpers such as +posts_path+ will now be +sekret_posts_path+
497
- #
498
- # [:shallow_path]
586
+ # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
499
587
  #
500
- # Prefixes nested shallow routes with the specified path.
588
+ # === Examples
501
589
  #
502
- # scope :shallow_path => "sekret" do
503
- # resources :posts do
504
- # resources :comments, :shallow => true
505
- # end
590
+ # # route /posts (without the prefix /admin) to Admin::PostsController
591
+ # scope :module => "admin" do
592
+ # resources :posts
593
+ # end
506
594
  #
507
- # The +comments+ resource here will have the following routes generated for it:
595
+ # # prefix the posts resource's requests with '/admin'
596
+ # scope :path => "/admin" do
597
+ # resources :posts
598
+ # end
508
599
  #
509
- # post_comments GET /sekret/posts/:post_id/comments(.:format)
510
- # post_comments POST /sekret/posts/:post_id/comments(.:format)
511
- # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
512
- # edit_comment GET /sekret/comments/:id/edit(.:format)
513
- # comment GET /sekret/comments/:id(.:format)
514
- # comment PUT /sekret/comments/:id(.:format)
515
- # comment DELETE /sekret/comments/:id(.:format)
600
+ # # prefix the routing helper name: sekret_posts_path instead of posts_path
601
+ # scope :as => "sekret" do
602
+ # resources :posts
603
+ # end
516
604
  def scope(*args)
517
605
  options = args.extract_options!
518
606
  options = options.dup
519
607
 
520
- if name_prefix = options.delete(:name_prefix)
521
- options[:as] ||= name_prefix
522
- ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller
523
- end
524
-
525
608
  options[:path] = args.first if args.first.is_a?(String)
526
609
  recover = {}
527
610
 
@@ -580,50 +663,38 @@ module ActionDispatch
580
663
  # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
581
664
  # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
582
665
  # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
583
- # === Supported options
584
666
  #
585
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace.
667
+ # === Options
586
668
  #
587
- # [:path]
588
- # The path prefix for the routes.
669
+ # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
670
+ # options all default to the name of the namespace.
589
671
  #
590
- # namespace :admin, :path => "sekret" do
591
- # resources :posts
592
- # end
672
+ # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
673
+ # <tt>Resources#resources</tt>.
593
674
  #
594
- # All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
675
+ # === Examples
595
676
  #
596
- # [:module]
597
- # The namespace for the controllers.
598
- #
599
- # namespace :admin, :module => "sekret" do
600
- # resources :posts
601
- # end
602
- #
603
- # The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this:
604
- #
605
- # class Sekret::PostsController < ApplicationController
606
- # # code go here
607
- # end
608
- #
609
- # [:as]
610
- # Changes the name used in routing helpers for this namespace.
611
- #
612
- # namespace :admin, :as => "sekret" do
613
- # resources :posts
614
- # end
677
+ # # accessible through /sekret/posts rather than /admin/posts
678
+ # namespace :admin, :path => "sekret" do
679
+ # resources :posts
680
+ # end
615
681
  #
616
- # Routing helpers such as +admin_posts_path+ will now be +sekret_posts_path+.
682
+ # # maps to Sekret::PostsController rather than Admin::PostsController
683
+ # namespace :admin, :module => "sekret" do
684
+ # resources :posts
685
+ # end
617
686
  #
618
- # [:shallow_path]
619
- # See the +scope+ method.
687
+ # # generates sekret_posts_path rather than admin_posts_path
688
+ # namespace :admin, :as => "sekret" do
689
+ # resources :posts
690
+ # end
620
691
  def namespace(path, options = {})
621
692
  path = path.to_s
622
693
  options = { :path => path, :as => path, :module => path,
623
694
  :shallow_path => path, :shallow_prefix => path }.merge!(options)
624
695
  scope(options) { yield }
625
696
  end
626
-
697
+
627
698
  # === Parameter Restriction
628
699
  # Allows you to constrain the nested routes based on a set of rules.
629
700
  # For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
@@ -634,8 +705,8 @@ module ActionDispatch
634
705
  #
635
706
  # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
636
707
  # The +id+ parameter must match the constraint passed in for this example.
637
- #
638
- # You may use this to also resrict other parameters:
708
+ #
709
+ # You may use this to also restrict other parameters:
639
710
  #
640
711
  # resources :posts do
641
712
  # constraints(:post_id => /\d+\.\d+) do
@@ -655,7 +726,7 @@ module ActionDispatch
655
726
  #
656
727
  # === Dynamic request matching
657
728
  #
658
- # Requests to routes can be constrained based on specific critera:
729
+ # Requests to routes can be constrained based on specific criteria:
659
730
  #
660
731
  # constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
661
732
  # resources :iphones
@@ -683,70 +754,70 @@ module ActionDispatch
683
754
  end
684
755
 
685
756
  # Allows you to set default parameters for a route, such as this:
686
- # defaults :id => 'home' do
687
- # match 'scoped_pages/(:id)', :to => 'pages#show'
688
- # end
757
+ # defaults :id => 'home' do
758
+ # match 'scoped_pages/(:id)', :to => 'pages#show'
759
+ # end
689
760
  # Using this, the +:id+ parameter here will default to 'home'.
690
761
  def defaults(defaults = {})
691
762
  scope(:defaults => defaults) { yield }
692
763
  end
693
764
 
694
765
  private
695
- def scope_options
766
+ def scope_options #:nodoc:
696
767
  @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
697
768
  end
698
769
 
699
- def merge_path_scope(parent, child)
770
+ def merge_path_scope(parent, child) #:nodoc:
700
771
  Mapper.normalize_path("#{parent}/#{child}")
701
772
  end
702
773
 
703
- def merge_shallow_path_scope(parent, child)
774
+ def merge_shallow_path_scope(parent, child) #:nodoc:
704
775
  Mapper.normalize_path("#{parent}/#{child}")
705
776
  end
706
777
 
707
- def merge_as_scope(parent, child)
778
+ def merge_as_scope(parent, child) #:nodoc:
708
779
  parent ? "#{parent}_#{child}" : child
709
780
  end
710
781
 
711
- def merge_shallow_prefix_scope(parent, child)
782
+ def merge_shallow_prefix_scope(parent, child) #:nodoc:
712
783
  parent ? "#{parent}_#{child}" : child
713
784
  end
714
785
 
715
- def merge_module_scope(parent, child)
786
+ def merge_module_scope(parent, child) #:nodoc:
716
787
  parent ? "#{parent}/#{child}" : child
717
788
  end
718
789
 
719
- def merge_controller_scope(parent, child)
790
+ def merge_controller_scope(parent, child) #:nodoc:
720
791
  child
721
792
  end
722
793
 
723
- def merge_path_names_scope(parent, child)
794
+ def merge_path_names_scope(parent, child) #:nodoc:
724
795
  merge_options_scope(parent, child)
725
796
  end
726
797
 
727
- def merge_constraints_scope(parent, child)
798
+ def merge_constraints_scope(parent, child) #:nodoc:
728
799
  merge_options_scope(parent, child)
729
800
  end
730
801
 
731
- def merge_defaults_scope(parent, child)
802
+ def merge_defaults_scope(parent, child) #:nodoc:
732
803
  merge_options_scope(parent, child)
733
804
  end
734
805
 
735
- def merge_blocks_scope(parent, child)
806
+ def merge_blocks_scope(parent, child) #:nodoc:
736
807
  merged = parent ? parent.dup : []
737
808
  merged << child if child
738
809
  merged
739
810
  end
740
811
 
741
- def merge_options_scope(parent, child)
812
+ def merge_options_scope(parent, child) #:nodoc:
742
813
  (parent || {}).except(*override_keys(child)).merge(child)
743
814
  end
744
815
 
745
- def merge_shallow_scope(parent, child)
816
+ def merge_shallow_scope(parent, child) #:nodoc:
746
817
  child ? true : false
747
818
  end
748
819
 
749
- def override_keys(child)
820
+ def override_keys(child) #:nodoc:
750
821
  child.key?(:only) || child.key?(:except) ? [:only, :except] : []
751
822
  end
752
823
  end
@@ -838,7 +909,8 @@ module ActionDispatch
838
909
 
839
910
  alias :member_name :singular
840
911
 
841
- # Checks for uncountable plurals, and appends "_index" if they're.
912
+ # Checks for uncountable plurals, and appends "_index" if the plural
913
+ # and singular form are the same.
842
914
  def collection_name
843
915
  singular == plural ? "#{plural}_index" : plural
844
916
  end
@@ -867,6 +939,7 @@ module ActionDispatch
867
939
  DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
868
940
 
869
941
  def initialize(entities, options)
942
+ @as = nil
870
943
  @name = entities.to_s
871
944
  @path = (options.delete(:path) || @name).to_s
872
945
  @controller = (options.delete(:controller) || plural).to_s
@@ -889,11 +962,6 @@ module ActionDispatch
889
962
  alias :nested_scope :path
890
963
  end
891
964
 
892
- def initialize(*args) #:nodoc:
893
- super
894
- @scope[:path_names] = @set.resources_path_names
895
- end
896
-
897
965
  def resources_path_names(options)
898
966
  @scope[:path_names].merge!(options)
899
967
  end
@@ -916,6 +984,9 @@ module ActionDispatch
916
984
  # GET /geocoder/edit
917
985
  # PUT /geocoder
918
986
  # DELETE /geocoder
987
+ #
988
+ # === Options
989
+ # Takes same options as +resources+.
919
990
  def resource(*resources, &block)
920
991
  options = resources.extract_options!
921
992
 
@@ -977,7 +1048,9 @@ module ActionDispatch
977
1048
  # PUT /photos/:id/comments/:id
978
1049
  # DELETE /photos/:id/comments/:id
979
1050
  #
980
- # === Supported options
1051
+ # === Options
1052
+ # Takes same options as <tt>Base#match</tt> as well as:
1053
+ #
981
1054
  # [:path_names]
982
1055
  # Allows you to change the paths of the seven default actions.
983
1056
  # Paths not specified are not changed.
@@ -986,20 +1059,59 @@ module ActionDispatch
986
1059
  #
987
1060
  # The above example will now change /posts/new to /posts/brand_new
988
1061
  #
989
- # [:module]
990
- # Set the module where the controller can be found. Defaults to nothing.
1062
+ # [:only]
1063
+ # Only generate routes for the given actions.
991
1064
  #
992
- # resources :posts, :module => "admin"
1065
+ # resources :cows, :only => :show
1066
+ # resources :cows, :only => [:show, :index]
993
1067
  #
994
- # All requests to the posts resources will now go to +Admin::PostsController+.
1068
+ # [:except]
1069
+ # Generate all routes except for the given actions.
995
1070
  #
996
- # [:path]
1071
+ # resources :cows, :except => :show
1072
+ # resources :cows, :except => [:show, :index]
1073
+ #
1074
+ # [:shallow]
1075
+ # Generates shallow routes for nested resource(s). When placed on a parent resource,
1076
+ # generates shallow routes for all nested resources.
997
1077
  #
998
- # Set a path prefix for this resource.
1078
+ # resources :posts, :shallow => true do
1079
+ # resources :comments
1080
+ # end
999
1081
  #
1000
- # resources :posts, :path => "admin/posts"
1082
+ # Is the same as:
1001
1083
  #
1002
- # All actions for this resource will now be at +/admin/posts+.
1084
+ # resources :posts do
1085
+ # resources :comments
1086
+ # end
1087
+ # resources :comments
1088
+ #
1089
+ # [:shallow_path]
1090
+ # Prefixes nested shallow routes with the specified path.
1091
+ #
1092
+ # scope :shallow_path => "sekret" do
1093
+ # resources :posts do
1094
+ # resources :comments, :shallow => true
1095
+ # end
1096
+ # end
1097
+ #
1098
+ # The +comments+ resource here will have the following routes generated for it:
1099
+ #
1100
+ # post_comments GET /sekret/posts/:post_id/comments(.:format)
1101
+ # post_comments POST /sekret/posts/:post_id/comments(.:format)
1102
+ # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
1103
+ # edit_comment GET /sekret/comments/:id/edit(.:format)
1104
+ # comment GET /sekret/comments/:id(.:format)
1105
+ # comment PUT /sekret/comments/:id(.:format)
1106
+ # comment DELETE /sekret/comments/:id(.:format)
1107
+ #
1108
+ # === Examples
1109
+ #
1110
+ # # routes call Admin::PostsController
1111
+ # resources :posts, :module => "admin"
1112
+ #
1113
+ # # resource actions are at /admin/posts.
1114
+ # resources :posts, :path => "admin"
1003
1115
  def resources(*resources, &block)
1004
1116
  options = resources.extract_options!
1005
1117
 
@@ -1121,7 +1233,7 @@ module ActionDispatch
1121
1233
  end
1122
1234
 
1123
1235
  def shallow
1124
- scope(:shallow => true) do
1236
+ scope(:shallow => true, :shallow_path => @scope[:path]) do
1125
1237
  yield
1126
1238
  end
1127
1239
  end
@@ -1191,7 +1303,7 @@ module ActionDispatch
1191
1303
  @scope[:scope_level_resource]
1192
1304
  end
1193
1305
 
1194
- def apply_common_behavior_for(method, resources, options, &block)
1306
+ def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
1195
1307
  if resources.length > 1
1196
1308
  resources.each { |r| send(method, r, options, &block) }
1197
1309
  return true
@@ -1221,24 +1333,24 @@ module ActionDispatch
1221
1333
  false
1222
1334
  end
1223
1335
 
1224
- def action_options?(options)
1336
+ def action_options?(options) #:nodoc:
1225
1337
  options[:only] || options[:except]
1226
1338
  end
1227
1339
 
1228
- def scope_action_options?
1340
+ def scope_action_options? #:nodoc:
1229
1341
  @scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
1230
1342
  end
1231
1343
 
1232
- def scope_action_options
1344
+ def scope_action_options #:nodoc:
1233
1345
  @scope[:options].slice(:only, :except)
1234
1346
  end
1235
1347
 
1236
- def resource_scope?
1237
- [:resource, :resources].include?(@scope[:scope_level])
1348
+ def resource_scope? #:nodoc:
1349
+ @scope[:scope_level].in?([:resource, :resources])
1238
1350
  end
1239
1351
 
1240
- def resource_method_scope?
1241
- [:collection, :member, :new].include?(@scope[:scope_level])
1352
+ def resource_method_scope? #:nodoc:
1353
+ @scope[:scope_level].in?([:collection, :member, :new])
1242
1354
  end
1243
1355
 
1244
1356
  def with_exclusive_scope
@@ -1263,7 +1375,7 @@ module ActionDispatch
1263
1375
  @scope[:scope_level_resource] = old_resource
1264
1376
  end
1265
1377
 
1266
- def resource_scope(resource)
1378
+ def resource_scope(resource) #:nodoc:
1267
1379
  with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
1268
1380
  scope(parent_resource.resource_scope) do
1269
1381
  yield
@@ -1271,30 +1383,30 @@ module ActionDispatch
1271
1383
  end
1272
1384
  end
1273
1385
 
1274
- def nested_options
1386
+ def nested_options #:nodoc:
1275
1387
  {}.tap do |options|
1276
1388
  options[:as] = parent_resource.member_name
1277
1389
  options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
1278
1390
  end
1279
1391
  end
1280
1392
 
1281
- def id_constraint?
1393
+ def id_constraint? #:nodoc:
1282
1394
  @scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
1283
1395
  end
1284
1396
 
1285
- def id_constraint
1397
+ def id_constraint #:nodoc:
1286
1398
  @scope[:constraints][:id]
1287
1399
  end
1288
1400
 
1289
- def canonical_action?(action, flag)
1401
+ def canonical_action?(action, flag) #:nodoc:
1290
1402
  flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
1291
1403
  end
1292
1404
 
1293
- def shallow_scoping?
1405
+ def shallow_scoping? #:nodoc:
1294
1406
  shallow? && @scope[:scope_level] == :member
1295
1407
  end
1296
1408
 
1297
- def path_for_action(action, path)
1409
+ def path_for_action(action, path) #:nodoc:
1298
1410
  prefix = shallow_scoping? ?
1299
1411
  "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
1300
1412
 
@@ -1305,11 +1417,11 @@ module ActionDispatch
1305
1417
  end
1306
1418
  end
1307
1419
 
1308
- def action_path(name, path = nil)
1420
+ def action_path(name, path = nil) #:nodoc:
1309
1421
  path || @scope[:path_names][name.to_sym] || name.to_s
1310
1422
  end
1311
1423
 
1312
- def prefix_name_for_action(as, action)
1424
+ def prefix_name_for_action(as, action) #:nodoc:
1313
1425
  if as
1314
1426
  as.to_s
1315
1427
  elsif !canonical_action?(action, @scope[:scope_level])
@@ -1317,7 +1429,7 @@ module ActionDispatch
1317
1429
  end
1318
1430
  end
1319
1431
 
1320
- def name_for_action(as, action)
1432
+ def name_for_action(as, action) #:nodoc:
1321
1433
  prefix = prefix_name_for_action(as, action)
1322
1434
  prefix = Mapper.normalize_name(prefix) if prefix
1323
1435
  name_prefix = @scope[:as]
@@ -1349,7 +1461,7 @@ module ActionDispatch
1349
1461
  end
1350
1462
  end
1351
1463
 
1352
- module Shorthand
1464
+ module Shorthand #:nodoc:
1353
1465
  def match(*args)
1354
1466
  if args.size == 1 && args.last.is_a?(Hash)
1355
1467
  options = args.pop
@@ -1362,8 +1474,14 @@ module ActionDispatch
1362
1474
  end
1363
1475
  end
1364
1476
 
1477
+ def initialize(set) #:nodoc:
1478
+ @set = set
1479
+ @scope = { :path_names => @set.resources_path_names }
1480
+ end
1481
+
1365
1482
  include Base
1366
1483
  include HttpHelpers
1484
+ include Redirection
1367
1485
  include Scoping
1368
1486
  include Resources
1369
1487
  include Shorthand