opulent 0.0.9 → 1.0.2

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.libold/opulent.rb +96 -0
  4. data/.libold/opulent/context.rb +80 -0
  5. data/.libold/opulent/engine.rb +88 -0
  6. data/.libold/opulent/filter.rb +101 -0
  7. data/.libold/opulent/logger.rb +67 -0
  8. data/.libold/opulent/nodes.rb +13 -0
  9. data/.libold/opulent/nodes/block.rb +29 -0
  10. data/.libold/opulent/nodes/comment.rb +35 -0
  11. data/.libold/opulent/nodes/control.rb +230 -0
  12. data/.libold/opulent/nodes/define.rb +42 -0
  13. data/.libold/opulent/nodes/eval.rb +41 -0
  14. data/.libold/opulent/nodes/expression.rb +28 -0
  15. data/.libold/opulent/nodes/filter.rb +70 -0
  16. data/.libold/opulent/nodes/helper.rb +69 -0
  17. data/.libold/opulent/nodes/node.rb +101 -0
  18. data/.libold/opulent/nodes/root.rb +62 -0
  19. data/.libold/opulent/nodes/text.rb +54 -0
  20. data/.libold/opulent/nodes/theme.rb +36 -0
  21. data/.libold/opulent/parser.rb +252 -0
  22. data/.libold/opulent/parser/block.rb +70 -0
  23. data/.libold/opulent/parser/comment.rb +32 -0
  24. data/.libold/opulent/parser/control.rb +83 -0
  25. data/.libold/opulent/parser/define.rb +39 -0
  26. data/.libold/opulent/parser/eval.rb +33 -0
  27. data/.libold/opulent/parser/expression.rb +350 -0
  28. data/.libold/opulent/parser/filter.rb +41 -0
  29. data/.libold/opulent/parser/node.rb +232 -0
  30. data/.libold/opulent/parser/root.rb +96 -0
  31. data/.libold/opulent/parser/text.rb +114 -0
  32. data/.libold/opulent/parser/theme.rb +36 -0
  33. data/.libold/opulent/preprocessor.rb +102 -0
  34. data/.libold/opulent/runtime.rb +144 -0
  35. data/.libold/opulent/template.rb +43 -0
  36. data/.libold/opulent/tokens.rb +276 -0
  37. data/.libold/opulent/version.rb +5 -0
  38. data/.rspec +2 -0
  39. data/.travis.yml +4 -0
  40. data/Gemfile +2 -0
  41. data/LICENSE +21 -0
  42. data/README.md +102 -0
  43. data/Rakefile +6 -0
  44. data/benchmark/benchmark.rb +46 -0
  45. data/benchmark/cases/node/node.haml +17 -0
  46. data/benchmark/cases/node/node.op +14 -0
  47. data/benchmark/cases/node/node.slim +19 -0
  48. data/bin/console +14 -0
  49. data/bin/setup +7 -0
  50. data/docs/syntax.md +3 -0
  51. data/docs/usage.md +3 -0
  52. data/lib/opulent.rb +11 -64
  53. data/lib/opulent/compiler.rb +132 -0
  54. data/lib/opulent/compiler/block.rb +31 -0
  55. data/lib/opulent/compiler/comment.rb +22 -0
  56. data/lib/opulent/compiler/control.rb +152 -0
  57. data/lib/opulent/compiler/define.rb +88 -0
  58. data/lib/opulent/compiler/eval.rb +15 -0
  59. data/lib/opulent/compiler/filter.rb +54 -0
  60. data/lib/opulent/compiler/node.rb +232 -0
  61. data/lib/opulent/compiler/root.rb +19 -0
  62. data/lib/opulent/compiler/text.rb +60 -0
  63. data/lib/opulent/context.rb +88 -0
  64. data/lib/opulent/engine.rb +60 -0
  65. data/lib/opulent/filters.rb +222 -0
  66. data/lib/opulent/logger.rb +47 -0
  67. data/lib/opulent/parser.rb +196 -0
  68. data/lib/opulent/parser/block.rb +56 -0
  69. data/lib/opulent/parser/comment.rb +27 -0
  70. data/lib/opulent/parser/control.rb +112 -0
  71. data/lib/opulent/parser/define.rb +30 -0
  72. data/lib/opulent/parser/eval.rb +21 -0
  73. data/lib/opulent/parser/expression.rb +344 -0
  74. data/lib/opulent/parser/filter.rb +25 -0
  75. data/lib/opulent/parser/node.rb +246 -0
  76. data/lib/opulent/parser/root.rb +48 -0
  77. data/lib/opulent/parser/text.rb +127 -0
  78. data/lib/opulent/settings.rb +79 -0
  79. data/lib/opulent/template.rb +67 -0
  80. data/lib/opulent/tokens.rb +166 -0
  81. data/lib/opulent/version.rb +4 -0
  82. data/opulent.gemspec +36 -0
  83. metadata +160 -7
@@ -0,0 +1,13 @@
1
+ # Include Opulent nodes which know how to evaluate themselves
2
+ require_relative 'nodes/helper'
3
+ require_relative 'nodes/root'
4
+ require_relative 'nodes/node'
5
+ require_relative 'nodes/define'
6
+ require_relative 'nodes/eval'
7
+ require_relative 'nodes/expression'
8
+ require_relative 'nodes/text'
9
+ require_relative 'nodes/filter'
10
+ require_relative 'nodes/control'
11
+ require_relative 'nodes/theme'
12
+ require_relative 'nodes/block'
13
+ require_relative 'nodes/comment'
@@ -0,0 +1,29 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Yield
6
+ #
7
+ class Yield < Node
8
+ # Node evaluation method which goes through all the child nodes and evaluates
9
+ # them using their own eval method
10
+ #
11
+ def evaluate(context)
12
+ self
13
+ end
14
+ end
15
+
16
+ # @Block
17
+ #
18
+ class Block < Node
19
+ # Node evaluation method which goes through all the child nodes and evaluates
20
+ # them using their own eval method
21
+ #
22
+ def evaluate(context)
23
+ @children.map do |child|
24
+ child.evaluate context
25
+ end.flatten.compact
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Text
6
+ #
7
+ # The text class will output raw or escaped HTML text
8
+ #
9
+ class Comment
10
+ # Allow direct access to literal value and type
11
+ attr_accessor :value, :visible, :parent, :indent, :name
12
+
13
+ # Initialize literal instance variables
14
+ #
15
+ # @param value stores the literal's explicit value
16
+ #
17
+ def initialize(value = nil, parent = nil, indent = 0)
18
+ @value = value
19
+ @parent = parent
20
+ @indent = indent
21
+ @name = :comment
22
+ end
23
+
24
+ # Value evaluation method which returns the processed value of the
25
+ # literal
26
+ #
27
+ def evaluate(context)
28
+ comment = self.dup
29
+ comment.value = context.evaluate "\"#{@value}\""
30
+
31
+ return comment
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,230 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @CnditionalControl
6
+ #
7
+ # Control structure for if-elsif-else and unless
8
+ #
9
+ class CnditionalControl
10
+ attr_accessor :name, :value, :parent, :indent, :children
11
+
12
+ # Ruby code evaluation node
13
+ #
14
+ # @param name [String] name of the html node
15
+ # @param value [String] condition to be met
16
+ # @param parent [Node] parent of the element
17
+ # @param indent [Fixnum] node indentation for restructuring
18
+ # @param children [Array] contents to be interpreted
19
+ #
20
+ def initialize(name = '', value = '', parent = nil, indent = 0, children = [[]])
21
+ @name = name
22
+ @value = [value]
23
+ @parent = parent
24
+ @indent = indent
25
+ @children = children
26
+ end
27
+
28
+ # Add a new node to the nodes array
29
+ #
30
+ def push(node)
31
+ @children[-1] << node
32
+ self
33
+ end
34
+
35
+ # Node evaluation method which goes through all the child nodes and evaluates
36
+ # them using their own eval method
37
+ #
38
+ def evaluate(context)
39
+ index = @value.index do |value|
40
+ value.empty? || context.evaluate(value)
41
+ end
42
+
43
+ if index
44
+ @children[index].map do |child|
45
+ child.evaluate context
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # @IfControl
52
+ #
53
+ # Control structure for if-elsif-else directive
54
+ #
55
+ class CaseControl
56
+ attr_accessor :name, :case, :value, :parent, :indent, :children
57
+
58
+ # Ruby code evaluation node
59
+ #
60
+ # @param name [String] name of the html node
61
+ # @param value [String] condition to be met
62
+ # @param parent [Node] parent of the element
63
+ # @param indent [Fixnum] node indentation for restructuring
64
+ # @param children [Array] contents to be interpreted
65
+ #
66
+ def initialize(name = '', switch_case = '', parent = nil, indent = 0, children = [[]])
67
+ @name = name
68
+ @case = switch_case
69
+ @value = []
70
+ @parent = parent
71
+ @indent = indent
72
+ @children = children
73
+ end
74
+
75
+ # Add a new node to the nodes array
76
+ #
77
+ def push(node)
78
+ @children[-1] << node
79
+ self
80
+ end
81
+
82
+ # Node evaluation method which goes through all the child nodes and evaluates
83
+ # them using their own eval method
84
+ #
85
+ def evaluate(context)
86
+ switch_case = context.evaluate @case
87
+ index = @value.index do |value|
88
+ value.empty? || switch_case == context.evaluate(value)
89
+ end
90
+
91
+ if index
92
+ @children[index].map do |child|
93
+ child.evaluate context
94
+ end.flatten.compact
95
+ end
96
+ end
97
+ end
98
+
99
+ # @LoopControl
100
+ #
101
+ # Control structure for while and until directives
102
+ #
103
+ class LoopControl
104
+ attr_accessor :name, :value, :parent, :indent, :children
105
+
106
+ # Ruby code evaluation node
107
+ #
108
+ # @param name [String] name of the html node
109
+ # @param value [String] condition to be met
110
+ # @param parent [Node] parent of the element
111
+ # @param indent [Fixnum] node indentation for restructuring
112
+ # @param children [Array] contents to be interpreted
113
+ #
114
+ def initialize(name = '', value = '', parent = nil, indent = 0, children = [])
115
+ @name = name
116
+ @value = value
117
+ @parent = parent
118
+ @indent = indent
119
+ @children = children
120
+ end
121
+
122
+ # Add a new node to the nodes array
123
+ #
124
+ def push(node)
125
+ @children << node
126
+ self
127
+ end
128
+
129
+ # Node evaluation method which goes through all the child nodes and evaluates
130
+ # them using their own eval method
131
+ #
132
+ def evaluate(context)
133
+ result = []
134
+
135
+ evaluate_children = Proc.new do |context|
136
+ result << @children.map do |child|
137
+ child.evaluate context
138
+ end.flatten
139
+ end
140
+
141
+ case @name
142
+ when :while
143
+ while context.evaluate @value
144
+ evaluate_children[context]
145
+ end
146
+ when :until
147
+ until context.evaluate @value
148
+ evaluate_children[context]
149
+ end
150
+ end
151
+
152
+ return result.flatten.compact
153
+ end
154
+ end
155
+
156
+ # @EachControl
157
+ #
158
+ # Control structure for while and until directives
159
+ #
160
+ class EachControl < LoopControl
161
+ # Node evaluation method which goes through all the child nodes and evaluates
162
+ # them using their own eval method
163
+ #
164
+ def evaluate(context)
165
+ result = []
166
+
167
+ # Process named variables for each structure
168
+ variables = @value[0].clone
169
+
170
+ # The each structure accept missing arguments as well, therefore we need to
171
+ # substitute them with our defaults
172
+ #
173
+ # each in iterable
174
+ # each value in iterable
175
+ # each key, value in iterable
176
+
177
+ # Value argument name provided only
178
+ if variables.length == 1
179
+ variables.unshift Engine[:each][:default_key]
180
+
181
+ # Missing key and value arguments
182
+ elsif variables.empty?
183
+ variables[0] = Engine[:each][:default_key]
184
+ variables[1] = Engine[:each][:default_value]
185
+ end
186
+
187
+ # Evaluate in current context and add to results
188
+ evaluate_children = Proc.new do |key, value, context|
189
+ # Update the local variables in the each context with the values from the
190
+ # current loop iteration
191
+ locals = {
192
+ variables[0] => key,
193
+ variables[1] => value
194
+ }
195
+ context.extend_context locals
196
+
197
+ # Add the mapped child elements
198
+ result << @children.map do |child|
199
+ child.evaluate context
200
+ end.flatten
201
+ end
202
+
203
+ # Create a new context based on the parent context and progressively update
204
+ # variables in the new context
205
+ each_context = Context.new({}, context.binding.clone)
206
+
207
+ # Evaluate the iterable object
208
+ enumerable = each_context.evaluate(@value[1])
209
+
210
+ # Check if input can be iterated
211
+ Runtime.error :enumerable, @value[1] unless enumerable.respond_to? :each
212
+
213
+ # Selectively iterate through the input and add the result using the previously
214
+ # defined proc object
215
+ case enumerable
216
+ when Hash
217
+ enumerable.each do |key, value|
218
+ evaluate_children[key, value, context]
219
+ end
220
+ else
221
+ enumerable.each_with_index do |value, key|
222
+ evaluate_children[key, value, context]
223
+ end
224
+ end
225
+
226
+ return result.flatten.compact
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,42 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Define
6
+ #
7
+ # Define a custom HTML element
8
+ #
9
+ class Define < Node
10
+ # Node evaluation method which goes through all the child nodes and
11
+ # evaluates them using their own eval method
12
+ #
13
+ def evaluate(context, blocks = {})
14
+ yields = @yields.clone
15
+
16
+ @children.map do |child|
17
+ evaluated_child = child.evaluate context
18
+
19
+ # Check to see if the child element being mapped is one of the yield
20
+ # node parent pointers
21
+ if yields.include? child
22
+ yields.delete child
23
+
24
+ # We need to replace the yield nodes with the matching named block
25
+ # in order to map the yield node correctly
26
+ evaluated_child.children.map! do |subchild|
27
+ if subchild.is_a?(Yield) && blocks[subchild.name]
28
+ blocks[subchild.name]
29
+ else
30
+ subchild
31
+ end
32
+ end
33
+ evaluated_child.children.compact!
34
+ evaluated_child.children.flatten!
35
+ end
36
+
37
+ evaluated_child
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Evaluate
6
+ #
7
+ # The eval node evaluates ruby code in the given context without printing
8
+ # any output in the page
9
+ #
10
+ class Evaluate
11
+ attr_accessor :value, :parent, :indent, :children, :name
12
+
13
+ # Ruby code evaluation node
14
+ #
15
+ # @param name [String] name of the html node
16
+ # @param parent [Node] parent of the element
17
+ # @param indent [Fixnum] node indentation for restructuring
18
+ # @param children [Array] contents to be interpreted
19
+ #
20
+ def initialize(value = '', parent = nil, indent = 0, children = [])
21
+ @name = :eval
22
+ @value = value
23
+ @parent = parent
24
+ @indent = indent
25
+ @children = children
26
+ end
27
+
28
+ # Add a new node to the nodes array
29
+ #
30
+ def push(node)
31
+ @children << node
32
+ self
33
+ end
34
+
35
+ def evaluate(context)
36
+ context.evaluate @value
37
+ return nil
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Expression
6
+ #
7
+ # Literals are static values that have a Ruby representation, eg.: a string, a number,
8
+ # true, false, nil, etc.
9
+ #
10
+ class Expression
11
+ attr_accessor :value, :escaped
12
+
13
+ def initialize(value = '')
14
+ @value = value
15
+ @escaped = true
16
+ end
17
+
18
+ def to_s
19
+ @value
20
+ end
21
+
22
+ def evaluate(context)
23
+ evaluated = context.evaluate @value
24
+ @escaped ? Runtime.escape(evaluated) : evaluated
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,70 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Nodes
4
+ module Nodes
5
+ # @Filter
6
+ #
7
+ # Node class used to run a value interpretation
8
+ #
9
+ class Filter
10
+ # Allow direct access to node variables
11
+ attr_accessor :name, :attributes, :value, :parent, :indent
12
+
13
+ # Initialize node instance variables
14
+ #
15
+ # @param name [String] name of the html node
16
+ # @param indentation [Fixnum] node indentation for restructuring
17
+ # @param attributes [Hash] stores key="value" attributes
18
+ # @param value [String] Contents to be interpreted
19
+ #
20
+ def initialize(name = '', attributes = {}, parent = nil, indent = 0, value = '')
21
+ @name = name
22
+ @parent = parent
23
+ @indent = indent
24
+ @attributes = attributes
25
+ @value = value
26
+ end
27
+
28
+ # Update attributes with values from current evaluation context
29
+ #
30
+ def get_attributes(context)
31
+ Hash[@attributes.map{ |key, val|
32
+ unless val.nil?
33
+ value = val.evaluate(context)
34
+ value.flatten! if value.is_a?(Array)
35
+ end
36
+ [key, value]
37
+ }]
38
+ end
39
+
40
+ # Node evaluation method which goes through all the child nodes and evaluates
41
+ # them using their own eval method
42
+ #
43
+ def evaluate(context)
44
+ # Set attributes for current context
45
+ attributes = Runtime.attributes @attributes, nil, context
46
+
47
+ # Check if filter is registered
48
+ Runtime.error :filter_registered, name unless Engine.filter? name
49
+
50
+ # Load the required filter
51
+ Engine.filters[name].load_filter
52
+
53
+ # Render output using the chosen engine
54
+ output = Engine.filters[name].render @value
55
+
56
+ # Main output node which contains filter rendered value
57
+ text_node = Text.new output, false, @parent, @indent
58
+
59
+ # If we have a provided filter tag, wrap the text node in the wrapper
60
+ # node tag and further indent
61
+ if (wrapper_tag = Engine.filters[name].options[:tag])
62
+ text_node.indent = @indent + Engine[:indent]
63
+ return Node.new wrapper_tag, Engine.filters[name].options[:attributes], @parent, @indent, [text_node]
64
+ else
65
+ return text_node
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end