opulent 1.4.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/benchmark/benchmark.rb +18 -7
  3. data/benchmark/cases/node/node.haml +6 -16
  4. data/benchmark/cases/node/node.op +6 -13
  5. data/benchmark/cases/node/node.slim +6 -18
  6. data/docs/comments.md +5 -0
  7. data/docs/filters.md +31 -0
  8. data/docs/includes.md +0 -0
  9. data/docs/nodes.md +88 -0
  10. data/docs/reference.md +7 -6
  11. data/lib/opulent.rb +2 -1
  12. data/lib/opulent/compiler.rb +19 -18
  13. data/lib/opulent/compiler/buffer.rb +260 -0
  14. data/lib/opulent/compiler/comment.rb +4 -9
  15. data/lib/opulent/compiler/control.rb +65 -67
  16. data/lib/opulent/compiler/define.rb +91 -63
  17. data/lib/opulent/compiler/doctype.rb +1 -5
  18. data/lib/opulent/compiler/eval.rb +1 -1
  19. data/lib/opulent/compiler/node.rb +10 -194
  20. data/lib/opulent/compiler/root.rb +1 -1
  21. data/lib/opulent/compiler/text.rb +3 -40
  22. data/lib/opulent/compiler/yield.rb +15 -0
  23. data/lib/opulent/context.rb +2 -2
  24. data/lib/opulent/engine.rb +56 -57
  25. data/lib/opulent/parser.rb +10 -10
  26. data/lib/opulent/parser/control.rb +2 -3
  27. data/lib/opulent/parser/expression.rb +1 -1
  28. data/lib/opulent/parser/{require.rb → include.rb} +17 -15
  29. data/lib/opulent/parser/node.rb +22 -17
  30. data/lib/opulent/parser/root.rb +3 -4
  31. data/lib/opulent/parser/text.rb +3 -7
  32. data/lib/opulent/parser/yield.rb +23 -0
  33. data/lib/opulent/settings.rb +5 -4
  34. data/lib/opulent/template.rb +12 -30
  35. data/lib/opulent/tokens.rb +5 -9
  36. data/lib/opulent/utils.rb +41 -0
  37. data/lib/opulent/version.rb +1 -1
  38. metadata +9 -5
  39. data/lib/opulent/compiler/block.rb +0 -31
  40. data/lib/opulent/parser/block.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44c953a9be6c659e56a0c33bc41637d7e1e0730d
4
- data.tar.gz: 260dd8c631ab34debe0748cc53a27024988246b6
3
+ metadata.gz: adcb317d2774dc8ee749c9a36ac819b409812ad4
4
+ data.tar.gz: 50c443fb0d1df18f2455aff4f52c0ba0f7187cea
5
5
  SHA512:
6
- metadata.gz: 59e7c5c79cca92558ca68f2ab5be229bdd2815e4cb5a488465d3e0d391e548626905e1398327ebdc52746fb70cf688705ef7d6eaf2b696b40f2e717b3f795140
7
- data.tar.gz: 5d0a8ad12ca13ba645417a1f73b0bfbb46f96b57db9d509976c777a5761feae6dcbfec48fee944c79f8063813116ca5d9d5e7ce08d59a72f912ab29a54dc8ccd
6
+ metadata.gz: e2ba4bdb98dc6754ade2083b1a812c8577398013ffb7f77265ac09c10a95c6da76fdbc3bb2b1f5f710b514d9c7854379f5aa6809ddea5ebe300ca91b5f0c85b1
7
+ data.tar.gz: f581c87ad7188a2de2821a5705e3e94481720eaf7d8a6865ddcef8e721b66e6ec92fc52e33792f88635e05cb65d0e11ac9ec6eae5e7ef15fa03e1929ec89d882
@@ -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
- scope = Object.new
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(scope, locals) do end
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(scope, locals) do end
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(scope, locals) do end
53
+ slim.render(Object.new, locals){}
43
54
  end
44
55
  end
45
56
  end
@@ -1,17 +1,7 @@
1
- %html
1
+ - a = "hello"
2
+ - kls = ['a', 'b', 'c']
3
+
4
+ %html hello=a
2
5
  %head
3
- %title Hello world
4
- %meta(name="description" content="Node benchmark")
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,14 +1,7 @@
1
- html
1
+ - a = "hello"
2
+ - kls = ['a', 'b', 'c']
3
+
4
+ html hello=a
2
5
  head
3
- title
4
- meta
5
- body
6
- nav
7
- ul
8
- li
9
- li
10
- li
11
- content
12
- container
13
- row
14
- col-md-12
6
+ body class=kls
7
+ p Hello world!
@@ -1,19 +1,7 @@
1
- html
1
+ - a = "hello"
2
+ - kls = ['a', 'b', 'c']
3
+
4
+ html hello=a
2
5
  head
3
- - a = 3
4
- title =a
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 &lt;escaped&gt; inline text</p>
66
+ <p>This is <unescaped> inline text</p>
67
+
68
+ <p>This is &lt;escaped&gt; 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. [Attributes](attributes.md)
7
- 2. [Control Structures](control-structures.md)
8
- 3. [Expressions](expressions.md)
9
- 4. [Comments](comments.md)
10
- 5. [Doctype](doctype.md)
11
- 5. [Nodes](nodes.md)
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 'htmlentities'
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'
@@ -1,4 +1,4 @@
1
- require_relative 'compiler/block.rb'
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 = :_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 require nodes
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
- # Quick accessor for default yield constant
41
- @default_yield = Settings::DefaultYield
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
- return @code
75
- end
81
+ @template << [:postamble]
76
82
 
77
- # Escape a given input value using htmlentities
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]}\" at \"#{data[1]}\"."
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