Skip to content

Commit 9de8305

Browse files
author
Carlhuda
committed
Add deprecation notices for <% %>.
* The approach is to compile <% %> into a method call that checks whether the value returned from a block is a String. If it is, it concats to the buffer and prints a deprecation warning. * <%= %> uses exactly the same logic to compile the template, which first checks to see whether it's compiling a block. * This should have no impact on other uses of block in templates. For instance, in <% [1,2,3].each do |i| %><%= i %><% end %>, the call to each returns an Array, not a String, so the result is not concatenated * In two cases (#capture and #cache), a String can be returned that should *never* be concatenated. We have temporarily created a String subclass called NonConcattingString which behaves (and is serialized) identically to String, but is not concatenated by the code that handles deprecated <% %> block helpers. Once we remove support for <% %> block helpers, we can remove NonConcattingString.
1 parent 1f27382 commit 9de8305

File tree

15 files changed

+88
-97
lines changed

15 files changed

+88
-97
lines changed

actionpack/lib/action_controller/caching/fragments.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,23 @@ def fragment_cache_key(key)
3434
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
3535
end
3636

37-
def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
37+
def fragment_for(name = {}, options = nil, &block) #:nodoc:
3838
if perform_caching
3939
if fragment_exist?(name, options)
40-
buffer.safe_concat(read_fragment(name, options))
40+
read_fragment(name, options)
4141
else
42+
# VIEW TODO: Make #capture usable outside of ERB
43+
# This dance is needed because Builder can't use capture
44+
buffer = view_context.output_buffer
4245
pos = buffer.length
43-
block.call
44-
write_fragment(name, buffer[pos..-1], options)
46+
yield
47+
fragment = buffer[pos..-1]
48+
write_fragment(name, fragment, options)
49+
ActionView::NonConcattingString.new(fragment)
4550
end
4651
else
47-
block.call
52+
ret = yield
53+
ActiveSupport::SafeBuffer.new(ret) if ret.is_a?(String)
4854
end
4955
end
5056

actionpack/lib/action_view/base.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
require 'active_support/core_ext/class/attribute'
44

55
module ActionView #:nodoc:
6+
class NonConcattingString < ActiveSupport::SafeBuffer
7+
end
8+
69
class ActionViewError < StandardError #:nodoc:
710
end
811

actionpack/lib/action_view/helpers/cache_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module CacheHelper
3232
# <i>Topics listed alphabetically</i>
3333
# <% end %>
3434
def cache(name = {}, options = nil, &block)
35-
controller.fragment_for(output_buffer, name, options, &block)
35+
controller.fragment_for(name, options, &block)
3636
end
3737
end
3838
end

actionpack/lib/action_view/helpers/capture_helper.rb

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ module ActionView
22
module Helpers
33
# CaptureHelper exposes methods to let you extract generated markup which
44
# can be used in other parts of a template or layout file.
5-
# It provides a method to capture blocks into variables through capture and
5+
# It provides a method to capture blocks into variables through capture and
66
# a way to capture a block of markup for use in a layout through content_for.
77
module CaptureHelper
8-
# The capture method allows you to extract part of a template into a
9-
# variable. You can then use this variable anywhere in your templates or layout.
10-
#
8+
# The capture method allows you to extract part of a template into a
9+
# variable. You can then use this variable anywhere in your templates or layout.
10+
#
1111
# ==== Examples
1212
# The capture method can be used in ERb templates...
13-
#
13+
#
1414
# <% @greeting = capture do %>
1515
# Welcome to my shiny new web page! The date and time is
1616
# <%= Time.now %>
1717
# <% end %>
1818
#
1919
# ...and Builder (RXML) templates.
20-
#
20+
#
2121
# @timestamp = capture do
2222
# "The current timestamp is #{Time.now}."
2323
# end
@@ -33,15 +33,17 @@ module CaptureHelper
3333
def capture(*args)
3434
value = nil
3535
buffer = with_output_buffer { value = yield *args }
36-
buffer.presence || value
36+
if string = buffer.presence || value and string.is_a?(String)
37+
NonConcattingString.new(string)
38+
end
3739
end
3840

3941
# Calling content_for stores a block of markup in an identifier for later use.
4042
# You can make subsequent calls to the stored content in other templates or the layout
4143
# by passing the identifier as an argument to <tt>yield</tt>.
42-
#
44+
#
4345
# ==== Examples
44-
#
46+
#
4547
# <% content_for :not_authorized do %>
4648
# alert('You are not authorized to do that!')
4749
# <% end %>
@@ -92,7 +94,7 @@ def capture(*args)
9294
# <% end %>
9395
#
9496
# <%# Add some other content, or use a different template: %>
95-
#
97+
#
9698
# <% content_for :navigation do %>
9799
# <li><%= link_to 'Login', :action => 'login' %></li>
98100
# <% end %>
@@ -109,13 +111,13 @@ def capture(*args)
109111
# for elements that will be fragment cached.
110112
def content_for(name, content = nil, &block)
111113
content = capture(&block) if block_given?
112-
return @_content_for[name] << content if content
113-
@_content_for[name]
114+
@_content_for[name] << content if content
115+
@_content_for[name] unless content
114116
end
115117

116118
# content_for? simply checks whether any content has been captured yet using content_for
117119
# Useful to render parts of your layout differently based on what is in your views.
118-
#
120+
#
119121
# ==== Examples
120122
#
121123
# Perhaps you will use different css in you layout if no content_for :right_column

actionpack/lib/action_view/helpers/deprecated_block_helpers.rb

Lines changed: 0 additions & 52 deletions
This file was deleted.

actionpack/lib/action_view/helpers/prototype_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,10 @@ def initialize(generator, root = nil)
689689
@generator << root if root
690690
end
691691

692+
def is_a?(klass)
693+
klass == JavaScriptProxy
694+
end
695+
692696
private
693697
def method_missing(method, *arguments, &block)
694698
if method.to_s =~ /(.*)=$/

actionpack/lib/action_view/render/rendering.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ def render(options = {}, locals = {}, &block)
1616
case options
1717
when Hash
1818
if block_given?
19-
content = _render_partial(options.merge(:partial => options[:layout]), &block)
20-
safe_concat(content)
19+
_render_partial(options.merge(:partial => options[:layout]), &block)
2120
elsif options.key?(:partial)
2221
_render_partial(options)
2322
else

actionpack/lib/action_view/template/handlers/erb.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ def <<(value)
88
super(value.to_s)
99
end
1010
alias :append= :<<
11+
12+
def append_if_string=(value)
13+
if value.is_a?(String) && !value.is_a?(NonConcattingString)
14+
ActiveSupport::Deprecation.warn("<% %> style block helpers are deprecated. Please use <%= %>", caller)
15+
self << value
16+
end
17+
end
1118
end
1219

1320
module Template::Handlers
@@ -21,14 +28,24 @@ def add_text(src, text)
2128
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
2229
end
2330

31+
BLOCK_EXPR = /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
32+
2433
def add_expr_literal(src, code)
25-
if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
34+
if code =~ BLOCK_EXPR
2635
src << '@output_buffer.append= ' << code
2736
else
2837
src << '@output_buffer.append= (' << code << ');'
2938
end
3039
end
3140

41+
def add_stmt(src, code)
42+
if code =~ BLOCK_EXPR
43+
src << '@output_buffer.append_if_string= ' << code
44+
else
45+
super
46+
end
47+
end
48+
3249
def add_expr_escaped(src, code)
3350
src << '@output_buffer.append= ' << escaped_expr(code) << ';'
3451
end

actionpack/test/controller/caching_test.rb

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ def test_fragment_for_with_disabled_caching
617617
fragment_computed = false
618618

619619
buffer = 'generated till now -> '.html_safe
620-
@controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
620+
buffer << @controller.fragment_for('expensive') { fragment_computed = true }
621621

622622
assert fragment_computed
623623
assert_equal 'generated till now -> ', buffer
@@ -628,7 +628,7 @@ def test_fragment_for
628628
fragment_computed = false
629629

630630
buffer = 'generated till now -> '.html_safe
631-
@controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
631+
buffer << @controller.fragment_for('expensive') { fragment_computed = true }
632632

633633
assert !fragment_computed
634634
assert_equal 'generated till now -> fragment content', buffer
@@ -742,15 +742,4 @@ def test_xml_formatted_fragment_caching
742742

743743
assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
744744
end
745-
746-
def test_js_formatted_fragment_caching
747-
get :formatted_fragment_cached, :format => "js"
748-
assert_response :success
749-
expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
750-
%($("element_2").visualEffect("highlight");\nfooter = "Bye";)
751-
assert_equal expected_body, @response.body
752-
753-
assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
754-
@store.read('views/test.host/functional_caching/formatted_fragment_cached')
755-
end
756745
end
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
1+
<%= render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
22
<%= yield %>
3-
<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
3+
<%= render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>

0 commit comments

Comments
 (0)