Module: Rxhp::Scope

Included in:
Element
Defined in:
lib/rxhp/scope.rb

Overview

A place for factory methods to be defined.

These are methods like Rxhp::Scope#h1 which creates an instance of Rxhp::Html::H1.

The actual HTML classes are defined in rxhp/html.rb

Class Method Summary collapse

Class Method Details

.currentObject

The element nesting scope.

Examples:

Rxhp::Scope.current.should be_a Rxhp::Fragment
html.do
  Rxhp::Scope.current.should be_a Rxhp::Html::Html
  body do
    Rxhp::Scope.current.should be_a Rxhp::Html::Body
  end
end

22
23
24
25
26
27
28
29
30
# File 'lib/rxhp/scope.rb', line 22

def self.current
  callcc do |cc|
    begin
      throw(:rxhp_parent, cc)
    rescue NameError, ArgumentError
      Rxhp::Fragment.new
    end
  end
end

.define_element(name, klass, namespace = Kernel) ⇒ Object

Define a factory method that takes a block, with a scope.

Examples:

define_element tag, Tag
tag
tag 'content'
tag(:attribute => value)
tag('content', :attribute => 'value')
tag(:attribute => 'value') do
  text 'content'
end

Parameters:

  • name (String)

    is the name of the method to define

  • klass (Class)

    is the Element subclass to construct

  • namespace (Module) (defaults to: Kernel)

    is where to define the method


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rxhp/scope.rb', line 60

def self.define_element name, klass, namespace = Kernel
  impl = Proc.new do |*args, &block|
    # Yay for faking named parameters as a hash :p
    children = nil
    attributes = {}
    args.each do |arg|
      if arg.is_a?(Hash)
        attributes = arg
      else
        children ||= []
        children.push arg
      end
    end

    # Create the actual element
    element = klass.new(attributes)
    Rxhp::Scope.current.children.push element

    # Append non-block children
    if children
      children.each do |child|
        element.children.push child
      end
    end

    if block
      Rxhp::Scope.with_parent(element) do
        if block.call.is_a? String
          raise Rxhp::ScriptError.new(
            "In a block, use the 'text' method to include Strings"
          )
        end
      end
      element.validate! if element.respond_to?(:validate!)
      nil
    end
    element
  end

  # Instance method if mixed in.
  #
  # Usage:
  #  include Rxhp::Html
  #  html do
  #    ...
  #  end
  namespace.send(:define_method, name, impl)  # Class method for fully-qualified.
  #
  # Usage:
  #  Rxhp::Html.html do
  #    ...
  #  end

  (class <<namespace; self; end).send(:define_method, name, impl)
end

.with_parent(parent, &block) ⇒ Object

Set the value of #current for a block, and call it.

Used by #define_element


35
36
37
38
39
40
41
42
43
# File 'lib/rxhp/scope.rb', line 35

def self.with_parent parent, &block  # push element onto the render stack...

  cc = catch(:rxhp_parent) do
    # ... and call the block with that new stack
    block.call
    nil
  end
  cc.call(parent) if cc
end