Module: Cybertrain::Html
- Defined in:
- cybertrain/html.rb
Overview
HTML escaping for controllers and models. Templates escape <%= %>
output themselves; these are for building HTML in Ruby, e.g. for
render html:.
Class Method Summary collapse
-
.escape(value) ⇒ String
Escapes the five characters that matter for HTML text/attribute contexts.
-
.safe(str) ⇒ SafeString
Marks trusted HTML as safe, so it is output as it is.
Class Method Details
.escape(value) ⇒ String
Escapes the five characters that matter for HTML text/attribute contexts. Scans bytes (the five are ASCII, so multibyte UTF-8 sequences are never split) and returns the input untouched when nothing needs escaping, since most interpolated values are plain text. Measured 2.7x faster than an each_char loop under Spinel; this runs for every <%= %>.
&, <, >, " and ' become entities.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'cybertrain/html.rb', line 19 def self.escape(value) # The parameter is polymorphic under Spinel (callers hand in Strings of # more than one static type), which made every getbyte and comparison # below a dynamic call: most of this method's time. value.to_s has one # static type (every to_s in the program returns a String; SafeString's # included, see SafeString#initialize), so the loops compile to plain # C, and for a String it is the String itself, not a copy. It is also # what comes back when nothing needs escaping, so callers get a typed # String rather than the polymorphic parameter. The case keeps value # polymorphic: used only through to_s, Spinel narrows it to whatever # one caller passes (it once inferred an Integer and turned Strings # into "0"). Anything but a String is converted anyway, so the # interpolation costs nothing extra. str = case value when String then value.to_s else "#{value.to_s}" end n = str.bytesize i = 0 # Most values need nothing: find the first byte that does before # allocating a buffer at all. while i < n b = str.getbyte(i) break if b == 38 || b == 60 || b == 62 || b == 34 || b == 39 i += 1 end return str if i == n buf = +"" dirty = false start = 0 while i < n b = str.getbyte(i) if b == 38 || b == 60 || b == 62 || b == 34 || b == 39 buf << str.byteslice(start, i - start) if i > start case b when 38 then buf << "&" when 60 then buf << "<" when 62 then buf << ">" when 34 then buf << """ else buf << "'" end start = i + 1 dirty = true end i += 1 end return str unless dirty buf << str.byteslice(start, n - start) if n > start buf end |
.safe(str) ⇒ SafeString
Marks trusted HTML as safe, so it is output as it is.
78 79 80 |
# File 'cybertrain/html.rb', line 78 def self.safe(str) SafeString.of(str) end |