opulent 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/benchmark/benchmark.rb +18 -7
- data/benchmark/cases/node/node.haml +6 -16
- data/benchmark/cases/node/node.op +6 -13
- data/benchmark/cases/node/node.slim +6 -18
- data/docs/comments.md +5 -0
- data/docs/filters.md +31 -0
- data/docs/includes.md +0 -0
- data/docs/nodes.md +88 -0
- data/docs/reference.md +7 -6
- data/lib/opulent.rb +2 -1
- data/lib/opulent/compiler.rb +19 -18
- data/lib/opulent/compiler/buffer.rb +260 -0
- data/lib/opulent/compiler/comment.rb +4 -9
- data/lib/opulent/compiler/control.rb +65 -67
- data/lib/opulent/compiler/define.rb +91 -63
- data/lib/opulent/compiler/doctype.rb +1 -5
- data/lib/opulent/compiler/eval.rb +1 -1
- data/lib/opulent/compiler/node.rb +10 -194
- data/lib/opulent/compiler/root.rb +1 -1
- data/lib/opulent/compiler/text.rb +3 -40
- data/lib/opulent/compiler/yield.rb +15 -0
- data/lib/opulent/context.rb +2 -2
- data/lib/opulent/engine.rb +56 -57
- data/lib/opulent/parser.rb +10 -10
- data/lib/opulent/parser/control.rb +2 -3
- data/lib/opulent/parser/expression.rb +1 -1
- data/lib/opulent/parser/{require.rb → include.rb} +17 -15
- data/lib/opulent/parser/node.rb +22 -17
- data/lib/opulent/parser/root.rb +3 -4
- data/lib/opulent/parser/text.rb +3 -7
- data/lib/opulent/parser/yield.rb +23 -0
- data/lib/opulent/settings.rb +5 -4
- data/lib/opulent/template.rb +12 -30
- data/lib/opulent/tokens.rb +5 -9
- data/lib/opulent/utils.rb +41 -0
- data/lib/opulent/version.rb +1 -1
- metadata +9 -5
- data/lib/opulent/compiler/block.rb +0 -31
- data/lib/opulent/parser/block.rb +0 -56
@@ -9,52 +9,15 @@ module Opulent
|
|
9
9
|
# @param context [Context] Processing environment data
|
10
10
|
#
|
11
11
|
def plain(node, indent, context)
|
12
|
-
|
13
|
-
|
14
|
-
inline = @inline_node.include? @node_stack.last
|
12
|
+
value = node[@options][:value]
|
15
13
|
|
16
14
|
# Evaluate text node if it's marked as such and print nodes in the
|
17
15
|
# current context
|
18
16
|
if node[@value] == :text
|
19
|
-
|
20
|
-
value = context.evaluate "\"#{node[@options][:value]}\""
|
21
|
-
else
|
22
|
-
value = node[@options][:value]
|
23
|
-
end
|
17
|
+
buffer_split_by_interpolation value, node[@options][:escaped]
|
24
18
|
else
|
25
|
-
|
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!
|
19
|
+
node[@options][:escaped] ? buffer_escape(value) : buffer(value)
|
44
20
|
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
21
|
end
|
59
22
|
end
|
60
23
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Compiler
|
4
|
+
class Compiler
|
5
|
+
# Generate the code for a while control structure
|
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 yield_node(node, indent, context)
|
12
|
+
buffer_eval "yield if block_given?"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/opulent/context.rb
CHANGED
@@ -33,9 +33,9 @@ module Opulent
|
|
33
33
|
#
|
34
34
|
# @param code [String] Code to be evaluated
|
35
35
|
#
|
36
|
-
def evaluate(code)
|
36
|
+
def evaluate(code, &block)
|
37
37
|
begin
|
38
|
-
eval code, @binding
|
38
|
+
eval code, @binding, &block
|
39
39
|
rescue NameError => variable
|
40
40
|
Compiler.error :binding, variable, code
|
41
41
|
end
|
data/lib/opulent/engine.rb
CHANGED
@@ -6,92 +6,91 @@ module Opulent
|
|
6
6
|
|
7
7
|
# Module method wrapper for creating a new engine instance
|
8
8
|
#
|
9
|
-
def Opulent.new(settings = {})
|
10
|
-
return Engine.new settings
|
9
|
+
def Opulent.new(input, settings = {})
|
10
|
+
return Engine.new input, settings
|
11
11
|
end
|
12
12
|
|
13
13
|
# @Engine
|
14
14
|
class Engine
|
15
|
-
attr_reader :nodes, :
|
15
|
+
attr_reader :nodes, :parser, :def, :file, :template, :buffer
|
16
16
|
|
17
17
|
# Update render settings
|
18
18
|
#
|
19
19
|
# @param settings [Hash] Opulent settings override
|
20
|
-
# @param
|
20
|
+
# @param def [Hash] def from previously parsed files
|
21
21
|
# @param overwrite [Boolean] Write changes directly to the parent binding
|
22
22
|
#
|
23
|
-
def initialize(settings = {})
|
24
|
-
|
23
|
+
def initialize(input, settings = {})
|
24
|
+
# Set def from other Opulent instances
|
25
|
+
@def = settings.delete(:def) || {}
|
25
26
|
|
27
|
+
# Update default settings with user settings
|
26
28
|
Settings.update_settings settings unless settings.empty?
|
27
|
-
end
|
28
|
-
|
29
|
-
# Avoid code duplication when layouting is set. When we have a layout, look
|
30
|
-
# in layouts/application by default.
|
31
|
-
#
|
32
|
-
# @param file [String] The file that needs to be analyzed
|
33
|
-
# @param locals [Hash] Render call local variables
|
34
|
-
# @param block [Proc] Processing environment data
|
35
|
-
#
|
36
|
-
def render(input, locals = {}, &block)
|
37
|
-
# If a layout is set, get the specific layout, otherwise, set the default
|
38
|
-
# one. If a layout is set to false, the page will be render as it is.
|
39
|
-
if Settings[:layouts]
|
40
|
-
layout = locals.has_key?(:layout) ? locals.delete(:layout) : Settings[:default_layout]
|
41
|
-
|
42
|
-
# Process with the built in layout system
|
43
|
-
process layout, locals, block do
|
44
|
-
process input, locals, block, &block
|
45
|
-
end
|
46
|
-
else
|
47
|
-
# We pass the same block as content block, in case we're using a
|
48
|
-
# different yielding system from within a web framework using Tilt
|
49
|
-
process input, locals, block, &block
|
50
|
-
end
|
51
|
-
end
|
52
29
|
|
53
|
-
# Analyze the input code and check for matching tokens. In case no match was
|
54
|
-
# found, throw an exception. In special cases, modify the token hash.
|
55
|
-
#
|
56
|
-
# @param file [String] The file that needs to be analyzed
|
57
|
-
# @param locals [Hash] Render call local variables
|
58
|
-
# @param block [Proc] Processing environment data
|
59
|
-
#
|
60
|
-
def process(input, locals, block, &content)
|
61
30
|
# Read input parameter based on opening mode. If we have a file mode, we
|
62
31
|
# get its path and read the code. We need to reset the mode in case the next
|
63
32
|
# render call is on code, not on a file.
|
64
33
|
@code = case input
|
65
34
|
when Symbol
|
66
|
-
@file = File.expand_path
|
67
|
-
File.read @file
|
35
|
+
@file = File.expand_path get_eval_file input; File.read @file
|
68
36
|
else
|
69
|
-
@file = File.expand_path __FILE__
|
70
|
-
input
|
37
|
+
@file = File.expand_path __FILE__; input
|
71
38
|
end
|
72
39
|
|
73
40
|
# Get the nodes tree
|
74
|
-
@nodes, @
|
41
|
+
@nodes, @def = Parser.new(@file, @def).parse @code
|
75
42
|
|
76
|
-
#
|
77
|
-
|
78
|
-
|
43
|
+
# Compile our syntax tree using input context
|
44
|
+
@template = Compiler.new.compile @nodes
|
45
|
+
end
|
79
46
|
|
80
|
-
|
81
|
-
|
47
|
+
# Avoid code duplication when layouting is set. When we have a layout, look
|
48
|
+
# in layouts/application by default.
|
49
|
+
#
|
50
|
+
# @param scope [Object] Template evaluation context
|
51
|
+
# @param locals [Hash] Render call local variables
|
52
|
+
# @param block [Proc] Processing environment data
|
53
|
+
#
|
54
|
+
def render(scope = Object.new, locals = {}, &block)
|
55
|
+
# Get opulent buffer value
|
56
|
+
initial_buffer = scope.instance_variable_defined?(:@_opulent_buffer) ? scope.instance_variable_get(:@_opulent_buffer) : []
|
82
57
|
|
83
|
-
#
|
84
|
-
|
58
|
+
# If a layout is set, get the specific layout, otherwise, set the default
|
59
|
+
# one. If a layout is set to false, the page will be render as it is.
|
60
|
+
if scope.is_a? binding.class
|
61
|
+
scope_object = eval "self", scope
|
62
|
+
scope = scope_object.instance_eval{ binding } if block_given?
|
63
|
+
else
|
64
|
+
scope_object = scope
|
65
|
+
scope = scope_object.instance_eval{ binding }
|
66
|
+
end
|
85
67
|
|
86
|
-
|
87
|
-
|
88
|
-
|
68
|
+
# Set input local variables in current scope
|
69
|
+
locals.each do |key, value|
|
70
|
+
scope.local_variable_set key, value
|
71
|
+
end
|
89
72
|
|
90
|
-
|
91
|
-
#
|
73
|
+
begin
|
74
|
+
# Evaluate the template in the given scope (context)
|
75
|
+
eval @template, scope
|
76
|
+
rescue ::SyntaxError => e
|
77
|
+
raise SyntaxError, e.message
|
78
|
+
ensure
|
79
|
+
# Get rid of the current buffer
|
80
|
+
scope_object.instance_variable_set :@_opulent_buffer, initial_buffer
|
92
81
|
end
|
82
|
+
end
|
93
83
|
|
94
|
-
|
84
|
+
private
|
85
|
+
# Add .op extension to input file if it isn't already set.
|
86
|
+
#
|
87
|
+
# @param input [Symbol] Input file
|
88
|
+
#
|
89
|
+
def get_eval_file(input)
|
90
|
+
input = input.to_s
|
91
|
+
input += Settings::FileExtension unless File.extname(input) == Settings::FileExtension
|
92
|
+
return input
|
95
93
|
end
|
94
|
+
|
96
95
|
end
|
97
96
|
end
|
data/lib/opulent/parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require_relative 'parser/block.rb'
|
2
1
|
require_relative 'parser/comment.rb'
|
3
2
|
require_relative 'parser/control.rb'
|
4
3
|
require_relative 'parser/define.rb'
|
@@ -7,9 +6,10 @@ require_relative 'parser/eval.rb'
|
|
7
6
|
require_relative 'parser/expression.rb'
|
8
7
|
require_relative 'parser/filter.rb'
|
9
8
|
require_relative 'parser/node.rb'
|
10
|
-
require_relative 'parser/
|
9
|
+
require_relative 'parser/include.rb'
|
11
10
|
require_relative 'parser/root.rb'
|
12
11
|
require_relative 'parser/text.rb'
|
12
|
+
require_relative 'parser/yield.rb'
|
13
13
|
|
14
14
|
# @Opulent
|
15
15
|
module Opulent
|
@@ -31,8 +31,8 @@ module Opulent
|
|
31
31
|
@indent = 4
|
32
32
|
|
33
33
|
# Set current compiled file as the first in the file stack together with
|
34
|
-
# its base indentation. The stack is used to allow
|
35
|
-
# be used with the last parent path found
|
34
|
+
# its base indentation. The stack is used to allow include directives to
|
35
|
+
# be used with the last parent path found
|
36
36
|
@file = [[file, -1]]
|
37
37
|
|
38
38
|
# Initialize definitions for the parser
|
@@ -206,12 +206,12 @@ module Opulent
|
|
206
206
|
when :self_enclosing_children
|
207
207
|
"Unexpected child elements found for self enclosing node on line #{data[0]+1} of input at:\n\n" +
|
208
208
|
"#{@code[data[0]]}#{Logger.red @code[data[0] + 1]}"
|
209
|
-
when :
|
210
|
-
"The
|
211
|
-
when :
|
212
|
-
"The
|
213
|
-
when :
|
214
|
-
"
|
209
|
+
when :include
|
210
|
+
"The included file #{data[0]} does not exist or an incorrect path has been specified."
|
211
|
+
when :include_dir
|
212
|
+
"The included file path #{data[0]} is a directory."
|
213
|
+
when :include_end
|
214
|
+
"Missing argument for include on line #{@i+1} of input at:\n\n" +
|
215
215
|
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
216
216
|
else
|
217
217
|
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
@@ -17,9 +17,8 @@ module Opulent
|
|
17
17
|
# Check if arguments provided correctly
|
18
18
|
error :each_arguments unless condition.match Tokens[:each_pattern]
|
19
19
|
|
20
|
-
|
21
|
-
condition
|
22
|
-
condition[0] = condition[0].split(',').map(&:strip).map(&:to_sym)
|
20
|
+
condition = [$1.split(' '), $2.split(/,(.+)$/).map(&:strip).map(&:to_sym)]
|
21
|
+
condition[0].unshift '{}' if condition[0].length == 1 # Array loop as default
|
23
22
|
end
|
24
23
|
|
25
24
|
# Else and default structures are not allowed to have any condition
|
@@ -13,42 +13,44 @@ module Opulent
|
|
13
13
|
#
|
14
14
|
# @param nodes [Array] Parent node to which we append to
|
15
15
|
#
|
16
|
-
def
|
17
|
-
if(match = accept :
|
16
|
+
def include_file(parent, indent)
|
17
|
+
if(match = accept :include)
|
18
18
|
|
19
19
|
# Process data
|
20
|
-
name = accept :
|
20
|
+
name = accept :line_feed || ""
|
21
|
+
name.strip!
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
|
24
|
+
# Check if there is any string after the include input
|
25
|
+
if name.empty?
|
26
|
+
error :include_end
|
25
27
|
end
|
26
28
|
|
27
29
|
# Get the complete file path based on the current file being compiled
|
28
|
-
|
30
|
+
include_path = File.expand_path name, File.dirname(@file[-1][0])
|
29
31
|
|
30
32
|
# Try to see if it has any existing extension, otherwise add .op
|
31
|
-
|
33
|
+
include_path += Settings::FileExtension if File.extname(name).empty?
|
32
34
|
|
33
35
|
# Throw an error if the file doesn't exist
|
34
|
-
error :
|
36
|
+
error :include, name unless Dir[include_path].any?
|
35
37
|
|
36
|
-
#
|
37
|
-
Dir[
|
38
|
+
# include entire directory tree
|
39
|
+
Dir[include_path].each do |file|
|
38
40
|
# Skip current file when including from same directory
|
39
41
|
next if file == @file[-1][0]
|
40
42
|
|
41
|
-
@file << [
|
43
|
+
@file << [include_path, indent]
|
42
44
|
|
43
45
|
# Throw an error if the file doesn't exist
|
44
|
-
error :
|
46
|
+
error :include_dir, file if File.directory? file
|
45
47
|
|
46
48
|
# Throw an error if the file doesn't exist
|
47
|
-
error :
|
49
|
+
error :include, file unless File.file? file
|
48
50
|
|
49
51
|
# Indent all lines and prepare them for the parser
|
50
52
|
lines = indent_lines File.read(file), " " * indent
|
51
|
-
|
53
|
+
lines << " "
|
52
54
|
# Indent all the output lines with the current indentation
|
53
55
|
@code.insert @i + 1, *lines.lines
|
54
56
|
end
|
data/lib/opulent/parser/node.rb
CHANGED
@@ -74,9 +74,9 @@ module Opulent
|
|
74
74
|
# Add the current node to the root
|
75
75
|
root(current_node, indent)
|
76
76
|
|
77
|
-
if current_node[@options][:self_enclosing] && current_node[@children].any?
|
78
|
-
|
79
|
-
end
|
77
|
+
# if current_node[@options][:self_enclosing] && current_node[@children].any?
|
78
|
+
# error :self_enclosing_children, line
|
79
|
+
# end
|
80
80
|
|
81
81
|
# Create a clone of the definition model. Cloning the options is also
|
82
82
|
# necessary because it's a shallow copy
|
@@ -99,16 +99,28 @@ module Opulent
|
|
99
99
|
model[@options] = {}.merge model[@options]
|
100
100
|
model[@options][:call] = call_context
|
101
101
|
|
102
|
-
# Recursively map each child
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
# Recursively map each child nodes to their definitions
|
103
|
+
# for the initial call node children and for the model
|
104
|
+
# children
|
105
|
+
process_definition_child model[@options][:call]
|
106
|
+
process_definition_child model
|
107
|
+
|
108
|
+
return model
|
109
|
+
end
|
110
|
+
|
111
|
+
def process_definition_child(node)
|
112
|
+
node[@children].map! do |child|
|
113
|
+
if child[@type] == :node
|
114
|
+
if @definitions.keys.include? child[@value]
|
115
|
+
process_definition child[@value], child
|
116
|
+
else
|
117
|
+
process_definition_child child if child[@children]
|
118
|
+
child
|
119
|
+
end
|
106
120
|
else
|
107
121
|
child
|
108
122
|
end
|
109
123
|
end
|
110
|
-
|
111
|
-
return model
|
112
124
|
end
|
113
125
|
|
114
126
|
# Helper method to create an array of values when an attribute is set
|
@@ -119,13 +131,6 @@ module Opulent
|
|
119
131
|
# @param value [String] Attribute value
|
120
132
|
#
|
121
133
|
def add_attribute(atts, key, value)
|
122
|
-
# Check whether the attribute value needs to be evaluated or not
|
123
|
-
value[@options][:evaluate] = if value[@value] =~ Settings::EvaluationCheck
|
124
|
-
value[@value] =~ Settings::InterpolationCheck ? true : false
|
125
|
-
else
|
126
|
-
true
|
127
|
-
end
|
128
|
-
|
129
134
|
# Check for unique key and arrays of attributes
|
130
135
|
if key == :class
|
131
136
|
# If the key is already associated to an array, add the value to the
|
@@ -229,7 +234,7 @@ module Opulent
|
|
229
234
|
error :assignments_colon
|
230
235
|
end
|
231
236
|
else
|
232
|
-
parent[argument] = [:expression, "nil", {
|
237
|
+
parent[argument] = [:expression, "nil", {escaped: false}] unless parent[argument]
|
233
238
|
end
|
234
239
|
|
235
240
|
# If our attributes are wrapped, we allow method calls without
|
data/lib/opulent/parser/root.rb
CHANGED
@@ -21,12 +21,12 @@ module Opulent
|
|
21
21
|
indent = accept(:indent).size
|
22
22
|
|
23
23
|
# Stop using the current parent as root if it does not match the
|
24
|
-
# minimum indentation
|
24
|
+
# minimum indentation includements
|
25
25
|
unless min_indent < indent
|
26
26
|
@i -= 1; break
|
27
27
|
end
|
28
28
|
|
29
|
-
# If last
|
29
|
+
# If last include path had a greater indentation, pop the last file path
|
30
30
|
@file.pop if @file[-1][1] > indent
|
31
31
|
|
32
32
|
# Try the main Opulent node types and process each one of them using
|
@@ -39,8 +39,7 @@ module Opulent
|
|
39
39
|
evaluate(parent, indent) ||
|
40
40
|
filter(parent, indent) ||
|
41
41
|
block_yield(parent, indent) ||
|
42
|
-
|
43
|
-
require_file(parent, indent)||
|
42
|
+
include_file(parent, indent)||
|
44
43
|
html_text(parent, indent) ||
|
45
44
|
doctype(parent, indent)
|
46
45
|
|