module Papercraft::Tags

Markup (HTML/XML) extensions

Constants

INITIAL_BUFFER_CAPACITY
S_EQUAL_QUOTE
S_GT
S_LT
S_LT_SLASH
S_QUOTE
S_SLASH_GT
S_SPACE
S_SPACE_LT_SLASH
S_TAG_METHOD
S_TAG_METHOD_LINE

The tag method template below is optimized for performance. Do not touch!

Public Class Methods

new(&template) click to toggle source

Initializes a tag renderer.

Calls superclass method
# File lib/papercraft/tags.rb, line 50
def initialize(&template)
  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
  super
end

Public Instance Methods

defer(&block) click to toggle source

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'
}

@param &block [Proc] Deferred block to be emitted @return [void]

# File lib/papercraft/tags.rb, line 102
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
method_missing(sym, *args, **opts, &block) click to toggle source

Catches undefined tag method call and handles it by defining the method.

@param sym [Symbol] XML tag or component identifier @param args [Array] method arguments @param opts [Hash] named method arguments @param &block [Proc] block passed to method @return [void]

# File lib/papercraft/tags.rb, line 162
def method_missing(sym, *args, **opts, &block)
  tag = sym.to_s
  if tag =~ /^[A-Z]/ && (Object.const_defined?(tag))
    define_const_tag_method(tag)
    # return send(tag, *args, **opts)
  else
    define_tag_method(tag)
  end

  send(sym, *args, **opts, &block)
end
tag(sym, text = nil, **props, &block) click to toggle source

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’

@param sym [Symbol, String] XML tag @param text [String, nil] tag content @param **props [Hash] tag attributes @param &block [Proc] optional inner XML @return [void]

# File lib/papercraft/tags.rb, line 129
def tag(sym, text = nil, **props, &block)
  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
  else
    @buffer << S_SLASH_GT
  end
end
text(data) click to toggle source

Emits text into the rendering buffer, escaping any special characters to the respective XML entities.

@param data [String] text @return [void]

# File lib/papercraft/tags.rb, line 179
def text(data)
  @buffer << escape_text(data)
end
to_s() click to toggle source

Returns the rendered template.

@return [String]

# File lib/papercraft/tags.rb, line 58
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

Private Instance Methods

att_repr(att) click to toggle source

Converts an attribute to its string representation. This method must be overriden in Renderers which include this module.

@param att [Symbol, String] attribute

# File lib/papercraft/tags.rb, line 258
def att_repr(att)
  raise NotImplementedError
end
define_const_tag_method(tag) click to toggle source

Defines a method that emits the given tag based on a constant. The constant must be defined on the main (Object) binding.

@param tag [Symbol, String] tag/method name @return [void]

# File lib/papercraft/tags.rb, line 190
def define_const_tag_method(tag)
  const = Object.const_get(tag)
  self.class.define_method(tag) { |*a, **b, &blk|
    emit const, *a, **b, &blk
  }
end
define_tag_method(tag) click to toggle source

Defines a normal tag method.

@param tag [Symbol, String] tag/method name @return [void]

# File lib/papercraft/tags.rb, line 201
def define_tag_method(tag)
  repr = tag_repr(tag)
  code = S_TAG_METHOD % {
    tag: tag,
    TAG: tag.upcase,
    tag_pre: "<#{repr}".inspect,
    tag_close: "</#{repr}>".inspect
  }
  self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
end
emit_object(obj) click to toggle source

Emits an arbitrary object by converting it to string, then adding it to the internal buffer. This method is called internally by ‘Renderer#emit`.

@param obj [Object] emitted object @return [void]

# File lib/papercraft/tags.rb, line 217
def emit_object(obj)
  @buffer << obj.to_s
end
emit_props(props) click to toggle source

Emits tag attributes into the rendering buffer.

@param props [Hash] tag attributes @return [void]

# File lib/papercraft/tags.rb, line 266
def emit_props(props)
  props.each { |k, v|
    case k
    when :src, :href
      @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
        EscapeUtils.escape_uri(v) << S_QUOTE
    else
      case v
      when true
        @buffer << S_SPACE << att_repr(k)
      when false, nil
        # emit nothing
      else
        @buffer << S_SPACE << att_repr(k) <<
          S_EQUAL_QUOTE << escape_text(v) << S_QUOTE
      end
    end
  }
end
escape_text(text) click to toggle source

Escapes text. This method must be overriden in Renderers which include this module.

@param text [String] text to be escaped

# File lib/papercraft/tags.rb, line 242
def escape_text(text)
  raise NotImplementedError
end
render_deferred_proc(&block) click to toggle source

Renders a deferred proc by evaluating it, then adding the rendered result to the buffer.

@param &block [Proc] deferred proc @return [void]

# File lib/papercraft/tags.rb, line 226
def render_deferred_proc(&block)
  old_buffer = @buffer
  
  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
  @parts = nil

  instance_eval(&block)

  old_buffer << to_s
  @buffer = old_buffer
end
tag_repr(tag) click to toggle source

Converts a tag to its string representation. This method must be overriden in Renderers which include this module.

@param tag [Symbol, String] tag

# File lib/papercraft/tags.rb, line 250
def tag_repr(tag)
  raise NotImplementedError
end