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.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.libold/opulent.rb +96 -0
- data/.libold/opulent/context.rb +80 -0
- data/.libold/opulent/engine.rb +88 -0
- data/.libold/opulent/filter.rb +101 -0
- data/.libold/opulent/logger.rb +67 -0
- data/.libold/opulent/nodes.rb +13 -0
- data/.libold/opulent/nodes/block.rb +29 -0
- data/.libold/opulent/nodes/comment.rb +35 -0
- data/.libold/opulent/nodes/control.rb +230 -0
- data/.libold/opulent/nodes/define.rb +42 -0
- data/.libold/opulent/nodes/eval.rb +41 -0
- data/.libold/opulent/nodes/expression.rb +28 -0
- data/.libold/opulent/nodes/filter.rb +70 -0
- data/.libold/opulent/nodes/helper.rb +69 -0
- data/.libold/opulent/nodes/node.rb +101 -0
- data/.libold/opulent/nodes/root.rb +62 -0
- data/.libold/opulent/nodes/text.rb +54 -0
- data/.libold/opulent/nodes/theme.rb +36 -0
- data/.libold/opulent/parser.rb +252 -0
- data/.libold/opulent/parser/block.rb +70 -0
- data/.libold/opulent/parser/comment.rb +32 -0
- data/.libold/opulent/parser/control.rb +83 -0
- data/.libold/opulent/parser/define.rb +39 -0
- data/.libold/opulent/parser/eval.rb +33 -0
- data/.libold/opulent/parser/expression.rb +350 -0
- data/.libold/opulent/parser/filter.rb +41 -0
- data/.libold/opulent/parser/node.rb +232 -0
- data/.libold/opulent/parser/root.rb +96 -0
- data/.libold/opulent/parser/text.rb +114 -0
- data/.libold/opulent/parser/theme.rb +36 -0
- data/.libold/opulent/preprocessor.rb +102 -0
- data/.libold/opulent/runtime.rb +144 -0
- data/.libold/opulent/template.rb +43 -0
- data/.libold/opulent/tokens.rb +276 -0
- data/.libold/opulent/version.rb +5 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +102 -0
- data/Rakefile +6 -0
- data/benchmark/benchmark.rb +46 -0
- data/benchmark/cases/node/node.haml +17 -0
- data/benchmark/cases/node/node.op +14 -0
- data/benchmark/cases/node/node.slim +19 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/docs/syntax.md +3 -0
- data/docs/usage.md +3 -0
- data/lib/opulent.rb +11 -64
- data/lib/opulent/compiler.rb +132 -0
- data/lib/opulent/compiler/block.rb +31 -0
- data/lib/opulent/compiler/comment.rb +22 -0
- data/lib/opulent/compiler/control.rb +152 -0
- data/lib/opulent/compiler/define.rb +88 -0
- data/lib/opulent/compiler/eval.rb +15 -0
- data/lib/opulent/compiler/filter.rb +54 -0
- data/lib/opulent/compiler/node.rb +232 -0
- data/lib/opulent/compiler/root.rb +19 -0
- data/lib/opulent/compiler/text.rb +60 -0
- data/lib/opulent/context.rb +88 -0
- data/lib/opulent/engine.rb +60 -0
- data/lib/opulent/filters.rb +222 -0
- data/lib/opulent/logger.rb +47 -0
- data/lib/opulent/parser.rb +196 -0
- data/lib/opulent/parser/block.rb +56 -0
- data/lib/opulent/parser/comment.rb +27 -0
- data/lib/opulent/parser/control.rb +112 -0
- data/lib/opulent/parser/define.rb +30 -0
- data/lib/opulent/parser/eval.rb +21 -0
- data/lib/opulent/parser/expression.rb +344 -0
- data/lib/opulent/parser/filter.rb +25 -0
- data/lib/opulent/parser/node.rb +246 -0
- data/lib/opulent/parser/root.rb +48 -0
- data/lib/opulent/parser/text.rb +127 -0
- data/lib/opulent/settings.rb +79 -0
- data/lib/opulent/template.rb +67 -0
- data/lib/opulent/tokens.rb +166 -0
- data/lib/opulent/version.rb +4 -0
- data/opulent.gemspec +36 -0
- metadata +160 -7
@@ -0,0 +1,19 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate code for all nodes by calling the method with their type name
|
6
|
+
#
|
7
|
+
# @param current [Array] Current node data with options
|
8
|
+
# @param indent [Fixnum] Indentation size for current node
|
9
|
+
# @param context [Context] Context holding environment variables
|
10
|
+
#
|
11
|
+
def root(current, indent, context)
|
12
|
+
if Keywords.include? current[@type]
|
13
|
+
send :"#{current[@type]}_node", current, indent, context
|
14
|
+
else
|
15
|
+
send current[@type], current, indent, context
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a standard text node
|
6
|
+
#
|
7
|
+
# @param node [Array] Node code generation data
|
8
|
+
# @param indent [Fixnum] Size of the indentation to be added
|
9
|
+
# @param context [Context] Processing environment data
|
10
|
+
#
|
11
|
+
def plain(node, indent, context)
|
12
|
+
indentation = " " * indent
|
13
|
+
|
14
|
+
inline = @inline_node.include? @node_stack.last
|
15
|
+
|
16
|
+
# Evaluate text node if it's marked as such and print nodes in the
|
17
|
+
# current context
|
18
|
+
if node[@value] == :text
|
19
|
+
if node[@options][:evaluate]
|
20
|
+
value = context.evaluate "\"#{node[@options][:value]}\""
|
21
|
+
else
|
22
|
+
value = node[@options][:value]
|
23
|
+
end
|
24
|
+
else
|
25
|
+
value = if node[@options][:value] == 'yield'
|
26
|
+
context.evaluate_yield
|
27
|
+
else
|
28
|
+
context.evaluate(node[@options][:value])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Indent all the lines with the given indentation
|
33
|
+
value = indent_lines value, indentation
|
34
|
+
|
35
|
+
# If the last node was an inline node, we remove the trailing newline
|
36
|
+
# character and we left strip the value
|
37
|
+
#pp @node_stack
|
38
|
+
if @node_stack.last == :text
|
39
|
+
remove_trailing_newline
|
40
|
+
value = " " + value.lstrip
|
41
|
+
elsif inline
|
42
|
+
remove_trailing_newline
|
43
|
+
value.lstrip!
|
44
|
+
end
|
45
|
+
value.rstrip!
|
46
|
+
|
47
|
+
# Escape the value unless explicitly set to false
|
48
|
+
value = node[@options][:escaped] ? escape(value) : value
|
49
|
+
|
50
|
+
# Create the text tag to be added
|
51
|
+
text_tag = "#{value}"
|
52
|
+
text_tag += "\n"
|
53
|
+
|
54
|
+
# Set the current child node as last processed node
|
55
|
+
@node_stack << :text
|
56
|
+
|
57
|
+
@code += text_tag
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Context
|
4
|
+
#
|
5
|
+
# The context class is used to differentiate local, instance and class variables
|
6
|
+
# and to define the current working environment. Each class, method and instance
|
7
|
+
# has its own context
|
8
|
+
#
|
9
|
+
class Context
|
10
|
+
attr_accessor :binding, :name, :parent
|
11
|
+
|
12
|
+
# Create a context from the environment binding, extended with the locals
|
13
|
+
# given as arguments
|
14
|
+
#
|
15
|
+
# @param locals [Hash] Binding extension
|
16
|
+
# @param bind [Binding] Call environment binding
|
17
|
+
#
|
18
|
+
def initialize(locals = {}, &block)
|
19
|
+
@block = block || Proc.new
|
20
|
+
@binding = if @block
|
21
|
+
@block.binding.clone
|
22
|
+
else
|
23
|
+
Binding.new
|
24
|
+
end
|
25
|
+
|
26
|
+
extend_locals locals
|
27
|
+
end
|
28
|
+
|
29
|
+
# Evaluate ruby code in current context
|
30
|
+
#
|
31
|
+
# @param code [String] Code to be evaluated
|
32
|
+
#
|
33
|
+
def evaluate(code)
|
34
|
+
begin
|
35
|
+
eval code, @binding
|
36
|
+
rescue NameError => variable
|
37
|
+
Compiler.error :binding, variable, code
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Call given input block and return the output
|
42
|
+
#
|
43
|
+
def evaluate_yield
|
44
|
+
@block.call
|
45
|
+
end
|
46
|
+
|
47
|
+
# Extend the call context with a Hash, String or other Object
|
48
|
+
#
|
49
|
+
# @param context [Object] Extension object
|
50
|
+
#
|
51
|
+
def extend_locals(locals)
|
52
|
+
# Create new local variables from the input hash
|
53
|
+
locals.each do |key, value|
|
54
|
+
begin
|
55
|
+
@binding.local_variable_set(key.to_sym, value)
|
56
|
+
rescue NameError => variable
|
57
|
+
Compiler.error :variable_name, variable, key
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Extend instance, class and global variables for use in definitions
|
63
|
+
#
|
64
|
+
# @param bind [Binding] Binding to extend current context binding
|
65
|
+
#
|
66
|
+
def extend_nonlocals(bind)
|
67
|
+
bind.eval('instance_variables').each do |var|
|
68
|
+
@binding.eval('self').instance_variable_set var, bind.eval(var.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
bind.eval('self.class.class_variables').each do |var|
|
72
|
+
@binding.eval('self').class_variable_set var, bind.eval(var.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
bind.eval('self.class.constants').each do |var|
|
76
|
+
@binding.eval('self').const_set var, bind.eval(var.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# @Binding
|
83
|
+
class Binding
|
84
|
+
def self.new
|
85
|
+
binding
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# Module method wrapper for creating a new engine instance
|
4
|
+
#
|
5
|
+
def Opulent.new(settings = nil)
|
6
|
+
return Engine.new settings
|
7
|
+
end
|
8
|
+
|
9
|
+
# @Engine
|
10
|
+
class Engine
|
11
|
+
attr_reader :nodes, :preamble, :buffer
|
12
|
+
|
13
|
+
def initialize(settings = nil)
|
14
|
+
# Update render settings
|
15
|
+
Settings.update_settings settings if settings
|
16
|
+
end
|
17
|
+
|
18
|
+
# Analyze the input code and check for matching tokens. In case no match was
|
19
|
+
# found, throw an exception. In special cases, modify the token hash.
|
20
|
+
#
|
21
|
+
# @param file [String] The file that needs to be analyzed
|
22
|
+
# @param locals [Hash] Render call local variables
|
23
|
+
# @param block [Proc] Processing environment data
|
24
|
+
#
|
25
|
+
def render_file(file, locals = {}, &block)
|
26
|
+
# Render the file
|
27
|
+
render File.read(file), locals, &block
|
28
|
+
end
|
29
|
+
|
30
|
+
# Analyze the input code and check for matching tokens. In case no match was
|
31
|
+
# found, throw an exception. In special cases, modify the token hash.
|
32
|
+
#
|
33
|
+
# @param file [String] The file that needs to be analyzed
|
34
|
+
# @param locals [Hash] Render call local variables
|
35
|
+
# @param block [Proc] Processing environment data
|
36
|
+
#
|
37
|
+
def render(code, locals = {}, &block)
|
38
|
+
# Get the nodes tree
|
39
|
+
@nodes = Parser.new.parse code
|
40
|
+
|
41
|
+
# @TODO
|
42
|
+
# Implement precompiled template handling
|
43
|
+
@preamble = @nodes.inspect.inspect
|
44
|
+
|
45
|
+
# Create a new context based on our rendering environment
|
46
|
+
@context = Context.new locals, &block
|
47
|
+
|
48
|
+
# Compile our syntax tree using input context
|
49
|
+
@output = Compiler.new.compile @nodes, @context
|
50
|
+
|
51
|
+
# puts "Nodes\n---\n"
|
52
|
+
# pp @nodes
|
53
|
+
#
|
54
|
+
# puts "\n\nCode\n---\n"
|
55
|
+
# puts @output
|
56
|
+
|
57
|
+
return @output
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Filters
|
4
|
+
module Filters
|
5
|
+
# @Singleton
|
6
|
+
class << self
|
7
|
+
attr_accessor :filters
|
8
|
+
|
9
|
+
# Add a new Opulent filter to the filters knowledgebase
|
10
|
+
#
|
11
|
+
# @param class [Class] Class to be used for filter instance
|
12
|
+
# @param name [Symbol] Identifier in the filters hash
|
13
|
+
# @param options [Hash] Filter engine instance options
|
14
|
+
#
|
15
|
+
def register(klass, name, options)
|
16
|
+
@filters ||= {}
|
17
|
+
@filters[name] = klass.new name, options
|
18
|
+
end
|
19
|
+
|
20
|
+
# Check if the chosen filter name is registed within our knowledgebase
|
21
|
+
#
|
22
|
+
def has_filter?(name)
|
23
|
+
@filters.has_key? name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @Filter
|
28
|
+
class Filter
|
29
|
+
attr_accessor :name, :options, :loaded
|
30
|
+
|
31
|
+
# Set tag and attribute options for filter
|
32
|
+
#
|
33
|
+
def initialize(name, options)
|
34
|
+
@name = name
|
35
|
+
@options = options
|
36
|
+
@loaded = false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Error output in case the filter does not exist
|
40
|
+
#
|
41
|
+
def load_filter
|
42
|
+
unless gem_name.nil? || @loaded
|
43
|
+
# Try to load the library associated to the chosen filter
|
44
|
+
begin
|
45
|
+
require gem_name
|
46
|
+
@loaded = true
|
47
|
+
rescue LoadError => error
|
48
|
+
# Error output with filter name and installation instructions
|
49
|
+
Compiler.error :filter_load, @name, install_error
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Error message to be shown in order to provide installation instructions
|
55
|
+
# for the developer
|
56
|
+
#
|
57
|
+
def install_error
|
58
|
+
"gem install #{gem_name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Process input code using this filter and return the output to the
|
62
|
+
# evaluation method from the Filter Node
|
63
|
+
#
|
64
|
+
def render(code, options = {})
|
65
|
+
raise NoMethodError
|
66
|
+
end
|
67
|
+
|
68
|
+
# RubyGems name for explicit library require
|
69
|
+
#
|
70
|
+
def gem_name
|
71
|
+
raise NoMethodError
|
72
|
+
# "gem_name"
|
73
|
+
end
|
74
|
+
|
75
|
+
# After defining how to render the code,
|
76
|
+
#
|
77
|
+
# Filters.register self, :filter, tag: :tag, attributes: { type: 'text/css' }
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Add the default registered rendering filters for Opulent
|
82
|
+
|
83
|
+
# @CoffeeScript
|
84
|
+
class CoffeeScript < Filter
|
85
|
+
def render(code, options = {})
|
86
|
+
::CoffeeScript.compile code, options
|
87
|
+
end
|
88
|
+
|
89
|
+
def gem_name
|
90
|
+
"coffee-script"
|
91
|
+
end
|
92
|
+
|
93
|
+
Filters.register self, :coffeescript, tag: :script, attributes: { type: 'javascript' }
|
94
|
+
end
|
95
|
+
|
96
|
+
# @JavaScript
|
97
|
+
class JavaScript < Filter
|
98
|
+
def render(code, options = {})
|
99
|
+
code
|
100
|
+
end
|
101
|
+
|
102
|
+
def gem_name
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
Filters.register self, :javascript, tag: :script, attributes: { type: 'javascript' }
|
107
|
+
end
|
108
|
+
|
109
|
+
# @Scss
|
110
|
+
class Scss < Filter
|
111
|
+
def render(code, options = {})
|
112
|
+
options[:style] ||= :expanded
|
113
|
+
|
114
|
+
::Sass.compile code, options
|
115
|
+
end
|
116
|
+
|
117
|
+
def gem_name
|
118
|
+
"sass"
|
119
|
+
end
|
120
|
+
|
121
|
+
Filters.register self, :scss, tag: :style, attributes: { type: 'text/css' }
|
122
|
+
end
|
123
|
+
|
124
|
+
# @Sass
|
125
|
+
class Sass < Filter
|
126
|
+
def render(code, options = {})
|
127
|
+
options[:syntax] = :sass
|
128
|
+
options[:style] ||= :expanded
|
129
|
+
|
130
|
+
::Sass.compile code, options
|
131
|
+
end
|
132
|
+
|
133
|
+
def gem_name
|
134
|
+
"sass"
|
135
|
+
end
|
136
|
+
|
137
|
+
Filters.register self, :sass, tag: :style, attributes: { type: 'text/css' }
|
138
|
+
end
|
139
|
+
|
140
|
+
# @Css
|
141
|
+
class Css < Filter
|
142
|
+
def render(code, options = {})
|
143
|
+
if options[:cdata]
|
144
|
+
"<![CDATA[\n" + code + "]]>"
|
145
|
+
else
|
146
|
+
code
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def gem_name
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
Filters.register self, :css, tag: :style, attributes: { type: 'text/css' }
|
155
|
+
end
|
156
|
+
|
157
|
+
# @CData
|
158
|
+
class CData < Filter
|
159
|
+
def render(code, options = {})
|
160
|
+
"<![CDATA[\n" + code + "]]>"
|
161
|
+
end
|
162
|
+
|
163
|
+
def gem_name
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
|
167
|
+
Filters.register self, :cdata, tag: nil, attributes: {}
|
168
|
+
end
|
169
|
+
|
170
|
+
# @Escaped
|
171
|
+
class Escaped < Filter
|
172
|
+
def render(code, options = {})
|
173
|
+
Compiler.escape code
|
174
|
+
end
|
175
|
+
|
176
|
+
def gem_name
|
177
|
+
nil
|
178
|
+
end
|
179
|
+
|
180
|
+
Filters.register self, :escaped, tag: nil, attributes: {}
|
181
|
+
end
|
182
|
+
|
183
|
+
# @Markdown
|
184
|
+
class Markdown < Filter
|
185
|
+
def render(code, options = {})
|
186
|
+
::Kramdown::Document.new(code, options).to_html
|
187
|
+
end
|
188
|
+
|
189
|
+
def gem_name
|
190
|
+
"kramdown"
|
191
|
+
end
|
192
|
+
|
193
|
+
Filters.register self, :markdown, tag: nil, attributes: {}
|
194
|
+
end
|
195
|
+
|
196
|
+
# @Maruku
|
197
|
+
class Maruku < Filter
|
198
|
+
def render(code, options = {})
|
199
|
+
::Maruku.new(code, options).to_html
|
200
|
+
end
|
201
|
+
|
202
|
+
def gem_name
|
203
|
+
"maruku"
|
204
|
+
end
|
205
|
+
|
206
|
+
Filters.register self, :maruku, tag: nil, attributes: {}
|
207
|
+
end
|
208
|
+
|
209
|
+
# @RedCloth
|
210
|
+
class RedCloth < Filter
|
211
|
+
def render(code, options = {})
|
212
|
+
::RedCloth.new(code, options).to_html
|
213
|
+
end
|
214
|
+
|
215
|
+
def gem_name
|
216
|
+
"RedCloth"
|
217
|
+
end
|
218
|
+
|
219
|
+
Filters.register self, :textile, tag: nil, attributes: {}
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|