flavour_saver 0.3.7 → 2.0.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 +5 -5
- data/.github/workflows/ci.yml +47 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile.lock +34 -62
- data/README.md +34 -11
- data/flavour_saver.gemspec +14 -9
- data/lib/flavour_saver/helpers.rb +28 -13
- data/lib/flavour_saver/lexer.rb +19 -6
- data/lib/flavour_saver/nodes.rb +1 -1
- data/lib/flavour_saver/parser.rb +8 -7
- data/lib/flavour_saver/runtime.rb +8 -8
- data/lib/flavour_saver/version.rb +1 -1
- data/spec/acceptance/backtrack_spec.rb +1 -1
- data/spec/acceptance/comment_spec.rb +1 -1
- data/spec/acceptance/custom_block_helper_spec.rb +2 -2
- data/spec/acceptance/custom_helper_spec.rb +1 -1
- data/spec/acceptance/ensure_no_rce_spec.rb +2 -2
- data/spec/acceptance/handlebars_qunit_spec.rb +299 -214
- data/spec/acceptance/if_else_spec.rb +37 -6
- data/spec/acceptance/multi_level_with_spec.rb +1 -1
- data/spec/acceptance/one_character_identifier_spec.rb +2 -2
- data/spec/acceptance/raw_block_spec.rb +1 -1
- data/spec/acceptance/runtime_run_spec.rb +1 -1
- data/spec/acceptance/sections_spec.rb +3 -3
- data/spec/acceptance/segment_literals_spec.rb +3 -3
- data/spec/acceptance/simple_expression_spec.rb +3 -3
- data/spec/acceptance/subexpression_spec.rb +37 -0
- data/spec/acceptance/unless_spec.rb +48 -0
- data/spec/fixtures/if_else.hbs +3 -3
- data/spec/fixtures/unless.hbs +5 -0
- data/spec/lib/flavour_saver/lexer_spec.rb +104 -28
- data/spec/lib/flavour_saver/parser_spec.rb +115 -82
- data/spec/lib/flavour_saver/runtime_spec.rb +44 -33
- metadata +27 -72
- data/.travis.yml +0 -14
- data/Guardfile +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 003f6b787316f313dc1a044deb52b4fcc9a87ef0b00819784ab364dca840d567
|
4
|
+
data.tar.gz: 5b9fd6980a44472f106e7e37b046d5abc896198f169896dedb8b58f41416986d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 243f501f9280e5394bcb861bf1689e365a9e43b925ab332fad4e1050c8c2aad9e03a6d76d86fb41e9273d4f935e2775fa7d2d5119b7cd938772daaa4ce6e9271
|
7
|
+
data.tar.gz: 936b3241cd8bad53c819e02bfc6cac334433207ead739628eaeafd619d36544c60f71da9b6a888c218a83afb292b639eb8246b07efed84d4d436868936950c7a
|
@@ -0,0 +1,47 @@
|
|
1
|
+
name: Continuous integration
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
tags: [ 'v*' ]
|
7
|
+
pull_request:
|
8
|
+
branches: [ master ]
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
test:
|
12
|
+
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby-version: ['2.6', '2.7', '3.0', '3.1']
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- name: Set up Ruby
|
21
|
+
uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: ${{ matrix.ruby-version }}
|
24
|
+
bundler-cache: true
|
25
|
+
- name: Run tests
|
26
|
+
run: bundle exec rspec
|
27
|
+
|
28
|
+
publish-gem:
|
29
|
+
if: github.ref_type == 'tag'
|
30
|
+
runs-on: ubuntu-latest
|
31
|
+
needs: [test]
|
32
|
+
steps:
|
33
|
+
- uses: actions/checkout@v2
|
34
|
+
- name: Set up Ruby 2.7
|
35
|
+
uses: ruby/setup-ruby@v1
|
36
|
+
with:
|
37
|
+
ruby-version: 2.7
|
38
|
+
- name: Publish to RubyGems
|
39
|
+
run: |
|
40
|
+
mkdir -p $HOME/.gem
|
41
|
+
touch $HOME/.gem/credentials
|
42
|
+
chmod 0600 $HOME/.gem/credentials
|
43
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
44
|
+
gem build *.gemspec
|
45
|
+
gem push *.gem
|
46
|
+
env:
|
47
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [2.0.0]
|
11
|
+
|
12
|
+
### Breaking Changes
|
13
|
+
|
14
|
+
* The #if and #unless helpers now treat zero as falsey (#54, #55)
|
15
|
+
* The #unless helper now treats empty objects as falsey (#55)
|
16
|
+
* Using #each on a hash now yields the value instead of an array of key, value (#52)
|
17
|
+
|
18
|
+
### Added
|
19
|
+
|
20
|
+
* Support for @root object (#49)
|
21
|
+
* Support for @key object inside #each blocks with Hashes (#52)
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
|
25
|
+
* Lex number literals that start with 0 (#53)
|
26
|
+
|
27
|
+
## [1.0.0] - 2022-01-19
|
28
|
+
|
29
|
+
### Added
|
30
|
+
|
31
|
+
* Ruby 3.0 and 3.1 support
|
32
|
+
|
33
|
+
### Changed
|
34
|
+
|
35
|
+
* The gem is now maintained by the FlavourSaver organization
|
36
|
+
|
37
|
+
### Removed
|
38
|
+
|
39
|
+
* Dropped support for Ruby 1.9, 2.0, 2.1, 2.2, 2.3, and 2.4
|
40
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,82 +1,54 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
flavour_saver (0.
|
4
|
+
flavour_saver (2.0.0)
|
5
5
|
rltk (~> 2.2.0)
|
6
6
|
tilt
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activesupport (4.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
diff-lcs (1.
|
19
|
-
ffi (1.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
lumberjack (>= 1.0.2)
|
25
|
-
pry (>= 0.9.10)
|
26
|
-
thor (>= 0.14.6)
|
27
|
-
guard-bundler (1.0.0)
|
28
|
-
bundler (~> 1.0)
|
29
|
-
guard (~> 1.1)
|
30
|
-
guard-rspec (3.1.0)
|
31
|
-
guard (>= 1.8)
|
32
|
-
rspec (~> 2.13)
|
33
|
-
i18n (0.7.0)
|
34
|
-
listen (1.3.1)
|
35
|
-
rb-fsevent (>= 0.9.3)
|
36
|
-
rb-inotify (>= 0.9)
|
37
|
-
rb-kqueue (>= 0.2)
|
38
|
-
lumberjack (1.0.4)
|
39
|
-
method_source (0.8.2)
|
40
|
-
minitest (4.7.5)
|
41
|
-
multi_json (1.10.1)
|
42
|
-
pry (0.9.12.2)
|
43
|
-
coderay (~> 1.0.5)
|
44
|
-
method_source (~> 0.8)
|
45
|
-
slop (~> 3.4)
|
46
|
-
rake (10.1.0)
|
47
|
-
rb-fsevent (0.9.3)
|
48
|
-
rb-inotify (0.9.2)
|
49
|
-
ffi (>= 0.5.0)
|
50
|
-
rb-kqueue (0.2.0)
|
51
|
-
ffi (>= 0.5.0)
|
11
|
+
activesupport (6.1.4.4)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 1.6, < 2)
|
14
|
+
minitest (>= 5.1)
|
15
|
+
tzinfo (~> 2.0)
|
16
|
+
zeitwerk (~> 2.3)
|
17
|
+
concurrent-ruby (1.1.9)
|
18
|
+
diff-lcs (1.5.0)
|
19
|
+
ffi (1.15.5)
|
20
|
+
i18n (1.8.11)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
minitest (5.15.0)
|
23
|
+
rake (13.0.6)
|
52
24
|
rltk (2.2.1)
|
53
25
|
ffi (>= 1.0.0)
|
54
|
-
rspec (
|
55
|
-
rspec-core (~>
|
56
|
-
rspec-expectations (~>
|
57
|
-
rspec-mocks (~>
|
58
|
-
rspec-core (
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
26
|
+
rspec (3.10.0)
|
27
|
+
rspec-core (~> 3.10.0)
|
28
|
+
rspec-expectations (~> 3.10.0)
|
29
|
+
rspec-mocks (~> 3.10.0)
|
30
|
+
rspec-core (3.10.1)
|
31
|
+
rspec-support (~> 3.10.0)
|
32
|
+
rspec-expectations (3.10.2)
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
+
rspec-support (~> 3.10.0)
|
35
|
+
rspec-mocks (3.10.2)
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
37
|
+
rspec-support (~> 3.10.0)
|
38
|
+
rspec-support (3.10.3)
|
39
|
+
tilt (2.0.10)
|
40
|
+
tzinfo (2.0.4)
|
41
|
+
concurrent-ruby (~> 1.0)
|
42
|
+
zeitwerk (2.5.3)
|
67
43
|
|
68
44
|
PLATFORMS
|
69
45
|
ruby
|
70
46
|
|
71
47
|
DEPENDENCIES
|
72
|
-
activesupport (
|
48
|
+
activesupport (< 7.0)
|
73
49
|
flavour_saver!
|
74
|
-
guard-bundler
|
75
|
-
guard-rspec
|
76
50
|
rake
|
77
|
-
rspec
|
78
|
-
rspec-expectations
|
79
|
-
rspec-mocks
|
51
|
+
rspec
|
80
52
|
|
81
53
|
BUNDLED WITH
|
82
|
-
|
54
|
+
2.2.17
|
data/README.md
CHANGED
@@ -2,25 +2,18 @@
|
|
2
2
|
|
3
3
|
[Handlebars.js](http://handlebarsjs.com) without the `.js`
|
4
4
|
|
5
|
-
[](https://badge.fury.io/rb/flavour_saver)
|
6
|
+

|
7
|
+
[](https://codeclimate.com/github/FlavourSaver/FlavourSaver/maintainability)
|
8
8
|
|
9
9
|
## WAT?
|
10
10
|
|
11
|
-
FlavourSaver is a ruby-based implementation of the [Handlebars.js](http://
|
11
|
+
FlavourSaver is a ruby-based implementation of the [Handlebars.js](http://handlebarsjs.com)
|
12
12
|
templating language. FlavourSaver supports Handlebars template rendering natively on
|
13
13
|
Rails and on other frameworks (such as Sinatra) via Tilt.
|
14
14
|
|
15
15
|
Please use it, break it, and send issues/PR's for improvement.
|
16
16
|
|
17
|
-
## Caveat
|
18
|
-
|
19
|
-
FlavourSaver is used in production by a lot of folks, none of whom are me. As
|
20
|
-
I don't use FlavourSaver in my daily life I will not be responding to issues
|
21
|
-
unless they have a corresponding PR. If you'd like to take over maintaining
|
22
|
-
this project then get in contact.
|
23
|
-
|
24
17
|
## License
|
25
18
|
|
26
19
|
FlavourSaver is Copyright (c) 2013 Resistor Limited and licensed under the terms
|
@@ -74,6 +67,7 @@ Currently supported:
|
|
74
67
|
- Inverse blocks
|
75
68
|
- Partials
|
76
69
|
- Raw content (`{{{{raw}}}} not parsed or validated {{{{/raw}}}}`)
|
70
|
+
- Subexpressions (`{{sum 1 (sum 1 1)}}` returns `3`)
|
77
71
|
|
78
72
|
## Helpers
|
79
73
|
|
@@ -248,6 +242,35 @@ Which could be used like so:
|
|
248
242
|
{{/isFemale}}
|
249
243
|
```
|
250
244
|
|
245
|
+
### Subexpressions
|
246
|
+
|
247
|
+
You can use a subexpression as any value for a helper, and it will be executed before it is ran. You can also nest them, and use them in assignment of variables.
|
248
|
+
|
249
|
+
Below are some examples, utilizing a "sum" helper than adds together two numbers.
|
250
|
+
|
251
|
+
```
|
252
|
+
{{sum (sum 5 10) (sum 2 (sum 1 4))}}
|
253
|
+
#=> 22
|
254
|
+
|
255
|
+
{{#if (sum 1 2) > 2}}its more{{/if}}
|
256
|
+
#=> its more
|
257
|
+
|
258
|
+
{{#student_heights size=(sum boys girls)}}
|
259
|
+
```
|
260
|
+
|
261
|
+
### Raw Content
|
262
|
+
|
263
|
+
Sometimes you don't want a section of content to be evaluted as handlebars, such as when you want to display it in a page that renders with handlebars. FlavourSaver offers a `raw` helper, that will allow you to pass anything through wrapped in those elements, and it will not be evaluated.
|
264
|
+
|
265
|
+
```
|
266
|
+
{{{{raw}}}}
|
267
|
+
{{if} this tries to parse, it will break on syntax
|
268
|
+
{{{{/raw}}}}
|
269
|
+
=> {{if} this tries to parse, it will break on syntax
|
270
|
+
```
|
271
|
+
|
272
|
+
Its important to note that while this looks like a block helper, it is not in practice. This is why you must omit the use of a `#` when writing it.
|
273
|
+
|
251
274
|
### Using Partials
|
252
275
|
|
253
276
|
Handlebars allows you to register a partial either as a function or a string template with
|
data/flavour_saver.gemspec
CHANGED
@@ -2,11 +2,18 @@
|
|
2
2
|
require File.expand_path('../lib/flavour_saver/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = ["James Harton"]
|
6
|
-
gem.email = ["
|
5
|
+
gem.authors = ["Clayton Passmore", "James Harton"]
|
6
|
+
gem.email = ["ctpassmore+flavoursaver@gmail.com"]
|
7
7
|
gem.description = %q{FlavourSaver is a pure-ruby implimentation of the Handlebars templating language}
|
8
8
|
gem.summary = %q{Handlebars.js without the .js}
|
9
|
-
gem.homepage = "http://
|
9
|
+
gem.homepage = "http://github.com/FlavourSaver/FlavourSaver"
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.metadata = {
|
12
|
+
"bug_tracker_uri" => "http://github.com/FlavourSaver/FlavourSaver/issues",
|
13
|
+
"changelog_uri" => "http://github.com/FlavourSaver/FlavourSaver/blob/master/CHANGELOG.md",
|
14
|
+
"homepage_uri" => "http://github.com/FlavourSaver/FlavourSaver",
|
15
|
+
"source_code_uri" => "http://github.com/FlavourSaver/FlavourSaver",
|
16
|
+
}
|
10
17
|
|
11
18
|
gem.files = `git ls-files`.split($\)
|
12
19
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -15,13 +22,11 @@ Gem::Specification.new do |gem|
|
|
15
22
|
gem.require_paths = ["lib"]
|
16
23
|
gem.version = FlavourSaver::VERSION
|
17
24
|
|
25
|
+
gem.required_ruby_version = ">= 2.6.0"
|
26
|
+
|
18
27
|
gem.add_development_dependency 'rake'
|
19
|
-
gem.add_development_dependency '
|
20
|
-
gem.add_development_dependency '
|
21
|
-
gem.add_development_dependency 'rspec-mocks'
|
22
|
-
gem.add_development_dependency 'rspec-expectations'
|
23
|
-
gem.add_development_dependency 'guard-bundler'
|
24
|
-
gem.add_development_dependency 'activesupport', '~> 4.0.2'
|
28
|
+
gem.add_development_dependency 'rspec'
|
29
|
+
gem.add_development_dependency 'activesupport', '< 7.0'
|
25
30
|
|
26
31
|
gem.add_dependency 'rltk', '~> 2.2.0'
|
27
32
|
gem.add_dependency 'tilt'
|
@@ -11,24 +11,31 @@ module FlavourSaver
|
|
11
11
|
r = []
|
12
12
|
count = 0
|
13
13
|
collection.each do |element|
|
14
|
-
|
14
|
+
locals ={
|
15
|
+
'index' => count,
|
16
|
+
'last' => count == collection.size - 1,
|
17
|
+
'first' => count == 0
|
18
|
+
}
|
19
|
+
|
20
|
+
locals['key'], element = element if collection.is_a?(Hash)
|
21
|
+
|
22
|
+
r << yield.contents(element, locals)
|
15
23
|
count += 1
|
16
24
|
end
|
17
25
|
yield.rendered!
|
18
26
|
r.join ''
|
19
27
|
end
|
20
28
|
|
21
|
-
def if(
|
22
|
-
|
23
|
-
if truthy
|
29
|
+
def if(value)
|
30
|
+
if truthy?(value)
|
24
31
|
yield.contents
|
25
32
|
else
|
26
33
|
yield.inverse
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
30
|
-
def unless(
|
31
|
-
self.if(!
|
37
|
+
def unless(value, &b)
|
38
|
+
self.if(!truthy?(value), &b)
|
32
39
|
end
|
33
40
|
|
34
41
|
def this
|
@@ -39,6 +46,16 @@ module FlavourSaver
|
|
39
46
|
FS.logger.debug("FlavourSaver: #{message}")
|
40
47
|
''
|
41
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Think of this as a compatability layer to make Ruby conditionals
|
53
|
+
# behave like JavaScript conditionals.
|
54
|
+
def truthy?(value)
|
55
|
+
value = false if value.respond_to?(:size) && (value.size == 0)
|
56
|
+
value = false if value.respond_to?(:zero?) && value.zero?
|
57
|
+
value
|
58
|
+
end
|
42
59
|
end
|
43
60
|
|
44
61
|
class Decorator < Defaults
|
@@ -59,7 +76,7 @@ module FlavourSaver
|
|
59
76
|
|
60
77
|
def [](accessor)
|
61
78
|
if array?
|
62
|
-
if accessor.match
|
79
|
+
if accessor.match?(/[0-9]+/)
|
63
80
|
return @source.at(accessor.to_i)
|
64
81
|
end
|
65
82
|
end
|
@@ -85,7 +102,7 @@ module FlavourSaver
|
|
85
102
|
end
|
86
103
|
|
87
104
|
def register_helper(method,&b)
|
88
|
-
if method.respond_to?
|
105
|
+
if !method.is_a?(Symbol) && method.respond_to?(:name)
|
89
106
|
registered_helpers[method.name.to_sym] = method
|
90
107
|
elsif b
|
91
108
|
registered_helpers[method.to_sym] = b
|
@@ -104,15 +121,13 @@ module FlavourSaver
|
|
104
121
|
|
105
122
|
def decorate_with(context, helper_names=[], locals={})
|
106
123
|
helpers = if helper_names.any?
|
107
|
-
|
108
|
-
registered_helpers.select { |k,v|
|
124
|
+
helper_symbols = helper_names.map(&:to_sym)
|
125
|
+
registered_helpers.select { |k,v| helper_symbols.member? k }
|
109
126
|
else
|
110
|
-
|
127
|
+
registered_helpers
|
111
128
|
end
|
112
129
|
helpers = helpers.merge(locals)
|
113
130
|
Decorator.new(helpers, context)
|
114
131
|
end
|
115
|
-
|
116
132
|
end
|
117
133
|
end
|
118
|
-
|
data/lib/flavour_saver/lexer.rb
CHANGED
@@ -52,7 +52,7 @@ module FlavourSaver
|
|
52
52
|
:GT
|
53
53
|
end
|
54
54
|
|
55
|
-
rule /([
|
55
|
+
rule /([0-9]+(\.[0-9]+)?)/, :expression do |n|
|
56
56
|
[ :NUMBER, n ]
|
57
57
|
end
|
58
58
|
|
@@ -78,7 +78,7 @@ module FlavourSaver
|
|
78
78
|
:ELSE
|
79
79
|
end
|
80
80
|
|
81
|
-
rule /([A-Za-
|
81
|
+
rule /([A-Za-z_]\w*)/, :expression do |name|
|
82
82
|
[ :IDENT, name ]
|
83
83
|
end
|
84
84
|
|
@@ -86,6 +86,14 @@ module FlavourSaver
|
|
86
86
|
:DOT
|
87
87
|
end
|
88
88
|
|
89
|
+
rule /\(/, :expression do
|
90
|
+
:OPAR
|
91
|
+
end
|
92
|
+
|
93
|
+
rule /\)/, :expression do
|
94
|
+
:CPAR
|
95
|
+
end
|
96
|
+
|
89
97
|
rule /\=/, :expression do
|
90
98
|
:EQ
|
91
99
|
end
|
@@ -114,10 +122,15 @@ module FlavourSaver
|
|
114
122
|
pop_state
|
115
123
|
end
|
116
124
|
|
117
|
-
# Handlebars allows
|
118
|
-
#
|
119
|
-
#
|
120
|
-
|
125
|
+
# Handlebars allows identifiers with characters in them which Ruby does not.
|
126
|
+
# These are mapped to the literal notation and accessed in this way.
|
127
|
+
#
|
128
|
+
# As per the http://handlebarsjs.com/expressions.html:
|
129
|
+
#
|
130
|
+
# Identifiers may be any unicode character except for the following:
|
131
|
+
# Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~
|
132
|
+
#
|
133
|
+
rule /([^\s!-#%-,.\/;->@\[-^`{-~]+)/, :expression do |str|
|
121
134
|
[ :LITERAL, str ]
|
122
135
|
end
|
123
136
|
|
data/lib/flavour_saver/nodes.rb
CHANGED
data/lib/flavour_saver/parser.rb
CHANGED
@@ -86,6 +86,10 @@ module FlavourSaver
|
|
86
86
|
clause('EXPRST expression_contents EXPRE') { |_,e,_| e }
|
87
87
|
end
|
88
88
|
|
89
|
+
production(:subexpr) do
|
90
|
+
clause('OPAR expression_contents CPAR') { |_,e,_| e }
|
91
|
+
end
|
92
|
+
|
89
93
|
production(:expr_comment) do
|
90
94
|
clause('EXPRST BANG COMMENT EXPRE') { |_,_,e,_| e }
|
91
95
|
end
|
@@ -111,7 +115,6 @@ module FlavourSaver
|
|
111
115
|
|
112
116
|
production(:expression_contents) do
|
113
117
|
clause('WHITE? call WHITE?') { |_,e,_| e }
|
114
|
-
clause('WHITE? local WHITE?') { |_,e,_| [e] }
|
115
118
|
end
|
116
119
|
|
117
120
|
production(:call) do
|
@@ -120,17 +123,13 @@ module FlavourSaver
|
|
120
123
|
clause('DOT') { |_| [CallNode.new('this', [])] }
|
121
124
|
end
|
122
125
|
|
123
|
-
production(:local) do
|
124
|
-
clause('AT IDENT') { |_,e| LocalVarNode.new(e) }
|
125
|
-
end
|
126
|
-
|
127
126
|
production('arguments') do
|
128
127
|
clause('argument_list') { |e| e }
|
129
128
|
clause('argument_list WHITE hash') { |e0,_,e1| e0 + [e1] }
|
130
129
|
clause('hash') { |e| [e] }
|
131
130
|
end
|
132
|
-
|
133
|
-
nonempty_list(:argument_list, [:object_path,:lit], :WHITE)
|
131
|
+
|
132
|
+
nonempty_list(:argument_list, [:object_path,:lit, :subexpr], :WHITE)
|
134
133
|
|
135
134
|
production(:lit) do
|
136
135
|
clause('string') { |e| e }
|
@@ -157,6 +156,7 @@ module FlavourSaver
|
|
157
156
|
end
|
158
157
|
|
159
158
|
production(:hash_item) do
|
159
|
+
clause('IDENT EQ subexpr') { |e0,_,e1| { e0.to_sym => e1 } }
|
160
160
|
clause('IDENT EQ string') { |e0,_,e1| { e0.to_sym => e1 } }
|
161
161
|
clause('IDENT EQ number') { |e0,_,e1| { e0.to_sym => e1 } }
|
162
162
|
clause('IDENT EQ object_path') { |e0,_,e1| { e0.to_sym => e1 } }
|
@@ -170,6 +170,7 @@ module FlavourSaver
|
|
170
170
|
nonempty_list(:object_path, :object, :object_sep)
|
171
171
|
|
172
172
|
production(:object) do
|
173
|
+
clause('AT IDENT') { |_,e| LocalVarNode.new(e) }
|
173
174
|
clause('IDENT') { |e| CallNode.new(e, []) }
|
174
175
|
clause('LITERAL') { |e| LiteralCallNode.new(e, []) }
|
175
176
|
clause('parent_call') { |e| e }
|
@@ -8,7 +8,7 @@ module FlavourSaver
|
|
8
8
|
UnknownHelperException = Class.new(RuntimeError)
|
9
9
|
class Runtime
|
10
10
|
|
11
|
-
attr_accessor :context, :parent, :ast
|
11
|
+
attr_accessor :context, :parent, :ast, :privates
|
12
12
|
|
13
13
|
def self.run(ast, context, locals={}, helpers=[])
|
14
14
|
self.new(ast, context, locals, helpers).to_s
|
@@ -20,7 +20,7 @@ module FlavourSaver
|
|
20
20
|
@helpers = helpers
|
21
21
|
@context = context
|
22
22
|
@parent = parent
|
23
|
-
@privates = {}
|
23
|
+
@privates = parent ? parent.privates.dup : { 'root' => context }
|
24
24
|
end
|
25
25
|
|
26
26
|
def to_s(tmp_context = nil,privates={})
|
@@ -34,7 +34,7 @@ module FlavourSaver
|
|
34
34
|
@privates = old_privates
|
35
35
|
@context = old_context
|
36
36
|
else
|
37
|
-
result = evaluate_node(@ast)
|
37
|
+
result = evaluate_node(@ast).to_s.force_encoding(Encoding::default_external)
|
38
38
|
end
|
39
39
|
result
|
40
40
|
end
|
@@ -46,13 +46,13 @@ module FlavourSaver
|
|
46
46
|
def private_variable_get(name)
|
47
47
|
begin
|
48
48
|
@privates.fetch(name)
|
49
|
-
rescue KeyError
|
49
|
+
rescue KeyError
|
50
50
|
raise UndefinedPrivateVariableException, "private variable not found @#{name}"
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
def strip(tmp_context = nil)
|
55
|
-
self.to_s(tmp_context).gsub(/[\s
|
55
|
+
self.to_s(tmp_context).gsub(/[\s]+/,' ').strip
|
56
56
|
end
|
57
57
|
|
58
58
|
def evaluate_node(node)
|
@@ -256,9 +256,9 @@ module FlavourSaver
|
|
256
256
|
"'"
|
257
257
|
when '"'
|
258
258
|
"""
|
259
|
-
|
260
|
-
|
261
|
-
|
259
|
+
when '`'
|
260
|
+
"`"
|
261
|
+
end
|
262
262
|
end
|
263
263
|
|
264
264
|
# Mark it as already escaped if we're in Rails
|
@@ -9,6 +9,6 @@ describe 'Fixture: backtrack.hbs' do
|
|
9
9
|
it 'renders correctly' do
|
10
10
|
context.person = Struct.new(:name).new('Alan')
|
11
11
|
context.company = Struct.new(:name).new('Rad, Inc.')
|
12
|
-
subject.
|
12
|
+
expect(subject).to eq "Alan - Rad, Inc."
|
13
13
|
end
|
14
14
|
end
|
@@ -18,7 +18,7 @@ describe 'Fixture: custom_block_helper.hbs' do
|
|
18
18
|
end.join ''
|
19
19
|
end
|
20
20
|
FlavourSaver.register_helper(method(:three_times))
|
21
|
-
subject.
|
21
|
+
expect(subject).to eq "1 time. 2 time. 3 time."
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -29,7 +29,7 @@ describe 'Fixture: custom_block_helper.hbs' do
|
|
29
29
|
b.call.contents i
|
30
30
|
end.join ''
|
31
31
|
}
|
32
|
-
subject.
|
32
|
+
expect(subject).to eq "1 time. 2 time. 3 time."
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
@@ -8,7 +8,7 @@ describe "Can't call methods that the context doesn't respond to" do
|
|
8
8
|
|
9
9
|
it 'renders correctly' do
|
10
10
|
expect(Kernel).not_to receive(:system)
|
11
|
-
expect { subject }.to raise_error
|
11
|
+
expect { subject }.to raise_error(RuntimeError)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -19,7 +19,7 @@ describe "Can't eval arbitrary Ruby code" do
|
|
19
19
|
|
20
20
|
it 'renders correctly' do
|
21
21
|
expect(Kernel).not_to receive(:eval)
|
22
|
-
expect { subject }.to raise_error
|
22
|
+
expect { subject }.to raise_error(RuntimeError)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|