opulent 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|