actionpack 4.2.11.3 → 5.0.0.beta1

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

Potentially problematic release.


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

Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +379 -462
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller.rb +0 -2
  6. data/lib/abstract_controller/base.rb +17 -32
  7. data/lib/abstract_controller/callbacks.rb +52 -19
  8. data/lib/abstract_controller/collector.rb +4 -9
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  11. data/lib/abstract_controller/rendering.rb +27 -22
  12. data/lib/abstract_controller/translation.rb +8 -7
  13. data/lib/action_controller.rb +4 -3
  14. data/lib/action_controller/api.rb +146 -0
  15. data/lib/action_controller/base.rb +6 -10
  16. data/lib/action_controller/caching.rb +1 -3
  17. data/lib/action_controller/caching/fragments.rb +48 -3
  18. data/lib/action_controller/form_builder.rb +48 -0
  19. data/lib/action_controller/log_subscriber.rb +1 -10
  20. data/lib/action_controller/metal.rb +89 -62
  21. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  22. data/lib/action_controller/metal/conditional_get.rb +65 -24
  23. data/lib/action_controller/metal/cookies.rb +0 -2
  24. data/lib/action_controller/metal/data_streaming.rb +2 -22
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  26. data/lib/action_controller/metal/exceptions.rb +11 -6
  27. data/lib/action_controller/metal/force_ssl.rb +6 -6
  28. data/lib/action_controller/metal/head.rb +14 -7
  29. data/lib/action_controller/metal/helpers.rb +9 -5
  30. data/lib/action_controller/metal/http_authentication.rb +37 -38
  31. data/lib/action_controller/metal/implicit_render.rb +23 -6
  32. data/lib/action_controller/metal/instrumentation.rb +0 -1
  33. data/lib/action_controller/metal/live.rb +17 -55
  34. data/lib/action_controller/metal/mime_responds.rb +17 -37
  35. data/lib/action_controller/metal/params_wrapper.rb +8 -8
  36. data/lib/action_controller/metal/redirecting.rb +32 -9
  37. data/lib/action_controller/metal/renderers.rb +10 -8
  38. data/lib/action_controller/metal/rendering.rb +38 -6
  39. data/lib/action_controller/metal/request_forgery_protection.rb +67 -35
  40. data/lib/action_controller/metal/rescue.rb +2 -4
  41. data/lib/action_controller/metal/streaming.rb +4 -4
  42. data/lib/action_controller/metal/strong_parameters.rb +231 -78
  43. data/lib/action_controller/metal/testing.rb +1 -12
  44. data/lib/action_controller/metal/url_for.rb +12 -5
  45. data/lib/action_controller/renderer.rb +111 -0
  46. data/lib/action_controller/template_assertions.rb +9 -0
  47. data/lib/action_controller/test_case.rb +267 -363
  48. data/lib/action_dispatch.rb +2 -1
  49. data/lib/action_dispatch/http/cache.rb +23 -26
  50. data/lib/action_dispatch/http/filter_parameters.rb +6 -8
  51. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  52. data/lib/action_dispatch/http/headers.rb +28 -11
  53. data/lib/action_dispatch/http/mime_negotiation.rb +40 -26
  54. data/lib/action_dispatch/http/mime_type.rb +92 -61
  55. data/lib/action_dispatch/http/mime_types.rb +1 -4
  56. data/lib/action_dispatch/http/parameter_filter.rb +18 -8
  57. data/lib/action_dispatch/http/parameters.rb +45 -41
  58. data/lib/action_dispatch/http/request.rb +146 -82
  59. data/lib/action_dispatch/http/response.rb +180 -99
  60. data/lib/action_dispatch/http/url.rb +117 -8
  61. data/lib/action_dispatch/journey/formatter.rb +34 -28
  62. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  64. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  65. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  66. data/lib/action_dispatch/journey/parser_extras.rb +4 -0
  67. data/lib/action_dispatch/journey/path/pattern.rb +37 -41
  68. data/lib/action_dispatch/journey/route.rb +71 -17
  69. data/lib/action_dispatch/journey/router.rb +5 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  71. data/lib/action_dispatch/journey/routes.rb +14 -15
  72. data/lib/action_dispatch/journey/visitors.rb +86 -43
  73. data/lib/action_dispatch/middleware/cookies.rb +184 -135
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +115 -45
  75. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -20
  76. data/lib/action_dispatch/middleware/flash.rb +61 -45
  77. data/lib/action_dispatch/middleware/load_interlock.rb +21 -0
  78. data/lib/action_dispatch/middleware/params_parser.rb +30 -46
  79. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  80. data/lib/action_dispatch/middleware/reloader.rb +2 -4
  81. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  82. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  83. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  84. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  85. data/lib/action_dispatch/middleware/session/cookie_store.rb +29 -23
  86. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  87. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  88. data/lib/action_dispatch/middleware/ssl.rb +93 -36
  89. data/lib/action_dispatch/middleware/stack.rb +43 -48
  90. data/lib/action_dispatch/middleware/static.rb +52 -40
  91. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  92. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  98. data/lib/action_dispatch/railtie.rb +0 -2
  99. data/lib/action_dispatch/request/session.rb +66 -34
  100. data/lib/action_dispatch/request/utils.rb +51 -19
  101. data/lib/action_dispatch/routing.rb +3 -8
  102. data/lib/action_dispatch/routing/inspector.rb +6 -30
  103. data/lib/action_dispatch/routing/mapper.rb +447 -322
  104. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  105. data/lib/action_dispatch/routing/redirection.rb +3 -3
  106. data/lib/action_dispatch/routing/route_set.rb +124 -227
  107. data/lib/action_dispatch/routing/url_for.rb +27 -10
  108. data/lib/action_dispatch/testing/assertions.rb +1 -1
  109. data/lib/action_dispatch/testing/assertions/response.rb +27 -9
  110. data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
  111. data/lib/action_dispatch/testing/integration.rb +237 -76
  112. data/lib/action_dispatch/testing/test_process.rb +5 -5
  113. data/lib/action_dispatch/testing/test_request.rb +12 -21
  114. data/lib/action_dispatch/testing/test_response.rb +1 -4
  115. data/lib/action_pack.rb +1 -1
  116. data/lib/action_pack/gem_version.rb +4 -4
  117. metadata +26 -25
  118. data/lib/action_controller/metal/hide_actions.rb +0 -40
  119. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  120. data/lib/action_controller/middleware.rb +0 -39
  121. data/lib/action_controller/model_naming.rb +0 -12
  122. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  123. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  124. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  125. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2004-2014 David Heinemeier Hansson
2
+ # Copyright (c) 2004-2015 David Heinemeier Hansson
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -52,6 +52,7 @@ module ActionDispatch
52
52
  autoload :DebugExceptions
53
53
  autoload :ExceptionWrapper
54
54
  autoload :Flash
55
+ autoload :LoadInterlock
55
56
  autoload :ParamsParser
56
57
  autoload :PublicExceptions
57
58
  autoload :Reloader
@@ -1,4 +1,3 @@
1
-
2
1
  module ActionDispatch
3
2
  module Http
4
3
  module Cache
@@ -8,13 +7,13 @@ module ActionDispatch
8
7
  HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
9
8
 
10
9
  def if_modified_since
11
- if since = env[HTTP_IF_MODIFIED_SINCE]
10
+ if since = get_header(HTTP_IF_MODIFIED_SINCE)
12
11
  Time.rfc2822(since) rescue nil
13
12
  end
14
13
  end
15
14
 
16
15
  def if_none_match
17
- env[HTTP_IF_NONE_MATCH]
16
+ get_header HTTP_IF_NONE_MATCH
18
17
  end
19
18
 
20
19
  def if_none_match_etags
@@ -51,52 +50,51 @@ module ActionDispatch
51
50
  end
52
51
 
53
52
  module Response
54
- attr_reader :cache_control, :etag
55
- alias :etag? :etag
53
+ attr_reader :cache_control
56
54
 
57
55
  def last_modified
58
- if last = headers[LAST_MODIFIED]
56
+ if last = get_header(LAST_MODIFIED)
59
57
  Time.httpdate(last)
60
58
  end
61
59
  end
62
60
 
63
61
  def last_modified?
64
- headers.include?(LAST_MODIFIED)
62
+ has_header? LAST_MODIFIED
65
63
  end
66
64
 
67
65
  def last_modified=(utc_time)
68
- headers[LAST_MODIFIED] = utc_time.httpdate
66
+ set_header LAST_MODIFIED, utc_time.httpdate
69
67
  end
70
68
 
71
69
  def date
72
- if date_header = headers[DATE]
70
+ if date_header = get_header(DATE)
73
71
  Time.httpdate(date_header)
74
72
  end
75
73
  end
76
74
 
77
75
  def date?
78
- headers.include?(DATE)
76
+ has_header? DATE
79
77
  end
80
78
 
81
79
  def date=(utc_time)
82
- headers[DATE] = utc_time.httpdate
80
+ set_header DATE, utc_time.httpdate
83
81
  end
84
82
 
85
83
  def etag=(etag)
86
84
  key = ActiveSupport::Cache.expand_cache_key(etag)
87
- @etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
85
+ super %("#{Digest::MD5.hexdigest(key)}")
88
86
  end
89
87
 
88
+ def etag?; etag; end
89
+
90
90
  private
91
91
 
92
92
  DATE = 'Date'.freeze
93
93
  LAST_MODIFIED = "Last-Modified".freeze
94
- ETAG = "ETag".freeze
95
- CACHE_CONTROL = "Cache-Control".freeze
96
94
  SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public must-revalidate])
97
95
 
98
96
  def cache_control_segments
99
- if cache_control = self[CACHE_CONTROL]
97
+ if cache_control = _cache_control
100
98
  cache_control.delete(' ').split(',')
101
99
  else
102
100
  []
@@ -123,12 +121,11 @@ module ActionDispatch
123
121
 
124
122
  def prepare_cache_control!
125
123
  @cache_control = cache_control_headers
126
- @etag = self[ETAG]
127
124
  end
128
125
 
129
126
  def handle_conditional_get!
130
127
  if etag? || last_modified? || !@cache_control.empty?
131
- set_conditional_cache_control!
128
+ set_conditional_cache_control!(@cache_control)
132
129
  end
133
130
  end
134
131
 
@@ -138,24 +135,24 @@ module ActionDispatch
138
135
  PRIVATE = "private".freeze
139
136
  MUST_REVALIDATE = "must-revalidate".freeze
140
137
 
141
- def set_conditional_cache_control!
138
+ def set_conditional_cache_control!(cache_control)
142
139
  control = {}
143
140
  cc_headers = cache_control_headers
144
141
  if extras = cc_headers.delete(:extras)
145
- @cache_control[:extras] ||= []
146
- @cache_control[:extras] += extras
147
- @cache_control[:extras].uniq!
142
+ cache_control[:extras] ||= []
143
+ cache_control[:extras] += extras
144
+ cache_control[:extras].uniq!
148
145
  end
149
146
 
150
147
  control.merge! cc_headers
151
- control.merge! @cache_control
148
+ control.merge! cache_control
152
149
 
153
150
  if control.empty?
154
- headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
151
+ self._cache_control = DEFAULT_CACHE_CONTROL
155
152
  elsif control[:no_cache]
156
- headers[CACHE_CONTROL] = NO_CACHE
153
+ self._cache_control = NO_CACHE
157
154
  if control[:extras]
158
- headers[CACHE_CONTROL] += ", #{control[:extras].join(', ')}"
155
+ self._cache_control = _cache_control + ", #{control[:extras].join(', ')}"
159
156
  end
160
157
  else
161
158
  extras = control[:extras]
@@ -167,7 +164,7 @@ module ActionDispatch
167
164
  options << MUST_REVALIDATE if control[:must_revalidate]
168
165
  options.concat(extras) if extras
169
166
 
170
- headers[CACHE_CONTROL] = options.join(", ")
167
+ self._cache_control = options.join(", ")
171
168
  end
172
169
  end
173
170
  end
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/object/duplicable'
3
1
  require 'action_dispatch/http/parameter_filter'
4
2
 
5
3
  module ActionDispatch
@@ -16,7 +14,7 @@ module ActionDispatch
16
14
  # env["action_dispatch.parameter_filter"] = [:foo, "bar"]
17
15
  # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
18
16
  #
19
- # env["action_dispatch.parameter_filter"] = lambda do |k,v|
17
+ # env["action_dispatch.parameter_filter"] = -> (k, v) do
20
18
  # v.reverse! if k =~ /secret/i
21
19
  # end
22
20
  # => reverses the value to all keys matching /secret/i
@@ -25,19 +23,19 @@ module ActionDispatch
25
23
  NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
26
24
  NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
27
25
 
28
- def initialize(env)
26
+ def initialize
29
27
  super
30
28
  @filtered_parameters = nil
31
29
  @filtered_env = nil
32
30
  @filtered_path = nil
33
31
  end
34
32
 
35
- # Return a hash of parameters with all sensitive data replaced.
33
+ # Returns a hash of parameters with all sensitive data replaced.
36
34
  def filtered_parameters
37
35
  @filtered_parameters ||= parameter_filter.filter(parameters)
38
36
  end
39
37
 
40
- # Return a hash of request.env with all sensitive data replaced.
38
+ # Returns a hash of request.env with all sensitive data replaced.
41
39
  def filtered_env
42
40
  @filtered_env ||= env_filter.filter(@env)
43
41
  end
@@ -50,13 +48,13 @@ module ActionDispatch
50
48
  protected
51
49
 
52
50
  def parameter_filter
53
- parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
51
+ parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
54
52
  return NULL_PARAM_FILTER
55
53
  }
56
54
  end
57
55
 
58
56
  def env_filter
59
- user_key = @env.fetch("action_dispatch.parameter_filter") {
57
+ user_key = fetch_header("action_dispatch.parameter_filter") {
60
58
  return NULL_ENV_FILTER
61
59
  }
62
60
  parameter_filter_for(Array(user_key) + ENV_MATCH)
@@ -4,9 +4,8 @@ module ActionDispatch
4
4
 
5
5
  FILTERED = '[FILTERED]'.freeze # :nodoc:
6
6
 
7
- def filtered_location
8
- filters = location_filter
9
- if !filters.empty? && location_filter_match?(filters)
7
+ def filtered_location # :nodoc:
8
+ if location_filter_match?
10
9
  FILTERED
11
10
  else
12
11
  location
@@ -15,20 +14,20 @@ module ActionDispatch
15
14
 
16
15
  private
17
16
 
18
- def location_filter
17
+ def location_filters
19
18
  if request
20
- request.env['action_dispatch.redirect_filter'] || []
19
+ request.get_header('action_dispatch.redirect_filter') || []
21
20
  else
22
21
  []
23
22
  end
24
23
  end
25
24
 
26
- def location_filter_match?(filters)
27
- filters.any? do |filter|
25
+ def location_filter_match?
26
+ location_filters.any? do |filter|
28
27
  if String === filter
29
28
  location.include?(filter)
30
29
  elsif Regexp === filter
31
- location.match(filter)
30
+ location =~ filter
32
31
  end
33
32
  end
34
33
  end
@@ -30,27 +30,37 @@ module ActionDispatch
30
30
  HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
31
31
 
32
32
  include Enumerable
33
- attr_reader :env
34
33
 
35
- def initialize(env = {}) # :nodoc:
36
- @env = env
34
+ def self.from_hash(hash)
35
+ new ActionDispatch::Request.new hash
36
+ end
37
+
38
+ def initialize(request) # :nodoc:
39
+ @req = request
37
40
  end
38
41
 
39
42
  # Returns the value for the given key mapped to @env.
40
43
  def [](key)
41
- @env[env_name(key)]
44
+ @req.get_header env_name(key)
42
45
  end
43
46
 
44
47
  # Sets the given value for the key mapped to @env.
45
48
  def []=(key, value)
46
- @env[env_name(key)] = value
49
+ @req.set_header env_name(key), value
50
+ end
51
+
52
+ # Add a value to a multivalued header like Vary or Accept-Encoding.
53
+ def add(key, value)
54
+ @req.add_header env_name(key), value
47
55
  end
48
56
 
49
57
  def key?(key)
50
- @env.key? env_name(key)
58
+ @req.has_header? env_name(key)
51
59
  end
52
60
  alias :include? :key?
53
61
 
62
+ DEFAULT = Object.new # :nodoc:
63
+
54
64
  # Returns the value for the given key mapped to @env.
55
65
  #
56
66
  # If the key is not found and an optional code block is not provided,
@@ -58,18 +68,22 @@ module ActionDispatch
58
68
  #
59
69
  # If the code block is provided, then it will be run and
60
70
  # its result returned.
61
- def fetch(key, *args, &block)
62
- @env.fetch env_name(key), *args, &block
71
+ def fetch(key, default = DEFAULT)
72
+ @req.fetch_header(env_name(key)) do
73
+ return default unless default == DEFAULT
74
+ return yield if block_given?
75
+ raise NameError, key
76
+ end
63
77
  end
64
78
 
65
79
  def each(&block)
66
- @env.each(&block)
80
+ @req.each_header(&block)
67
81
  end
68
82
 
69
83
  # Returns a new Http::Headers instance containing the contents of
70
84
  # <tt>headers_or_env</tt> and the original instance.
71
85
  def merge(headers_or_env)
72
- headers = Http::Headers.new(env.dup)
86
+ headers = @req.dup.headers
73
87
  headers.merge!(headers_or_env)
74
88
  headers
75
89
  end
@@ -79,11 +93,14 @@ module ActionDispatch
79
93
  # <tt>headers_or_env</tt>.
80
94
  def merge!(headers_or_env)
81
95
  headers_or_env.each do |key, value|
82
- self[env_name(key)] = value
96
+ @req.set_header env_name(key), value
83
97
  end
84
98
  end
85
99
 
100
+ def env; @req.env.dup; end
101
+
86
102
  private
103
+
87
104
  # Converts a HTTP header name to an environment variable name if it is
88
105
  # not contained within the headers hash.
89
106
  def env_name(key)
@@ -10,19 +10,18 @@ module ActionDispatch
10
10
  self.ignore_accept_header = false
11
11
  end
12
12
 
13
- attr_reader :variant
14
-
15
- # The MIME type of the HTTP request, such as Mime::XML.
13
+ # The MIME type of the HTTP request, such as Mime[:xml].
16
14
  #
17
15
  # For backward compatibility, the post \format is extracted from the
18
16
  # X-Post-Data-Format HTTP header if present.
19
17
  def content_mime_type
20
- @env["action_dispatch.request.content_type"] ||= begin
21
- if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
18
+ fetch_header("action_dispatch.request.content_type") do |k|
19
+ v = if get_header('CONTENT_TYPE') =~ /^([^,\;]*)/
22
20
  Mime::Type.lookup($1.strip.downcase)
23
21
  else
24
22
  nil
25
23
  end
24
+ set_header k, v
26
25
  end
27
26
  end
28
27
 
@@ -30,31 +29,36 @@ module ActionDispatch
30
29
  content_mime_type && content_mime_type.to_s
31
30
  end
32
31
 
32
+ def has_content_type?
33
+ has_header? 'CONTENT_TYPE'
34
+ end
35
+
33
36
  # Returns the accepted MIME type for the request.
34
37
  def accepts
35
- @env["action_dispatch.request.accepts"] ||= begin
36
- header = @env['HTTP_ACCEPT'].to_s.strip
38
+ fetch_header("action_dispatch.request.accepts") do |k|
39
+ header = get_header('HTTP_ACCEPT').to_s.strip
37
40
 
38
- if header.empty?
41
+ v = if header.empty?
39
42
  [content_mime_type]
40
43
  else
41
44
  Mime::Type.parse(header)
42
45
  end
46
+ set_header k, v
43
47
  end
44
48
  end
45
49
 
46
50
  # Returns the MIME type for the \format used in the request.
47
51
  #
48
- # GET /posts/5.xml | request.format => Mime::XML
49
- # GET /posts/5.xhtml | request.format => Mime::HTML
50
- # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
52
+ # GET /posts/5.xml | request.format => Mime[:xml]
53
+ # GET /posts/5.xhtml | request.format => Mime[:html]
54
+ # GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
51
55
  #
52
56
  def format(view_path = [])
53
57
  formats.first || Mime::NullType.instance
54
58
  end
55
59
 
56
60
  def formats
57
- @env["action_dispatch.request.formats"] ||= begin
61
+ fetch_header("action_dispatch.request.formats") do |k|
58
62
  params_readable = begin
59
63
  parameters[:format]
60
64
  rescue ActionController::BadRequest
@@ -63,34 +67,37 @@ module ActionDispatch
63
67
 
64
68
  v = if params_readable
65
69
  Array(Mime[parameters[:format]])
70
+ elsif format = format_from_path_extension
71
+ Array(Mime[format])
66
72
  elsif use_accept_header && valid_accept_header
67
73
  accepts
68
74
  elsif xhr?
69
- [Mime::JS]
75
+ [Mime[:js]]
70
76
  else
71
- [Mime::HTML]
72
- end
73
-
74
- v.select do |format|
75
- format.symbol || format.ref == "*/*"
77
+ [Mime[:html]]
76
78
  end
79
+ set_header k, v
77
80
  end
78
81
  end
79
82
 
80
83
  # Sets the \variant for template.
81
84
  def variant=(variant)
82
- if variant.is_a?(Symbol)
83
- @variant = [variant]
84
- elsif variant.nil? || variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
85
- @variant = variant
85
+ variant = Array(variant)
86
+
87
+ if variant.all? { |v| v.is_a?(Symbol) }
88
+ @variant = ActiveSupport::ArrayInquirer.new(variant)
86
89
  else
87
- raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
90
+ raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols. " \
88
91
  "For security reasons, never directly set the variant to a user-provided value, " \
89
92
  "like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
90
93
  "then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
91
94
  end
92
95
  end
93
96
 
97
+ def variant
98
+ @variant ||= ActiveSupport::ArrayInquirer.new
99
+ end
100
+
94
101
  # Sets the \format by string extension, which can be used to force custom formats
95
102
  # that are not controlled by the extension.
96
103
  #
@@ -104,7 +111,7 @@ module ActionDispatch
104
111
  # end
105
112
  def format=(extension)
106
113
  parameters[:format] = extension.to_s
107
- @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
114
+ set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
108
115
  end
109
116
 
110
117
  # Sets the \formats by string extensions. This differs from #format= by allowing you
@@ -123,9 +130,9 @@ module ActionDispatch
123
130
  # end
124
131
  def formats=(extensions)
125
132
  parameters[:format] = extensions.first.to_s
126
- @env["action_dispatch.request.formats"] = extensions.collect do |extension|
133
+ set_header "action_dispatch.request.formats", extensions.collect { |extension|
127
134
  Mime::Type.lookup_by_extension(extension)
128
- end
135
+ }
129
136
  end
130
137
 
131
138
  # Receives an array of mimes and return the first user sent mime that
@@ -155,6 +162,13 @@ module ActionDispatch
155
162
  def use_accept_header
156
163
  !self.class.ignore_accept_header
157
164
  end
165
+
166
+ def format_from_path_extension
167
+ path = @env['action_dispatch.original_path'] || @env['PATH_INFO']
168
+ if match = path && path.match(/\.(\w+)\z/)
169
+ match.captures.first
170
+ end
171
+ end
158
172
  end
159
173
  end
160
174
  end