opulent 0.0.9 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.libold/opulent.rb +96 -0
- data/.libold/opulent/context.rb +80 -0
- data/.libold/opulent/engine.rb +88 -0
- data/.libold/opulent/filter.rb +101 -0
- data/.libold/opulent/logger.rb +67 -0
- data/.libold/opulent/nodes.rb +13 -0
- data/.libold/opulent/nodes/block.rb +29 -0
- data/.libold/opulent/nodes/comment.rb +35 -0
- data/.libold/opulent/nodes/control.rb +230 -0
- data/.libold/opulent/nodes/define.rb +42 -0
- data/.libold/opulent/nodes/eval.rb +41 -0
- data/.libold/opulent/nodes/expression.rb +28 -0
- data/.libold/opulent/nodes/filter.rb +70 -0
- data/.libold/opulent/nodes/helper.rb +69 -0
- data/.libold/opulent/nodes/node.rb +101 -0
- data/.libold/opulent/nodes/root.rb +62 -0
- data/.libold/opulent/nodes/text.rb +54 -0
- data/.libold/opulent/nodes/theme.rb +36 -0
- data/.libold/opulent/parser.rb +252 -0
- data/.libold/opulent/parser/block.rb +70 -0
- data/.libold/opulent/parser/comment.rb +32 -0
- data/.libold/opulent/parser/control.rb +83 -0
- data/.libold/opulent/parser/define.rb +39 -0
- data/.libold/opulent/parser/eval.rb +33 -0
- data/.libold/opulent/parser/expression.rb +350 -0
- data/.libold/opulent/parser/filter.rb +41 -0
- data/.libold/opulent/parser/node.rb +232 -0
- data/.libold/opulent/parser/root.rb +96 -0
- data/.libold/opulent/parser/text.rb +114 -0
- data/.libold/opulent/parser/theme.rb +36 -0
- data/.libold/opulent/preprocessor.rb +102 -0
- data/.libold/opulent/runtime.rb +144 -0
- data/.libold/opulent/template.rb +43 -0
- data/.libold/opulent/tokens.rb +276 -0
- data/.libold/opulent/version.rb +5 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +102 -0
- data/Rakefile +6 -0
- data/benchmark/benchmark.rb +46 -0
- data/benchmark/cases/node/node.haml +17 -0
- data/benchmark/cases/node/node.op +14 -0
- data/benchmark/cases/node/node.slim +19 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/docs/syntax.md +3 -0
- data/docs/usage.md +3 -0
- data/lib/opulent.rb +11 -64
- data/lib/opulent/compiler.rb +132 -0
- data/lib/opulent/compiler/block.rb +31 -0
- data/lib/opulent/compiler/comment.rb +22 -0
- data/lib/opulent/compiler/control.rb +152 -0
- data/lib/opulent/compiler/define.rb +88 -0
- data/lib/opulent/compiler/eval.rb +15 -0
- data/lib/opulent/compiler/filter.rb +54 -0
- data/lib/opulent/compiler/node.rb +232 -0
- data/lib/opulent/compiler/root.rb +19 -0
- data/lib/opulent/compiler/text.rb +60 -0
- data/lib/opulent/context.rb +88 -0
- data/lib/opulent/engine.rb +60 -0
- data/lib/opulent/filters.rb +222 -0
- data/lib/opulent/logger.rb +47 -0
- data/lib/opulent/parser.rb +196 -0
- data/lib/opulent/parser/block.rb +56 -0
- data/lib/opulent/parser/comment.rb +27 -0
- data/lib/opulent/parser/control.rb +112 -0
- data/lib/opulent/parser/define.rb +30 -0
- data/lib/opulent/parser/eval.rb +21 -0
- data/lib/opulent/parser/expression.rb +344 -0
- data/lib/opulent/parser/filter.rb +25 -0
- data/lib/opulent/parser/node.rb +246 -0
- data/lib/opulent/parser/root.rb +48 -0
- data/lib/opulent/parser/text.rb +127 -0
- data/lib/opulent/settings.rb +79 -0
- data/lib/opulent/template.rb +67 -0
- data/lib/opulent/tokens.rb +166 -0
- data/lib/opulent/version.rb +4 -0
- data/opulent.gemspec +36 -0
- metadata +160 -7
@@ -0,0 +1,31 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a while control structure
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def yield_node(node, indent, context)
|
12
|
+
if @block_stack[-1].has_key? node[@value]
|
13
|
+
@block_stack[-1][node[@value]].each do |child|
|
14
|
+
root child, indent, context.parent
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generate the code for a while control structure
|
20
|
+
#
|
21
|
+
# @param node [Array] Node code generation data
|
22
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
23
|
+
# @param context [Context] Processing environment data
|
24
|
+
#
|
25
|
+
def block_node(node, indent, context)
|
26
|
+
node[@children].each do |child|
|
27
|
+
root child, indent, context
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a while control structure
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def comment(node, indent, context)
|
12
|
+
indentation = " " * indent
|
13
|
+
|
14
|
+
value = context.evaluate "\"#{node[@value]}\""
|
15
|
+
|
16
|
+
comment_tag = "#{indentation}<!-- #{value} -->\n"
|
17
|
+
|
18
|
+
@node_stack << :comment
|
19
|
+
@code += comment_tag
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a if-elsif-else control structure
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def if_node(node, indent, context)
|
12
|
+
# Check if we have any condition met, or an else branch
|
13
|
+
index = node[@value].index do |value|
|
14
|
+
value.empty? || context.evaluate(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
# If we have a branch that meets the condition, generate code for the
|
18
|
+
# children related to that specific branch
|
19
|
+
if index
|
20
|
+
node[@children][index].each do |child|
|
21
|
+
root child, indent, context
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generate the code for a case-when-else control structure
|
27
|
+
#
|
28
|
+
# @param node [Array] Node code generation data
|
29
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
30
|
+
# @param context [Context] Processing environment data
|
31
|
+
#
|
32
|
+
def case_node(node, indent, context)
|
33
|
+
# Evaluate the switching condition
|
34
|
+
switch_case = context.evaluate node[@options][:condition]
|
35
|
+
|
36
|
+
# Check if we have any condition met, or an else branch
|
37
|
+
index = node[@value].index do |value|
|
38
|
+
value.empty? || switch_case == context.evaluate(value)
|
39
|
+
end
|
40
|
+
|
41
|
+
# If we have a branch that meets the condition, generate code for the
|
42
|
+
# children related to that specific branch
|
43
|
+
if index
|
44
|
+
node[@children][index].each do |child|
|
45
|
+
root child, indent, context
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Generate the code for a while control structure
|
51
|
+
#
|
52
|
+
# @param node [Array] Node code generation data
|
53
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
54
|
+
# @param context [Context] Processing environment data
|
55
|
+
#
|
56
|
+
def while_node(node, indent, context)
|
57
|
+
# While we have a branch that meets the condition, generate code for the
|
58
|
+
# children related to that specific branch
|
59
|
+
while context.evaluate node[@value]
|
60
|
+
node[@children].each do |child|
|
61
|
+
root child, indent, context
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Generate the code for a while control structure
|
67
|
+
#
|
68
|
+
# @param node [Array] Node code generation data
|
69
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
70
|
+
# @param context [Context] Processing environment data
|
71
|
+
#
|
72
|
+
def until_node(node, indent, context)
|
73
|
+
# Until we have a branch that doesn't meet the condition, generate code for the
|
74
|
+
# children related to that specific branch
|
75
|
+
until context.evaluate node[@value]
|
76
|
+
node[@children].each do |child|
|
77
|
+
root child, indent, context
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Generate the code for a while control structure
|
83
|
+
#
|
84
|
+
# @param node [Array] Node code generation data
|
85
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
86
|
+
# @param context [Context] Processing environment data
|
87
|
+
#
|
88
|
+
def each_node(node, indent, context)
|
89
|
+
result = []
|
90
|
+
|
91
|
+
# Process named variables for each structure
|
92
|
+
variables = node[@value][0].clone
|
93
|
+
|
94
|
+
# The each structure accept missing arguments as well, therefore we need to
|
95
|
+
# substitute them with our defaults
|
96
|
+
#
|
97
|
+
# each in iterable
|
98
|
+
# each value in iterable
|
99
|
+
# each key, value in iterable
|
100
|
+
|
101
|
+
# Value argument name provided only
|
102
|
+
if variables.length == 1
|
103
|
+
variables.unshift Settings::DefaultEachKey
|
104
|
+
|
105
|
+
# Missing key and value arguments
|
106
|
+
elsif variables.empty?
|
107
|
+
variables[0] = Settings::DefaultEachKey
|
108
|
+
variables[1] = Settings::DefaultEachValue
|
109
|
+
end
|
110
|
+
|
111
|
+
# Evaluate in current context and add to results
|
112
|
+
evaluate_children = Proc.new do |key, value, context|
|
113
|
+
# Update the local variables in the each context with the values from the
|
114
|
+
# current loop iteration
|
115
|
+
locals = {
|
116
|
+
variables[0] => key,
|
117
|
+
variables[1] => value
|
118
|
+
}
|
119
|
+
context.extend_locals locals
|
120
|
+
|
121
|
+
# Add the mapped child elements
|
122
|
+
node[@children].each do |child|
|
123
|
+
root child, indent, context
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Create a new context based on the parent context and progressively update
|
128
|
+
# variables in the new context
|
129
|
+
each_context = Context.new Hash.new, context.binding.clone
|
130
|
+
each_context.parent = context
|
131
|
+
|
132
|
+
# Evaluate the iterable object
|
133
|
+
enumerable = each_context.evaluate(node[@value][1])
|
134
|
+
|
135
|
+
# Check if input can be iterated
|
136
|
+
error :enumerable, node[@value][1] unless enumerable.respond_to? :each
|
137
|
+
|
138
|
+
# Selectively iterate through the input and add the result using the previously
|
139
|
+
# defined proc object
|
140
|
+
case enumerable
|
141
|
+
when Hash
|
142
|
+
enumerable.each do |key, value|
|
143
|
+
evaluate_children[key, value, context]
|
144
|
+
end
|
145
|
+
else
|
146
|
+
enumerable.each_with_index do |value, key|
|
147
|
+
evaluate_children[key, value, context]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate code for all nodes by calling the method with their type name
|
6
|
+
#
|
7
|
+
# @param current [Array] Current node data with options
|
8
|
+
# @param indent [Fixnum] Indentation size for current node
|
9
|
+
# @param context [Context] Context holding environment variables
|
10
|
+
#
|
11
|
+
def def_node(node, indent, context)
|
12
|
+
# Create a new definition context
|
13
|
+
definition_context = Context.new
|
14
|
+
definition_context.extend_nonlocals context.binding
|
15
|
+
definition_context.name = node[@value]
|
16
|
+
definition_context.parent = context
|
17
|
+
|
18
|
+
# Set call node
|
19
|
+
call_node = node[@options][:call]
|
20
|
+
|
21
|
+
# Get call node attributes
|
22
|
+
attributes = call_node[@options][:attributes]
|
23
|
+
|
24
|
+
# Evaluate node extension in the current context
|
25
|
+
if call_node[@options][:extension]
|
26
|
+
extension = context.evaluate call_node[@options][:extension][@value]
|
27
|
+
else
|
28
|
+
extension = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Evaluate and generate node attributes, then process each one to
|
32
|
+
# by generating the required attribute code
|
33
|
+
attributes = {}
|
34
|
+
call_node[@options][:attributes].each do |key, attribute|
|
35
|
+
attributes[key] = map_attribute key, attribute, context
|
36
|
+
end
|
37
|
+
|
38
|
+
# Go through each extension attribute and use the value where applicable
|
39
|
+
extend_attributes attributes, extension
|
40
|
+
|
41
|
+
# Definition call arguments
|
42
|
+
arguments = {}
|
43
|
+
|
44
|
+
# Extract values which appear as definition parameters. If we have the
|
45
|
+
# key passed as argument, get its value. Otherwise, set the default
|
46
|
+
# parameter value
|
47
|
+
node[@options][:parameters].each do |key, value|
|
48
|
+
if attributes[key]
|
49
|
+
arguments[key] = attributes.delete key
|
50
|
+
else
|
51
|
+
arguments[key] = definition_context.evaluate value[@value]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set the remaining attributes as a value in the arguments
|
56
|
+
arguments[:attributes] = attributes
|
57
|
+
|
58
|
+
# Add call children to the block stack, depending on whether they're
|
59
|
+
# block elements or child elements
|
60
|
+
@block_stack << { @default_yield => [] }
|
61
|
+
|
62
|
+
# If we have a direct child, add it to the default yield (children)
|
63
|
+
# block and allow same block multiple times by appending nodes
|
64
|
+
call_node[@children].each do |child|
|
65
|
+
if child[@type] == :block
|
66
|
+
@block_stack[-1][child[@value]] ||= []
|
67
|
+
@block_stack[-1][child[@value]] += child[@children]
|
68
|
+
else
|
69
|
+
@block_stack[-1][@default_yield] << child
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Set variable to determine available blocks
|
74
|
+
#
|
75
|
+
arguments[:blocks] = Hash[@block_stack[-1].keys.map{|key| [key, true]}]
|
76
|
+
|
77
|
+
# Create local variables from argument variables
|
78
|
+
definition_context.extend_locals arguments
|
79
|
+
|
80
|
+
# Evaluate the model using the new context
|
81
|
+
node[@children].each do |child|
|
82
|
+
root child, indent, definition_context
|
83
|
+
end
|
84
|
+
|
85
|
+
@block_stack.pop
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Evaluate the embedded ruby code using the current context
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def evaluate(node, indent, context)
|
12
|
+
context.evaluate node[@value]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a while control structure
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def filter(node, indent, context)
|
12
|
+
# Evaluate and generate node attributes, then process each one to
|
13
|
+
# by generating the required attribute code
|
14
|
+
attributes = {}
|
15
|
+
node[@options].each do |key, attribute|
|
16
|
+
attributes[key] = map_attribute key, attribute, context
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get registered filter name
|
20
|
+
name = node[@value]
|
21
|
+
|
22
|
+
# Check if filter is registered
|
23
|
+
error :filter_registered, name unless Filters.filters.has_key? name
|
24
|
+
|
25
|
+
# Load the required filter
|
26
|
+
Filters.filters[name].load_filter
|
27
|
+
|
28
|
+
# Render output using the chosen engine
|
29
|
+
output = Filters.filters[name].render node[@children]
|
30
|
+
|
31
|
+
# Main output node which contains filter rendered value
|
32
|
+
text_node = [:plain, :text, {value: output.rstrip, escaped: false, evaluate: false}, [], nil]
|
33
|
+
|
34
|
+
# If we have a provided filter tag, wrap the text node in the wrapper
|
35
|
+
# node tag and further indent
|
36
|
+
if (wrapper_tag = Filters.filters[name].options[:tag])
|
37
|
+
# Set wrapper tag attributes as evaluable expressions
|
38
|
+
atts = {}
|
39
|
+
Filters.filters[name].options[:attributes].each do |key, value|
|
40
|
+
atts[key] = [:expression, value.inspect, {evaluate: false, escaped: false}]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create the wrapper node containing the output text node as a child
|
44
|
+
wrapper_node = [:node, wrapper_tag, {attributes: atts}, [text_node], indent]
|
45
|
+
|
46
|
+
# Begin code generation from the wrapper node
|
47
|
+
root wrapper_node, indent, context
|
48
|
+
else
|
49
|
+
# Generate code for output text node
|
50
|
+
root text_node, indent, context
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a standard node element, with closing tags or
|
6
|
+
# self enclosing elements
|
7
|
+
#
|
8
|
+
# @param node [Array] Node code generation data
|
9
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
10
|
+
# @param context [Context] Processing environment data
|
11
|
+
#
|
12
|
+
def node(node, indent, context)
|
13
|
+
indentation = " " * indent
|
14
|
+
|
15
|
+
# Check if the current node and last node should be displayed inline
|
16
|
+
inline_current = @inline_node.include? node[@value]
|
17
|
+
inline_last = @inline_node.include? @node_stack.last
|
18
|
+
|
19
|
+
# Check if the node is a special node which can be either inline or
|
20
|
+
# block structure. Write the special node as inline if its children
|
21
|
+
# are all inline nodes
|
22
|
+
if @multi_node.include?(node[@value])
|
23
|
+
# First condition should be removed to ignore preceding node and make
|
24
|
+
# it be inline no matter what. Using the first check, we write it
|
25
|
+
# inline only if the element before it was inline
|
26
|
+
unless @sibling_stack.last > 1 && node[@children].all? do |child|
|
27
|
+
@inline_node.include?(child[@value])
|
28
|
+
end
|
29
|
+
inline_current = false
|
30
|
+
multi = true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# If we have an inline node, we remove the trailing newline character
|
35
|
+
# and write the tag code directly. Otherwise we add the tag code with
|
36
|
+
# normal indentation
|
37
|
+
if inline_last && inline_current
|
38
|
+
remove_trailing_newline
|
39
|
+
else
|
40
|
+
@code += indentation
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add the tag opening, with leading whitespace to the code buffer
|
44
|
+
tag_open = "<#{node[@value]}"
|
45
|
+
@code += " " if node[@options][:leading_whitespace]
|
46
|
+
@code += tag_open
|
47
|
+
|
48
|
+
# Evaluate node extension in the current context
|
49
|
+
if node[@options][:extension]
|
50
|
+
extension = context.evaluate node[@options][:extension][@value]
|
51
|
+
else
|
52
|
+
extension = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Evaluate and generate node attributes, then process each one to
|
56
|
+
# by generating the required attribute code
|
57
|
+
attributes = {}
|
58
|
+
node[@options][:attributes].each do |key, attribute|
|
59
|
+
attributes[key] = map_attribute key, attribute, context
|
60
|
+
end
|
61
|
+
|
62
|
+
# Go through each extension attribute and use the value where applicable
|
63
|
+
extend_attributes attributes, extension
|
64
|
+
|
65
|
+
# Join arrays, create new attributes by hash and set the
|
66
|
+
# value otherwise
|
67
|
+
attributes.each do |key, value|
|
68
|
+
@code += attribute_code key, value
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set the current node as a parent for the node elements to follow
|
72
|
+
@node_stack << (multi ? :multi : node[@value])
|
73
|
+
|
74
|
+
# Check if the current node is self enclosing. Self enclosing nodes
|
75
|
+
# do not have any child elements
|
76
|
+
if node[@options][:self_enclosing]
|
77
|
+
# If the tag is self enclosing, it cannot have any child elements.
|
78
|
+
tag_close = ">"
|
79
|
+
tag_close += "\n"
|
80
|
+
|
81
|
+
@code += tag_close
|
82
|
+
else
|
83
|
+
# Set tag ending code
|
84
|
+
tag_end = ">"
|
85
|
+
|
86
|
+
# If the node is an inline node and doesn't have any child elements,
|
87
|
+
# we close it on the same line, without adding indentation
|
88
|
+
tag_end += "\n" unless inline_current || node[@children].empty?
|
89
|
+
|
90
|
+
# Set tag closing code
|
91
|
+
tag_close = "</#{node[@value]}>"
|
92
|
+
tag_close += " " if node[@options][:trailing_whitespace]
|
93
|
+
tag_close += "\n"
|
94
|
+
|
95
|
+
# Add tag ending to the buffer
|
96
|
+
@code += tag_end
|
97
|
+
|
98
|
+
# Get number of siblings
|
99
|
+
@sibling_stack << node[@children].size
|
100
|
+
|
101
|
+
# Process each child element recursively, increasing indentation
|
102
|
+
node[@children].each do |child|
|
103
|
+
root child, indent + Settings[:indent], context
|
104
|
+
end
|
105
|
+
|
106
|
+
# Remove the current node children count from the sibling stack
|
107
|
+
@sibling_stack.pop
|
108
|
+
|
109
|
+
# Remove all child nodes of the current node from the node stack
|
110
|
+
@node_stack.pop(node[@children].size)
|
111
|
+
|
112
|
+
# If we have an inline node, we remove the trailing newline from
|
113
|
+
# our buffer, otherwise add indentation
|
114
|
+
if inline_current
|
115
|
+
remove_trailing_newline
|
116
|
+
|
117
|
+
# If the node doesn't have any child elements, we close it on the same
|
118
|
+
# line, without adding indentation
|
119
|
+
elsif node[@children].any?
|
120
|
+
@code += indentation
|
121
|
+
end
|
122
|
+
|
123
|
+
# Close the current tag
|
124
|
+
@code += tag_close
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Map attributes by evaluating them in the current working context
|
129
|
+
#
|
130
|
+
# @param key [Symbol] Name of the attribute being processed
|
131
|
+
# @param attribute [Array] Attribute instance data
|
132
|
+
# @param context [Context] Processing environment data
|
133
|
+
#
|
134
|
+
def map_attribute(key, attribute, context)
|
135
|
+
# Process input value depending on its type. When array or hash, iterate
|
136
|
+
# and escape each string value.
|
137
|
+
process = Proc.new do |value|
|
138
|
+
case value
|
139
|
+
when Array
|
140
|
+
value.map do |v|
|
141
|
+
v.is_a?(String) ? escape(v) : v
|
142
|
+
end
|
143
|
+
when Hash
|
144
|
+
value.each do |k,v|
|
145
|
+
value[k] = value[k].is_a?(String) ? escape(v) : v
|
146
|
+
end
|
147
|
+
when String
|
148
|
+
escape value
|
149
|
+
else
|
150
|
+
value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Process each attribute depending on whether it's an array of values,
|
155
|
+
# exclusive to the class attribute, or an individual attribute value
|
156
|
+
if key == :class
|
157
|
+
attribute.map do |attrib|
|
158
|
+
value = context.evaluate attrib[@value]
|
159
|
+
attrib[@options][:escaped] ? process[value] : value
|
160
|
+
end
|
161
|
+
else
|
162
|
+
value = context.evaluate attribute[@value]
|
163
|
+
attribute[@options][:escaped] ? process[value] : value
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Extend attributes using the extension directive where applicable.
|
168
|
+
# Concatenate arrays, merge hashes and replace otherwise
|
169
|
+
#
|
170
|
+
# @param attributes [Hash] Evaluated node attributes
|
171
|
+
# @param extension [Hash] Node extension input
|
172
|
+
#
|
173
|
+
def extend_attributes(attributes, extension)
|
174
|
+
extension.each do |key, value|
|
175
|
+
case attributes[key]
|
176
|
+
when Array
|
177
|
+
if key == :class
|
178
|
+
attributes[key] << value
|
179
|
+
attributes[key].flatten!
|
180
|
+
else
|
181
|
+
attributes[key] = value
|
182
|
+
end
|
183
|
+
when Hash
|
184
|
+
if value.is_a? Hash
|
185
|
+
attributes[key].merge! value
|
186
|
+
else
|
187
|
+
attributes[key] = value
|
188
|
+
end
|
189
|
+
else
|
190
|
+
attributes[key] = value
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Generate attribute code for the current key value pair. For string
|
196
|
+
# values, generate a key value pair. For false values, remove the
|
197
|
+
# attribute. For true values, generate a standalone attribute key
|
198
|
+
#
|
199
|
+
# @param key [Symbol] Name of the attribute being generated
|
200
|
+
# @param value [Object] Value of the attribute
|
201
|
+
#
|
202
|
+
def attribute_code(key, value)
|
203
|
+
attribute_code = ""
|
204
|
+
|
205
|
+
case value
|
206
|
+
when Array
|
207
|
+
if key == :class
|
208
|
+
attribute_value = value.join ' '
|
209
|
+
else
|
210
|
+
attribute_value = value.join '_'
|
211
|
+
end
|
212
|
+
|
213
|
+
attribute_code += " #{key}"
|
214
|
+
attribute_code += "=\"#{attribute_value}\"" unless attribute_value.empty?
|
215
|
+
when Hash
|
216
|
+
value.each do |k,v|
|
217
|
+
if v
|
218
|
+
attribute_code += " #{key}-#{k}"
|
219
|
+
attribute_code += "=\"#{v.to_s}\"" unless v == true
|
220
|
+
end
|
221
|
+
end
|
222
|
+
else
|
223
|
+
if value
|
224
|
+
attribute_code += " #{key}"
|
225
|
+
attribute_code += "=\"#{value.to_s}\"" unless value == true
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
return attribute_code
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|