actionpack 2.3.4 → 2.3.5

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 (74) hide show
  1. data/CHANGELOG +12 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller.rb +3 -1
  4. data/lib/action_controller/assertions/dom_assertions.rb +19 -3
  5. data/lib/action_controller/assertions/selector_assertions.rb +16 -10
  6. data/lib/action_controller/base.rb +1 -1
  7. data/lib/action_controller/caching.rb +1 -0
  8. data/lib/action_controller/cookies.rb +2 -1
  9. data/lib/action_controller/http_authentication.rb +3 -2
  10. data/lib/action_controller/integration.rb +15 -3
  11. data/lib/action_controller/layout.rb +6 -1
  12. data/lib/action_controller/middlewares.rb +2 -0
  13. data/lib/action_controller/polymorphic_routes.rb +6 -21
  14. data/lib/action_controller/rack_lint_patch.rb +36 -0
  15. data/lib/action_controller/request_forgery_protection.rb +6 -2
  16. data/lib/action_controller/response.rb +2 -1
  17. data/lib/action_controller/string_coercion.rb +29 -0
  18. data/lib/action_controller/test_case.rb +6 -1
  19. data/lib/action_controller/test_process.rb +1 -1
  20. data/lib/action_controller/translation.rb +2 -2
  21. data/lib/action_controller/uploaded_file.rb +2 -2
  22. data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
  23. data/lib/action_pack/version.rb +1 -1
  24. data/lib/action_view.rb +3 -3
  25. data/lib/action_view/base.rb +6 -1
  26. data/lib/action_view/erb/util.rb +6 -0
  27. data/lib/action_view/helpers.rb +2 -0
  28. data/lib/action_view/helpers/active_record_helper.rb +3 -3
  29. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  30. data/lib/action_view/helpers/capture_helper.rb +2 -2
  31. data/lib/action_view/helpers/date_helper.rb +27 -15
  32. data/lib/action_view/helpers/form_helper.rb +32 -11
  33. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  34. data/lib/action_view/helpers/form_tag_helper.rb +3 -3
  35. data/lib/action_view/helpers/number_helper.rb +6 -1
  36. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  37. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +10 -2
  39. data/lib/action_view/helpers/tag_helper.rb +4 -4
  40. data/lib/action_view/helpers/translation_helper.rb +1 -1
  41. data/lib/action_view/helpers/url_helper.rb +3 -3
  42. data/lib/action_view/locale/en.yml +3 -0
  43. data/lib/action_view/partials.rb +1 -1
  44. data/lib/action_view/safe_buffer.rb +28 -0
  45. data/lib/action_view/template.rb +8 -2
  46. data/lib/action_view/test_case.rb +102 -27
  47. data/lib/actionpack.rb +1 -0
  48. data/test/controller/cookie_test.rb +7 -0
  49. data/test/controller/dom_assertions_test.rb +53 -0
  50. data/test/controller/filter_params_test.rb +1 -0
  51. data/test/controller/html-scanner/sanitizer_test.rb +1 -0
  52. data/test/controller/http_digest_authentication_test.rb +22 -0
  53. data/test/controller/integration_test.rb +38 -0
  54. data/test/controller/layout_test.rb +11 -0
  55. data/test/controller/polymorphic_routes_test.rb +4 -0
  56. data/test/controller/request_forgery_protection_test.rb +19 -1
  57. data/test/controller/routing_test.rb +23 -15
  58. data/test/controller/session/test_session_test.rb +2 -2
  59. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  60. data/test/fixtures/test/_from_helper.erb +1 -0
  61. data/test/template/active_record_helper_test.rb +1 -1
  62. data/test/template/asset_tag_helper_test.rb +12 -0
  63. data/test/template/benchmark_helper_test.rb +6 -6
  64. data/test/template/compiled_templates_test.rb +2 -1
  65. data/test/template/date_helper_i18n_test.rb +10 -9
  66. data/test/template/date_helper_test.rb +29 -13
  67. data/test/template/form_helper_test.rb +90 -10
  68. data/test/template/number_helper_test.rb +4 -0
  69. data/test/template/raw_output_helper_test.rb +21 -0
  70. data/test/template/sanitize_helper_test.rb +10 -1
  71. data/test/template/tag_helper_test.rb +1 -0
  72. data/test/view/safe_buffer_test.rb +36 -0
  73. data/test/view/test_case_test.rb +173 -5
  74. metadata +13 -4
@@ -393,7 +393,7 @@ module ActionView
393
393
 
394
394
  concat(form_remote_tag(options))
395
395
  fields_for(object_name, *(args << options), &proc)
396
- concat('</form>')
396
+ concat('</form>'.html_safe!)
397
397
  end
398
398
  alias_method :form_remote_for, :remote_form_for
399
399
 
@@ -0,0 +1,9 @@
1
+ module ActionView #:nodoc:
2
+ module Helpers #:nodoc:
3
+ module RawOutputHelper
4
+ def raw(stringish)
5
+ stringish.to_s.html_safe!
6
+ end
7
+ end
8
+ end
9
+ end
@@ -49,7 +49,11 @@ module ActionView
49
49
  # confuse browsers.
50
50
  #
51
51
  def sanitize(html, options = {})
52
- self.class.white_list_sanitizer.sanitize(html, options)
52
+ returning self.class.white_list_sanitizer.sanitize(html, options) do |sanitized|
53
+ if sanitized
54
+ sanitized.html_safe!
55
+ end
56
+ end
53
57
  end
54
58
 
55
59
  # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
@@ -72,7 +76,11 @@ module ActionView
72
76
  # strip_tags("<div id='top-bar'>Welcome to my website!</div>")
73
77
  # # => Welcome to my website!
74
78
  def strip_tags(html)
75
- self.class.full_sanitizer.sanitize(html)
79
+ returning self.class.full_sanitizer.sanitize(html) do |sanitized|
80
+ if sanitized
81
+ sanitized.html_safe!
82
+ end
83
+ end
76
84
  end
77
85
 
78
86
  # Strips all link tags from +text+ leaving just the link text.
@@ -38,7 +38,7 @@ module ActionView
38
38
  # tag("img", { :src => "open &amp; shut.png" }, false, false)
39
39
  # # => <img src="open &amp; shut.png" />
40
40
  def tag(name, options = nil, open = false, escape = true)
41
- "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}"
41
+ "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe!
42
42
  end
43
43
 
44
44
  # Returns an HTML block tag of type +name+ surrounding the +content+. Add
@@ -91,7 +91,7 @@ module ActionView
91
91
  # cdata_section(File.read("hello_world.txt"))
92
92
  # # => <![CDATA[<hello from a text file]]>
93
93
  def cdata_section(content)
94
- "<![CDATA[#{content}]]>"
94
+ "<![CDATA[#{content}]]>".html_safe!
95
95
  end
96
96
 
97
97
  # Returns an escaped version of +html+ without affecting existing escaped entities.
@@ -125,7 +125,7 @@ module ActionView
125
125
 
126
126
  def content_tag_string(name, content, options, escape = true)
127
127
  tag_options = tag_options(options, escape) if options
128
- "<#{name}#{tag_options}>#{content}</#{name}>"
128
+ "<#{name}#{tag_options}>#{content}</#{name}>".html_safe!
129
129
  end
130
130
 
131
131
  def tag_options(options, escape = true)
@@ -142,7 +142,7 @@ module ActionView
142
142
  else
143
143
  attrs = options.map { |key, value| %(#{key}="#{value}") }
144
144
  end
145
- " #{attrs.sort * ' '}" unless attrs.empty?
145
+ " #{attrs.sort * ' '}".html_safe! unless attrs.empty?
146
146
  end
147
147
  end
148
148
  end
@@ -21,7 +21,7 @@ module ActionView
21
21
 
22
22
  # Delegates to I18n.localize with no additional functionality.
23
23
  def localize(*args)
24
- I18n.localize *args
24
+ I18n.localize(*args)
25
25
  end
26
26
  alias :l :localize
27
27
 
@@ -219,7 +219,7 @@ module ActionView
219
219
  if block_given?
220
220
  options = args.first || {}
221
221
  html_options = args.second
222
- concat(link_to(capture(&block), options, html_options))
222
+ concat(link_to(capture(&block), options, html_options).html_safe!)
223
223
  else
224
224
  name = args.first
225
225
  options = args.second || {}
@@ -237,7 +237,7 @@ module ActionView
237
237
  end
238
238
 
239
239
  href_attr = "href=\"#{url}\"" unless href
240
- "<a #{href_attr}#{tag_options}>#{name || url}</a>"
240
+ "<a #{href_attr}#{tag_options}>#{name || url}</a>".html_safe!
241
241
  end
242
242
  end
243
243
 
@@ -309,7 +309,7 @@ module ActionView
309
309
  html_options.merge!("type" => "submit", "value" => name)
310
310
 
311
311
  "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
312
- method_tag + tag("input", html_options) + request_token_tag + "</div></form>"
312
+ method_tag + tag("input", html_options) + request_token_tag + "</div></form>".html_safe!
313
313
  end
314
314
 
315
315
 
@@ -91,6 +91,9 @@
91
91
  over_x_years:
92
92
  one: "over 1 year"
93
93
  other: "over {{count}} years"
94
+ almost_x_years:
95
+ one: "almost 1 year"
96
+ other: "almost {{count}} years"
94
97
  prompts:
95
98
  year: "Year"
96
99
  month: "Month"
@@ -221,7 +221,7 @@ module ActionView
221
221
  result = template.render_partial(self, object, local_assigns.dup, as)
222
222
  index += 1
223
223
  result
224
- end.join(spacer)
224
+ end.join(spacer).html_safe!
225
225
  end
226
226
 
227
227
  def _pick_partial_template(partial_path) #:nodoc:
@@ -0,0 +1,28 @@
1
+
2
+ module ActionView #:nodoc:
3
+ class SafeBuffer < String
4
+ def <<(value)
5
+ if value.html_safe?
6
+ super(value)
7
+ else
8
+ super(ERB::Util.h(value))
9
+ end
10
+ end
11
+
12
+ def concat(value)
13
+ self << value
14
+ end
15
+
16
+ def html_safe?
17
+ true
18
+ end
19
+
20
+ def html_safe!
21
+ self
22
+ end
23
+
24
+ def to_s
25
+ self
26
+ end
27
+ end
28
+ end
@@ -57,9 +57,14 @@ module ActionView #:nodoc:
57
57
  end
58
58
 
59
59
  class EagerPath < Path
60
+ def initialize(path)
61
+ super
62
+ @loaded = false
63
+ end
64
+
60
65
  def load!
61
66
  return if @loaded
62
-
67
+
63
68
  @paths = {}
64
69
  templates_in_path do |template|
65
70
  template.load!
@@ -103,8 +108,9 @@ module ActionView #:nodoc:
103
108
  @@exempt_from_layout.merge(regexps)
104
109
  end
105
110
 
106
- attr_accessor :template_path, :filename, :load_path, :base_path
111
+ attr_accessor :template_path, :load_path, :base_path
107
112
  attr_accessor :locale, :name, :format, :extension
113
+ attr_writer :filename
108
114
  delegate :to_s, :to => :path
109
115
 
110
116
  def initialize(template_path, load_path = nil)
@@ -22,11 +22,52 @@ module ActionView
22
22
  end
23
23
 
24
24
  class TestCase < ActiveSupport::TestCase
25
+ class TestController < ActionController::Base
26
+ attr_accessor :request, :response, :params
27
+
28
+ def self.controller_path
29
+ ''
30
+ end
31
+
32
+ def initialize
33
+ @request = ActionController::TestRequest.new
34
+ @response = ActionController::TestResponse.new
35
+
36
+ @params = {}
37
+ send(:initialize_current_url)
38
+ end
39
+ end
40
+
25
41
  include ActionController::TestCase::Assertions
26
42
  include ActionController::TestProcess
27
43
 
44
+ include ActionController::PolymorphicRoutes
45
+ include ActionController::RecordIdentifier
46
+
47
+ include ActionView::Helpers
48
+ include ActionController::Helpers
49
+
28
50
  class_inheritable_accessor :helper_class
29
- @@helper_class = nil
51
+ attr_accessor :controller, :output_buffer, :rendered
52
+
53
+ setup :setup_with_controller
54
+ def setup_with_controller
55
+ @controller = TestController.new
56
+ @output_buffer = ''
57
+ @rendered = ''
58
+
59
+ self.class.send(:include_helper_modules!)
60
+ make_test_case_available_to_view!
61
+ end
62
+
63
+ def render(options = {}, local_assigns = {}, &block)
64
+ @rendered << output = _view.render(options, local_assigns, &block)
65
+ output
66
+ end
67
+
68
+ def protect_against_forgery?
69
+ false
70
+ end
30
71
 
31
72
  class << self
32
73
  def tests(helper_class)
@@ -46,42 +87,76 @@ module ActionView
46
87
  rescue NameError
47
88
  nil
48
89
  end
49
- end
50
90
 
51
- include ActionView::Helpers
52
- include ActionController::PolymorphicRoutes
53
- include ActionController::RecordIdentifier
91
+ def helper_method(*methods)
92
+ # Almost a duplicate from ActionController::Helpers
93
+ methods.flatten.each do |method|
94
+ master_helper_module.module_eval <<-end_eval
95
+ def #{method}(*args, &block) # def current_user(*args, &block)
96
+ _test_case.send(%(#{method}), *args, &block) # test_case.send(%(current_user), *args, &block)
97
+ end # end
98
+ end_eval
99
+ end
100
+ end
54
101
 
55
- setup :setup_with_helper_class
102
+ private
103
+ def include_helper_modules!
104
+ helper(helper_class) if helper_class
105
+ include master_helper_module
106
+ end
107
+ end
56
108
 
57
- def setup_with_helper_class
58
- if helper_class && !self.class.ancestors.include?(helper_class)
59
- self.class.send(:include, helper_class)
109
+ private
110
+ def make_test_case_available_to_view!
111
+ test_case_instance = self
112
+ master_helper_module.module_eval do
113
+ define_method(:_test_case) { test_case_instance }
114
+ private :_test_case
115
+ end
60
116
  end
61
117
 
62
- self.output_buffer = ''
63
- end
118
+ def _view
119
+ view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller)
120
+ view.helpers.include master_helper_module
121
+ view.output_buffer = self.output_buffer
122
+ view
123
+ end
64
124
 
65
- class TestController < ActionController::Base
66
- attr_accessor :request, :response, :params
125
+ # Support the selector assertions
126
+ #
127
+ # Need to experiment if this priority is the best one: rendered => output_buffer
128
+ def response_from_page_or_rjs
129
+ HTML::Document.new(rendered.blank? ? output_buffer : rendered).root
130
+ end
67
131
 
68
- def initialize
69
- @request = ActionController::TestRequest.new
70
- @response = ActionController::TestResponse.new
71
-
72
- @params = {}
73
- send(:initialize_current_url)
132
+ EXCLUDE_IVARS = %w{
133
+ @output_buffer
134
+ @fixture_cache
135
+ @method_name
136
+ @_result
137
+ @loaded_fixtures
138
+ @test_passed
139
+ @view
140
+ }
141
+
142
+ def _instance_variables
143
+ instance_variables - EXCLUDE_IVARS
74
144
  end
75
- end
76
145
 
77
- protected
78
- attr_accessor :output_buffer
146
+ def _assigns
147
+ _instance_variables.inject({}) do |hash, var|
148
+ name = var[1..-1].to_sym
149
+ hash[name] = instance_variable_get(var)
150
+ hash
151
+ end
152
+ end
79
153
 
80
- private
81
154
  def method_missing(selector, *args)
82
- controller = TestController.new
83
- return controller.__send__(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
84
- super
155
+ if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
156
+ @controller.__send__(selector, *args)
157
+ else
158
+ super
159
+ end
85
160
  end
86
161
  end
87
- end
162
+ end
@@ -1 +1,2 @@
1
1
  require 'action_pack'
2
+ ActiveSupport::Deprecation.warn 'require "actionpack" is deprecated and will be removed in Rails 3. Use require "action_pack" instead.'
@@ -114,6 +114,13 @@ class CookieTest < ActionController::TestCase
114
114
  assert_equal %w{1 2 3}, jar["pages"]
115
115
  end
116
116
 
117
+ def test_cookiejar_delete_removes_item_and_returns_its_value
118
+ @request.cookies["user_name"] = "david"
119
+ @controller.response = @response
120
+ jar = ActionController::CookieJar.new(@controller)
121
+ assert_equal "david", jar.delete("user_name")
122
+ end
123
+
117
124
  def test_delete_cookie_with_path
118
125
  get :delete_cookie_with_path
119
126
  assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
@@ -0,0 +1,53 @@
1
+ require 'abstract_unit'
2
+
3
+ class DomAssertionsTest < ActionView::TestCase
4
+ def setup
5
+ super
6
+ @html_only = '<ul><li>foo</li><li>bar</li></ul>'
7
+ @html_with_meaningless_whitespace = %{
8
+ <ul>
9
+ <li>\tfoo </li>
10
+ <li>
11
+ bar
12
+ </li>
13
+ </ul>
14
+ }
15
+ @more_html_with_meaningless_whitespace = %{<ul>
16
+
17
+ <li>foo</li>
18
+
19
+ <li>bar</li></ul>}
20
+ end
21
+
22
+ test "assert_dom_equal strips meaningless whitespace from expected string" do
23
+ assert_dom_equal @html_with_meaningless_whitespace, @html_only
24
+ end
25
+
26
+ test "assert_dom_equal strips meaningless whitespace from actual string" do
27
+ assert_dom_equal @html_only, @html_with_meaningless_whitespace
28
+ end
29
+
30
+ test "assert_dom_equal strips meaningless whitespace from both expected and actual strings" do
31
+ assert_dom_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace
32
+ end
33
+
34
+ test "assert_dom_not_equal strips meaningless whitespace from expected string" do
35
+ assert_assertion_fails { assert_dom_not_equal @html_with_meaningless_whitespace, @html_only }
36
+ end
37
+
38
+ test "assert_dom_not_equal strips meaningless whitespace from actual string" do
39
+ assert_assertion_fails { assert_dom_not_equal @html_only, @html_with_meaningless_whitespace }
40
+ end
41
+
42
+ test "assert_dom_not_equal strips meaningless whitespace from both expected and actual strings" do
43
+ assert_assertion_fails do
44
+ assert_dom_not_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def assert_assertion_fails
51
+ assert_raise(ActiveSupport::TestCase::Assertion) { yield }
52
+ end
53
+ end
@@ -18,6 +18,7 @@ class FilterParamTest < Test::Unit::TestCase
18
18
  test_hashes = [[{},{},[]],
19
19
  [{'foo'=>nil},{'foo'=>nil},[]],
20
20
  [{'foo'=>'bar'},{'foo'=>'bar'},[]],
21
+ [{'foo'=>1},{'foo'=>1},[]],
21
22
  [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
22
23
  [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
23
24
  [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
@@ -19,6 +19,7 @@ class SanitizerTest < ActionController::TestCase
19
19
  assert_equal "This has a here.", sanitizer.sanitize("This has a <!-- comment --> here.")
20
20
  assert_equal "This has a here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.")
21
21
  assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...")
22
+ assert_equal "non printable char is a tag", sanitizer.sanitize("<\x07a href='/hello'>non printable char is a tag</a>")
22
23
  [nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) }
23
24
  end
24
25
 
@@ -92,6 +92,23 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
92
92
  assert_equal "Authentication Failed", @response.body
93
93
  end
94
94
 
95
+ test "authentication request with missing nonce should return 401" do
96
+ @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :remove_nonce => true)
97
+ get :display
98
+
99
+ assert_response :unauthorized
100
+ assert_equal "Authentication Failed", @response.body
101
+ end
102
+
103
+ test "authentication request with Basic auth credentials should return 401" do
104
+ ActionController::Base.session_options[:secret] = "session_options_secret"
105
+ @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('pretty', 'please')
106
+ get :display
107
+
108
+ assert_response :unauthorized
109
+ assert_equal "Authentication Failed", @response.body
110
+ end
111
+
95
112
  test "authentication request with invalid opaque" do
96
113
  @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'foo', :opaque => "xxyyzz")
97
114
  get :display
@@ -220,9 +237,14 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
220
237
 
221
238
  assert_response :unauthorized
222
239
 
240
+ remove_nonce = options.delete(:remove_nonce)
241
+
223
242
  credentials = decode_credentials(@response.headers['WWW-Authenticate'])
224
243
  credentials.merge!(options)
225
244
  credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s)
245
+
246
+ credentials.delete(:nonce) if remove_nonce
247
+
226
248
  ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
227
249
  end
228
250