Class: Cybertrain::SafeString

Inherits:
Object
  • Object
show all
Defined in:
cybertrain/html.rb

Overview

A string that has already been through Html.escape (or was built from only safe pieces) and should not be escaped again when rendered.

Templates print it unescaped (raw(x) and x.html_safe make one); Controller#render html: sends it as it is, where a plain String is escaped. It is a wrapper, not a String subclass. There is no String#html_safe in Ruby code: use Html.safe or SafeString.of.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(str) ⇒ SafeString

Returns a new instance of SafeString.

Parameters:

  • str (String) —

    trusted HTML



106
107
108
# File 'cybertrain/html.rb', line 106

def initialize(str)
  @str = str
end

Class Method Details

.of(str) ⇒ SafeString

SafeString.of(str), safe for a String made in the argument itself (SafeString.of("<a>#{x}</a>")). Under Spinel 2026.09.12 the generated new allocates the object before it roots its argument, so a garbage collection in that allocation frees a String that only the argument holds (a use-after-free that showed as a rare crash under load). The parameter of an ordinary method is rooted on entry, so going through one is enough; bind the String to a local first if you call new.

Parameters:

  • str (String) —

    trusted HTML

Returns:



120
121
122
# File 'cybertrain/html.rb', line 120

def self.of(str)
  SafeString.new(str)
end

Instance Method Details

#+(other) ⇒ SafeString

Concatenating with a plain String escapes it first, so building up a SafeString piece by piece never lets unsafe content slip through.

Parameters:

Returns:



151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'cybertrain/html.rb', line 151

def +(other)
  # Built in two steps: a case expression whose branches both construct
  # a SafeString fails to compile when SafeString gets Spinel's unboxed
  # value-type layout (every SafeString in the program travelling only
  # as a keyword argument).
  piece = case other
          when SafeString then other.to_s
          # other.to_s rather than other: in a program that never calls
          # +, Spinel types the unused parameter as an Integer and passed
          # that type on to Html.escape's (test/router.rb caught it).
          else Html.escape(other.to_s)
          end
  SafeString.of(@str + piece)
end

#to_s ⇒ String

Returns the HTML.

Returns:

  • (String) —

    the HTML



126
127
128
# File 'cybertrain/html.rb', line 126

def to_s
  @str
end