Skip to content

Commit

Permalink
Add deprecation notices for <% %>.
Browse files Browse the repository at this point in the history
  * 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.
  • Loading branch information
Carlhuda committed Mar 15, 2010
1 parent 1f27382 commit 9de8305
Show file tree
Hide file tree
Showing 15 changed files with 88 additions and 97 deletions.
16 changes: 11 additions & 5 deletions actionpack/lib/action_controller/caching/fragments.rb
Expand Up @@ -34,17 +34,23 @@ def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end

def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
def fragment_for(name = {}, options = nil, &block) #:nodoc:
if perform_caching
if fragment_exist?(name, options)
buffer.safe_concat(read_fragment(name, options))
read_fragment(name, options)
else
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
buffer = view_context.output_buffer
pos = buffer.length
block.call
write_fragment(name, buffer[pos..-1], options)
yield
fragment = buffer[pos..-1]
write_fragment(name, fragment, options)
ActionView::NonConcattingString.new(fragment)
end
else
block.call
ret = yield
ActiveSupport::SafeBuffer.new(ret) if ret.is_a?(String)
end
end

Expand Down
3 changes: 3 additions & 0 deletions actionpack/lib/action_view/base.rb
Expand Up @@ -3,6 +3,9 @@
require 'active_support/core_ext/class/attribute'

module ActionView #:nodoc:
class NonConcattingString < ActiveSupport::SafeBuffer
end

class ActionViewError < StandardError #:nodoc:
end

Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_view/helpers/cache_helper.rb
Expand Up @@ -32,7 +32,7 @@ module CacheHelper
# <i>Topics listed alphabetically</i>
# <% end %>
def cache(name = {}, options = nil, &block)
controller.fragment_for(output_buffer, name, options, &block)
controller.fragment_for(name, options, &block)
end
end
end
Expand Down
28 changes: 15 additions & 13 deletions actionpack/lib/action_view/helpers/capture_helper.rb
Expand Up @@ -2,22 +2,22 @@ module ActionView
module Helpers
# CaptureHelper exposes methods to let you extract generated markup which
# can be used in other parts of a template or layout file.
# It provides a method to capture blocks into variables through capture and
# It provides a method to capture blocks into variables through capture and
# a way to capture a block of markup for use in a layout through content_for.
module CaptureHelper
# The capture method allows you to extract part of a template into a
# variable. You can then use this variable anywhere in your templates or layout.
#
# The capture method allows you to extract part of a template into a
# variable. You can then use this variable anywhere in your templates or layout.
#
# ==== Examples
# The capture method can be used in ERb templates...
#
#
# <% @greeting = capture do %>
# Welcome to my shiny new web page! The date and time is
# <%= Time.now %>
# <% end %>
#
# ...and Builder (RXML) templates.
#
#
# @timestamp = capture do
# "The current timestamp is #{Time.now}."
# end
Expand All @@ -33,15 +33,17 @@ module CaptureHelper
def capture(*args)
value = nil
buffer = with_output_buffer { value = yield *args }
buffer.presence || value
if string = buffer.presence || value and string.is_a?(String)
NonConcattingString.new(string)
end
end

# Calling content_for stores a block of markup in an identifier for later use.
# You can make subsequent calls to the stored content in other templates or the layout
# by passing the identifier as an argument to <tt>yield</tt>.
#
#
# ==== Examples
#
#
# <% content_for :not_authorized do %>
# alert('You are not authorized to do that!')
# <% end %>
Expand Down Expand Up @@ -92,7 +94,7 @@ def capture(*args)
# <% end %>
#
# <%# Add some other content, or use a different template: %>
#
#
# <% content_for :navigation do %>
# <li><%= link_to 'Login', :action => 'login' %></li>
# <% end %>
Expand All @@ -109,13 +111,13 @@ def capture(*args)
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
return @_content_for[name] << content if content
@_content_for[name]
@_content_for[name] << content if content
@_content_for[name] unless content
end

# content_for? simply checks whether any content has been captured yet using content_for
# Useful to render parts of your layout differently based on what is in your views.
#
#
# ==== Examples
#
# Perhaps you will use different css in you layout if no content_for :right_column
Expand Down
52 changes: 0 additions & 52 deletions actionpack/lib/action_view/helpers/deprecated_block_helpers.rb

This file was deleted.

4 changes: 4 additions & 0 deletions actionpack/lib/action_view/helpers/prototype_helper.rb
Expand Up @@ -689,6 +689,10 @@ def initialize(generator, root = nil)
@generator << root if root
end

def is_a?(klass)
klass == JavaScriptProxy
end

private
def method_missing(method, *arguments, &block)
if method.to_s =~ /(.*)=$/
Expand Down
3 changes: 1 addition & 2 deletions actionpack/lib/action_view/render/rendering.rb
Expand Up @@ -16,8 +16,7 @@ def render(options = {}, locals = {}, &block)
case options
when Hash
if block_given?
content = _render_partial(options.merge(:partial => options[:layout]), &block)
safe_concat(content)
_render_partial(options.merge(:partial => options[:layout]), &block)
elsif options.key?(:partial)
_render_partial(options)
else
Expand Down
19 changes: 18 additions & 1 deletion actionpack/lib/action_view/template/handlers/erb.rb
Expand Up @@ -8,6 +8,13 @@ def <<(value)
super(value.to_s)
end
alias :append= :<<

def append_if_string=(value)
if value.is_a?(String) && !value.is_a?(NonConcattingString)
ActiveSupport::Deprecation.warn("<% %> style block helpers are deprecated. Please use <%= %>", caller)
self << value
end
end
end

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

BLOCK_EXPR = /(do|\{)(\s*\|[^|]*\|)?\s*\Z/

def add_expr_literal(src, code)
if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
if code =~ BLOCK_EXPR
src << '@output_buffer.append= ' << code
else
src << '@output_buffer.append= (' << code << ');'
end
end

def add_stmt(src, code)
if code =~ BLOCK_EXPR
src << '@output_buffer.append_if_string= ' << code
else
super
end
end

def add_expr_escaped(src, code)
src << '@output_buffer.append= ' << escaped_expr(code) << ';'
end
Expand Down
15 changes: 2 additions & 13 deletions actionpack/test/controller/caching_test.rb
Expand Up @@ -617,7 +617,7 @@ def test_fragment_for_with_disabled_caching
fragment_computed = false

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

assert fragment_computed
assert_equal 'generated till now -> ', buffer
Expand All @@ -628,7 +628,7 @@ def test_fragment_for
fragment_computed = false

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

assert !fragment_computed
assert_equal 'generated till now -> fragment content', buffer
Expand Down Expand Up @@ -742,15 +742,4 @@ def test_xml_formatted_fragment_caching

assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
end

def test_js_formatted_fragment_caching
get :formatted_fragment_cached, :format => "js"
assert_response :success
expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
%($("element_2").visualEffect("highlight");\nfooter = "Bye";)
assert_equal expected_body, @response.body

assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
@store.read('views/test.host/functional_caching/formatted_fragment_cached')
end
end
4 changes: 2 additions & 2 deletions actionpack/test/fixtures/layouts/block_with_layout.erb
@@ -1,3 +1,3 @@
<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
<%= render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
<%= yield %>
<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
<%= render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
3 changes: 3 additions & 0 deletions actionpack/test/fixtures/test/deprecated_nested_layout.erb
@@ -0,0 +1,3 @@
<% content_for :title, "title" -%>
<% content_for :column do -%>column<% end -%>
<% render :layout => 'layouts/column' do -%>content<% end -%>
2 changes: 1 addition & 1 deletion actionpack/test/fixtures/test/nested_layout.erb
@@ -1,3 +1,3 @@
<% content_for :title, "title" -%>
<% content_for :column do -%>column<% end -%>
<% render :layout => 'layouts/column' do -%>content<% end -%>
<%= render :layout => 'layouts/column' do -%>content<% end -%>
@@ -1 +1 @@
<% render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %>
<%= render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %>
24 changes: 18 additions & 6 deletions actionpack/test/template/erb/tag_helper_test.rb
Expand Up @@ -20,7 +20,7 @@ def protect_against_forgery?() false end
end

class DeprecatedViewContext < ViewContext
include ActionView::Helpers::DeprecatedBlockHelpers
# include ActionView::Helpers::DeprecatedBlockHelpers
end

module SharedTagHelpers
Expand All @@ -31,28 +31,36 @@ def render_content(start, inside)
ActionView::Template::Handlers::Erubis.new(template).evaluate(context.new)
end

def maybe_deprecated
if @deprecated
assert_deprecated { yield }
else
yield
end
end

test "percent equals works for content_tag and does not require parenthesis on method call" do
assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world")
maybe_deprecated { assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world") }
end

test "percent equals works for javascript_tag" do
expected_output = "<script type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
assert_equal expected_output, render_content("javascript_tag", "alert('Hello')")
maybe_deprecated { assert_equal expected_output, render_content("javascript_tag", "alert('Hello')") }
end

test "percent equals works for javascript_tag with options" do
expected_output = "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
assert_equal expected_output, render_content("javascript_tag(:id => 'the_js_tag')", "alert('Hello')")
maybe_deprecated { assert_equal expected_output, render_content("javascript_tag(:id => 'the_js_tag')", "alert('Hello')") }
end

test "percent equals works with form tags" do
expected_output = "<form action=\"foo\" method=\"post\">hello</form>"
assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>")
maybe_deprecated { assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>") }
end

test "percent equals works with fieldset tags" do
expected_output = "<fieldset><legend>foo</legend>hello</fieldset>"
assert_equal expected_output, render_content("field_set_tag('foo')", "<%= 'hello' %>")
maybe_deprecated { assert_equal expected_output, render_content("field_set_tag('foo')", "<%= 'hello' %>") }
end
end

Expand All @@ -77,6 +85,10 @@ def block_helper(str, rest)
"<% __in_erb_template=true %><% #{str} do %>#{rest}<% end %>"
end

def setup
@deprecated = true
end

include SharedTagHelpers
end
end
8 changes: 8 additions & 0 deletions actionpack/test/template/render_test.rb
Expand Up @@ -228,6 +228,14 @@ def test_render_with_layout
@view.render(:file => "test/hello_world.erb", :layout => "layouts/yield")
end

# TODO: Move to deprecated_tests.rb
def test_render_with_nested_layout_deprecated
assert_deprecated do
assert_equal %(<title>title</title>\n\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
@view.render(:file => "test/deprecated_nested_layout.erb", :layout => "layouts/yield")
end
end

def test_render_with_nested_layout
assert_equal %(<title>title</title>\n\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
@view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
Expand Down

0 comments on commit 9de8305

Please sign in to comment.