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 +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/faml/ast.rb +11 -7
- data/lib/faml/cli.rb +3 -3
- data/lib/faml/compiler.rb +22 -4
- data/lib/faml/element_parser.rb +2 -0
- data/lib/faml/error.rb +2 -1
- data/lib/faml/filter_compilers.rb +4 -4
- data/lib/faml/filter_compilers/base.rb +3 -3
- data/lib/faml/filter_compilers/cdata.rb +2 -2
- data/lib/faml/filter_compilers/coffee.rb +2 -2
- data/lib/faml/filter_compilers/css.rb +2 -2
- data/lib/faml/filter_compilers/escaped.rb +2 -2
- data/lib/faml/filter_compilers/javascript.rb +2 -2
- data/lib/faml/filter_compilers/markdown.rb +2 -2
- data/lib/faml/filter_compilers/plain.rb +2 -2
- data/lib/faml/filter_compilers/preserve.rb +2 -2
- data/lib/faml/filter_compilers/ruby.rb +2 -2
- data/lib/faml/filter_compilers/sass.rb +2 -2
- data/lib/faml/filter_compilers/scss.rb +2 -2
- data/lib/faml/filter_compilers/tilt_base.rb +4 -4
- data/lib/faml/filter_parser.rb +3 -1
- data/lib/faml/line_parser.rb +3 -2
- data/lib/faml/parser.rb +23 -12
- data/lib/faml/script_parser.rb +40 -18
- data/lib/faml/text_compiler.rb +8 -7
- data/lib/faml/tilt.rb +6 -1
- data/lib/faml/version.rb +1 -1
- data/spec/rails/app/views/books/filter_invalid_interpolation.html.haml +5 -0
- data/spec/rails/app/views/books/filter_not_found.html.haml +4 -0
- data/spec/rails/app/views/books/invalid_interpolation.html.haml +3 -0
- data/spec/rails/app/views/books/unparsable.html.haml +3 -0
- data/spec/rails/config/routes.rb +5 -0
- data/spec/rails/spec/requests/faml_spec.rb +32 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd9450955cdd17a06278c80b23b5b4b566e3ac15
|
4
|
+
data.tar.gz: e115460cd407681b8c8768f976ad9f4a33b74313
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89aab3814554aef1673f164c666bc974f3fc7e8b2ff45b2e4bb438861caaac7796d4a2b76b9ccb612259e5737aac0001877d1d1d20e42bfb49867054e5547010
|
7
|
+
data.tar.gz: ab7478d4edefc0e5d3fc6f70cd1b6af2a6c1b8e40ea4d73175592b1e67d72500fc767ecaf2d9d7fc4022f684e0b78b0fa99112544f5dc22a8ad033101c87076b
|
data/CHANGELOG.md
CHANGED
data/lib/faml/ast.rb
CHANGED
@@ -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
|
data/lib/faml/cli.rb
CHANGED
@@ -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
|
data/lib/faml/compiler.rb
CHANGED
@@ -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 <
|
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
|
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
|
data/lib/faml/element_parser.rb
CHANGED
data/lib/faml/error.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
require 'faml/error'
|
2
|
+
|
1
3
|
module Faml
|
2
4
|
module FilterCompilers
|
3
|
-
class NotFound <
|
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.
|
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(
|
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(
|
6
|
+
def compile(ast)
|
7
7
|
temple = [:multi, [:static, "\n"], [:newline]]
|
8
|
-
compile_with_tilt(temple, 'coffee',
|
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(
|
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(
|
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(
|
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
|
@@ -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(
|
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(
|
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],
|
@@ -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(
|
6
|
+
def compile(ast)
|
7
7
|
temple = [:multi, [:static, "\n"], [:newline]]
|
8
|
-
compile_with_tilt(temple, 'sass',
|
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(
|
6
|
+
def compile(ast)
|
7
7
|
temple = [:multi, [:static, "\n"], [:newline]]
|
8
|
-
compile_with_tilt(temple, 'scss',
|
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,
|
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
|
data/lib/faml/filter_parser.rb
CHANGED
data/lib/faml/line_parser.rb
CHANGED
@@ -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
|
data/lib/faml/parser.rb
CHANGED
@@ -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
|
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.
|
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.
|
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
|
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.
|
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
|
144
|
+
@ast << create_node(Ast::HamlComment)
|
145
145
|
return
|
146
146
|
end
|
147
|
-
|
148
|
-
|
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 <<
|
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
|
data/lib/faml/script_parser.rb
CHANGED
@@ -25,46 +25,58 @@ module Faml
|
|
25
25
|
|
26
26
|
def parse_script(text)
|
27
27
|
if text[1] == '='
|
28
|
-
Ast::Text.
|
28
|
+
create_node(Ast::Text) { |t| t.text = text[2 .. -1].strip }
|
29
29
|
else
|
30
|
-
|
31
|
-
|
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
|
-
|
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.
|
43
|
+
create_node(Ast::Text) { |t| t.text = text[3 .. -1].lstrip }
|
43
44
|
when text[1] == '=' || text[1] == '~'
|
44
|
-
|
45
|
-
|
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
|
-
|
50
|
+
node.script += RubyMultiline.read(@line_parser, node.script)
|
51
|
+
node.preserve = text[1] == '~'
|
52
|
+
node
|
50
53
|
else
|
51
|
-
Ast::Text.
|
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
|
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
|
-
|
61
|
-
|
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
|
-
|
72
|
+
node.script += RubyMultiline.read(@line_parser, node.script)
|
73
|
+
node.preserve = text[1] == '~'
|
74
|
+
node
|
66
75
|
else
|
67
|
-
Ast::Text
|
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.
|
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
|
data/lib/faml/text_compiler.rb
CHANGED
@@ -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 <
|
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
|
data/lib/faml/tilt.rb
CHANGED
@@ -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
|
-
|
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 = {})
|
data/lib/faml/version.rb
CHANGED
data/spec/rails/config/routes.rb
CHANGED
@@ -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.
|
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.
|
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
|