Skip to content

Instantly share code, notes, and snippets.

@mieko
Last active August 29, 2015 13:59
Show Gist options
  • Save mieko/10577913 to your computer and use it in GitHub Desktop.
Save mieko/10577913 to your computer and use it in GitHub Desktop.
Rails-style ERB output escaping for Opal
# Implement automatic output escaping for ERB. It works a lot like Rails
#
# Check out this article by Yehuda Katz to get the idea:
# http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/
#
# Effectively any generated output is escaped unless it is html_safe?
#
# Nothing is safe except a few built-in classes, and Dirt::SafeString.
#
# You can generate a Dirt::SafeString from any object by calling #html_safe.
# The result of its #to_s is used to generate a new SafeString.
#
# As to not interfere with other users of ERB, use Template#safe_render instead
# of Template#render to get the safe-by-default behaviour.
require 'erb'
require 'template'
# By default, nothing is safe.
class BasicObject
def html_safe?
false
end
# But we can generate a marked safe version with html_safe
def html_safe
::Dirt::SafeString.new(to_s)
end
end
# to_s on instances of the following classes cannot generate characters that
# require escaping, so we consider them safe.
[Boolean, NilClass, Numeric].each do |cls|
cls.define_method(:html_safe?) { true }
end
module Dirt
class SafeString < String
def html_safe?
true
end
def html_safe
self
end
# SafeString + (an html_safe? object) returns another SafeString.
# Anything else returns a String.
def +(other)
result_cls = other.html_safe? ? SafeString : String
result_cls.new(to_s + other.to_s)
end
alias_method :concat, :+
end
end
class Template
# SafeOutputBuffer behaves like OutputBuffer, but escapes unsafe values
class SafeOutputBuffer < OutputBuffer
def append=(value)
super(value.html_safe? ? value : ::ERB::Util.html_escape(value.to_s))
end
end
# render_safe acts as render, but uses a SafeOutputBuffer instead of an
# OutputBuffer
def render_safe(ctx = self)
ctx.instance_exec(SafeOutputBuffer.new, &body)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment