actionpack 3.0.20 → 3.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 (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -0,0 +1,35 @@
1
+ module ActionDispatch
2
+ module Routing
3
+ class RoutesProxy #:nodoc:
4
+ include ActionDispatch::Routing::UrlFor
5
+
6
+ attr_accessor :scope, :routes
7
+ alias :_routes :routes
8
+
9
+ def initialize(routes, scope)
10
+ @routes, @scope = routes, scope
11
+ end
12
+
13
+ def url_options
14
+ scope.send(:_with_routes, routes) do
15
+ scope.url_options
16
+ end
17
+ end
18
+
19
+ def method_missing(method, *args)
20
+ if routes.url_helpers.respond_to?(method)
21
+ self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
22
+ def #{method}(*args)
23
+ options = args.extract_options!
24
+ args << url_options.merge((options || {}).symbolize_keys)
25
+ routes.url_helpers.#{method}(*args)
26
+ end
27
+ RUBY
28
+ send(method, *args)
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -98,6 +98,11 @@ module ActionDispatch
98
98
  end
99
99
  end
100
100
 
101
+ def initialize(*)
102
+ @_routes = nil
103
+ super
104
+ end
105
+
101
106
  def url_options
102
107
  default_url_options
103
108
  end
@@ -110,6 +115,13 @@ module ActionDispatch
110
115
  # * <tt>:host</tt> - Specifies the host the link should be targeted at.
111
116
  # If <tt>:only_path</tt> is false, this option must be
112
117
  # provided either explicitly, or via +default_url_options+.
118
+ # * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
119
+ # to split the domain from the host.
120
+ # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
121
+ # to split the subdomain from the host.
122
+ # * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
123
+ # <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
124
+ # <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
113
125
  # * <tt>:port</tt> - Optionally specify the port to connect to.
114
126
  # * <tt>:anchor</tt> - An anchor name to be appended to the path.
115
127
  # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
@@ -128,11 +140,23 @@ module ActionDispatch
128
140
  when String
129
141
  options
130
142
  when nil, Hash
131
- _routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
143
+ _routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
132
144
  else
133
145
  polymorphic_url(options)
134
146
  end
135
147
  end
148
+
149
+ protected
150
+ def _with_routes(routes)
151
+ old_routes, @_routes = @_routes, routes
152
+ yield
153
+ ensure
154
+ @_routes = old_routes
155
+ end
156
+
157
+ def _routes_context
158
+ self
159
+ end
136
160
  end
137
161
  end
138
162
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
1
3
  module ActionDispatch
2
4
  module Assertions
3
5
  # A small suite of assertions that test responses from \Rails applications.
@@ -20,7 +22,7 @@ module ActionDispatch
20
22
  #
21
23
  # You can also pass an explicit status number like <tt>assert_response(501)</tt>
22
24
  # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
23
- # See ActionDispatch::StatusCodes for a full list.
25
+ # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
24
26
  #
25
27
  # ==== Examples
26
28
  #
@@ -33,14 +35,14 @@ module ActionDispatch
33
35
  def assert_response(type, message = nil)
34
36
  validate_request!
35
37
 
36
- if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
38
+ if type.in?([:success, :missing, :redirect, :error]) && @response.send("#{type}?")
37
39
  assert_block("") { true } # to count the assertion
38
40
  elsif type.is_a?(Fixnum) && @response.response_code == type
39
41
  assert_block("") { true } # to count the assertion
40
42
  elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
41
43
  assert_block("") { true } # to count the assertion
42
44
  else
43
- assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
45
+ flunk(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
44
46
  end
45
47
  end
46
48
 
@@ -81,20 +83,16 @@ module ActionDispatch
81
83
 
82
84
  def normalize_argument_to_redirection(fragment)
83
85
  case fragment
84
- when %r{^\w[\w\d+.-]*:.*}
86
+ when %r{^\w[A-Za-z\d+.-]*:.*}
85
87
  fragment
86
88
  when String
87
- if fragment =~ %r{^\w[\w\d+.-]*:.*}
88
- fragment
89
- else
90
- @request.protocol + @request.host_with_port + fragment
91
- end
89
+ @request.protocol + @request.host_with_port + fragment
92
90
  when :back
93
91
  raise RedirectBackError unless refer = @request.headers["Referer"]
94
92
  refer
95
93
  else
96
94
  @controller.url_for(fragment)
97
- end.gsub(/[\0\r\n]/, '')
95
+ end.gsub(/[\r\n]/, '')
98
96
  end
99
97
 
100
98
  def validate_request!
@@ -46,7 +46,7 @@ module ActionDispatch
46
46
  expected_options.stringify_keys!
47
47
  msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
48
48
  request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
49
- assert_block(msg) { request.path_parameters == expected_options }
49
+ assert_equal(expected_options, request.path_parameters, msg)
50
50
  end
51
51
 
52
52
  # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
@@ -84,11 +84,11 @@ module ActionDispatch
84
84
  found_extras = options.reject {|k, v| ! extra_keys.include? k}
85
85
 
86
86
  msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
87
- assert_block(msg) { found_extras == extras }
87
+ assert_equal(extras, found_extras, msg)
88
88
 
89
89
  msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
90
90
  expected_path)
91
- assert_block(msg) { expected_path == generated_path }
91
+ assert_equal(expected_path, generated_path, msg)
92
92
  end
93
93
 
94
94
  # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
@@ -144,16 +144,16 @@ module ActionDispatch
144
144
  #
145
145
  def with_routing
146
146
  old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
147
- old_controller, @controller = @controller, @controller.clone if @controller
148
- _routes = @routes
149
-
150
- # Unfortunately, there is currently an abstraction leak between AC::Base
151
- # and AV::Base which requires having the URL helpers in both AC and AV.
152
- # To do this safely at runtime for tests, we need to bump up the helper serial
153
- # to that the old AV subclass isn't cached.
154
- #
155
- # TODO: Make this unnecessary
156
- if @controller
147
+ if defined?(@controller) && @controller
148
+ old_controller, @controller = @controller, @controller.clone
149
+ _routes = @routes
150
+
151
+ # Unfortunately, there is currently an abstraction leak between AC::Base
152
+ # and AV::Base which requires having the URL helpers in both AC and AV.
153
+ # To do this safely at runtime for tests, we need to bump up the helper serial
154
+ # to that the old AV subclass isn't cached.
155
+ #
156
+ # TODO: Make this unnecessary
157
157
  @controller.singleton_class.send(:include, _routes.url_helpers)
158
158
  @controller.view_context_class = Class.new(@controller.view_context_class) do
159
159
  include _routes.url_helpers
@@ -162,14 +162,14 @@ module ActionDispatch
162
162
  yield @routes
163
163
  ensure
164
164
  @routes = old_routes
165
- if @controller
165
+ if defined?(@controller) && @controller
166
166
  @controller = old_controller
167
167
  end
168
168
  end
169
169
 
170
170
  # ROUTES TODO: These assertions should really work in an integration context
171
171
  def method_missing(selector, *args, &block)
172
- if @controller && @routes && @routes.named_routes.helpers.include?(selector)
172
+ if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
173
173
  @controller.send(selector, *args, &block)
174
174
  else
175
175
  super
@@ -1,4 +1,5 @@
1
1
  require 'action_controller/vendor/html-scanner'
2
+ require 'active_support/core_ext/object/inclusion'
2
3
 
3
4
  #--
4
5
  # Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
@@ -18,16 +19,12 @@ module ActionDispatch
18
19
  # from the response HTML or elements selected by the enclosing assertion.
19
20
  #
20
21
  # In addition to HTML responses, you can make the following assertions:
21
- # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
22
+ #
22
23
  # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
23
24
  # * +assert_select_email+ - Assertions on the HTML body of an e-mail.
24
25
  #
25
26
  # Also see HTML::Selector to learn how to use selectors.
26
27
  module SelectorAssertions
27
- # :call-seq:
28
- # css_select(selector) => array
29
- # css_select(element, selector) => array
30
- #
31
28
  # Select and return all matching elements.
32
29
  #
33
30
  # If called with a single argument, uses that argument as a selector
@@ -71,7 +68,7 @@ module ActionDispatch
71
68
  arg = args.shift
72
69
  elsif arg == nil
73
70
  raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
74
- elsif @selected
71
+ elsif defined?(@selected) && @selected
75
72
  matches = []
76
73
 
77
74
  @selected.each do |selected|
@@ -83,7 +80,7 @@ module ActionDispatch
83
80
 
84
81
  return matches
85
82
  else
86
- root = response_from_page_or_rjs
83
+ root = response_from_page
87
84
  end
88
85
 
89
86
  case arg
@@ -99,10 +96,6 @@ module ActionDispatch
99
96
  selector.select(root)
100
97
  end
101
98
 
102
- # :call-seq:
103
- # assert_select(selector, equality?, message?)
104
- # assert_select(element, selector, equality?, message?)
105
- #
106
99
  # An assertion that selects elements and makes one or more equality tests.
107
100
  #
108
101
  # If the first argument is an element, selects all matching elements
@@ -195,6 +188,7 @@ module ActionDispatch
195
188
  def assert_select(*args, &block)
196
189
  # Start with optional element followed by mandatory selector.
197
190
  arg = args.shift
191
+ @selected ||= nil
198
192
 
199
193
  if arg.is_a?(HTML::Node)
200
194
  # First argument is a node (tag or text, but also HTML root),
@@ -210,7 +204,7 @@ module ActionDispatch
210
204
  root.children.concat @selected
211
205
  else
212
206
  # Otherwise just operate on the response document.
213
- root = response_from_page_or_rjs
207
+ root = response_from_page
214
208
  end
215
209
 
216
210
  # First or second argument is the selector: string and we pass
@@ -332,151 +326,6 @@ module ActionDispatch
332
326
  end
333
327
  end
334
328
 
335
- # :call-seq:
336
- # assert_select_rjs(id?) { |elements| ... }
337
- # assert_select_rjs(statement, id?) { |elements| ... }
338
- # assert_select_rjs(:insert, position, id?) { |elements| ... }
339
- #
340
- # Selects content from the RJS response.
341
- #
342
- # === Narrowing down
343
- #
344
- # With no arguments, asserts that one or more elements are updated or
345
- # inserted by RJS statements.
346
- #
347
- # Use the +id+ argument to narrow down the assertion to only statements
348
- # that update or insert an element with that identifier.
349
- #
350
- # Use the first argument to narrow down assertions to only statements
351
- # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
352
- # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>,
353
- # <tt>:insert_html</tt> and <tt>:redirect</tt>.
354
- #
355
- # Use the argument <tt>:insert</tt> followed by an insertion position to narrow
356
- # down the assertion to only statements that insert elements in that
357
- # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
358
- # and <tt>:after</tt>.
359
- #
360
- # Use the argument <tt>:redirect</tt> followed by a path to check that an statement
361
- # which redirects to the specified path is generated.
362
- #
363
- # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
364
- # be ignored as there is no HTML passed for this statement.
365
- #
366
- # === Using blocks
367
- #
368
- # Without a block, +assert_select_rjs+ merely asserts that the response
369
- # contains one or more RJS statements that replace or update content.
370
- #
371
- # With a block, +assert_select_rjs+ also selects all elements used in
372
- # these statements and passes them to the block. Nested assertions are
373
- # supported.
374
- #
375
- # Calling +assert_select_rjs+ with no arguments and using nested asserts
376
- # asserts that the HTML content is returned by one or more RJS statements.
377
- # Using +assert_select+ directly makes the same assertion on the content,
378
- # but without distinguishing whether the content is returned in an HTML
379
- # or JavaScript.
380
- #
381
- # ==== Examples
382
- #
383
- # # Replacing the element foo.
384
- # # page.replace 'foo', ...
385
- # assert_select_rjs :replace, "foo"
386
- #
387
- # # Replacing with the chained RJS proxy.
388
- # # page[:foo].replace ...
389
- # assert_select_rjs :chained_replace, 'foo'
390
- #
391
- # # Inserting into the element bar, top position.
392
- # assert_select_rjs :insert, :top, "bar"
393
- #
394
- # # Remove the element bar
395
- # assert_select_rjs :remove, "bar"
396
- #
397
- # # Changing the element foo, with an image.
398
- # assert_select_rjs "foo" do
399
- # assert_select "img[src=/images/logo.gif""
400
- # end
401
- #
402
- # # RJS inserts or updates a list with four items.
403
- # assert_select_rjs do
404
- # assert_select "ol>li", 4
405
- # end
406
- #
407
- # # The same, but shorter.
408
- # assert_select "ol>li", 4
409
- #
410
- # # Checking for a redirect.
411
- # assert_select_rjs :redirect, root_path
412
- def assert_select_rjs(*args, &block)
413
- rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
414
- id = args.first.is_a?(String) ? args.shift : nil
415
-
416
- # If the first argument is a symbol, it's the type of RJS statement we're looking
417
- # for (update, replace, insertion, etc). Otherwise, we're looking for just about
418
- # any RJS statement.
419
- if rjs_type
420
- if rjs_type == :insert
421
- position = args.shift
422
- id = args.shift
423
- insertion = "insert_#{position}".to_sym
424
- raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
425
- statement = "(#{RJS_STATEMENTS[insertion]})"
426
- else
427
- raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
428
- statement = "(#{RJS_STATEMENTS[rjs_type]})"
429
- end
430
- else
431
- statement = "#{RJS_STATEMENTS[:any]}"
432
- end
433
-
434
- # Next argument we're looking for is the element identifier. If missing, we pick
435
- # any element, otherwise we replace it in the statement.
436
- pattern = Regexp.new(
437
- id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
438
- )
439
-
440
- # Duplicate the body since the next step involves destroying it.
441
- matches = nil
442
- case rjs_type
443
- when :remove, :show, :hide, :toggle
444
- matches = @response.body.match(pattern)
445
- else
446
- @response.body.gsub(pattern) do |match|
447
- html = unescape_rjs(match)
448
- matches ||= []
449
- matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
450
- ""
451
- end
452
- end
453
-
454
- if matches
455
- assert_block("") { true } # to count the assertion
456
- if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
457
- begin
458
- in_scope, @selected = @selected, matches
459
- yield matches
460
- ensure
461
- @selected = in_scope
462
- end
463
- end
464
- matches
465
- else
466
- # RJS statement not found.
467
- case rjs_type
468
- when :remove, :show, :hide, :toggle
469
- flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered."
470
- else
471
- flunk_message = "No RJS statement that replaces or inserts HTML content."
472
- end
473
- flunk args.shift || flunk_message
474
- end
475
- end
476
-
477
- # :call-seq:
478
- # assert_select_encoded(element?) { |elements| ... }
479
- #
480
329
  # Extracts the content of an element, treats it as encoded HTML and runs
481
330
  # nested assertion on it.
482
331
  #
@@ -529,8 +378,8 @@ module ActionDispatch
529
378
  node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
530
379
  end
531
380
 
532
- selected = elements.map do |element|
533
- text = element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
381
+ selected = elements.map do |_element|
382
+ text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
534
383
  root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
535
384
  css_select(root, "encoded:root", &block)[0]
536
385
  end
@@ -543,9 +392,6 @@ module ActionDispatch
543
392
  end
544
393
  end
545
394
 
546
- # :call-seq:
547
- # assert_select_email { }
548
- #
549
395
  # Extracts the body of an email and runs nested assertions on it.
550
396
  #
551
397
  # You must enable deliveries for this assertion to work, use:
@@ -569,9 +415,9 @@ module ActionDispatch
569
415
  assert !deliveries.empty?, "No e-mail in delivery list"
570
416
 
571
417
  for delivery in deliveries
572
- for part in (delivery.parts.empty? ? [delivery] : delivery.parts)
418
+ for part in delivery.parts
573
419
  if part["Content-Type"].to_s =~ /^text\/html\W/
574
- root = HTML::Document.new(part.body.to_s).root
420
+ root = HTML::Document.new(part.body).root
575
421
  assert_select root, ":root", &block
576
422
  end
577
423
  end
@@ -579,62 +425,9 @@ module ActionDispatch
579
425
  end
580
426
 
581
427
  protected
582
- RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
583
- RJS_ANY_ID = "\"([^\"])*\""
584
- RJS_STATEMENTS = {
585
- :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
586
- :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
587
- :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
588
- :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
589
- :redirect => "window.location.href = #{RJS_ANY_ID}"
590
- }
591
- [:remove, :show, :hide, :toggle].each do |action|
592
- RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
593
- end
594
- RJS_INSERTIONS = ["top", "bottom", "before", "after"]
595
- RJS_INSERTIONS.each do |insertion|
596
- RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
597
- end
598
- RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
599
- RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
600
- RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
601
-
602
- # +assert_select+ and +css_select+ call this to obtain the content in the HTML
603
- # page, or from all the RJS statements, depending on the type of response.
604
- def response_from_page_or_rjs()
605
- content_type = @response.content_type
606
-
607
- if content_type && Mime::JS =~ content_type
608
- body = @response.body.dup
609
- root = HTML::Node.new(nil)
610
-
611
- while true
612
- next if body.sub!(RJS_STATEMENTS[:any]) do |match|
613
- html = unescape_rjs(match)
614
- matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
615
- root.children.concat matches
616
- ""
617
- end
618
- break
619
- end
620
-
621
- root
622
- else
623
- html_document.root
624
- end
625
- end
626
-
627
- # Unescapes a RJS string.
628
- def unescape_rjs(rjs_string)
629
- # RJS encodes double quotes and line breaks.
630
- unescaped= rjs_string.gsub('\"', '"')
631
- unescaped.gsub!(/\\\//, '/')
632
- unescaped.gsub!('\n', "\n")
633
- unescaped.gsub!('\076', '>')
634
- unescaped.gsub!('\074', '<')
635
- # RJS encodes non-ascii characters.
636
- unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
637
- unescaped
428
+ # +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
429
+ def response_from_page
430
+ html_document.root
638
431
  end
639
432
  end
640
433
  end