flavour_saver 0.3.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +47 -0
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.lock +34 -62
  5. data/README.md +34 -11
  6. data/flavour_saver.gemspec +14 -9
  7. data/lib/flavour_saver/helpers.rb +28 -13
  8. data/lib/flavour_saver/lexer.rb +19 -6
  9. data/lib/flavour_saver/nodes.rb +1 -1
  10. data/lib/flavour_saver/parser.rb +8 -7
  11. data/lib/flavour_saver/runtime.rb +8 -8
  12. data/lib/flavour_saver/version.rb +1 -1
  13. data/spec/acceptance/backtrack_spec.rb +1 -1
  14. data/spec/acceptance/comment_spec.rb +1 -1
  15. data/spec/acceptance/custom_block_helper_spec.rb +2 -2
  16. data/spec/acceptance/custom_helper_spec.rb +1 -1
  17. data/spec/acceptance/ensure_no_rce_spec.rb +2 -2
  18. data/spec/acceptance/handlebars_qunit_spec.rb +299 -214
  19. data/spec/acceptance/if_else_spec.rb +37 -6
  20. data/spec/acceptance/multi_level_with_spec.rb +1 -1
  21. data/spec/acceptance/one_character_identifier_spec.rb +2 -2
  22. data/spec/acceptance/raw_block_spec.rb +1 -1
  23. data/spec/acceptance/runtime_run_spec.rb +1 -1
  24. data/spec/acceptance/sections_spec.rb +3 -3
  25. data/spec/acceptance/segment_literals_spec.rb +3 -3
  26. data/spec/acceptance/simple_expression_spec.rb +3 -3
  27. data/spec/acceptance/subexpression_spec.rb +37 -0
  28. data/spec/acceptance/unless_spec.rb +48 -0
  29. data/spec/fixtures/if_else.hbs +3 -3
  30. data/spec/fixtures/unless.hbs +5 -0
  31. data/spec/lib/flavour_saver/lexer_spec.rb +104 -28
  32. data/spec/lib/flavour_saver/parser_spec.rb +115 -82
  33. data/spec/lib/flavour_saver/runtime_spec.rb +44 -33
  34. metadata +27 -72
  35. data/.travis.yml +0 -14
  36. data/Guardfile +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c3efa30121a2e226ebb1274cf5597d35ab7f9c47
4
- data.tar.gz: c89bc7f065e3dfbb07882c44465711981643d454
2
+ SHA256:
3
+ metadata.gz: 003f6b787316f313dc1a044deb52b4fcc9a87ef0b00819784ab364dca840d567
4
+ data.tar.gz: 5b9fd6980a44472f106e7e37b046d5abc896198f169896dedb8b58f41416986d
5
5
  SHA512:
6
- metadata.gz: 1292541b248293451704808a7458b2132ab7109f9664845c77d01c375581396192af2ee2a19c6cfae4fa567294757cb5b563803e23ccfd8110961f204adbdd03
7
- data.tar.gz: a74f63a36b4864fbc248fde7ff9e03c19ffc49412f37b472521cf2ed13bf7403e24243e423211c1599c924a92a8e9f9b1472ba15c07b12a12955de70f131da7c
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.3.7)
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.0.13)
12
- i18n (~> 0.6, >= 0.6.9)
13
- minitest (~> 4.2)
14
- multi_json (~> 1.3)
15
- thread_safe (~> 0.1)
16
- tzinfo (~> 0.3.37)
17
- coderay (1.0.9)
18
- diff-lcs (1.2.4)
19
- ffi (1.9.0)
20
- formatador (0.2.4)
21
- guard (1.8.3)
22
- formatador (>= 0.2.4)
23
- listen (~> 1.3)
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 (2.14.1)
55
- rspec-core (~> 2.14.0)
56
- rspec-expectations (~> 2.14.0)
57
- rspec-mocks (~> 2.14.0)
58
- rspec-core (2.14.5)
59
- rspec-expectations (2.14.3)
60
- diff-lcs (>= 1.1.3, < 2.0)
61
- rspec-mocks (2.14.3)
62
- slop (3.4.6)
63
- thor (0.18.1)
64
- thread_safe (0.3.4)
65
- tilt (2.0.1)
66
- tzinfo (0.3.42)
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 (~> 4.0.2)
48
+ activesupport (< 7.0)
73
49
  flavour_saver!
74
- guard-bundler
75
- guard-rspec
76
50
  rake
77
- rspec-core
78
- rspec-expectations
79
- rspec-mocks
51
+ rspec
80
52
 
81
53
  BUNDLED WITH
82
- 1.10.6
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
- [![Build Status](https://travis-ci.org/jamesotron/FlavourSaver.png)](https://travis-ci.org/jamesotron/FlavourSaver)
6
- [![Dependency Status](https://gemnasium.com/jamesotron/FlavourSaver.png)](https://gemnasium.com/jamesotron/FlavourSaver)
7
- [![Code Climate](https://codeclimate.com/github/jamesotron/FlavourSaver.png)](https://codeclimate.com/github/jamesotron/FlavourSaver)
5
+ [![Gem Version](https://badge.fury.io/rb/flavour_saver.svg)](https://badge.fury.io/rb/flavour_saver)
6
+ ![Build Status](https://github.com/FlavourSaver/FlavourSaver/actions/workflows/ci.yml/badge.svg)
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/89a99bec5bbf49359081/maintainability)](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://handlebars.js)
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
@@ -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 = ["james@resistor.io"]
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://jamesotron.github.com/FlavourSaver/"
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 'guard-rspec'
20
- gem.add_development_dependency 'rspec-core'
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
- r << yield.contents(element, 'index' => count)
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(truthy)
22
- truthy = false if truthy.respond_to?(:size) && (truthy.size == 0)
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(falsy,&b)
31
- self.if(!falsy,&b)
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 /[0-9]+/
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? :name
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
- helper_names = helper_names.map(&:to_sym)
108
- registered_helpers.select { |k,v| helper_names.member? k }.merge(locals)
124
+ helper_symbols = helper_names.map(&:to_sym)
125
+ registered_helpers.select { |k,v| helper_symbols.member? k }
109
126
  else
110
- helpers = registered_helpers
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
-
@@ -52,7 +52,7 @@ module FlavourSaver
52
52
  :GT
53
53
  end
54
54
 
55
- rule /([1-9][0-9]*(\.[0-9]+)?)/, :expression do |n|
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-z]\w*)/, :expression do |name|
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 methods with hyphens in them. Ruby doesn't, so
118
- # we'll assume you're trying to index the context with the identifier
119
- # and call the result.
120
- rule /([A-Za-z][a-z0-9_-]*[a-z0-9])/, :expression do |str|
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
 
@@ -95,7 +95,7 @@ module FlavourSaver
95
95
  end
96
96
 
97
97
  class ParentCallNode < CallNode
98
- value :depth, Fixnum
98
+ value :depth, Integer
99
99
 
100
100
  def to_callnode
101
101
  CallNode.new(name,arguments)
@@ -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 => e
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\r\n]+/,' ').strip
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
  "&#x27;"
257
257
  when '"'
258
258
  "&quot;"
259
- when '`'
260
- "&#x60;"
261
- end
259
+ when '`'
260
+ "&#x60;"
261
+ end
262
262
  end
263
263
 
264
264
  # Mark it as already escaped if we're in Rails
@@ -1,3 +1,3 @@
1
1
  module FlavourSaver
2
- VERSION = "0.3.7"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -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.should == "Alan - Rad, Inc."
12
+ expect(subject).to eq "Alan - Rad, Inc."
13
13
  end
14
14
  end
@@ -7,6 +7,6 @@ describe 'Fixture: comment.hbs' do
7
7
  let(:context) { double(:context) }
8
8
 
9
9
  it 'renders correctly' do
10
- subject.should == "I am a very nice person!"
10
+ expect(subject).to eq "I am a very nice person!"
11
11
  end
12
12
  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.should == "1 time. 2 time. 3 time."
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.should == "1 time. 2 time. 3 time."
32
+ expect(subject).to eq "1 time. 2 time. 3 time."
33
33
  end
34
34
  end
35
35
  end
@@ -10,6 +10,6 @@ describe 'Fixture: custom_helper.hbs' do
10
10
  FlavourSaver.register_helper(:say_what_again) do
11
11
  'What?'
12
12
  end
13
- subject.should == "What?"
13
+ expect(subject).to eq "What?"
14
14
  end
15
15
  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