faml 0.2.6 → 0.2.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e56f6b1d1e1616fb0c3fad21f37e222335c035a5
4
- data.tar.gz: 75024b5bd093c4fcc27505e42d9fd17e3ff4cca5
3
+ metadata.gz: fd9450955cdd17a06278c80b23b5b4b566e3ac15
4
+ data.tar.gz: e115460cd407681b8c8768f976ad9f4a33b74313
5
5
  SHA512:
6
- metadata.gz: c5c2e4fd53a113be080c4a36e93dfc9c9472fd2bd2d2beecb5f60b7c0a8e47534380a9c3116239dcf2206710951d247b4a673f30441abd7eb07cc6d0b977f6a4
7
- data.tar.gz: 3bb17b5f494fed5b47bbbe7507156b90cf7cc8f2378e52398527e97228e854249b50d4c976335055e8fad919ee90ad490e22004e52b30c867404900ea552bc3b
6
+ metadata.gz: 89aab3814554aef1673f164c666bc974f3fc7e8b2ff45b2e4bb438861caaac7796d4a2b76b9ccb612259e5737aac0001877d1d1d20e42bfb49867054e5547010
7
+ data.tar.gz: ab7478d4edefc0e5d3fc6f70cd1b6af2a6c1b8e40ea4d73175592b1e67d72500fc767ecaf2d9d7fc4022f684e0b78b0fa99112544f5dc22a8ad033101c87076b
@@ -1,3 +1,8 @@
1
+ ## 0.2.7 (2015-03-31)
2
+ - Improve backtrace of compile time errors
3
+ - Annotate Faml::Ast with filename and line number (internal)
4
+ - Fix error with tilt 1.x.
5
+
1
6
  ## 0.2.6 (2015-03-31)
2
7
  - Fix dependency on temple
3
8
  - Improve backtrace when syntax error is raised
@@ -15,7 +15,7 @@ module Faml
15
15
  include HasChildren
16
16
  end
17
17
 
18
- class Doctype < Struct.new(:doctype)
18
+ class Doctype < Struct.new(:doctype, :filename, :lineno)
19
19
  end
20
20
 
21
21
  class Element < Struct.new(
@@ -28,6 +28,8 @@ module Faml
28
28
  :self_closing,
29
29
  :nuke_inner_whitespace,
30
30
  :nuke_outer_whitespace,
31
+ :filename,
32
+ :lineno,
31
33
  )
32
34
  include HasChildren
33
35
 
@@ -48,6 +50,8 @@ module Faml
48
50
  :escape_html,
49
51
  :preserve,
50
52
  :mid_block_keyword,
53
+ :filename,
54
+ :lineno,
51
55
  )
52
56
  include HasChildren
53
57
 
@@ -65,7 +69,7 @@ module Faml
65
69
  end
66
70
  end
67
71
 
68
- class SilentScript < Struct.new(:children, :script, :mid_block_keyword)
72
+ class SilentScript < Struct.new(:children, :script, :mid_block_keyword, :filename, :lineno)
69
73
  include HasChildren
70
74
 
71
75
  def initialize(*)
@@ -76,7 +80,7 @@ module Faml
76
80
  end
77
81
  end
78
82
 
79
- class HtmlComment < Struct.new(:children, :comment, :conditional)
83
+ class HtmlComment < Struct.new(:children, :comment, :conditional, :filename, :lineno)
80
84
  include HasChildren
81
85
 
82
86
  def initialize(*)
@@ -86,11 +90,11 @@ module Faml
86
90
  end
87
91
  end
88
92
 
89
- class HamlComment < Struct.new(:children)
93
+ class HamlComment < Struct.new(:children, :filename, :lineno)
90
94
  include HasChildren
91
95
  end
92
96
 
93
- class Text < Struct.new(:text, :escape_html)
97
+ class Text < Struct.new(:text, :escape_html, :filename, :lineno)
94
98
  def initialize(*)
95
99
  super
96
100
  if self.escape_html.nil?
@@ -99,14 +103,14 @@ module Faml
99
103
  end
100
104
  end
101
105
 
102
- class Filter < Struct.new(:name, :texts)
106
+ class Filter < Struct.new(:name, :texts, :filename, :lineno)
103
107
  def initialize(*)
104
108
  super
105
109
  self.texts ||= []
106
110
  end
107
111
  end
108
112
 
109
- class Empty
113
+ class Empty < Struct.new(:filename, :lineno)
110
114
  end
111
115
  end
112
116
  end
@@ -22,17 +22,17 @@ module Faml
22
22
  desc 'temple FILE', 'Render temple AST'
23
23
  def temple(file)
24
24
  require 'pp'
25
- pp Faml::Compiler.new.call(parse_file(file))
25
+ pp Faml::Compiler.new(filename: file).call(parse_file(file))
26
26
  end
27
27
 
28
28
  private
29
29
 
30
30
  def compile_file(file)
31
- Faml::Engine.new.call(File.read(file))
31
+ Faml::Engine.new(filename: file).call(File.read(file))
32
32
  end
33
33
 
34
34
  def parse_file(file)
35
- Faml::Parser.new.call(File.read(file))
35
+ Faml::Parser.new(filename: file).call(File.read(file))
36
36
  end
37
37
  end
38
38
  end
@@ -1,6 +1,7 @@
1
1
  require 'parser/current'
2
2
  require 'temple'
3
3
  require 'faml/ast'
4
+ require 'faml/error'
4
5
  require 'faml/filter_compilers'
5
6
  require 'faml/helpers'
6
7
  require 'faml/rails_helpers'
@@ -9,7 +10,7 @@ require 'faml/text_compiler'
9
10
 
10
11
  module Faml
11
12
  class Compiler < Temple::Parser
12
- class UnparsableRubyCode < StandardError
13
+ class UnparsableRubyCode < Error
13
14
  end
14
15
 
15
16
  DEFAULT_AUTO_CLOSE_TAGS = %w[
@@ -23,15 +24,22 @@ module Faml
23
24
  format: :html,
24
25
  preserve: DEFAULT_PRESERVE_TAGS,
25
26
  use_html_safe: false,
27
+ filename: nil,
26
28
  )
27
29
 
28
30
  def initialize(*)
29
31
  super
30
32
  @text_compiler = TextCompiler.new
33
+ @filename = options[:filename]
31
34
  end
32
35
 
33
36
  def call(ast)
34
37
  compile(ast)
38
+ rescue Error => e
39
+ if @filename && e.lineno
40
+ e.backtrace.unshift "#{@filename}:#{e.lineno}"
41
+ end
42
+ raise e
35
43
  end
36
44
 
37
45
  def self.find_and_preserve(input)
@@ -122,7 +130,7 @@ module Faml
122
130
  end
123
131
 
124
132
  def compile_text(ast)
125
- @text_compiler.compile(ast.text, escape_html: ast.escape_html)
133
+ @text_compiler.compile(ast.text, ast.lineno, escape_html: ast.escape_html)
126
134
  end
127
135
 
128
136
  # html5 and html4 is deprecated in temple.
@@ -186,6 +194,11 @@ module Faml
186
194
  else
187
195
  temple
188
196
  end
197
+ rescue UnparsableRubyCode => e
198
+ unless e.lineno
199
+ e.lineno = ast.lineno
200
+ end
201
+ raise e
189
202
  end
190
203
 
191
204
  def self_closing?(ast)
@@ -282,7 +295,7 @@ module Faml
282
295
  parser.parse(buffer)
283
296
  true
284
297
  rescue ::Parser::SyntaxError
285
- raise UnparsableRubyCode.new("Unparsable Ruby code is given to attributes: #{text}")
298
+ raise UnparsableRubyCode.new("Unparsable Ruby code is given to attributes: #{text}", nil)
286
299
  end
287
300
 
288
301
  def build_optimized_attributes(parser, static_id, static_class)
@@ -374,7 +387,12 @@ module Faml
374
387
  end
375
388
 
376
389
  def compile_filter(ast)
377
- FilterCompilers.find(ast.name).compile(ast.texts)
390
+ FilterCompilers.find(ast.name).compile(ast)
391
+ rescue FilterCompilers::NotFound => e
392
+ unless e.lineno
393
+ e.lineno = ast.lineno
394
+ end
395
+ raise e
378
396
  end
379
397
  end
380
398
  end
@@ -20,6 +20,8 @@ module Faml
20
20
  end
21
21
 
22
22
  element = Ast::Element.new
23
+ element.filename = @line_parser.filename
24
+ element.lineno = @line_parser.lineno
23
25
  element.tag_name = m[1]
24
26
  element.static_class, element.static_id = parse_class_and_id(m[2])
25
27
  rest = m[3] || ''
@@ -1,6 +1,7 @@
1
1
  module Faml
2
2
  class Error < StandardError
3
- attr_reader :lineno
3
+ attr_accessor :lineno
4
+
4
5
  def initialize(message, lineno)
5
6
  super(message)
6
7
  @lineno = lineno
@@ -1,10 +1,10 @@
1
+ require 'faml/error'
2
+
1
3
  module Faml
2
4
  module FilterCompilers
3
- class NotFound < StandardError
4
- attr_reader
5
-
5
+ class NotFound < Error
6
6
  def initialize(name)
7
- super("Unable to find compiler for #{name}")
7
+ super("Unable to find compiler for #{name}", nil)
8
8
  @name = name
9
9
  end
10
10
  end
@@ -9,14 +9,14 @@ module Faml
9
9
 
10
10
  protected
11
11
 
12
- def compile_texts(temple, texts, tab_width: 0, keep_last_empty_lines: false)
12
+ def compile_texts(temple, lineno, texts, tab_width: 0, keep_last_empty_lines: false)
13
13
  tabs = ' ' * tab_width
14
14
  n = 0
15
15
  unless keep_last_empty_lines
16
16
  texts, n = strip_last_empty_lines(texts)
17
17
  end
18
- texts.each do |text|
19
- temple << [:static, tabs] << text_compiler.compile(text)
18
+ texts.each_with_index do |text, i|
19
+ temple << [:static, tabs] << text_compiler.compile(text, lineno + i + 1)
20
20
  unless texts.last.equal?(text)
21
21
  temple << [:static, "\n"] << [:newline]
22
22
  end
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Cdata < Base
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "<![CDATA[\n"], [:newline]]
8
- compile_texts(temple, texts, tab_width: 4)
8
+ compile_texts(temple, ast.lineno, ast.texts, tab_width: 4)
9
9
  temple << [:static, "\n]]>"]
10
10
  end
11
11
  end
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/tilt_base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Coffee < TiltBase
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "\n"], [:newline]]
8
- compile_with_tilt(temple, 'coffee', texts)
8
+ compile_with_tilt(temple, 'coffee', ast)
9
9
  temple << [:static, "\n"]
10
10
  [:haml, :tag, 'script', false, [:html, :attrs], [:html, :js, temple]]
11
11
  end
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Css < Base
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "\n"], [:newline]]
8
- compile_texts(temple, texts, tab_width: 2)
8
+ compile_texts(temple, ast.lineno, ast.texts, tab_width: 2)
9
9
  temple << [:static, "\n"]
10
10
  [:haml, :tag, 'style', false, [:html, :attrs], temple]
11
11
  end
@@ -5,9 +5,9 @@ module Faml
5
5
  class Escaped < Base
6
6
  include Temple::Utils
7
7
 
8
- def compile(texts)
8
+ def compile(ast)
9
9
  temple = [:multi, [:newline]]
10
- compile_texts(temple, texts)
10
+ compile_texts(temple, ast.lineno, ast.texts)
11
11
  temple << [:static, "\n"]
12
12
  escape_code = Temple::Filters::Escapable.new(use_html_safe: false).instance_variable_get(:@escape_code)
13
13
  sym = unique_name
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Javascript < Base
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "\n"], [:newline]]
8
- compile_texts(temple, texts, tab_width: 2)
8
+ compile_texts(temple, ast.lineno, ast.texts, tab_width: 2)
9
9
  temple << [:static, "\n"]
10
10
  [:haml, :tag, 'script', false, [:html, :attrs], [:html, :js, temple]]
11
11
  end
@@ -7,9 +7,9 @@ module Faml
7
7
  false
8
8
  end
9
9
 
10
- def compile(texts)
10
+ def compile(ast)
11
11
  temple = [:multi, [:newline]]
12
- compile_with_tilt(temple, 'markdown', texts)
12
+ compile_with_tilt(temple, 'markdown', ast)
13
13
  end
14
14
  end
15
15
 
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Plain < Base
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:newline]]
8
- compile_texts(temple, texts)
8
+ compile_texts(temple, ast.lineno, ast.texts)
9
9
  temple
10
10
  end
11
11
  end
@@ -5,10 +5,10 @@ module Faml
5
5
  class Preserve < Base
6
6
  include Temple::Utils
7
7
 
8
- def compile(texts)
8
+ def compile(ast)
9
9
  temple = [:multi, [:newline]]
10
10
  # I don't know why only :preserve filter keeps the last empty lines.
11
- compile_texts(temple, texts, keep_last_empty_lines: true)
11
+ compile_texts(temple, ast.lineno, ast.texts, keep_last_empty_lines: true)
12
12
  sym = unique_name
13
13
  [:multi,
14
14
  [:capture, sym, temple],
@@ -7,8 +7,8 @@ module Faml
7
7
  false
8
8
  end
9
9
 
10
- def compile(texts)
11
- [:multi, [:newline], [:code, strip_last_empty_lines(texts).join("\n")]]
10
+ def compile(ast)
11
+ [:multi, [:newline], [:code, strip_last_empty_lines(ast.texts).join("\n")]]
12
12
  end
13
13
  end
14
14
 
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/tilt_base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Sass < TiltBase
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "\n"], [:newline]]
8
- compile_with_tilt(temple, 'sass', texts)
8
+ compile_with_tilt(temple, 'sass', ast)
9
9
  [:haml, :tag, 'style', false, [:html, :attrs], temple]
10
10
  end
11
11
  end
@@ -3,9 +3,9 @@ require 'faml/filter_compilers/tilt_base'
3
3
  module Faml
4
4
  module FilterCompilers
5
5
  class Scss < TiltBase
6
- def compile(texts)
6
+ def compile(ast)
7
7
  temple = [:multi, [:static, "\n"], [:newline]]
8
- compile_with_tilt(temple, 'scss', texts)
8
+ compile_with_tilt(temple, 'scss', ast)
9
9
  temple << [:static, "\n"]
10
10
  [:haml, :tag, 'style', false, [:html, :attrs], temple]
11
11
  end
@@ -14,18 +14,18 @@ module Faml
14
14
 
15
15
  protected
16
16
 
17
- def compile_with_tilt(temple, name, texts)
18
- source = texts.join("\n")
17
+ def compile_with_tilt(temple, name, ast)
18
+ source = ast.texts.join("\n")
19
19
  if TextCompiler.contains_interpolation?(source)
20
20
  text_temple = [:multi]
21
- compile_texts(text_temple, texts)
21
+ compile_texts(text_temple, ast.lineno, ast.texts)
22
22
  sym = unique_name
23
23
  temple << [:capture, sym, text_temple]
24
24
  temple << [:dynamic, "::Faml::FilterCompilers::TiltBase.render_with_tilt(#{name.inspect}, #{sym})"]
25
25
  else
26
26
  compiled = self.class.render_with_tilt(name, source)
27
27
  temple << [:static, compiled]
28
- temple.concat([[:newline]] * (texts.size - 1))
28
+ temple.concat([[:newline]] * (ast.texts.size - 1))
29
29
  end
30
30
  temple
31
31
  end
@@ -10,9 +10,11 @@ module Faml
10
10
  !!@ast
11
11
  end
12
12
 
13
- def start(name)
13
+ def start(name, filename, lineno)
14
14
  @ast = Ast::Filter.new
15
15
  @ast.name = name
16
+ @ast.filename
17
+ @ast.lineno = lineno
16
18
  end
17
19
 
18
20
  def append(line)
@@ -1,8 +1,9 @@
1
1
  module Faml
2
2
  class LineParser
3
- attr_reader :lineno
3
+ attr_reader :filename, :lineno
4
4
 
5
- def initialize(template_str)
5
+ def initialize(filename, template_str)
6
+ @filename = filename
6
7
  @lines = template_str.each_line.map { |line| line.chomp.rstrip }
7
8
  @lineno = 0
8
9
  end
@@ -18,7 +18,7 @@ module Faml
18
18
  def call(template_str)
19
19
  @ast = Ast::Root.new
20
20
  @stack = []
21
- @line_parser = LineParser.new(template_str)
21
+ @line_parser = LineParser.new(@filename, template_str)
22
22
  @indent_tracker = IndentTracker.new(on_enter: method(:indent_enter), on_leave: method(:indent_leave))
23
23
  @filter_parser = FilterParser.new(@indent_tracker)
24
24
 
@@ -63,12 +63,12 @@ module Faml
63
63
  text, indent = @indent_tracker.process(line, @line_parser.lineno)
64
64
 
65
65
  if text.empty?
66
- @ast << Ast::Empty.new
66
+ @ast << create_node(Ast::Empty)
67
67
  return
68
68
  end
69
69
 
70
70
  if @ast.is_a?(Ast::HamlComment)
71
- @ast << Ast::Text.new(text)
71
+ @ast << create_node(Ast::Text) { |t| t.text = text }
72
72
  return
73
73
  end
74
74
 
@@ -101,12 +101,12 @@ module Faml
101
101
  end
102
102
 
103
103
  def parse_doctype(text)
104
- @ast << Ast::Doctype.new(text[3 .. -1].strip)
104
+ @ast << create_node(Ast::Doctype) { |d| d.doctype = text[3 .. -1].strip }
105
105
  end
106
106
 
107
107
  def parse_comment(text)
108
108
  text = text[1, text.size-1].strip
109
- comment = Ast::HtmlComment.new
109
+ comment = create_node(Ast::HtmlComment)
110
110
  comment.comment = text
111
111
  if text[0] == '['
112
112
  comment.conditional, rest = parse_conditional_comment(text)
@@ -128,7 +128,7 @@ module Faml
128
128
  end
129
129
 
130
130
  def parse_plain(text)
131
- @ast << Ast::Text.new(text)
131
+ @ast << create_node(Ast::Text) { |t| t.text = text }
132
132
  end
133
133
 
134
134
  def parse_element(text)
@@ -141,15 +141,16 @@ module Faml
141
141
 
142
142
  def parse_silent_script(text)
143
143
  if text.start_with?('-#')
144
- @ast << Ast::HamlComment.new
144
+ @ast << create_node(Ast::HamlComment)
145
145
  return
146
146
  end
147
- script = text[/\A- *(.*)\z/, 1]
148
- if script.empty?
147
+ node = create_node(Ast::SilentScript)
148
+ node.script = text[/\A- *(.*)\z/, 1]
149
+ if node.script.empty?
149
150
  syntax_error!("No Ruby code to evaluate")
150
151
  end
151
- script += RubyMultiline.read(@line_parser, script)
152
- @ast << Ast::SilentScript.new([], script)
152
+ node.script += RubyMultiline.read(@line_parser, node.script)
153
+ @ast << node
153
154
  end
154
155
 
155
156
  def parse_filter(text)
@@ -157,7 +158,7 @@ module Faml
157
158
  unless filter_name
158
159
  syntax_error!("Invalid filter name: #{text}")
159
160
  end
160
- @filter_parser.start(filter_name)
161
+ @filter_parser.start(filter_name, @line_parser.filename, @line_parser.lineno)
161
162
  end
162
163
 
163
164
  def indent_enter(_, text)
@@ -214,5 +215,15 @@ module Faml
214
215
  def syntax_error!(message)
215
216
  raise SyntaxError.new(message, @line_parser.lineno)
216
217
  end
218
+
219
+ def create_node(klass, &block)
220
+ klass.new.tap do |node|
221
+ node.filename = @line_parser.filename
222
+ node.lineno = @line_parser.lineno
223
+ if block
224
+ block.call(node)
225
+ end
226
+ end
227
+ end
217
228
  end
218
229
  end
@@ -25,46 +25,58 @@ module Faml
25
25
 
26
26
  def parse_script(text)
27
27
  if text[1] == '='
28
- Ast::Text.new(text[2 .. -1].strip)
28
+ create_node(Ast::Text) { |t| t.text = text[2 .. -1].strip }
29
29
  else
30
- script = text[1 .. -1].lstrip
31
- if script.empty?
30
+ node = create_node(Ast::Script)
31
+ node.script = text[1 .. -1].lstrip
32
+ if node.script.empty?
32
33
  syntax_error!('No Ruby code to evaluate')
33
34
  end
34
- script += RubyMultiline.read(@line_parser, script)
35
- Ast::Script.new([], script)
35
+ node.script += RubyMultiline.read(@line_parser, node.script)
36
+ node
36
37
  end
37
38
  end
38
39
 
39
40
  def parse_sanitized(text)
40
41
  case
41
42
  when text.start_with?('&==')
42
- Ast::Text.new(text[3 .. -1].lstrip)
43
+ create_node(Ast::Text) { |t| t.text = text[3 .. -1].lstrip }
43
44
  when text[1] == '=' || text[1] == '~'
44
- script = text[2 .. -1].lstrip
45
- if script.empty?
45
+ node = create_node(Ast::Script)
46
+ node.script = text[2 .. -1].lstrip
47
+ if node.script.empty?
46
48
  syntax_error!('No Ruby code to evaluate')
47
49
  end
48
- script += RubyMultiline.read(@line_parser, script)
49
- Ast::Script.new([], script, true, text[1] == '~')
50
+ node.script += RubyMultiline.read(@line_parser, node.script)
51
+ node.preserve = text[1] == '~'
52
+ node
50
53
  else
51
- Ast::Text.new(text[1 .. -1].strip)
54
+ create_node(Ast::Text) { |t| t.text = text[1 .. -1].strip }
52
55
  end
53
56
  end
54
57
 
55
58
  def parse_unescape(text)
56
59
  case
57
60
  when text.start_with?('!==')
58
- Ast::Text.new(text[3 .. -1].lstrip, false)
61
+ create_node(Ast::Text) do |t|
62
+ t.text = text[3 .. -1].lstrip
63
+ t.escape_html = false
64
+ end
59
65
  when text[1] == '=' || text[1] == '~'
60
- script = text[2 .. -1].lstrip
61
- if script.empty?
66
+ node = create_node(Ast::Script)
67
+ node.escape_html = false
68
+ node.script = text[2 .. -1].lstrip
69
+ if node.script.empty?
62
70
  syntax_error!('No Ruby code to evaluate')
63
71
  end
64
- script += RubyMultiline.read(@line_parser, script)
65
- Ast::Script.new([], script, false, text[1] == '~')
72
+ node.script += RubyMultiline.read(@line_parser, node.script)
73
+ node.preserve = text[1] == '~'
74
+ node
66
75
  else
67
- Ast::Text.new(text[1 .. -1].lstrip, false)
76
+ create_node(Ast::Text) do |t|
77
+ t.text = text[1 .. -1].lstrip
78
+ t.escape_html = false
79
+ end
68
80
  end
69
81
  end
70
82
 
@@ -73,12 +85,22 @@ module Faml
73
85
  if text.empty?
74
86
  nil
75
87
  else
76
- Ast::Text.new(text)
88
+ create_node(Ast::Text) { |t| t.text = text }
77
89
  end
78
90
  end
79
91
 
80
92
  def syntax_error!(message)
81
93
  raise SyntaxError.new(message, @line_parser.lineno)
82
94
  end
95
+
96
+ def create_node(klass, &block)
97
+ klass.new.tap do |node|
98
+ node.filename = @line_parser.filename
99
+ node.lineno = @line_parser.lineno
100
+ if block
101
+ block.call(node)
102
+ end
103
+ end
104
+ end
83
105
  end
84
106
  end
@@ -1,18 +1,19 @@
1
1
  require 'strscan'
2
+ require 'faml/error'
2
3
  require 'faml/parser_utils'
3
4
 
4
5
  module Faml
5
6
  class TextCompiler
6
- class InvalidInterpolation < StandardError
7
+ class InvalidInterpolation < Error
7
8
  end
8
9
 
9
10
  def initialize(escape_html: true)
10
11
  @escape_html = escape_html
11
12
  end
12
13
 
13
- def compile(text, escape_html: @escape_html)
14
+ def compile(text, lineno, escape_html: @escape_html)
14
15
  if self.class.contains_interpolation?(text)
15
- compile_interpolation(text, escape_html: escape_html)
16
+ compile_interpolation(text, lineno, escape_html: escape_html)
16
17
  else
17
18
  [:static, text]
18
19
  end
@@ -26,7 +27,7 @@ module Faml
26
27
 
27
28
  private
28
29
 
29
- def compile_interpolation(text, escape_html: @escape_html)
30
+ def compile_interpolation(text, lineno, escape_html: @escape_html)
30
31
  s = StringScanner.new(text)
31
32
  temple = [:multi]
32
33
  pos = s.pos
@@ -37,7 +38,7 @@ module Faml
37
38
  if escapes % 2 == 0
38
39
  # perform interpolation
39
40
  if s[2] == '#{'
40
- temple << [:escape, escape_html, [:dynamic, find_close_brace(s)]]
41
+ temple << [:escape, escape_html, [:dynamic, find_close_brace(s, lineno)]]
41
42
  else
42
43
  var = s[2][-1]
43
44
  s.scan(/\w+/)
@@ -56,11 +57,11 @@ module Faml
56
57
 
57
58
  INTERPOLATION_BRACE = /[\{\}]/o
58
59
 
59
- def find_close_brace(scanner)
60
+ def find_close_brace(scanner, lineno)
60
61
  pos = scanner.pos
61
62
  depth = ParserUtils.balance(scanner, '{', '}')
62
63
  if depth != 0
63
- raise InvalidInterpolation.new(scanner.string)
64
+ raise InvalidInterpolation.new(scanner.string, lineno)
64
65
  else
65
66
  scanner.string.byteslice(pos ... (scanner.pos-1))
66
67
  end
@@ -1,10 +1,15 @@
1
1
  require 'tilt'
2
+ require 'tilt/haml'
2
3
  require 'faml/engine'
3
4
 
4
5
  module Faml
5
6
  class Tilt < Tilt::Template
6
7
  def prepare
7
- @code = Engine.new(options.merge(filename: File.expand_path(file))).call(data)
8
+ filename = nil
9
+ if file
10
+ filename = File.expand_path(file)
11
+ end
12
+ @code = Engine.new(options.merge(filename: filename)).call(data)
8
13
  end
9
14
 
10
15
  def precompiled_template(locals = {})
@@ -1,3 +1,3 @@
1
1
  module Faml
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.7"
3
3
  end
@@ -0,0 +1,5 @@
1
+ .container
2
+ :javascript
3
+ (function() {
4
+ document.write('<script>#{content</script>');
5
+ })();
@@ -0,0 +1,4 @@
1
+ .container
2
+ :java
3
+ public static void main(String[] args) {
4
+ }
@@ -0,0 +1,3 @@
1
+ %div
2
+ %h1 hello
3
+ %p world#{
@@ -0,0 +1,3 @@
1
+ %div
2
+ %span{foo: 1, bar::}
3
+ hello
@@ -5,7 +5,12 @@ Rails.application.routes.draw do
5
5
  get :with_capture
6
6
  get :escaped
7
7
  get :preserve
8
+
8
9
  get :syntax_error
9
10
  get :indent_error
11
+ get :unparsable
12
+ get :invalid_interpolation
13
+ get :filter_not_found
14
+ get :filter_invalid_interpolation
10
15
  end
11
16
  end
@@ -69,5 +69,37 @@ RSpec.describe 'Faml with Rails', type: :request do
69
69
  }
70
70
  end
71
71
  end
72
+
73
+ describe Faml::Compiler::UnparsableRubyCode do
74
+ it 'has proper backtrace' do
75
+ expect { get '/books/unparsable' }.to raise_error { |e|
76
+ expect(e.backtrace[0]).to end_with('app/views/books/unparsable.html.haml:2')
77
+ }
78
+ end
79
+ end
80
+
81
+ describe Faml::FilterCompilers::NotFound do
82
+ it 'has proper backtrace' do
83
+ expect { get '/books/filter_not_found' }.to raise_error { |e|
84
+ expect(e.backtrace[0]).to end_with('app/views/books/filter_not_found.html.haml:2')
85
+ }
86
+ end
87
+ end
88
+
89
+ describe Faml::TextCompiler::InvalidInterpolation do
90
+ it 'has proper backtrace' do
91
+ expect { get '/books/invalid_interpolation' }.to raise_error { |e|
92
+ expect(e.backtrace[0]).to end_with('app/views/books/invalid_interpolation.html.haml:3')
93
+ }
94
+ end
95
+
96
+ context 'inside filter' do
97
+ it 'has proper backtrace' do
98
+ expect { get '/books/filter_invalid_interpolation' }.to raise_error { |e|
99
+ expect(e.backtrace[0]).to end_with('app/views/books/filter_invalid_interpolation.html.haml:4')
100
+ }
101
+ end
102
+ end
103
+ end
72
104
  end
73
105
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
@@ -336,10 +336,14 @@ files:
336
336
  - spec/rails/app/models/book.rb
337
337
  - spec/rails/app/models/concerns/.keep
338
338
  - spec/rails/app/views/books/escaped.html.haml
339
+ - spec/rails/app/views/books/filter_invalid_interpolation.html.haml
340
+ - spec/rails/app/views/books/filter_not_found.html.haml
339
341
  - spec/rails/app/views/books/hello.html.haml
340
342
  - spec/rails/app/views/books/indent_error.html.haml
343
+ - spec/rails/app/views/books/invalid_interpolation.html.haml
341
344
  - spec/rails/app/views/books/preserve.html.haml
342
345
  - spec/rails/app/views/books/syntax_error.html.haml
346
+ - spec/rails/app/views/books/unparsable.html.haml
343
347
  - spec/rails/app/views/books/with_capture.html.haml
344
348
  - spec/rails/app/views/books/with_variables.html.haml
345
349
  - spec/rails/app/views/layouts/application.html.haml
@@ -428,7 +432,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
428
432
  version: '0'
429
433
  requirements: []
430
434
  rubyforge_project:
431
- rubygems_version: 2.2.2
435
+ rubygems_version: 2.4.5
432
436
  signing_key:
433
437
  specification_version: 4
434
438
  summary: Faster implementation of Haml template language.
@@ -447,10 +451,14 @@ test_files:
447
451
  - spec/rails/app/models/book.rb
448
452
  - spec/rails/app/models/concerns/.keep
449
453
  - spec/rails/app/views/books/escaped.html.haml
454
+ - spec/rails/app/views/books/filter_invalid_interpolation.html.haml
455
+ - spec/rails/app/views/books/filter_not_found.html.haml
450
456
  - spec/rails/app/views/books/hello.html.haml
451
457
  - spec/rails/app/views/books/indent_error.html.haml
458
+ - spec/rails/app/views/books/invalid_interpolation.html.haml
452
459
  - spec/rails/app/views/books/preserve.html.haml
453
460
  - spec/rails/app/views/books/syntax_error.html.haml
461
+ - spec/rails/app/views/books/unparsable.html.haml
454
462
  - spec/rails/app/views/books/with_capture.html.haml
455
463
  - spec/rails/app/views/books/with_variables.html.haml
456
464
  - spec/rails/app/views/layouts/application.html.haml