handlebars-engine 0.1.0 → 0.2.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/CHANGELOG.md +25 -0
- data/README.md +138 -1
- data/lib/handlebars/engine/init.js +43 -0
- data/lib/handlebars/engine/version.rb +2 -2
- data/lib/handlebars/engine.rb +216 -1
- metadata +34 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b6bc490b95cc268a0cc7ee638660caecbfe9d978a13335b8c3588f89e16c3db
|
4
|
+
data.tar.gz: 014eb30c05c4edba8b9f9a340e3539a8542edabd94954efa189b2611456e10d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a092e8fdae258e997d6afb3505b20a67898caf7d08bfd37efee1a50f663b0e084648902b6d396fe392acb46a8e5587deee277e2ec750f31ce103ad8dd42d1d6b
|
7
|
+
data.tar.gz: 99c271de356730e76c660fb806d8dd619edb0f522951a5e822f6460e0e415c02e20311aab2e5be55e89f9b2ffd161e87b75784726512df26d5c59d002ec06f9a
|
data/CHANGELOG.md
CHANGED
@@ -6,3 +6,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
|
+
|
10
|
+
## [0.2.0] - 2022-01-27
|
11
|
+
|
12
|
+
This is the initial implementation, wrapping the JavaScript Handlebars.
|
13
|
+
|
14
|
+
### Added
|
15
|
+
- `Handlebars::Engine#compile`
|
16
|
+
- `Handlebars::Engine#precompile`
|
17
|
+
- `Handlebars::Engine#template`
|
18
|
+
- `Handlebars::Engine#register_helper`
|
19
|
+
- `Handlebars::Engine#unregister_helper`
|
20
|
+
- `Handlebars::Engine#register_partial`
|
21
|
+
- `Handlebars::Engine#unregister_partial`
|
22
|
+
- `Handlebars::Engine#register_helper_missing`
|
23
|
+
- `Handlebars::Engine#unregister_helper_missing`
|
24
|
+
- `Handlebars::Engine#register_partial_missing`
|
25
|
+
- `Handlebars::Engine#unregister_partial_missing`
|
26
|
+
- `Handlebars::Engine#version`
|
27
|
+
|
28
|
+
## [0.1.0] - 2022-01-13
|
29
|
+
|
30
|
+
This is the initial package.
|
31
|
+
|
32
|
+
### Added
|
33
|
+
- gem init
|
data/README.md
CHANGED
@@ -26,7 +26,144 @@ Or install it yourself as:
|
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
29
|
-
|
29
|
+
### Quick Start
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
handlebars = Handlebars::Engine.new
|
33
|
+
template = handlebars.compile("{{firstname}} {{lastname}}")
|
34
|
+
template.call({ firstname: "Yehuda", lastname: "Katz" })
|
35
|
+
# => "Yehuda Katz"
|
36
|
+
```
|
37
|
+
|
38
|
+
### Custom Helpers
|
39
|
+
|
40
|
+
Handlebars helpers can be accessed from any context in a template. You can
|
41
|
+
register a helper with the `register_helper` method:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
handlebars = Handlebars::Engine.new
|
45
|
+
handlebars.register_helper(:loud) do |ctx, arg, opts|
|
46
|
+
arg.upcase
|
47
|
+
end
|
48
|
+
template = handlebars.compile("{{firstname}} {{loud lastname}}")
|
49
|
+
template.call({ firstname: "Yehuda", lastname: "Katz" })
|
50
|
+
# => "Yehuda KATZ"
|
51
|
+
```
|
52
|
+
|
53
|
+
#### Helper Arguments
|
54
|
+
|
55
|
+
Helpers receive the current context as the first argument of the block.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
handlebars = Handlebars::Engine.new
|
59
|
+
handlebars.register_helper(:full_name) do |ctx, opts|
|
60
|
+
"#{ctx["firstname"]} #{ctx["lastname"]}"
|
61
|
+
end
|
62
|
+
template = handlebars.compile("{{full_name}}")
|
63
|
+
template.call({ firstname: "Yehuda", lastname: "Katz" })
|
64
|
+
# => "Yehuda Katz"
|
65
|
+
```
|
66
|
+
|
67
|
+
Any arguments to the helper are included as individual positional arguments.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
handlebars = Handlebars::Engine.new
|
71
|
+
handlebars.register_helper(:join) do |ctx, *args, opts|
|
72
|
+
args.join(" ")
|
73
|
+
end
|
74
|
+
template = handlebars.compile("{{join firstname lastname}}")
|
75
|
+
template.call({ firstname: "Yehuda", lastname: "Katz" })
|
76
|
+
# => "Yehuda Katz"
|
77
|
+
```
|
78
|
+
|
79
|
+
The last argument is a hash of options.
|
80
|
+
|
81
|
+
See https://handlebarsjs.com/guide/#custom-helpers.
|
82
|
+
|
83
|
+
### Block Helpers
|
84
|
+
|
85
|
+
See https://handlebarsjs.com/guide/#block-helpers.
|
86
|
+
|
87
|
+
### Partials
|
88
|
+
|
89
|
+
Handlebars partials allow for code reuse by creating shared templates.
|
90
|
+
|
91
|
+
You can register a partial using the `register_partial` method:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
handlebars = Handlebars::Engine.new
|
95
|
+
handlebars.register_partial(:person, "{{person.name}} is {{person.age}}.")
|
96
|
+
template = handlebars.compile("{{> person person=.}}")
|
97
|
+
template.call({ name: "Yehuda Katz", age: 20 })
|
98
|
+
# => "Yehuda Katz is 20."
|
99
|
+
```
|
100
|
+
|
101
|
+
See https://handlebarsjs.com/guide/#partials.
|
102
|
+
See https://handlebarsjs.com/guide/partials.html.
|
103
|
+
|
104
|
+
### Hooks
|
105
|
+
|
106
|
+
#### Helper Missing
|
107
|
+
|
108
|
+
This hook is called for a mustache or a block-statement when
|
109
|
+
* a simple mustache-expression is not a registered helper, *and*
|
110
|
+
* it is not a property of the current evaluation context.
|
111
|
+
|
112
|
+
You can add custom handling for those situations by registering a helper with
|
113
|
+
the `register_helper_missing` method:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
handlebars = Handlebars::Engine.new
|
117
|
+
handlebars.register_helper_missing do |ctx, *args, opts|
|
118
|
+
"Missing: #{opts["name"]}(#{args.join(", ")})"
|
119
|
+
end
|
120
|
+
|
121
|
+
template = handlebars.compile("{{foo 2 true}}")
|
122
|
+
template.call
|
123
|
+
# => "Missing: foo(2, true)"
|
124
|
+
|
125
|
+
template = handlebars.compile("{{#foo true}}{{/foo}}")
|
126
|
+
template.call
|
127
|
+
# => "Missing: foo(true)"
|
128
|
+
```
|
129
|
+
|
130
|
+
See https://handlebarsjs.com/guide/hooks.html#helpermissing.
|
131
|
+
|
132
|
+
##### Blocks
|
133
|
+
|
134
|
+
This hook is called for a block-statement when
|
135
|
+
* a block-expression calls a helper that is not registered, *and*
|
136
|
+
* the name is a property of the current evaluation context.
|
137
|
+
|
138
|
+
You can add custom handling for those situations by registering a helper with
|
139
|
+
the `register_helper_missing` method (with a `:block` argument):
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
handlebars = Handlebars::Engine.new
|
143
|
+
handlebars.register_helper_missing(:block) do |ctx, *args, opts|
|
144
|
+
"Missing: #{opts["name"]}(#{args.join(", ")})"
|
145
|
+
end
|
146
|
+
|
147
|
+
template = handlebars.compile("{{#person}}{{name}}{{/person}}")
|
148
|
+
template.call({ person: { name: "Yehuda Katz" } })
|
149
|
+
# => "Missing: person"
|
150
|
+
```
|
151
|
+
|
152
|
+
See https://handlebarsjs.com/guide/hooks.html#blockhelpermissing.
|
153
|
+
|
154
|
+
#### Partial Missing
|
155
|
+
|
156
|
+
This hook is called for a partial that is not registered.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
handlebars = Handlebars::Engine.new
|
160
|
+
handlebars.register_partial_missing do |name|
|
161
|
+
"partial: #{name}"
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
Note: This is not a part of the offical Handlebars API. It is provided for
|
166
|
+
convenience.
|
30
167
|
|
31
168
|
## Changelog
|
32
169
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
var {
|
2
|
+
compile,
|
3
|
+
precompile,
|
4
|
+
registerPartial,
|
5
|
+
unregisterPartial,
|
6
|
+
registerHelper,
|
7
|
+
unregisterHelper,
|
8
|
+
VERSION,
|
9
|
+
} = Handlebars;
|
10
|
+
|
11
|
+
var template = (spec) => {
|
12
|
+
eval(`spec = ${spec}`);
|
13
|
+
return Handlebars.template(spec);
|
14
|
+
};
|
15
|
+
|
16
|
+
var registerPartial = Handlebars.registerPartial.bind(Handlebars);
|
17
|
+
var unregisterPartial = Handlebars.unregisterPartial.bind(Handlebars);
|
18
|
+
|
19
|
+
var registerHelper = (...args) => {
|
20
|
+
const fn = args[args.length - 1];
|
21
|
+
function wrapper(...args) {
|
22
|
+
args.unshift(this);
|
23
|
+
return fn(...args);
|
24
|
+
}
|
25
|
+
args[args.length - 1] = wrapper;
|
26
|
+
return Handlebars.registerHelper(...args);
|
27
|
+
};
|
28
|
+
|
29
|
+
var unregisterHelper = Handlebars.unregisterHelper.bind(Handlebars);
|
30
|
+
|
31
|
+
var partialMissing;
|
32
|
+
|
33
|
+
const partialsHandler = {
|
34
|
+
get(partials, name) {
|
35
|
+
const partial = partials[name] ?? partialMissing?.(name);
|
36
|
+
if (partial) {
|
37
|
+
partials[name] = partial;
|
38
|
+
}
|
39
|
+
return partial;
|
40
|
+
},
|
41
|
+
};
|
42
|
+
|
43
|
+
Handlebars.partials = new Proxy(Handlebars.partials, partialsHandler);
|
data/lib/handlebars/engine.rb
CHANGED
@@ -1,9 +1,224 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "handlebars/source"
|
4
|
+
require "json"
|
5
|
+
require "mini_racer"
|
6
|
+
require "securerandom"
|
3
7
|
require_relative "engine/version"
|
4
8
|
|
5
9
|
module Handlebars
|
6
10
|
# The Handlebars engine.
|
7
|
-
|
11
|
+
#
|
12
|
+
# This API follows the JavaScript API as closely as possible:
|
13
|
+
# https://handlebarsjs.com/api-reference/.
|
14
|
+
class Engine
|
15
|
+
# Creates a new instance.
|
16
|
+
#
|
17
|
+
# @param lazy [true, false] immediately loads and initializes the JavaScript
|
18
|
+
# environment.
|
19
|
+
def initialize(lazy: false)
|
20
|
+
init! unless lazy
|
21
|
+
end
|
22
|
+
|
23
|
+
###################################
|
24
|
+
# Compilation
|
25
|
+
###################################
|
26
|
+
|
27
|
+
# Compiles a template so it can be executed immediately.
|
28
|
+
#
|
29
|
+
# @param template [String] the template string to compile
|
30
|
+
# @param options [Hash] the options
|
31
|
+
# @return [Proc] the template function to call
|
32
|
+
# @see https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options
|
33
|
+
def compile(*args)
|
34
|
+
call(__method__, args, assign: true)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Precompiles a given template so it can be executed without compilation.
|
38
|
+
#
|
39
|
+
# @param template [String] the template string to precompiled
|
40
|
+
# @param options [Hash] the options
|
41
|
+
# @return [String] the precompiled template spec
|
42
|
+
# @see https://handlebarsjs.com/api-reference/compilation.html#handlebars-precompile-template-options
|
43
|
+
def precompile(*args)
|
44
|
+
call(__method__, args)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets up a template that was precompiled with `precompile`.
|
48
|
+
#
|
49
|
+
# @param spec [String] the precompiled template spec
|
50
|
+
# @return [Proc] the template function to call
|
51
|
+
# @see #precompile
|
52
|
+
# @see https://handlebarsjs.com/api-reference/compilation.html#handlebars-template-templatespec
|
53
|
+
def template(*args)
|
54
|
+
call(__method__, args, assign: true)
|
55
|
+
end
|
56
|
+
|
57
|
+
###################################
|
58
|
+
# Runtime
|
59
|
+
###################################
|
60
|
+
|
61
|
+
# Registers helpers accessible by any template in the environment.
|
62
|
+
#
|
63
|
+
# @param name [String, Symbol] the name of the helper
|
64
|
+
# @yieldparam context [Hash] the current context
|
65
|
+
# @yieldparam arguments [Object] the arguments (optional)
|
66
|
+
# @yieldparam options [Hash] the options hash (optional)
|
67
|
+
# @see https://handlebarsjs.com/api-reference/runtime.html#handlebars-registerhelper-name-helper
|
68
|
+
def register_helper(name, &block)
|
69
|
+
attach(name, &block)
|
70
|
+
call(:registerHelper, [name.to_s, name.to_sym], eval: true)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Unregisters a previously registered helper.
|
74
|
+
#
|
75
|
+
# @param name [String, Symbol] the name of the helper
|
76
|
+
# @see https://handlebarsjs.com/api-reference/runtime.html#handlebars-unregisterhelper-name
|
77
|
+
def unregister_helper(name)
|
78
|
+
call(:unregisterHelper, [name])
|
79
|
+
end
|
80
|
+
|
81
|
+
# Registers partials accessible by any template in the environment.
|
82
|
+
#
|
83
|
+
# @param name [String, Symbol] the name of the partial
|
84
|
+
# @param partial [String] the partial template
|
85
|
+
# @see https://handlebarsjs.com/api-reference/runtime.html#handlebars-registerpartial-name-partial
|
86
|
+
def register_partial(name = nil, partial = nil, **partials)
|
87
|
+
partials[name] = partial if name
|
88
|
+
call(:registerPartial, [partials])
|
89
|
+
end
|
90
|
+
|
91
|
+
# Unregisters a previously registered partial.
|
92
|
+
#
|
93
|
+
# @param name [String, Symbol] the name of the partial
|
94
|
+
# @see https://handlebarsjs.com/api-reference/runtime.html#handlebars-unregisterpartial-name
|
95
|
+
def unregister_partial(name)
|
96
|
+
call(:unregisterPartial, [name])
|
97
|
+
end
|
98
|
+
|
99
|
+
###################################
|
100
|
+
# Hooks
|
101
|
+
###################################
|
102
|
+
|
103
|
+
# Registers the hook called when a mustache or a block-statement is missing.
|
104
|
+
#
|
105
|
+
# @param type [Symbol] the type of hook to register (`:basic` or `:block`)
|
106
|
+
# @yieldparam arguments [Object] the arguments (optional)
|
107
|
+
# @yieldparam options [Hash] the options hash (optional)
|
108
|
+
# @see https://handlebarsjs.com/guide/hooks.html#helpermissing
|
109
|
+
def register_helper_missing(type = :basic, &block)
|
110
|
+
name = helper_missing_name(type)
|
111
|
+
register_helper(name, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Unregisters the previously registered hook.
|
115
|
+
#
|
116
|
+
# @param type [Symbol] the type of hook to register (`:basic` or `:block`)
|
117
|
+
# @see https://handlebarsjs.com/guide/hooks.html#helpermissing
|
118
|
+
def unregister_helper_missing(type = :basic)
|
119
|
+
name = helper_missing_name(type)
|
120
|
+
unregister_helper(name)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Registers the hook called when a partial is missing.
|
124
|
+
#
|
125
|
+
# Note: This is not a part of the offical Handlebars API. It is provided for
|
126
|
+
# convenience.
|
127
|
+
#
|
128
|
+
# @yieldparam name [String] the name of the undefined partial
|
129
|
+
def register_partial_missing(&block)
|
130
|
+
attach(:partialMissing, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Unregisters the previously registered hook.
|
134
|
+
def unregister_partial_missing
|
135
|
+
evaluate("delete partialMissing")
|
136
|
+
end
|
137
|
+
|
138
|
+
###################################
|
139
|
+
# Miscellaneous
|
140
|
+
###################################
|
141
|
+
|
142
|
+
# Returns the version of Handlebars.
|
143
|
+
#
|
144
|
+
# @return [String] the Handlebars version.
|
145
|
+
def version
|
146
|
+
evaluate("VERSION")
|
147
|
+
end
|
148
|
+
|
149
|
+
###################################
|
150
|
+
# Private
|
151
|
+
###################################
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def attach(name, &block)
|
156
|
+
init!
|
157
|
+
@context.attach(name.to_s, block)
|
158
|
+
end
|
159
|
+
|
160
|
+
def call(name, args, assign: false, eval: false)
|
161
|
+
init!
|
162
|
+
name = name.to_s
|
163
|
+
|
164
|
+
if assign || eval
|
165
|
+
call_via_eval(name, args, assign: assign)
|
166
|
+
else
|
167
|
+
@context.call(name, *args)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def call_via_eval(name, args, assign: false)
|
172
|
+
args = js_args(args)
|
173
|
+
|
174
|
+
var = assign ? "v#{SecureRandom.alphanumeric}" : nil
|
175
|
+
|
176
|
+
code = "#{name}(#{args.join(", ")})"
|
177
|
+
code = "#{var} = #{code}" if var
|
178
|
+
|
179
|
+
result = evaluate(code)
|
180
|
+
|
181
|
+
if var && result.is_a?(MiniRacer::JavaScriptFunction)
|
182
|
+
result = ->(*a) { @context.call(var, *a) }
|
183
|
+
finalizer = ->(*) { evaluate("delete #{var}") }
|
184
|
+
ObjectSpace.define_finalizer(result, finalizer)
|
185
|
+
end
|
186
|
+
|
187
|
+
result
|
188
|
+
end
|
189
|
+
|
190
|
+
def evaluate(code)
|
191
|
+
@context.eval(code)
|
192
|
+
end
|
193
|
+
|
194
|
+
def helper_missing_name(type)
|
195
|
+
case type
|
196
|
+
when :basic
|
197
|
+
:helperMissing
|
198
|
+
when :block
|
199
|
+
:blockHelperMissing
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def init!
|
204
|
+
return if @init
|
205
|
+
|
206
|
+
@context = MiniRacer::Context.new
|
207
|
+
@context.load(::Handlebars::Source.bundled_path)
|
208
|
+
@context.load(File.absolute_path("engine/init.js", __dir__))
|
209
|
+
|
210
|
+
@init = true
|
211
|
+
end
|
212
|
+
|
213
|
+
def js_args(args)
|
214
|
+
args.map { |arg|
|
215
|
+
case arg
|
216
|
+
when Symbol
|
217
|
+
arg
|
218
|
+
else
|
219
|
+
JSON.generate(arg)
|
220
|
+
end
|
221
|
+
}
|
222
|
+
end
|
8
223
|
end
|
9
224
|
end
|
metadata
CHANGED
@@ -1,18 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: handlebars-engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Gianos
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: handlebars-source
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mini_racer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
description: " A simple interface to Handlebars.js for Ruby.\n"
|
14
42
|
email:
|
15
|
-
- zach.gianos@gmail.com
|
43
|
+
- zach.gianos+git@gmail.com
|
16
44
|
executables:
|
17
45
|
- handlebars
|
18
46
|
extensions: []
|
@@ -23,14 +51,15 @@ files:
|
|
23
51
|
- README.md
|
24
52
|
- exe/handlebars
|
25
53
|
- lib/handlebars/engine.rb
|
54
|
+
- lib/handlebars/engine/init.js
|
26
55
|
- lib/handlebars/engine/version.rb
|
27
56
|
homepage: https://github.com/gi/handlebars-ruby
|
28
57
|
licenses:
|
29
58
|
- MIT
|
30
59
|
metadata:
|
31
60
|
changelog_uri: https://github.com/gi/handlebars-ruby/CHANGELOG.md
|
61
|
+
github_repo: https://github.com/gi/handlebars-ruby
|
32
62
|
homepage_uri: https://github.com/gi/handlebars-ruby
|
33
|
-
rubygems_mfa_required: 'true'
|
34
63
|
source_code_uri: https://github.com/gi/handlebars-ruby
|
35
64
|
post_install_message:
|
36
65
|
rdoc_options: []
|