temple 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +13 -3
- data/CHANGES +10 -1
- data/EXPRESSIONS.md +1 -0
- data/lib/temple.rb +1 -0
- data/lib/temple/filters/static_analyzer.rb +17 -74
- data/lib/temple/filters/string_splitter.rb +3 -3
- data/lib/temple/static_analyzer.rb +77 -0
- data/lib/temple/version.rb +1 -1
- data/test/filters/test_static_analyzer.rb +23 -9
- data/test/filters/test_string_splitter.rb +7 -0
- data/test/test_static_analyzer.rb +39 -0
- metadata +31 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 524637aaba62c678fcfe02760bb49ec94a2073c3
|
4
|
+
data.tar.gz: a342126edf52f3e3578c4cc9f70540aed6b32b84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bbc62f26373b9fb48c57cad57672ef3c379af1b991ba2538f3d7b699f6f0c22b19199520849869463349af07b810946a52029c345e7ec532aa81fb8b893d1e8
|
7
|
+
data.tar.gz: 98da558eb0435b349b4c5ee44c407c92c07ee7eba8d08619d8a5158e447c423e16693707a052077ac4822aea810b1784f7fa0cc446fcf2648267b17dced0f421
|
data/.travis.yml
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
language: ruby
|
2
|
+
dist: trusty
|
3
|
+
|
4
|
+
cache: bundler
|
2
5
|
|
3
6
|
rvm:
|
4
7
|
- 1.9.3
|
5
8
|
- 2.0.0
|
6
|
-
- 2.1.
|
7
|
-
- 2.
|
9
|
+
- 2.1.10
|
10
|
+
- 2.2.6
|
11
|
+
- 2.3.3
|
8
12
|
- ruby-head
|
9
13
|
- jruby-19mode
|
10
|
-
- rbx-
|
14
|
+
- rbx-3
|
11
15
|
|
12
16
|
sudo: false
|
13
17
|
|
@@ -18,3 +22,9 @@ env:
|
|
18
22
|
matrix:
|
19
23
|
allow_failures:
|
20
24
|
- rvm: ruby-head
|
25
|
+
- rvm: rbx-3
|
26
|
+
exclude:
|
27
|
+
- rvm: jruby-19mode
|
28
|
+
env: ESCAPE_UTILS=1
|
29
|
+
- rvm: rbx-3
|
30
|
+
env: ESCAPE_UTILS=1
|
data/CHANGES
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
0.8.0
|
2
|
+
|
3
|
+
* Add Temple::StaticAnalyzer to analyze Ruby expressions
|
4
|
+
* Support newlines in Temple::Filters::StaticAnalyzer
|
5
|
+
|
6
|
+
0.7.8
|
7
|
+
|
8
|
+
* Fix an warning in StaticAnalyzer
|
9
|
+
|
1
10
|
0.7.7
|
2
11
|
|
3
|
-
* Add StaticAnalyzer, StringSplitter
|
12
|
+
* Add Temple::Filters::StaticAnalyzer, Temple::Filters::StringSplitter
|
4
13
|
* Freeze string literals
|
5
14
|
|
6
15
|
0.7.6
|
data/EXPRESSIONS.md
CHANGED
data/lib/temple.rb
CHANGED
@@ -1,85 +1,28 @@
|
|
1
|
-
begin
|
2
|
-
require 'ripper'
|
3
|
-
rescue LoadError
|
4
|
-
end
|
5
|
-
|
6
1
|
module Temple
|
7
2
|
module Filters
|
8
3
|
# Convert [:dynamic, code] to [:static, text] if code is static Ruby expression.
|
9
4
|
class StaticAnalyzer < Filter
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
:on_lbrace, :on_rbrace, :on_label,
|
17
|
-
:on_int, :on_float, :on_imaginary,
|
18
|
-
:on_comma, :on_sp,
|
19
|
-
].freeze
|
20
|
-
|
21
|
-
DYNAMIC_TOKENS = [
|
22
|
-
:on_ident, :on_period,
|
23
|
-
].freeze
|
24
|
-
|
25
|
-
STATIC_KEYWORDS = [
|
26
|
-
'true', 'false', 'nil',
|
27
|
-
].freeze
|
28
|
-
|
29
|
-
STATIC_OPERATORS = [
|
30
|
-
'=>',
|
31
|
-
].freeze
|
32
|
-
|
33
|
-
if defined?(Ripper)
|
34
|
-
def self.static?(code)
|
35
|
-
return false if code.nil? || code.strip.empty?
|
36
|
-
return false if SyntaxChecker.syntax_error?(code)
|
37
|
-
|
38
|
-
Ripper.lex(code).each do |(_, col), token, str|
|
39
|
-
case token
|
40
|
-
when *STATIC_TOKENS
|
41
|
-
# noop
|
42
|
-
when :on_kw
|
43
|
-
return false unless STATIC_KEYWORDS.include?(str)
|
44
|
-
when :on_op
|
45
|
-
return false unless STATIC_OPERATORS.include?(str)
|
46
|
-
when *DYNAMIC_TOKENS
|
47
|
-
return false
|
48
|
-
else
|
49
|
-
return false
|
50
|
-
end
|
51
|
-
end
|
52
|
-
true
|
53
|
-
end
|
54
|
-
|
55
|
-
def on_dynamic(code)
|
56
|
-
if StaticAnalyzer.static?(code)
|
57
|
-
[:static, eval(code).to_s]
|
58
|
-
else
|
59
|
-
[:dynamic, code]
|
60
|
-
end
|
5
|
+
def call(exp)
|
6
|
+
# Optimize only when Ripper is available.
|
7
|
+
if ::Temple::StaticAnalyzer.available?
|
8
|
+
super
|
9
|
+
else
|
10
|
+
exp
|
61
11
|
end
|
12
|
+
end
|
62
13
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
def self.syntax_error?(code)
|
67
|
-
self.new(code).parse
|
68
|
-
false
|
69
|
-
rescue ParseError
|
70
|
-
true
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
14
|
+
def on_dynamic(code)
|
15
|
+
if ::Temple::StaticAnalyzer.static?(code)
|
16
|
+
exp = [:static, eval(code).to_s]
|
74
17
|
|
75
|
-
|
76
|
-
|
18
|
+
newlines = code.count("\n")
|
19
|
+
if newlines == 0
|
20
|
+
exp
|
21
|
+
else
|
22
|
+
[:multi, exp, *newlines.times.map { [:newline] }]
|
77
23
|
end
|
78
|
-
|
79
|
-
|
80
|
-
# Do nothing if ripper is unavailable
|
81
|
-
def call(ast)
|
82
|
-
ast
|
24
|
+
else
|
25
|
+
[:dynamic, code]
|
83
26
|
end
|
84
27
|
end
|
85
28
|
end
|
@@ -16,7 +16,7 @@ module Temple
|
|
16
16
|
tokens.pop while tokens.last && [:on_comment, :on_sp].include?(tokens.last[1])
|
17
17
|
|
18
18
|
if tokens.size < 2
|
19
|
-
raise "Expected token size >= 2 but got: #{tokens.size}"
|
19
|
+
raise(FilterError, "Expected token size >= 2 but got: #{tokens.size}")
|
20
20
|
end
|
21
21
|
compile_tokens!(exps, tokens)
|
22
22
|
end
|
@@ -27,12 +27,12 @@ module Temple
|
|
27
27
|
def strip_quotes!(tokens)
|
28
28
|
_, type, beg_str = tokens.shift
|
29
29
|
if type != :on_tstring_beg
|
30
|
-
raise "Expected :on_tstring_beg but got: #{type}"
|
30
|
+
raise(FilterError, "Expected :on_tstring_beg but got: #{type}")
|
31
31
|
end
|
32
32
|
|
33
33
|
_, type, end_str = tokens.pop
|
34
34
|
if type != :on_tstring_end
|
35
|
-
raise "Expected :on_tstring_end but got: #{type}"
|
35
|
+
raise(FilterError, "Expected :on_tstring_end but got: #{type}")
|
36
36
|
end
|
37
37
|
|
38
38
|
[beg_str, end_str]
|
@@ -0,0 +1,77 @@
|
|
1
|
+
begin
|
2
|
+
require 'ripper'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Temple
|
7
|
+
module StaticAnalyzer
|
8
|
+
STATIC_TOKENS = [
|
9
|
+
:on_tstring_beg, :on_tstring_end, :on_tstring_content,
|
10
|
+
:on_embexpr_beg, :on_embexpr_end,
|
11
|
+
:on_lbracket, :on_rbracket,
|
12
|
+
:on_qwords_beg, :on_words_sep, :on_qwords_sep,
|
13
|
+
:on_lparen, :on_rparen,
|
14
|
+
:on_lbrace, :on_rbrace, :on_label,
|
15
|
+
:on_int, :on_float, :on_imaginary,
|
16
|
+
:on_comma, :on_sp, :on_ignored_nl,
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
DYNAMIC_TOKENS = [
|
20
|
+
:on_ident, :on_period,
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
STATIC_KEYWORDS = [
|
24
|
+
'true', 'false', 'nil',
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
STATIC_OPERATORS = [
|
28
|
+
'=>',
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def available?
|
33
|
+
defined?(Ripper)
|
34
|
+
end
|
35
|
+
|
36
|
+
def static?(code)
|
37
|
+
return false if code.nil? || code.strip.empty?
|
38
|
+
return false if syntax_error?(code)
|
39
|
+
|
40
|
+
Ripper.lex(code).each do |_, token, str|
|
41
|
+
case token
|
42
|
+
when *STATIC_TOKENS
|
43
|
+
# noop
|
44
|
+
when :on_kw
|
45
|
+
return false unless STATIC_KEYWORDS.include?(str)
|
46
|
+
when :on_op
|
47
|
+
return false unless STATIC_OPERATORS.include?(str)
|
48
|
+
when *DYNAMIC_TOKENS
|
49
|
+
return false
|
50
|
+
else
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def syntax_error?(code)
|
58
|
+
SyntaxChecker.new(code).parse
|
59
|
+
false
|
60
|
+
rescue SyntaxChecker::ParseError
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if defined?(Ripper)
|
66
|
+
class SyntaxChecker < Ripper
|
67
|
+
class ParseError < StandardError; end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def on_parse_error(*)
|
72
|
+
raise ParseError
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/temple/version.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
require 'helper'
|
2
|
-
begin
|
3
|
-
require 'ripper'
|
4
|
-
rescue LoadError
|
5
|
-
end
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
describe Temple::Filters::StaticAnalyzer do
|
4
|
+
before do
|
5
|
+
@filter = Temple::Filters::StaticAnalyzer.new
|
6
|
+
@generator = Temple::Generator.new
|
7
|
+
end
|
12
8
|
|
9
|
+
if Temple::StaticAnalyzer.available?
|
13
10
|
it 'should convert :dynamic to :static if code is static' do
|
14
11
|
@filter.call([:dynamic, '"#{"hello"}#{100}"']
|
15
12
|
).should.equal [:static, 'hello100']
|
@@ -19,5 +16,22 @@ if defined?(Ripper)
|
|
19
16
|
exp = [:dynamic, '"#{hello}#{100}"']
|
20
17
|
@filter.call(exp).should.equal(exp)
|
21
18
|
end
|
19
|
+
|
20
|
+
it 'should not change number of newlines in generated code' do
|
21
|
+
exp = [:dynamic, "[100,\n200,\n]"]
|
22
|
+
@filter.call(exp).should.equal([:multi, [:static, '[100, 200]'], [:newline], [:newline]])
|
23
|
+
|
24
|
+
@generator.call(@filter.call(exp)).count("\n").
|
25
|
+
should.equal(@generator.call(exp).count("\n"))
|
26
|
+
end
|
27
|
+
else
|
28
|
+
it 'should do nothing' do
|
29
|
+
[
|
30
|
+
[:dynamic, '"#{"hello"}#{100}"'],
|
31
|
+
[:dynamic, '"#{hello}#{100}"'],
|
32
|
+
].each do |exp|
|
33
|
+
@filter.call(exp).should.equal(exp)
|
34
|
+
end
|
35
|
+
end
|
22
36
|
end
|
23
37
|
end
|
@@ -14,5 +14,12 @@ if defined?(Ripper) && RUBY_VERSION >= "2.0.0"
|
|
14
14
|
@filter.call([:dynamic, '"static#{dynamic}"']
|
15
15
|
).should.equal [:multi, [:static, 'static'], [:dynamic, 'dynamic']]
|
16
16
|
end
|
17
|
+
|
18
|
+
describe '.compile' do
|
19
|
+
it 'should raise CompileError for non-string literals' do
|
20
|
+
lambda { Temple::Filters::StringSplitter.compile('1') }.
|
21
|
+
should.raise(Temple::FilterError)
|
22
|
+
end
|
23
|
+
end
|
17
24
|
end
|
18
25
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Temple::StaticAnalyzer do
|
4
|
+
describe '.available?' do
|
5
|
+
it 'should return true if its dependency is available' do
|
6
|
+
Temple::StaticAnalyzer.available?.should.equal(defined?(Ripper))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
if Temple::StaticAnalyzer.available?
|
11
|
+
describe '.static?' do
|
12
|
+
it 'should return true if given Ruby expression is static' do
|
13
|
+
['true', 'false', '"hello world"', "[1, { 2 => 3 }]", "[\n1,\n]"].each do |exp|
|
14
|
+
Temple::StaticAnalyzer.static?(exp).should.equal(true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should return false if given Ruby expression is dynamic' do
|
19
|
+
['1 + 2', 'variable', 'method_call(a)', 'CONSTANT'].each do |exp|
|
20
|
+
Temple::StaticAnalyzer.static?(exp).should.equal(false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.syntax_error?' do
|
26
|
+
it 'should return false if given Ruby expression is valid' do
|
27
|
+
['Foo.bar.baz { |c| c.d! }', '{ foo: bar }'].each do |exp|
|
28
|
+
Temple::StaticAnalyzer.syntax_error?(exp).should.equal(false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return true if given Ruby expression is invalid' do
|
33
|
+
['Foo.bar.baz { |c| c.d! ', ' foo: bar '].each do |exp|
|
34
|
+
Temple::StaticAnalyzer.syntax_error?(exp).should.equal(true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: temple
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Magnus Holm
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-02-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tilt
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/temple/mixins/options.rb
|
127
127
|
- lib/temple/mixins/template.rb
|
128
128
|
- lib/temple/parser.rb
|
129
|
+
- lib/temple/static_analyzer.rb
|
129
130
|
- lib/temple/templates.rb
|
130
131
|
- lib/temple/templates/rails.rb
|
131
132
|
- lib/temple/templates/tilt.rb
|
@@ -155,6 +156,7 @@ files:
|
|
155
156
|
- test/test_generator.rb
|
156
157
|
- test/test_grammar.rb
|
157
158
|
- test/test_map.rb
|
159
|
+
- test/test_static_analyzer.rb
|
158
160
|
- test/test_utils.rb
|
159
161
|
homepage: https://github.com/judofyr/temple
|
160
162
|
licenses:
|
@@ -176,8 +178,33 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
178
|
version: '0'
|
177
179
|
requirements: []
|
178
180
|
rubyforge_project:
|
179
|
-
rubygems_version: 2.
|
181
|
+
rubygems_version: 2.6.8
|
180
182
|
signing_key:
|
181
183
|
specification_version: 4
|
182
184
|
summary: Template compilation framework in Ruby
|
183
|
-
test_files:
|
185
|
+
test_files:
|
186
|
+
- test/filters/test_code_merger.rb
|
187
|
+
- test/filters/test_control_flow.rb
|
188
|
+
- test/filters/test_dynamic_inliner.rb
|
189
|
+
- test/filters/test_eraser.rb
|
190
|
+
- test/filters/test_escapable.rb
|
191
|
+
- test/filters/test_multi_flattener.rb
|
192
|
+
- test/filters/test_static_analyzer.rb
|
193
|
+
- test/filters/test_static_merger.rb
|
194
|
+
- test/filters/test_string_splitter.rb
|
195
|
+
- test/helper.rb
|
196
|
+
- test/html/test_attribute_merger.rb
|
197
|
+
- test/html/test_attribute_remover.rb
|
198
|
+
- test/html/test_attribute_sorter.rb
|
199
|
+
- test/html/test_fast.rb
|
200
|
+
- test/html/test_pretty.rb
|
201
|
+
- test/mixins/test_dispatcher.rb
|
202
|
+
- test/mixins/test_grammar_dsl.rb
|
203
|
+
- test/test_engine.rb
|
204
|
+
- test/test_erb.rb
|
205
|
+
- test/test_filter.rb
|
206
|
+
- test/test_generator.rb
|
207
|
+
- test/test_grammar.rb
|
208
|
+
- test/test_map.rb
|
209
|
+
- test/test_static_analyzer.rb
|
210
|
+
- test/test_utils.rb
|