mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Change ActionView ERB Handler from Erubis to Erubi
Erubi offers the following advantages for Rails: * Works with ruby's --enable-frozen-string-literal option * Has 88% smaller memory footprint * Does no freedom patching (Erubis adds a method to Kernel) * Has simpler internals (1 file, <150 lines of code) * Has an open development model (Erubis doesn't have a public source control repository or bug tracker) * Is not dead (Erubis hasn't been updated since 2011) Erubi is a simplified fork of Erubis that contains just the parts that are generally needed (which includes the parts that Rails uses). The only intentional difference in behavior is that it does not include support for <%=== tags for debug output. That could be added to the ActionView ERB handler if it is desired. The Erubis template handler remains in a deprecated state so that code that accesses it directly does not break. It can be removed after Rails 5.1.
This commit is contained in:
parent
72a869e626
commit
7da8d76206
13 changed files with 214 additions and 81 deletions
3
Gemfile
3
Gemfile
|
@ -51,6 +51,9 @@ gem "dalli", ">= 2.2.1"
|
|||
gem "listen", ">= 3.0.5", "< 3.2", require: false
|
||||
gem "libxml-ruby", platforms: :ruby
|
||||
|
||||
# Action View. For testing Erubis handler deprecation.
|
||||
gem "erubis", "~> 2.7.0", require: false
|
||||
|
||||
# Active Job.
|
||||
group :job do
|
||||
gem "resque", github: "resque/resque", require: false
|
||||
|
|
|
@ -78,7 +78,7 @@ PATH
|
|||
actionview (5.1.0.alpha)
|
||||
activesupport (= 5.1.0.alpha)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
activejob (5.1.0.alpha)
|
||||
|
@ -178,6 +178,7 @@ GEM
|
|||
http_parser.rb (>= 0.6.0)
|
||||
em-socksify (0.3.1)
|
||||
eventmachine (>= 1.0.0.beta.4)
|
||||
erubi (1.4.0)
|
||||
erubis (2.7.0)
|
||||
event_emitter (0.2.5)
|
||||
eventmachine (1.2.1)
|
||||
|
@ -390,6 +391,7 @@ DEPENDENCIES
|
|||
delayed_job!
|
||||
delayed_job_active_record!
|
||||
em-hiredis
|
||||
erubis (~> 2.7.0)
|
||||
hiredis
|
||||
jquery-rails
|
||||
json (>= 2.0.0)
|
||||
|
@ -432,4 +434,4 @@ DEPENDENCIES
|
|||
websocket-client-simple!
|
||||
|
||||
BUNDLED WITH
|
||||
1.13.7
|
||||
1.14.3
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require "erubis"
|
||||
require "abstract_controller/error"
|
||||
require "active_support/configurable"
|
||||
require "active_support/descendants_tracker"
|
||||
|
@ -22,7 +21,6 @@ module AbstractController
|
|||
include ActiveSupport::Configurable
|
||||
extend ActiveSupport::DescendantsTracker
|
||||
|
||||
undef_method :not_implemented
|
||||
class << self
|
||||
attr_reader :abstract
|
||||
alias_method :abstract?, :abstract
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
* Change the ERB handler from Erubis to Erubi.
|
||||
|
||||
Erubi is an Erubis fork that's svelte, simple, and currently maintained.
|
||||
Plus it supports `--enable-frozen-string-literal` in Ruby 2.3+.
|
||||
|
||||
Compatibility: Drops support for `<%===` tags for debug output.
|
||||
These were an unused, undocumented side effect of the Erubis
|
||||
implementation.
|
||||
|
||||
Deprecation: The Erubis handler will be removed in Rails 5.2, for the
|
||||
handful of folks using it directly.
|
||||
|
||||
*Jeremy Evans*
|
||||
|
||||
* Allow render locals to be assigned to instance variables in a view.
|
||||
|
||||
Fixes #27480.
|
||||
|
|
|
@ -22,7 +22,7 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency "activesupport", version
|
||||
|
||||
s.add_dependency "builder", "~> 3.1"
|
||||
s.add_dependency "erubis", "~> 2.7.0"
|
||||
s.add_dependency "erubi", "~> 1.4"
|
||||
s.add_dependency "rails-html-sanitizer", "~> 1.0", ">= 1.0.2"
|
||||
s.add_dependency "rails-dom-testing", "~> 2.0"
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module ActionView #:nodoc:
|
|||
# = Action View Base
|
||||
#
|
||||
# Action View templates can be written in several ways.
|
||||
# If the template file has a <tt>.erb</tt> extension, then it uses the erubis[https://rubygems.org/gems/erubis]
|
||||
# If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
|
||||
# template system which can embed Ruby into an HTML document.
|
||||
# If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
|
||||
#
|
||||
|
|
|
@ -1,79 +1,12 @@
|
|||
require "erubis"
|
||||
|
||||
module ActionView
|
||||
class Template
|
||||
module Handlers
|
||||
class Erubis < ::Erubis::Eruby
|
||||
def add_preamble(src)
|
||||
@newline_pending = 0
|
||||
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
|
||||
end
|
||||
|
||||
def add_text(src, text)
|
||||
return if text.empty?
|
||||
|
||||
if text == "\n"
|
||||
@newline_pending += 1
|
||||
else
|
||||
src << "@output_buffer.safe_append='"
|
||||
src << "\n" * @newline_pending if @newline_pending > 0
|
||||
src << escape_text(text)
|
||||
src << "'.freeze;"
|
||||
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
|
||||
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
||||
# We override to always treat <%== as escaped.
|
||||
def add_expr(src, code, indicator)
|
||||
case indicator
|
||||
when "=="
|
||||
add_expr_escaped(src, code)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
||||
|
||||
def add_expr_literal(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
if BLOCK_EXPR.match?(code)
|
||||
src << "@output_buffer.append= " << code
|
||||
else
|
||||
src << "@output_buffer.append=(" << code << ");"
|
||||
end
|
||||
end
|
||||
|
||||
def add_expr_escaped(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
if BLOCK_EXPR.match?(code)
|
||||
src << "@output_buffer.safe_expr_append= " << code
|
||||
else
|
||||
src << "@output_buffer.safe_expr_append=(" << code << ");"
|
||||
end
|
||||
end
|
||||
|
||||
def add_stmt(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
super
|
||||
end
|
||||
|
||||
def add_postamble(src)
|
||||
flush_newline_if_pending(src)
|
||||
src << "@output_buffer.to_s"
|
||||
end
|
||||
|
||||
def flush_newline_if_pending(src)
|
||||
if @newline_pending > 0
|
||||
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
Erubis = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Erubis", "ActionView::Template::Handlers::ERB::Erubis", message: "ActionView::Template::Handlers::Erubis is deprecated and will be removed from Rails 5.2. Switch to ActionView::Template::Handlers::ERB::Erubi instead.")
|
||||
|
||||
class ERB
|
||||
autoload :Erubi, "action_view/template/handlers/erb/erubi"
|
||||
autoload :Erubis, "action_view/template/handlers/erb/erubis"
|
||||
|
||||
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
||||
# See ERB documentation for suitable values.
|
||||
class_attribute :erb_trim_mode
|
||||
|
@ -81,7 +14,7 @@ module ActionView
|
|||
|
||||
# Default implementation used.
|
||||
class_attribute :erb_implementation
|
||||
self.erb_implementation = Erubis
|
||||
self.erb_implementation = Erubi
|
||||
|
||||
# Do not escape templates of these mime types.
|
||||
class_attribute :escape_whitelist
|
||||
|
|
81
actionview/lib/action_view/template/handlers/erb/erubi.rb
Normal file
81
actionview/lib/action_view/template/handlers/erb/erubi.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
require "erubi"
|
||||
|
||||
module ActionView
|
||||
class Template
|
||||
module Handlers
|
||||
class ERB
|
||||
class Erubi < ::Erubi::Engine
|
||||
# :nodoc: all
|
||||
def initialize(input, properties = {})
|
||||
@newline_pending = 0
|
||||
|
||||
# Dup properties so that we don't modify argument
|
||||
properties = Hash[properties]
|
||||
properties[:preamble] = "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
|
||||
properties[:postamble] = "@output_buffer.to_s"
|
||||
properties[:bufvar] = "@output_buffer"
|
||||
properties[:escapefunc] = ""
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def evaluate(action_view_erb_handler_context)
|
||||
pr = eval("proc { #{@src} }", binding, @filename || "(erubi)")
|
||||
action_view_erb_handler_context.instance_eval(&pr)
|
||||
end
|
||||
|
||||
private
|
||||
def add_text(text)
|
||||
return if text.empty?
|
||||
|
||||
if text == "\n"
|
||||
@newline_pending += 1
|
||||
else
|
||||
src << "@output_buffer.safe_append='"
|
||||
src << "\n" * @newline_pending if @newline_pending > 0
|
||||
src << text.gsub(/['\\]/, '\\\\\&')
|
||||
src << "'.freeze;"
|
||||
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
|
||||
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
||||
|
||||
def add_expression(indicator, code)
|
||||
flush_newline_if_pending(src)
|
||||
|
||||
if (indicator == "==") || @escape
|
||||
src << "@output_buffer.safe_expr_append="
|
||||
else
|
||||
src << "@output_buffer.append="
|
||||
end
|
||||
|
||||
if BLOCK_EXPR.match?(code)
|
||||
src << " " << code
|
||||
else
|
||||
src << "(" << code << ");"
|
||||
end
|
||||
end
|
||||
|
||||
def add_code(code)
|
||||
flush_newline_if_pending(src)
|
||||
super
|
||||
end
|
||||
|
||||
def add_postamble(_)
|
||||
flush_newline_if_pending(src)
|
||||
super
|
||||
end
|
||||
|
||||
def flush_newline_if_pending(src)
|
||||
if @newline_pending > 0
|
||||
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
80
actionview/lib/action_view/template/handlers/erb/erubis.rb
Normal file
80
actionview/lib/action_view/template/handlers/erb/erubis.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require "erubis"
|
||||
|
||||
module ActionView
|
||||
class Template
|
||||
module Handlers
|
||||
class ERB
|
||||
class Erubis < ::Erubis::Eruby
|
||||
# :nodoc: all
|
||||
def add_preamble(src)
|
||||
@newline_pending = 0
|
||||
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
|
||||
end
|
||||
|
||||
def add_text(src, text)
|
||||
return if text.empty?
|
||||
|
||||
if text == "\n"
|
||||
@newline_pending += 1
|
||||
else
|
||||
src << "@output_buffer.safe_append='"
|
||||
src << "\n" * @newline_pending if @newline_pending > 0
|
||||
src << escape_text(text)
|
||||
src << "'.freeze;"
|
||||
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
|
||||
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
||||
# We override to always treat <%== as escaped.
|
||||
def add_expr(src, code, indicator)
|
||||
case indicator
|
||||
when "=="
|
||||
add_expr_escaped(src, code)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
||||
|
||||
def add_expr_literal(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
if BLOCK_EXPR.match?(code)
|
||||
src << "@output_buffer.append= " << code
|
||||
else
|
||||
src << "@output_buffer.append=(" << code << ");"
|
||||
end
|
||||
end
|
||||
|
||||
def add_expr_escaped(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
if BLOCK_EXPR.match?(code)
|
||||
src << "@output_buffer.safe_expr_append= " << code
|
||||
else
|
||||
src << "@output_buffer.safe_expr_append=(" << code << ");"
|
||||
end
|
||||
end
|
||||
|
||||
def add_stmt(src, code)
|
||||
flush_newline_if_pending(src)
|
||||
super
|
||||
end
|
||||
|
||||
def add_postamble(src)
|
||||
flush_newline_if_pending(src)
|
||||
src << "@output_buffer.to_s"
|
||||
end
|
||||
|
||||
def flush_newline_if_pending(src)
|
||||
if @newline_pending > 0
|
||||
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
||||
@newline_pending = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
require "abstract_unit"
|
||||
|
||||
module ERBTest
|
||||
class DeprecatedErubisImplementationTest < ActionView::TestCase
|
||||
test "Erubis implementation is deprecated" do
|
||||
assert_deprecated "ActionView::Template::Handlers::Erubis is deprecated and will be removed from Rails 5.2. Switch to ActionView::Template::Handlers::ERB::Erubi instead." do
|
||||
assert_equal "ActionView::Template::Handlers::ERB::Erubis", ActionView::Template::Handlers::Erubis.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,7 +14,7 @@ module ERBTest
|
|||
class BlockTestCase < ActiveSupport::TestCase
|
||||
def render_content(start, inside)
|
||||
template = block_helper(start, inside)
|
||||
ActionView::Template::Handlers::Erubis.new(template).evaluate(ViewContext.new)
|
||||
ActionView::Template::Handlers::ERB.erb_implementation.new(template).evaluate(ViewContext.new)
|
||||
end
|
||||
|
||||
def block_helper(str, rest)
|
||||
|
|
|
@ -122,10 +122,11 @@ module ActiveSupport
|
|||
# (Backtrace information…)
|
||||
# ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
|
||||
class DeprecatedConstantProxy < DeprecationProxy
|
||||
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
|
||||
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
|
||||
@old_const = old_const
|
||||
@new_const = new_const
|
||||
@deprecator = deprecator
|
||||
@message = message
|
||||
end
|
||||
|
||||
# Returns the class of the new constant.
|
||||
|
@ -143,7 +144,7 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
def warn(callstack, called, args)
|
||||
@deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
|
||||
@deprecator.warn(@message, callstack)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -285,6 +285,16 @@ class DeprecationTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_deprecated_constant_with_custom_message
|
||||
deprecator = deprecator_with_messages
|
||||
|
||||
klass = Class.new
|
||||
klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new("klass::OLD", "Object", deprecator, message: "foo"))
|
||||
|
||||
klass::OLD.to_s
|
||||
assert_match "foo", deprecator.messages.last
|
||||
end
|
||||
|
||||
def test_deprecated_instance_variable_with_instance_deprecator
|
||||
deprecator = deprecator_with_messages
|
||||
|
||||
|
|
Loading…
Reference in a new issue