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 +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
|