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.
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