Class: Erector::Widget
Overview
A Widget is the center of the Erector universe.
To create a widget, extend Erector::Widget and implement the render
method. Inside this method you may call any of the tag methods like span
or p
to emit HTML/XML tags.
You can also define a widget on the fly by passing a block to new
. This block will get executed when the widget’s render
method is called.
To render a widget from the outside, instantiate it and call its to_s
method.
To call one widget from another, inside the parent widget’s render method, instantiate the child widget and call its render_to
method, passing in self
(or self.doc if you prefer). This assures that the same Doc stream is used, which gives better performance than using capture
or to_s
.
In this documentation we’ve tried to keep the distinction clear between methods that emit text and those that return text. “Emit” means that it writes Doc to the doc stream; “return” means that it returns a string like a normal method and leaves it up to the caller to emit that string if it wants.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#assigns ⇒ Object
readonly
Returns the value of attribute assigns.
-
#block ⇒ Object
readonly
Returns the value of attribute block.
-
#doc ⇒ Object
readonly
Returns the value of attribute doc.
-
#helpers ⇒ Object
readonly
Returns the value of attribute helpers.
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
Class Method Summary collapse
- .after_initialize(instance = nil, &blk) ⇒ Object
- .all_tags ⇒ Object
-
.empty_tags ⇒ Object
tags which are always self-closing.
-
.full_tags ⇒ Object
tags which can contain other stuff.
Instance Method Summary collapse
-
#assign_locals(local_assigns) ⇒ Object
– methods for other classes to call, left public for ease of testing and documentation ++.
-
#capture(&block) ⇒ Object
Creates a whole new doc stream, executes the block, then converts the doc stream to a string and emits it as raw text.
-
#character(code_point_or_name) ⇒ Object
Return a character given its unicode code point or unicode name.
-
#close_tag(tag_name) ⇒ Object
Emits a close tag, consisting of ‘<’, tag name, and ‘>’.
-
#css(href) ⇒ Object
Convenience method to emit a css file link, which looks like this: <link href=“erector.css” rel=“stylesheet” type=“text/css” /> The parameter is the full contents of the href attribute, including any “.css” extension.
-
#element(*args, &block) ⇒ Object
Internal method used to emit an HTML/XML element, including an open tag, attributes (optional, via the default hash), contents (also optional), and close tag.
-
#empty_element(*args, &block) ⇒ Object
Internal method used to emit a self-closing HTML/XML element, including a tag name and optional attributes (passed in via the default hash).
-
#enable_prettyprint(enable) ⇒ Object
Set whether Erector should add newlines and indentation in to_s.
-
#h(content) ⇒ Object
Returns an HTML-escaped version of its parameter.
-
#html_escape ⇒ Object
(Should we make this hidden?).
-
#initialize(helpers = nil, assigns = {}, io = StringIO.new(""), &block) ⇒ Widget
constructor
A new instance of Widget.
-
#instruct(attributes = {:version => "1.0", :encoding => "UTF-8"}) ⇒ Object
(also: #instruct!)
Emits an XML instruction, which looks like this: <?xml version="1.0" encoding="UTF-8"?>.
-
#javascript(*args, &block) ⇒ Object
Emits a javascript block inside a
script
tag, wrapped in CDATA doohickeys like all the cool JS kids do. -
#nbsp(value = " ") ⇒ Object
Returns a copy of value with spaces replaced by non-breaking space characters.
-
#open_tag(tag_name, attributes = {}) ⇒ Object
Emits an open tag, comprising ‘<’, tag name, optional attributes, and ‘>’.
-
#raw(value) ⇒ Object
Returns text which will not be HTML-escaped.
-
#rawtext(value) ⇒ Object
Emits text which will not be HTML-escaped.
-
#render ⇒ Object
Template method which must be overridden by all widget subclasses.
-
#render_to(doc_or_widget) ⇒ Object
To call one widget from another, inside the parent widget’s render method, instantiate the child widget and call its
render_to
method, passing inself
(or self.doc if you prefer). -
#text(value) ⇒ Object
Emits text.
-
#to_pretty ⇒ Object
Render (like to_s) but adding newlines and indentation.
-
#to_s(render_method_name = :render, &blk) ⇒ Object
(also: #inspect)
Entry point for rendering a widget (and all its children).
-
#url(href) ⇒ Object
Convenience method to emit an anchor tag whose href and text are the same, e.g.
-
#widget(widget_class, assigns = {}, &block) ⇒ Object
Convenience method for on-the-fly widgets.
Constructor Details
#initialize(helpers = nil, assigns = {}, io = StringIO.new(""), &block) ⇒ Widget
Returns a new instance of Widget.
73 74 75 76 77 78 79 80 81 |
# File 'lib/erector/widget.rb', line 73 def initialize(helpers=nil, assigns={}, io = StringIO.new(""), &block) @assigns = assigns assign_locals(assigns) @helpers = helpers @parent = block ? eval("self", block.binding) : nil @doc = Doc.new(io) @block = block self.class.after_initialize self end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object (protected)
This is part of the sub-widget/parent feature (see #widget method).
355 356 357 358 359 360 361 362 |
# File 'lib/erector/widget.rb', line 355 def method_missing(name, *args, &block) block ||= lambda {} # captures self HERE if @parent @parent.send(name, *args, &block) else super end end |
Instance Attribute Details
#assigns ⇒ Object (readonly)
Returns the value of attribute assigns.
68 69 70 |
# File 'lib/erector/widget.rb', line 68 def assigns @assigns end |
#block ⇒ Object (readonly)
Returns the value of attribute block.
70 71 72 |
# File 'lib/erector/widget.rb', line 70 def block @block end |
#doc ⇒ Object (readonly)
Returns the value of attribute doc.
69 70 71 |
# File 'lib/erector/widget.rb', line 69 def doc @doc end |
#helpers ⇒ Object (readonly)
Returns the value of attribute helpers.
67 68 69 |
# File 'lib/erector/widget.rb', line 67 def helpers @helpers end |
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
71 72 73 |
# File 'lib/erector/widget.rb', line 71 def parent @parent end |
Class Method Details
.after_initialize(instance = nil, &blk) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/erector/widget.rb', line 46 def after_initialize(instance=nil, &blk) if blk after_initialize_parts << blk elsif instance if superclass.respond_to?(:after_initialize) superclass.after_initialize instance end after_initialize_parts.each do |part| instance.instance_eval &part end else raise ArgumentError, "You must provide either an instance or a block" end end |
.all_tags ⇒ Object
23 24 25 |
# File 'lib/erector/widget.rb', line 23 def Erector::Widget. + Erector::Widget. end |
.empty_tags ⇒ Object
tags which are always self-closing
28 29 30 |
# File 'lib/erector/widget.rb', line 28 def ['area', 'base', 'br', 'hr', 'img', 'input', 'link', 'meta'] end |
.full_tags ⇒ Object
tags which can contain other stuff
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/erector/widget.rb', line 33 def [ 'a', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code', 'dd', 'del', 'div', 'dl', 'dt', 'em', 'fieldset', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'map', 'noframes', 'noscript', 'ol', 'optgroup', 'option', 'p', 'param', 'pre', 'samp', 'script', 'select', 'small', 'span', 'strong', 'style', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'th', 'thead', 'title', 'tr', 'tt', 'u', 'ul', 'var' ] end |
Instance Method Details
#assign_locals(local_assigns) ⇒ Object
– methods for other classes to call, left public for ease of testing and documentation ++
86 87 88 89 90 91 92 93 |
# File 'lib/erector/widget.rb', line 86 def assign_locals(local_assigns) local_assigns.each do |name, value| instance_variable_set("@#{name}", value) .module_eval do attr_reader name end end end |
#capture(&block) ⇒ Object
Creates a whole new doc stream, executes the block, then converts the doc stream to a string and emits it as raw text. If at all possible you should avoid this method since it hurts performance, and use #render_to instead.
267 268 269 270 271 272 273 274 275 276 |
# File 'lib/erector/widget.rb', line 267 def capture(&block) begin original_doc = @doc @doc = Doc.new(StringIO.new) yield raw(@doc.to_s) ensure @doc = original_doc end end |
#character(code_point_or_name) ⇒ Object
Return a character given its unicode code point or unicode name.
237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/erector/widget.rb', line 237 def character(code_point_or_name) if code_point_or_name.is_a?(Symbol) found = Erector::CHARACTERS[code_point_or_name] if found.nil? raise "Unrecognized character #{code_point_or_name}" end raw("&#x#{sprintf '%x', found};") elsif code_point_or_name.is_a?(Integer) raw("&#x#{sprintf '%x', code_point_or_name};") else raise "Unrecognized argument to character: #{code_point_or_name}" end end |
#close_tag(tag_name) ⇒ Object
Emits a close tag, consisting of ‘<’, tag name, and ‘>’
252 253 254 |
# File 'lib/erector/widget.rb', line 252 def close_tag(tag_name) @doc.close_tag tag_name end |
#css(href) ⇒ Object
Convenience method to emit a css file link, which looks like this: <link href=“erector.css” rel=“stylesheet” type=“text/css” /> The parameter is the full contents of the href attribute, including any “.css” extension.
If you want to emit raw CSS inline, use the #script method instead.
341 342 343 |
# File 'lib/erector/widget.rb', line 341 def css(href) link :rel => 'stylesheet', :type => 'text/css', :href => href end |
#element(*args, &block) ⇒ Object
Internal method used to emit an HTML/XML element, including an open tag, attributes (optional, via the default hash), contents (also optional), and close tag.
Using the arcane powers of Ruby, there are magic methods that call element
for all the standard HTML tags, like a
, body
, p
, and so forth. Look at the source of #full_tags for the full list. Unfortunately, this big mojo confuses rdoc, so we can’t see each method in this rdoc page, but trust us, they’re there.
When calling one of these magic methods, put attributes in the default hash. If there is a string parameter, then it is used as the contents. If there is a block, then it is executed (yielded), and the string parameter is ignored. The block will usually be in the scope of the child widget, which means it has access to all the methods of Widget, which will eventually end up appending text to the doc
Doc stream. See how elegant it is? Not confusing at all if you don’t think about it.
180 181 182 |
# File 'lib/erector/widget.rb', line 180 def element(*args, &block) __element__(*args, &block) end |
#empty_element(*args, &block) ⇒ Object
Internal method used to emit a self-closing HTML/XML element, including a tag name and optional attributes (passed in via the default hash).
Using the arcane powers of Ruby, there are magic methods that call empty_element
for all the standard HTML tags, like img
, br
, and so forth. Look at the source of #empty_tags for the full list. Unfortunately, this big mojo confuses rdoc, so we can’t see each method in this rdoc page, but trust us, they’re there.
192 193 194 |
# File 'lib/erector/widget.rb', line 192 def empty_element(*args, &block) __empty_element__(*args, &block) end |
#enable_prettyprint(enable) ⇒ Object
Set whether Erector should add newlines and indentation in to_s. This is an experimental feature and is subject to change (either in terms of how it is enabled, or in terms of what decisions Erector makes about where to add whitespace). This flag should be set prior to any rendering being done (for example, calls to to_s or to_pretty).
101 102 103 104 |
# File 'lib/erector/widget.rb', line 101 def enable_prettyprint(enable) @doc.enable_prettyprint = enable self end |
#h(content) ⇒ Object
Returns an HTML-escaped version of its parameter. Leaves the Doc stream untouched. Note that the #text method automatically HTML-escapes its parameter, so be careful not to do something like text(h(“2<4”)) since that will double-escape the less-than sign (you’ll get “2&lt;4” instead of “2<4”).
200 201 202 |
# File 'lib/erector/widget.rb', line 200 def h(content) content.html_escape end |
#html_escape ⇒ Object
(Should we make this hidden?)
159 160 161 |
# File 'lib/erector/widget.rb', line 159 def html_escape return to_s end |
#instruct(attributes = {:version => "1.0", :encoding => "UTF-8"}) ⇒ Object Also known as: instruct!
Emits an XML instruction, which looks like this: <?xml version="1.0" encoding="UTF-8"?>
257 258 259 |
# File 'lib/erector/widget.rb', line 257 def instruct(attributes={:version => "1.0", :encoding => "UTF-8"}) @doc.instruct attributes end |
#javascript(*args, &block) ⇒ Object
Emits a javascript block inside a script
tag, wrapped in CDATA doohickeys like all the cool JS kids do.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/erector/widget.rb', line 299 def javascript(*args, &block) if args.length > 2 raise ArgumentError, "Cannot accept more than two arguments" end attributes, value = nil, nil arg0 = args[0] if arg0.is_a?(Hash) attributes = arg0 else value = arg0 arg1 = args[1] if arg1.is_a?(Hash) attributes = arg1 end end attributes ||= {} attributes[:type] = "text/javascript" open_tag 'script', attributes # Shouldn't this be a "cdata" HtmlPart? # (maybe, but the syntax is specific to javascript; it isn't # really a generic XML CDATA section. Specifically, # ]]> within value is not treated as ending the # CDATA section by Firefox2 when parsing text/html, # although I guess we could refuse to generate ]]> # there, for the benefit of XML/XHTML parsers). rawtext "\n// <![CDATA[\n" if block instance_eval(&block) else rawtext value end rawtext "\n// ]]>\n" close_tag 'script' rawtext "\n" end |
#nbsp(value = " ") ⇒ Object
Returns a copy of value with spaces replaced by non-breaking space characters. With no arguments, return a single non-breaking space. The output uses the escaping format ‘ ’ since that works in both HTML and XML (as opposed to ‘ ’ which only works in HTML).
232 233 234 |
# File 'lib/erector/widget.rb', line 232 def nbsp(value = " ") raw(value.html_escape.gsub(/ /,' ')) end |
#open_tag(tag_name, attributes = {}) ⇒ Object
Emits an open tag, comprising ‘<’, tag name, optional attributes, and ‘>’
205 206 207 |
# File 'lib/erector/widget.rb', line 205 def open_tag(tag_name, attributes={}) @doc.open_tag tag_name, attributes end |
#raw(value) ⇒ Object
Returns text which will not be HTML-escaped.
219 220 221 |
# File 'lib/erector/widget.rb', line 219 def raw(value) RawString.new(value.to_s) end |
#rawtext(value) ⇒ Object
Emits text which will not be HTML-escaped. Same effect as text(raw(s))
224 225 226 |
# File 'lib/erector/widget.rb', line 224 def rawtext(value) text raw(value) end |
#render ⇒ Object
Template method which must be overridden by all widget subclasses. Inside this method you call the magic #element methods which emit HTML and text to the Doc stream.
130 131 132 133 134 |
# File 'lib/erector/widget.rb', line 130 def render if @block instance_eval(&@block) end end |
#render_to(doc_or_widget) ⇒ Object
To call one widget from another, inside the parent widget’s render method, instantiate the child widget and call its render_to
method, passing in self
(or self.doc if you prefer). This assures that the same Doc stream is used, which gives better performance than using capture
or to_s
.
139 140 141 142 143 144 145 146 147 |
# File 'lib/erector/widget.rb', line 139 def render_to() if .is_a?(Widget) @parent = @doc = @parent.doc else @doc = end render end |
#text(value) ⇒ Object
Emits text. If a string is passed in, it will be HTML-escaped. If a widget or the result of calling methods such as raw is passed in, the HTML will not be HTML-escaped again. If another kind of object is passed in, the result of calling its to_s method will be treated as a string would be.
214 215 216 |
# File 'lib/erector/widget.rb', line 214 def text(value) @doc.text value end |
#to_pretty ⇒ Object
Render (like to_s) but adding newlines and indentation.
107 108 109 |
# File 'lib/erector/widget.rb', line 107 def to_pretty enable_prettyprint(true).to_s end |
#to_s(render_method_name = :render, &blk) ⇒ Object Also known as: inspect
Entry point for rendering a widget (and all its children). This method creates a new Doc doc stream, calls this widget’s #render method, converts the Doc to a string, and returns the string.
If it’s called again later then it returns the earlier rendered string, which leads to higher performance, but may have confusing effects if some underlying state has changed. In general we recommend you create a new instance of every widget for each render, unless you know what you’re doing.
118 119 120 121 122 123 124 |
# File 'lib/erector/widget.rb', line 118 def to_s(render_method_name=:render, &blk) # The @__to_s variable is used as a cache. # If it's useful we should add a test for it. -ac return @__to_s if @__to_s send(render_method_name, &blk) @__to_s = @doc.to_s end |
#url(href) ⇒ Object
Convenience method to emit an anchor tag whose href and text are the same, e.g. <a href=“example.com”>example.com</a>
346 347 348 |
# File 'lib/erector/widget.rb', line 346 def url(href) a href, :href => href end |
#widget(widget_class, assigns = {}, &block) ⇒ Object
Convenience method for on-the-fly widgets. This is a way of making a sub-widget which still has access to the methods of the parent class. This is an experimental erector feature which may disappear in future versions of erector (see #widget in widget_spec in the Erector tests).
153 154 155 156 |
# File 'lib/erector/widget.rb', line 153 def (, assigns={}, &block) child = .new(helpers, assigns, doc.output, &block) child.render end |