actionpack 4.0.0 → 4.0.1.rc1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +221 -0
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/callbacks.rb +1 -1
  5. data/lib/abstract_controller/helpers.rb +6 -1
  6. data/lib/action_controller/base.rb +1 -1
  7. data/lib/action_controller/metal/flash.rb +1 -1
  8. data/lib/action_controller/metal/strong_parameters.rb +7 -2
  9. data/lib/action_controller/test_case.rb +1 -1
  10. data/lib/action_dispatch/http/mime_type.rb +1 -1
  11. data/lib/action_dispatch/http/response.rb +3 -1
  12. data/lib/action_dispatch/journey/formatter.rb +5 -1
  13. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -12
  14. data/lib/action_dispatch/journey/route.rb +12 -0
  15. data/lib/action_dispatch/journey/router/utils.rb +3 -0
  16. data/lib/action_dispatch/journey/routes.rb +1 -0
  17. data/lib/action_dispatch/journey/visitors.rb +5 -2
  18. data/lib/action_dispatch/middleware/cookies.rb +1 -1
  19. data/lib/action_dispatch/middleware/params_parser.rb +1 -1
  20. data/lib/action_dispatch/middleware/remote_ip.rb +1 -1
  21. data/lib/action_dispatch/middleware/ssl.rb +2 -3
  22. data/lib/action_dispatch/routing/mapper.rb +5 -0
  23. data/lib/action_dispatch/routing/redirection.rb +18 -0
  24. data/lib/action_dispatch/routing/route_set.rb +26 -1
  25. data/lib/action_dispatch/testing/assertions/dom.rb +4 -4
  26. data/lib/action_dispatch/testing/test_request.rb +5 -5
  27. data/lib/action_pack/version.rb +1 -1
  28. data/lib/action_view/dependency_tracker.rb +1 -1
  29. data/lib/action_view/digestor.rb +34 -6
  30. data/lib/action_view/helpers/date_helper.rb +2 -2
  31. data/lib/action_view/helpers/form_helper.rb +1 -1
  32. data/lib/action_view/helpers/form_options_helper.rb +11 -6
  33. data/lib/action_view/helpers/tags/base.rb +2 -1
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -1
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
  36. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  37. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  38. data/lib/action_view/helpers/tags/label.rb +1 -0
  39. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  40. data/lib/action_view/helpers/tags/text_field.rb +2 -2
  41. data/lib/action_view/helpers/text_helper.rb +3 -2
  42. data/lib/action_view/helpers/url_helper.rb +4 -3
  43. data/lib/action_view/railtie.rb +4 -0
  44. data/lib/action_view/renderer/template_renderer.rb +1 -1
  45. data/lib/action_view/tasks/dependencies.rake +17 -0
  46. metadata +10 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f4ecbcfd3111cc54096fa65af027af557e6b7e3
4
- data.tar.gz: dfdfd55057a638b0c95f434b70ec14864c444f15
3
+ metadata.gz: b6c04ba63b482170976504173eb76a7606468917
4
+ data.tar.gz: cec22e4ca260d29bf97824bb03b607fb8aead200
5
5
  SHA512:
6
- metadata.gz: 69dbb3d11bbc52495acb69ac846abad0f6263b17c2284837860d56cd2a22a18ee785d9e1f1e3c3fc4bf8a39eb7d9a982168d3bb42392629af75f9c80bcf64f68
7
- data.tar.gz: cf4e149fe77d767835201792e56a7744f9ee83dba081b1f807082d4991aba60caf3f54d1f676b10e3dd82cb91438a979aa1499d041af53116f29268b0be47f35
6
+ metadata.gz: 01bcd33e7a28dcae4833ce06977d6b540ae2b59d505926b8c95569f70ac2b371a2fe135a7aa87a8219faa1b6309a327f5babc1c884f5d075ead9939ce130a5e9
7
+ data.tar.gz: ffff6e9d696185d003e9ab97dfe7bb32ba25dcd95a46af76552b4fca01900803b693145b51986642183c3c6d562149b592b782658091874b9374ecb448d334be
@@ -1,3 +1,220 @@
1
+ ## Rails 4.0.1.rc1 (October 17, 2013) ##
2
+
3
+ * Respect `SCRIPT_NAME` when using `redirect` with a relative path
4
+
5
+ Example:
6
+ # application routes.rb
7
+ mount BlogEngine => '/blog'
8
+
9
+ # engine routes.rb
10
+ get '/admin' => redirect('admin/dashboard')
11
+
12
+ This now redirects to the path `/blog/admin/dashboard`, whereas before it would've
13
+ generated an invalid url because there would be no slash between the host name and
14
+ the path. It also allows redirects to work where the application is deployed to a
15
+ subdirectory of a website.
16
+
17
+ Fixes #7977.
18
+
19
+ *Andrew White*
20
+
21
+ * Fix `ActionDispatch::RemoteIp::GetIp#calculate_ip` to only check for spoofing
22
+ attacks if both `HTTP_CLIENT_IP` and `HTTP_X_FORWARDED_FOR` are set.
23
+
24
+ Fixes #10844.
25
+
26
+ *Tamir Duberstein*
27
+
28
+ * Strong parameters should permit nested number as key.
29
+
30
+ Fixes #12293.
31
+
32
+ *kennyj*
33
+
34
+ * Fix `collection_check_boxes` generated hidden input to use the name attribute provided
35
+ in the options hash.
36
+
37
+ *Angel N. Sciortino*
38
+
39
+ * Fix some edge cases for AV `select` helper with `:selected` option
40
+
41
+ *Bogdan Gusiev*
42
+
43
+ * Handle `:namespace` form option in collection labels
44
+
45
+ *Vasiliy Ermolovich*
46
+
47
+ * Fix an issue where router can't recognize downcased url encoding path.
48
+
49
+ Fixes #12269.
50
+
51
+ *kennyj*
52
+
53
+ * Fix custom flash type definition. Misusage of the `_flash_types` class variable
54
+ caused an error when reloading controllers with custom flash types.
55
+
56
+ Fixes #12057.
57
+
58
+ *Ricardo de Cillo*
59
+
60
+ * Do not break params filtering on `nil` values.
61
+
62
+ Fixes #12149.
63
+
64
+ *Vasiliy Ermolovich*
65
+
66
+ * Fix `excerpt` when `:separator` is `nil`.
67
+
68
+ *Paul Nikitochkin*
69
+
70
+ * Make Live Streaming work with basic authentication or builder.
71
+
72
+ Fixes #10984.
73
+
74
+ *Aaron Patterson*
75
+
76
+ * Always use `Rack::Sendfile` to make possible to it be automatically
77
+ configured by the webserver.
78
+
79
+ Fixes #11440.
80
+
81
+ *Martin Schürrer*
82
+
83
+ * Flag cookies as secure with ignore case in `ActionDispatch::SSL`.
84
+
85
+ *Yamagishi Kazutoshi*
86
+
87
+ * Don't include STS header in non-HTTPS responses.
88
+
89
+ *Geoff Buesing*
90
+
91
+ * Fix an issue where rails raise exception about missing helper where it
92
+ should throw `LoadError`. When helper file exists and only loaded file from
93
+ this helper does not exist rails should throw LoadError instead of
94
+ `MissingHelperError`.
95
+
96
+ *Piotr Niełacny*
97
+
98
+ * Only cache template digests if `config.cache_template_loading` is true.
99
+
100
+ *Josh Lauer*, *Justin Ridgewell*
101
+
102
+ * Fix an issue where `:if` and `:unless` controller action procs were being run
103
+ before checking for the correct action in the `:only` and `:unless` options.
104
+
105
+ Fixes #11799.
106
+
107
+ *Nicholas Jakobsen*
108
+
109
+ * Fix an issue where `assert_dom_equal` and `assert_dom_not_equal` were
110
+ ignoring the passed failure message argument.
111
+
112
+ Fixes #11751.
113
+
114
+ *Ryan McGeary*
115
+
116
+ * Fix `current_page?` when the URL contains escaped characters and the
117
+ original URL is using the hexadecimal lowercased.
118
+
119
+ *Rafael Mendonça França*
120
+
121
+ * Allow `REMOTE_ADDR`, `HTTP_HOST` and `HTTP_USER_AGENT` to be overridden from
122
+ the environment passed into `ActionDispatch::TestRequest.new`.
123
+
124
+ Fixes #11590.
125
+
126
+ *Andrew White*
127
+
128
+ * Fix `text_area` to behave like `text_field` when `nil` is given as
129
+ value.
130
+
131
+ Before:
132
+
133
+ f.text_field :field, value: nil #=> <input value="">
134
+ f.text_area :field, value: nil #=> <textarea>value of field</textarea>
135
+
136
+ After:
137
+
138
+ f.text_area :field, value: nil #=> <textarea></textarea>
139
+
140
+ *Joel Cogen*
141
+
142
+ * Fix an issue where Journey was failing to clear the named routes hash when the
143
+ routes were reloaded and since it doesn't overwrite existing routes then if a
144
+ route changed but wasn't renamed it kept the old definition. This was being
145
+ masked by the optimised url helpers so it only became apparent when passing an
146
+ options hash to the url helper.
147
+
148
+ *Andrew White*
149
+
150
+ * Skip routes pointing to a redirect or mounted application when generating urls
151
+ using an options hash as they aren't relevant and generate incorrect urls.
152
+
153
+ Fixes #8018.
154
+
155
+ *Andrew White*
156
+
157
+ * Fix default rendered format problem when calling `render` without `:content_type` option.
158
+ It should return `:html`.
159
+
160
+ Fixes #11393.
161
+
162
+ *Gleb Mazovetskiy*, *Oleg*, *kennyj*
163
+
164
+ * Fix `ActionDispatch::ParamsParser#parse_formatted_parameters` to rewind body input stream on
165
+ parsing json params.
166
+
167
+ Fixes #11345.
168
+
169
+ *Yuri Bol*, *Paul Nikitochkin*
170
+
171
+ * Fix `link_to` with block and url hashes.
172
+
173
+ Before:
174
+
175
+ link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') }
176
+ # => "<a action=\"bar\" controller=\"foo\"><span>Example site</span></a>"
177
+
178
+ After:
179
+
180
+ link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') }
181
+ # => "<a href=\"/foo/bar\"><span>Example site</span></a>"
182
+
183
+ *Murahashi Sanemat Kenichi*
184
+
185
+ * Fix "Stack Level Too Deep" error when redering recursive partials.
186
+
187
+ Fixes #11340.
188
+
189
+ *Rafael Mendonça França*
190
+
191
+ * Pick `DateField` `DateTimeField` and `ColorField` values from stringified options allowing use of symbol keys with helpers.
192
+
193
+ *Jon Rowe*
194
+
195
+ * Fix `Mime::Type.parse` when bad accepts header is looked up. Previously it
196
+ was setting `request.formats` with an array containing a `nil` value, which
197
+ raised an error when setting the controller formats.
198
+
199
+ Fixes #10965.
200
+
201
+ *Becker*
202
+
203
+ * Always escape the result of `link_to_unless` method.
204
+
205
+ Before:
206
+
207
+ link_to_unless(true, '<b>Showing</b>', 'github.com')
208
+ # => "<b>Showing</b>"
209
+
210
+ After:
211
+
212
+ link_to_unless(true, '<b>Showing</b>', 'github.com')
213
+ # => "&lt;b&gt;Showing&lt;/b&gt;"
214
+
215
+ *dtaniwaki*
216
+
217
+
1
218
  ## Rails 4.0.0 (June 25, 2013) ##
2
219
 
3
220
  * Merge `:action` from routing scope and assign endpoint if both `:controller`
@@ -30,6 +247,10 @@
30
247
 
31
248
  *David Celis*
32
249
 
250
+ * Add `has_named_route?(route_name)` to the mapper API.
251
+
252
+ *José Valim*
253
+
33
254
  * Fix an issue where partials with a number in the filename weren't being digested for cache dependencies.
34
255
 
35
256
  *Bryan Ricker*
@@ -37,7 +37,7 @@ The latest version of Action Pack can be installed with RubyGems:
37
37
 
38
38
  Source code can be downloaded as part of the Rails project on GitHub
39
39
 
40
- * https://github.com/rails/rails/tree/master/actionpack
40
+ * https://github.com/rails/rails/tree/4-0-stable/actionpack
41
41
 
42
42
 
43
43
  == License
@@ -36,7 +36,7 @@ module AbstractController
36
36
  def _normalize_callback_option(options, from, to) # :nodoc:
37
37
  if from = options[from]
38
38
  from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
39
- options[to] = Array(options[to]) << from
39
+ options[to] = Array(options[to]).unshift(from)
40
40
  end
41
41
  end
42
42
 
@@ -150,7 +150,12 @@ module AbstractController
150
150
  @error = error
151
151
  @path = "helpers/#{path}.rb"
152
152
  set_backtrace error.backtrace
153
- super("Missing helper file helpers/%s.rb" % path)
153
+
154
+ if error.path =~ /^#{path}(\.rb)?$/
155
+ super("Missing helper file helpers/%s.rb" % path)
156
+ else
157
+ raise error
158
+ end
154
159
  end
155
160
  end
156
161
 
@@ -59,7 +59,7 @@ module ActionController
59
59
  # <input type="text" name="post[address]" value="hyacintvej">
60
60
  #
61
61
  # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
62
- # If the address input had been named "post[address][street]", the params would have included
62
+ # If the address input had been named <tt>post[address][street]</tt>, the params would have included
63
63
  # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
64
64
  #
65
65
  # == Sessions
@@ -20,7 +20,7 @@ module ActionController #:nodoc:
20
20
  end
21
21
  helper_method type
22
22
 
23
- _flash_types << type
23
+ self._flash_types += [type]
24
24
  end
25
25
  end
26
26
  end
@@ -201,6 +201,7 @@ module ActionController
201
201
  # You may declare that the parameter should be an array of permitted scalars
202
202
  # by mapping it to an empty array:
203
203
  #
204
+ # params = ActionController::Parameters.new(tags: ['rails', 'parameters'])
204
205
  # params.permit(tags: [])
205
206
  #
206
207
  # You can also use +permit+ on nested parameters, like:
@@ -328,7 +329,7 @@ module ActionController
328
329
  def each_element(object)
329
330
  if object.is_a?(Array)
330
331
  object.map { |el| yield el }.compact
331
- elsif object.is_a?(Hash) && object.keys.all? { |k| k =~ /\A-?\d+\z/ }
332
+ elsif fields_for_style?(object)
332
333
  hash = object.class.new
333
334
  object.each { |k,v| hash[k] = yield v }
334
335
  hash
@@ -337,6 +338,10 @@ module ActionController
337
338
  end
338
339
  end
339
340
 
341
+ def fields_for_style?(object)
342
+ object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
343
+ end
344
+
340
345
  def unpermitted_parameters!(params)
341
346
  unpermitted_keys = unpermitted_keys(params)
342
347
  if unpermitted_keys.any?
@@ -415,7 +420,7 @@ module ActionController
415
420
 
416
421
  # Slicing filters out non-declared keys.
417
422
  slice(*filter.keys).each do |key, value|
418
- return unless value
423
+ next unless value
419
424
 
420
425
  if filter[key] == EMPTY_ARRAY
421
426
  # Declaration { comment_ids: [] }.
@@ -552,7 +552,7 @@ module ActionController
552
552
  parameters ||= {}
553
553
  controller_class_name = @controller.class.anonymous? ?
554
554
  "anonymous" :
555
- @controller.class.name.underscore.sub(/_controller$/, '')
555
+ @controller.class.controller_path
556
556
 
557
557
  @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
558
558
 
@@ -179,7 +179,7 @@ module Mime
179
179
  def parse(accept_header)
180
180
  if accept_header !~ /,/
181
181
  accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
182
- parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)]
182
+ parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
183
183
  else
184
184
  list, index = AcceptList.new, 0
185
185
  accept_header.split(',').each do |header|
@@ -198,7 +198,9 @@ module ActionDispatch # :nodoc:
198
198
  if body.respond_to?(:to_path)
199
199
  @stream = body
200
200
  else
201
- @stream = build_buffer self, munge_body_object(body)
201
+ synchronize do
202
+ @stream = build_buffer self, munge_body_object(body)
203
+ end
202
204
  end
203
205
  end
204
206
 
@@ -18,7 +18,11 @@ module ActionDispatch
18
18
 
19
19
  match_route(name, constraints) do |route|
20
20
  parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
21
- next if !name && route.requirements.empty? && route.parts.empty?
21
+
22
+ # Skip this route unless a name has been provided or it is a
23
+ # standard Rails route since we can't determine whether an options
24
+ # hash passed to url_for matches a Rack application or a redirect.
25
+ next unless name || route.dispatcher?
22
26
 
23
27
  missing_keys = missing_keys(route, parameterized_parts)
24
28
  next unless missing_keys.empty?
@@ -9,8 +9,8 @@ module ActionDispatch
9
9
  attr_reader :memos
10
10
 
11
11
  def initialize
12
- @regexp_states = Hash.new { |h,k| h[k] = {} }
13
- @string_states = Hash.new { |h,k| h[k] = {} }
12
+ @regexp_states = {}
13
+ @string_states = {}
14
14
  @accepting = {}
15
15
  @memos = Hash.new { |h,k| h[k] = [] }
16
16
  end
@@ -111,14 +111,8 @@ module ActionDispatch
111
111
  end
112
112
 
113
113
  def []=(from, to, sym)
114
- case sym
115
- when String
116
- @string_states[from][sym] = to
117
- when Regexp
118
- @regexp_states[from][sym] = to
119
- else
120
- raise ArgumentError, 'unknown symbol: %s' % sym.class
121
- end
114
+ to_mappings = states_hash_for(sym)[from] ||= {}
115
+ to_mappings[sym] = to
122
116
  end
123
117
 
124
118
  def states
@@ -137,18 +131,35 @@ module ActionDispatch
137
131
 
138
132
  private
139
133
 
134
+ def states_hash_for(sym)
135
+ case sym
136
+ when String
137
+ @string_states
138
+ when Regexp
139
+ @regexp_states
140
+ else
141
+ raise ArgumentError, 'unknown symbol: %s' % sym.class
142
+ end
143
+ end
144
+
140
145
  def move_regexp(t, a)
141
146
  return [] if t.empty?
142
147
 
143
148
  t.map { |s|
144
- @regexp_states[s].map { |re, v| re === a ? v : nil }
149
+ if states = @regexp_states[s]
150
+ states.map { |re, v| re === a ? v : nil }
151
+ end
145
152
  }.flatten.compact.uniq
146
153
  end
147
154
 
148
155
  def move_string(t, a)
149
156
  return [] if t.empty?
150
157
 
151
- t.map { |s| @string_states[s][a] }.compact
158
+ t.map do |s|
159
+ if states = @string_states[s]
160
+ states[a]
161
+ end
162
+ end.compact
152
163
  end
153
164
  end
154
165
  end
@@ -16,6 +16,14 @@ module ActionDispatch
16
16
  @app = app
17
17
  @path = path
18
18
 
19
+ # Unwrap any constraints so we can see what's inside for route generation.
20
+ # This allows the formatter to skip over any mounted applications or redirects
21
+ # that shouldn't be matched when using a url_for without a route name.
22
+ while app.is_a?(Routing::Mapper::Constraints) do
23
+ app = app.app
24
+ end
25
+ @dispatcher = app.is_a?(Routing::RouteSet::Dispatcher)
26
+
19
27
  @constraints = constraints
20
28
  @defaults = defaults
21
29
  @required_defaults = nil
@@ -93,6 +101,10 @@ module ActionDispatch
93
101
  end
94
102
  end
95
103
 
104
+ def dispatcher?
105
+ @dispatcher
106
+ end
107
+
96
108
  def matches?(request)
97
109
  constraints.all? do |method, value|
98
110
  next true unless request.respond_to?(method)
@@ -7,15 +7,18 @@ module ActionDispatch
7
7
  # Normalizes URI path.
8
8
  #
9
9
  # Strips off trailing slash and ensures there is a leading slash.
10
+ # Also converts downcase url encoded string to uppercase.
10
11
  #
11
12
  # normalize_path("/foo") # => "/foo"
12
13
  # normalize_path("/foo/") # => "/foo"
13
14
  # normalize_path("foo") # => "/foo"
14
15
  # normalize_path("") # => "/"
16
+ # normalize_path("/%ab") # => "/%AB"
15
17
  def self.normalize_path(path)
16
18
  path = "/#{path}"
17
19
  path.squeeze!('/')
18
20
  path.sub!(%r{/+\Z}, '')
21
+ path.gsub!(/(%[a-f0-9]{2}+)/) { $1.upcase }
19
22
  path = '/' if path == ''
20
23
  path
21
24
  end
@@ -30,6 +30,7 @@ module ActionDispatch
30
30
 
31
31
  def clear
32
32
  routes.clear
33
+ named_routes.clear
33
34
  end
34
35
 
35
36
  def partitioned_routes
@@ -1,10 +1,13 @@
1
1
  # encoding: utf-8
2
+
3
+ require 'thread_safe'
4
+
2
5
  module ActionDispatch
3
6
  module Journey # :nodoc:
4
7
  module Visitors # :nodoc:
5
8
  class Visitor # :nodoc:
6
- DISPATCH_CACHE = Hash.new { |h,k|
7
- h[k] = "visit_#{k}"
9
+ DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k|
10
+ h[k] = :"visit_#{k}"
8
11
  }
9
12
 
10
13
  def accept(node)
@@ -160,7 +160,7 @@ module ActionDispatch
160
160
  end
161
161
  end
162
162
 
163
- # Returns the +signed+ or +encrypted jar, preferring +encrypted+ if +secret_key_base+ is set.
163
+ # Returns the +signed+ or +encrypted+ jar, preferring +encrypted+ if +secret_key_base+ is set.
164
164
  # Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
165
165
  def signed_or_encrypted
166
166
  @signed_or_encrypted ||=
@@ -41,7 +41,7 @@ module ActionDispatch
41
41
  when Proc
42
42
  strategy.call(request.raw_post)
43
43
  when :json
44
- data = ActiveSupport::JSON.decode(request.body)
44
+ data = ActiveSupport::JSON.decode(request.raw_post)
45
45
  data = {:_json => data} unless data.is_a?(Hash)
46
46
  request.deep_munge(data).with_indifferent_access
47
47
  else
@@ -143,7 +143,7 @@ module ActionDispatch
143
143
  # proxies with incompatible IP header conventions, and there is no way
144
144
  # for us to determine which header is the right one after the fact.
145
145
  # Since we have no idea, we give up and explode.
146
- should_check_ip = @check_ip && client_ips.last
146
+ should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
147
147
  if should_check_ip && !forwarded_ips.include?(client_ips.last)
148
148
  # We don't know which came from the proxy, and which from the user
149
149
  raise IpSpoofAttackError, "IP spoofing attack?! " +
@@ -36,8 +36,7 @@ module ActionDispatch
36
36
  url.scheme = "https"
37
37
  url.host = @host if @host
38
38
  url.port = @port if @port
39
- headers = hsts_headers.merge('Content-Type' => 'text/html',
40
- 'Location' => url.to_s)
39
+ headers = { 'Content-Type' => 'text/html', 'Location' => url.to_s }
41
40
 
42
41
  [301, headers, []]
43
42
  end
@@ -58,7 +57,7 @@ module ActionDispatch
58
57
  cookies = cookies.split("\n")
59
58
 
60
59
  headers['Set-Cookie'] = cookies.map { |cookie|
61
- if cookie !~ /;\s+secure(;|$)/
60
+ if cookie !~ /;\s+secure(;|$)/i
62
61
  "#{cookie}; secure"
63
62
  else
64
63
  cookie
@@ -515,6 +515,11 @@ module ActionDispatch
515
515
  end
516
516
  end
517
517
 
518
+ # Query if the following named route was already defined.
519
+ def has_named_route?(name)
520
+ @set.named_routes.routes[name.to_sym]
521
+ end
522
+
518
523
  private
519
524
  def app_name(app)
520
525
  return unless app.respond_to?(:routes)
@@ -30,6 +30,10 @@ module ActionDispatch
30
30
  uri.host ||= req.host
31
31
  uri.port ||= req.port unless req.standard_port?
32
32
 
33
+ if relative_path?(uri.path)
34
+ uri.path = "#{req.script_name}/#{uri.path}"
35
+ end
36
+
33
37
  body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
34
38
 
35
39
  headers = {
@@ -48,6 +52,11 @@ module ActionDispatch
48
52
  def inspect
49
53
  "redirect(#{status})"
50
54
  end
55
+
56
+ private
57
+ def relative_path?(path)
58
+ path && !path.empty? && path[0] != '/'
59
+ end
51
60
  end
52
61
 
53
62
  class PathRedirect < Redirect
@@ -81,6 +90,11 @@ module ActionDispatch
81
90
  url_options[:path] = (url_options[:path] % escape_path(params))
82
91
  end
83
92
 
93
+ if relative_path?(url_options[:path])
94
+ url_options[:path] = "/#{url_options[:path]}"
95
+ url_options[:script_name] = request.script_name
96
+ end
97
+
84
98
  ActionDispatch::Http::URL.url_for url_options
85
99
  end
86
100
 
@@ -104,6 +118,10 @@ module ActionDispatch
104
118
  #
105
119
  # get 'docs/:article', to: redirect('/wiki/%{article}')
106
120
  #
121
+ # Note that if you return a path without a leading slash then the url is prefixed with the
122
+ # current SCRIPT_NAME environment variable. This is typically '/' but may be different in
123
+ # a mounted engine or where the application is deployed to a subdirectory of a website.
124
+ #
107
125
  # Alternatively you can use one of the other syntaxes:
108
126
  #
109
127
  # The block version of redirect allows for the easy encapsulation of any logic associated with
@@ -186,10 +186,16 @@ module ActionDispatch
186
186
  klass = Journey::Router::Utils
187
187
 
188
188
  @path_parts.zip(args) do |part, arg|
189
+ parameterized_arg = arg.to_param
190
+
191
+ if parameterized_arg.nil? || parameterized_arg.empty?
192
+ raise_generation_error(args)
193
+ end
194
+
189
195
  # Replace each route parameter
190
196
  # e.g. :id for regular parameter or *path for globbing
191
197
  # with ruby string interpolation code
192
- path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param))
198
+ path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(parameterized_arg))
193
199
  end
194
200
  path
195
201
  end
@@ -197,6 +203,25 @@ module ActionDispatch
197
203
  def optimize_routes_generation?(t)
198
204
  t.send(:optimize_routes_generation?)
199
205
  end
206
+
207
+ def raise_generation_error(args)
208
+ parts, missing_keys = [], []
209
+
210
+ @path_parts.zip(args) do |part, arg|
211
+ parameterized_arg = arg.to_param
212
+
213
+ if parameterized_arg.nil? || parameterized_arg.empty?
214
+ missing_keys << part
215
+ end
216
+
217
+ parts << [part, arg]
218
+ end
219
+
220
+ message = "No route matches #{Hash[parts].inspect}"
221
+ message << " missing required keys: #{missing_keys.inspect}"
222
+
223
+ raise ActionController::UrlGenerationError, message
224
+ end
200
225
  end
201
226
 
202
227
  def initialize(route, options)
@@ -7,20 +7,20 @@ module ActionDispatch
7
7
  #
8
8
  # # assert that the referenced method generates the appropriate HTML string
9
9
  # assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
10
- def assert_dom_equal(expected, actual, message = "")
10
+ def assert_dom_equal(expected, actual, message = nil)
11
11
  expected_dom = HTML::Document.new(expected).root
12
12
  actual_dom = HTML::Document.new(actual).root
13
- assert_equal expected_dom, actual_dom
13
+ assert_equal expected_dom, actual_dom, message
14
14
  end
15
15
 
16
16
  # The negated form of +assert_dom_equivalent+.
17
17
  #
18
18
  # # assert that the referenced method does not generate the specified HTML string
19
19
  # assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
20
- def assert_dom_not_equal(expected, actual, message = "")
20
+ def assert_dom_not_equal(expected, actual, message = nil)
21
21
  expected_dom = HTML::Document.new(expected).root
22
22
  actual_dom = HTML::Document.new(actual).root
23
- assert_not_equal expected_dom, actual_dom
23
+ assert_not_equal expected_dom, actual_dom, message
24
24
  end
25
25
  end
26
26
  end
@@ -3,7 +3,11 @@ require 'rack/utils'
3
3
 
4
4
  module ActionDispatch
5
5
  class TestRequest < Request
6
- DEFAULT_ENV = Rack::MockRequest.env_for('/')
6
+ DEFAULT_ENV = Rack::MockRequest.env_for('/',
7
+ 'HTTP_HOST' => 'test.host',
8
+ 'REMOTE_ADDR' => '0.0.0.0',
9
+ 'HTTP_USER_AGENT' => 'Rails Testing'
10
+ )
7
11
 
8
12
  def self.new(env = {})
9
13
  super
@@ -12,10 +16,6 @@ module ActionDispatch
12
16
  def initialize(env = {})
13
17
  env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
14
18
  super(default_env.merge(env))
15
-
16
- self.host = 'test.host'
17
- self.remote_addr = '0.0.0.0'
18
- self.user_agent = 'Rails Testing'
19
19
  end
20
20
 
21
21
  def request_method=(method)
@@ -1,7 +1,7 @@
1
1
  module ActionPack
2
2
  # Returns the version of the currently loaded ActionPack as a Gem::Version
3
3
  def self.version
4
- Gem::Version.new "4.0.0"
4
+ Gem::Version.new "4.0.1.rc1"
5
5
  end
6
6
 
7
7
  module VERSION #:nodoc:
@@ -74,7 +74,7 @@ module ActionView
74
74
  # render(@topic) => render("topics/topic")
75
75
  # render(topics) => render("topics/topic")
76
76
  # render(message.topics) => render("topics/topic")
77
- collect { |name| name.sub(/\A@?([a-z]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
77
+ collect { |name| name.sub(/\A@?([a-z_]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
78
78
 
79
79
  # render("headline") => render("message/headline")
80
80
  collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
@@ -1,16 +1,44 @@
1
1
  require 'thread_safe'
2
2
  require 'action_view/dependency_tracker'
3
+ require 'monitor'
3
4
 
4
5
  module ActionView
5
6
  class Digestor
6
7
  cattr_reader(:cache)
7
- @@cache = ThreadSafe::Cache.new
8
+ @@cache = ThreadSafe::Cache.new
9
+ @@digest_monitor = Monitor.new
10
+
11
+ class << self
12
+ def digest(name, format, finder, options = {})
13
+ cache_key = ([name, format] + Array.wrap(options[:dependencies])).join('.')
14
+ # this is a correctly done double-checked locking idiom
15
+ # (ThreadSafe::Cache's lookups have volatile semantics)
16
+ @@cache[cache_key] || @@digest_monitor.synchronize do
17
+ @@cache.fetch(cache_key) do # re-check under lock
18
+ compute_and_store_digest(cache_key, name, format, finder, options)
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+ def compute_and_store_digest(cache_key, name, format, finder, options) # called under @@digest_monitor lock
25
+ klass = if options[:partial] || name.include?("/_")
26
+ # Prevent re-entry or else recursive templates will blow the stack.
27
+ # There is no need to worry about other threads seeing the +false+ value,
28
+ # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
29
+ pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
30
+ PartialDigestor
31
+ else
32
+ Digestor
33
+ end
8
34
 
9
- def self.digest(name, format, finder, options = {})
10
- cache_key = [name, format] + Array.wrap(options[:dependencies])
11
- @@cache[cache_key.join('.')] ||= begin
12
- klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
13
- klass.new(name, format, finder, options).digest
35
+ digest = klass.new(name, format, finder, options).digest
36
+ # Store the actual digest if config.cache_template_loading is true
37
+ @@cache[cache_key] = stored_digest = digest if ActionView::Resolver.caching?
38
+ digest
39
+ ensure
40
+ # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
41
+ @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
14
42
  end
15
43
  end
16
44
 
@@ -539,7 +539,7 @@ module ActionView
539
539
  # my_date = Time.now + 2.days
540
540
  #
541
541
  # # Generates a select field for days that defaults to the day for the date in my_date.
542
- # select_day(my_time)
542
+ # select_day(my_date)
543
543
  #
544
544
  # # Generates a select field for days that defaults to the number given.
545
545
  # select_day(5)
@@ -549,7 +549,7 @@ module ActionView
549
549
  #
550
550
  # # Generates a select field for days that defaults to the day for the date in my_date
551
551
  # # that is named 'due' rather than 'day'.
552
- # select_day(my_time, field_name: 'due')
552
+ # select_day(my_date, field_name: 'due')
553
553
  #
554
554
  # # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
555
555
  # # generic prompt.
@@ -1172,7 +1172,7 @@ module ActionView
1172
1172
  # methods in the +FormHelper+ module. This class, however, allows you to
1173
1173
  # call methods with the model object you are building the form for.
1174
1174
  #
1175
- # You can create your own custom FormBuilder templates by subclasses this
1175
+ # You can create your own custom FormBuilder templates by subclassing this
1176
1176
  # class. For example:
1177
1177
  #
1178
1178
  # class MyFormBuilder < ActionView::Helpers::FormBuilder
@@ -97,14 +97,17 @@ module ActionView
97
97
  # Create a select tag and a series of contained option tags for the provided object and method.
98
98
  # The option currently held by the object will be selected, provided that the object is available.
99
99
  #
100
- # There are two possible formats for the choices parameter, corresponding to other helpers' output:
101
- # * A flat collection: see options_for_select
102
- # * A nested collection: see grouped_options_for_select
100
+ # There are two possible formats for the +choices+ parameter, corresponding to other helpers' output:
101
+ #
102
+ # * A flat collection (see +options_for_select+).
103
+ #
104
+ # * A nested collection (see +grouped_options_for_select+).
105
+ #
106
+ # For example:
103
107
  #
104
- # Example with @post.person_id => 1:
105
108
  # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
106
109
  #
107
- # could become:
110
+ # would become:
108
111
  #
109
112
  # <select name="post[person_id]">
110
113
  # <option value=""></option>
@@ -113,6 +116,8 @@ module ActionView
113
116
  # <option value="3">Tobias</option>
114
117
  # </select>
115
118
  #
119
+ # assuming the associated person has ID 1.
120
+ #
116
121
  # This can be used to provide a default set of options in the standard way: before rendering the create form, a
117
122
  # new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
118
123
  # to the database. Instead, a second model object is created when the create request is received.
@@ -654,7 +659,7 @@ module ActionView
654
659
  #
655
660
  # Example object structure for use with this method:
656
661
  # class Post < ActiveRecord::Base
657
- # has_and_belongs_to_many :author
662
+ # has_and_belongs_to_many :authors
658
663
  # end
659
664
  # class Author < ActiveRecord::Base
660
665
  # has_and_belongs_to_many :posts
@@ -119,7 +119,8 @@ module ActionView
119
119
  html_options = html_options.stringify_keys
120
120
  add_default_name_and_id(html_options)
121
121
  options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
122
- select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
122
+ value = options.fetch(:selected) { value(object) }
123
+ select = content_tag("select", add_options(option_tags, options, value), html_options)
123
124
 
124
125
  if html_options["multiple"] && options.fetch(:include_hidden, true)
125
126
  tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
@@ -27,7 +27,8 @@ module ActionView
27
27
 
28
28
  # Append a hidden field to make sure something will be sent back to the
29
29
  # server if all check boxes are unchecked.
30
- hidden = @template_object.hidden_field_tag("#{tag_name}[]", "", :id => nil)
30
+ hidden_name = @html_options[:name] || "#{tag_name}[]"
31
+ hidden = @template_object.hidden_field_tag(hidden_name, "", :id => nil)
31
32
 
32
33
  rendered_collection + hidden
33
34
  end
@@ -18,7 +18,8 @@ module ActionView
18
18
  end
19
19
 
20
20
  def label(label_html_options={}, &block)
21
- @template_object.label(@object_name, @sanitized_attribute_name, @text, label_html_options, &block)
21
+ html_options = label_html_options.merge(@input_html_options)
22
+ @template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block)
22
23
  end
23
24
  end
24
25
 
@@ -4,7 +4,7 @@ module ActionView
4
4
  class ColorField < TextField # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
- options["value"] = @options.fetch("value") { validate_color_string(value(object)) }
7
+ options["value"] = options.fetch("value") { validate_color_string(value(object)) }
8
8
  @options = options
9
9
  super
10
10
  end
@@ -4,7 +4,7 @@ module ActionView
4
4
  class DatetimeField < TextField # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
- options["value"] = @options.fetch("value") { format_date(value(object)) }
7
+ options["value"] = options.fetch("value") { format_date(value(object)) }
8
8
  options["min"] = format_date(options["min"])
9
9
  options["max"] = format_date(options["max"])
10
10
  @options = options
@@ -30,6 +30,7 @@ module ActionView
30
30
  add_default_name_and_id_for_value(tag_value, name_and_id)
31
31
  options.delete("index")
32
32
  options.delete("namespace")
33
+ options.delete("multiple")
33
34
  options["for"] = name_and_id["id"] unless options.key?("for")
34
35
 
35
36
  if block_given?
@@ -10,7 +10,7 @@ module ActionView
10
10
  options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
11
11
  end
12
12
 
13
- content_tag("textarea", options.delete('value') || value_before_type_cast(object), options)
13
+ content_tag("textarea", options.delete("value") { value_before_type_cast(object) }, options)
14
14
  end
15
15
  end
16
16
  end
@@ -5,8 +5,8 @@ module ActionView
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
  options["size"] = options["maxlength"] unless options.key?("size")
8
- options["type"] ||= field_type
9
- options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
8
+ options["type"] ||= field_type
9
+ options["value"] = options.fetch("value") { value_before_type_cast(object) } unless field_type == "file"
10
10
  options["value"] &&= ERB::Util.html_escape(options["value"])
11
11
  add_default_name_and_id(options)
12
12
  tag("input", options)
@@ -150,7 +150,7 @@ module ActionView
150
150
  def excerpt(text, phrase, options = {})
151
151
  return unless text && phrase
152
152
 
153
- separator = options.fetch(:separator, "")
153
+ separator = options[:separator] || ''
154
154
  phrase = Regexp.escape(phrase)
155
155
  regex = /#{phrase}/i
156
156
 
@@ -169,7 +169,8 @@ module ActionView
169
169
  prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
170
170
  postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
171
171
 
172
- prefix + (first_part + separator + phrase + separator + second_part).strip + postfix
172
+ affix = [first_part, separator, phrase, separator, second_part].join.strip
173
+ [prefix, affix, postfix].join
173
174
  end
174
175
 
175
176
  # Attempts to pluralize the +singular+ word unless +count+ is 1. If
@@ -172,7 +172,7 @@ module ActionView
172
172
  # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
173
173
  # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
174
174
  def link_to(name = nil, options = nil, html_options = nil, &block)
175
- html_options, options = options, name if block_given?
175
+ html_options, options, name = options, name, block if block_given?
176
176
  options ||= {}
177
177
 
178
178
  html_options = convert_options_to_data_attributes(options, html_options)
@@ -380,7 +380,7 @@ module ActionView
380
380
  if block_given?
381
381
  block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
382
382
  else
383
- name
383
+ ERB::Util.html_escape(name)
384
384
  end
385
385
  else
386
386
  link_to(name, options, html_options)
@@ -528,12 +528,13 @@ module ActionView
528
528
 
529
529
  return false unless request.get? || request.head?
530
530
 
531
- url_string = url_for(options)
531
+ url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY)
532
532
 
533
533
  # We ignore any extra parameters in the request_uri if the
534
534
  # submitted url doesn't have any either. This lets the function
535
535
  # work with things like ?order=asc
536
536
  request_uri = url_string.index("?") ? request.fullpath : request.path
537
+ request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
537
538
 
538
539
  if url_string =~ /^\w+:\/\//
539
540
  url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
@@ -35,5 +35,9 @@ module ActionView
35
35
  end
36
36
  end
37
37
  end
38
+
39
+ rake_tasks do
40
+ load "action_view/tasks/dependencies.rake"
41
+ end
38
42
  end
39
43
  end
@@ -11,7 +11,7 @@ module ActionView
11
11
  prepend_formats(template.formats)
12
12
 
13
13
  unless context.rendered_format
14
- context.rendered_format = template.formats.first || formats.last
14
+ context.rendered_format = template.formats.first || formats.first
15
15
  end
16
16
 
17
17
  render_template(template, options[:layout], options[:locals])
@@ -0,0 +1,17 @@
1
+ namespace :cache_digests do
2
+ desc 'Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
3
+ task :nested_dependencies => :environment do
4
+ abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
5
+ template, format = ENV['TEMPLATE'].split(".")
6
+ format ||= :html
7
+ puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).nested_dependencies
8
+ end
9
+
10
+ desc 'Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
11
+ task :dependencies => :environment do
12
+ abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
13
+ template, format = ENV['TEMPLATE'].split(".")
14
+ format ||= :html
15
+ puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).dependencies
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-25 00:00:00.000000000 Z
11
+ date: 2013-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.0
19
+ version: 4.0.1.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 4.0.0
26
+ version: 4.0.1.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 4.0.0
89
+ version: 4.0.1.rc1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 4.0.0
96
+ version: 4.0.1.rc1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: tzinfo
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -340,6 +340,7 @@ files:
340
340
  - lib/action_view/renderer/streaming_template_renderer.rb
341
341
  - lib/action_view/renderer/template_renderer.rb
342
342
  - lib/action_view/routing_url_for.rb
343
+ - lib/action_view/tasks/dependencies.rake
343
344
  - lib/action_view/template/error.rb
344
345
  - lib/action_view/template/handlers/builder.rb
345
346
  - lib/action_view/template/handlers/erb.rb
@@ -374,13 +375,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
374
375
  version: 1.9.3
375
376
  required_rubygems_version: !ruby/object:Gem::Requirement
376
377
  requirements:
377
- - - '>='
378
+ - - '>'
378
379
  - !ruby/object:Gem::Version
379
- version: '0'
380
+ version: 1.3.1
380
381
  requirements:
381
382
  - none
382
383
  rubyforge_project:
383
- rubygems_version: 2.0.2
384
+ rubygems_version: 2.0.6
384
385
  signing_key:
385
386
  specification_version: 4
386
387
  summary: Web-flow and rendering framework putting the VC in MVC (part of Rails).