actionpack 4.0.0.beta1 → 4.0.0.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +195 -11
  3. data/lib/abstract_controller/base.rb +1 -1
  4. data/lib/abstract_controller/helpers.rb +2 -2
  5. data/lib/abstract_controller/layouts.rb +10 -5
  6. data/lib/abstract_controller/rendering.rb +11 -3
  7. data/lib/abstract_controller/translation.rb +1 -1
  8. data/lib/action_controller/log_subscriber.rb +5 -0
  9. data/lib/action_controller/metal.rb +2 -3
  10. data/lib/action_controller/metal/force_ssl.rb +52 -17
  11. data/lib/action_controller/metal/helpers.rb +0 -1
  12. data/lib/action_controller/metal/hide_actions.rb +1 -1
  13. data/lib/action_controller/metal/http_authentication.rb +3 -2
  14. data/lib/action_controller/metal/live.rb +34 -0
  15. data/lib/action_controller/metal/rendering.rb +1 -1
  16. data/lib/action_controller/metal/strong_parameters.rb +7 -3
  17. data/lib/action_controller/test_case.rb +45 -11
  18. data/lib/action_dispatch.rb +4 -6
  19. data/lib/action_dispatch/http/cache.rb +2 -2
  20. data/lib/action_dispatch/http/headers.rb +39 -15
  21. data/lib/action_dispatch/http/mime_negotiation.rb +1 -1
  22. data/lib/action_dispatch/http/mime_type.rb +11 -3
  23. data/lib/action_dispatch/http/parameters.rb +17 -24
  24. data/lib/action_dispatch/http/request.rb +17 -2
  25. data/lib/action_dispatch/http/response.rb +2 -1
  26. data/lib/action_dispatch/http/upload.rb +5 -5
  27. data/lib/action_dispatch/http/url.rb +53 -12
  28. data/lib/action_dispatch/journey/formatter.rb +1 -1
  29. data/lib/action_dispatch/journey/path/pattern.rb +1 -1
  30. data/lib/action_dispatch/journey/route.rb +8 -0
  31. data/lib/action_dispatch/journey/router.rb +3 -1
  32. data/lib/action_dispatch/journey/visitors.rb +8 -0
  33. data/lib/action_dispatch/middleware/cookies.rb +169 -135
  34. data/lib/action_dispatch/middleware/exception_wrapper.rb +1 -0
  35. data/lib/action_dispatch/middleware/remote_ip.rb +2 -2
  36. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  37. data/lib/action_dispatch/middleware/session/cookie_store.rb +38 -58
  38. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +1 -1
  39. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +4 -6
  40. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +1 -1
  41. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +1 -1
  42. data/lib/action_dispatch/routing.rb +28 -64
  43. data/lib/action_dispatch/routing/mapper.rb +61 -48
  44. data/lib/action_dispatch/routing/route_set.rb +17 -14
  45. data/lib/action_dispatch/testing/assertions/routing.rb +2 -2
  46. data/lib/action_dispatch/testing/assertions/selector.rb +2 -2
  47. data/lib/action_dispatch/testing/integration.rb +36 -35
  48. data/lib/action_dispatch/testing/test_process.rb +1 -1
  49. data/lib/action_pack/version.rb +7 -6
  50. data/lib/action_view/buffers.rb +6 -0
  51. data/lib/action_view/dependency_tracker.rb +3 -1
  52. data/lib/action_view/helpers/asset_tag_helper.rb +13 -8
  53. data/lib/action_view/helpers/capture_helper.rb +2 -2
  54. data/lib/action_view/helpers/date_helper.rb +1 -1
  55. data/lib/action_view/helpers/form_helper.rb +56 -19
  56. data/lib/action_view/helpers/form_options_helper.rb +3 -3
  57. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  58. data/lib/action_view/helpers/javascript_helper.rb +2 -2
  59. data/lib/action_view/helpers/number_helper.rb +25 -0
  60. data/lib/action_view/helpers/tags/base.rb +9 -10
  61. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  62. data/lib/action_view/helpers/tags/checkable.rb +2 -2
  63. data/lib/action_view/helpers/tags/collection_check_boxes.rb +3 -3
  64. data/lib/action_view/helpers/tags/collection_helpers.rb +3 -3
  65. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -3
  66. data/lib/action_view/helpers/tags/collection_select.rb +1 -1
  67. data/lib/action_view/helpers/tags/color_field.rb +2 -2
  68. data/lib/action_view/helpers/tags/date_field.rb +2 -2
  69. data/lib/action_view/helpers/tags/date_select.rb +2 -2
  70. data/lib/action_view/helpers/tags/datetime_field.rb +2 -2
  71. data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -2
  72. data/lib/action_view/helpers/tags/datetime_select.rb +2 -2
  73. data/lib/action_view/helpers/tags/email_field.rb +2 -2
  74. data/lib/action_view/helpers/tags/file_field.rb +2 -2
  75. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  76. data/lib/action_view/helpers/tags/hidden_field.rb +2 -2
  77. data/lib/action_view/helpers/tags/label.rb +2 -2
  78. data/lib/action_view/helpers/tags/month_field.rb +2 -2
  79. data/lib/action_view/helpers/tags/number_field.rb +2 -2
  80. data/lib/action_view/helpers/tags/password_field.rb +2 -2
  81. data/lib/action_view/helpers/tags/radio_button.rb +2 -2
  82. data/lib/action_view/helpers/tags/range_field.rb +2 -2
  83. data/lib/action_view/helpers/tags/search_field.rb +2 -2
  84. data/lib/action_view/helpers/tags/select.rb +2 -3
  85. data/lib/action_view/helpers/tags/tel_field.rb +2 -2
  86. data/lib/action_view/helpers/tags/text_area.rb +2 -2
  87. data/lib/action_view/helpers/tags/text_field.rb +2 -2
  88. data/lib/action_view/helpers/tags/time_field.rb +2 -2
  89. data/lib/action_view/helpers/tags/time_select.rb +2 -2
  90. data/lib/action_view/helpers/tags/time_zone_select.rb +2 -2
  91. data/lib/action_view/helpers/tags/url_field.rb +2 -2
  92. data/lib/action_view/helpers/tags/week_field.rb +2 -2
  93. data/lib/action_view/helpers/text_helper.rb +8 -5
  94. data/lib/action_view/helpers/url_helper.rb +18 -6
  95. data/lib/action_view/lookup_context.rb +7 -1
  96. data/lib/action_view/path_set.rb +6 -0
  97. data/lib/action_view/renderer/abstract_renderer.rb +15 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +14 -0
  99. data/lib/action_view/renderer/renderer.rb +6 -0
  100. data/lib/action_view/template.rb +3 -2
  101. data/lib/action_view/template/handlers/erb.rb +29 -3
  102. data/lib/action_view/template/resolver.rb +3 -3
  103. data/lib/action_view/test_case.rb +1 -0
  104. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +5 -5
  105. data/lib/action_view/vendor/html-scanner/html/selector.rb +8 -8
  106. metadata +8 -8
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class DatetimeSelect < DateSelect #:nodoc:
3
+ module Tags # :nodoc:
4
+ class DatetimeSelect < DateSelect # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class EmailField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class EmailField < TextField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class FileField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class FileField < TextField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class GroupedCollectionSelect < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class GroupedCollectionSelect < Base # :nodoc:
5
5
  def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
6
6
  @collection = collection
7
7
  @group_method = group_method
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class HiddenField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class HiddenField < TextField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class Label < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class Label < Base # :nodoc:
5
5
  def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
6
6
  options ||= {}
7
7
 
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class MonthField < DatetimeField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class MonthField < DatetimeField # :nodoc:
5
5
  private
6
6
 
7
7
  def format_date(value)
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class NumberField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class NumberField < TextField # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
 
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class PasswordField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class PasswordField < TextField # :nodoc:
5
5
  def render
6
6
  @options = {:value => nil}.merge!(@options)
7
7
  super
@@ -2,8 +2,8 @@ require 'action_view/helpers/tags/checkable'
2
2
 
3
3
  module ActionView
4
4
  module Helpers
5
- module Tags
6
- class RadioButton < Base #:nodoc:
5
+ module Tags # :nodoc:
6
+ class RadioButton < Base # :nodoc:
7
7
  include Checkable
8
8
 
9
9
  def initialize(object_name, method_name, template_object, tag_value, options)
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class RangeField < NumberField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class RangeField < NumberField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class SearchField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class SearchField < TextField # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
 
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class Select < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class Select < Base # :nodoc:
5
5
  def initialize(object_name, method_name, template_object, choices, options, html_options)
6
6
  @choices = choices
7
7
  @choices = @choices.to_a if @choices.is_a?(Range)
@@ -31,7 +31,6 @@ module ActionView
31
31
  #
32
32
  # [nil, []]
33
33
  # { nil => [] }
34
- #
35
34
  def grouped_choices?
36
35
  !@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
37
36
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TelField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TelField < TextField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TextArea < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TextArea < Base # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
  add_default_name_and_id(options)
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TextField < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TextField < Base # :nodoc:
5
5
  def render
6
6
  options = @options.stringify_keys
7
7
  options["size"] = options["maxlength"] unless options.key?("size")
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TimeField < DatetimeField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TimeField < DatetimeField # :nodoc:
5
5
  private
6
6
 
7
7
  def format_date(value)
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TimeSelect < DateSelect #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TimeSelect < DateSelect # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class TimeZoneSelect < Base #:nodoc:
3
+ module Tags # :nodoc:
4
+ class TimeZoneSelect < Base # :nodoc:
5
5
  def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
6
6
  @priority_zones = priority_zones
7
7
  @html_options = html_options
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class UrlField < TextField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class UrlField < TextField # :nodoc:
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module ActionView
2
2
  module Helpers
3
- module Tags
4
- class WeekField < DatetimeField #:nodoc:
3
+ module Tags # :nodoc:
4
+ class WeekField < DatetimeField # :nodoc:
5
5
  private
6
6
 
7
7
  def format_date(value)
@@ -126,8 +126,8 @@ module ActionView
126
126
  # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
127
127
  # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
128
128
  # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
129
- # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
130
- # <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
129
+ # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
130
+ # <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
131
131
  # isn't found, nil is returned.
132
132
  #
133
133
  # excerpt('This is an example', 'an', radius: 5)
@@ -145,7 +145,7 @@ module ActionView
145
145
  # excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
146
146
  # # => <chop> is also an example
147
147
  #
148
- # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
148
+ # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
149
149
  # # => ...a very beautiful...
150
150
  def excerpt(text, phrase, options = {})
151
151
  return unless text && phrase
@@ -250,8 +250,11 @@ module ActionView
250
250
  # simple_format("Look ma! A class!", class: 'description')
251
251
  # # => "<p class='description'>Look ma! A class!</p>"
252
252
  #
253
- # simple_format("<span>I'm allowed!</span> It's true.", {}, sanitize: false)
254
- # # => "<p><span>I'm allowed!</span> It's true.</p>"
253
+ # simple_format("<blink>Unblinkable.</blink>")
254
+ # # => "<p>Unblinkable.</p>"
255
+ #
256
+ # simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
257
+ # # => "<p><blink>Blinkable!</span> It's true.</p>"
255
258
  def simple_format(text, html_options = {}, options = {})
256
259
  wrapper_tag = options.fetch(:wrapper_tag, :p)
257
260
 
@@ -425,8 +425,8 @@ module ActionView
425
425
  # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
426
426
  #
427
427
  # ==== Obfuscation
428
- # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
429
- # in order to hinder email harvesters. To take advantage of these options,
428
+ # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
429
+ # in order to hinder email harvesters. To take advantage of these options,
430
430
  # install the +actionview-encoded_mail_to+ gem.
431
431
  #
432
432
  # ==== Examples
@@ -439,18 +439,30 @@ module ActionView
439
439
  # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
440
440
  # subject: "This is an example email"
441
441
  # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
442
- def mail_to(email_address, name = nil, html_options = {})
442
+ #
443
+ # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
444
+ #
445
+ # <%= mail_to "me@domain.com" do %>
446
+ # <strong>Email me:</strong> <span>me@domain.com</span>
447
+ # <% end %>
448
+ # # => <a href="mailto:me@domain.com">
449
+ # <strong>Email me:</strong> <span>me@domain.com</span>
450
+ # </a>
451
+ def mail_to(email_address, name = nil, html_options = {}, &block)
443
452
  email_address = ERB::Util.html_escape(email_address)
444
453
 
445
- html_options.stringify_keys!
454
+ html_options, name = name, nil if block_given?
455
+ html_options = (html_options || {}).stringify_keys
446
456
 
447
457
  extras = %w{ cc bcc body subject }.map { |item|
448
458
  option = html_options.delete(item) || next
449
459
  "#{item}=#{Rack::Utils.escape_path(option)}"
450
460
  }.compact
451
461
  extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
452
-
453
- content_tag "a", name || email_address.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
462
+
463
+ html_options["href"] = "mailto:#{email_address}#{extras}".html_safe
464
+
465
+ content_tag(:a, name || email_address.html_safe, html_options, &block)
454
466
  end
455
467
 
456
468
  # True if the current request URI was generated by the given +options+.
@@ -43,7 +43,13 @@ module ActionView
43
43
  module Accessors #:nodoc:
44
44
  end
45
45
 
46
- register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
46
+ register_detail(:locale) do
47
+ locales = [I18n.locale]
48
+ locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks
49
+ locales << I18n.default_locale
50
+ locales.uniq!
51
+ locales
52
+ end
47
53
  register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
48
54
  register_detail(:handlers){ Template::Handlers.extensions }
49
55
 
@@ -1,5 +1,11 @@
1
1
  module ActionView #:nodoc:
2
2
  # = Action View PathSet
3
+ #
4
+ # This class is used to store and access paths in Action View. A number of
5
+ # operations are defined so that you can search among the paths in this
6
+ # set and also perform operations on other +PathSet+ objects.
7
+ #
8
+ # A +LookupContext+ will use a +PathSet+ to store the paths in its context.
3
9
  class PathSet #:nodoc:
4
10
  include Enumerable
5
11
 
@@ -1,4 +1,19 @@
1
1
  module ActionView
2
+ # This class defines the interface for a renderer. Each class that
3
+ # subclasses +AbstractRenderer+ is used by the base +Renderer+ class to
4
+ # render a specific type of object.
5
+ #
6
+ # The base +Renderer+ class uses its +render+ method to delegate to the
7
+ # renderers. These currently consist of
8
+ #
9
+ # PartialRenderer - Used for rendering partials
10
+ # TemplateRenderer - Used for rendering other types of templates
11
+ # StreamingTemplateRenderer - Used for streaming
12
+ #
13
+ # Whenever the +render+ method is called on the base +Renderer+ class, a new
14
+ # renderer object of the correct type is created, and the +render+ method on
15
+ # that new object is called in turn. This abstracts the setup and rendering
16
+ # into a separate classes for partials and templates.
2
17
  class AbstractRenderer #:nodoc:
3
18
  delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
4
19
 
@@ -313,6 +313,13 @@ module ActionView
313
313
 
314
314
  private
315
315
 
316
+ # Sets up instance variables needed for rendering a partial. This method
317
+ # finds the options and details and extracts them. The method also contains
318
+ # logic that handles the type of object passed in as the partial.
319
+ #
320
+ # If +options[:partial]+ is a string, then the +@path+ instance variable is
321
+ # set to that string. Otherwise, the +options[:partial]+ object must
322
+ # respond to +to_partial_path+ in order to setup the path.
316
323
  def setup(context, options, block)
317
324
  @view = context
318
325
  partial = options[:partial]
@@ -413,6 +420,13 @@ module ActionView
413
420
  end
414
421
  end
415
422
 
423
+ # Obtains the path to where the object's partial is located. If the object
424
+ # responds to +to_partial_path+, then +to_partial_path+ will be called and
425
+ # will provide the path. If the object does not respond to +to_partial_path+,
426
+ # then an +ArgumentError+ is raised.
427
+ #
428
+ # If +prefix_partial_path_with_controller_namespace+ is true, then this
429
+ # method will prefix the partial paths with a namespace.
416
430
  def partial_path(object = @object)
417
431
  object = object.to_model if object.respond_to?(:to_model)
418
432
 
@@ -2,6 +2,12 @@ module ActionView
2
2
  # This is the main entry point for rendering. It basically delegates
3
3
  # to other objects like TemplateRenderer and PartialRenderer which
4
4
  # actually renders the template.
5
+ #
6
+ # The Renderer will parse the options from the +render+ or +render_body+
7
+ # method and render a partial or a template based on the options. The
8
+ # +TemplateRenderer+ and +PartialRenderer+ objects are wrappers which do all
9
+ # the setup and logic necessary to render a view and a new object is created
10
+ # each time +render+ is called.
5
11
  class Renderer
6
12
  attr_accessor :lookup_context
7
13
 
@@ -138,7 +138,7 @@ module ActionView
138
138
  # we use a bang in this instrumentation because you don't want to
139
139
  # consume this in production. This is only slow if it's being listened to.
140
140
  def render(view, locals, buffer=nil, &block)
141
- ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
141
+ ActiveSupport::Notifications.instrument("!render_template.action_view", virtual_path: @virtual_path, identifier: @identifier) do
142
142
  compile!(view)
143
143
  view.send(method_name, locals, buffer, &block)
144
144
  end
@@ -324,7 +324,8 @@ module ActionView
324
324
  end
325
325
 
326
326
  def locals_code #:nodoc:
327
- @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
327
+ # Double assign to suppress the dreaded 'assigned but unused variable' warning
328
+ @locals.map { |key| "#{key} = #{key} = local_assigns[:#{key}];" }.join
328
329
  end
329
330
 
330
331
  def method_name #:nodoc:
@@ -6,12 +6,23 @@ module ActionView
6
6
  module Handlers
7
7
  class Erubis < ::Erubis::Eruby
8
8
  def add_preamble(src)
9
+ @newline_pending = 0
9
10
  src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
10
11
  end
11
12
 
12
13
  def add_text(src, text)
13
14
  return if text.empty?
14
- src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
15
+
16
+ if text == "\n"
17
+ @newline_pending += 1
18
+ else
19
+ src << "@output_buffer.safe_append='"
20
+ src << "\n" * @newline_pending if @newline_pending > 0
21
+ src << escape_text(text)
22
+ src << "';"
23
+
24
+ @newline_pending = 0
25
+ end
15
26
  end
16
27
 
17
28
  # Erubis toggles <%= and <%== behavior when escaping is enabled.
@@ -28,24 +39,39 @@ module ActionView
28
39
  BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
29
40
 
30
41
  def add_expr_literal(src, code)
42
+ flush_newline_if_pending(src)
31
43
  if code =~ BLOCK_EXPR
32
44
  src << '@output_buffer.append= ' << code
33
45
  else
34
- src << '@output_buffer.append= (' << code << ');'
46
+ src << '@output_buffer.append=(' << code << ');'
35
47
  end
36
48
  end
37
49
 
38
50
  def add_expr_escaped(src, code)
51
+ flush_newline_if_pending(src)
39
52
  if code =~ BLOCK_EXPR
40
53
  src << "@output_buffer.safe_append= " << code
41
54
  else
42
- src << "@output_buffer.safe_concat((" << code << ").to_s);"
55
+ src << "@output_buffer.safe_append=(" << code << ");"
43
56
  end
44
57
  end
45
58
 
59
+ def add_stmt(src, code)
60
+ flush_newline_if_pending(src)
61
+ super
62
+ end
63
+
46
64
  def add_postamble(src)
65
+ flush_newline_if_pending(src)
47
66
  src << '@output_buffer.to_s'
48
67
  end
68
+
69
+ def flush_newline_if_pending(src)
70
+ if @newline_pending > 0
71
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}';"
72
+ @newline_pending = 0
73
+ end
74
+ end
49
75
  end
50
76
 
51
77
  class ERB