actionpack 4.2.11.3 → 5.0.7.2

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +890 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +54 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +14 -11
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +14 -34
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +11 -11
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +71 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +9 -9
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +83 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +527 -134
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/railtie.rb +11 -7
  48. data/lib/action_controller/renderer.rb +113 -0
  49. data/lib/action_controller/template_assertions.rb +9 -0
  50. data/lib/action_controller/test_case.rb +311 -374
  51. data/lib/action_controller.rb +12 -9
  52. data/lib/action_dispatch/http/cache.rb +73 -34
  53. data/lib/action_dispatch/http/filter_parameters.rb +16 -12
  54. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  55. data/lib/action_dispatch/http/headers.rb +45 -14
  56. data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
  57. data/lib/action_dispatch/http/mime_type.rb +126 -90
  58. data/lib/action_dispatch/http/mime_types.rb +3 -4
  59. data/lib/action_dispatch/http/parameter_filter.rb +19 -9
  60. data/lib/action_dispatch/http/parameters.rb +70 -40
  61. data/lib/action_dispatch/http/request.rb +144 -89
  62. data/lib/action_dispatch/http/response.rb +215 -102
  63. data/lib/action_dispatch/http/upload.rb +6 -2
  64. data/lib/action_dispatch/http/url.rb +117 -8
  65. data/lib/action_dispatch/journey/formatter.rb +47 -30
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  67. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  68. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  69. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  70. data/lib/action_dispatch/journey/parser.rb +2 -0
  71. data/lib/action_dispatch/journey/parser_extras.rb +8 -2
  72. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  73. data/lib/action_dispatch/journey/route.rb +88 -26
  74. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  75. data/lib/action_dispatch/journey/router.rb +8 -10
  76. data/lib/action_dispatch/journey/routes.rb +14 -15
  77. data/lib/action_dispatch/journey/visitors.rb +89 -44
  78. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  79. data/lib/action_dispatch/middleware/cookies.rb +188 -134
  80. data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
  81. data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
  82. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  83. data/lib/action_dispatch/middleware/executor.rb +19 -0
  84. data/lib/action_dispatch/middleware/flash.rb +66 -45
  85. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  86. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  87. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  88. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  89. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  90. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  91. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  92. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  93. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  94. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  95. data/lib/action_dispatch/middleware/ssl.rb +124 -36
  96. data/lib/action_dispatch/middleware/stack.rb +44 -40
  97. data/lib/action_dispatch/middleware/static.rb +51 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  99. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  101. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  103. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  104. data/lib/action_dispatch/railtie.rb +2 -2
  105. data/lib/action_dispatch/request/session.rb +69 -33
  106. data/lib/action_dispatch/request/utils.rb +51 -19
  107. data/lib/action_dispatch/routing/inspector.rb +32 -43
  108. data/lib/action_dispatch/routing/mapper.rb +515 -348
  109. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  110. data/lib/action_dispatch/routing/redirection.rb +5 -4
  111. data/lib/action_dispatch/routing/route_set.rb +148 -240
  112. data/lib/action_dispatch/routing/url_for.rb +27 -10
  113. data/lib/action_dispatch/routing.rb +17 -13
  114. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  115. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  116. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  117. data/lib/action_dispatch/testing/assertions.rb +1 -1
  118. data/lib/action_dispatch/testing/integration.rb +377 -149
  119. data/lib/action_dispatch/testing/request_encoder.rb +53 -0
  120. data/lib/action_dispatch/testing/test_process.rb +24 -20
  121. data/lib/action_dispatch/testing/test_request.rb +22 -31
  122. data/lib/action_dispatch/testing/test_response.rb +12 -4
  123. data/lib/action_dispatch.rb +4 -1
  124. data/lib/action_pack/gem_version.rb +4 -4
  125. data/lib/action_pack.rb +1 -1
  126. metadata +32 -34
  127. data/lib/action_controller/metal/hide_actions.rb +0 -40
  128. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  129. data/lib/action_controller/middleware.rb +0 -39
  130. data/lib/action_controller/model_naming.rb +0 -12
  131. data/lib/action_dispatch/journey/backwards.rb +0 -5
  132. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  133. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  134. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  135. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  136. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -0,0 +1,14 @@
1
+ module ActionController
2
+ module ApiRendering
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Rendering
7
+ end
8
+
9
+ def render_to_body(options = {})
10
+ _process_options(options)
11
+ super
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,147 @@
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
+ # Normally, +ApplicationController+ is the only controller that inherits from
20
+ # <tt>ActionController::API</tt>. All other controllers in turn inherit from
21
+ # +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_to</tt> in
41
+ # all actions, otherwise it will return 204 No Content.
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_to</tt> method in your controllers in the same way as in
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 the modules included in
85
+ # <tt>ActionController::Base</tt> if you want to use any other
86
+ # functionality that is not provided by <tt>ActionController::API</tt>
87
+ # out of the box.
88
+ class API < Metal
89
+ abstract!
90
+
91
+ # Shortcut helper that returns all the ActionController::API modules except
92
+ # the ones passed as arguments:
93
+ #
94
+ # class MyAPIBaseController < ActionController::Metal
95
+ # ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
96
+ # include left
97
+ # end
98
+ # end
99
+ #
100
+ # This gives better control over what you want to exclude and makes it easier
101
+ # to create an API controller class, instead of listing the modules required
102
+ # manually.
103
+ def self.without_modules(*modules)
104
+ modules = modules.map do |m|
105
+ m.is_a?(Symbol) ? ActionController.const_get(m) : m
106
+ end
107
+
108
+ MODULES - modules
109
+ end
110
+
111
+ MODULES = [
112
+ AbstractController::Rendering,
113
+
114
+ UrlFor,
115
+ Redirecting,
116
+ ApiRendering,
117
+ Renderers::All,
118
+ ConditionalGet,
119
+ BasicImplicitRender,
120
+ StrongParameters,
121
+
122
+ ForceSSL,
123
+ DataStreaming,
124
+
125
+ # Before callbacks should also be executed as early as possible, so
126
+ # also include them at the bottom.
127
+ AbstractController::Callbacks,
128
+
129
+ # Append rescue at the bottom to wrap as much as possible.
130
+ Rescue,
131
+
132
+ # Add instrumentations hooks at the bottom, to ensure they instrument
133
+ # all the methods properly.
134
+ Instrumentation,
135
+
136
+ # Params wrapper should come before instrumentation so they are
137
+ # properly showed in logs
138
+ ParamsWrapper
139
+ ]
140
+
141
+ MODULES.each do |mod|
142
+ include mod
143
+ end
144
+
145
+ ActiveSupport.run_load_hooks(:action_controller, self)
146
+ end
147
+ 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,
@@ -230,7 +229,7 @@ module ActionController
230
229
  HttpAuthentication::Digest::ControllerMethods,
231
230
  HttpAuthentication::Token::ControllerMethods,
232
231
 
233
- # Before callbacks should also be executed the earliest as possible, so
232
+ # Before callbacks should also be executed as early as possible, so
234
233
  # also include them at the bottom.
235
234
  AbstractController::Callbacks,
236
235
 
@@ -249,18 +248,22 @@ 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
- PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
255
- :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
256
- :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
254
+ PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i(
255
+ @_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class
256
+ @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
257
+ )
257
258
 
258
259
  def _protected_ivars # :nodoc:
259
260
  PROTECTED_IVARS
260
261
  end
261
262
 
262
- def self.protected_instance_variables
263
- PROTECTED_IVARS
263
+ def self.make_response!(request)
264
+ ActionDispatch::Response.create.tap do |res|
265
+ res.request = request
266
+ end
264
267
  end
265
268
 
266
269
  ActiveSupport.run_load_hooks(:action_controller, self)
@@ -1,14 +1,10 @@
1
- require 'fileutils'
2
- require 'uri'
3
- require 'set'
4
-
5
1
  module ActionController
6
2
  # \Caching is a cheap way of speeding up slow applications by keeping the result of
7
3
  # calculations, renderings, and database calls around for subsequent requests.
8
4
  #
9
5
  # You can read more about each approach by clicking the modules below.
10
6
  #
11
- # Note: To turn off all caching, set
7
+ # Note: To turn off all caching provided by Action Controller, set
12
8
  # config.action_controller.perform_caching = false
13
9
  #
14
10
  # == \Caching stores
@@ -24,66 +20,25 @@ module ActionController
24
20
  # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
25
21
  # config.action_controller.cache_store = MyOwnStore.new('parameter')
26
22
  module Caching
27
- extend ActiveSupport::Concern
28
23
  extend ActiveSupport::Autoload
29
-
30
- eager_autoload do
31
- autoload :Fragments
32
- end
33
-
34
- module ConfigMethods
35
- def cache_store
36
- config.cache_store
37
- end
38
-
39
- def cache_store=(store)
40
- config.cache_store = ActiveSupport::Cache.lookup_store(store)
41
- end
42
-
43
- private
44
- def cache_configured?
45
- perform_caching && cache_store
46
- end
47
- end
48
-
49
- include RackDelegation
50
- include AbstractController::Callbacks
51
-
52
- include ConfigMethods
53
- include Fragments
24
+ extend ActiveSupport::Concern
54
25
 
55
26
  included do
56
- extend ConfigMethods
57
-
58
- config_accessor :default_static_extension
59
- self.default_static_extension ||= '.html'
60
-
61
- config_accessor :perform_caching
62
- self.perform_caching = true if perform_caching.nil?
63
-
64
- class_attribute :_view_cache_dependencies
65
- self._view_cache_dependencies = []
66
- helper_method :view_cache_dependencies if respond_to?(:helper_method)
27
+ include AbstractController::Caching
67
28
  end
68
29
 
69
- module ClassMethods
70
- def view_cache_dependency(&dependency)
71
- self._view_cache_dependencies += [dependency]
72
- end
73
- end
30
+ private
74
31
 
75
- def view_cache_dependencies
76
- self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
77
- end
32
+ def instrument_payload(key)
33
+ {
34
+ controller: controller_name,
35
+ action: action_name,
36
+ key: key
37
+ }
38
+ end
78
39
 
79
- protected
80
- # Convenience accessor.
81
- def cache(key, options = {}, &block)
82
- if cache_configured?
83
- cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
84
- else
85
- yield
86
- end
40
+ def instrument_name
41
+ "action_controller"
87
42
  end
88
43
  end
89
44
  end
@@ -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,9 @@ 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.empty?
29
+ message << "\n\n" if defined?(Rails.env) && Rails.env.development?
30
+
29
31
  message
30
32
  end
31
33
  end
@@ -53,15 +55,6 @@ module ActionController
53
55
  end
54
56
  end
55
57
 
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
58
  %w(write_fragment read_fragment exist_fragment?
66
59
  expire_fragment expire_page write_page).each do |method|
67
60
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -0,0 +1,11 @@
1
+ module ActionController
2
+ module BasicImplicitRender # :nodoc:
3
+ def send_action(method, *args)
4
+ super.tap { default_render unless performed? }
5
+ end
6
+
7
+ def default_render(*args)
8
+ head :no_content
9
+ end
10
+ end
11
+ end
@@ -4,7 +4,6 @@ module ActionController
4
4
  module ConditionalGet
5
5
  extend ActiveSupport::Concern
6
6
 
7
- include RackDelegation
8
7
  include Head
9
8
 
10
9
  included do
@@ -15,7 +14,7 @@ module ActionController
15
14
  module ClassMethods
16
15
  # Allows you to consider additional controller-wide information when generating an ETag.
17
16
  # For example, if you serve pages tailored depending on who's logged in at the moment, you
18
- # may want to add the current user id to be part of the ETag to prevent authorized displaying
17
+ # may want to add the current user id to be part of the ETag to prevent unauthorized displaying
19
18
  # of cached pages.
20
19
  #
21
20
  # class InvoicesController < ApplicationController
@@ -37,10 +36,25 @@ module ActionController
37
36
  #
38
37
  # === Parameters:
39
38
  #
40
- # * <tt>:etag</tt>.
41
- # * <tt>:last_modified</tt>.
39
+ # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
40
+ # +:weak_etag+ option.
41
+ # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
42
+ # Requests that set If-None-Match header may return a 304 Not Modified
43
+ # response if it matches the ETag exactly. A weak ETag indicates semantic
44
+ # equivalence, not byte-for-byte equality, so they're good for caching
45
+ # HTML pages in browser caches. They can't be used for responses that
46
+ # must be byte-identical, like serving Range requests within a PDF file.
47
+ # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
48
+ # Requests that set If-None-Match header may return a 304 Not Modified
49
+ # response if it matches the ETag exactly. A strong ETag implies exact
50
+ # equality: the response must match byte for byte. This is necessary for
51
+ # doing Range requests within a large video or PDF file, for example, or
52
+ # for compatibility with some CDNs that don't support weak ETags.
53
+ # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
54
+ # response. Subsequent requests that set If-Modified-Since may return a
55
+ # 304 Not Modified response if last_modified <= If-Modified-Since.
42
56
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
43
- # +true+ if you want your application to be cachable by other devices (proxy caches).
57
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
44
58
  # * <tt>:template</tt> By default, the template digest for the current
45
59
  # controller/action is included in ETags. If the action renders a
46
60
  # different template, you can include its digest instead. If the action
@@ -51,21 +65,31 @@ module ActionController
51
65
  #
52
66
  # def show
53
67
  # @article = Article.find(params[:id])
54
- # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
68
+ # fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
55
69
  # end
56
70
  #
57
71
  # This will render the show template if the request isn't sending a matching ETag or
58
72
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
59
73
  #
60
- # You can also just pass a record where +last_modified+ will be set by calling
61
- # +updated_at+ and the +etag+ by passing the object itself.
74
+ # You can also just pass a record. In this case +last_modified+ will be set
75
+ # by calling +updated_at+ and +etag+ by passing the object itself.
62
76
  #
63
77
  # def show
64
78
  # @article = Article.find(params[:id])
65
79
  # fresh_when(@article)
66
80
  # end
67
81
  #
68
- # When passing a record, you can still set whether the public header:
82
+ # You can also pass an object that responds to +maximum+, such as a
83
+ # collection of active records. In this case +last_modified+ will be set by
84
+ # calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
85
+ # most recently updated record) and the +etag+ by passing the object itself.
86
+ #
87
+ # def index
88
+ # @articles = Article.all
89
+ # fresh_when(@articles)
90
+ # end
91
+ #
92
+ # When passing a record or a collection, you can still set the public header:
69
93
  #
70
94
  # def show
71
95
  # @article = Article.find(params[:id])
@@ -77,18 +101,20 @@ module ActionController
77
101
  #
78
102
  # before_action { fresh_when @article, template: 'widgets/show' }
79
103
  #
80
- def fresh_when(record_or_options, additional_options = {})
81
- if record_or_options.is_a? Hash
82
- options = record_or_options
83
- options.assert_valid_keys(:etag, :last_modified, :public, :template)
84
- else
85
- record = record_or_options
86
- options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
104
+ def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
105
+ weak_etag ||= etag || object unless strong_etag
106
+ last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
107
+
108
+ if strong_etag
109
+ response.strong_etag = combine_etags strong_etag,
110
+ last_modified: last_modified, public: public, template: template
111
+ elsif weak_etag || template
112
+ response.weak_etag = combine_etags weak_etag,
113
+ last_modified: last_modified, public: public, template: template
87
114
  end
88
115
 
89
- response.etag = combine_etags(options) if options[:etag] || options[:template]
90
- response.last_modified = options[:last_modified] if options[:last_modified]
91
- response.cache_control[:public] = true if options[:public]
116
+ response.last_modified = last_modified if last_modified
117
+ response.cache_control[:public] = true if public
92
118
 
93
119
  head :not_modified if request.fresh?(response)
94
120
  end
@@ -100,10 +126,25 @@ module ActionController
100
126
  #
101
127
  # === Parameters:
102
128
  #
103
- # * <tt>:etag</tt>.
104
- # * <tt>:last_modified</tt>.
129
+ # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
130
+ # +:weak_etag+ option.
131
+ # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
132
+ # requests that set If-None-Match header may return a 304 Not Modified
133
+ # response if it matches the ETag exactly. A weak ETag indicates semantic
134
+ # equivalence, not byte-for-byte equality, so they're good for caching
135
+ # HTML pages in browser caches. They can't be used for responses that
136
+ # must be byte-identical, like serving Range requests within a PDF file.
137
+ # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
138
+ # Requests that set If-None-Match header may return a 304 Not Modified
139
+ # response if it matches the ETag exactly. A strong ETag implies exact
140
+ # equality: the response must match byte for byte. This is necessary for
141
+ # doing Range requests within a large video or PDF file, for example, or
142
+ # for compatibility with some CDNs that don't support weak ETags.
143
+ # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
144
+ # response. Subsequent requests that set If-Modified-Since may return a
145
+ # 304 Not Modified response if last_modified <= If-Modified-Since.
105
146
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
106
- # +true+ if you want your application to be cachable by other devices (proxy caches).
147
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
107
148
  # * <tt>:template</tt> By default, the template digest for the current
108
149
  # controller/action is included in ETags. If the action renders a
109
150
  # different template, you can include its digest instead. If the action
@@ -115,7 +156,7 @@ module ActionController
115
156
  # def show
116
157
  # @article = Article.find(params[:id])
117
158
  #
118
- # if stale?(etag: @article, last_modified: @article.created_at)
159
+ # if stale?(etag: @article, last_modified: @article.updated_at)
119
160
  # @statistics = @article.really_expensive_call
120
161
  # respond_to do |format|
121
162
  # # all the supported formats
@@ -123,8 +164,8 @@ module ActionController
123
164
  # end
124
165
  # end
125
166
  #
126
- # You can also just pass a record where +last_modified+ will be set by calling
127
- # +updated_at+ and the +etag+ by passing the object itself.
167
+ # You can also just pass a record. In this case +last_modified+ will be set
168
+ # by calling +updated_at+ and +etag+ by passing the object itself.
128
169
  #
129
170
  # def show
130
171
  # @article = Article.find(params[:id])
@@ -137,7 +178,23 @@ module ActionController
137
178
  # end
138
179
  # end
139
180
  #
140
- # When passing a record, you can still set whether the public header:
181
+ # You can also pass an object that responds to +maximum+, such as a
182
+ # collection of active records. In this case +last_modified+ will be set by
183
+ # calling +maximum(:updated_at)+ on the collection (the timestamp of the
184
+ # most recently updated record) and the +etag+ by passing the object itself.
185
+ #
186
+ # def index
187
+ # @articles = Article.all
188
+ #
189
+ # if stale?(@articles)
190
+ # @statistics = @articles.really_expensive_call
191
+ # respond_to do |format|
192
+ # # all the supported formats
193
+ # end
194
+ # end
195
+ # end
196
+ #
197
+ # When passing a record or a collection, you can still set the public header:
141
198
  #
142
199
  # def show
143
200
  # @article = Article.find(params[:id])
@@ -157,12 +214,12 @@ module ActionController
157
214
  # super if stale? @article, template: 'widgets/show'
158
215
  # end
159
216
  #
160
- def stale?(record_or_options, additional_options = {})
161
- fresh_when(record_or_options, additional_options)
217
+ def stale?(object = nil, **freshness_kwargs)
218
+ fresh_when(object, **freshness_kwargs)
162
219
  !request.fresh?(response)
163
220
  end
164
221
 
165
- # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
222
+ # Sets an HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
166
223
  # instruction, so that intermediate caches must not cache the response.
167
224
  #
168
225
  # expires_in 20.minutes
@@ -172,7 +229,7 @@ module ActionController
172
229
  # This method will overwrite an existing Cache-Control header.
173
230
  # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
174
231
  #
175
- # The method will also ensure a HTTP Date header for client compatibility.
232
+ # The method will also ensure an HTTP Date header for client compatibility.
176
233
  def expires_in(seconds, options = {})
177
234
  response.cache_control.merge!(
178
235
  :max_age => seconds,
@@ -185,16 +242,31 @@ module ActionController
185
242
  response.date = Time.now unless response.date?
186
243
  end
187
244
 
188
- # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
245
+ # Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
189
246
  # occur by the browser or intermediate caches (like caching proxy servers).
190
247
  def expires_now
191
248
  response.cache_control.replace(:no_cache => true)
192
249
  end
193
250
 
251
+ # Cache or yield the block. The cache is supposed to never expire.
252
+ #
253
+ # You can use this method when you have an HTTP response that never changes,
254
+ # and the browser and proxies should cache it indefinitely.
255
+ #
256
+ # * +public+: By default, HTTP responses are private, cached only on the
257
+ # user's web browser. To allow proxies to cache the response, set +true+ to
258
+ # indicate that they can serve the cached response to all users.
259
+ def http_cache_forever(public: false)
260
+ expires_in 100.years, public: public
261
+
262
+ yield if stale?(etag: request.fullpath,
263
+ last_modified: Time.new(2011, 1, 1).utc,
264
+ public: public)
265
+ end
266
+
194
267
  private
195
- def combine_etags(options)
196
- etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
197
- etags.unshift options[:etag]
268
+ def combine_etags(validator, options)
269
+ [validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
198
270
  end
199
271
  end
200
272
  end
@@ -2,10 +2,8 @@ module ActionController #:nodoc:
2
2
  module Cookies
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include RackDelegation
6
-
7
5
  included do
8
- helper_method :cookies
6
+ helper_method :cookies if defined?(helper_method)
9
7
  end
10
8
 
11
9
  private