opulent 0.0.9 → 1.0.2

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