Module: Papercraft::Tags
Overview
Markup (HTML/XML) extensions
Constant Summary collapse
- S_LT =
'<'
- S_GT =
'>'
- S_LT_SLASH =
'</'
- S_SPACE_LT_SLASH =
' </'
- S_SLASH_GT =
'/>'
- S_GT_LT_SLASH =
'></'
- S_SPACE =
' '
- S_EQUAL_QUOTE =
'="'
- S_QUOTE =
'"'
- S_TAG_METHOD_LINE =
The tag method template below is optimized for performance. Do not touch!
__LINE__ + 2
- S_TAG_METHOD =
<<~EOF S_TAG_%<TAG>s_PRE = %<tag_pre>s S_TAG_%<TAG>s_CLOSE = %<tag_close>s def %<tag>s(text = nil, _for: nil, **props, &block) return if @render_fragment && @fragment != @render_fragment return _for.each { |*a| %<tag>s(text, **props) { block.(*a)} } if _for if text.is_a?(Hash) && props.empty? props = text text = nil end @buffer << S_TAG_%<TAG>s_PRE emit_props(props) unless props.empty? if block @buffer << S_GT instance_eval(&block) @buffer << S_TAG_%<TAG>s_CLOSE elsif Proc === text @buffer << S_GT emit(text) @buffer << S_TAG_%<TAG>s_CLOSE elsif text @buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE else @buffer << S_GT << S_TAG_%<TAG>s_CLOSE end end EOF
- S_VOID_TAG_METHOD_LINE =
__LINE__ + 2
- S_VOID_TAG_METHOD =
<<~EOF S_TAG_%<TAG>s_PRE = %<tag_pre>s S_TAG_%<TAG>s_CLOSE = %<tag_close>s def %<tag>s(text = nil, _for: nil, **props, &block) return if @render_fragment && @fragment != @render_fragment return _for.each { |*a| %<tag>s(text, **props) { block.(*a)} } if _for if text.is_a?(Hash) && props.empty? props = text text = nil end @buffer << S_TAG_%<TAG>s_PRE emit_props(props) unless props.empty? if block @buffer << S_GT instance_eval(&block) @buffer << S_TAG_%<TAG>s_CLOSE elsif Proc === text @buffer << S_GT emit(text) @buffer << S_TAG_%<TAG>s_CLOSE elsif text @buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE else @buffer << S_SLASH_GT end end EOF
- INITIAL_BUFFER_CAPACITY =
8192
Instance Method Summary collapse
-
#def_tag(tag, &block) ⇒ void
Defines a custom tag.
-
#defer(&block) ⇒ void
Defers the given block to be evaluated later.
-
#extend(ext) ⇒ Object
Extends the template with the provided module or map of modules.
-
#initialize(render_fragment = nil, &template) ⇒ Object
Initializes a tag renderer.
-
#method_missing(sym, *args, **opts, &block) ⇒ void
Catches undefined tag method call and handles it by defining the method.
- #orig_extend ⇒ Object
-
#tag(sym, text = nil, _for: nil, **props, &block) ⇒ void
Emits an XML tag with the given content, properties and optional block.
-
#text(data) ⇒ void
Emits text into the rendering buffer, escaping any special characters to the respective XML entities.
-
#to_s ⇒ String
Returns the rendered template.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, **opts, &block) ⇒ void
This method returns an undefined value.
Catches undefined tag method call and handles it by defining the method.
207 208 209 210 211 212 213 214 215 216 |
# File 'lib/papercraft/tags.rb', line 207 def method_missing(sym, *args, **opts, &block) tag = sym.to_s if tag =~ /^[A-Z]/ && (Object.const_defined?(tag)) define_const_tag_method(tag) else define_tag_method(tag) end send(sym, *args, **opts, &block) end |
Instance Method Details
#def_tag(tag, &block) ⇒ void
This method returns an undefined value.
Defines a custom tag. This is handy for defining helper or extension methods inside the template body.
Papercraft.html {
def_tag(:section) { |title, &inner|
div {
h1 title
emit inner
}
}
section('Foo') {
p 'Bar'
}
}
248 249 250 |
# File 'lib/papercraft/tags.rb', line 248 def def_tag(tag, &block) self.class.define_method(tag, &block) end |
#defer(&block) ⇒ void
This method returns an undefined value.
Defers the given block to be evaluated later. Deferred evaluation allows Papercraft templates to inject state into sibling components, regardless of the component’s order in the container component. For example, a nested component may set an instance variable used by another component. This is an elegant solution to the problem of setting the XML page’s title, or adding elements to the ‘<head>` section. Here’s how a title can be controlled from a nested component:
layout = Papercraft.html {
html {
head {
defer { title @title }
}
body {
emit_yield
}
}
}
html.render {
@title = 'My super page'
h1 'content'
}
143 144 145 146 147 148 149 150 151 |
# File 'lib/papercraft/tags.rb', line 143 def defer(&block) if !@parts @parts = [@buffer, block] else @parts << @buffer unless @buffer.empty? @parts << block end @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY) end |
#extend(ext) ⇒ Object
Extends the template with the provided module or map of modules. When given a module, the template body will be extended with the module, and will have access to all the module’s methods:
module CustomTags
def label(text)
span text, class: 'label'
end
end
Papercraft.html {
extend CustomTags
label('foo')
}
When given a hash, each module in the hash is namespaced, and can be accessed using its key:
Papercraft.html {
extend custom: CustomTags
custom.label('foo')
}
279 280 281 282 283 284 285 286 287 |
# File 'lib/papercraft/tags.rb', line 279 def extend(ext) if ext.is_a?(Module) orig_extend(ext) else ext.each do |sym, mod| define_extension_method(sym, mod) end end end |
#initialize(render_fragment = nil, &template) ⇒ Object
Initializes a tag renderer.
92 93 94 95 |
# File 'lib/papercraft/tags.rb', line 92 def initialize(render_fragment = nil, &template) @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY) super(render_fragment) end |
#orig_extend ⇒ Object
252 |
# File 'lib/papercraft/tags.rb', line 252 alias_method :orig_extend, :extend |
#tag(sym, text = nil, _for: nil, **props, &block) ⇒ void
This method returns an undefined value.
Emits an XML tag with the given content, properties and optional block. This method is an alternative to emitting XML tags using dynamically created methods. This is particularly useful when using extensions that have method names that clash with XML tags, such as ‘button` or `a`, or when you need to override the behaviour of a particular XML tag.
The following two method calls have the same effect:
button ‘text’, id: ‘button1’ tag :button, ‘text’, id: ‘button1’
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/papercraft/tags.rb', line 169 def tag(sym, text = nil, _for: nil, **props, &block) return if @render_fragment && @fragment != @render_fragment return _for.each { |*a| tag(sym, text, **props) { block.(*a)} } if _for if text.is_a?(Hash) && props.empty? props = text text = nil end tag = tag_repr(sym) @buffer << S_LT << tag emit_props(props) unless props.empty? if block @buffer << S_GT instance_eval(&block) @buffer << S_LT_SLASH << tag << S_GT elsif Proc === text @buffer << S_GT emit(text) @buffer << S_LT_SLASH << tag << S_GT elsif text @buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT elsif is_void_element_tag?(sym) @buffer << S_SLASH_GT else @buffer << S_GT_LT_SLASH << tag << S_GT end end |
#text(data) ⇒ void
This method returns an undefined value.
Emits text into the rendering buffer, escaping any special characters to the respective XML entities.
223 224 225 226 227 |
# File 'lib/papercraft/tags.rb', line 223 def text(data) return if @render_fragment && @fragment != @render_fragment @buffer << escape_text(data) end |
#to_s ⇒ String
Returns the rendered template.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/papercraft/tags.rb', line 100 def to_s if @parts last = @buffer @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY) parts = @parts @parts = nil parts.each do |p| if Proc === p render_deferred_proc(&p) else @buffer << p end end @buffer << last unless last.empty? end @buffer end |