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