actionpack 4.2.11.3 → 5.0.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 (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +379 -462
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller.rb +0 -2
  6. data/lib/abstract_controller/base.rb +17 -32
  7. data/lib/abstract_controller/callbacks.rb +52 -19
  8. data/lib/abstract_controller/collector.rb +4 -9
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  11. data/lib/abstract_controller/rendering.rb +27 -22
  12. data/lib/abstract_controller/translation.rb +8 -7
  13. data/lib/action_controller.rb +4 -3
  14. data/lib/action_controller/api.rb +146 -0
  15. data/lib/action_controller/base.rb +6 -10
  16. data/lib/action_controller/caching.rb +1 -3
  17. data/lib/action_controller/caching/fragments.rb +48 -3
  18. data/lib/action_controller/form_builder.rb +48 -0
  19. data/lib/action_controller/log_subscriber.rb +1 -10
  20. data/lib/action_controller/metal.rb +89 -62
  21. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  22. data/lib/action_controller/metal/conditional_get.rb +65 -24
  23. data/lib/action_controller/metal/cookies.rb +0 -2
  24. data/lib/action_controller/metal/data_streaming.rb +2 -22
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  26. data/lib/action_controller/metal/exceptions.rb +11 -6
  27. data/lib/action_controller/metal/force_ssl.rb +6 -6
  28. data/lib/action_controller/metal/head.rb +14 -7
  29. data/lib/action_controller/metal/helpers.rb +9 -5
  30. data/lib/action_controller/metal/http_authentication.rb +37 -38
  31. data/lib/action_controller/metal/implicit_render.rb +23 -6
  32. data/lib/action_controller/metal/instrumentation.rb +0 -1
  33. data/lib/action_controller/metal/live.rb +17 -55
  34. data/lib/action_controller/metal/mime_responds.rb +17 -37
  35. data/lib/action_controller/metal/params_wrapper.rb +8 -8
  36. data/lib/action_controller/metal/redirecting.rb +32 -9
  37. data/lib/action_controller/metal/renderers.rb +10 -8
  38. data/lib/action_controller/metal/rendering.rb +38 -6
  39. data/lib/action_controller/metal/request_forgery_protection.rb +67 -35
  40. data/lib/action_controller/metal/rescue.rb +2 -4
  41. data/lib/action_controller/metal/streaming.rb +4 -4
  42. data/lib/action_controller/metal/strong_parameters.rb +231 -78
  43. data/lib/action_controller/metal/testing.rb +1 -12
  44. data/lib/action_controller/metal/url_for.rb +12 -5
  45. data/lib/action_controller/renderer.rb +111 -0
  46. data/lib/action_controller/template_assertions.rb +9 -0
  47. data/lib/action_controller/test_case.rb +267 -363
  48. data/lib/action_dispatch.rb +2 -1
  49. data/lib/action_dispatch/http/cache.rb +23 -26
  50. data/lib/action_dispatch/http/filter_parameters.rb +6 -8
  51. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  52. data/lib/action_dispatch/http/headers.rb +28 -11
  53. data/lib/action_dispatch/http/mime_negotiation.rb +40 -26
  54. data/lib/action_dispatch/http/mime_type.rb +92 -61
  55. data/lib/action_dispatch/http/mime_types.rb +1 -4
  56. data/lib/action_dispatch/http/parameter_filter.rb +18 -8
  57. data/lib/action_dispatch/http/parameters.rb +45 -41
  58. data/lib/action_dispatch/http/request.rb +146 -82
  59. data/lib/action_dispatch/http/response.rb +180 -99
  60. data/lib/action_dispatch/http/url.rb +117 -8
  61. data/lib/action_dispatch/journey/formatter.rb +34 -28
  62. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  64. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  65. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  66. data/lib/action_dispatch/journey/parser_extras.rb +4 -0
  67. data/lib/action_dispatch/journey/path/pattern.rb +37 -41
  68. data/lib/action_dispatch/journey/route.rb +71 -17
  69. data/lib/action_dispatch/journey/router.rb +5 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  71. data/lib/action_dispatch/journey/routes.rb +14 -15
  72. data/lib/action_dispatch/journey/visitors.rb +86 -43
  73. data/lib/action_dispatch/middleware/cookies.rb +184 -135
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +115 -45
  75. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -20
  76. data/lib/action_dispatch/middleware/flash.rb +61 -45
  77. data/lib/action_dispatch/middleware/load_interlock.rb +21 -0
  78. data/lib/action_dispatch/middleware/params_parser.rb +30 -46
  79. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  80. data/lib/action_dispatch/middleware/reloader.rb +2 -4
  81. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  82. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  83. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  84. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  85. data/lib/action_dispatch/middleware/session/cookie_store.rb +29 -23
  86. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  87. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  88. data/lib/action_dispatch/middleware/ssl.rb +93 -36
  89. data/lib/action_dispatch/middleware/stack.rb +43 -48
  90. data/lib/action_dispatch/middleware/static.rb +52 -40
  91. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  92. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  98. data/lib/action_dispatch/railtie.rb +0 -2
  99. data/lib/action_dispatch/request/session.rb +66 -34
  100. data/lib/action_dispatch/request/utils.rb +51 -19
  101. data/lib/action_dispatch/routing.rb +3 -8
  102. data/lib/action_dispatch/routing/inspector.rb +6 -30
  103. data/lib/action_dispatch/routing/mapper.rb +447 -322
  104. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  105. data/lib/action_dispatch/routing/redirection.rb +3 -3
  106. data/lib/action_dispatch/routing/route_set.rb +124 -227
  107. data/lib/action_dispatch/routing/url_for.rb +27 -10
  108. data/lib/action_dispatch/testing/assertions.rb +1 -1
  109. data/lib/action_dispatch/testing/assertions/response.rb +27 -9
  110. data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
  111. data/lib/action_dispatch/testing/integration.rb +237 -76
  112. data/lib/action_dispatch/testing/test_process.rb +5 -5
  113. data/lib/action_dispatch/testing/test_request.rb +12 -21
  114. data/lib/action_dispatch/testing/test_response.rb +1 -4
  115. data/lib/action_pack.rb +1 -1
  116. data/lib/action_pack/gem_version.rb +4 -4
  117. metadata +26 -25
  118. data/lib/action_controller/metal/hide_actions.rb +0 -40
  119. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  120. data/lib/action_controller/middleware.rb +0 -39
  121. data/lib/action_controller/model_naming.rb +0 -12
  122. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  123. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  124. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  125. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -7,13 +7,15 @@ require 'action_controller/metal/strong_parameters'
7
7
  module ActionController
8
8
  extend ActiveSupport::Autoload
9
9
 
10
+ autoload :API
10
11
  autoload :Base
11
12
  autoload :Caching
12
13
  autoload :Metal
13
14
  autoload :Middleware
15
+ autoload :Renderer
16
+ autoload :FormBuilder
14
17
 
15
18
  autoload_under "metal" do
16
- autoload :Compatibility
17
19
  autoload :ConditionalGet
18
20
  autoload :Cookies
19
21
  autoload :DataStreaming
@@ -22,13 +24,12 @@ module ActionController
22
24
  autoload :ForceSSL
23
25
  autoload :Head
24
26
  autoload :Helpers
25
- autoload :HideActions
26
27
  autoload :HttpAuthentication
28
+ autoload :BasicImplicitRender
27
29
  autoload :ImplicitRender
28
30
  autoload :Instrumentation
29
31
  autoload :MimeResponds
30
32
  autoload :ParamsWrapper
31
- autoload :RackDelegation
32
33
  autoload :Redirecting
33
34
  autoload :Renderers
34
35
  autoload :Rendering
@@ -0,0 +1,146 @@
1
+ require 'action_view'
2
+ require 'action_controller'
3
+ require 'action_controller/log_subscriber'
4
+
5
+ module ActionController
6
+ # API Controller is a lightweight version of <tt>ActionController::Base</tt>,
7
+ # created for applications that don't require all functionalities that a complete
8
+ # \Rails controller provides, allowing you to create controllers with just the
9
+ # features that you need for API only applications.
10
+ #
11
+ # An API Controller is different from a normal controller in the sense that
12
+ # by default it doesn't include a number of features that are usually required
13
+ # by browser access only: layouts and templates rendering, cookies, sessions,
14
+ # flash, assets, and so on. This makes the entire controller stack thinner,
15
+ # suitable for API applications. It doesn't mean you won't have such
16
+ # features if you need them: they're all available for you to include in
17
+ # your application, they're just not part of the default API Controller stack.
18
+ #
19
+ # By default, only the ApplicationController in a \Rails application inherits
20
+ # from <tt>ActionController::API</tt>. All other controllers in turn inherit
21
+ # from ApplicationController.
22
+ #
23
+ # A sample controller could look like this:
24
+ #
25
+ # class PostsController < ApplicationController
26
+ # def index
27
+ # @posts = Post.all
28
+ # render json: @posts
29
+ # end
30
+ # end
31
+ #
32
+ # Request, response and parameters objects all work the exact same way as
33
+ # <tt>ActionController::Base</tt>.
34
+ #
35
+ # == Renders
36
+ #
37
+ # The default API Controller stack includes all renderers, which means you
38
+ # can use <tt>render :json</tt> and brothers freely in your controllers. Keep
39
+ # in mind that templates are not going to be rendered, so you need to ensure
40
+ # your controller is calling either <tt>render</tt> or <tt>redirect</tt> in
41
+ # all actions, otherwise it will return 204 No Content response.
42
+ #
43
+ # def show
44
+ # @post = Post.find(params[:id])
45
+ # render json: @post
46
+ # end
47
+ #
48
+ # == Redirects
49
+ #
50
+ # Redirects are used to move from one action to another. You can use the
51
+ # <tt>redirect</tt> method in your controllers in the same way as
52
+ # <tt>ActionController::Base</tt>. For example:
53
+ #
54
+ # def create
55
+ # redirect_to root_url and return if not_authorized?
56
+ # # do stuff here
57
+ # end
58
+ #
59
+ # == Adding new behavior
60
+ #
61
+ # In some scenarios you may want to add back some functionality provided by
62
+ # <tt>ActionController::Base</tt> that is not present by default in
63
+ # <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
64
+ # module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
65
+ # you just need to include the module in a specific controller or in
66
+ # +ApplicationController+ in case you want it available in your entire
67
+ # application:
68
+ #
69
+ # class ApplicationController < ActionController::API
70
+ # include ActionController::MimeResponds
71
+ # end
72
+ #
73
+ # class PostsController < ApplicationController
74
+ # def index
75
+ # @posts = Post.all
76
+ #
77
+ # respond_to do |format|
78
+ # format.json { render json: @posts }
79
+ # format.xml { render xml: @posts }
80
+ # end
81
+ # end
82
+ # end
83
+ #
84
+ # Quite straightforward. Make sure to check <tt>ActionController::Base</tt>
85
+ # available modules if you want to include any other functionality that is
86
+ # not provided by <tt>ActionController::API</tt> out of the box.
87
+ class API < Metal
88
+ abstract!
89
+
90
+ # Shortcut helper that returns all the ActionController::API modules except
91
+ # the ones passed as arguments:
92
+ #
93
+ # class MyAPIBaseController < ActionController::Metal
94
+ # ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
95
+ # include left
96
+ # end
97
+ # end
98
+ #
99
+ # This gives better control over what you want to exclude and makes it easier
100
+ # to create an API controller class, instead of listing the modules required
101
+ # manually.
102
+ def self.without_modules(*modules)
103
+ modules = modules.map do |m|
104
+ m.is_a?(Symbol) ? ActionController.const_get(m) : m
105
+ end
106
+
107
+ MODULES - modules
108
+ end
109
+
110
+ MODULES = [
111
+ AbstractController::Rendering,
112
+
113
+ UrlFor,
114
+ Redirecting,
115
+ Rendering,
116
+ Renderers::All,
117
+ ConditionalGet,
118
+ BasicImplicitRender,
119
+ StrongParameters,
120
+
121
+ ForceSSL,
122
+ DataStreaming,
123
+
124
+ # Before callbacks should also be executed as early as possible, so
125
+ # also include them at the bottom.
126
+ AbstractController::Callbacks,
127
+
128
+ # Append rescue at the bottom to wrap as much as possible.
129
+ Rescue,
130
+
131
+ # Add instrumentations hooks at the bottom, to ensure they instrument
132
+ # all the methods properly.
133
+ Instrumentation,
134
+
135
+ # Params wrapper should come before instrumentation so they are
136
+ # properly showed in logs
137
+ ParamsWrapper
138
+ ]
139
+
140
+ MODULES.each do |mod|
141
+ include mod
142
+ end
143
+
144
+ ActiveSupport.run_load_hooks(:action_controller, self)
145
+ end
146
+ end
@@ -50,9 +50,9 @@ module ActionController
50
50
  #
51
51
  # == Parameters
52
52
  #
53
- # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
54
- # which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
55
- # <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
53
+ # All request parameters, whether they come from a query string in the URL or form data submitted through a POST request are
54
+ # available through the params method which returns a hash. For example, an action that was performed through
55
+ # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
56
56
  #
57
57
  # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
58
58
  #
@@ -206,7 +206,6 @@ module ActionController
206
206
  AbstractController::AssetPaths,
207
207
 
208
208
  Helpers,
209
- HideActions,
210
209
  UrlFor,
211
210
  Redirecting,
212
211
  ActionView::Layouts,
@@ -214,7 +213,6 @@ module ActionController
214
213
  Renderers::All,
215
214
  ConditionalGet,
216
215
  EtagWithTemplateDigest,
217
- RackDelegation,
218
216
  Caching,
219
217
  MimeResponds,
220
218
  ImplicitRender,
@@ -222,6 +220,7 @@ module ActionController
222
220
 
223
221
  Cookies,
224
222
  Flash,
223
+ FormBuilder,
225
224
  RequestForgeryProtection,
226
225
  ForceSSL,
227
226
  Streaming,
@@ -249,20 +248,17 @@ module ActionController
249
248
  MODULES.each do |mod|
250
249
  include mod
251
250
  end
251
+ setup_renderer!
252
252
 
253
253
  # Define some internal variables that should not be propagated to the view.
254
254
  PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
255
- :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
255
+ :@_params, :@_response, :@_request,
256
256
  :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
257
257
 
258
258
  def _protected_ivars # :nodoc:
259
259
  PROTECTED_IVARS
260
260
  end
261
261
 
262
- def self.protected_instance_variables
263
- PROTECTED_IVARS
264
- end
265
-
266
262
  ActiveSupport.run_load_hooks(:action_controller, self)
267
263
  end
268
264
  end
@@ -1,6 +1,5 @@
1
1
  require 'fileutils'
2
2
  require 'uri'
3
- require 'set'
4
3
 
5
4
  module ActionController
6
5
  # \Caching is a cheap way of speeding up slow applications by keeping the result of
@@ -8,7 +7,7 @@ module ActionController
8
7
  #
9
8
  # You can read more about each approach by clicking the modules below.
10
9
  #
11
- # Note: To turn off all caching, set
10
+ # Note: To turn off all caching provided by Action Controller, set
12
11
  # config.action_controller.perform_caching = false
13
12
  #
14
13
  # == \Caching stores
@@ -46,7 +45,6 @@ module ActionController
46
45
  end
47
46
  end
48
47
 
49
- include RackDelegation
50
48
  include AbstractController::Callbacks
51
49
 
52
50
  include ConfigMethods
@@ -14,12 +14,57 @@ module ActionController
14
14
  #
15
15
  # expire_fragment('name_of_cache')
16
16
  module Fragments
17
+ extend ActiveSupport::Concern
18
+
19
+ included do
20
+ if respond_to?(:class_attribute)
21
+ class_attribute :fragment_cache_keys
22
+ else
23
+ mattr_writer :fragment_cache_keys
24
+ end
25
+
26
+ self.fragment_cache_keys = []
27
+
28
+ helper_method :fragment_cache_key if respond_to?(:helper_method)
29
+ end
30
+
31
+ module ClassMethods
32
+ # Allows you to specify controller-wide key prefixes for
33
+ # cache fragments. Pass either a constant +value+, or a block
34
+ # which computes a value each time a cache key is generated.
35
+ #
36
+ # For example, you may want to prefix all fragment cache keys
37
+ # with a global version identifier, so you can easily
38
+ # invalidate all caches.
39
+ #
40
+ # class ApplicationController
41
+ # fragment_cache_key "v1"
42
+ # end
43
+ #
44
+ # When it's time to invalidate all fragments, simply change
45
+ # the string constant. Or, progressively roll out the cache
46
+ # invalidation using a computed value:
47
+ #
48
+ # class ApplicationController
49
+ # fragment_cache_key do
50
+ # @account.id.odd? ? "v1" : "v2"
51
+ # end
52
+ # end
53
+ def fragment_cache_key(value = nil, &key)
54
+ self.fragment_cache_keys += [key || ->{ value }]
55
+ end
56
+ end
57
+
17
58
  # Given a key (as described in +expire_fragment+), returns
18
59
  # a key suitable for use in reading, writing, or expiring a
19
- # cached fragment. All keys are prefixed with <tt>views/</tt> and uses
20
- # ActiveSupport::Cache.expand_cache_key for the expansion.
60
+ # cached fragment. All keys begin with <tt>views/</tt>,
61
+ # followed by any controller-wide key prefix values, ending
62
+ # with the specified +key+ value. The key is expanded using
63
+ # ActiveSupport::Cache.expand_cache_key.
21
64
  def fragment_cache_key(key)
22
- ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
65
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
66
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
67
+ ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
23
68
  end
24
69
 
25
70
  # Writes +content+ to the location signified by
@@ -0,0 +1,48 @@
1
+ module ActionController
2
+ # Override the default form builder for all views rendered by this
3
+ # controller and any of its descendants. Accepts a subclass of
4
+ # +ActionView::Helpers::FormBuilder+.
5
+ #
6
+ # For example, given a form builder:
7
+ #
8
+ # class AdminFormBuilder < ActionView::Helpers::FormBuilder
9
+ # def special_field(name)
10
+ # end
11
+ # end
12
+ #
13
+ # The controller specifies a form builder as its default:
14
+ #
15
+ # class AdminAreaController < ApplicationController
16
+ # default_form_builder AdminFormBuilder
17
+ # end
18
+ #
19
+ # Then in the view any form using +form_for+ will be an instance of the
20
+ # specified form builder:
21
+ #
22
+ # <%= form_for(@instance) do |builder| %>
23
+ # <%= builder.special_field(:name) %>
24
+ # <% end %>
25
+ module FormBuilder
26
+ extend ActiveSupport::Concern
27
+
28
+ included do
29
+ class_attribute :_default_form_builder, instance_accessor: false
30
+ end
31
+
32
+ module ClassMethods
33
+ # Set the form builder to be used as the default for all forms
34
+ # in the views rendered by this controller and its subclasses.
35
+ #
36
+ # ==== Parameters
37
+ # * <tt>builder</tt> - Default form builder, an instance of +ActionView::Helpers::FormBuilder+
38
+ def default_form_builder(builder)
39
+ self._default_form_builder = builder
40
+ end
41
+ end
42
+
43
+ # Default form builder for the controller
44
+ def default_form_builder
45
+ self.class._default_form_builder
46
+ end
47
+ end
48
+ end
@@ -25,7 +25,7 @@ module ActionController
25
25
  status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
26
26
  end
27
27
  message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
28
- message << " (#{additions.join(" | ")})" unless additions.blank?
28
+ message << " (#{additions.join(" | ".freeze)})" unless additions.blank?
29
29
  message
30
30
  end
31
31
  end
@@ -53,15 +53,6 @@ module ActionController
53
53
  end
54
54
  end
55
55
 
56
- def deep_munge(event)
57
- debug do
58
- "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
59
- "to nil, because it was one of [], [null] or [null, null, ...]. "\
60
- "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
61
- "for more information."\
62
- end
63
- end
64
-
65
56
  %w(write_fragment read_fragment exist_fragment?
66
57
  expire_fragment expire_page write_page).each do |method|
67
58
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -1,5 +1,7 @@
1
1
  require 'active_support/core_ext/array/extract_options'
2
2
  require 'action_dispatch/middleware/stack'
3
+ require 'action_dispatch/http/request'
4
+ require 'action_dispatch/http/response'
3
5
 
4
6
  module ActionController
5
7
  # Extend ActionDispatch middleware stack to make it aware of options
@@ -11,22 +13,14 @@ module ActionController
11
13
  #
12
14
  class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
13
15
  class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
14
- def initialize(klass, *args, &block)
15
- options = args.extract_options!
16
- @only = Array(options.delete(:only)).map(&:to_s)
17
- @except = Array(options.delete(:except)).map(&:to_s)
18
- args << options unless options.empty?
19
- super
16
+ def initialize(klass, args, actions, strategy, block)
17
+ @actions = actions
18
+ @strategy = strategy
19
+ super(klass, args, block)
20
20
  end
21
21
 
22
22
  def valid?(action)
23
- if @only.present?
24
- @only.include?(action)
25
- elsif @except.present?
26
- !@except.include?(action)
27
- else
28
- true
29
- end
23
+ @strategy.call @actions, action
30
24
  end
31
25
  end
32
26
 
@@ -37,6 +31,32 @@ module ActionController
37
31
  middleware.valid?(action) ? middleware.build(a) : a
38
32
  end
39
33
  end
34
+
35
+ private
36
+
37
+ INCLUDE = ->(list, action) { list.include? action }
38
+ EXCLUDE = ->(list, action) { !list.include? action }
39
+ NULL = ->(list, action) { true }
40
+
41
+ def build_middleware(klass, args, block)
42
+ options = args.extract_options!
43
+ only = Array(options.delete(:only)).map(&:to_s)
44
+ except = Array(options.delete(:except)).map(&:to_s)
45
+ args << options unless options.empty?
46
+
47
+ strategy = NULL
48
+ list = nil
49
+
50
+ if only.any?
51
+ strategy = INCLUDE
52
+ list = only
53
+ elsif except.any?
54
+ strategy = EXCLUDE
55
+ list = except
56
+ end
57
+
58
+ Middleware.new(get_class(klass), args, list, strategy, block)
59
+ end
40
60
  end
41
61
 
42
62
  # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
@@ -98,11 +118,10 @@ module ActionController
98
118
  class Metal < AbstractController::Base
99
119
  abstract!
100
120
 
101
- attr_internal_writer :env
102
-
103
121
  def env
104
- @_env ||= {}
122
+ @_request.env
105
123
  end
124
+ deprecate :env
106
125
 
107
126
  # Returns the last part of the controller's name, underscored, without the ending
108
127
  # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
@@ -114,23 +133,23 @@ module ActionController
114
133
  @controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
115
134
  end
116
135
 
136
+ def self.make_response!(request)
137
+ ActionDispatch::Response.create.tap do |res|
138
+ res.request = request
139
+ end
140
+ end
141
+
117
142
  # Delegates to the class' <tt>controller_name</tt>
118
143
  def controller_name
119
144
  self.class.controller_name
120
145
  end
121
146
 
122
- # The details below can be overridden to support a specific
123
- # Request and Response object. The default ActionController::Base
124
- # implementation includes RackDelegation, which makes a request
125
- # and response object available. You might wish to control the
126
- # environment and response manually for performance reasons.
127
-
128
- attr_internal :headers, :response, :request
147
+ attr_internal :response, :request
129
148
  delegate :session, :to => "@_request"
149
+ delegate :headers, :status=, :location=, :content_type=,
150
+ :status, :location, :content_type, :to => "@_response"
130
151
 
131
152
  def initialize
132
- @_headers = {"Content-Type" => "text/html"}
133
- @_status = 200
134
153
  @_request = nil
135
154
  @_response = nil
136
155
  @_routes = nil
@@ -145,60 +164,51 @@ module ActionController
145
164
  @_params = val
146
165
  end
147
166
 
148
- # Basic implementations for content_type=, location=, and headers are
149
- # provided to reduce the dependency on the RackDelegation module
150
- # in Renderer and Redirector.
151
-
152
- def content_type=(type)
153
- headers["Content-Type"] = type.to_s
154
- end
155
-
156
- def content_type
157
- headers["Content-Type"]
158
- end
159
-
160
- def location
161
- headers["Location"]
162
- end
163
-
164
- def location=(url)
165
- headers["Location"] = url
166
- end
167
+ alias :response_code :status # :nodoc:
167
168
 
168
169
  # Basic url_for that can be overridden for more robust functionality
169
170
  def url_for(string)
170
171
  string
171
172
  end
172
173
 
173
- def status
174
- @_status
175
- end
176
- alias :response_code :status # :nodoc:
177
-
178
- def status=(status)
179
- @_status = Rack::Utils.status_code(status)
180
- end
181
-
182
174
  def response_body=(body)
183
175
  body = [body] unless body.nil? || body.respond_to?(:each)
176
+ response.reset_body!
177
+ body.each { |part|
178
+ next if part.empty?
179
+ response.write part
180
+ }
184
181
  super
185
182
  end
186
183
 
187
184
  # Tests if render or redirect has already happened.
188
185
  def performed?
189
- response_body || (response && response.committed?)
186
+ response_body || response.committed?
190
187
  end
191
188
 
192
- def dispatch(name, request) #:nodoc:
193
- @_request = request
194
- @_env = request.env
195
- @_env['action_controller.instance'] = self
189
+ def dispatch(name, request, response) #:nodoc:
190
+ set_request!(request)
191
+ set_response!(response)
196
192
  process(name)
193
+ request.commit_flash
197
194
  to_a
198
195
  end
199
196
 
197
+ def set_response!(response) # :nodoc:
198
+ @_response = response
199
+ end
200
+
201
+ def set_request!(request) #:nodoc:
202
+ @_request = request
203
+ @_request.controller_instance = self
204
+ end
205
+
200
206
  def to_a #:nodoc:
201
- response ? response.to_a : [status, headers, response_body]
207
+ response.to_a
208
+ end
209
+
210
+ def reset_session
211
+ @_request.reset_session
202
212
  end
203
213
 
204
214
  class_attribute :middleware_stack
@@ -226,15 +236,32 @@ module ActionController
226
236
  req = ActionDispatch::Request.new env
227
237
  action(req.path_parameters[:action]).call(env)
228
238
  end
239
+ class << self; deprecate :call; end
229
240
 
230
241
  # Returns a Rack endpoint for the given action name.
231
- def self.action(name, klass = ActionDispatch::Request)
242
+ def self.action(name)
232
243
  if middleware_stack.any?
233
244
  middleware_stack.build(name) do |env|
234
- new.dispatch(name, klass.new(env))
245
+ req = ActionDispatch::Request.new(env)
246
+ res = make_response! req
247
+ new.dispatch(name, req, res)
235
248
  end
236
249
  else
237
- lambda { |env| new.dispatch(name, klass.new(env)) }
250
+ lambda { |env|
251
+ req = ActionDispatch::Request.new(env)
252
+ res = make_response! req
253
+ new.dispatch(name, req, res)
254
+ }
255
+ end
256
+ end
257
+
258
+ # Direct dispatch to the controller. Instantiates the controller, then
259
+ # executes the action named +name+.
260
+ def self.dispatch(name, req, res)
261
+ if middleware_stack.any?
262
+ middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
263
+ else
264
+ new.dispatch(name, req, res)
238
265
  end
239
266
  end
240
267
  end