actionpack 4.0.13 → 4.1.0.beta1

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

Potentially problematic release.


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

Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +131 -1636
  3. data/README.rdoc +1 -6
  4. data/lib/abstract_controller.rb +1 -2
  5. data/lib/abstract_controller/base.rb +3 -25
  6. data/lib/abstract_controller/callbacks.rb +4 -2
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +18 -15
  9. data/lib/abstract_controller/rendering.rb +48 -127
  10. data/lib/action_controller.rb +1 -17
  11. data/lib/action_controller/base.rb +14 -6
  12. data/lib/action_controller/caching.rb +1 -11
  13. data/lib/action_controller/log_subscriber.rb +1 -1
  14. data/lib/action_controller/metal.rb +0 -4
  15. data/lib/action_controller/metal/flash.rb +17 -0
  16. data/lib/action_controller/metal/force_ssl.rb +1 -1
  17. data/lib/action_controller/metal/head.rb +1 -3
  18. data/lib/action_controller/metal/helpers.rb +6 -2
  19. data/lib/action_controller/metal/http_authentication.rb +7 -14
  20. data/lib/action_controller/metal/instrumentation.rb +1 -1
  21. data/lib/action_controller/metal/live.rb +74 -0
  22. data/lib/action_controller/metal/mime_responds.rb +93 -16
  23. data/lib/action_controller/metal/params_wrapper.rb +4 -11
  24. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  25. data/lib/action_controller/metal/redirecting.rb +20 -20
  26. data/lib/action_controller/metal/renderers.rb +8 -5
  27. data/lib/action_controller/metal/rendering.rb +14 -11
  28. data/lib/action_controller/metal/request_forgery_protection.rb +67 -13
  29. data/lib/action_controller/metal/responder.rb +12 -2
  30. data/lib/action_controller/metal/streaming.rb +18 -20
  31. data/lib/action_controller/metal/strong_parameters.rb +22 -34
  32. data/lib/action_controller/railtie.rb +0 -1
  33. data/lib/action_controller/test_case.rb +0 -15
  34. data/lib/action_dispatch.rb +1 -0
  35. data/lib/action_dispatch/http/headers.rb +1 -3
  36. data/lib/action_dispatch/http/mime_negotiation.rb +16 -2
  37. data/lib/action_dispatch/http/mime_type.rb +4 -22
  38. data/lib/action_dispatch/http/mime_types.rb +1 -0
  39. data/lib/action_dispatch/http/parameters.rb +18 -19
  40. data/lib/action_dispatch/http/request.rb +16 -25
  41. data/lib/action_dispatch/http/response.rb +21 -8
  42. data/lib/action_dispatch/http/upload.rb +0 -13
  43. data/lib/action_dispatch/http/url.rb +10 -18
  44. data/lib/action_dispatch/journey/formatter.rb +3 -3
  45. data/lib/action_dispatch/journey/gtg/transition_table.rb +3 -5
  46. data/lib/action_dispatch/journey/parser.rb +1 -1
  47. data/lib/action_dispatch/journey/parser.y +1 -0
  48. data/lib/action_dispatch/journey/router.rb +7 -1
  49. data/lib/action_dispatch/journey/router/utils.rb +1 -1
  50. data/lib/action_dispatch/journey/visitors.rb +26 -47
  51. data/lib/action_dispatch/middleware/callbacks.rb +6 -6
  52. data/lib/action_dispatch/middleware/cookies.rb +15 -15
  53. data/lib/action_dispatch/middleware/debug_exceptions.rb +21 -13
  54. data/lib/action_dispatch/middleware/exception_wrapper.rb +1 -1
  55. data/lib/action_dispatch/middleware/flash.rb +5 -11
  56. data/lib/action_dispatch/middleware/params_parser.rb +1 -1
  57. data/lib/action_dispatch/middleware/public_exceptions.rb +1 -5
  58. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  59. data/lib/action_dispatch/middleware/session/cookie_store.rb +4 -3
  60. data/lib/action_dispatch/middleware/show_exceptions.rb +5 -2
  61. data/lib/action_dispatch/middleware/ssl.rb +1 -1
  62. data/lib/action_dispatch/middleware/static.rb +5 -25
  63. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  64. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  65. data/lib/action_dispatch/middleware/templates/rescues/{_trace.erb → _trace.html.erb} +0 -0
  66. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +15 -0
  67. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +1 -1
  68. data/lib/action_dispatch/middleware/templates/rescues/{missing_template.erb → missing_template.html.erb} +1 -1
  69. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  70. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +1 -1
  71. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  72. data/lib/action_dispatch/middleware/templates/rescues/{template_error.erb → template_error.html.erb} +1 -1
  73. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +8 -0
  74. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  75. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  76. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -3
  77. data/lib/action_dispatch/railtie.rb +1 -2
  78. data/lib/action_dispatch/request/session.rb +12 -0
  79. data/lib/action_dispatch/request/utils.rb +24 -0
  80. data/lib/action_dispatch/routing.rb +7 -6
  81. data/lib/action_dispatch/routing/inspector.rb +4 -4
  82. data/lib/action_dispatch/routing/mapper.rb +81 -138
  83. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -0
  84. data/lib/action_dispatch/routing/redirection.rb +34 -27
  85. data/lib/action_dispatch/routing/route_set.rb +43 -37
  86. data/lib/action_dispatch/routing/url_for.rb +3 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -15
  88. data/lib/action_dispatch/testing/assertions/selector.rb +4 -4
  89. data/lib/action_dispatch/testing/integration.rb +1 -7
  90. data/lib/action_pack/version.rb +1 -1
  91. metadata +43 -167
  92. data/lib/abstract_controller/layouts.rb +0 -423
  93. data/lib/abstract_controller/view_paths.rb +0 -96
  94. data/lib/action_controller/deprecated.rb +0 -7
  95. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  96. data/lib/action_controller/record_identifier.rb +0 -31
  97. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  98. data/lib/action_view.rb +0 -93
  99. data/lib/action_view/base.rb +0 -205
  100. data/lib/action_view/buffers.rb +0 -49
  101. data/lib/action_view/context.rb +0 -36
  102. data/lib/action_view/dependency_tracker.rb +0 -93
  103. data/lib/action_view/digestor.rb +0 -113
  104. data/lib/action_view/flows.rb +0 -76
  105. data/lib/action_view/helpers.rb +0 -58
  106. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  107. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  108. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  109. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  110. data/lib/action_view/helpers/cache_helper.rb +0 -196
  111. data/lib/action_view/helpers/capture_helper.rb +0 -216
  112. data/lib/action_view/helpers/controller_helper.rb +0 -25
  113. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  114. data/lib/action_view/helpers/date_helper.rb +0 -1087
  115. data/lib/action_view/helpers/debug_helper.rb +0 -39
  116. data/lib/action_view/helpers/form_helper.rb +0 -1882
  117. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  118. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  119. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  120. data/lib/action_view/helpers/number_helper.rb +0 -451
  121. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  122. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  123. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  124. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  125. data/lib/action_view/helpers/tag_helper.rb +0 -173
  126. data/lib/action_view/helpers/tags.rb +0 -39
  127. data/lib/action_view/helpers/tags/base.rb +0 -148
  128. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  129. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  130. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -53
  131. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  132. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  133. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  134. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  135. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  136. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  137. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  138. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  139. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  140. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  141. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  142. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  143. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  144. data/lib/action_view/helpers/tags/label.rb +0 -65
  145. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  146. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  147. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  148. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  149. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  150. data/lib/action_view/helpers/tags/search_field.rb +0 -22
  151. data/lib/action_view/helpers/tags/select.rb +0 -40
  152. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  153. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  154. data/lib/action_view/helpers/tags/text_field.rb +0 -30
  155. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  156. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  157. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  158. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  159. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  160. data/lib/action_view/helpers/text_helper.rb +0 -448
  161. data/lib/action_view/helpers/translation_helper.rb +0 -112
  162. data/lib/action_view/helpers/url_helper.rb +0 -635
  163. data/lib/action_view/locale/en.yml +0 -56
  164. data/lib/action_view/log_subscriber.rb +0 -30
  165. data/lib/action_view/lookup_context.rb +0 -248
  166. data/lib/action_view/model_naming.rb +0 -12
  167. data/lib/action_view/path_set.rb +0 -77
  168. data/lib/action_view/railtie.rb +0 -43
  169. data/lib/action_view/record_identifier.rb +0 -84
  170. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  171. data/lib/action_view/renderer/partial_renderer.rb +0 -500
  172. data/lib/action_view/renderer/renderer.rb +0 -50
  173. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  174. data/lib/action_view/renderer/template_renderer.rb +0 -96
  175. data/lib/action_view/routing_url_for.rb +0 -107
  176. data/lib/action_view/tasks/dependencies.rake +0 -17
  177. data/lib/action_view/template.rb +0 -339
  178. data/lib/action_view/template/error.rb +0 -138
  179. data/lib/action_view/template/handlers.rb +0 -53
  180. data/lib/action_view/template/handlers/builder.rb +0 -26
  181. data/lib/action_view/template/handlers/erb.rb +0 -146
  182. data/lib/action_view/template/handlers/raw.rb +0 -11
  183. data/lib/action_view/template/resolver.rb +0 -340
  184. data/lib/action_view/template/text.rb +0 -34
  185. data/lib/action_view/template/types.rb +0 -57
  186. data/lib/action_view/test_case.rb +0 -270
  187. data/lib/action_view/testing/resolvers.rb +0 -50
  188. data/lib/action_view/vendor/html-scanner.rb +0 -20
  189. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  190. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  191. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  192. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  193. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  194. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
@@ -1,9 +1,10 @@
1
+ require 'action_view'
1
2
  require "action_controller/log_subscriber"
2
3
  require "action_controller/metal/params_wrapper"
3
4
 
4
5
  module ActionController
5
6
  # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
6
- # on request and then either render a template or redirect to another action. An action is defined as a public method
7
+ # on request and then either it renders a template or redirects to another action. An action is defined as a public method
7
8
  # on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
8
9
  #
9
10
  # By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
@@ -200,7 +201,7 @@ module ActionController
200
201
  end
201
202
 
202
203
  MODULES = [
203
- AbstractController::Layouts,
204
+ AbstractController::Rendering,
204
205
  AbstractController::Translation,
205
206
  AbstractController::AssetPaths,
206
207
 
@@ -208,6 +209,7 @@ module ActionController
208
209
  HideActions,
209
210
  UrlFor,
210
211
  Redirecting,
212
+ ActionView::Layouts,
211
213
  Rendering,
212
214
  Renderers::All,
213
215
  ConditionalGet,
@@ -223,7 +225,6 @@ module ActionController
223
225
  ForceSSL,
224
226
  Streaming,
225
227
  DataStreaming,
226
- RecordIdentifier,
227
228
  HttpAuthentication::Basic::ControllerMethods,
228
229
  HttpAuthentication::Digest::ControllerMethods,
229
230
  HttpAuthentication::Token::ControllerMethods,
@@ -249,10 +250,17 @@ module ActionController
249
250
  end
250
251
 
251
252
  # Define some internal variables that should not be propagated to the view.
252
- self.protected_instance_variables = [
253
+ PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
253
254
  :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
254
- :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
255
- ]
255
+ :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
256
+
257
+ def _protected_ivars # :nodoc:
258
+ PROTECTED_IVARS
259
+ end
260
+
261
+ def self.protected_instance_variables
262
+ PROTECTED_IVARS
263
+ end
256
264
 
257
265
  ActiveSupport.run_load_hooks(:action_controller, self)
258
266
  end
@@ -9,7 +9,7 @@ module ActionController
9
9
  # You can read more about each approach by clicking the modules below.
10
10
  #
11
11
  # Note: To turn off all caching, set
12
- # config.action_controller.perform_caching = false.
12
+ # config.action_controller.perform_caching = false
13
13
  #
14
14
  # == \Caching stores
15
15
  #
@@ -58,16 +58,6 @@ module ActionController
58
58
  config_accessor :default_static_extension
59
59
  self.default_static_extension ||= '.html'
60
60
 
61
- def self.page_cache_extension=(extension)
62
- ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
63
- self.default_static_extension = extension
64
- end
65
-
66
- def self.page_cache_extension
67
- ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
68
- default_static_extension
69
- end
70
-
71
61
  config_accessor :perform_caching
72
62
  self.perform_caching = true if perform_caching.nil?
73
63
 
@@ -33,7 +33,7 @@ module ActionController
33
33
  end
34
34
 
35
35
  def halted_callback(event)
36
- info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
36
+ info("Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected")
37
37
  end
38
38
 
39
39
  def send_file(event)
@@ -231,9 +231,5 @@ module ActionController
231
231
  new.dispatch(name, klass.new(env))
232
232
  end
233
233
  end
234
-
235
- def _status_code
236
- @_status
237
- end
238
234
  end
239
235
  end
@@ -11,6 +11,23 @@ module ActionController #:nodoc:
11
11
  end
12
12
 
13
13
  module ClassMethods
14
+ # Creates new flash types. You can pass as many types as you want to create
15
+ # flash types other than the default <tt>alert</tt> and <tt>notice</tt> in
16
+ # your controllers and views. For instance:
17
+ #
18
+ # # in application_controller.rb
19
+ # class ApplicationController < ActionController::Base
20
+ # add_flash_types :warning
21
+ # end
22
+ #
23
+ # # in your controller
24
+ # redirect_to user_path(@user), warning: "Incomplete profile"
25
+ #
26
+ # # in your view
27
+ # <%= warning %>
28
+ #
29
+ # This method will automatically define a new method for each of the given
30
+ # names, and it will be available in your views.
14
31
  def add_flash_types(*types)
15
32
  types.each do |type|
16
33
  next if _flash_types.include?(type)
@@ -48,7 +48,7 @@ module ActionController
48
48
  # You can pass any of the following options to affect the redirect status and response
49
49
  # * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently)
50
50
  # * <tt>flash</tt> - Set a flash message when redirecting
51
- # * <tt>alert</tt> - Set a alert message when redirecting
51
+ # * <tt>alert</tt> - Set an alert message when redirecting
52
52
  # * <tt>notice</tt> - Set a notice message when redirecting
53
53
  #
54
54
  # ==== Action Options
@@ -1,7 +1,5 @@
1
1
  module ActionController
2
2
  module Head
3
- extend ActiveSupport::Concern
4
-
5
3
  # Return a response that has no content (merely headers). The options
6
4
  # argument is interpreted to be a hash of header names and values.
7
5
  # This allows you to easily return a response that consists only of
@@ -29,7 +27,7 @@ module ActionController
29
27
  self.status = status
30
28
  self.location = url_for(location) if location
31
29
 
32
- if include_content?(self._status_code)
30
+ if include_content?(self.status)
33
31
  self.content_type = content_type || (Mime[formats.first] if formats)
34
32
  self.response.charset = false if self.response
35
33
  self.response_body = " "
@@ -5,7 +5,7 @@ module ActionController
5
5
  #
6
6
  # In addition to using the standard template helpers provided, creating custom helpers to
7
7
  # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
8
- # will include all helpers.
8
+ # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>
9
9
  #
10
10
  # In previous versions of \Rails the controller will include a helper whose
11
11
  # name matches that of the controller, e.g., <tt>MyController</tt> will automatically
@@ -73,7 +73,11 @@ module ActionController
73
73
 
74
74
  # Provides a proxy to access helpers methods from outside the view.
75
75
  def helpers
76
- @helper_proxy ||= ActionView::Base.new.extend(_helpers)
76
+ @helper_proxy ||= begin
77
+ proxy = ActionView::Base.new
78
+ proxy.config = config.inheritable_copy
79
+ proxy.extend(_helpers)
80
+ end
77
81
  end
78
82
 
79
83
  # Overwrite modules_for_helpers to accept :all as argument, which loads
@@ -96,7 +96,7 @@ module ActionController
96
96
  end
97
97
 
98
98
  def user_name_and_password(request)
99
- decode_credentials(request).split(':', 2)
99
+ decode_credentials(request).split(/:/, 2)
100
100
  end
101
101
 
102
102
  def decode_credentials(request)
@@ -109,8 +109,8 @@ module ActionController
109
109
 
110
110
  def authentication_request(controller, realm)
111
111
  controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
112
- controller.status = 401
113
112
  controller.response_body = "HTTP Basic: Access denied.\n"
113
+ controller.status = 401
114
114
  end
115
115
  end
116
116
 
@@ -244,8 +244,8 @@ module ActionController
244
244
  def authentication_request(controller, realm, message = nil)
245
245
  message ||= "HTTP Digest: Access denied.\n"
246
246
  authentication_header(controller, realm)
247
- controller.status = 401
248
247
  controller.response_body = message
248
+ controller.status = 401
249
249
  end
250
250
 
251
251
  def secret_token(request)
@@ -385,7 +385,6 @@ module ActionController
385
385
  #
386
386
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
387
387
  module Token
388
- TOKEN_KEY = 'token='
389
388
  TOKEN_REGEX = /^Token /
390
389
  AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
391
390
  extend self
@@ -438,7 +437,7 @@ module ActionController
438
437
  authorization_request = request.authorization.to_s
439
438
  if authorization_request[TOKEN_REGEX]
440
439
  params = token_params_from authorization_request
441
- [params.shift[1], Hash[params].with_indifferent_access]
440
+ [params.shift.last, Hash[params].with_indifferent_access]
442
441
  end
443
442
  end
444
443
 
@@ -453,20 +452,14 @@ module ActionController
453
452
 
454
453
  # This removes the `"` characters wrapping the value.
455
454
  def rewrite_param_values(array_params)
456
- array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
455
+ array_params.each { |param| param.last.gsub! %r/^"|"$/, '' }
457
456
  end
458
457
 
459
458
  # This method takes an authorization body and splits up the key-value
460
459
  # pairs by the standardized `:`, `;`, or `\t` delimiters defined in
461
460
  # `AUTHN_PAIR_DELIMITERS`.
462
461
  def raw_params(auth)
463
- _raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
464
-
465
- if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
466
- _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
467
- end
468
-
469
- _raw_params
462
+ auth.sub(TOKEN_REGEX, '').split(/"\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
470
463
  end
471
464
 
472
465
  # Encodes the given token and options into an Authorization header value.
@@ -476,7 +469,7 @@ module ActionController
476
469
  #
477
470
  # Returns String.
478
471
  def encode_credentials(token, options = {})
479
- values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
472
+ values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
480
473
  "#{key}=#{value.to_s.inspect}"
481
474
  end
482
475
  "Token #{values * ", "}"
@@ -67,7 +67,7 @@ module ActionController
67
67
 
68
68
  private
69
69
 
70
- # A hook invoked everytime a before callback is halted.
70
+ # A hook invoked every time a before callback is halted.
71
71
  def halted_callback_hook(filter)
72
72
  ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
73
73
  end
@@ -1,5 +1,6 @@
1
1
  require 'action_dispatch/http/response'
2
2
  require 'delegate'
3
+ require 'active_support/json'
3
4
 
4
5
  module ActionController
5
6
  # Mix this module in to your controller, and all actions in that controller
@@ -32,6 +33,79 @@ module ActionController
32
33
  # the main thread. Make sure your actions are thread safe, and this shouldn't
33
34
  # be a problem (don't share state across threads, etc).
34
35
  module Live
36
+ # This class provides the ability to write an SSE (Server Sent Event)
37
+ # to an IO stream. The class is initialized with a stream and can be used
38
+ # to either write a JSON string or an object which can be converted to JSON.
39
+ #
40
+ # Writing an object will convert it into standard SSE format with whatever
41
+ # options you have configured. You may choose to set the following options:
42
+ #
43
+ # 1) Event. If specified, an event with this name will be dispatched on
44
+ # the browser.
45
+ # 2) Retry. The reconnection time in milliseconds used when attempting
46
+ # to send the event.
47
+ # 3) Id. If the connection dies while sending an SSE to the browser, then
48
+ # the server will receive a +Last-Event-ID+ header with value equal to +id+.
49
+ #
50
+ # After setting an option in the constructor of the SSE object, all future
51
+ # SSEs sent across the stream will use those options unless overridden.
52
+ #
53
+ # Example Usage:
54
+ #
55
+ # class MyController < ActionController::Base
56
+ # include ActionController::Live
57
+ #
58
+ # def index
59
+ # response.headers['Content-Type'] = 'text/event-stream'
60
+ # sse = SSE.new(response.stream, retry: 300, event: "event-name")
61
+ # sse.write({ name: 'John'})
62
+ # sse.write({ name: 'John'}, id: 10)
63
+ # sse.write({ name: 'John'}, id: 10, event: "other-event")
64
+ # sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
65
+ # ensure
66
+ # sse.close
67
+ # end
68
+ # end
69
+ #
70
+ # Note: SSEs are not currently supported by IE. However, they are supported
71
+ # by Chrome, Firefox, Opera, and Safari.
72
+ class SSE
73
+
74
+ WHITELISTED_OPTIONS = %w( retry event id )
75
+
76
+ def initialize(stream, options = {})
77
+ @stream = stream
78
+ @options = options
79
+ end
80
+
81
+ def close
82
+ @stream.close
83
+ end
84
+
85
+ def write(object, options = {})
86
+ case object
87
+ when String
88
+ perform_write(object, options)
89
+ else
90
+ perform_write(ActiveSupport::JSON.encode(object), options)
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def perform_write(json, options)
97
+ current_options = @options.merge(options).stringify_keys
98
+
99
+ WHITELISTED_OPTIONS.each do |option_name|
100
+ if (option_value = current_options[option_name])
101
+ @stream.write "#{option_name}: #{option_value}\n"
102
+ end
103
+ end
104
+
105
+ @stream.write "data: #{json}\n\n"
106
+ end
107
+ end
108
+
35
109
  class Buffer < ActionDispatch::Response::Buffer #:nodoc:
36
110
  def initialize(response)
37
111
  @error_callback = nil
@@ -181,13 +181,50 @@ module ActionController #:nodoc:
181
181
  # end
182
182
  # end
183
183
  #
184
+ # Formats can have different variants.
185
+ #
186
+ # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
187
+ # <tt>:phone</tt>, or <tt>:desktop</tt>.
188
+ #
189
+ # We often want to render different html/json/xml templates for phones,
190
+ # tablets, and desktop browsers. Variants make it easy.
191
+ #
192
+ # You can set the variant in a +before_action+:
193
+ #
194
+ # request.variant = :tablet if request.user_agent =~ /iPad/
195
+ #
196
+ # Respond to variants in the action just like you respond to formats:
197
+ #
198
+ # respond_to do |format|
199
+ # format.html do |variant|
200
+ # variant.tablet # renders app/views/projects/show.html+tablet.erb
201
+ # variant.phone { extra_setup; render ... }
202
+ # variant.none { special_setup } # executed only if there is no variant set
203
+ # end
204
+ # end
205
+ #
206
+ # Provide separate templates for each format and variant:
207
+ #
208
+ # app/views/projects/show.html.erb
209
+ # app/views/projects/show.html+tablet.erb
210
+ # app/views/projects/show.html+phone.erb
211
+ #
212
+ # When you're not sharing any code within the format, you can simplify defining variants
213
+ # using the inline syntax:
214
+ #
215
+ # respond_to do |format|
216
+ # format.js { render "trash" }
217
+ # format.html.phone { redirect_to progress_path }
218
+ # format.html.none { render "trash" }
219
+ # end
220
+ #
184
221
  # Be sure to check the documentation of +respond_with+ and
185
222
  # <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
186
223
  def respond_to(*mimes, &block)
187
224
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
188
225
 
189
226
  if collector = retrieve_collector_from_mimes(mimes, &block)
190
- response = collector.response
227
+ response = collector.response(request.variant)
191
228
  response ? response.call : render({})
192
229
  end
193
230
  end
@@ -260,7 +297,7 @@ module ActionController #:nodoc:
260
297
  # * for other requests - i.e. data formats such as xml, json, csv etc, if
261
298
  # the resource passed to +respond_with+ responds to <code>to_<format></code>,
262
299
  # the method attempts to render the resource in the requested format
263
- # directly, e.g. for an xml request, the response is equivalent to calling
300
+ # directly, e.g. for an xml request, the response is equivalent to calling
264
301
  # <code>render xml: resource</code>.
265
302
  #
266
303
  # === Nested resources
@@ -321,12 +358,15 @@ module ActionController #:nodoc:
321
358
  # 2. <tt>:action</tt> - overwrites the default render action used after an
322
359
  # unsuccessful html +post+ request.
323
360
  def respond_with(*resources, &block)
324
- raise "In order to use respond_with, first you need to declare the formats your " \
325
- "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
361
+ if self.class.mimes_for_respond_to.empty?
362
+ raise "In order to use respond_with, first you need to declare the " \
363
+ "formats your controller responds to in the class level."
364
+ end
326
365
 
327
366
  if collector = retrieve_collector_from_mimes(&block)
328
367
  options = resources.size == 1 ? {} : resources.extract_options!
329
- options[:default_response] = collector.response
368
+ options = options.clone
369
+ options[:default_response] = collector.response(request.variant)
330
370
  (options.delete(:responder) || self.class.responder).call(self, resources, options)
331
371
  end
332
372
  end
@@ -364,9 +404,7 @@ module ActionController #:nodoc:
364
404
  format = collector.negotiate_format(request)
365
405
 
366
406
  if format
367
- self.content_type ||= format.to_s
368
- lookup_context.formats = [format.to_sym]
369
- lookup_context.rendered_format = lookup_context.formats.first
407
+ _process_format(format)
370
408
  collector
371
409
  else
372
410
  raise ActionController::UnknownFormat
@@ -397,11 +435,12 @@ module ActionController #:nodoc:
397
435
  # request, with this response then being accessible by calling #response.
398
436
  class Collector
399
437
  include AbstractController::Collector
400
- attr_accessor :order, :format
438
+ attr_accessor :format
401
439
 
402
440
  def initialize(mimes)
403
- @order, @responses = [], {}
404
- mimes.each { |mime| send(mime) }
441
+ @responses = {}
442
+
443
+ mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
405
444
  end
406
445
 
407
446
  def any(*args, &block)
@@ -415,16 +454,54 @@ module ActionController #:nodoc:
415
454
 
416
455
  def custom(mime_type, &block)
417
456
  mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
418
- @order << mime_type
419
- @responses[mime_type] ||= block
457
+ @responses[mime_type] ||= if block_given?
458
+ block
459
+ else
460
+ VariantCollector.new
461
+ end
420
462
  end
421
463
 
422
- def response
423
- @responses.fetch(format, @responses[Mime::ALL])
464
+ def response(variant)
465
+ response = @responses.fetch(format, @responses[Mime::ALL])
466
+ if response.is_a?(VariantCollector)
467
+ response.variant(variant)
468
+ elsif response.nil? || response.arity == 0
469
+ response
470
+ else
471
+ lambda { response.call VariantFilter.new(variant) }
472
+ end
424
473
  end
425
474
 
426
475
  def negotiate_format(request)
427
- @format = request.negotiate_mime(order)
476
+ @format = request.negotiate_mime(@responses.keys)
477
+ end
478
+
479
+ #Used for inline syntax
480
+ class VariantCollector #:nodoc:
481
+ def initialize
482
+ @variants = {}
483
+ end
484
+
485
+ def method_missing(name, *args, &block)
486
+ @variants[name] = block if block_given?
487
+ end
488
+
489
+ def variant(name)
490
+ @variants[name.nil? ? :none : name]
491
+ end
492
+ end
493
+
494
+ #Used for nested block syntax
495
+ class VariantFilter #:nodoc:
496
+ def initialize(variant)
497
+ @variant = variant
498
+ end
499
+
500
+ def method_missing(name)
501
+ if block_given?
502
+ yield if name == @variant || (name == :none && @variant.nil?)
503
+ end
504
+ end
428
505
  end
429
506
  end
430
507
  end