flavour_saver 4.0.2 → 5.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +14 -0
  3. data/.standard.yml +1 -0
  4. data/CHANGELOG.md +57 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +48 -5
  7. data/README.md +153 -271
  8. data/flavour_saver.gemspec +17 -19
  9. data/lib/flavour_saver/error.rb +5 -0
  10. data/lib/flavour_saver/helpers.rb +19 -18
  11. data/lib/flavour_saver/lexer.rb +123 -50
  12. data/lib/flavour_saver/nodes.rb +107 -19
  13. data/lib/flavour_saver/parser.rb +339 -161
  14. data/lib/flavour_saver/partial.rb +4 -6
  15. data/lib/flavour_saver/rails_partial.rb +10 -4
  16. data/lib/flavour_saver/runtime.rb +76 -81
  17. data/lib/flavour_saver/template.rb +3 -5
  18. data/lib/flavour_saver/version.rb +1 -1
  19. data/lib/flavour_saver.rb +17 -17
  20. data/spec/acceptance/backtrack_spec.rb +9 -9
  21. data/spec/acceptance/comment_spec.rb +7 -7
  22. data/spec/acceptance/custom_block_helper_spec.rb +12 -12
  23. data/spec/acceptance/custom_helper_spec.rb +8 -8
  24. data/spec/acceptance/ensure_no_rce_spec.rb +64 -53
  25. data/spec/acceptance/handlebars_qunit_spec.rb +295 -297
  26. data/spec/acceptance/if_else_spec.rb +6 -6
  27. data/spec/acceptance/multi_level_with_spec.rb +10 -11
  28. data/spec/acceptance/nesting_depth_spec.rb +34 -0
  29. data/spec/acceptance/one_character_identifier_spec.rb +8 -8
  30. data/spec/acceptance/raw_block_spec.rb +7 -7
  31. data/spec/acceptance/sections_spec.rb +17 -14
  32. data/spec/acceptance/segment_literals_spec.rb +5 -5
  33. data/spec/acceptance/simple_expression_spec.rb +9 -9
  34. data/spec/acceptance/subexpression_spec.rb +18 -17
  35. data/spec/acceptance/unless_spec.rb +19 -6
  36. data/spec/lib/flavour_saver/lexer_spec.rb +103 -89
  37. data/spec/lib/flavour_saver/nodes_spec.rb +85 -0
  38. data/spec/lib/flavour_saver/parser_spec.rb +155 -95
  39. data/spec/lib/flavour_saver/runtime_spec.rb +94 -96
  40. data/spec/lib/flavour_saver/template_spec.rb +1 -2
  41. metadata +19 -48
data/README.md CHANGED
@@ -1,376 +1,258 @@
1
1
  # FlavourSaver
2
2
 
3
- [Handlebars.js](http://handlebarsjs.com) without the `.js`
3
+ [![Gem Version](https://badge.fury.io/rb/flavour_saver.svg)](https://rubygems.org/gems/flavour_saver)
4
+ [![CI](https://github.com/FlavourSaver/FlavourSaver/actions/workflows/ci.yml/badge.svg)](https://github.com/FlavourSaver/FlavourSaver/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
6
 
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)
7
+ [Handlebars](https://handlebarsjs.com) templates in pure Ruby. Use the same templates on the server and in the browser.
8
8
 
9
- FlavourSaver is a Ruby-based implementation of the [Handlebars.js](http://handlebarsjs.com)
10
- templating language. FlavourSaver supports Handlebars template rendering natively on
11
- Rails and on other frameworks (such as Sinatra) via Tilt.
12
-
13
- Please use it, break it, and send issues/PR's for improvement.
14
-
15
- ## Status
16
-
17
- This project is currently in maintenance mode. Here's what this means:
18
-
19
- * The maintainers of this project are not actively developing new features
20
- * Issues and pull requests are still welcome
21
- * New versions of the gem will be released as changes are merged
22
-
23
- ## License
24
-
25
- FlavourSaver is Copyright (c) 2013 Resistor Limited and licensed under the terms
26
- of the MIT Public License (see the LICENSE file included with this distribution
27
- for more details).
9
+ - Works with Rails, and with Sinatra or anything else that uses [Tilt](https://github.com/jeremyevans/tilt)
10
+ - One runtime dependency (`tilt`), no native extensions
11
+ - Ruby 3.1+, tested on 3.1 through 4.0
28
12
 
29
13
  ## Installation
30
14
 
31
- Add this line to your application's Gemfile:
32
-
33
- gem 'flavour_saver'
34
-
35
- And then execute:
15
+ ```ruby
16
+ # Gemfile
17
+ gem "flavour_saver"
18
+ ```
36
19
 
37
- $ bundle
20
+ ## Quick start
38
21
 
39
- Or install it yourself as:
22
+ ```ruby
23
+ require "flavour_saver"
40
24
 
41
- $ gem install flavour_saver
25
+ Person = Struct.new(:name, :admin)
42
26
 
43
- ## Usage
27
+ FlavourSaver.evaluate("Hello {{name}}!", Person.new("Ana", true))
28
+ # => "Hello Ana!"
29
+ ```
44
30
 
45
- FlavourSaver provides an interface to the amazing
46
- [Tilt](https://github.com/jeremyevans/tilt) templating library, meaning that it
47
- should work with anything that has Tilt support (Sinatra, etc) and has a
48
- native Rails template handler.
31
+ `FS` is an alias for `FlavourSaver`.
49
32
 
50
- ## Features
33
+ The context can be any object. Templates read its public methods: `{{name}}` calls `context.name`. Use a Struct, a `Data` class, a model or a presenter.
51
34
 
52
- FlavourSaver is in its infancy, your pull requests are greatly appreciated.
35
+ > [!NOTE]
36
+ > A plain Hash doesn't work as the context, because `{{name}}` calls a method. Hash values can be read with segment literals: `{{settings.[theme]}}` calls `settings["theme"]` (string keys only).
53
37
 
54
- Currently supported:
38
+ ### With Tilt
55
39
 
56
- - Full support of Mustache and Handlebars templates.
57
- - Expressions:
58
- - with object-paths (`{{some.method.chain}}`)
59
- - containing object-literals (`{{object.['index'].method}}`):
60
- Ruby's `:[](index)` method is called for literals, making FlavourSaver
61
- compatible with `Hash` and hashlike objects.
62
- - with list arguments (`{{method arg1 "arg2"}}`)
63
- - with hash arguments (`{{method foo=bar bar="baz"}}`)
64
- - with list and hash arguments (`{{method arg1 arg2 foo=bar bar="baz"}}`)
65
- provided that the hash is the last argument.
66
- - Comments (`{{! a comment}}`)
67
- - Expression output is HTML escaped
68
- - Safe expressions
69
- - Expressions wrapped in triple-stashes are not HTML escaped (`{{{an expression}}}`)
70
- - Block expressions
71
- - Simple API for adding block helpers.
72
- - Block expressions with inverse blocks
73
- - Inverse blocks
74
- - Partials
75
- - Raw content (`{{{{raw}}}} not parsed or validated {{{{/raw}}}}`)
76
- - Subexpressions (`{{sum 1 (sum 1 1)}}` returns `3`)
40
+ ```ruby
41
+ template = Tilt["handlebars"].new { "{{greeting}}, {{name}}!" }
42
+ template.render(Struct.new(:greeting, :name).new("Hi", "Ana"))
43
+ # => "Hi, Ana!"
44
+ ```
77
45
 
78
- ## Helpers
46
+ `.hbs` and `.handlebars` files are registered with Tilt, so `Tilt.new("welcome.hbs")` works too.
79
47
 
80
- FlavourSaver implements the following helpers by default:
48
+ ### With Rails
81
49
 
82
- ### #with
50
+ Put templates in `app/views` with a `.hbs` or `.handlebars` extension. See [Rails](#rails).
83
51
 
84
- Yields its argument into the context of the block contents:
52
+ ## Syntax
85
53
 
86
- ```handlebars
87
- {{#with person}}
88
- {{name}}
89
- {{/with}}
90
- ```
54
+ | Syntax | Example |
55
+ | --- | --- |
56
+ | Expression (HTML-escaped) | `{{name}}` |
57
+ | Raw output | `{{{bio}}}` or `{{&bio}}` |
58
+ | Path | `{{author.name}}` |
59
+ | Array index / hash key | `{{posts.[0].title}}`, `{{settings.[theme]}}` |
60
+ | Parent context | `{{../title}}` |
61
+ | Root context | `{{@root.title}}` |
62
+ | Comment | `{{! hidden }}`, `{{!-- hidden --}}` |
63
+ | Section / inverted section | `{{#posts}}…{{/posts}}`, `{{^posts}}none{{/posts}}` |
64
+ | Helper with arguments | `{{link "Home" href="/"}}` |
65
+ | Subexpression | `{{sum 1 (sum 2 3)}}` |
66
+ | Partial | `{{> user_card author}}` |
67
+ | Raw block (not parsed) | `{{{{raw}}}} {{left as is}} {{{{/raw}}}}` |
91
68
 
92
- ### #each
69
+ ## Built-in helpers
93
70
 
94
- Takes a single collection argument and yields the block's contents once
95
- for each member of the collection:
71
+ | Helper | Does |
72
+ | --- | --- |
73
+ | `#if` / `#unless` | Renders the block when the value is truthy / falsy. Supports `{{else}}`. `nil`, `false`, `0` and empty collections are falsy. |
74
+ | `#each` | Renders the block for each item. Exposes `@index`, `@first`, `@last`, and `@key` for hashes. |
75
+ | `#with` | Renders the block with the value as the context. |
76
+ | `this` | The current context. |
77
+ | `log` | Writes to `FlavourSaver.logger` at debug level. |
96
78
 
97
79
  ```handlebars
98
- {{#each people}}
99
- {{name}}
80
+ {{#each posts}}
81
+ {{@index}}. {{title}}{{#if @last}} (latest){{/if}}
100
82
  {{/each}}
101
- ```
102
-
103
- ### #if
104
-
105
- Takes a single argument and yields the contents of the block if that argument
106
- is truthy.
107
83
 
108
- ```handlebars
109
- {{#if person}}
110
- Hi {{person.name}}!
111
- {{/if}}
112
- ```
113
-
114
- It can also handle a special case `{{else}}` expression:
115
-
116
- ```handlebars
117
- {{#if person}}
118
- Hi {{person.name}}!
84
+ {{#if author.admin}}
85
+ Admin
119
86
  {{else}}
120
- Nobody to say hi to.
87
+ Member
121
88
  {{/if}}
122
89
  ```
123
90
 
124
- ### #unless
91
+ On Rails, `FlavourSaver.logger` is `Rails.logger`. Elsewhere, set one before using `log`:
125
92
 
126
- Exactly the same is `#if` but backwards.
127
-
128
- ### this
129
-
130
- In JavaScript `this` is a native keyword, in Ruby not-so-much. FlavourSaver's `this` helper
131
- returns `self`:
132
-
133
- ```handlebars
134
- {{#each names}}
135
- {{this}}
136
- {{/each}}
93
+ ```ruby
94
+ require "logger"
95
+ FlavourSaver.logger = Logger.new($stdout)
137
96
  ```
138
97
 
139
- ### log
140
-
141
- Writes log output. The destination can be changed by assigning a `Logger` instance to
142
- `FlavourSaver.logger=`. On Rails `FlavourSaver.logger` automatically points at
143
- `Rails.logger`.
98
+ ## Custom helpers
144
99
 
145
- ### Adding additional helpers
146
-
147
- Additional helpers can easy be added by calling `FS.register_helper`, eg:
100
+ Register a helper with a block. Positional arguments come first; `key=value` arguments arrive as a final hash with symbol keys.
148
101
 
149
102
  ```ruby
150
- FS.register_helper(:whom) { 'world' }
103
+ FS.register_helper(:shout) { |text| text.upcase }
104
+ FS.register_helper(:greet) { |name, options| "#{options[:greeting]}, #{name}" }
151
105
  ```
152
106
 
153
- Now if you were to render the following template:
154
-
155
107
  ```handlebars
156
- <h1>Hello {{whom}}!</h1>
108
+ {{shout "hi"}} {{! HI }}
109
+ {{greet "Ana" greeting="Hola"}} {{! Hola, Ana }}
157
110
  ```
158
111
 
159
- You would receive the following output:
160
-
161
- ```html
162
- <h1>Hello world!</h1>
163
- ```
112
+ Helper output is HTML-escaped. Return an `html_safe` string (from ActiveSupport) to skip escaping.
164
113
 
165
- ### Adding block helpers
114
+ ### Block helpers
166
115
 
167
- Creating a block helper works exactly like adding a regular helper, except that
168
- the helper implementation can call `yield.contents` one or more times, with an
169
- optional argument setting the context of the block execution:
116
+ A block helper takes the template block as `&block`. `block.call.contents` renders the main section, optionally with a new context. `block.call.inverse` renders the `{{else}}` section.
170
117
 
171
118
  ```ruby
172
- FS.register_helper(:three_times) do
173
- yield.contents
174
- yield.contents
175
- yield.contents
119
+ FS.register_helper(:admins) do |people, &block|
120
+ admins = people.select(&:admin)
121
+ if admins.any?
122
+ admins.map { |person| block.call.contents(person) }.join
123
+ else
124
+ block.call.inverse
125
+ end
176
126
  end
177
127
  ```
178
128
 
179
- Which when called with the following template:
180
-
181
129
  ```handlebars
182
- {{#three_times}}
183
- hello
184
- {{/three_times}}
185
- ```
186
-
187
- would result in the following output:
188
- ```
189
- hello
190
- hello
191
- hello
130
+ {{#admins people}}
131
+ <li>{{name}}</li>
132
+ {{else}}
133
+ <li>No admins</li>
134
+ {{/admins}}
192
135
  ```
193
136
 
194
- Implementing a simple iterator is dead easy:
137
+ You can also register a method. Inside a method, `yield` works the same way as `block.call`:
195
138
 
196
139
  ```ruby
197
- FS.register_helper(:list_people) do |people|
198
- people.each do |person|
199
- yield.contents person
200
- end
140
+ def twice
141
+ yield.contents * 2
201
142
  end
202
- ```
203
-
204
- Which could be used like so:
205
143
 
206
- ```handlebars
207
- {{#list_people people}}
208
- <b>{{name}}<b><br />
209
- Age: {{age}}<br />
210
- Sex: {{sex}}<br />
211
- {{/list_people}}
144
+ FS.register_helper(method(:twice))
212
145
  ```
213
146
 
214
- Block helpers can also contain an `{{else}}` statement, which, when used creates
215
- a second set of block contents (called `inverse`) which can be yielded to the output:
216
-
217
- ```ruby
218
- FS.register_helper(:isFemale) do |person,&block|
219
- if person.sex == 'female'
220
- block.call.contents
221
- else
222
- block.call.inverse
223
- end
224
- end
225
- ```
147
+ ### Subexpressions
226
148
 
227
- You can also register an existing method:
149
+ Wrap a helper call in parentheses to use its result as an argument:
228
150
 
229
151
  ```ruby
230
- def isFemale(person)
231
- if person.sex == 'female'
232
- yield.contents
233
- else
234
- yield.inverse
235
- end
236
- end
237
-
238
- FS.register_helper(method(:isFemale))
152
+ FS.register_helper(:sum) { |a, b| a + b }
239
153
  ```
240
154
 
241
- Which could be used like so:
242
-
243
155
  ```handlebars
244
- {{#isFemale person}}
245
- {{person.name}} is female.
246
- {{else}}
247
- {{person.name}} is male.
248
- {{/isFemale}}
249
- ```
250
-
251
- ### Subexpressions
252
-
253
- 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.
254
-
255
- Below are some examples, utilizing a "sum" helper than adds together two numbers.
256
-
156
+ {{sum (sum 5 10) (sum 2 3)}} {{! 20 }}
157
+ {{chart total=(sum boys girls)}}
257
158
  ```
258
- {{sum (sum 5 10) (sum 2 (sum 1 4))}}
259
- #=> 22
260
159
 
261
- {{#if (sum 1 2) > 2}}its more{{/if}}
262
- #=> its more
160
+ ## Partials
263
161
 
264
- {{#student_heights size=(sum boys girls)}}
162
+ ```ruby
163
+ FS.register_partial(:user_card, "<b>{{name}}</b>")
265
164
  ```
266
165
 
267
- ### Raw Content
268
-
269
- 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.
270
-
271
- ```
272
- {{{{raw}}}}
273
- {{if} this tries to parse, it will break on syntax
274
- {{{{/raw}}}}
275
- => {{if} this tries to parse, it will break on syntax
166
+ ```handlebars
167
+ {{#each people}}{{> user_card this}}{{/each}}
276
168
  ```
277
169
 
278
- 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.
279
-
280
- ### Using Partials
281
-
282
- Handlebars allows you to register a partial either as a function or a string template with
283
- the engine before compiling, FlavourSaver retains this behaviour (with the notable exception
284
- of within Rails - see below).
285
-
286
- To register a partial you call `FlavourSaver.register_partial` with a name and a string:
170
+ A partial can also be a block that receives the context and returns a string:
287
171
 
288
172
  ```ruby
289
- FlavourSaver.register_partial(:my_partial, "{{this}} is a partial")
173
+ FS.register_partial(:user_card) { |person| "<b>#{person.name}</b>" }
290
174
  ```
291
175
 
292
- You can then use this partial within your templates:
176
+ On Rails, partials come from your views instead. See below.
293
177
 
294
- ```handlebars
295
- {{#each people}}{{> my_partial this}}{{/each}}
296
- ```
297
-
298
- ## Using with Rails
299
-
300
- One potential gotcha of using FlavourSaver with Rails is that FlavourSaver doesn't let you
301
- have any access to the controller's instance variables. This is done to maintain compatibility
302
- with the original JavaScript implementation of Handlebars so that templates can be used on
303
- both the server and client side without any change.
304
-
305
- When accessing controller instance variables you should access them by way of a helper method
306
- or a presenter object.
178
+ ## Rails
307
179
 
308
- For example, in `ApplicationController.rb` you may have a `before_filter` which authenticates
309
- the current user's session cookie and stores it in the controller's `@current_user` instance
310
- variable.
180
+ FlavourSaver registers `.hbs` and `.handlebars` template handlers with Action View.
311
181
 
312
- To access this variable you could create a simple helper method in `ApplicationHelpers`:
182
+ **No instance variables.** Templates can't see controller instance variables. This keeps them portable to Handlebars.js. Expose data through helper methods or a presenter:
313
183
 
314
184
  ```ruby
315
- def current_user
316
- @current_user
185
+ # app/helpers/application_helper.rb
186
+ module ApplicationHelper
187
+ def current_user
188
+ Current.user
189
+ end
317
190
  end
318
191
  ```
319
192
 
320
- Which would mean that you are able to access it in your template:
321
-
322
193
  ```handlebars
323
194
  {{#if current_user}}
324
195
  Welcome back, {{current_user.first_name}}!
325
196
  {{/if}}
326
197
  ```
327
198
 
328
- ## Using the Tilt Interface Directly
199
+ **Call view helpers through `@root` inside blocks.** The view is the root context. Inside `{{#each}}` or `{{#with}}`, names are looked up on the current item, so `{{post_url id}}` renders blank. Use `@root` to reach the view:
329
200
 
330
- You can use the registered Tilt interface directly to render template strings with a hash of template variables.
201
+ ```handlebars
202
+ {{#each posts}}
203
+ <a href="{{@root.post_url id}}">{{title}}</a>
204
+ {{/each}}
205
+ ```
331
206
 
332
- The Tilt template's `render` method expects an object that can respond to messages using dot notation. In the following example, the template variable `{{foo}}` will result in a call to `.foo` on the `data` object. For this reason the `data` object can't be a simple hash. A model would work, but if you have a plain old Ruby hash, use it to create a new OpenStruct object, which will provide the dot notation needed.
207
+ **Partials use `render`.** You don't register partials in Rails. The partial syntax maps to Rails partials, and the argument is passed as `object:`:
333
208
 
334
- ```ruby
335
- template = Tilt['handlebars'].new { "{{foo}} {{bar}}" }
336
- data = OpenStruct.new foo: "hello", bar: "world"
209
+ | Template | Rails call |
210
+ | --- | --- |
211
+ | `{{> user_card}}` | `render partial: "user_card", object: <current context>` |
212
+ | `{{> user_card author}}` | `render partial: "user_card", object: author` |
337
213
 
338
- template.render data # => "hello world"
214
+ ## Errors
215
+
216
+ Invalid templates raise a subclass of `FlavourSaver::Error`:
217
+
218
+ ```ruby
219
+ begin
220
+ FS.evaluate("{{#if ok}}unclosed", context)
221
+ rescue FlavourSaver::Error => e
222
+ # FlavourSaver::Parser::NotInLanguage, ::UnbalancedBlockError,
223
+ # or FlavourSaver::Lexer::LexingError
224
+ end
339
225
  ```
340
226
 
341
- ### Special behaviour of Handlebars' partial syntax
227
+ ## Security
342
228
 
343
- In Handlebars.js all partial templates must be pre-registered with the engine before they are
344
- able to be used. When running inside Rails FlavourSaver modifies this behaviour to use Rails'
345
- render partial helper:
229
+ A template can only call registered helpers and the public methods of its context. Methods every object inherits, like `send`, `instance_eval` or `class`, raise `FlavourSaver::ForbiddenMethodException`. Private methods like `system` and `eval` can't be reached at all. Even so, only pass objects you're comfortable exposing to template authors.
346
230
 
347
- ```handlebars
348
- {{> my_partial}}
349
- ```
231
+ ## Not supported yet
350
232
 
351
- Will be translated into:
233
+ These Handlebars features don't parse or don't behave like Handlebars.js yet. Pull requests are welcome.
352
234
 
353
- ```ruby
354
- render :partial => 'my_partial'
355
- ```
235
+ - Whitespace control (`{{~foo~}}`)
236
+ - `{{else if …}}` chains
237
+ - Block parameters (`{{#each items as |item|}}`)
238
+ - `{{else}}` inside `{{#each}}` and `{{#with}}`
239
+ - Inline partials, partial blocks, dynamic partials, and hash arguments on partials
240
+ - The `lookup` helper
241
+ - `./` paths
242
+
243
+ ## Contributing
356
244
 
357
- Handlebars allows you to send a context object into the partial, which sets the execution
358
- context of the partial. In Rails this behaviour would be confusing and non-standard, so
359
- instead any argument passed to the partial is evaluated and passed to the partial's
360
- `:object` argument:
245
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/FlavourSaver/FlavourSaver/issues).
361
246
 
362
- ```handlebars
363
- {{> my_partial my_context}}
247
+ ```sh
248
+ git clone https://github.com/FlavourSaver/FlavourSaver.git
249
+ cd FlavourSaver
250
+ bundle install
251
+ bundle exec rspec
364
252
  ```
365
253
 
366
- ```ruby
367
- render :partial => 'my_partial', :object => my_context
368
- ```
254
+ Changes are listed in the [CHANGELOG](CHANGELOG.md).
369
255
 
370
- ## Contributing
256
+ ## License
371
257
 
372
- 1. Fork it
373
- 2. Create your feature branch (`git checkout -b my-new-feature`)
374
- 3. Commit your changes (`git commit -am 'Added some feature'`)
375
- 4. Push to the branch (`git push origin my-new-feature`)
376
- 5. Create new Pull Request
258
+ MIT. Copyright (c) 2013 Resistor Limited. See [LICENSE](LICENSE).
@@ -1,33 +1,31 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/flavour_saver/version', __FILE__)
1
+ require File.expand_path("../lib/flavour_saver/version", __FILE__)
3
2
 
4
3
  Gem::Specification.new do |gem|
5
- gem.authors = ["Clayton Passmore", "James Harton"]
6
- gem.email = ["ctpassmore+flavoursaver@gmail.com"]
7
- gem.description = %q{FlavourSaver is a pure-ruby implimentation of the Handlebars templating language}
8
- gem.summary = %q{Handlebars.js without the .js}
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",
4
+ gem.authors = ["Clayton Passmore", "James Harton"]
5
+ gem.email = ["ctpassmore+flavoursaver@gmail.com"]
6
+ gem.description = "FlavourSaver is a pure-ruby implementation of the Handlebars templating language"
7
+ gem.summary = "Handlebars.js without the .js"
8
+ gem.homepage = "http://github.com/FlavourSaver/FlavourSaver"
9
+ gem.license = "MIT"
10
+ gem.metadata = {
11
+ "bug_tracker_uri" => "http://github.com/FlavourSaver/FlavourSaver/issues",
12
+ "changelog_uri" => "http://github.com/FlavourSaver/FlavourSaver/blob/master/CHANGELOG.md",
13
+ "homepage_uri" => "http://github.com/FlavourSaver/FlavourSaver",
14
+ "source_code_uri" => "http://github.com/FlavourSaver/FlavourSaver"
16
15
  }
17
16
 
18
- gem.files = `git ls-files`.split($\)
19
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
- gem.name = "flavour_saver"
17
+ gem.files = `git ls-files`.split($\)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
+ gem.name = "flavour_saver"
22
20
  gem.require_paths = ["lib"]
23
- gem.version = FlavourSaver::VERSION
21
+ gem.version = FlavourSaver::VERSION
24
22
 
25
23
  gem.required_ruby_version = ">= 3.1.0"
26
24
 
27
25
  gem.add_development_dependency "rake"
28
26
  gem.add_development_dependency "rspec"
27
+ gem.add_development_dependency "standard"
29
28
  gem.add_development_dependency "activesupport", "~> 7.2.0"
30
29
 
31
- gem.add_dependency "rltk", "< 3.0"
32
30
  gem.add_dependency "tilt", "~> 2.6"
33
31
  end
@@ -0,0 +1,5 @@
1
+ module FlavourSaver
2
+ # Base class for errors raised when a template can't be lexed or parsed.
3
+ # Rescue this to catch every invalid template.
4
+ class Error < StandardError; end
5
+ end