luoma 0.1.0 → 0.3.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/CHANGELOG.md +9 -2
- data/README.md +4 -4
- data/lib/luoma/context.rb +2 -1
- data/lib/luoma/drop.rb +2 -3
- data/lib/luoma/drops/range.rb +1 -1
- data/lib/luoma/drops/undefined.rb +1 -1
- data/lib/luoma/environment.rb +14 -14
- data/lib/luoma/escape.rb +53 -0
- data/lib/luoma/expression.rb +8 -8
- data/lib/luoma/filter.rb +3 -3
- data/lib/luoma/filters/array.rb +9 -0
- data/lib/luoma/filters/escape_js.rb +12 -0
- data/lib/luoma/filters/slice.rb +1 -1
- data/lib/luoma/filters/sort.rb +0 -25
- data/lib/luoma/filters/string.rb +2 -2
- data/lib/luoma/loaders/file_system_loader.rb +10 -9
- data/lib/luoma/parser_unified.rb +2 -1
- data/lib/luoma/tags/assign.rb +4 -0
- data/lib/luoma/tags/case.rb +1 -1
- data/lib/luoma/template.rb +0 -2
- data/lib/luoma/version.rb +1 -1
- data/lib/luoma.rb +2 -0
- data/sig/luoma/drop.rbs +2 -2
- data/sig/luoma/environment.rbs +8 -8
- data/sig/luoma/escape.rbs +7 -0
- data/sig/luoma/filters/array.rbs +4 -0
- data/sig/luoma/filters/escape_js.rbs +6 -0
- data/sig/luoma/filters/sort.rbs +0 -4
- data/sig/luoma/loaders/file_system_loader.rbs +4 -0
- data.tar.gz.sig +0 -0
- metadata +6 -28
- metadata.gz.sig +0 -0
- data/docs/configuration.md +0 -180
- data/docs/custom_filters.md +0 -3
- data/docs/custom_tags.md +0 -3
- data/docs/expressions.md +0 -3
- data/docs/extension_types.md +0 -41
- data/docs/filter_reference.md +0 -1702
- data/docs/index.md +0 -73
- data/docs/luoma_for_template_authors.md +0 -3
- data/docs/markdown.md +0 -111
- data/docs/predicate_reference.md +0 -3
- data/docs/static_analysis.md +0 -3
- data/docs/tag_reference.md +0 -352
- data/docs/template_loaders.md +0 -177
- data/docs/undefined_variables.md +0 -3
- data/docs-requirements.txt +0 -11
- data/docs_/cycle.md +0 -17
- data/docs_/font_rendering_example.md +0 -157
- data/docs_/header_block_example.md +0 -51
- data/docs_/increment_and_decrement.md +0 -49
- data/docs_/logo_style_example.md +0 -40
- data/docs_/migration.md +0 -11
- data/docs_/notes.md +0 -19
- data/zensical.toml +0 -363
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9fc9f4fa3b7794116a4de61966341ef712606e0c97b5834cc5c115701471bea2
|
|
4
|
+
data.tar.gz: d6b462a7b470daca6feb3df50e43867404e2e3e50ae10810c3883e9e8e32065d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40a51ce357387f02f253e454d20b3523d1373916ae067e5ece624f4abcef3195a68711c54080f899aea74af2f02180b47e28dbf30243d6ba68f7eef748240ac4
|
|
7
|
+
data.tar.gz: '03938e09549fbab02058a08fd8678d6845a08174eee1f9865fd3fdadd4e8b13790e21456d91825bd5f22d3bb4c5cca0da1454f06e631dcefa9b817be1d98de7e'
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.3.0] - 2026-08-04
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
- Added the `escape_js` filter ([docs](https://jg-rp.github.io/luoma-ruby/filter_reference/#escape_js)).
|
|
4
|
+
- Removed the `sort_natural` filter. Use `a | sort: i -> (i | upcase)` instead.
|
|
5
|
+
|
|
6
|
+
## [0.2.0] - 2026-07-29
|
|
7
|
+
|
|
8
|
+
- Added the `take` filter. `a | take: 5` is equivalent to `a | slice: stop=6`.
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-07-29
|
|
4
11
|
|
|
5
12
|
- Initial release
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h1 align="center">Luoma - Ruby</h1>
|
|
2
2
|
|
|
3
|
-
<p align="center">A modern template engine
|
|
3
|
+
<p align="center">A modern template engine with a well-defined, composable expression language.</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://github.com/jg-rp/luoma-ruby/blob/main/LICENSE.txt">
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<img alt="Gem Version" src="https://img.shields.io/gem/v/luoma?style=flat-square">
|
|
15
15
|
</a>
|
|
16
16
|
<a href="https://github.com/jg-rp/luoma-ruby">
|
|
17
|
-
<img alt="Static Badge" src="https://img.shields.io/badge/Ruby-3.
|
|
17
|
+
<img alt="Static Badge" src="https://img.shields.io/badge/Ruby-3.3%20%7C%203.4%20%7C%204.0-CC342D?style=flat-square">
|
|
18
18
|
</a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
Add `'luoma'` to your Gemfile:
|
|
31
31
|
|
|
32
32
|
```
|
|
33
|
-
gem 'luoma', '~> 0.
|
|
33
|
+
gem 'luoma', '~> 0.3.0'
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Or
|
|
@@ -57,7 +57,7 @@ puts template.render("you" => "Luoma") # Hello, Luoma!
|
|
|
57
57
|
|
|
58
58
|
## Links
|
|
59
59
|
|
|
60
|
-
- Documentation: https://jg-rp.github.io/luoma/
|
|
60
|
+
- Documentation: https://jg-rp.github.io/luoma-ruby/
|
|
61
61
|
- Change log: https://github.com/jg-rp/luoma-ruby/blob/main/CHANGELOG.md
|
|
62
62
|
- RubyGems: https://rubygems.org/gems/luoma
|
|
63
63
|
- Source code: https://github.com/jg-rp/luoma-ruby
|
data/lib/luoma/context.rb
CHANGED
data/lib/luoma/drop.rb
CHANGED
|
@@ -51,9 +51,8 @@ module Luoma
|
|
|
51
51
|
0
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def slice(start, stop, step)
|
|
54
|
+
#: (Integer, Integer, Integer, RenderContext) -> Enumerable[untyped]
|
|
55
|
+
def slice(start, stop, step, context)
|
|
57
56
|
self
|
|
58
57
|
end
|
|
59
58
|
|
data/lib/luoma/drops/range.rb
CHANGED
data/lib/luoma/environment.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
5
|
module Luoma
|
|
6
|
+
# Luoma template parsing and render configuration.
|
|
6
7
|
class Environment
|
|
7
8
|
attr_reader :persistent_registers
|
|
8
9
|
|
|
@@ -164,6 +165,7 @@ module Luoma
|
|
|
164
165
|
register_filter("default", Luoma::Filters.method(:default))
|
|
165
166
|
register_filter("divided_by", Luoma::Filters.method(:divided_by))
|
|
166
167
|
register_filter("downcase", Luoma::Filters.method(:downcase))
|
|
168
|
+
register_filter("escape_js", Luoma::Filters.method(:escape_js))
|
|
167
169
|
register_filter("escape_once", Luoma::Filters.method(:escape_once))
|
|
168
170
|
register_filter("escape", Luoma::Filters.method(:escape))
|
|
169
171
|
register_filter("find_index", Luoma::Filters.method(:find_index))
|
|
@@ -198,13 +200,13 @@ module Luoma
|
|
|
198
200
|
register_filter("size", Luoma::Filters.method(:size))
|
|
199
201
|
register_filter("slice", Luoma::Filters.method(:slice))
|
|
200
202
|
register_filter("sort", Luoma::Filters.method(:sort))
|
|
201
|
-
register_filter("sort_natural", Luoma::Filters.method(:sort_natural))
|
|
202
203
|
register_filter("sort_numeric", Luoma::Filters.method(:sort_numeric))
|
|
203
204
|
register_filter("split", Luoma::Filters.method(:split))
|
|
204
205
|
register_filter("strip_html", Luoma::Filters.method(:strip_html))
|
|
205
206
|
register_filter("strip_newlines", Luoma::Filters.method(:strip_newlines))
|
|
206
207
|
register_filter("strip", Luoma::Filters.method(:strip))
|
|
207
208
|
register_filter("sum", Luoma::Filters.method(:sum))
|
|
209
|
+
register_filter("take", Luoma::Filters.method(:take))
|
|
208
210
|
register_filter("times", Luoma::Filters.method(:times))
|
|
209
211
|
register_filter("truncate", Luoma::Filters.method(:truncate))
|
|
210
212
|
register_filter("truncatewords", Luoma::Filters.method(:truncatewords))
|
|
@@ -227,8 +229,8 @@ module Luoma
|
|
|
227
229
|
register_predicate("string", Luoma::Predicates.method(:string?))
|
|
228
230
|
end
|
|
229
231
|
|
|
230
|
-
#: (untyped, untyped, RenderContext
|
|
231
|
-
def contains?(left, right, context
|
|
232
|
+
#: (untyped, untyped, RenderContext) -> bool
|
|
233
|
+
def contains?(left, right, context)
|
|
232
234
|
return left.contains?(right, context) if left.is_a?(Drop)
|
|
233
235
|
|
|
234
236
|
right = right.to_primitive(:data, context) if right.is_a?(Drop)
|
|
@@ -240,18 +242,16 @@ module Luoma
|
|
|
240
242
|
end
|
|
241
243
|
end
|
|
242
244
|
|
|
243
|
-
#: (untyped, untyped, RenderContext
|
|
244
|
-
def eq?(left, right, context
|
|
245
|
-
# TODO: Don't accepts token
|
|
245
|
+
#: (untyped, untyped, RenderContext) -> bool
|
|
246
|
+
def eq?(left, right, context)
|
|
246
247
|
return left.eq?(right, context) if left.is_a?(Drop)
|
|
247
248
|
return right.eq?(left, context) if right.is_a?(Drop)
|
|
248
249
|
|
|
249
250
|
left == right
|
|
250
251
|
end
|
|
251
252
|
|
|
252
|
-
#: (untyped, untyped, RenderContext
|
|
253
|
-
def lt?(left, right, context
|
|
254
|
-
# TODO: Don't accepts token
|
|
253
|
+
#: (untyped, untyped, RenderContext) -> bool?
|
|
254
|
+
def lt?(left, right, context)
|
|
255
255
|
return left.lt?(right, context) if left.is_a?(Drop)
|
|
256
256
|
return right.gt?(left, context) if right.is_a?(Drop)
|
|
257
257
|
|
|
@@ -260,11 +260,11 @@ module Luoma
|
|
|
260
260
|
nil
|
|
261
261
|
end
|
|
262
262
|
|
|
263
|
-
#: (untyped, untyped, RenderContext
|
|
264
|
-
def cmp(left, right, context
|
|
265
|
-
return -1 if lt?(left, right, context
|
|
266
|
-
return 1 if lt?(right, left, context
|
|
267
|
-
return 0 if eq?(left, right, context
|
|
263
|
+
#: (untyped, untyped, RenderContext) -> (-1 | 1 | 0 | nil)
|
|
264
|
+
def cmp(left, right, context)
|
|
265
|
+
return -1 if lt?(left, right, context)
|
|
266
|
+
return 1 if lt?(right, left, context)
|
|
267
|
+
return 0 if eq?(left, right, context)
|
|
268
268
|
|
|
269
269
|
if left.nil? || nothing?(left)
|
|
270
270
|
1
|
data/lib/luoma/escape.rb
CHANGED
|
@@ -23,6 +23,55 @@ module Luoma
|
|
|
23
23
|
"`" => "`"
|
|
24
24
|
}.freeze #: Hash[String, String]
|
|
25
25
|
|
|
26
|
+
ESCAPE_JS_MAP = {
|
|
27
|
+
"\\" => "\\u005C",
|
|
28
|
+
"'" => "\\u0027",
|
|
29
|
+
"\"" => "\\u0022",
|
|
30
|
+
">" => "\\u003E",
|
|
31
|
+
"<" => "\\u003C",
|
|
32
|
+
"&" => "\\u0026",
|
|
33
|
+
"=" => "\\u003D",
|
|
34
|
+
"-" => "\\u002D",
|
|
35
|
+
";" => "\\u003B",
|
|
36
|
+
"`" => "\\u0060",
|
|
37
|
+
"\u2028" => "\\u2028",
|
|
38
|
+
"\u2029" => "\\u2029",
|
|
39
|
+
"\x00" => "\\u0000",
|
|
40
|
+
"\x01" => "\\u0001",
|
|
41
|
+
"\x02" => "\\u0002",
|
|
42
|
+
"\x03" => "\\u0003",
|
|
43
|
+
"\x04" => "\\u0004",
|
|
44
|
+
"\x05" => "\\u0005",
|
|
45
|
+
"\x06" => "\\u0006",
|
|
46
|
+
"\x07" => "\\u0007",
|
|
47
|
+
"\x08" => "\\u0008",
|
|
48
|
+
"\t" => "\\u0009",
|
|
49
|
+
"\n" => "\\u000A",
|
|
50
|
+
"\x0b" => "\\u000B",
|
|
51
|
+
"\x0c" => "\\u000C",
|
|
52
|
+
"\r" => "\\u000D",
|
|
53
|
+
"\x0e" => "\\u000E",
|
|
54
|
+
"\x0f" => "\\u000F",
|
|
55
|
+
"\x10" => "\\u0010",
|
|
56
|
+
"\x11" => "\\u0011",
|
|
57
|
+
"\x12" => "\\u0012",
|
|
58
|
+
"\x13" => "\\u0013",
|
|
59
|
+
"\x14" => "\\u0014",
|
|
60
|
+
"\x15" => "\\u0015",
|
|
61
|
+
"\x16" => "\\u0016",
|
|
62
|
+
"\x17" => "\\u0017",
|
|
63
|
+
"\x18" => "\\u0018",
|
|
64
|
+
"\x19" => "\\u0019",
|
|
65
|
+
"\x1a" => "\\u001A",
|
|
66
|
+
"\x1b" => "\\u001B",
|
|
67
|
+
"\x1c" => "\\u001C",
|
|
68
|
+
"\x1d" => "\\u001D",
|
|
69
|
+
"\x1e" => "\\u001E",
|
|
70
|
+
"\x1f" => "\\u001F"
|
|
71
|
+
}.freeze #: Hash[String, String]
|
|
72
|
+
|
|
73
|
+
RE_ESCAPE_JS = Regexp.new(ESCAPE_JS_MAP.keys.map { |k| Regexp.escape(k) }.join("|"))
|
|
74
|
+
|
|
26
75
|
#: (String) -> String
|
|
27
76
|
def self.escape(s)
|
|
28
77
|
s.gsub(RE_ESCAPE) { |m| ESCAPE_MAP[m] }
|
|
@@ -32,4 +81,8 @@ module Luoma
|
|
|
32
81
|
def self.unescape(s)
|
|
33
82
|
s.gsub(RE_UNESCAPE) { |m| UNESCAPE_MAP[m] }
|
|
34
83
|
end
|
|
84
|
+
|
|
85
|
+
def self.escape_js(s)
|
|
86
|
+
s.gsub(RE_ESCAPE_JS) { |m| ESCAPE_JS_MAP[m] }
|
|
87
|
+
end
|
|
35
88
|
end
|
data/lib/luoma/expression.rb
CHANGED
|
@@ -450,7 +450,7 @@ module Luoma
|
|
|
450
450
|
class EqExpression < InfixExpression
|
|
451
451
|
#: (RenderContext) -> untyped
|
|
452
452
|
def evaluate(context)
|
|
453
|
-
context.env.eq?(@left.evaluate(context), @right.evaluate(context), context
|
|
453
|
+
context.env.eq?(@left.evaluate(context), @right.evaluate(context), context)
|
|
454
454
|
end
|
|
455
455
|
|
|
456
456
|
#: () -> String
|
|
@@ -462,7 +462,7 @@ module Luoma
|
|
|
462
462
|
class NeExpression < InfixExpression
|
|
463
463
|
#: (RenderContext) -> untyped
|
|
464
464
|
def evaluate(context)
|
|
465
|
-
!context.env.eq?(@left.evaluate(context), @right.evaluate(context), context
|
|
465
|
+
!context.env.eq?(@left.evaluate(context), @right.evaluate(context), context)
|
|
466
466
|
end
|
|
467
467
|
|
|
468
468
|
#: () -> String
|
|
@@ -474,7 +474,7 @@ module Luoma
|
|
|
474
474
|
class LtExpression < InfixExpression
|
|
475
475
|
#: (RenderContext) -> untyped
|
|
476
476
|
def evaluate(context)
|
|
477
|
-
context.env.lt?(@left.evaluate(context), @right.evaluate(context), context
|
|
477
|
+
context.env.lt?(@left.evaluate(context), @right.evaluate(context), context)
|
|
478
478
|
end
|
|
479
479
|
|
|
480
480
|
#: () -> String
|
|
@@ -488,7 +488,7 @@ module Luoma
|
|
|
488
488
|
def evaluate(context)
|
|
489
489
|
left = @left.evaluate(context)
|
|
490
490
|
right = @right.evaluate(context)
|
|
491
|
-
context.env.lt?(left, right, context
|
|
491
|
+
context.env.lt?(left, right, context) || context.env.eq?(left, right, context)
|
|
492
492
|
end
|
|
493
493
|
|
|
494
494
|
#: () -> String
|
|
@@ -500,7 +500,7 @@ module Luoma
|
|
|
500
500
|
class GtExpression < InfixExpression
|
|
501
501
|
#: (RenderContext) -> untyped
|
|
502
502
|
def evaluate(context)
|
|
503
|
-
context.env.lt?(@right.evaluate(context), @left.evaluate(context), context
|
|
503
|
+
context.env.lt?(@right.evaluate(context), @left.evaluate(context), context)
|
|
504
504
|
end
|
|
505
505
|
|
|
506
506
|
#: () -> String
|
|
@@ -514,7 +514,7 @@ module Luoma
|
|
|
514
514
|
def evaluate(context)
|
|
515
515
|
left = @left.evaluate(context)
|
|
516
516
|
right = @right.evaluate(context)
|
|
517
|
-
context.env.lt?(right, left, context
|
|
517
|
+
context.env.lt?(right, left, context) || context.env.eq?(left, right, context)
|
|
518
518
|
end
|
|
519
519
|
|
|
520
520
|
#: () -> String
|
|
@@ -526,7 +526,7 @@ module Luoma
|
|
|
526
526
|
class ContainsExpression < InfixExpression
|
|
527
527
|
#: (RenderContext) -> untyped
|
|
528
528
|
def evaluate(context)
|
|
529
|
-
context.env.contains?(@left.evaluate(context), @right.evaluate(context), context
|
|
529
|
+
context.env.contains?(@left.evaluate(context), @right.evaluate(context), context)
|
|
530
530
|
end
|
|
531
531
|
|
|
532
532
|
#: () -> String
|
|
@@ -538,7 +538,7 @@ module Luoma
|
|
|
538
538
|
class InExpression < InfixExpression
|
|
539
539
|
#: (RenderContext) -> untyped
|
|
540
540
|
def evaluate(context)
|
|
541
|
-
context.env.contains?(@right.evaluate(context), @left.evaluate(context), context
|
|
541
|
+
context.env.contains?(@right.evaluate(context), @left.evaluate(context), context)
|
|
542
542
|
end
|
|
543
543
|
|
|
544
544
|
#: () -> String
|
data/lib/luoma/filter.rb
CHANGED
|
@@ -71,17 +71,17 @@ module Luoma
|
|
|
71
71
|
|
|
72
72
|
#: (untyped, untyped) -> bool
|
|
73
73
|
def eq?(obj, other)
|
|
74
|
-
@env.eq?(obj, other, @render_context
|
|
74
|
+
@env.eq?(obj, other, @render_context)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
#: (untyped, untyped) -> bool
|
|
78
78
|
def lt?(obj, other)
|
|
79
|
-
@env.lt?(obj, other, @render_context
|
|
79
|
+
@env.lt?(obj, other, @render_context)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
#: (untyped, untyped) -> (-1 | 1 | 0 | nil)
|
|
83
83
|
def cmp(left, right)
|
|
84
|
-
@env.cmp(left, right, @render_context
|
|
84
|
+
@env.cmp(left, right, @render_context)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
#: (untyped) -> bool
|
data/lib/luoma/filters/array.rb
CHANGED
|
@@ -368,5 +368,14 @@ module Luoma
|
|
|
368
368
|
max_.call(left) { |item, _index| context.fetch(item, key, default: nil) }
|
|
369
369
|
end
|
|
370
370
|
end
|
|
371
|
+
|
|
372
|
+
# Return at most _count_ items from `_left` as a new array.
|
|
373
|
+
# Coerce _left_ to an array if it's not an array already.
|
|
374
|
+
# Coerce _count_ to an integer, or zero if it is not numeric.
|
|
375
|
+
def self.take(context, left, count)
|
|
376
|
+
count_ = context.to_i(count, default: 0)
|
|
377
|
+
count_ = 0 if count_.negative?
|
|
378
|
+
context.to_enumerable(left).take(count_)
|
|
379
|
+
end
|
|
371
380
|
end
|
|
372
381
|
end
|
data/lib/luoma/filters/slice.rb
CHANGED
|
@@ -39,7 +39,7 @@ module Luoma
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
if left.is_a?(Drop)
|
|
42
|
-
left.slice(normalized_start, normalized_stop, step)
|
|
42
|
+
left.slice(normalized_start, normalized_stop, step, context.render_context)
|
|
43
43
|
else
|
|
44
44
|
# This does not work for strings.
|
|
45
45
|
# left[(normalized_start...normalized_stop).step(step)]
|
data/lib/luoma/filters/sort.rb
CHANGED
|
@@ -54,21 +54,6 @@ module Luoma
|
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
def self.sort_natural(context, left, key = :nothing)
|
|
58
|
-
left = context.to_enumerable(left)
|
|
59
|
-
|
|
60
|
-
if key.nil? || context.nothing?(key)
|
|
61
|
-
left.sort { |a, b| nil_safe_casecmp(a, b) }
|
|
62
|
-
elsif key.is_a?(ExpressionDrop)
|
|
63
|
-
key.expr.broadcast_with_index(left).zip(left).sort do |a, b|
|
|
64
|
-
nil_safe_casecmp(a.first, b.first)
|
|
65
|
-
end.map(&:last)
|
|
66
|
-
else
|
|
67
|
-
key = context.to_string(key)
|
|
68
|
-
left.sort { |a, b| nil_safe_casecmp(context.fetch(a, key), context.fetch(b, key)) }
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
57
|
def self.sort_numeric(context, left, key = :nothing)
|
|
73
58
|
left = context.to_enumerable(left)
|
|
74
59
|
|
|
@@ -84,16 +69,6 @@ module Luoma
|
|
|
84
69
|
end
|
|
85
70
|
end
|
|
86
71
|
|
|
87
|
-
def self.nil_safe_casecmp(left, right)
|
|
88
|
-
if !left.nil? && !right.nil?
|
|
89
|
-
left.to_s.casecmp(right.to_s)
|
|
90
|
-
elsif left.nil? && right.nil?
|
|
91
|
-
0
|
|
92
|
-
else
|
|
93
|
-
left.nil? ? 1 : -1
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
72
|
def self.numeric_compare(left, right, context)
|
|
98
73
|
res = ints(left, context) <=> ints(right, context)
|
|
99
74
|
res || -1
|
data/lib/luoma/filters/string.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Luoma
|
|
|
31
31
|
# Return _left_ with special HTML characters replaced with their HTML-safe escape sequences.
|
|
32
32
|
# Coerce _left_ to a string if it is not one already.
|
|
33
33
|
def self.escape(context, left)
|
|
34
|
-
CGI.
|
|
34
|
+
CGI.escapeHTML(context.to_string(left)) unless left.nil?
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# Return _left_ with special HTML characters replaced with their HTML-safe escape sequences.
|
|
@@ -39,7 +39,7 @@ module Luoma
|
|
|
39
39
|
#
|
|
40
40
|
# It is safe to use `escape_once` on string values that already contain HTML-escape sequences.
|
|
41
41
|
def self.escape_once(context, left)
|
|
42
|
-
CGI.
|
|
42
|
+
CGI.escapeHTML(CGI.unescapeHTML(context.to_string(left)))
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Return _left_ with leading whitespace removed.
|
|
@@ -31,23 +31,24 @@ module Luoma
|
|
|
31
31
|
template_path = template_path.sub_ext(@default_extension || raise)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# TODO: better relative path check
|
|
35
|
-
|
|
36
|
-
# Don't alow template names to escape the search path with "../".
|
|
37
|
-
template_path.each_filename do |part|
|
|
38
|
-
next unless part == ".."
|
|
39
|
-
|
|
40
|
-
raise TemplateNotFoundError.new("template not found #{template_name}")
|
|
41
|
-
end
|
|
42
|
-
|
|
43
34
|
# Search each path in turn.
|
|
44
35
|
@search_path.each do |path|
|
|
45
36
|
source_path = path.join(template_path)
|
|
37
|
+
next unless child_path?(source_path, path)
|
|
46
38
|
return source_path if source_path.file?
|
|
47
39
|
end
|
|
48
40
|
|
|
49
41
|
raise TemplateNotFoundError.new("template not found #{template_name}")
|
|
50
42
|
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
#: (Pathname, Pathname) -> bool
|
|
47
|
+
def child_path?(child, parent)
|
|
48
|
+
child_ = child.expand_path
|
|
49
|
+
parent_ = parent.expand_path
|
|
50
|
+
child_.ascend.drop(1).include?(parent_)
|
|
51
|
+
end
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# A file system template loader that caches parsed templates.
|
data/lib/luoma/parser_unified.rb
CHANGED
|
@@ -770,7 +770,8 @@ module Luoma
|
|
|
770
770
|
#: () -> Expression
|
|
771
771
|
def parse_int_literal
|
|
772
772
|
token = self.next
|
|
773
|
-
|
|
773
|
+
# Float first for scientific notation
|
|
774
|
+
IntegerLiteral.new(token, Float(Luoma.get_token_value(token, @source)).to_i)
|
|
774
775
|
end
|
|
775
776
|
|
|
776
777
|
#: () -> Expression
|
data/lib/luoma/tags/assign.rb
CHANGED
data/lib/luoma/tags/case.rb
CHANGED
|
@@ -51,7 +51,7 @@ module Luoma
|
|
|
51
51
|
block.right.each do |expr|
|
|
52
52
|
right = expr.evaluate(context)
|
|
53
53
|
if (right.is_a?(PredicateFunction) && context.env.truthy?(right.call(context, left), context)) ||
|
|
54
|
-
context.env.eq?(left, right, context
|
|
54
|
+
context.env.eq?(left, right, context)
|
|
55
55
|
return Luoma.render_block(block.block, context, buffer)
|
|
56
56
|
end
|
|
57
57
|
end
|
data/lib/luoma/template.rb
CHANGED
data/lib/luoma/version.rb
CHANGED
data/lib/luoma.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "luoma/context"
|
|
|
7
7
|
require_relative "luoma/drop"
|
|
8
8
|
require_relative "luoma/environment"
|
|
9
9
|
require_relative "luoma/errors"
|
|
10
|
+
require_relative "luoma/escape"
|
|
10
11
|
require_relative "luoma/expression"
|
|
11
12
|
require_relative "luoma/filter"
|
|
12
13
|
require_relative "luoma/fnv"
|
|
@@ -29,6 +30,7 @@ require_relative "luoma/drops/undefined"
|
|
|
29
30
|
require_relative "luoma/filters/array"
|
|
30
31
|
require_relative "luoma/filters/date"
|
|
31
32
|
require_relative "luoma/filters/default"
|
|
33
|
+
require_relative "luoma/filters/escape_js"
|
|
32
34
|
require_relative "luoma/filters/json"
|
|
33
35
|
require_relative "luoma/filters/math"
|
|
34
36
|
require_relative "luoma/filters/size"
|
data/sig/luoma/drop.rbs
CHANGED
|
@@ -29,8 +29,8 @@ module Luoma
|
|
|
29
29
|
# (RenderContext) -> Integer
|
|
30
30
|
def length: (Luoma::RenderContext context) -> Integer
|
|
31
31
|
|
|
32
|
-
# (Integer, Integer, Integer) -> Enumerable[untyped]
|
|
33
|
-
def slice: (Integer start, Integer stop, Integer step) -> untyped
|
|
32
|
+
# (Integer, Integer, Integer, RenderContext) -> Enumerable[untyped]
|
|
33
|
+
def slice: (Integer start, Integer stop, Integer step, RenderContext context) -> untyped
|
|
34
34
|
|
|
35
35
|
# (:data | :numeric | :string | :boolean, RenderContext) -> untyped
|
|
36
36
|
def to_primitive: (:data | :numeric | :string | :boolean hint, Luoma::RenderContext context) -> untyped
|
data/sig/luoma/environment.rbs
CHANGED
|
@@ -157,17 +157,17 @@ module Luoma
|
|
|
157
157
|
# () -> void
|
|
158
158
|
def setup_tags_filters_and_predicates: () -> void
|
|
159
159
|
|
|
160
|
-
# (untyped, untyped, RenderContext
|
|
161
|
-
def contains?: (untyped left, untyped right, RenderContext context
|
|
160
|
+
# (untyped, untyped, RenderContext) -> bool
|
|
161
|
+
def contains?: (untyped left, untyped right, RenderContext context) -> bool
|
|
162
162
|
|
|
163
|
-
# (untyped, untyped, RenderContext
|
|
164
|
-
def eq?: (untyped left, untyped right, RenderContext context
|
|
163
|
+
# (untyped, untyped, RenderContext) -> bool
|
|
164
|
+
def eq?: (untyped left, untyped right, RenderContext context) -> bool
|
|
165
165
|
|
|
166
|
-
# (untyped, untyped, RenderContext
|
|
167
|
-
def lt?: (untyped left, untyped right, RenderContext context
|
|
166
|
+
# (untyped, untyped, RenderContext) -> bool?
|
|
167
|
+
def lt?: (untyped left, untyped right, RenderContext context) -> bool?
|
|
168
168
|
|
|
169
|
-
# (untyped, untyped, RenderContext
|
|
170
|
-
def cmp: (untyped left, untyped right, RenderContext context
|
|
169
|
+
# (untyped, untyped, RenderContext) -> (-1 | 1 | 0 | nil)
|
|
170
|
+
def cmp: (untyped left, untyped right, RenderContext context) -> (-1 | 1 | 0 | nil)
|
|
171
171
|
|
|
172
172
|
# (untyped) -> bool
|
|
173
173
|
def nothing?: (untyped obj) -> bool
|
data/sig/luoma/escape.rbs
CHANGED
|
@@ -7,9 +7,16 @@ module Luoma
|
|
|
7
7
|
|
|
8
8
|
UNESCAPE_MAP: Hash[String, String]
|
|
9
9
|
|
|
10
|
+
RE_ESCAPE_JS: ::Regexp
|
|
11
|
+
|
|
12
|
+
ESCAPE_JS_MAP: Hash[String, String]
|
|
13
|
+
|
|
10
14
|
# (String) -> String
|
|
11
15
|
def self.escape: (String s) -> String
|
|
12
16
|
|
|
13
17
|
# (String) -> String
|
|
14
18
|
def self.unescape: (String s) -> String
|
|
19
|
+
|
|
20
|
+
# (String) -> String
|
|
21
|
+
def self.escape_js: (String s) -> String
|
|
15
22
|
end
|
data/sig/luoma/filters/array.rbs
CHANGED
|
@@ -70,5 +70,9 @@ module Luoma
|
|
|
70
70
|
# Return the maximum numeric value found in _left_.
|
|
71
71
|
# Coerce _left_ to an array if it's not an array already.
|
|
72
72
|
def self.max: (FilterContext context, untyped left, ?untyped key) -> untyped
|
|
73
|
+
|
|
74
|
+
# Return at most _count_ items from `_left` as a new array.
|
|
75
|
+
# Coerce _left_ to an array if it's not an array already.
|
|
76
|
+
def self.take: (FilterContext context, untyped left, untyped count) -> Array[untyped]
|
|
73
77
|
end
|
|
74
78
|
end
|
data/sig/luoma/filters/sort.rbs
CHANGED
|
@@ -4,12 +4,8 @@ module Luoma
|
|
|
4
4
|
|
|
5
5
|
def self.sort: (FilterContext context, untyped left, ?untyped? key) -> untyped
|
|
6
6
|
|
|
7
|
-
def self.sort_natural: (FilterContext context, untyped left, ?untyped? key) -> untyped
|
|
8
|
-
|
|
9
7
|
def self.sort_numeric: (FilterContext context, untyped left, ?untyped? key) -> untyped
|
|
10
8
|
|
|
11
|
-
def self.nil_safe_casecmp: (untyped left, untyped right) -> (untyped | 0 | 1 | -1)
|
|
12
|
-
|
|
13
9
|
def self.numeric_compare: (untyped left, untyped right, FilterContext context) -> untyped
|
|
14
10
|
|
|
15
11
|
def self.ints: (untyped obj, FilterContext context) -> (::Array[untyped] | untyped)
|
|
@@ -8,6 +8,10 @@ module Luoma
|
|
|
8
8
|
def initialize: (String | Array[String] search_path, ?default_extension: String?) -> void
|
|
9
9
|
|
|
10
10
|
def resolve_path: (String template_name) -> Pathname
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def child_path?: (Pathname child, Pathname parent) -> bool
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
# A file system template loader that caches parsed templates.
|
data.tar.gz.sig
CHANGED
|
Binary file
|