opulent 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/benchmark/benchmark.rb +18 -7
- data/benchmark/cases/node/node.haml +6 -16
- data/benchmark/cases/node/node.op +6 -13
- data/benchmark/cases/node/node.slim +6 -18
- data/docs/comments.md +5 -0
- data/docs/filters.md +31 -0
- data/docs/includes.md +0 -0
- data/docs/nodes.md +88 -0
- data/docs/reference.md +7 -6
- data/lib/opulent.rb +2 -1
- data/lib/opulent/compiler.rb +19 -18
- data/lib/opulent/compiler/buffer.rb +260 -0
- data/lib/opulent/compiler/comment.rb +4 -9
- data/lib/opulent/compiler/control.rb +65 -67
- data/lib/opulent/compiler/define.rb +91 -63
- data/lib/opulent/compiler/doctype.rb +1 -5
- data/lib/opulent/compiler/eval.rb +1 -1
- data/lib/opulent/compiler/node.rb +10 -194
- data/lib/opulent/compiler/root.rb +1 -1
- data/lib/opulent/compiler/text.rb +3 -40
- data/lib/opulent/compiler/yield.rb +15 -0
- data/lib/opulent/context.rb +2 -2
- data/lib/opulent/engine.rb +56 -57
- data/lib/opulent/parser.rb +10 -10
- data/lib/opulent/parser/control.rb +2 -3
- data/lib/opulent/parser/expression.rb +1 -1
- data/lib/opulent/parser/{require.rb → include.rb} +17 -15
- data/lib/opulent/parser/node.rb +22 -17
- data/lib/opulent/parser/root.rb +3 -4
- data/lib/opulent/parser/text.rb +3 -7
- data/lib/opulent/parser/yield.rb +23 -0
- data/lib/opulent/settings.rb +5 -4
- data/lib/opulent/template.rb +12 -30
- data/lib/opulent/tokens.rb +5 -9
- data/lib/opulent/utils.rb +41 -0
- data/lib/opulent/version.rb +1 -1
- metadata +9 -5
- data/lib/opulent/compiler/block.rb +0 -31
- data/lib/opulent/parser/block.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: adcb317d2774dc8ee749c9a36ac819b409812ad4
|
4
|
+
data.tar.gz: 50c443fb0d1df18f2455aff4f52c0ba0f7187cea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2ba4bdb98dc6754ade2083b1a812c8577398013ffb7f77265ac09c10a95c6da76fdbc3bb2b1f5f710b514d9c7854379f5aa6809ddea5ebe300ca91b5f0c85b1
|
7
|
+
data.tar.gz: f581c87ad7188a2de2821a5705e3e94481720eaf7d8a6865ddcef8e721b66e6ec92fc52e33792f88635e05cb65d0e11ac9ec6eae5e7ef15fa03e1929ec89d882
|
data/benchmark/benchmark.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'benchmark'
|
2
2
|
require_relative '../lib/opulent'
|
3
|
-
require 'slim'
|
4
|
-
require 'haml'
|
5
3
|
|
6
4
|
# Choose the benchmark you want to run
|
7
5
|
BENCHMARK = :node
|
@@ -10,7 +8,8 @@ BENCHMARK = :node
|
|
10
8
|
N = 1000
|
11
9
|
|
12
10
|
# Templating engine initialization
|
13
|
-
puts "BENCHMARK"
|
11
|
+
puts "BENCHMARK\n--\n"
|
12
|
+
|
14
13
|
case BENCHMARK
|
15
14
|
when :node
|
16
15
|
case_folder = 'cases/node/'
|
@@ -24,22 +23,34 @@ when :node
|
|
24
23
|
c: 5
|
25
24
|
}
|
26
25
|
|
27
|
-
|
26
|
+
op = Opulent.new :"#{case_folder}node"
|
27
|
+
# op2 = Opulent.new :"#{case_folder}yield", def: op.def
|
28
|
+
|
29
|
+
puts op.render(op, locals){}
|
30
|
+
#
|
31
|
+
# puts op.render(op, locals){
|
32
|
+
# op2.render(op, locals){}
|
33
|
+
# }
|
34
|
+
|
35
|
+
puts "\n\n"
|
36
|
+
|
37
|
+
puts op.template
|
38
|
+
puts "\n\n"
|
28
39
|
|
29
40
|
Benchmark.bm do |x|
|
30
41
|
x.report("haml") do
|
31
42
|
N.times do
|
32
|
-
haml.render(
|
43
|
+
haml.render(Object.new, locals){}
|
33
44
|
end
|
34
45
|
end
|
35
46
|
x.report("opulent") do
|
36
47
|
N.times do
|
37
|
-
opulent.render(
|
48
|
+
opulent.render(Object.new, locals){}
|
38
49
|
end
|
39
50
|
end
|
40
51
|
x.report("slim") do
|
41
52
|
N.times do
|
42
|
-
slim.render(
|
53
|
+
slim.render(Object.new, locals){}
|
43
54
|
end
|
44
55
|
end
|
45
56
|
end
|
@@ -1,17 +1,7 @@
|
|
1
|
-
|
1
|
+
- a = "hello"
|
2
|
+
- kls = ['a', 'b', 'c']
|
3
|
+
|
4
|
+
%html hello=a
|
2
5
|
%head
|
3
|
-
|
4
|
-
%
|
5
|
-
%body
|
6
|
-
%nav.navbar
|
7
|
-
%ul.nav.left
|
8
|
-
%li Home
|
9
|
-
%li About
|
10
|
-
%li Contact
|
11
|
-
#content
|
12
|
-
.container
|
13
|
-
.row
|
14
|
-
.col-md-12
|
15
|
-
| Hello world, this is a dummy text used for the speed benchmark in Opulent
|
16
|
-
%footer
|
17
|
-
| Copyright © Hello world!
|
6
|
+
%body class=kls
|
7
|
+
%p Hello world!
|
@@ -1,19 +1,7 @@
|
|
1
|
-
|
1
|
+
- a = "hello"
|
2
|
+
- kls = ['a', 'b', 'c']
|
3
|
+
|
4
|
+
html hello=a
|
2
5
|
head
|
3
|
-
|
4
|
-
|
5
|
-
meta name="description" content="Node benchmark"
|
6
|
-
body
|
7
|
-
nav.navbar
|
8
|
-
ul.nav.left
|
9
|
-
li Home
|
10
|
-
li About
|
11
|
-
li Contact
|
12
|
-
#content
|
13
|
-
.container
|
14
|
-
.row
|
15
|
-
.col-md-12
|
16
|
-
| Hello world, this is a dummy text used for
|
17
|
-
the speed benchmark in Opulent
|
18
|
-
footer
|
19
|
-
| Copyright © Hello world!
|
6
|
+
body class=kls
|
7
|
+
p Hello world!
|
data/docs/comments.md
CHANGED
@@ -17,6 +17,11 @@ Block comments are marked up with two forward slash characters `//`.
|
|
17
17
|
## Output Comments
|
18
18
|
By default, comments are only for reference inside Opulent files. You can output them
|
19
19
|
by adding an exclamation character `!`, to resemble HTML.
|
20
|
+
|
20
21
|
```html
|
21
22
|
/! This comment will be outputted
|
22
23
|
```
|
24
|
+
|
25
|
+
```html
|
26
|
+
<!-- This comment will be outputted -->
|
27
|
+
```
|
data/docs/filters.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Opulent Filters
|
2
|
+
|
3
|
+
Filters let you use other languages within a opulent template. They take a block of plain text as an input.
|
4
|
+
Some filters also have a default tag associated with them, such as the `css` filter and `javascript` filter.
|
5
|
+
|
6
|
+
```html
|
7
|
+
:markdown
|
8
|
+
# Markdown
|
9
|
+
|
10
|
+
I often like including markdown documents.
|
11
|
+
|
12
|
+
:coffee-script
|
13
|
+
console.log 'This is coffeescript'
|
14
|
+
```
|
15
|
+
|
16
|
+
By default, the following filters are included:
|
17
|
+
|
18
|
+
```
|
19
|
+
:coffeescript
|
20
|
+
:javascript
|
21
|
+
:scss
|
22
|
+
:sass
|
23
|
+
:css
|
24
|
+
:cdata
|
25
|
+
:escaped
|
26
|
+
:markdown
|
27
|
+
:maruku
|
28
|
+
:textile
|
29
|
+
```
|
30
|
+
|
31
|
+
Each filter uses its associated gem and requires it to be installed.
|
data/docs/includes.md
ADDED
File without changes
|
data/docs/nodes.md
CHANGED
@@ -30,4 +30,92 @@ Nodes such as text can be written inline without any identifier. However, when w
|
|
30
30
|
```sass
|
31
31
|
ul
|
32
32
|
li > a href="http://google.com" Google
|
33
|
+
|
34
|
+
a href="google" > i.fa.fa-circle
|
35
|
+
| Hello world
|
36
|
+
```
|
37
|
+
|
38
|
+
```html
|
39
|
+
<ul>
|
40
|
+
<li>
|
41
|
+
<a href="http://google.com">Google</a>
|
42
|
+
</li>
|
43
|
+
</ul>
|
44
|
+
|
45
|
+
<a href="http://google.com">
|
46
|
+
<i class="fa fa-circle"></i>
|
47
|
+
Google
|
48
|
+
</a>
|
49
|
+
```
|
50
|
+
|
51
|
+
## Inline Text Feed
|
52
|
+
We can write text directly inline with our node.
|
53
|
+
|
54
|
+
```sass
|
55
|
+
p This is <escaped> inline text
|
56
|
+
p ~ This is <unescaped> inline text
|
57
|
+
|
58
|
+
p | This is <escaped> multiline text
|
59
|
+
which is more indented than the paragraph node.
|
60
|
+
p |~ This is <unescaped> multiline text
|
61
|
+
which is more indented than the paragraph node.
|
62
|
+
```
|
63
|
+
|
64
|
+
```html
|
65
|
+
<p>This is <escaped> inline text</p>
|
66
|
+
<p>This is <unescaped> inline text</p>
|
67
|
+
|
68
|
+
<p>This is <escaped> multiline text which is more indented than the paragraph node.</p>
|
69
|
+
<p>This is <escaped> multiline text which is more indented than the paragraph node.</p>
|
70
|
+
```
|
71
|
+
|
72
|
+
## Explicit Inline Text Feed
|
73
|
+
If we want Opulent to parse the following part as text explicitly, we can use a backslash `\` character,
|
74
|
+
which stops the parsing of the current line and starts getting a text feed.
|
75
|
+
|
76
|
+
```
|
77
|
+
p id="paragraph" Inside
|
78
|
+
|
79
|
+
p \id="paragraph" Inside
|
80
|
+
```
|
81
|
+
|
82
|
+
The `\` character will gather the rest of the line as text.
|
83
|
+
|
84
|
+
```html
|
85
|
+
<p id="paragraph">Inside</p>
|
86
|
+
|
87
|
+
<p>id="paragraph" Inside</p>
|
88
|
+
```
|
89
|
+
|
90
|
+
## Whitespace
|
91
|
+
|
92
|
+
Sometimes we need to leave a leading or trailing whitespace at a certain node, like `strong` or `a`.
|
93
|
+
We can do that using pointer arrows.
|
94
|
+
|
95
|
+
```
|
96
|
+
| We want a space before
|
97
|
+
strong<- this text.
|
98
|
+
```
|
99
|
+
|
100
|
+
```
|
101
|
+
strong-> This text
|
102
|
+
| has a space after it.
|
103
|
+
```
|
104
|
+
|
105
|
+
```
|
106
|
+
| Before
|
107
|
+
strong<-> this text
|
108
|
+
| and after it.
|
109
|
+
```
|
110
|
+
|
111
|
+
## Self Enclosing
|
112
|
+
|
113
|
+
We can explicitly self enclose nodes. By default, Opulent knows that nodes such as `img` need to be self enclosed.
|
114
|
+
|
115
|
+
```
|
116
|
+
mynode /
|
117
|
+
```
|
118
|
+
|
119
|
+
```html
|
120
|
+
<mynode>
|
33
121
|
```
|
data/docs/reference.md
CHANGED
@@ -3,9 +3,10 @@ Opulent is an __Intelligent Web Templating Engine__ created for extremely fast a
|
|
3
3
|
|
4
4
|
### Language Reference
|
5
5
|
|
6
|
-
1. [
|
7
|
-
2. [
|
8
|
-
3. [
|
9
|
-
4. [
|
10
|
-
5. [
|
11
|
-
|
6
|
+
1. [Nodes](nodes.md)
|
7
|
+
2. [Attributes](attributes.md)
|
8
|
+
3. [Control Structures](control-structures.md)
|
9
|
+
4. [Expressions](expressions.md)
|
10
|
+
5. [Comments](comments.md)
|
11
|
+
6. [Doctype](doctype.md)
|
12
|
+
7. [Filters](filters.md)
|
data/lib/opulent.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'tilt'
|
2
|
-
require '
|
2
|
+
require 'escape_utils'
|
3
3
|
require_relative 'opulent/compiler.rb'
|
4
4
|
require_relative 'opulent/context.rb'
|
5
5
|
require_relative 'opulent/engine.rb'
|
@@ -9,3 +9,4 @@ require_relative 'opulent/parser.rb'
|
|
9
9
|
require_relative 'opulent/settings.rb'
|
10
10
|
require_relative 'opulent/template.rb'
|
11
11
|
require_relative 'opulent/tokens.rb'
|
12
|
+
require_relative 'opulent/utils.rb'
|
data/lib/opulent/compiler.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative 'compiler/
|
1
|
+
require_relative 'compiler/buffer.rb'
|
2
2
|
require_relative 'compiler/comment.rb'
|
3
3
|
require_relative 'compiler/control.rb'
|
4
4
|
require_relative 'compiler/define.rb'
|
@@ -8,19 +8,23 @@ require_relative 'compiler/filter.rb'
|
|
8
8
|
require_relative 'compiler/node.rb'
|
9
9
|
require_relative 'compiler/root.rb'
|
10
10
|
require_relative 'compiler/text.rb'
|
11
|
+
require_relative 'compiler/yield.rb'
|
11
12
|
|
12
13
|
# @Opulent
|
13
14
|
module Opulent
|
14
15
|
# @Compiler
|
15
16
|
class Compiler
|
16
|
-
Buffer =
|
17
|
+
Buffer = :@_opulent_buffer
|
18
|
+
|
19
|
+
OpulentKey = :_opulent_key
|
20
|
+
OpulentValue = :_opulent_value
|
17
21
|
|
18
22
|
# All node Objects (Array) must follow the next convention in order
|
19
23
|
# to make parsing faster
|
20
24
|
#
|
21
25
|
# [:node_type, :value, :attributes, :children, :indent]
|
22
26
|
#
|
23
|
-
# @param path [String] Current file path needed for
|
27
|
+
# @param path [String] Current file path needed for include nodes
|
24
28
|
#
|
25
29
|
def initialize
|
26
30
|
# Setup convention accessors
|
@@ -30,15 +34,18 @@ module Opulent
|
|
30
34
|
@children = 3
|
31
35
|
@indent = 4
|
32
36
|
|
33
|
-
# Create the HTML Entities encoder/decoder
|
34
|
-
@entities = HTMLEntities.new
|
35
|
-
|
36
37
|
# Get special node types from the settings
|
37
38
|
@multi_node = Settings::MultiNode
|
38
39
|
@inline_node = Settings::InlineNode
|
39
40
|
|
40
|
-
#
|
41
|
-
@
|
41
|
+
# Initialize amble object
|
42
|
+
@template = [[:preamble]]
|
43
|
+
|
44
|
+
# Incrmental counters
|
45
|
+
@current_variable_count = 0
|
46
|
+
@current_attribute = 0
|
47
|
+
@current_extension = 0
|
48
|
+
@current_definition = 0
|
42
49
|
|
43
50
|
# The node stack is needed to keep track of all the visited nodes
|
44
51
|
# from the current branch level
|
@@ -58,7 +65,7 @@ module Opulent
|
|
58
65
|
# @param root_node [Array] Root node containing all document nodes
|
59
66
|
# @param context [Context] Context holding environment variables
|
60
67
|
#
|
61
|
-
def compile(root_node, context)
|
68
|
+
def compile(root_node, context = nil)
|
62
69
|
# Compiler generated code
|
63
70
|
@code = ""
|
64
71
|
@generator = ""
|
@@ -71,15 +78,9 @@ module Opulent
|
|
71
78
|
root child, 0, context
|
72
79
|
end
|
73
80
|
|
74
|
-
|
75
|
-
end
|
81
|
+
@template << [:postamble]
|
76
82
|
|
77
|
-
|
78
|
-
#
|
79
|
-
# @param value [String] String to be escaped
|
80
|
-
#
|
81
|
-
def escape(value)
|
82
|
-
@entities.encode value
|
83
|
+
return templatize
|
83
84
|
end
|
84
85
|
|
85
86
|
# Remove the last newline from the current code buffer
|
@@ -113,7 +114,7 @@ module Opulent
|
|
113
114
|
when :binding
|
114
115
|
data[0] = data[0].to_s.match(/\`(.*)\'/)
|
115
116
|
data[0] = data[0][1] if data[0]
|
116
|
-
"Found an undefined local variable or method \"#{data[0]}\"
|
117
|
+
"Found an undefined local variable or method \"#{data[0]}\"."
|
117
118
|
when :variable_name
|
118
119
|
data[0] = data[0].to_s.match(/\`(.*)\'/)[1]
|
119
120
|
"Found an undefined local variable or method \"#{data[0]}\" in locals."
|
@@ -0,0 +1,260 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
class Compiler
|
4
|
+
# Output an object stream into the template
|
5
|
+
#
|
6
|
+
# @param string [String] Buffer input string
|
7
|
+
#
|
8
|
+
def buffer(string)
|
9
|
+
@template << [:buffer, string]
|
10
|
+
end
|
11
|
+
|
12
|
+
# Output and escape an object stream into the template
|
13
|
+
#
|
14
|
+
# @param string [String] Buffer input string
|
15
|
+
#
|
16
|
+
def buffer_escape(string)
|
17
|
+
@template << [:escape, string]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Output and freeze a String stream into the template
|
21
|
+
#
|
22
|
+
# @param string [String] Buffer input string
|
23
|
+
#
|
24
|
+
def buffer_freeze(string)
|
25
|
+
if @template[-1][0] == :freeze
|
26
|
+
@template[-1][1] += string
|
27
|
+
else
|
28
|
+
@template << [:freeze, string]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Evaluate a stream into the template
|
33
|
+
#
|
34
|
+
# @param string [String] Buffer input string
|
35
|
+
#
|
36
|
+
def buffer_eval(string)
|
37
|
+
@template << [:eval, string]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set a local variable through buffer eval an object stream into the template
|
41
|
+
#
|
42
|
+
# @param name [String] Variable identifier to be set
|
43
|
+
# @param name [String] Variable value to be set
|
44
|
+
#
|
45
|
+
def buffer_set_variable(name, value)
|
46
|
+
local_variable = "_opulent_#{name}_#{@current_variable_count += 1}"
|
47
|
+
buffer_eval "#{local_variable} = #{value}"
|
48
|
+
local_variable
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove last n characters from the most recent template item
|
52
|
+
#
|
53
|
+
# @param type [Symbol] Remove only if last buffer part is of this type
|
54
|
+
# @param n [Fixnum] Number of characters to be removed
|
55
|
+
#
|
56
|
+
def buffer_remove_last_character(type = :freeze, n = 1)
|
57
|
+
@template[-1][1] = @template[-1][1][0..-1-n] if @template[-1][0] == type
|
58
|
+
end
|
59
|
+
|
60
|
+
# Turn call node attributes into a hash string
|
61
|
+
#
|
62
|
+
# @param attributes [Array] Array of node attributes
|
63
|
+
#
|
64
|
+
def buffer_attributes_to_hash(attributes)
|
65
|
+
"{" + attributes.inject([]) do |extend_map, (key, attribute)|
|
66
|
+
extend_map << ("#{key}: " + if key == :class
|
67
|
+
'[' + attribute.map do |exp|
|
68
|
+
exp[@value]
|
69
|
+
end.join(", ") + ']'
|
70
|
+
else
|
71
|
+
attribute[@value]
|
72
|
+
end)
|
73
|
+
end.join(', ') + "}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Go through the node attributes and apply extension where needed
|
77
|
+
#
|
78
|
+
# @param attributes [Array] Array of node attributes, from parser
|
79
|
+
# @param extension [String] Extension identifier
|
80
|
+
#
|
81
|
+
def buffer_attributes(attributes, extension)
|
82
|
+
# Proc for setting class attribute extension, used as DRY closure
|
83
|
+
#
|
84
|
+
buffer_class_attribute_type_check = Proc.new do |variable, escape = true|
|
85
|
+
class_variable = buffer_set_variable :local, variable
|
86
|
+
buffer_eval "if #{class_variable}.is_a? Array"
|
87
|
+
escape ? buffer_escape("#{class_variable}.join ' '") : buffer("#{class_variable}.join ' '")
|
88
|
+
buffer_eval "elsif #{class_variable}.is_a? Hash"
|
89
|
+
escape ? buffer_escape("#{class_variable}.to_a.join ' '") : buffer("#{class_variable}.to_a.join ' '")
|
90
|
+
buffer_eval "elsif [TrueClass, NilClass, FalseClass].include? #{class_variable}.class"
|
91
|
+
buffer_eval "else"
|
92
|
+
escape ? buffer_escape("#{class_variable}") : buffer("#{class_variable}")
|
93
|
+
buffer_eval "end"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Handle class attributes by checking if they're simple, noninterpolated
|
97
|
+
# strings and extend them if needed
|
98
|
+
#
|
99
|
+
buffer_class_attribute = Proc.new do |attribute|
|
100
|
+
if attribute[@value] =~ Tokens[:exp_string]
|
101
|
+
buffer_split_by_interpolation attribute[@value][1..-2]
|
102
|
+
else
|
103
|
+
buffer_class_attribute_type_check[attribute[@value], attribute[@options][:escaped]]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# If we have class attributes, process each one and check if we have an
|
109
|
+
# extension for them
|
110
|
+
if attributes[:class]
|
111
|
+
buffer_freeze " class=\""
|
112
|
+
|
113
|
+
# Process every class attribute
|
114
|
+
attributes[:class].each do |node_class|
|
115
|
+
buffer_class_attribute[node_class]
|
116
|
+
buffer_freeze " "
|
117
|
+
end
|
118
|
+
|
119
|
+
# Remove trailing whitespace from the buffer
|
120
|
+
buffer_remove_last_character
|
121
|
+
|
122
|
+
# Check for extension with :class key
|
123
|
+
if extension
|
124
|
+
buffer_eval "if #{extension}.has_key? :class"
|
125
|
+
buffer_freeze " "
|
126
|
+
buffer_class_attribute_type_check["#{extension}.delete(:class)"]
|
127
|
+
buffer_eval "end"
|
128
|
+
end
|
129
|
+
|
130
|
+
buffer_freeze '"'
|
131
|
+
elsif extension
|
132
|
+
# If we do not have class attributes but we do have an extension, try to
|
133
|
+
# see if the extension contains a class attribute
|
134
|
+
buffer_eval "if #{extension}.has_key? :class"
|
135
|
+
buffer_freeze " class=\""
|
136
|
+
buffer_class_attribute_type_check["#{extension}.delete(:class)"]
|
137
|
+
buffer_freeze '"'
|
138
|
+
buffer_eval "end"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Proc for setting class attribute extension, used as DRY closure
|
142
|
+
#
|
143
|
+
buffer_data_attribute_type_check = Proc.new do |key, variable, escape = true, dynamic = false|
|
144
|
+
# @Array
|
145
|
+
buffer_eval "if #{variable}.is_a? Array"
|
146
|
+
dynamic ? buffer("\" #{key}=\\\"\"") : buffer_freeze(" #{key}=\"")
|
147
|
+
escape ? buffer_escape("#{variable}.join '_'") : buffer("#{variable}.join '_'")
|
148
|
+
buffer_freeze '"'
|
149
|
+
|
150
|
+
# @Hash
|
151
|
+
buffer_eval "elsif #{variable}.is_a? Hash"
|
152
|
+
buffer_eval "#{variable}.each do |#{OpulentKey}, #{OpulentValue}|"
|
153
|
+
dynamic ? buffer("\" #{key}-\"") : buffer_freeze(" #{key}-") # key-hashkey="
|
154
|
+
buffer "\"\#{#{OpulentKey}}\""
|
155
|
+
buffer_freeze "=\""
|
156
|
+
escape ? buffer_escape("_opulent_value") : buffer("_opulent_value") # value
|
157
|
+
buffer_freeze '"'
|
158
|
+
buffer_eval "end"
|
159
|
+
|
160
|
+
# @TrueClass
|
161
|
+
buffer_eval "elsif #{variable}.is_a? TrueClass"
|
162
|
+
dynamic ? buffer("\" #{key}\"") : buffer_freeze(" #{key}")
|
163
|
+
|
164
|
+
# @FalseClass
|
165
|
+
buffer_eval "elsif [NilClass, FalseClass].include? #{variable}.class"
|
166
|
+
|
167
|
+
# @Object
|
168
|
+
buffer_eval "else"
|
169
|
+
dynamic ? buffer("\" #{key}=\\\"\"") : buffer_freeze(" #{key}=\"")
|
170
|
+
escape ? buffer_escape("#{variable}") : buffer("#{variable}")
|
171
|
+
buffer_freeze '"'
|
172
|
+
|
173
|
+
# End
|
174
|
+
buffer_eval "end"
|
175
|
+
end
|
176
|
+
|
177
|
+
# Handle data (normal) attributes by checking if they're simple, noninterpolated
|
178
|
+
# strings and extend them if needed
|
179
|
+
#
|
180
|
+
buffer_data_attribute = Proc.new do |key, attribute|
|
181
|
+
# When we have an extension for our attributes, check current key.
|
182
|
+
# If it exists, check it's type and generate everything dynamically
|
183
|
+
if extension
|
184
|
+
buffer_eval "if #{extension}.has_key? :#{key}"
|
185
|
+
variable = buffer_set_variable :local, "#{extension}.delete(:#{key})"
|
186
|
+
buffer_data_attribute_type_check[key, variable, attribute[@options][:escaped]]
|
187
|
+
buffer_eval "else"
|
188
|
+
end
|
189
|
+
|
190
|
+
# Check if the set attribute is a simple string. If it is, freeze it or
|
191
|
+
# escape it. Otherwise, evaluate and initialize the type check.
|
192
|
+
if attribute[@value] =~ Tokens[:exp_string]
|
193
|
+
buffer_freeze " #{key}=\""
|
194
|
+
buffer_split_by_interpolation attribute[@value][1..-2]
|
195
|
+
buffer_freeze "\""
|
196
|
+
else
|
197
|
+
# Evaluate and type check
|
198
|
+
variable = buffer_set_variable :local, attribute[@value]
|
199
|
+
buffer_data_attribute_type_check[key, variable, attribute[@options][:escaped]]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Extension end
|
203
|
+
if extension
|
204
|
+
buffer_eval "end"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Process the remaining, non-class related attributes
|
209
|
+
attributes.each do |key, attribute|
|
210
|
+
next if key == :class
|
211
|
+
buffer_data_attribute[key, attribute]
|
212
|
+
end
|
213
|
+
|
214
|
+
# Process remaining extension keys if there are any
|
215
|
+
if extension
|
216
|
+
buffer_eval "#{extension}.each do |ext#{OpulentKey}, ext#{OpulentValue}|"
|
217
|
+
buffer_data_attribute_type_check["\#{ext#{OpulentKey}}", "ext#{OpulentValue}", true, true]
|
218
|
+
buffer_eval "end"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Transform buffer array into a reusable template
|
223
|
+
#
|
224
|
+
def templatize
|
225
|
+
separator = DEBUG ? "\n" : "; " # Readablity during development
|
226
|
+
@template.inject("") do |buffer, input|
|
227
|
+
buffer += case input[0]
|
228
|
+
when :preamble
|
229
|
+
"#{Buffer} = []#{separator}"
|
230
|
+
when :buffer
|
231
|
+
"#{Buffer} << (#{input[1]})#{separator}"
|
232
|
+
when :escape
|
233
|
+
"#{Buffer} << (::Opulent::Utils::escape(#{input[1]}))#{separator}"
|
234
|
+
when :freeze
|
235
|
+
"#{Buffer} << (#{input[1].inspect}.freeze)#{separator}"
|
236
|
+
when :eval
|
237
|
+
"#{input[1]}#{separator}"
|
238
|
+
when :postamble
|
239
|
+
"#{Buffer}.join"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Split a string by its interpolation, then check if it really needs to be
|
245
|
+
# escaped or not. Huge performance boost!
|
246
|
+
#
|
247
|
+
# @param string [String] Input string
|
248
|
+
# @param escape [Boolean] Escape string
|
249
|
+
#
|
250
|
+
def buffer_split_by_interpolation(string, escape = true)
|
251
|
+
string.split(Utils::InterpolationPattern).each_with_index do |input, index|
|
252
|
+
if index % 2 == 0
|
253
|
+
escape ? (input =~ Utils::EscapeHTMLPattern ? buffer_escape(input.inspect) : buffer_freeze(input)) : buffer_freeze(input)
|
254
|
+
else
|
255
|
+
escape ? buffer_escape(input) : buffer(input)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|