superfluid 0.0.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
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +530 -48
- data/lib/superfluid/configuration.rb +26 -0
- data/lib/superfluid/environment.rb +40 -42
- data/lib/superfluid/filters/container.rb +34 -0
- data/lib/superfluid/registries/filter.rb +58 -37
- data/lib/superfluid/registries/tag.rb +34 -0
- data/lib/superfluid/renderer.rb +8 -2
- data/lib/superfluid/tags/container.rb +14 -0
- data/lib/superfluid.rb +1 -1
- data/superfluid.gemspec +4 -2
- data.tar.gz.sig +0 -0
- metadata +36 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe8974f8f93054605035f4adfc394fe98414fcec3c9db66b58fd957fedbc0797
|
|
4
|
+
data.tar.gz: 95af81f7354548f0a908c429704a2338faafd60bd427d843b3495d045b89835b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1890b05c7e813598d6aed1877b2ce863a31d832a36ff7feef458afc82cada0c424209a6afbe02629ac167917a308c1ccbcfe7a1f28241c2bf9513fb4ff518a75
|
|
7
|
+
data.tar.gz: 85d24dff9714182d95c76edb935316945e24fc402ae203c77dced39b51b3be44782b774b2c868862bbaad5695b1405988064524c304de30c70e19fd9c9e7a9ee
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/README.adoc
CHANGED
|
@@ -3,19 +3,25 @@
|
|
|
3
3
|
:figure-caption!:
|
|
4
4
|
|
|
5
5
|
:command_pattern_link: link:https://alchemists.io/articles/command_pattern[Command Pattern]
|
|
6
|
+
:containable_link: link:https://alchemists.io/projects/containable[Containable]
|
|
6
7
|
:core_link: link:https://alchemists.io/projects/core[Core]
|
|
8
|
+
:functionable_link: link:https://alchemists.io/projects/functionable[Functionable]
|
|
7
9
|
:liquid_link: link:https://shopify.github.io/liquid/[Liquid]
|
|
8
|
-
:
|
|
10
|
+
:struct_link: link:https://alchemists.io/articles/ruby_structs[Struct]
|
|
9
11
|
|
|
10
12
|
= Superfluid
|
|
11
13
|
|
|
14
|
+
Enhances {liquid_link} by smoothing out the rough edges with a strong focus on object composition and functional design. This includes a fully functional filter and tag registry so you can reuse lower level filters and tags within all aspects of your view layer.
|
|
15
|
+
|
|
12
16
|
toc::[]
|
|
13
17
|
|
|
14
18
|
== Features
|
|
15
19
|
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* Uses a customizable default environment with a cleaner Object API.
|
|
21
|
+
* Uses {containable_link} for filter and tag registries.
|
|
22
|
+
* Registers filters via a `Module`, {functionable_link}, commands (see {command_pattern_link} for details), or containers (see {containable_link} for details).
|
|
23
|
+
* Registers tags via classes or containers (see {containable_link} for details).
|
|
24
|
+
* Provides a default renderer for quick rendering of templates and associated data.
|
|
19
25
|
|
|
20
26
|
== Requirements
|
|
21
27
|
|
|
@@ -63,19 +69,48 @@ The quickest way to get started is to create a new instance:
|
|
|
63
69
|
renderer = Superfluid.new
|
|
64
70
|
----
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
Then you can render your template and associated data:
|
|
67
73
|
|
|
68
74
|
[source,ruby]
|
|
69
75
|
----
|
|
70
|
-
renderer.call "
|
|
76
|
+
renderer.call "Hi, this is a {{ value }}.", "value" => "demo"
|
|
71
77
|
----
|
|
72
78
|
|
|
73
79
|
The above will yield the following output:
|
|
74
80
|
|
|
75
81
|
----
|
|
76
|
-
"
|
|
82
|
+
"Hi, this is a demo."
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
The following sections detail how you can configure your environment, register filters, and register tags.
|
|
86
|
+
|
|
87
|
+
=== Configuration
|
|
88
|
+
|
|
89
|
+
The configuration is used by the environment. This is created for you automatically but if you want an instance of the default configuration, use:
|
|
90
|
+
|
|
91
|
+
[source,ruby]
|
|
92
|
+
----
|
|
93
|
+
configuration = Superfluid::Configuration.new
|
|
94
|
+
|
|
95
|
+
#<Struct:Superfluid::Configuration:0x00001780
|
|
96
|
+
default_resource_limits = {},
|
|
97
|
+
error_mode = :strict,
|
|
98
|
+
exception_renderer = #<Proc:0x0000000131200ee0 (lambda)>,
|
|
99
|
+
file_system = #<data Superfluid::Systems::Memory:0x000017b0 templates = {}>,
|
|
100
|
+
filter_registry = #<Superfluid::Registries::Filter:0x000000012f5a1290 @container=Superfluid::Filters::Container, @mode=:strict>,
|
|
101
|
+
tag_registry = #<Superfluid::Registries::Tag:0x000000012f5860d0 @container=Superfluid::Tags::Container>
|
|
102
|
+
>
|
|
77
103
|
----
|
|
78
104
|
|
|
105
|
+
As you can see, the configuration is a {struct_link} which means you can customize using any of these attributes:
|
|
106
|
+
|
|
107
|
+
* `default_resource_limits`: Limits the resources that a template can consume. Default: `{}`.
|
|
108
|
+
* `error_mode`: The error mode (link:https://github.com/Shopify/liquid#error-modes[details]). Default: `:strict`.
|
|
109
|
+
* `exception_renderer`: The function used to render error messages. Default: `Core::Identity` (see {core_link}).
|
|
110
|
+
* `file_system`: The file system. Default: `Superfluid::Systems::Memory.new`.
|
|
111
|
+
* `filter_registry`: The filter registry. Default: `Superfluid::Registries::Filter.new`.
|
|
112
|
+
* `tag_registry`: The tag registry. Default: `Superfluid::Registries::Tag.new`.
|
|
113
|
+
|
|
79
114
|
=== Environments
|
|
80
115
|
|
|
81
116
|
To build the default environment, use:
|
|
@@ -85,100 +120,547 @@ To build the default environment, use:
|
|
|
85
120
|
environment = Superfluid.build
|
|
86
121
|
----
|
|
87
122
|
|
|
88
|
-
|
|
123
|
+
To build the default environment directly (which is what the above does), use:
|
|
89
124
|
|
|
90
125
|
[source,ruby]
|
|
91
126
|
----
|
|
92
|
-
environment = Superfluid::Environment.
|
|
127
|
+
environment = Superfluid::Environment.new
|
|
93
128
|
----
|
|
94
129
|
|
|
95
|
-
|
|
130
|
+
If you'd like to build a custom environment, you can do this several ways. For demonstration purposes, only the `error_mode` is used below but all configuration attributes are accepted:
|
|
96
131
|
|
|
97
132
|
[source,ruby]
|
|
98
133
|
----
|
|
99
|
-
environment = Superfluid
|
|
134
|
+
environment = Superfluid.build error_mode: :lax
|
|
135
|
+
environment = Superfluid::Environment.for error_mode: :lax
|
|
136
|
+
environment = Superfluid::Environment.new Superfluid::Configuration[error_mode: :lax]
|
|
100
137
|
----
|
|
101
138
|
|
|
102
|
-
You can
|
|
139
|
+
You can also freeze the environment -- which also freezes the filter and tag registries -- once you've registered any/all filters and tags as follows:
|
|
103
140
|
|
|
104
141
|
[source,ruby]
|
|
105
142
|
----
|
|
106
143
|
environment.freeze
|
|
107
144
|
----
|
|
108
145
|
|
|
109
|
-
|
|
146
|
+
=== Systems
|
|
110
147
|
|
|
111
|
-
|
|
148
|
+
These are the file systems used to obtain templates for use within your custom tags. By default, an in memory system is provided for you: `Superfluid::Systems::Memory`. {liquid_link} also provides it's own systems as well. Each are described below.
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
150
|
+
==== Memory
|
|
151
|
+
|
|
152
|
+
Provided by this gem. Use to access templates via an in memory file system. This is enabled for you be default when using `Superfluid.new` and works in conjunction with the `template` tag which is also provided by this gem (see below for details). Again, this system is provided for you but you can be explicit if desired:
|
|
153
|
+
|
|
154
|
+
[source,ruby]
|
|
155
|
+
----
|
|
156
|
+
environment = Superfluid.build file_system: Superfluid::Systems::Memory.new
|
|
157
|
+
renderer = Superfluid.new(environment:)
|
|
158
|
+
|
|
159
|
+
template = <<~CONTENT
|
|
160
|
+
{% template demo %}
|
|
161
|
+
Hi, this is a {{ subject }}.
|
|
162
|
+
{% endtemplate %}
|
|
163
|
+
|
|
164
|
+
{% render "demo", subject: "demo" %}
|
|
165
|
+
CONTENT
|
|
166
|
+
|
|
167
|
+
renderer.call template
|
|
168
|
+
# "Hi, this is a demo."
|
|
169
|
+
----
|
|
170
|
+
|
|
171
|
+
==== Local
|
|
172
|
+
|
|
173
|
+
Provided by {liquid_link}. Use to access templates via your local file system within a specific root folder. Usage:
|
|
174
|
+
|
|
175
|
+
[source,ruby]
|
|
176
|
+
----
|
|
177
|
+
Pathname("templates").tap(&:mkdir).join("_demo.liquid").tap { it.write "{{ salutation }}" }
|
|
178
|
+
|
|
179
|
+
environment = Superfluid.build file_system: Liquid::LocalFileSystem.new(Pathname("templates"))
|
|
180
|
+
renderer = Superfluid.new(environment:)
|
|
181
|
+
|
|
182
|
+
renderer.call(%({% include "demo" %}, this is a demo), {"salutation" => "Hi"})
|
|
183
|
+
# "Hi, this is a demo"
|
|
184
|
+
----
|
|
185
|
+
|
|
186
|
+
The above configures the local system to look for partials (templates) within the `templates` folder. You then reference them via the `include` tag.
|
|
187
|
+
|
|
188
|
+
By default, this system uses link:https://docs.ruby-lang.org/en/master/Kernel.html#method-i-format[Kernel#format] syntax to find your partials (i.e. `"_%s.liquid"`). If you don't like this pattern, you can configure differently when creating a new instance. Example:
|
|
189
|
+
|
|
190
|
+
[source,ruby]
|
|
191
|
+
----
|
|
192
|
+
Liquid::LocalFileSystem.new "/a/path", "%s.html"
|
|
193
|
+
----
|
|
194
|
+
|
|
195
|
+
==== Blank
|
|
196
|
+
|
|
197
|
+
Provided by {liquid_link}. This is an abstract class -- meant for subclassing -- because it will fail with a `FileSystemError` when `read_template_file` isn't implemented. Usage:
|
|
198
|
+
|
|
199
|
+
[source,ruby]
|
|
200
|
+
----
|
|
201
|
+
environment = Superfluid.build file_system: Liquid::BlankFileSystem.new(Pathname("templates"))
|
|
202
|
+
renderer = Superfluid.new(environment:)
|
|
203
|
+
----
|
|
204
|
+
|
|
205
|
+
The above does nothing and is benign.
|
|
119
206
|
|
|
120
207
|
=== Filters
|
|
121
208
|
|
|
122
|
-
By default, {liquid_link} only allows registration of filters as instance methods on a namespace (`Module`). Unfortunately, this leads to terrible design, bad practices, and unnecessarily hard to test objects. You can still register namespaces but this gem encourages the use of commands (see {command_pattern_link}) which allows you tap into Functional Programming by using procs, lambdas, and any object that responds to `#call`. This
|
|
209
|
+
By default, {liquid_link} only allows registration of filters as instance methods on a namespace (`Module`). Unfortunately, this leads to terrible design, bad practices, and unnecessarily hard to test objects. You can still register namespaces but this gem encourages the use of commands (see {command_pattern_link} for further details) and containers via {containable_link} which allows you tap into Functional Programming by using procs, lambdas, and any object that responds to `#call`. This allows you to build -- and reuse -- more powerful filters. The following details the different kinds of filters you can use, each broken down by category.
|
|
210
|
+
|
|
211
|
+
==== Defaults
|
|
212
|
+
|
|
213
|
+
Default filters are provided for you automatically and can be viewed via your environment. Example:
|
|
214
|
+
|
|
215
|
+
[source,ruby]
|
|
216
|
+
----
|
|
217
|
+
environment = Superfluid.build
|
|
218
|
+
environment.filter_registry.names
|
|
219
|
+
----
|
|
220
|
+
|
|
221
|
+
The above will produce a list of all registered filters. These including all filters provided by {liquid_link} and those unique to this gem which are:
|
|
222
|
+
|
|
223
|
+
===== `jsonify`
|
|
224
|
+
|
|
225
|
+
Renders objects as JSON. Example:
|
|
226
|
+
|
|
227
|
+
[source,ruby]
|
|
228
|
+
----
|
|
229
|
+
renderer.call(%({{ data | jsonify }}), {"data" => {"a" => 1}})
|
|
230
|
+
# "{\"a\":1}"
|
|
231
|
+
----
|
|
232
|
+
|
|
233
|
+
===== `parse_json`
|
|
234
|
+
|
|
235
|
+
Parses JSON as a primitive. Example:
|
|
236
|
+
|
|
237
|
+
[source,ruby]
|
|
238
|
+
----
|
|
239
|
+
renderer.call(
|
|
240
|
+
"{% assign data = payload | parse_json %}{{ data.one }}",
|
|
241
|
+
{"payload" => %({"one": 1, "two": 2})}
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# "1"
|
|
245
|
+
----
|
|
246
|
+
|
|
247
|
+
===== `pluralize`
|
|
248
|
+
|
|
249
|
+
Renders a plural string. Example:
|
|
250
|
+
|
|
251
|
+
[source,ruby]
|
|
252
|
+
----
|
|
253
|
+
renderer.call(%({{ text | pluralize: 0 }}), {"text" => "apple"})
|
|
254
|
+
# "0 apples"
|
|
255
|
+
|
|
256
|
+
renderer.call(%({{ text | pluralize: 1 }}), {"text" => "apple"})
|
|
257
|
+
# "1 apple"
|
|
258
|
+
|
|
259
|
+
renderer.call(%({{ text | pluralize: 2 }}), {"text" => "apple"})
|
|
260
|
+
# "2 apples"
|
|
261
|
+
|
|
262
|
+
renderer.call(%({{ text | pluralize: 3, "i", "us" }}), {"text" => "octopus"})
|
|
263
|
+
# "3 octopi"
|
|
264
|
+
----
|
|
265
|
+
|
|
266
|
+
===== `singularize`
|
|
267
|
+
|
|
268
|
+
Renders a singular string. Example:
|
|
269
|
+
|
|
270
|
+
[source,ruby]
|
|
271
|
+
----
|
|
272
|
+
renderer.call(%({{ text | singularize: 0 }}), {"text" => "apples"})
|
|
273
|
+
# "0 apples"
|
|
274
|
+
|
|
275
|
+
renderer.call(%({{ text | singularize: 1 }}), {"text" => "apples"})
|
|
276
|
+
# "1 apple"
|
|
277
|
+
|
|
278
|
+
renderer.call(%({{ text | singularize: 2 }}), {"text" => "apples"})
|
|
279
|
+
# "2 apples"
|
|
280
|
+
|
|
281
|
+
renderer.call(%({{ text | singularize: 1, "i", "us" }}), {"text" => "octopi"})
|
|
282
|
+
# "1 octopus"
|
|
283
|
+
----
|
|
284
|
+
|
|
285
|
+
===== `trim_end`
|
|
286
|
+
|
|
287
|
+
Renders string with trimmed end. Example:
|
|
123
288
|
|
|
124
289
|
[source,ruby]
|
|
125
290
|
----
|
|
126
|
-
|
|
291
|
+
renderer.call(%({{ text | trim_end: 10 }}), {"text" => "A demo."})
|
|
292
|
+
# "A demo."
|
|
127
293
|
|
|
128
|
-
|
|
129
|
-
|
|
294
|
+
renderer.call(%({{ text | trim_end: 10 }}), {"text" => "This is a demo."})
|
|
295
|
+
# "This is..."
|
|
296
|
+
|
|
297
|
+
renderer.call(%({{ text | trim_end: 10, "", "---" }}), {"text" => "This is a demo."})
|
|
298
|
+
# "This is---"
|
|
299
|
+
|
|
300
|
+
renderer.call(%({{ text | trim_end: 10, "", "" }}), {"text" => "This is a demo."})
|
|
301
|
+
# "This is a"
|
|
302
|
+
----
|
|
303
|
+
|
|
304
|
+
===== `trim_middle`
|
|
305
|
+
|
|
306
|
+
Renders string with trimmed middle. Example:
|
|
307
|
+
|
|
308
|
+
[source,ruby]
|
|
309
|
+
----
|
|
310
|
+
renderer.call(%({{ text | trim_middle: 20 }}), {"text" => "This is a demo."})
|
|
311
|
+
# "A demo."
|
|
130
312
|
|
|
131
|
-
|
|
313
|
+
renderer.call(%({{ text | trim_middle: 13 }}), {"text" => "This is a demo."})
|
|
314
|
+
# "This...demo."
|
|
315
|
+
|
|
316
|
+
renderer.call(%({{ text | trim_middle: 13, "--" }}), {"text" => "This is a demo."})
|
|
317
|
+
# "This--demo."
|
|
318
|
+
----
|
|
319
|
+
|
|
320
|
+
==== Modules
|
|
321
|
+
|
|
322
|
+
Module instance methods are what {liquid_link} supports by default. You only need to define instance methods within a module and then register your module. Example:
|
|
323
|
+
|
|
324
|
+
[source,ruby]
|
|
325
|
+
----
|
|
326
|
+
# Namespaces
|
|
327
|
+
module Primary
|
|
328
|
+
def echo(text) = text
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
module Secondary
|
|
332
|
+
def capitalize(text) = text.capitalize
|
|
333
|
+
|
|
334
|
+
def suffix(text, count = 1) = "#{text}-#{count}"
|
|
132
335
|
end
|
|
133
336
|
|
|
134
|
-
|
|
135
|
-
|
|
337
|
+
# Renderer
|
|
338
|
+
renderer = Superfluid.new { it.register_filters Primary, Secondary }
|
|
339
|
+
|
|
340
|
+
# Results
|
|
341
|
+
renderer.call %({{ "demo" | echo }}) # "demo"
|
|
342
|
+
renderer.call %({{ "demo" | capitalize }}) # "Demo"
|
|
343
|
+
renderer.call %({{ "demo" | suffix }}) # "demo-1"
|
|
344
|
+
renderer.call %({{ "demo" | suffix: 2 }}) # "demo-2"
|
|
345
|
+
----
|
|
346
|
+
|
|
347
|
+
As you can see, all three methods via the two namespaces are properly registered and immediately available for use. You'll also notice multiple namespaces were registered at once but you can register them individually and/or chain them. Examples:
|
|
348
|
+
|
|
349
|
+
[source,ruby]
|
|
350
|
+
----
|
|
351
|
+
Superfluid.new do |environment|
|
|
352
|
+
# Single.
|
|
353
|
+
environment.register_filter Primary
|
|
136
354
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
355
|
+
# Multiple.
|
|
356
|
+
environment.register_filters Primary, Secondary
|
|
357
|
+
|
|
358
|
+
# Chained.
|
|
359
|
+
environment.register_filter(Primary)
|
|
360
|
+
.register_filter(Secondary)
|
|
361
|
+
.register_filters(Primary, Secondary)
|
|
362
|
+
end
|
|
363
|
+
----
|
|
364
|
+
|
|
365
|
+
==== Functionables
|
|
366
|
+
|
|
367
|
+
{functionable_link} modules are identical to the module examples, as shown above, except you require and extend instead. Example:
|
|
368
|
+
|
|
369
|
+
[source,ruby]
|
|
370
|
+
----
|
|
371
|
+
require "functionable"
|
|
372
|
+
|
|
373
|
+
# Namespaces
|
|
374
|
+
module Primary
|
|
375
|
+
extend Functionable
|
|
376
|
+
|
|
377
|
+
def echo(text) = text
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
module Secondary
|
|
381
|
+
extend Functionable
|
|
382
|
+
|
|
383
|
+
def capitalize(text) = text.capitalize
|
|
384
|
+
|
|
385
|
+
def suffix(text, count = 1) = "#{text}-#{count}"
|
|
386
|
+
end
|
|
387
|
+
----
|
|
388
|
+
|
|
389
|
+
This allows you to use purely functionable methods (see {functionable_link} documentation for details). At this point, you can register your functionable namespaces as follows:
|
|
390
|
+
|
|
391
|
+
[source,ruby]
|
|
392
|
+
----
|
|
393
|
+
# Renderer
|
|
394
|
+
renderer = Superfluid.new { it.register_filters Primary, Secondary }
|
|
395
|
+
|
|
396
|
+
# Results
|
|
397
|
+
renderer.call %({{ "demo" | echo }}) # "demo"
|
|
398
|
+
renderer.call %({{ "demo" | capitalize }}) # "Demo"
|
|
399
|
+
renderer.call %({{ "demo" | suffix }}) # "demo-1"
|
|
400
|
+
renderer.call %({{ "demo" | suffix: 2 }}) # "demo-2"
|
|
401
|
+
----
|
|
402
|
+
|
|
403
|
+
Registration is identical to modules:
|
|
404
|
+
|
|
405
|
+
[source,ruby]
|
|
406
|
+
----
|
|
407
|
+
Superfluid.new do |environment|
|
|
408
|
+
# Single.
|
|
409
|
+
environment.register_filter Primary
|
|
410
|
+
|
|
411
|
+
# Multiple.
|
|
412
|
+
environment.register_filters Primary, Secondary
|
|
413
|
+
|
|
414
|
+
# Chained.
|
|
415
|
+
environment.register_filter(Primary)
|
|
416
|
+
.register_filter(Secondary)
|
|
417
|
+
.register_filters(Primary, Secondary)
|
|
418
|
+
end
|
|
419
|
+
----
|
|
420
|
+
|
|
421
|
+
==== Procs and Lambdas
|
|
422
|
+
|
|
423
|
+
Procs and lambdas are not supported by {liquid_link} but are via this gem. Example:
|
|
424
|
+
|
|
425
|
+
[source,ruby]
|
|
426
|
+
----
|
|
427
|
+
# Setup
|
|
428
|
+
|
|
429
|
+
echo = proc { it }
|
|
430
|
+
capitalize = -> text { text.capitalize }
|
|
431
|
+
suffix = -> text, count = 1 { "#{text}-#{count}" }
|
|
432
|
+
|
|
433
|
+
# Renderer
|
|
434
|
+
renderer = Superfluid.new { it.register_filters echo:, capitalize:, suffix: }
|
|
435
|
+
|
|
436
|
+
# Results
|
|
437
|
+
|
|
438
|
+
renderer.call %({{ "demo" | echo }}) # "demo"
|
|
439
|
+
renderer.call %({{ "demo" | capitalize }}) # "Demo"
|
|
440
|
+
renderer.call %({{ "demo" | suffix }}) # "demo-1"
|
|
441
|
+
renderer.call %({{ "demo" | suffix: 2 }}) # "demo-2"
|
|
442
|
+
----
|
|
443
|
+
|
|
444
|
+
Registration can be singular, plural, or chained:
|
|
445
|
+
|
|
446
|
+
[source,ruby]
|
|
447
|
+
----
|
|
448
|
+
renderer = Superfluid.new do |environment|
|
|
449
|
+
# Single.
|
|
450
|
+
environment.register_filter(echo:)
|
|
451
|
+
|
|
452
|
+
# Multiple.
|
|
453
|
+
environment.register_filters(echo:, capitalize:, suffix:)
|
|
454
|
+
|
|
455
|
+
# Chained.
|
|
456
|
+
environment.register_filter(echo:)
|
|
457
|
+
.register_filter(capitalize:)
|
|
458
|
+
.register_filters(echo:, capitalize:, suffix:)
|
|
459
|
+
end
|
|
460
|
+
----
|
|
461
|
+
|
|
462
|
+
==== Classes
|
|
463
|
+
|
|
464
|
+
Classes, especially composable classes, are not supported by {liquid_link} but are via this gem. You only need to ensure you include `Core::Composable` and implement the `#call` method. Example:
|
|
465
|
+
|
|
466
|
+
[source,ruby]
|
|
467
|
+
----
|
|
468
|
+
require "core"
|
|
469
|
+
|
|
470
|
+
class Suffixer
|
|
471
|
+
include Core::Composable
|
|
472
|
+
|
|
473
|
+
def initialize default: 1
|
|
474
|
+
@default = default
|
|
140
475
|
end
|
|
141
476
|
|
|
142
|
-
def call(
|
|
477
|
+
def call(text, suffix = default) = "#{text}-#{suffix}"
|
|
143
478
|
|
|
144
479
|
private
|
|
145
480
|
|
|
146
|
-
attr_reader :
|
|
481
|
+
attr_reader :default
|
|
147
482
|
end
|
|
483
|
+
----
|
|
484
|
+
|
|
485
|
+
Now you can register and render with the above class as follows:
|
|
486
|
+
|
|
487
|
+
[source,ruby]
|
|
488
|
+
----
|
|
489
|
+
# Renderer
|
|
490
|
+
renderer = Superfluid.new { it.register_filters suffix: Suffixer.new }
|
|
491
|
+
|
|
492
|
+
# Results
|
|
493
|
+
|
|
494
|
+
renderer.call %({{ "demo" | suffix }}) # "demo-1"
|
|
495
|
+
renderer.call %({{ "demo" | suffix: 2 }}) # "demo-2"
|
|
496
|
+
----
|
|
497
|
+
|
|
498
|
+
Registration can be singular, plural, or chained:
|
|
499
|
+
|
|
500
|
+
[source,ruby]
|
|
501
|
+
----
|
|
502
|
+
suffix = Suffixer.new
|
|
503
|
+
|
|
504
|
+
renderer = Superfluid.new do |environment|
|
|
505
|
+
# Single.
|
|
506
|
+
environment.register_filter(suffix:)
|
|
507
|
+
|
|
508
|
+
# Multiple.
|
|
509
|
+
environment.register_filters(suffix:)
|
|
510
|
+
|
|
511
|
+
# Chained.
|
|
512
|
+
environment.register_filter(suffix:)
|
|
513
|
+
.register_filters(suffix:)
|
|
514
|
+
end
|
|
515
|
+
----
|
|
516
|
+
|
|
517
|
+
==== Containers
|
|
518
|
+
|
|
519
|
+
Containers are not supported by {liquid_link} but are via this gem using {containable_link}. To use, create a container and then register your dependencies:
|
|
520
|
+
|
|
521
|
+
[source,ruby]
|
|
522
|
+
----
|
|
523
|
+
require "containable"
|
|
524
|
+
|
|
525
|
+
module Container
|
|
526
|
+
extend Containable
|
|
148
527
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.register_filter(primary, four: function_a, sayer: Sayer.new)
|
|
152
|
-
.register_filters(primary, secondary, four: function_a, five: function_b)
|
|
528
|
+
register(:echo) { |text| text }
|
|
529
|
+
register(:suffix) { |text, count = 1| "#{text}-#{count}" }
|
|
153
530
|
end
|
|
154
531
|
----
|
|
155
532
|
|
|
156
|
-
|
|
533
|
+
Now you can merge the container as follows:
|
|
534
|
+
|
|
535
|
+
[source,ruby]
|
|
536
|
+
----
|
|
537
|
+
renderer = Superfluid.new { it.merge_filters Container }
|
|
538
|
+
----
|
|
539
|
+
|
|
540
|
+
You can also selectively merge by supplying only the filters you care about from the container:
|
|
541
|
+
|
|
542
|
+
[source,ruby]
|
|
543
|
+
----
|
|
544
|
+
renderer = Superfluid.new { it.merge_filters Container, :suffix }
|
|
545
|
+
----
|
|
546
|
+
|
|
547
|
+
Once registered, then you can use as follows:
|
|
548
|
+
|
|
549
|
+
[source,ruby]
|
|
550
|
+
----
|
|
551
|
+
renderer.call %({{ "demo" | echo }}) # "demo"
|
|
552
|
+
renderer.call %({{ "demo" | suffix }}) # "demo-1"
|
|
553
|
+
renderer.call %({{ "demo" | suffix: 2 }}) # "demo-2"
|
|
554
|
+
----
|
|
555
|
+
|
|
556
|
+
With containers, you have the full capabilities of the {containable_link}. The above only scratches the surface of what's possible.
|
|
157
557
|
|
|
158
558
|
=== Tags
|
|
159
559
|
|
|
160
|
-
As per {liquid_link} documentation, tags only need to inherit from `Liquid::Tag` and be initialized with `tag_name`, `factor`, and `tokens
|
|
560
|
+
As per {liquid_link} documentation, tags only need to inherit from `Liquid::Tag` and be initialized with `tag_name`, `factor`, and `tokens` parameters. Then you can implement a render method which accepts a `context`. Example:
|
|
161
561
|
|
|
162
562
|
[source,ruby]
|
|
163
563
|
----
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
end
|
|
564
|
+
# Tag
|
|
565
|
+
class Sample < Liquid::Block
|
|
566
|
+
def render(context) = super.sub("<placeholder>", rand(100).to_s)
|
|
567
|
+
end
|
|
169
568
|
|
|
170
|
-
|
|
569
|
+
# Renderer
|
|
570
|
+
renderer = Superfluid.new { it.register_tag :sample, Sample }
|
|
171
571
|
|
|
172
|
-
|
|
572
|
+
# Results
|
|
573
|
+
renderer.call "{% sample %}Your value is: <placeholder>.{% endsample %}"
|
|
574
|
+
# "Your value is: 80."
|
|
575
|
+
----
|
|
576
|
+
|
|
577
|
+
You can also register single or multiple tags at once:
|
|
578
|
+
|
|
579
|
+
[source,ruby]
|
|
580
|
+
----
|
|
581
|
+
renderer = Superfluid.new do |environment|
|
|
582
|
+
# Single.
|
|
583
|
+
environment.register_tag(:sample, Sample)
|
|
584
|
+
|
|
585
|
+
# Multiple.
|
|
586
|
+
environment.register_tags(one: Sample, two: Sample)
|
|
173
587
|
|
|
174
|
-
|
|
588
|
+
# Chained.
|
|
589
|
+
environment.register_tag(:sample, Sample)
|
|
590
|
+
.register_tags(one: Sample, two: Sample)
|
|
175
591
|
end
|
|
592
|
+
----
|
|
593
|
+
|
|
594
|
+
==== Defaults
|
|
595
|
+
|
|
596
|
+
You have access to all tags supported by {liquid_link} including the tags provided by this gem (which is only the `template` tag at the moment).
|
|
597
|
+
|
|
598
|
+
===== `template`
|
|
599
|
+
|
|
600
|
+
This tag must be used with the `Memory` system (see _Systems_, mentioned earlier, for details). You can use this tag as follows:
|
|
601
|
+
|
|
602
|
+
[source,ruby]
|
|
603
|
+
----
|
|
604
|
+
renderer = Superfluid.new
|
|
605
|
+
|
|
606
|
+
template = <<~CONTENT
|
|
607
|
+
{% template demo %}
|
|
608
|
+
Hi, this is a {{ subject }}.
|
|
609
|
+
{% endtemplate %}
|
|
610
|
+
|
|
611
|
+
{% render "demo", subject: "demo" %}
|
|
612
|
+
CONTENT
|
|
613
|
+
|
|
614
|
+
renderer.call template
|
|
615
|
+
# "Hi, this is a demo."
|
|
616
|
+
----
|
|
617
|
+
|
|
618
|
+
As you can see this tag requires two steps:
|
|
619
|
+
|
|
620
|
+
. Define by using the `template` tag followed by a unique name for your template (in this case: `demo`). You can then use any filters and/or additional tags with your template. Finally, close the tag with `endtemplate`.
|
|
621
|
+
. Render your template by using `render` followed by the unique name of your template (i.e. `demo`) and any attributes you need to provide to the template.
|
|
176
622
|
|
|
177
|
-
|
|
178
|
-
|
|
623
|
+
The above allows you to define multiple templates for rendering later. Great for situations where you want to organize your in memory code for final later rendering.
|
|
624
|
+
|
|
625
|
+
==== Containers
|
|
626
|
+
|
|
627
|
+
Containers are not supported by {liquid_link} but are via this gem using {containable_link}. To use, create a container and then register your dependencies:
|
|
628
|
+
|
|
629
|
+
[source,ruby]
|
|
630
|
+
----
|
|
631
|
+
require "containable"
|
|
632
|
+
|
|
633
|
+
module Container
|
|
634
|
+
extend Containable
|
|
635
|
+
|
|
636
|
+
register :sample, Sample
|
|
179
637
|
end
|
|
180
638
|
----
|
|
181
639
|
|
|
640
|
+
Now you can merge the container as follows:
|
|
641
|
+
|
|
642
|
+
[source,ruby]
|
|
643
|
+
----
|
|
644
|
+
renderer = Superfluid.new { it.merge_tags Container }
|
|
645
|
+
----
|
|
646
|
+
|
|
647
|
+
You can also selectively merge by supplying only the tags you care about from the container:
|
|
648
|
+
|
|
649
|
+
[source,ruby]
|
|
650
|
+
----
|
|
651
|
+
renderer = Superfluid.new { it.merge_tags Container, :sample }
|
|
652
|
+
----
|
|
653
|
+
|
|
654
|
+
Once registered, then you can use as follows:
|
|
655
|
+
|
|
656
|
+
[source,ruby]
|
|
657
|
+
----
|
|
658
|
+
renderer.call "{% sample %}Your value is: <placeholder>.{% endsample %}"
|
|
659
|
+
# "Your value is: 85."
|
|
660
|
+
----
|
|
661
|
+
|
|
662
|
+
With containers, you have the full capabilities of the {containable_link}. The above only scratches the surface of what's possible.
|
|
663
|
+
|
|
182
664
|
== Development
|
|
183
665
|
|
|
184
666
|
To contribute, run:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "core"
|
|
4
|
+
|
|
5
|
+
module Superfluid
|
|
6
|
+
# The default configuration.
|
|
7
|
+
Configuration = Struct.new(
|
|
8
|
+
:default_resource_limits,
|
|
9
|
+
:error_mode,
|
|
10
|
+
:exception_renderer,
|
|
11
|
+
:file_system,
|
|
12
|
+
:filter_registry,
|
|
13
|
+
:tag_registry
|
|
14
|
+
) do
|
|
15
|
+
def initialize(**)
|
|
16
|
+
super
|
|
17
|
+
|
|
18
|
+
self[:default_resource_limits] ||= Core::EMPTY_HASH
|
|
19
|
+
self[:error_mode] ||= :strict
|
|
20
|
+
self[:exception_renderer] ||= Core::Identity
|
|
21
|
+
self[:file_system] ||= Systems::Memory.new
|
|
22
|
+
self[:filter_registry] ||= Registries::Filter.new mode: error_mode
|
|
23
|
+
self[:tag_registry] ||= Registries::Tag.new
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,84 +1,82 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "core"
|
|
4
|
+
require "forwardable"
|
|
4
5
|
|
|
5
6
|
module Superfluid
|
|
6
|
-
# The default
|
|
7
|
-
Environment
|
|
8
|
-
|
|
9
|
-
:error_mode,
|
|
10
|
-
:exception_renderer,
|
|
11
|
-
:file_system,
|
|
12
|
-
:filter_registry,
|
|
13
|
-
:tags
|
|
14
|
-
) do
|
|
15
|
-
def self.for(**)
|
|
16
|
-
instance = new(**)
|
|
17
|
-
|
|
18
|
-
yield instance if block_given?
|
|
19
|
-
|
|
20
|
-
instance.freeze
|
|
21
|
-
end
|
|
7
|
+
# The default environment.
|
|
8
|
+
class Environment
|
|
9
|
+
extend Forwardable
|
|
22
10
|
|
|
23
11
|
def self.default
|
|
24
12
|
@default ||= new
|
|
25
13
|
end
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
delegate Configuration.members => :configuration
|
|
16
|
+
delegate Configuration.members.map { :"#{it}=" } => :configuration
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
self[:error_mode] ||= :strict
|
|
32
|
-
self[:exception_renderer] ||= Core::Identity
|
|
33
|
-
self[:file_system] ||= Systems::Memory.new
|
|
34
|
-
self[:filter_registry] ||= Registries::Filter.for mode: error_mode
|
|
35
|
-
self[:tags] ||= Liquid::Tags::STANDARD_TAGS.dup
|
|
18
|
+
def self.for(configuration = Configuration, **, &) = new(configuration[**], &)
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
def initialize configuration = Configuration.new
|
|
21
|
+
@configuration = configuration
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
namespace: Liquid::StandardFilters
|
|
43
|
-
return filter_registry if filters.empty?
|
|
23
|
+
yield self if block_given?
|
|
24
|
+
end
|
|
44
25
|
|
|
45
|
-
|
|
26
|
+
def create_filter_registry _context = nil, filters = Core::EMPTY_ARRAY
|
|
27
|
+
filters.empty? ? filter_registry : filter_registry.add(*filters)
|
|
46
28
|
end
|
|
47
29
|
|
|
48
|
-
|
|
30
|
+
alias create_strainer create_filter_registry
|
|
49
31
|
|
|
50
|
-
def filter_names = filter_registry.names
|
|
32
|
+
def filter_names = configuration.filter_registry.names
|
|
51
33
|
|
|
52
|
-
|
|
34
|
+
alias filter_method_names filter_names
|
|
53
35
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
def merge_filters(container, *)
|
|
37
|
+
filter_registry.merge(container, *)
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def merge_tags(container, *)
|
|
42
|
+
tag_registry.merge(container, *)
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def register_filter(*, **)
|
|
47
|
+
filter_registry.add(*, **)
|
|
57
48
|
self
|
|
58
49
|
end
|
|
59
50
|
|
|
60
51
|
def register_filters(*, **)
|
|
61
|
-
filters_cache.clear
|
|
62
52
|
filter_registry.add(*, **)
|
|
63
53
|
self
|
|
64
54
|
end
|
|
65
55
|
|
|
66
56
|
def register_tag name, klass
|
|
67
|
-
|
|
57
|
+
tag_registry.add name => klass
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def register_tags(**)
|
|
62
|
+
tag_registry.add(**)
|
|
68
63
|
self
|
|
69
64
|
end
|
|
70
65
|
|
|
71
66
|
def find_tag(name) = tags[name.to_s]
|
|
72
67
|
|
|
73
|
-
|
|
68
|
+
alias tag_for_name find_tag
|
|
69
|
+
|
|
70
|
+
def tags = tag_registry.to_h
|
|
74
71
|
|
|
75
72
|
def freeze
|
|
76
|
-
|
|
73
|
+
filter_registry.freeze
|
|
74
|
+
tag_registry.freeze
|
|
77
75
|
super
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
private
|
|
81
79
|
|
|
82
|
-
attr_reader :
|
|
80
|
+
attr_reader :configuration
|
|
83
81
|
end
|
|
84
82
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "containable"
|
|
4
|
+
require "core"
|
|
5
|
+
require "json"
|
|
6
|
+
require "refinements/string"
|
|
7
|
+
|
|
8
|
+
module Superfluid
|
|
9
|
+
module Filters
|
|
10
|
+
# A container of filter dependencies.
|
|
11
|
+
module Container
|
|
12
|
+
extend Containable
|
|
13
|
+
|
|
14
|
+
using Refinements::String
|
|
15
|
+
|
|
16
|
+
register(:jsonify) { |value| JSON.generate value }
|
|
17
|
+
register(:parse_json) { |value| JSON.parse value }
|
|
18
|
+
|
|
19
|
+
register :pluralize do |value, count = 0, suffix = "s", replace = /$/|
|
|
20
|
+
"#{count} #{value.pluralize suffix, count, replace:}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
register :singularize do |value, count = 0, suffix = "s", replace = Core::EMPTY_STRING|
|
|
24
|
+
"#{count} #{value.singularize suffix, count, replace:}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
register :trim_end do |value, maximum, trailer = "..."|
|
|
28
|
+
value.trim_end maximum, nil, trailer:
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
register(:trim_middle) { |value, maximum, gap = "..."| value.trim_middle maximum, gap: }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "core"
|
|
4
|
+
require "functionable"
|
|
5
|
+
|
|
3
6
|
module Superfluid
|
|
4
7
|
module Registries
|
|
5
|
-
#
|
|
6
|
-
Filter
|
|
7
|
-
|
|
8
|
+
# The default filter registry.
|
|
9
|
+
class Filter
|
|
10
|
+
attr_reader :mode
|
|
11
|
+
|
|
12
|
+
def initialize container = Filters::Container,
|
|
13
|
+
defaults: Liquid::StandardFilters,
|
|
14
|
+
mode: :strict
|
|
15
|
+
@container = container
|
|
16
|
+
@mode = mode
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
super
|
|
18
|
+
add defaults
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
def add(*, **)
|
|
@@ -16,61 +24,74 @@ module Superfluid
|
|
|
16
24
|
self
|
|
17
25
|
end
|
|
18
26
|
|
|
27
|
+
def merge(other, *)
|
|
28
|
+
container.merge(other, *)
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
19
32
|
def call(name, *)
|
|
20
33
|
dispatch(name, *)
|
|
21
34
|
rescue ArgumentError => error
|
|
22
35
|
raise Liquid::ArgumentError, error.message, error.backtrace
|
|
23
36
|
end
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def clear
|
|
28
|
-
commands.clear
|
|
29
|
-
self
|
|
30
|
-
end
|
|
38
|
+
alias invoke call
|
|
31
39
|
|
|
32
|
-
def names =
|
|
40
|
+
def names = container.keys.sort
|
|
33
41
|
|
|
34
42
|
private
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
attr_reader :container
|
|
45
|
+
|
|
46
|
+
def add_namespaces(*collection) = collection.each { add_methods it }
|
|
37
47
|
|
|
38
48
|
def add_methods namespace
|
|
49
|
+
# Order matters.
|
|
50
|
+
case namespace
|
|
51
|
+
in Class
|
|
52
|
+
fail Liquid::ArgumentError,
|
|
53
|
+
"Invalid type (#{namespace}) for namespace. Must be a Module or Functionable."
|
|
54
|
+
in Functionable then add_function_methods namespace
|
|
55
|
+
in Module then add_instance_methods namespace
|
|
56
|
+
# simplecov:disable
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def add_function_methods namespace
|
|
61
|
+
namespace.function_methods
|
|
62
|
+
.each
|
|
63
|
+
.with_object({}) { |name, all| all[name] = namespace.method name }
|
|
64
|
+
.then { add_commands(**it) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def add_instance_methods namespace
|
|
39
68
|
shadow = Class.new.include(namespace).new
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
collection = namespace.instance_methods
|
|
71
|
+
.each
|
|
72
|
+
.with_object({}) do |method, all|
|
|
73
|
+
name = method.name
|
|
74
|
+
all[name] = shadow.method name
|
|
75
|
+
end
|
|
47
76
|
|
|
48
|
-
add_commands(**
|
|
77
|
+
add_commands(**collection)
|
|
49
78
|
end
|
|
50
79
|
|
|
51
|
-
def add_commands(**
|
|
80
|
+
def add_commands(**collection) = collection.each { |name, command| add_command name, command }
|
|
52
81
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
key = name.to_s
|
|
82
|
+
def add_command name, callable
|
|
83
|
+
return if container.key? name
|
|
56
84
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
case function
|
|
60
|
-
in Proc | Method | Object if function.respond_to? :call then commands[key] = function
|
|
85
|
+
case callable
|
|
86
|
+
in Proc | Method | Core::Composable then container.register(name) { callable }
|
|
61
87
|
else fail Liquid::ArgumentError, "Filter must be callable."
|
|
62
88
|
end
|
|
63
89
|
end
|
|
64
90
|
|
|
65
|
-
def dispatch name, *
|
|
66
|
-
key
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
commands[key].call(*positionals)
|
|
70
|
-
elsif mode == :strict
|
|
71
|
-
fail Liquid::UndefinedFilter, "Undefined filter: #{name}."
|
|
72
|
-
else
|
|
73
|
-
positionals.first
|
|
91
|
+
def dispatch name, *arguments
|
|
92
|
+
if container.key? name then container[name].call(*arguments)
|
|
93
|
+
elsif mode == :strict then fail Liquid::UndefinedFilter, "Undefined filter: #{name}."
|
|
94
|
+
else arguments.first
|
|
74
95
|
end
|
|
75
96
|
end
|
|
76
97
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Superfluid
|
|
4
|
+
module Registries
|
|
5
|
+
# The default tag registry.
|
|
6
|
+
class Tag
|
|
7
|
+
def initialize container = Tags::Container, defaults: Liquid::Tags::STANDARD_TAGS
|
|
8
|
+
@container = container
|
|
9
|
+
|
|
10
|
+
add(**defaults)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add **collection
|
|
14
|
+
collection.each { |name, tag| container.register name, tag unless container.key? name }
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def merge(other, *)
|
|
19
|
+
container.merge(other, *)
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get(name) = (container[name] if container.key? name)
|
|
24
|
+
|
|
25
|
+
def names = container.keys.sort
|
|
26
|
+
|
|
27
|
+
def to_h = container.each.to_h
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :container
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/superfluid/renderer.rb
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "core"
|
|
4
|
+
|
|
3
5
|
module Superfluid
|
|
4
6
|
# Renders template and associated data.
|
|
5
7
|
class Renderer
|
|
6
|
-
def initialize environment: Environment.
|
|
8
|
+
def initialize environment: Environment.new, parser: Liquid::Template
|
|
7
9
|
@environment = environment
|
|
8
10
|
@parser = parser
|
|
11
|
+
|
|
12
|
+
yield environment if block_given?
|
|
13
|
+
|
|
14
|
+
environment.freeze
|
|
9
15
|
end
|
|
10
16
|
|
|
11
|
-
def call(template, data) = parser.parse(template, environment:).render data
|
|
17
|
+
def call(template, data = Core::EMPTY_HASH) = parser.parse(template, environment:).render data
|
|
12
18
|
|
|
13
19
|
private
|
|
14
20
|
|
data/lib/superfluid.rb
CHANGED
data/superfluid.gemspec
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = "superfluid"
|
|
5
|
-
spec.version = "0.
|
|
5
|
+
spec.version = "0.2.0"
|
|
6
6
|
spec.authors = ["Brooke Kuhlmann"]
|
|
7
7
|
spec.email = ["brooke@alchemists.io"]
|
|
8
8
|
spec.homepage = "https://alchemists.io/projects/superfluid"
|
|
@@ -25,7 +25,9 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.required_ruby_version = ">= 4.0"
|
|
26
26
|
|
|
27
27
|
spec.add_dependency "base64", "~> 0.3"
|
|
28
|
-
spec.add_dependency "
|
|
28
|
+
spec.add_dependency "containable", "~> 2.5"
|
|
29
|
+
spec.add_dependency "core", "~> 3.4"
|
|
30
|
+
spec.add_dependency "functionable", "~> 1.4"
|
|
29
31
|
spec.add_dependency "liquid", "~> 5.13"
|
|
30
32
|
spec.add_dependency "refinements", "~> 14.0"
|
|
31
33
|
spec.add_dependency "zeitwerk", "~> 2.8"
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: superfluid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brooke Kuhlmann
|
|
@@ -49,20 +49,48 @@ dependencies:
|
|
|
49
49
|
- - "~>"
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
51
|
version: '0.3'
|
|
52
|
+
- !ruby/object:Gem::Dependency
|
|
53
|
+
name: containable
|
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - "~>"
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '2.5'
|
|
59
|
+
type: :runtime
|
|
60
|
+
prerelease: false
|
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - "~>"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '2.5'
|
|
52
66
|
- !ruby/object:Gem::Dependency
|
|
53
67
|
name: core
|
|
54
68
|
requirement: !ruby/object:Gem::Requirement
|
|
55
69
|
requirements:
|
|
56
70
|
- - "~>"
|
|
57
71
|
- !ruby/object:Gem::Version
|
|
58
|
-
version: '3.
|
|
72
|
+
version: '3.4'
|
|
73
|
+
type: :runtime
|
|
74
|
+
prerelease: false
|
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - "~>"
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '3.4'
|
|
80
|
+
- !ruby/object:Gem::Dependency
|
|
81
|
+
name: functionable
|
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - "~>"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '1.4'
|
|
59
87
|
type: :runtime
|
|
60
88
|
prerelease: false
|
|
61
89
|
version_requirements: !ruby/object:Gem::Requirement
|
|
62
90
|
requirements:
|
|
63
91
|
- - "~>"
|
|
64
92
|
- !ruby/object:Gem::Version
|
|
65
|
-
version: '
|
|
93
|
+
version: '1.4'
|
|
66
94
|
- !ruby/object:Gem::Dependency
|
|
67
95
|
name: liquid
|
|
68
96
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -116,10 +144,14 @@ files:
|
|
|
116
144
|
- LICENSE.adoc
|
|
117
145
|
- README.adoc
|
|
118
146
|
- lib/superfluid.rb
|
|
147
|
+
- lib/superfluid/configuration.rb
|
|
119
148
|
- lib/superfluid/environment.rb
|
|
149
|
+
- lib/superfluid/filters/container.rb
|
|
120
150
|
- lib/superfluid/registries/filter.rb
|
|
151
|
+
- lib/superfluid/registries/tag.rb
|
|
121
152
|
- lib/superfluid/renderer.rb
|
|
122
153
|
- lib/superfluid/systems/memory.rb
|
|
154
|
+
- lib/superfluid/tags/container.rb
|
|
123
155
|
- lib/superfluid/tags/template.rb
|
|
124
156
|
- superfluid.gemspec
|
|
125
157
|
homepage: https://alchemists.io/projects/superfluid
|
|
@@ -147,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
147
179
|
- !ruby/object:Gem::Version
|
|
148
180
|
version: '0'
|
|
149
181
|
requirements: []
|
|
150
|
-
rubygems_version: 4.0.
|
|
182
|
+
rubygems_version: 4.0.20
|
|
151
183
|
specification_version: 4
|
|
152
184
|
summary: Enhances Liquid with object composition and functional design.
|
|
153
185
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|