opulent 1.4.0 → 1.4.1
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/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
|
|