class Pakyow::Presenter::View

Provides an interface for manipulating view templates.

Constants

INFO_MERGER

Thanks Dan! stackoverflow.com/a/30225093 @api private

Attributes

logical_path[R]

The logical path to the view template.

object[RW]

The object responsible for transforming and rendering the underlying document or node.

@api private

Public Class Methods

from_object(object) click to toggle source

Creates a view wrapping an object.

# File lib/pakyow/presenter/view.rb, line 26
def from_object(object)
  instance = if object.is_a?(StringDoc::Node) && object.labeled?(:view_type)
    object.label(:view_type).allocate
  else
    allocate
  end

  instance.instance_variable_set(:@object, object)
  instance.instance_variable_set(:@info, {})
  instance.instance_variable_set(:@logical_path, nil)

  if object.respond_to?(:attributes)
    instance.attributes = object.attributes
  else
    instance.instance_variable_set(:@attributes, nil)
  end

  instance
end
from_view_or_string(view_or_string) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 47
def from_view_or_string(view_or_string)
  case view_or_string
  when View, VersionedView
    view_or_string
  else
    View.new(Support::SafeStringHelpers.ensure_html_safety(view_or_string.to_s))
  end
end
load(path, content: nil) click to toggle source

Creates a view from a file.

# File lib/pakyow/presenter/view.rb, line 20
def load(path, content: nil)
  new(content || File.read(path))
end
new(html, info: {}, logical_path: nil) click to toggle source

Creates a view with html.

# File lib/pakyow/presenter/view.rb, line 77
def initialize(html, info: {}, logical_path: nil)
  @object = StringDoc.new(html)
  @info, @logical_path = Support::IndifferentHash.deep(info), logical_path

  if @object.respond_to?(:attributes)
    self.attributes = @object.attributes
  else
    @attributes = nil
  end
end

Public Instance Methods

==(other) click to toggle source

Returns true if self equals other.

# File lib/pakyow/presenter/view.rb, line 392
def ==(other)
  other.is_a?(self.class) && @object == other.object
end
add_info(*infos) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 559
def add_info(*infos)
  tap do
    infos.each do |info|
      @info.merge!(Support::IndifferentHash.deep(info), &INFO_MERGER)
    end
  end
end
after(view_or_string) click to toggle source

Inserts a view or string after self.

# File lib/pakyow/presenter/view.rb, line 322
def after(view_or_string)
  tap do
    @object.after(self.class.from_view_or_string(view_or_string).object)
  end
end
append(view_or_string) click to toggle source

Appends a view or string to self.

# File lib/pakyow/presenter/view.rb, line 306
def append(view_or_string)
  tap do
    @object.append(self.class.from_view_or_string(view_or_string).object)
  end
end
attributes() click to toggle source

Returns attributes object for self.

# File lib/pakyow/presenter/view.rb, line 398
def attributes
  @attributes
end
Also aliased as: attrs
attributes=(attributes) click to toggle source

Wraps attributes in a {Attributes} instance.

# File lib/pakyow/presenter/view.rb, line 405
def attributes=(attributes)
  @attributes = Attributes.new(attributes)
end
Also aliased as: attrs=
attrs()
Alias for: attributes
attrs=(attributes)
Alias for: attributes=
before(view_or_string) click to toggle source

Inserts a view or string before self.

# File lib/pakyow/presenter/view.rb, line 330
def before(view_or_string)
  tap do
    @object.before(self.class.from_view_or_string(view_or_string).object)
  end
end
bind(object) click to toggle source

Binds a single object.

# File lib/pakyow/presenter/view.rb, line 276
def bind(object)
  tap do
    unless object.nil?
      each_binding_prop do |binding|
        binding_name = if binding.significant?(:multipart_binding)
          binding.label(:binding_prop)
        else
          binding.label(:binding)
        end

        if object.include?(binding_name)
          value = if object.is_a?(Binder)
            object.__content(binding_name, binding)
          else
            object[binding_name]
          end

          bind_value_to_node(value, binding)
          binding.set_label(:bound, true)
        end
      end

      attributes[:"data-id"] = object[:id]
      self.object.set_label(:bound, true)
    end
  end
end
binding?() click to toggle source

Returns true if self is a binding.

# File lib/pakyow/presenter/view.rb, line 368
def binding?
  @object.significant?(:binding)
end
binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 427
def binding_name
  label(:binding)
end
binding_prop?(node) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 527
def binding_prop?(node)
  node.significant?(:binding) && node.label(:version) != :empty && (!node.significant?(:binding_within) || node.significant?(:multipart_binding))
end
binding_props(descend: false) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 517
def binding_props(descend: false)
  each_binding_prop(descend: descend).map(&:itself)
end
binding_scope?(node) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 522
def binding_scope?(node)
  node.significant?(:binding) && (node.significant?(:binding_within) || node.significant?(:multipart_binding) || node.label(:version) == :empty)
end
binding_scopes(descend: false) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 512
def binding_scopes(descend: false)
  each_binding_scope(descend: descend).map(&:itself)
end
body() click to toggle source

Returns a view for the +<body>+ node.

# File lib/pakyow/presenter/view.rb, line 221
def body
  if body_node = @object.find_first_significant_node(:body)
    View.from_object(body_node)
  else
    nil
  end
end
channeled_binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 442
def channeled_binding_name
  label(:channeled_binding)
end
channeled_binding_scope?(scope) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 568
def channeled_binding_scope?(scope)
  binding_scopes.select { |node|
    node.label(:binding) == scope
  }.any? { |node|
    node.label(:channel).any?
  }
end
clear() click to toggle source

Removes self's children.

# File lib/pakyow/presenter/view.rb, line 354
def clear
  tap do
    @object.clear
  end
end
component(name, renderable: false) click to toggle source

Finds a component matching name.

# File lib/pakyow/presenter/view.rb, line 179
def component(name, renderable: false)
  name = name.to_sym
  components(renderable: renderable).find { |component|
    component.object.label(:components).any? { |possible_component|
      possible_component[:name] == name
    }
  }
end
components(renderable: false) click to toggle source

Returns all components.

@api private

# File lib/pakyow/presenter/view.rb, line 191
def components(renderable: false)
  @object.each_significant_node_without_descending_into_type(:component, descend: true).select { |node|
    !renderable || node.label(:components).any? { |component| component[:renderable] }
  }.map { |node|
    View.from_object(node)
  }
end
container?() click to toggle source

Returns true if self is a container.

# File lib/pakyow/presenter/view.rb, line 374
def container?
  @object.significant?(:container)
end
each_binding(name) { |node| ... } click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 495
def each_binding(name)
  return enum_for(:each_binding, name) unless block_given?

  each_binding_scope do |node|
    if node.label(:channeled_binding) == name
      yield node
    end
  end

  each_binding_prop do |node|
    if (node.significant?(:multipart_binding) && node.label(:binding_prop) == name) || (!node.significant?(:multipart_binding) && node.label(:binding) == name)
      yield node
    end
  end
end
each_binding_prop(descend: false) { |object| ... } click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 474
def each_binding_prop(descend: false)
  return enum_for(:each_binding_prop, descend: descend) unless block_given?

  if (@object.is_a?(StringDoc::Node) || @object.is_a?(StringDoc::MetaNode)) && @object.significant?(:multipart_binding)
    yield @object
  else
    method = if descend
      :each_significant_node
    else
      :each_significant_node_without_descending_into_type
    end

    @object.send(method, :binding, descend: descend) do |node|
      if binding_prop?(node)
        yield node
      end
    end
  end
end
each_binding_scope(descend: false) { |node| ... } click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 457
def each_binding_scope(descend: false)
  return enum_for(:each_binding_scope, descend: descend) unless block_given?

  method = if descend
    :each_significant_node
  else
    :each_significant_node_without_descending_into_type
  end

  @object.send(method, :binding, descend: descend) do |node|
    if binding_scope?(node)
      yield node
    end
  end
end
find(*names) { |result| ... } click to toggle source

Finds a view binding by name. When passed more than one value, the view will be traversed through each name. Returns a {VersionedView}.

# File lib/pakyow/presenter/view.rb, line 122
def find(*names)
  if names.any?
    named = names.shift.to_sym
    found = each_binding(named).map(&:itself)

    result = if names.empty? && !found.empty? # found everything; wrap it up
      if found[0].is_a?(StringDoc::MetaNode)
        VersionedView.new(View.from_object(found[0]))
      else
        VersionedView.new(View.from_object(StringDoc::MetaNode.new(found)))
      end
    elsif !found.empty? && names.count > 0 # descend further
      View.from_object(found[0]).find(*names)
    else
      nil
    end

    if result && block_given?
      yield result
    end

    result
  else
    nil
  end
end
find_all(named) click to toggle source

Finds all view bindings by name, returning an array of {View} objects.

@api private

# File lib/pakyow/presenter/view.rb, line 152
def find_all(named)
  each_binding(named).map { |node|
    View.from_object(node)
  }
end
find_partials(partials, found = []) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 532
def find_partials(partials, found = [])
  found.tap do
    @object.each_significant_node(:partial, descend: true) do |node|
      if replacement = partials[node.label(:partial)]
        found << node.label(:partial)
        replacement.find_partials(partials, found)
      end
    end
  end
end
form(name) click to toggle source

Finds a form with a binding matching name.

# File lib/pakyow/presenter/view.rb, line 160
def form(name)
  @object.each_significant_node(:form) do |form_node|
    return Views::Form.from_object(form_node) if form_node.label(:binding) == name
  end

  nil
end
form?() click to toggle source

Returns true if self is a form.

# File lib/pakyow/presenter/view.rb, line 386
def form?
  @object.significant?(:form)
end
forms() click to toggle source

Returns all forms.

@api private

# File lib/pakyow/presenter/view.rb, line 171
def forms
  @object.each_significant_node(:form, descend: true).map { |node|
    Views::Form.from_object(node)
  }
end
head() click to toggle source

Returns a view for the +<head>+ node.

# File lib/pakyow/presenter/view.rb, line 211
def head
  if head_node = @object.find_first_significant_node(:head)
    View.from_object(head_node)
  else
    nil
  end
end
html=(html) click to toggle source

Safely sets the html value of self.

# File lib/pakyow/presenter/view.rb, line 362
def html=(html)
  @object.html = ensure_html_safety(html.to_s)
end
info(key = nil) click to toggle source

Returns all view info when key is nil, otherwise returns the value for key.

# File lib/pakyow/presenter/view.rb, line 201
def info(key = nil)
  if key.nil?
    @info
  else
    @info.fetch(key, nil)
  end
end
initialize_copy(_) click to toggle source
Calls superclass method
# File lib/pakyow/presenter/view.rb, line 88
def initialize_copy(_)
  super

  @info = @info.dup
  @object = @object.dup

  if @object.respond_to?(:attributes)
    self.attributes = @object.attributes
  else
    @attributes = nil
  end
end
mixin(partials) click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 544
def mixin(partials)
  tap do
    @object.each_significant_node(:partial, descend: true) do |partial_node|
      if replacement = partials[partial_node.label(:partial)]
        partial_node.replace(replacement.mixin(partials).object)
      end
    end
  end
end
partial?() click to toggle source

Returns true if self is a partial.

# File lib/pakyow/presenter/view.rb, line 380
def partial?
  @object.significant?(:partial)
end
plural_binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 437
def plural_binding_name
  label(:plural_binding)
end
plural_channeled_binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 447
def plural_channeled_binding_name
  label(:plural_channeled_binding)
end
prepend(view_or_string) click to toggle source

Prepends a view or string to self.

# File lib/pakyow/presenter/view.rb, line 314
def prepend(view_or_string)
  tap do
    @object.prepend(self.class.from_view_or_string(view_or_string).object)
  end
end
remove() click to toggle source

Removes self.

# File lib/pakyow/presenter/view.rb, line 346
def remove
  tap do
    @object.remove
  end
end
replace(view_or_string) click to toggle source

Replaces self with a view or string.

# File lib/pakyow/presenter/view.rb, line 338
def replace(view_or_string)
  tap do
    @object.replace(self.class.from_view_or_string(view_or_string).object)
  end
end
singular_binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 432
def singular_binding_name
  label(:singular_binding)
end
singular_channeled_binding_name() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 452
def singular_channeled_binding_name
  label(:singular_channeled_binding)
end
soft_copy() click to toggle source

@api private

# File lib/pakyow/presenter/view.rb, line 102
def soft_copy
  instance = self.class.allocate

  instance.instance_variable_set(:@info, @info.dup)

  new_object = @object.soft_copy
  instance.instance_variable_set(:@object, new_object)

  if new_object.respond_to?(:attributes)
    instance.attributes = new_object.attributes
  else
    instance.instance_variable_set(:@attributes, nil)
  end

  instance
end
title() click to toggle source

Returns a view for the +<title>+ node.

# File lib/pakyow/presenter/view.rb, line 231
def title
  if title_node = @object.find_first_significant_node(:title)
    View.from_object(title_node)
  else
    nil
  end
end
to_html() click to toggle source

Converts self to html, rendering the view.

# File lib/pakyow/presenter/view.rb, line 418
def to_html
  @object.to_html
end
to_s() click to toggle source
# File lib/pakyow/presenter/view.rb, line 422
def to_s
  @object.to_s
end
transform(object) { |self, object| ... } click to toggle source

Transforms self to match structure of object.

# File lib/pakyow/presenter/view.rb, line 249
def transform(object)
  tap do
    if object.nil? || (object.respond_to?(:empty?) && object.empty?)
      remove
    else
      removals = []
      each_binding_prop(descend: false) do |binding|
        binding_name = if binding.significant?(:multipart_binding)
          binding.label(:binding_prop)
        else
          binding.label(:binding)
        end

        unless object.present?(binding_name)
          removals << binding
        end
      end

      removals.each(&:remove)
    end

    yield self, object if block_given?
  end
end
version() click to toggle source

Returns the version name for self.

# File lib/pakyow/presenter/view.rb, line 412
def version
  (label(:version) || VersionedView::DEFAULT_VERSION).to_sym
end
with() { |self| ... } click to toggle source

Yields self.

# File lib/pakyow/presenter/view.rb, line 241
def with
  tap do
    yield self
  end
end

Private Instance Methods

bind_value_to_node(value, node) click to toggle source
# File lib/pakyow/presenter/view.rb, line 578
def bind_value_to_node(value, node)
  tag = node.tagname
  unless StringDoc::Node.without_value?(tag)
    value = String(value)

    if StringDoc::Node.self_closing?(tag)
      node.attributes[:value] = ensure_html_safety(value) if node.attributes[:value].nil?
    else
      node.html = ensure_html_safety(value)
    end
  end
end