opulent 1.5.5 → 1.6.0

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +13 -0
  3. data/{LICENSE → LICENSE.md} +0 -0
  4. data/bin/opulent +0 -0
  5. data/lib/opulent.rb +10 -10
  6. data/lib/opulent/compiler.rb +15 -9
  7. data/lib/opulent/compiler/buffer.rb +123 -62
  8. data/lib/opulent/compiler/comment.rb +3 -4
  9. data/lib/opulent/compiler/control.rb +20 -26
  10. data/lib/opulent/compiler/define.rb +88 -36
  11. data/lib/opulent/compiler/doctype.rb +20 -22
  12. data/lib/opulent/compiler/eval.rb +23 -2
  13. data/lib/opulent/compiler/filter.rb +4 -5
  14. data/lib/opulent/compiler/node.rb +18 -12
  15. data/lib/opulent/compiler/root.rb +4 -5
  16. data/lib/opulent/compiler/text.rb +7 -2
  17. data/lib/opulent/compiler/yield.rb +2 -3
  18. data/lib/opulent/engine.rb +21 -20
  19. data/lib/opulent/exec.rb +21 -63
  20. data/lib/opulent/logger.rb +230 -3
  21. data/lib/opulent/parser.rb +14 -76
  22. data/lib/opulent/parser/comment.rb +45 -34
  23. data/lib/opulent/parser/control.rb +132 -111
  24. data/lib/opulent/parser/define.rb +15 -12
  25. data/lib/opulent/parser/doctype.rb +15 -15
  26. data/lib/opulent/parser/eval.rb +16 -6
  27. data/lib/opulent/parser/expression.rb +87 -84
  28. data/lib/opulent/parser/filter.rb +31 -25
  29. data/lib/opulent/parser/include.rb +38 -42
  30. data/lib/opulent/parser/node.rb +136 -118
  31. data/lib/opulent/parser/root.rb +24 -18
  32. data/lib/opulent/parser/text.rb +150 -123
  33. data/lib/opulent/parser/yield.rb +23 -23
  34. data/lib/opulent/settings.rb +70 -51
  35. data/lib/opulent/tokens.rb +17 -15
  36. data/lib/opulent/utils.rb +5 -4
  37. data/lib/opulent/version.rb +1 -1
  38. metadata +4 -43
  39. data/.libold/opulent.rb +0 -96
  40. data/.libold/opulent/context.rb +0 -80
  41. data/.libold/opulent/engine.rb +0 -88
  42. data/.libold/opulent/filter.rb +0 -101
  43. data/.libold/opulent/logger.rb +0 -67
  44. data/.libold/opulent/nodes.rb +0 -13
  45. data/.libold/opulent/nodes/block.rb +0 -29
  46. data/.libold/opulent/nodes/comment.rb +0 -35
  47. data/.libold/opulent/nodes/control.rb +0 -230
  48. data/.libold/opulent/nodes/define.rb +0 -42
  49. data/.libold/opulent/nodes/eval.rb +0 -41
  50. data/.libold/opulent/nodes/expression.rb +0 -28
  51. data/.libold/opulent/nodes/filter.rb +0 -70
  52. data/.libold/opulent/nodes/helper.rb +0 -69
  53. data/.libold/opulent/nodes/node.rb +0 -101
  54. data/.libold/opulent/nodes/root.rb +0 -62
  55. data/.libold/opulent/nodes/text.rb +0 -54
  56. data/.libold/opulent/nodes/theme.rb +0 -36
  57. data/.libold/opulent/parser.rb +0 -252
  58. data/.libold/opulent/parser/block.rb +0 -70
  59. data/.libold/opulent/parser/comment.rb +0 -32
  60. data/.libold/opulent/parser/control.rb +0 -83
  61. data/.libold/opulent/parser/define.rb +0 -39
  62. data/.libold/opulent/parser/eval.rb +0 -33
  63. data/.libold/opulent/parser/expression.rb +0 -350
  64. data/.libold/opulent/parser/filter.rb +0 -41
  65. data/.libold/opulent/parser/node.rb +0 -232
  66. data/.libold/opulent/parser/root.rb +0 -96
  67. data/.libold/opulent/parser/text.rb +0 -114
  68. data/.libold/opulent/parser/theme.rb +0 -36
  69. data/.libold/opulent/preprocessor.rb +0 -102
  70. data/.libold/opulent/runtime.rb +0 -144
  71. data/.libold/opulent/template.rb +0 -43
  72. data/.libold/opulent/tokens.rb +0 -276
  73. data/.libold/opulent/version.rb +0 -5
  74. data/.travis.yml +0 -4
  75. data/benchmark/benchmark.rb +0 -57
  76. data/benchmark/cases/node/node.haml +0 -7
  77. data/benchmark/cases/node/node.op +0 -7
  78. data/benchmark/cases/node/node.slim +0 -7
@@ -6,13 +6,12 @@ module Opulent
6
6
  #
7
7
  # @param current [Array] Current node data with options
8
8
  # @param indent [Fixnum] Indentation size for current node
9
- # @param context [Context] Context holding environment variables
10
9
  #
11
- def root(current, indent, context)
10
+ def root(current, indent)
12
11
  if KEYWORDS.include? current[@type]
13
- send :"#{current[@type]}_node", current, indent, context
14
- else
15
- send current[@type], current, indent, context
12
+ send :"#{current[@type]}_node", current, indent
13
+ else
14
+ send current[@type], current, indent
16
15
  end
17
16
  end
18
17
  end
@@ -6,11 +6,13 @@ module Opulent
6
6
  #
7
7
  # @param node [Array] Node code generation data
8
8
  # @param indent [Fixnum] Size of the indentation to be added
9
- # @param context [Context] Processing environment data
10
9
  #
11
- def plain(node, indent, context)
10
+ def plain(node, indent)
12
11
  value = node[@options][:value]
13
12
 
13
+ # Leading whitespace
14
+ buffer_freeze ' ' if node[@options][:leading_whitespace]
15
+
14
16
  # Evaluate text node if it's marked as such and print nodes in the
15
17
  # current context
16
18
  if node[@value] == :text
@@ -18,6 +20,9 @@ module Opulent
18
20
  else
19
21
  node[@options][:escaped] ? buffer_escape(value) : buffer(value)
20
22
  end
23
+
24
+ # Trailing whitespace
25
+ buffer_freeze ' ' if node[@options][:trailing_whitespace]
21
26
  end
22
27
  end
23
28
  end
@@ -6,10 +6,9 @@ module Opulent
6
6
  #
7
7
  # @param node [Array] Node code generation data
8
8
  # @param indent [Fixnum] Size of the indentation to be added
9
- # @param context [Context] Processing environment data
10
9
  #
11
- def yield_node(node, indent, context)
12
- buffer_eval "yield if block_given?"
10
+ def yield_node(node, indent)
11
+ buffer_eval 'yield if block_given?'
13
12
  end
14
13
  end
15
14
  end
@@ -21,36 +21,23 @@ module Opulent
21
21
  # @param overwrite [Boolean] Write changes directly to the parent binding
22
22
  #
23
23
  def initialize(input, settings = {})
24
- # Set def from other Opulent instances
25
- @def = settings.delete(:def) || {}
26
-
27
24
  # Update default settings with user settings
28
- Settings.update_settings settings unless settings.empty?
25
+ @settings = Settings.new
26
+ @settings.update_settings settings unless settings.empty?
29
27
 
30
28
  # Read input parameter based on opening mode. If we have a file mode, we
31
29
  # get its path and read the code. We need to reset the mode in case the
32
30
  # next render call is on code, not on a file.
33
31
  @code = read input
34
32
 
33
+ # Pass filename into settings for Parser and Compiler
34
+ @settings[:file] = @file
35
+
35
36
  # Get the nodes tree
36
- @nodes, @def = Parser.new(@file, @def).parse @code
37
+ @nodes, @def = Parser.new(@settings).parse @code
37
38
 
38
39
  # Compile our syntax tree using input context
39
- @template = Compiler.new.compile @nodes
40
- end
41
-
42
- # Read input as file or string input
43
- #
44
- # @param input [Object]
45
- #
46
- def read(input)
47
- if input.is_a? Symbol
48
- @file = File.expand_path get_eval_file input
49
- File.read @file
50
- else
51
- @file = File.expand_path __FILE__
52
- input
53
- end
40
+ @template = Compiler.new(@settings).compile @nodes, @def
54
41
  end
55
42
 
56
43
  # Avoid code duplication when layouting is set. When we have a layout, look
@@ -96,6 +83,20 @@ module Opulent
96
83
 
97
84
  private
98
85
 
86
+ # Read input as file or string input
87
+ #
88
+ # @param input [Object]
89
+ #
90
+ def read(input)
91
+ if input.is_a? Symbol
92
+ @file = File.expand_path get_eval_file input
93
+ File.read @file
94
+ else
95
+ @file = File.expand_path __FILE__
96
+ input
97
+ end
98
+ end
99
+
99
100
  # Add .op extension to input file if it isn't already set.
100
101
  #
101
102
  # @param input [Symbol] Input file
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require 'yaml'
3
3
  require 'opulent/version'
4
+ require 'opulent/logger'
4
5
 
5
6
  # @Opulent
6
7
  module Opulent
@@ -13,10 +14,6 @@ module Opulent
13
14
  def initialize(arguments)
14
15
  i = 0
15
16
 
16
- layout_error = 'Missing input or incorrect file extension for ' \
17
- 'layout [-l] argument. '
18
- layout_error += "Found #{arguments[i + 1]} instead." if arguments[i + 1]
19
-
20
17
  while arguments[i]
21
18
  case arguments[i]
22
19
 
@@ -33,20 +30,19 @@ module Opulent
33
30
  if arguments[i + 1] =~ EXTENSION
34
31
  @layout = arguments[(i += 1)]
35
32
  else
36
- error layout_error
33
+ Logger.error :exec, arguments[i + 1], :layout_error
37
34
  end
38
35
 
39
36
  # opulent -v
40
37
  when '-v', 'version'
41
- write "Version #{Opulent::VERSION} is currently installed."
38
+ Logger.log :version
42
39
  return
43
40
 
44
41
  # opulent -c
45
42
  when '-c', 'context', '-lc', 'locals'
46
43
  @locals_file = arguments[(i += 1)]
47
44
  unless File.file? @locals_file
48
- error "Given context file #{@locals_file.inspect} does not exist" \
49
- ' or an incorrect path has been specified.'
45
+ Logger.error :exec, @locals_file, :locals_file
50
46
  end
51
47
 
52
48
  if File.extname(@locals_file) == '.json'
@@ -54,16 +50,15 @@ module Opulent
54
50
  elsif File.extname(@locals_file) == '.yml'
55
51
  @locals = YAML.load_file @locals_file
56
52
  else
57
- error "Unknown file extension #{@locals_file.inspect} given as" \
58
- ' locals file. Please use JSON or YAML as input.'
53
+ Logger.error :exec, @locals_file, :locals_file_format
59
54
  end
60
55
 
61
56
  # opulent -h
62
57
  when '-h', 'help'
63
- write help
58
+ Logger.log :help
64
59
  return
65
60
  else
66
- error "Unknown input argument [#{arguments[i]}] has been encountered."
61
+ Logger.error :exec, arguments[i], :input_arguments
67
62
  end
68
63
 
69
64
  i += 1
@@ -72,72 +67,35 @@ module Opulent
72
67
  if @input
73
68
  @locals ||= {}
74
69
 
75
- unless File.file? @input
76
- error "Given input file #{@input.inspect} does not exist or " \
77
- 'an incorrect path has been specified.'
78
- end
70
+ Logger.error :exec, @input, :input unless File.file? @input
71
+
72
+ opulent_layout = Opulent.new @layout
73
+ opulent_page = Opulent.new @input
74
+ scope = Object.new
79
75
 
80
- message = ''
81
- opulent = Opulent.new
82
76
  if @layout
83
77
  output = proc do
84
- opulent.render_file(@layout, @locals) {
85
- opulent.render_file(@input, @locals) {}
86
- }
78
+ opulent_layout.render scope, @locals do
79
+ opulent_page.render scope, @locals do
80
+ end
81
+ end
87
82
  end[]
88
83
  else
89
84
  output = proc do
90
- opulent.render_file(@input, @locals)
85
+ opulent_page.render scope, @locals do
86
+ end
91
87
  end[]
92
88
  end
93
89
 
94
90
  if @output
95
91
  File.open(@output, 'w') { |file| file.write output }
96
- message += "Successfully rendered #{@input.inspect} " \
97
- "to #{@output.inspect}."
92
+ Logger.log :successful_render, @input, @output
98
93
  else
99
- message += "Successfully rendered #{@input.inspect}.\n\n"
100
- message += 'No output file specified. Writing result to ' \
101
- "terminal.\n\n#{output}"
94
+ Logger.log :successful_render_print, @input, output
102
95
  end
103
-
104
- write message
105
96
  else
106
- error "You haven't specified an input file."
97
+ Logger.error :exec, :no_input
107
98
  end
108
99
  end
109
-
110
- def help(_extra = '')
111
- <<-HELP #{_extra} You can use the following commands with the Opulent
112
- Command Line Interface: \n\n
113
- opulent input.op output.op Render an input file and write the result to
114
- the output file.\n
115
- opulent layout [-l] layout.op Render an input file using given input
116
- layout file.\n
117
- opulent help [-h] Show available command line options.\n
118
- opulent version [-v] Show installed version.\n"
119
- HELP
120
- end
121
-
122
- # Give an explicit response for the given input arguments
123
- #
124
- # @param message [String] Response message to display to the user
125
- #
126
- def write(message)
127
- # Reconstruct lines to display where errors occur
128
- puts "\nOpulent " + Logger.green('[Engine]') +
129
- "\n---\n#{message}\n\n\n"
130
- end
131
-
132
- # Give an explicit error report where an unexpected sequence of tokens
133
- # appears and give indications on how to solve it
134
- #
135
- # @param message [String] Error message to display to the user
136
- #
137
- def error(message)
138
- # Reconstruct lines to display where errors occur
139
- fail "\nOpulent " + Logger.red('[Engine]') +
140
- "\n---\n#{message}\n\n#{help}\n\n\n"
141
- end
142
100
  end
143
101
  end
@@ -55,17 +55,244 @@ module Opulent
55
55
  # Require windows libraries for ANSI Console output
56
56
  #
57
57
  def require_windows_libs
58
+ return unless RUBY_PLATFORM =~ /win32/
59
+
58
60
  begin
59
- require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
61
+ require 'Win32/Console/ANSI'
60
62
  rescue LoadError
61
63
  raise 'You must run "gem install win32console" to use Opulent\'s
62
64
  error reporting on Windows.'
63
65
  end
64
66
  end
65
67
 
66
- # Pretty print Nodes with their important details
68
+
69
+ # Output an error message based on class context and input data
70
+ #
71
+ # @param message [Symbol] Message to be displayed
72
+ # @param data [Array] Data to be displayed with the message
73
+ #
74
+ def log(message, *data)
75
+ case error
76
+ when :version
77
+ title = 'Version'
78
+ message = <<-LOG
79
+ Version #{Opulent::VERSION} is currently installed.
80
+ LOG
81
+ when :successful_render
82
+ title = 'Render Complete'
83
+ message = <<-LOG
84
+ Successfully rendered #{data[0].inspect} to #{data[1].inspect}.
85
+ LOG
86
+ when :successful_render_print
87
+ title = 'Render Complete'
88
+ message = <<-LOG
89
+ Successfully rendered #{data[0].inspect}.
90
+
91
+ No output file specified. Writing result to terminal.
92
+
93
+ #{data[1]}"
94
+
95
+ LOG
96
+ when :help
97
+ title = 'Help'
98
+ message = <<-LOG
99
+ You can use the following commands with the Opulent Command Line Interface:
100
+
101
+ opulent input.op output.op Render an input file and write the result to
102
+ the output file.
103
+ opulent layout [-l] layout.op Render an input file using given input
104
+ layout file.
105
+ opulent help [-h] Show available command line options.
106
+ opulent version [-v] Show installed version.
107
+ LOG
108
+ end
109
+
110
+ puts <<-OPULENT_LOG
111
+ \n
112
+ [Opulent Engine] #{title}
113
+
114
+ #{message}
115
+
116
+ OPULENT_LOG
117
+ end
118
+
119
+ # Display an error message based on context
120
+ #
121
+ def error(type, *data)
122
+ case type
123
+ when :parse
124
+ parse_error data[0], data[1], data[2], data[3], data[4..-1]
125
+ when :compile
126
+ compile_error data[0], data[1], data[2..-1]
127
+ when :exec
128
+ exec_error data[0], data[1..-1]
129
+ end
130
+ end
131
+
132
+ # Output an error message based on class context and input data
133
+ #
134
+ # @param klass [Symbol] Class in which the error happens
135
+ # @param error [Symbol] Error identification symbol
136
+ # @param data [Array] Data to be displayed with the error
137
+ #
138
+ def exec_error(error, *data)
139
+ case error
140
+ when :input
141
+ message = <<-ERROR
142
+ Given input file #{data[0].inspect} does not exist or an incorrect path
143
+ has been specified.
144
+ ERROR
145
+ when :layout_error
146
+ message = <<-ERROR
147
+ Missing input or incorrect file extension for layout [-l] argument.
148
+ Found #{data[0]} instead.
149
+ ERROR
150
+ when :locals_file
151
+ message = <<-ERROR
152
+ Given context file #{data[0].inspect} does not exist or
153
+ an incorrect path has been specified.
154
+ ERROR
155
+ when :locals_file_format
156
+ message = <<-ERROR
157
+ Unknown file extension #{data[0].inspect} given as locals file. Please use
158
+ JSON or YAML as input.
159
+ ERROR
160
+ when :input_arguments
161
+ message = <<-ERROR
162
+ Unknown input argument [#{data[0]}] has been encountered.
163
+ ERROR
164
+ when :no_input
165
+ message = <<-ERROR
166
+ You haven't specified an input file.
167
+ ERROR
168
+ end
169
+
170
+ fail <<-OPULENT_ERROR
171
+ \n
172
+ [Opulent Engine] Runtime Error
173
+
174
+ #{message}
175
+
176
+ OPULENT_ERROR
177
+ end
178
+
179
+ # Output an error message based on class context and input data
180
+ #
181
+ # @param klass [Symbol] Class in which the error happens
182
+ # @param error [Symbol] Error identification symbol
183
+ # @param data [Array] Data to be displayed with the error
184
+ #
185
+ def compile_error(template, error, *data)
186
+ case error
187
+ when :explicit_end
188
+ message = <<-ERROR
189
+ Explicit "end" evaluation nodes are not allowed. End expressions are
190
+ inserted automatically.
191
+ ERROR
192
+ end
193
+
194
+ fail <<-OPULENT_ERROR
195
+ \n
196
+ [Opulent Compiler] Runtime Error
197
+
198
+ #{message}
199
+
200
+ OPULENT_ERROR
201
+ end
202
+
203
+ # Output an error message based on class context and input data
204
+ #
205
+ # @param klass [Symbol] Class in which the error happens
206
+ # @param error [Symbol] Error identification symbol
207
+ # @param data [Array] Data to be displayed with the error
67
208
  #
68
- def pretty_print(_model, _indent = 0)
209
+ def parse_error(code, line, character, error, *data)
210
+ line += 1
211
+
212
+ case error
213
+ when :unknown_node_type
214
+ message = <<-ERROR
215
+ An unknown node type has been encountered.
216
+ ERROR
217
+ when :expected
218
+ if [:'(', :'{', :'[', :'<'].include? data[0]
219
+ data[0] = "#{Tokens.bracket data[0]}"
220
+ end
221
+ message = <<-ERROR
222
+ Expected to find a :#{data[0]} token on line #{line} of input.
223
+ ERROR
224
+ when :root
225
+ message = <<-ERROR
226
+ Unknown node type encountered on line #{line} of input.
227
+ ERROR
228
+ when :assignments_colon
229
+ message = <<-ERROR
230
+ Unexpected end of element attributes reached on line #{line}
231
+ of input.
232
+ ERROR
233
+ when :assignments_comma
234
+ message = <<-ERROR
235
+ Unexpected end of element attributes reached on line #{line}
236
+ of input. Expected to find an attribute value.
237
+ ERROR
238
+ when :expression
239
+ message = <<-ERROR
240
+ Unexpected end of expression reached on line #{line} of input.
241
+ Expected to find another expression term.
242
+ ERROR
243
+ when :control_child
244
+ message = <<-ERROR
245
+ Unexpected control structure child found on line #{line} of input.
246
+ Expected to find a parent #{data[0]} structure.
247
+ ERROR
248
+ when :whitespace_expression
249
+ message = <<-ERROR
250
+ Unexpected end of expression reached on line #{line} of input.
251
+ Please use paranthesis for method parameters
252
+ ERROR
253
+ when :definition
254
+ message = <<-ERROR
255
+ Unexpected start of definition on line #{line} of input.
256
+ Found a definition inside another definition or element.
257
+ ERROR
258
+ when :self_enclosing
259
+ message = <<-ERROR
260
+ Unexpected content found after self enclosing node on line
261
+ #{line} of input.
262
+ ERROR
263
+ when :self_enclosing_children
264
+ message = <<-ERROR
265
+ Unexpected child elements found for self enclosing node on line
266
+ #{data[0] + 1} of input.
267
+ ERROR
268
+ when :include
269
+ message = <<-ERROR
270
+ The included file #{data[0]} does not exist or an incorrect path
271
+ has been specified.
272
+ ERROR
273
+ when :include_dir
274
+ message = <<-ERROR
275
+ The included file path #{data[0]} is a directory.
276
+ ERROR
277
+ when :include_end
278
+ message = <<-ERROR
279
+ Missing argument for include on line #{line} of input.
280
+ ERROR
281
+ else
282
+ message = <<-ERROR
283
+ Unexpected syntax error on line #{line} of input.
284
+ ERROR
285
+ end
286
+
287
+ fail <<-OPULENT_ERROR
288
+ \n
289
+ [Opulent Parser] Runtime Error
290
+
291
+ #{message}
292
+ #{code[line - 1].chomp}
293
+ #{' ' * character}^
294
+
295
+ OPULENT_ERROR
69
296
  end
70
297
  end
71
298
  end