actionpack 4.1.7 → 4.2.1

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +311 -527
  3. data/README.rdoc +7 -2
  4. data/lib/abstract_controller/base.rb +16 -6
  5. data/lib/abstract_controller/callbacks.rb +28 -51
  6. data/lib/abstract_controller/helpers.rb +11 -4
  7. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  8. data/lib/abstract_controller/url_for.rb +1 -1
  9. data/lib/action_controller/base.rb +2 -1
  10. data/lib/action_controller/caching/fragments.rb +7 -1
  11. data/lib/action_controller/caching.rb +1 -1
  12. data/lib/action_controller/log_subscriber.rb +26 -26
  13. data/lib/action_controller/metal/conditional_get.rb +37 -12
  14. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  15. data/lib/action_controller/metal/exceptions.rb +1 -1
  16. data/lib/action_controller/metal/force_ssl.rb +1 -1
  17. data/lib/action_controller/metal/head.rb +7 -3
  18. data/lib/action_controller/metal/http_authentication.rb +14 -9
  19. data/lib/action_controller/metal/instrumentation.rb +8 -5
  20. data/lib/action_controller/metal/live.rb +57 -6
  21. data/lib/action_controller/metal/mime_responds.rb +23 -246
  22. data/lib/action_controller/metal/params_wrapper.rb +2 -2
  23. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  24. data/lib/action_controller/metal/redirecting.rb +14 -8
  25. data/lib/action_controller/metal/renderers.rb +30 -10
  26. data/lib/action_controller/metal/rendering.rb +2 -6
  27. data/lib/action_controller/metal/request_forgery_protection.rb +78 -7
  28. data/lib/action_controller/metal/streaming.rb +1 -1
  29. data/lib/action_controller/metal/strong_parameters.rb +125 -12
  30. data/lib/action_controller/metal/url_for.rb +11 -12
  31. data/lib/action_controller/metal.rb +12 -11
  32. data/lib/action_controller/model_naming.rb +1 -1
  33. data/lib/action_controller/railtie.rb +4 -0
  34. data/lib/action_controller/test_case.rb +112 -75
  35. data/lib/action_controller.rb +1 -1
  36. data/lib/action_dispatch/http/cache.rb +5 -4
  37. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  38. data/lib/action_dispatch/http/headers.rb +43 -9
  39. data/lib/action_dispatch/http/mime_negotiation.rb +10 -3
  40. data/lib/action_dispatch/http/mime_type.rb +2 -2
  41. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  42. data/lib/action_dispatch/http/parameters.rb +11 -26
  43. data/lib/action_dispatch/http/request.rb +37 -11
  44. data/lib/action_dispatch/http/response.rb +70 -18
  45. data/lib/action_dispatch/http/upload.rb +3 -8
  46. data/lib/action_dispatch/http/url.rb +88 -69
  47. data/lib/action_dispatch/journey/formatter.rb +33 -17
  48. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  49. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  50. data/lib/action_dispatch/journey/gtg/transition_table.rb +20 -28
  51. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  52. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  53. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  54. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  55. data/lib/action_dispatch/journey/parser.rb +52 -60
  56. data/lib/action_dispatch/journey/parser.y +11 -10
  57. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  58. data/lib/action_dispatch/journey/route.rb +3 -18
  59. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  60. data/lib/action_dispatch/journey/router.rb +53 -77
  61. data/lib/action_dispatch/journey/scanner.rb +5 -5
  62. data/lib/action_dispatch/journey/visitors.rb +81 -92
  63. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  64. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  65. data/lib/action_dispatch/middleware/callbacks.rb +1 -1
  66. data/lib/action_dispatch/middleware/cookies.rb +29 -29
  67. data/lib/action_dispatch/middleware/debug_exceptions.rb +15 -4
  68. data/lib/action_dispatch/middleware/exception_wrapper.rb +50 -18
  69. data/lib/action_dispatch/middleware/flash.rb +13 -7
  70. data/lib/action_dispatch/middleware/params_parser.rb +1 -1
  71. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  72. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  73. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  74. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  75. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
  76. data/lib/action_dispatch/middleware/static.rb +66 -37
  77. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  78. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +37 -9
  79. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
  80. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  83. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -0
  84. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +2 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
  86. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
  87. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  88. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  89. data/lib/action_dispatch/routing/inspector.rb +5 -12
  90. data/lib/action_dispatch/routing/mapper.rb +410 -281
  91. data/lib/action_dispatch/routing/polymorphic_routes.rb +191 -79
  92. data/lib/action_dispatch/routing/redirection.rb +10 -12
  93. data/lib/action_dispatch/routing/route_set.rb +297 -168
  94. data/lib/action_dispatch/routing/url_for.rb +15 -4
  95. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  96. data/lib/action_dispatch/testing/assertions/response.rb +2 -7
  97. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  98. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  99. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  100. data/lib/action_dispatch/testing/assertions.rb +11 -7
  101. data/lib/action_dispatch/testing/integration.rb +24 -19
  102. data/lib/action_dispatch/testing/test_request.rb +1 -1
  103. data/lib/action_dispatch/testing/test_response.rb +7 -0
  104. data/lib/action_pack/gem_version.rb +3 -3
  105. metadata +55 -13
  106. data/lib/action_controller/metal/responder.rb +0 -297
data/README.rdoc CHANGED
@@ -32,7 +32,7 @@ The latest version of Action Pack can be installed with RubyGems:
32
32
 
33
33
  Source code can be downloaded as part of the Rails project on GitHub
34
34
 
35
- * https://github.com/rails/rails/tree/4-1-stable/actionpack
35
+ * https://github.com/rails/rails/tree/4-2-stable/actionpack
36
36
 
37
37
 
38
38
  == License
@@ -48,6 +48,11 @@ API documentation is at
48
48
 
49
49
  * http://api.rubyonrails.org
50
50
 
51
- Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
51
+ Bug reports can be filed for the Ruby on Rails project here:
52
52
 
53
53
  * https://github.com/rails/rails/issues
54
+
55
+ Feature requests should be discussed on the rails-core mailing list here:
56
+
57
+ * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
58
+
@@ -8,7 +8,8 @@ module AbstractController
8
8
  class Error < StandardError #:nodoc:
9
9
  end
10
10
 
11
- class ActionNotFound < StandardError #:nodoc:
11
+ # Raised when a non-existing controller action is triggered.
12
+ class ActionNotFound < StandardError
12
13
  end
13
14
 
14
15
  # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
@@ -120,14 +121,14 @@ module AbstractController
120
121
  #
121
122
  # The actual method that is called is determined by calling
122
123
  # #method_for_action. If no method can handle the action, then an
123
- # ActionNotFound error is raised.
124
+ # AbstractController::ActionNotFound error is raised.
124
125
  #
125
126
  # ==== Returns
126
127
  # * <tt>self</tt>
127
128
  def process(action, *args)
128
- @_action_name = action_name = action.to_s
129
+ @_action_name = action.to_s
129
130
 
130
- unless action_name = _find_action_name(action_name)
131
+ unless action_name = _find_action_name(@_action_name)
131
132
  raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
132
133
  end
133
134
 
@@ -163,6 +164,14 @@ module AbstractController
163
164
  _find_action_name(action_name).present?
164
165
  end
165
166
 
167
+ # Returns true if the given controller is capable of rendering
168
+ # a path. A subclass of +AbstractController::Base+
169
+ # may return false. An Email controller for example does not
170
+ # support paths, only full URLs.
171
+ def self.supports_path?
172
+ true
173
+ end
174
+
166
175
  private
167
176
 
168
177
  # Returns true if the name can be considered an action because
@@ -215,7 +224,8 @@ module AbstractController
215
224
  #
216
225
  # ==== Returns
217
226
  # * <tt>string</tt> - The name of the method that handles the action
218
- # * false - No valid method name could be found. Raise ActionNotFound.
227
+ # * false - No valid method name could be found.
228
+ # Raise AbstractController::ActionNotFound.
219
229
  def _find_action_name(action_name)
220
230
  _valid_action_name?(action_name) && method_for_action(action_name)
221
231
  end
@@ -235,7 +245,7 @@ module AbstractController
235
245
  # the case.
236
246
  #
237
247
  # If none of these conditions are true, and method_for_action
238
- # returns nil, an ActionNotFound exception will be raised.
248
+ # returns nil, an AbstractController::ActionNotFound exception will be raised.
239
249
  #
240
250
  # ==== Parameters
241
251
  # * <tt>action_name</tt> - An action name to find a method name for
@@ -42,20 +42,18 @@ module AbstractController
42
42
  end
43
43
  end
44
44
 
45
- # Skip before, after, and around action callbacks matching any of the names
46
- # Aliased as skip_filter.
45
+ # Skip before, after, and around action callbacks matching any of the names.
47
46
  #
48
47
  # ==== Parameters
49
48
  # * <tt>names</tt> - A list of valid names that could be used for
50
49
  # callbacks. Note that skipping uses Ruby equality, so it's
51
50
  # impossible to skip a callback defined using an anonymous proc
52
- # using #skip_filter
51
+ # using #skip_action_callback
53
52
  def skip_action_callback(*names)
54
53
  skip_before_action(*names)
55
54
  skip_after_action(*names)
56
55
  skip_around_action(*names)
57
56
  end
58
-
59
57
  alias_method :skip_filter, :skip_action_callback
60
58
 
61
59
  # Take callback names and an optional callback proc, normalize them,
@@ -85,7 +83,6 @@ module AbstractController
85
83
  # :call-seq: before_action(names, block)
86
84
  #
87
85
  # Append a callback before actions. See _insert_callbacks for parameter details.
88
- # Aliased as before_filter.
89
86
 
90
87
  ##
91
88
  # :method: prepend_before_action
@@ -93,7 +90,6 @@ module AbstractController
93
90
  # :call-seq: prepend_before_action(names, block)
94
91
  #
95
92
  # Prepend a callback before actions. See _insert_callbacks for parameter details.
96
- # Aliased as prepend_before_filter.
97
93
 
98
94
  ##
99
95
  # :method: skip_before_action
@@ -101,7 +97,6 @@ module AbstractController
101
97
  # :call-seq: skip_before_action(names)
102
98
  #
103
99
  # Skip a callback before actions. See _insert_callbacks for parameter details.
104
- # Aliased as skip_before_filter.
105
100
 
106
101
  ##
107
102
  # :method: append_before_action
@@ -109,7 +104,6 @@ module AbstractController
109
104
  # :call-seq: append_before_action(names, block)
110
105
  #
111
106
  # Append a callback before actions. See _insert_callbacks for parameter details.
112
- # Aliased as append_before_filter.
113
107
 
114
108
  ##
115
109
  # :method: after_action
@@ -117,7 +111,6 @@ module AbstractController
117
111
  # :call-seq: after_action(names, block)
118
112
  #
119
113
  # Append a callback after actions. See _insert_callbacks for parameter details.
120
- # Aliased as after_filter.
121
114
 
122
115
  ##
123
116
  # :method: prepend_after_action
@@ -125,7 +118,6 @@ module AbstractController
125
118
  # :call-seq: prepend_after_action(names, block)
126
119
  #
127
120
  # Prepend a callback after actions. See _insert_callbacks for parameter details.
128
- # Aliased as prepend_after_filter.
129
121
 
130
122
  ##
131
123
  # :method: skip_after_action
@@ -133,7 +125,6 @@ module AbstractController
133
125
  # :call-seq: skip_after_action(names)
134
126
  #
135
127
  # Skip a callback after actions. See _insert_callbacks for parameter details.
136
- # Aliased as skip_after_filter.
137
128
 
138
129
  ##
139
130
  # :method: append_after_action
@@ -141,7 +132,6 @@ module AbstractController
141
132
  # :call-seq: append_after_action(names, block)
142
133
  #
143
134
  # Append a callback after actions. See _insert_callbacks for parameter details.
144
- # Aliased as append_after_filter.
145
135
 
146
136
  ##
147
137
  # :method: around_action
@@ -149,7 +139,6 @@ module AbstractController
149
139
  # :call-seq: around_action(names, block)
150
140
  #
151
141
  # Append a callback around actions. See _insert_callbacks for parameter details.
152
- # Aliased as around_filter.
153
142
 
154
143
  ##
155
144
  # :method: prepend_around_action
@@ -157,7 +146,6 @@ module AbstractController
157
146
  # :call-seq: prepend_around_action(names, block)
158
147
  #
159
148
  # Prepend a callback around actions. See _insert_callbacks for parameter details.
160
- # Aliased as prepend_around_filter.
161
149
 
162
150
  ##
163
151
  # :method: skip_around_action
@@ -165,7 +153,6 @@ module AbstractController
165
153
  # :call-seq: skip_around_action(names)
166
154
  #
167
155
  # Skip a callback around actions. See _insert_callbacks for parameter details.
168
- # Aliased as skip_around_filter.
169
156
 
170
157
  ##
171
158
  # :method: append_around_action
@@ -173,46 +160,36 @@ module AbstractController
173
160
  # :call-seq: append_around_action(names, block)
174
161
  #
175
162
  # Append a callback around actions. See _insert_callbacks for parameter details.
176
- # Aliased as append_around_filter.
177
163
 
178
164
  # set up before_action, prepend_before_action, skip_before_action, etc.
179
165
  # for each of before, after, and around.
180
166
  [:before, :after, :around].each do |callback|
181
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
182
- # Append a before, after or around callback. See _insert_callbacks
183
- # for details on the allowed parameters.
184
- def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
185
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
186
- set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
187
- end # end
188
- end # end
189
-
190
- alias_method :#{callback}_filter, :#{callback}_action
191
-
192
- # Prepend a before, after or around callback. See _insert_callbacks
193
- # for details on the allowed parameters.
194
- def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
195
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
196
- set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
197
- end # end
198
- end # end
199
-
200
- alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
201
-
202
- # Skip a before, after or around callback. See _insert_callbacks
203
- # for details on the allowed parameters.
204
- def skip_#{callback}_action(*names) # def skip_before_action(*names)
205
- _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
206
- skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
207
- end # end
208
- end # end
209
-
210
- alias_method :skip_#{callback}_filter, :skip_#{callback}_action
211
-
212
- # *_action is the same as append_*_action
213
- alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action
214
- alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action
215
- RUBY_EVAL
167
+ define_method "#{callback}_action" do |*names, &blk|
168
+ _insert_callbacks(names, blk) do |name, options|
169
+ set_callback(:process_action, callback, name, options)
170
+ end
171
+ end
172
+ alias_method :"#{callback}_filter", :"#{callback}_action"
173
+
174
+ define_method "prepend_#{callback}_action" do |*names, &blk|
175
+ _insert_callbacks(names, blk) do |name, options|
176
+ set_callback(:process_action, callback, name, options.merge(:prepend => true))
177
+ end
178
+ end
179
+ alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"
180
+
181
+ # Skip a before, after or around callback. See _insert_callbacks
182
+ # for details on the allowed parameters.
183
+ define_method "skip_#{callback}_action" do |*names|
184
+ _insert_callbacks(names) do |name, options|
185
+ skip_callback(:process_action, callback, name, options)
186
+ end
187
+ end
188
+ alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"
189
+
190
+ # *_action is the same as append_*_action
191
+ alias_method :"append_#{callback}_action", :"#{callback}_action"
192
+ alias_method :"append_#{callback}_filter", :"#{callback}_action"
216
193
  end
217
194
  end
218
195
  end
@@ -27,9 +27,6 @@ module AbstractController
27
27
  end
28
28
 
29
29
  module ClassMethods
30
- MissingHelperError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('AbstractController::Helpers::ClassMethods::MissingHelperError',
31
- 'AbstractController::Helpers::MissingHelperError')
32
-
33
30
  # When a class is inherited, wrap its helper module in a new module.
34
31
  # This ensures that the parent class's module can be changed
35
32
  # independently of the child class's.
@@ -153,7 +150,17 @@ module AbstractController
153
150
  rescue LoadError => e
154
151
  raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
155
152
  end
156
- file_name.camelize.constantize
153
+
154
+ mod_name = file_name.camelize
155
+ begin
156
+ mod_name.constantize
157
+ rescue LoadError
158
+ # dependencies.rb gives a similar error message but its wording is
159
+ # not as clear because it mentions autoloading. To the user all it
160
+ # matters is that a helper module couldn't be loaded, autoloading
161
+ # is an internal mechanism that should not leak.
162
+ raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
163
+ end
157
164
  when Module
158
165
  arg
159
166
  else
@@ -1,14 +1,14 @@
1
1
  module AbstractController
2
2
  module Railties
3
3
  module RoutesHelpers
4
- def self.with(routes)
4
+ def self.with(routes, include_path_helpers = true)
5
5
  Module.new do
6
6
  define_method(:inherited) do |klass|
7
7
  super(klass)
8
8
  if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
9
- klass.send(:include, namespace.railtie_routes_url_helpers)
9
+ klass.send(:include, namespace.railtie_routes_url_helpers(include_path_helpers))
10
10
  else
11
- klass.send(:include, routes.url_helpers)
11
+ klass.send(:include, routes.url_helpers(include_path_helpers))
12
12
  end
13
13
  end
14
14
  end
@@ -11,7 +11,7 @@ module AbstractController
11
11
 
12
12
  def _routes
13
13
  raise "In order to use #url_for, you must include routing helpers explicitly. " \
14
- "For instance, `include Rails.application.routes.url_helpers"
14
+ "For instance, `include Rails.application.routes.url_helpers`."
15
15
  end
16
16
 
17
17
  module ClassMethods
@@ -44,7 +44,7 @@ module ActionController
44
44
  # The full request object is available via the request accessor and is primarily used to query for HTTP headers:
45
45
  #
46
46
  # def server_ip
47
- # location = request.env["SERVER_ADDR"]
47
+ # location = request.env["REMOTE_ADDR"]
48
48
  # render plain: "This server hosted at #{location}"
49
49
  # end
50
50
  #
@@ -213,6 +213,7 @@ module ActionController
213
213
  Rendering,
214
214
  Renderers::All,
215
215
  ConditionalGet,
216
+ EtagWithTemplateDigest,
216
217
  RackDelegation,
217
218
  Caching,
218
219
  MimeResponds,
@@ -90,7 +90,13 @@ module ActionController
90
90
  end
91
91
 
92
92
  def instrument_fragment_cache(name, key) # :nodoc:
93
- ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
93
+ payload = {
94
+ controller: controller_name,
95
+ action: action_name,
96
+ key: key
97
+ }
98
+
99
+ ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield }
94
100
  end
95
101
  end
96
102
  end
@@ -16,7 +16,7 @@ module ActionController
16
16
  # All the caching stores from ActiveSupport::Cache are available to be used as backends
17
17
  # for Action Controller caching.
18
18
  #
19
- # Configuration examples (MemoryStore is the default):
19
+ # Configuration examples (FileStore is the default):
20
20
  #
21
21
  # config.action_controller.cache_store = :memory_store
22
22
  # config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
@@ -1,4 +1,3 @@
1
-
2
1
  module ActionController
3
2
  class LogSubscriber < ActiveSupport::LogSubscriber
4
3
  INTERNAL_PARAMS = %w(controller action format _method only_path)
@@ -16,50 +15,51 @@ module ActionController
16
15
  end
17
16
 
18
17
  def process_action(event)
19
- return unless logger.info?
20
-
21
- payload = event.payload
22
- additions = ActionController::Base.log_process_action(payload)
23
-
24
- status = payload[:status]
25
- if status.nil? && payload[:exception].present?
26
- exception_class_name = payload[:exception].first
27
- status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
18
+ info do
19
+ payload = event.payload
20
+ additions = ActionController::Base.log_process_action(payload)
21
+
22
+ status = payload[:status]
23
+ if status.nil? && payload[:exception].present?
24
+ exception_class_name = payload[:exception].first
25
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
26
+ end
27
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
28
+ message << " (#{additions.join(" | ")})" unless additions.blank?
29
+ message
28
30
  end
29
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
30
- message << " (#{additions.join(" | ")})" unless additions.blank?
31
-
32
- info(message)
33
31
  end
34
32
 
35
33
  def halted_callback(event)
36
- info("Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected")
34
+ info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
37
35
  end
38
36
 
39
37
  def send_file(event)
40
- info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
38
+ info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
41
39
  end
42
40
 
43
41
  def redirect_to(event)
44
- info("Redirected to #{event.payload[:location]}")
42
+ info { "Redirected to #{event.payload[:location]}" }
45
43
  end
46
44
 
47
45
  def send_data(event)
48
- info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
46
+ info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
49
47
  end
50
48
 
51
49
  def unpermitted_parameters(event)
52
- unpermitted_keys = event.payload[:keys]
53
- debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
50
+ debug do
51
+ unpermitted_keys = event.payload[:keys]
52
+ "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
53
+ end
54
54
  end
55
55
 
56
56
  def deep_munge(event)
57
- message = "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
58
- "to nil, because it was one of [], [null] or [null, null, ...]. "\
59
- "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
60
- "for more information."\
61
-
62
- debug(message)
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
63
  end
64
64
 
65
65
  %w(write_fragment read_fragment exist_fragment?
@@ -13,9 +13,9 @@ module ActionController
13
13
  end
14
14
 
15
15
  module ClassMethods
16
- # Allows you to consider additional controller-wide information when generating an etag.
16
+ # Allows you to consider additional controller-wide information when generating an ETag.
17
17
  # 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
18
+ # may want to add the current user id to be part of the ETag to prevent authorized displaying
19
19
  # of cached pages.
20
20
  #
21
21
  # class InvoicesController < ApplicationController
@@ -32,7 +32,7 @@ module ActionController
32
32
  end
33
33
  end
34
34
 
35
- # Sets the etag, +last_modified+, or both on the response and renders a
35
+ # Sets the +etag+, +last_modified+, or both on the response and renders a
36
36
  # <tt>304 Not Modified</tt> response if the request is already fresh.
37
37
  #
38
38
  # === Parameters:
@@ -41,6 +41,11 @@ module ActionController
41
41
  # * <tt>:last_modified</tt>.
42
42
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
43
43
  # +true+ if you want your application to be cachable by other devices (proxy caches).
44
+ # * <tt>:template</tt> By default, the template digest for the current
45
+ # controller/action is included in ETags. If the action renders a
46
+ # different template, you can include its digest instead. If the action
47
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
48
+ # to skip any attempt to check for a template digest.
44
49
  #
45
50
  # === Example:
46
51
  #
@@ -49,11 +54,11 @@ module ActionController
49
54
  # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
50
55
  # end
51
56
  #
52
- # This will render the show template if the request isn't sending a matching etag or
57
+ # This will render the show template if the request isn't sending a matching ETag or
53
58
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
54
59
  #
55
60
  # You can also just pass a record where +last_modified+ will be set by calling
56
- # +updated_at+ and the etag by passing the object itself.
61
+ # +updated_at+ and the +etag+ by passing the object itself.
57
62
  #
58
63
  # def show
59
64
  # @article = Article.find(params[:id])
@@ -66,18 +71,24 @@ module ActionController
66
71
  # @article = Article.find(params[:id])
67
72
  # fresh_when(@article, public: true)
68
73
  # end
74
+ #
75
+ # When rendering a different template than the default controller/action
76
+ # style, you can indicate which digest to include in the ETag:
77
+ #
78
+ # before_action { fresh_when @article, template: 'widgets/show' }
79
+ #
69
80
  def fresh_when(record_or_options, additional_options = {})
70
81
  if record_or_options.is_a? Hash
71
82
  options = record_or_options
72
- options.assert_valid_keys(:etag, :last_modified, :public)
83
+ options.assert_valid_keys(:etag, :last_modified, :public, :template)
73
84
  else
74
85
  record = record_or_options
75
86
  options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
76
87
  end
77
88
 
78
- response.etag = combine_etags(options[:etag]) if options[:etag]
79
- response.last_modified = options[:last_modified] if options[:last_modified]
80
- response.cache_control[:public] = true if options[:public]
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]
81
92
 
82
93
  head :not_modified if request.fresh?(response)
83
94
  end
@@ -93,6 +104,11 @@ module ActionController
93
104
  # * <tt>:last_modified</tt>.
94
105
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
95
106
  # +true+ if you want your application to be cachable by other devices (proxy caches).
107
+ # * <tt>:template</tt> By default, the template digest for the current
108
+ # controller/action is included in ETags. If the action renders a
109
+ # different template, you can include its digest instead. If the action
110
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
111
+ # to skip any attempt to check for a template digest.
96
112
  #
97
113
  # === Example:
98
114
  #
@@ -108,7 +124,7 @@ module ActionController
108
124
  # end
109
125
  #
110
126
  # You can also just pass a record where +last_modified+ will be set by calling
111
- # updated_at and the etag by passing the object itself.
127
+ # +updated_at+ and the +etag+ by passing the object itself.
112
128
  #
113
129
  # def show
114
130
  # @article = Article.find(params[:id])
@@ -133,6 +149,14 @@ module ActionController
133
149
  # end
134
150
  # end
135
151
  # end
152
+ #
153
+ # When rendering a different template than the default controller/action
154
+ # style, you can indicate which digest to include in the ETag:
155
+ #
156
+ # def show
157
+ # super if stale? @article, template: 'widgets/show'
158
+ # end
159
+ #
136
160
  def stale?(record_or_options, additional_options = {})
137
161
  fresh_when(record_or_options, additional_options)
138
162
  !request.fresh?(response)
@@ -168,8 +192,9 @@ module ActionController
168
192
  end
169
193
 
170
194
  private
171
- def combine_etags(etag)
172
- [ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
195
+ def combine_etags(options)
196
+ etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
197
+ etags.unshift options[:etag]
173
198
  end
174
199
  end
175
200
  end