faml 0.2.6 → 0.2.7

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