luoma 0.1.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 (152) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data/.rdoc_options +16 -0
  4. data/CHANGELOG.md +5 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +68 -0
  7. data/Rakefile +69 -0
  8. data/Steepfile +41 -0
  9. data/certs/jgrp.pem +27 -0
  10. data/docs/configuration.md +180 -0
  11. data/docs/custom_filters.md +3 -0
  12. data/docs/custom_tags.md +3 -0
  13. data/docs/expressions.md +3 -0
  14. data/docs/extension_types.md +41 -0
  15. data/docs/filter_reference.md +1702 -0
  16. data/docs/index.md +73 -0
  17. data/docs/luoma_for_template_authors.md +3 -0
  18. data/docs/markdown.md +111 -0
  19. data/docs/predicate_reference.md +3 -0
  20. data/docs/static_analysis.md +3 -0
  21. data/docs/tag_reference.md +352 -0
  22. data/docs/template_loaders.md +177 -0
  23. data/docs/undefined_variables.md +3 -0
  24. data/docs-requirements.txt +11 -0
  25. data/docs_/cycle.md +17 -0
  26. data/docs_/font_rendering_example.md +157 -0
  27. data/docs_/header_block_example.md +51 -0
  28. data/docs_/increment_and_decrement.md +49 -0
  29. data/docs_/logo_style_example.md +40 -0
  30. data/docs_/migration.md +11 -0
  31. data/docs_/notes.md +19 -0
  32. data/lib/luoma/cache.rb +80 -0
  33. data/lib/luoma/chain_hash.rb +54 -0
  34. data/lib/luoma/context.rb +227 -0
  35. data/lib/luoma/drop.rb +84 -0
  36. data/lib/luoma/drops/block.rb +33 -0
  37. data/lib/luoma/drops/expression.rb +29 -0
  38. data/lib/luoma/drops/html_safe.rb +36 -0
  39. data/lib/luoma/drops/range.rb +69 -0
  40. data/lib/luoma/drops/undefined.rb +119 -0
  41. data/lib/luoma/environment.rb +479 -0
  42. data/lib/luoma/errors.rb +79 -0
  43. data/lib/luoma/escape.rb +35 -0
  44. data/lib/luoma/expression.rb +1271 -0
  45. data/lib/luoma/filter.rb +97 -0
  46. data/lib/luoma/filters/array.rb +372 -0
  47. data/lib/luoma/filters/date.rb +19 -0
  48. data/lib/luoma/filters/default.rb +16 -0
  49. data/lib/luoma/filters/json.rb +14 -0
  50. data/lib/luoma/filters/math.rb +84 -0
  51. data/lib/luoma/filters/size.rb +13 -0
  52. data/lib/luoma/filters/slice.rb +52 -0
  53. data/lib/luoma/filters/sort.rb +113 -0
  54. data/lib/luoma/filters/string.rb +186 -0
  55. data/lib/luoma/fnv.rb +15 -0
  56. data/lib/luoma/lexer.rb +106 -0
  57. data/lib/luoma/lexer_legacy.rb +396 -0
  58. data/lib/luoma/lexer_unified.rb +279 -0
  59. data/lib/luoma/loader.rb +50 -0
  60. data/lib/luoma/loaders/choice_loader.rb +20 -0
  61. data/lib/luoma/loaders/file_system_loader.rb +75 -0
  62. data/lib/luoma/loaders/mixins.rb +52 -0
  63. data/lib/luoma/markup.rb +109 -0
  64. data/lib/luoma/parser.rb +252 -0
  65. data/lib/luoma/parser_unified.rb +979 -0
  66. data/lib/luoma/predicates/blank.rb +19 -0
  67. data/lib/luoma/predicates/defined.rb +10 -0
  68. data/lib/luoma/predicates/empty.rb +15 -0
  69. data/lib/luoma/predicates/type.rb +35 -0
  70. data/lib/luoma/static_analysis.rb +427 -0
  71. data/lib/luoma/tags/assign.rb +63 -0
  72. data/lib/luoma/tags/break.rb +23 -0
  73. data/lib/luoma/tags/capture.rb +43 -0
  74. data/lib/luoma/tags/case.rb +137 -0
  75. data/lib/luoma/tags/comment.rb +17 -0
  76. data/lib/luoma/tags/continue.rb +23 -0
  77. data/lib/luoma/tags/define.rb +42 -0
  78. data/lib/luoma/tags/else.rb +30 -0
  79. data/lib/luoma/tags/for.rb +108 -0
  80. data/lib/luoma/tags/if.rb +109 -0
  81. data/lib/luoma/tags/import.rb +91 -0
  82. data/lib/luoma/tags/include.rb +77 -0
  83. data/lib/luoma/tags/output.rb +20 -0
  84. data/lib/luoma/tags/raw.rb +26 -0
  85. data/lib/luoma/tags/render.rb +92 -0
  86. data/lib/luoma/tags/with.rb +55 -0
  87. data/lib/luoma/template.rb +178 -0
  88. data/lib/luoma/token.rb +84 -0
  89. data/lib/luoma/version.rb +5 -0
  90. data/lib/luoma.rb +75 -0
  91. data/sig/luoma/cache.rbs +44 -0
  92. data/sig/luoma/chain_hash.rbs +26 -0
  93. data/sig/luoma/context.rbs +115 -0
  94. data/sig/luoma/drop.rbs +44 -0
  95. data/sig/luoma/drops/block.rbs +10 -0
  96. data/sig/luoma/drops/expression.rbs +10 -0
  97. data/sig/luoma/drops/html_safe.rbs +14 -0
  98. data/sig/luoma/drops/range.rbs +15 -0
  99. data/sig/luoma/drops/undefined.rbs +27 -0
  100. data/sig/luoma/environment.rbs +212 -0
  101. data/sig/luoma/errors.rbs +67 -0
  102. data/sig/luoma/escape.rbs +15 -0
  103. data/sig/luoma/expression.rbs +514 -0
  104. data/sig/luoma/filter.rbs +66 -0
  105. data/sig/luoma/filters/array.rbs +74 -0
  106. data/sig/luoma/filters/date.rbs +9 -0
  107. data/sig/luoma/filters/default.rbs +8 -0
  108. data/sig/luoma/filters/json.rbs +7 -0
  109. data/sig/luoma/filters/math.rbs +37 -0
  110. data/sig/luoma/filters/size.rbs +6 -0
  111. data/sig/luoma/filters/slice.rbs +10 -0
  112. data/sig/luoma/filters/sort.rbs +17 -0
  113. data/sig/luoma/filters/string.rbs +103 -0
  114. data/sig/luoma/fnv.rbs +3 -0
  115. data/sig/luoma/lexer.rbs +42 -0
  116. data/sig/luoma/lexer_legacy.rbs +81 -0
  117. data/sig/luoma/lexer_unified.rbs +49 -0
  118. data/sig/luoma/loader.rbs +40 -0
  119. data/sig/luoma/loaders/choice_loader.rbs +9 -0
  120. data/sig/luoma/loaders/file_system_loader.rbs +19 -0
  121. data/sig/luoma/loaders/mixins.rbs +16 -0
  122. data/sig/luoma/markup.rbs +68 -0
  123. data/sig/luoma/parser.rbs +105 -0
  124. data/sig/luoma/parser_unified.rbs +150 -0
  125. data/sig/luoma/predicates/blank.rbs +6 -0
  126. data/sig/luoma/predicates/defined.rbs +6 -0
  127. data/sig/luoma/predicates/empty.rbs +6 -0
  128. data/sig/luoma/predicates/type.rbs +21 -0
  129. data/sig/luoma/static_analysis.rbs +141 -0
  130. data/sig/luoma/tags/assign.rbs +12 -0
  131. data/sig/luoma/tags/break.rbs +11 -0
  132. data/sig/luoma/tags/capture.rbs +14 -0
  133. data/sig/luoma/tags/case.rbs +36 -0
  134. data/sig/luoma/tags/comment.rbs +11 -0
  135. data/sig/luoma/tags/continue.rbs +11 -0
  136. data/sig/luoma/tags/define.rbs +16 -0
  137. data/sig/luoma/tags/else.rbs +17 -0
  138. data/sig/luoma/tags/for.rbs +26 -0
  139. data/sig/luoma/tags/if.rbs +45 -0
  140. data/sig/luoma/tags/import.rbs +13 -0
  141. data/sig/luoma/tags/include.rbs +13 -0
  142. data/sig/luoma/tags/output.rbs +13 -0
  143. data/sig/luoma/tags/raw.rbs +11 -0
  144. data/sig/luoma/tags/render.rbs +13 -0
  145. data/sig/luoma/tags/with.rbs +15 -0
  146. data/sig/luoma/template.rbs +117 -0
  147. data/sig/luoma/token.rbs +81 -0
  148. data/sig/luoma.rbs +11 -0
  149. data/zensical.toml +363 -0
  150. data.tar.gz.sig +0 -0
  151. metadata +233 -0
  152. metadata.gz.sig +0 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: acb537ba303e1ce24a308b621d0ea4f878531d04d321d0ead9e2a261c2b76832
4
+ data.tar.gz: cc19b11460124b48b80766259d508408570323e596b6dbea5ee120edb874d298
5
+ SHA512:
6
+ metadata.gz: 05e51ff2461365ee5b41c0bd50545dc5c7444e44ee18da6fe4b15aa058cf9cc4d6d1e43420a41c61d9e37fa9fcdce096842212dd54f52f4ecd0cd34434799b04
7
+ data.tar.gz: 76bc4f2e919b162257b4eaf55331e7b436614442433d40056bb759ead9392a7099e195185cb8bed78219dbcd14380c93e2a89117c3d2ddfbe63e3396cdaab2e3
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ ES)��IX`�x%u j�l�vC���x�k��e���Ֆ3�wXmUuc р��0b�4QY���A���#��EL+L���[����S� J���1��i��'衐�#6?�$s������[t
2
+ �/�4m�JB_�;�I�w��?~�~��So�(� �$�n~��
data/.rdoc_options ADDED
@@ -0,0 +1,16 @@
1
+ # .rdoc_options
2
+ title: Luoma Ruby
3
+ main_page: README.md
4
+ markup: markdown
5
+ exclude:
6
+ - bin/
7
+ - dev.rb
8
+ - Gemfile
9
+ - Gemfile.lock
10
+ - performance/
11
+ - Rakefile
12
+ - sig/
13
+ - Steepfile
14
+ - test/
15
+ - tmp/
16
+ - docs/
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-06-05
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 James Prior
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ <h1 align="center">Luoma - Ruby</h1>
2
+
3
+ <p align="center">A modern template engine for Ruby.</p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/jg-rp/luoma-ruby/blob/main/LICENSE.txt">
7
+ <img alt="GitHub License" src="https://img.shields.io/github/license/jg-rp/luoma-ruby?style=flat-square">
8
+ </a>
9
+ <a href="https://github.com/jg-rp/luoma-ruby/actions">
10
+ <img src="https://img.shields.io/github/actions/workflow/status/jg-rp/luoma-ruby/main.yml?branch=main&label=tests&style=flat-square" alt="Tests">
11
+ </a>
12
+ <br>
13
+ <a href="https://rubygems.org/gems/luoma">
14
+ <img alt="Gem Version" src="https://img.shields.io/gem/v/luoma?style=flat-square">
15
+ </a>
16
+ <a href="https://github.com/jg-rp/luoma-ruby">
17
+ <img alt="Static Badge" src="https://img.shields.io/badge/Ruby-3.1%20%7C%203.2%20%7C%203.3%20%7C%203.4-CC342D?style=flat-square">
18
+ </a>
19
+ </p>
20
+
21
+ **Table of Contents**
22
+
23
+ - [Install](#install)
24
+ - [Example](#example)
25
+ - [Links](#links)
26
+ - [License](#license)
27
+
28
+ ## Install
29
+
30
+ Add `'luoma'` to your Gemfile:
31
+
32
+ ```
33
+ gem 'luoma', '~> 0.1.0'
34
+ ```
35
+
36
+ Or
37
+
38
+ ```
39
+ gem install luoma
40
+ ```
41
+
42
+ Or
43
+
44
+ ```
45
+ bundle add luoma
46
+ ```
47
+
48
+ ## Example
49
+
50
+ ```ruby
51
+ require "luoma"
52
+
53
+ template = Luoma.parse("Hello, {{ you }}!")
54
+ puts template.render("you" => "World") # Hello, World!
55
+ puts template.render("you" => "Luoma") # Hello, Luoma!
56
+ ```
57
+
58
+ ## Links
59
+
60
+ - Documentation: https://jg-rp.github.io/luoma/
61
+ - Change log: https://github.com/jg-rp/luoma-ruby/blob/main/CHANGELOG.md
62
+ - RubyGems: https://rubygems.org/gems/luoma
63
+ - Source code: https://github.com/jg-rp/luoma-ruby
64
+ - Issue tracker: https://github.com/jg-rp/luoma-ruby/issues
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "tmpdir"
5
+
6
+ require "bundler/gem_tasks"
7
+ require "minitest/test_task"
8
+
9
+ Minitest::TestTask.create
10
+
11
+ require "rubocop/rake_task"
12
+
13
+ RuboCop::RakeTask.new do |task|
14
+ task.plugins << "rubocop-minitest"
15
+ task.plugins << "rubocop-rake"
16
+ task.plugins << "rubocop-performance"
17
+ end
18
+
19
+ require "steep/rake_task"
20
+
21
+ Steep::RakeTask.new do |t|
22
+ t.check.severity_level = :error
23
+ t.watch.verbose
24
+ end
25
+
26
+ namespace :docs do
27
+ desc "Build the documentation using Zensical"
28
+ task :build do
29
+ puts "Building documentation with Zensical..."
30
+ build_success = system("zensical build --clean")
31
+ abort("Error: Failed to build documentation.") unless build_success
32
+ end
33
+
34
+ desc "Build and publish documentation to GitHub Pages"
35
+ task deploy: :build do
36
+ site_dir = "site"
37
+
38
+ abort("Error: Output directory '#{site_dir}' missing. Build may have failed.") unless Dir.exist?(site_dir)
39
+
40
+ # Retrieve the remote URL of your main Git repository
41
+ remote = `git config --get remote.origin.url`.strip
42
+ abort("Error: Could not determine Git remote URL for 'origin'.") if remote.empty?
43
+
44
+ target_branch = "gh-pages"
45
+
46
+ puts "Publishing '#{site_dir}' to #{target_branch} branch on #{remote}..."
47
+
48
+ # Use a temporary directory to avoid touching local working tree state
49
+ Dir.mktmpdir do |tmp_dir|
50
+ # Copy all contents of the build folder into the temp directory
51
+ FileUtils.cp_r(File.join(site_dir, "."), tmp_dir)
52
+
53
+ # Initialize a temporary git repo and push to the remote branch
54
+ Dir.chdir(tmp_dir) do
55
+ system("git init -q")
56
+ system("git checkout -b #{target_branch}")
57
+ system("git add .")
58
+ system("git commit -q -m 'Deploy documentation updates [skip ci]'")
59
+
60
+ pushed = system("git push --force #{remote} #{target_branch}")
61
+ abort("Error: Failed to push to GitHub Pages.") unless pushed
62
+ end
63
+ end
64
+
65
+ puts "Successfully deployed documentation to GitHub Pages!"
66
+ end
67
+ end
68
+
69
+ task default: %i[test rubocop steep]
data/Steepfile ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ target :lib do
6
+ signature "sig"
7
+ # ignore_signature "sig/test"
8
+
9
+ check "lib" # Directory name
10
+ # check "path/to/source.rb" # File name
11
+ # check "app/models/**/*.rb" # Glob
12
+ # ignore "lib/templates/*.rb"
13
+
14
+ library "base64"
15
+ library "bigdecimal"
16
+ library "cgi"
17
+ library "json"
18
+ library "strscan"
19
+ library "stringio"
20
+ library "time"
21
+ library "pathname"
22
+ library "monitor"
23
+
24
+ # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
25
+ # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
26
+ # configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
27
+ # configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
28
+ # configure_code_diagnostics do |hash| # You can setup everything yourself
29
+ # hash[D::Ruby::NoMethod] = :information
30
+ # end
31
+ end
32
+
33
+ # target :test do
34
+ # unreferenced! # Skip type checking the `lib` code when types in `test` target is changed
35
+ # signature "sig/test" # Put RBS files for tests under `sig/test`
36
+ # check "test" # Type check Ruby scripts under `test`
37
+ #
38
+ # configure_code_diagnostics(D::Ruby.lenient) # Weak type checking for test code
39
+ #
40
+ # # library "pathname" # Standard libraries
41
+ # end
data/certs/jgrp.pem ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEhTCCAu2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBEMRYwFAYDVQQDDA1qYW1l
3
+ c2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
4
+ FgNjb20wHhcNMjUxMDI1MDc0ODI4WhcNMjYxMDI1MDc0ODI4WjBEMRYwFAYDVQQD
5
+ DA1qYW1lc2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
6
+ k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDYTvQW
7
+ 0JzsHenYu4w0iHgJRtU9uK8vkDx1wlMioKlPFkaiMfxqUcTTfg80U/n8n/KKmpdq
8
+ y2rpUqv+VPE2nrfCUncf7ukqs+gF8uehbciMXV4iOzsZmqmtlPUJiGuSyO8REe/O
9
+ ERAyxYV8y5psF+WnW4uQzsWCKTnKbexIc6rf237veuYvYoBpnodJu0mJ0m9tBCFU
10
+ qH3Nk1sHBRIEEwn+nTiIiOMb+QMU5p0NIQkIhJO3el5nEQgIGxMPnie7PnwxPquQ
11
+ 1rouuISbLmQy5oKYc9kScwnHdAu2PtnnJFNNuQ37vED375iQgMem/tK8EgMm9/Qr
12
+ Qxd2DQqqehgtEySWntGHTxNLXQouqHIRyqzkbcCatJqC+PBBql1x2N1KqgyvbLM3
13
+ B0dZCdJECr5J8zrluMxYsy7uGMmLz3LZ7PskGaJg4XxXP3j/B/e03/AxDj0neZZu
14
+ xuDLIfMDSNILO1pskMc+Xp0xdnkbS4IjiMDTrhqABuEcdNYH/GpyTiA/I0cCAwEA
15
+ AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBR4j7UhnpT9
16
+ mLcAkshwkFtwVpMdejAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
17
+ bTAiBgNVHRIEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNvbTANBgkqhkiG9w0B
18
+ AQsFAAOCAYEAbTkg946BsGPQFX3jskBjwINeFnMP0IppuStonPQv3SNQu1SWP0xS
19
+ o1Qji1dxkWDfOcp1ffrRMUo7ecrJbTqQ5Sdx+7zw0OLodSVqSu1LMIVDw/T73smg
20
+ ADO2r3eZ8vxCKh7z4wUeKXvmBXi5NvpmvhH7D+qeG4OMLI8jnfmfbSHZQZbe2tpd
21
+ tJC+MYVok/m91fBrfv5wqLsR5QiJl3vcVhG+xACd46gr3JZFOxBqpSJrCYL8Gwqx
22
+ BaKEySCLGqkb0d1Rnt2/zTR2KtW2QJVH9rBMnJ+LsPEFKs1UHH4qHzxmwmmaQLon
23
+ MoE4UJ51GHGrjJ4WsHOUSOy5UbjaQec3kuIDwTXKbHws0J0Ut9S4S38opZKdQHDD
24
+ PUBpY0QA1CgE/VjKAYEDHOJyfUCNOl+sEESM+WxEXZAUVZLrFF7MljJqyQq+JyTD
25
+ vFKHe1ZAA76NFrPscB/ODPTBK8i+u4gwrD1UAsVAHuIbOpQu4LYAujluCu5cl2A8
26
+ HhmlCT7sTkqG
27
+ -----END CERTIFICATE-----
@@ -0,0 +1,180 @@
1
+ ---
2
+ title: Setup
3
+ ---
4
+
5
+ # Luoma environments
6
+
7
+ Template parsing and rendering behavior is configured using an instance of `Luoma::Environment`. Once configured, parse templates with `Environment#parse(source)` or `Environment#get_template(name)`, both of which return an instance of `Luoma::Template`.
8
+
9
+ An `Environment` is where you'd register custom filters or tags, or define variables that should be available to all templates, for example.
10
+
11
+ !!! tip
12
+
13
+ In addition to the environment options shown here, `Luoma::Environment` is designed to be extended. You can, for example, override `Luoma::Environment#eq?` to redefine expression equality, or override `Luoma::Environment#serialize` to change the way objects are rendered. See [environment.rb](https://github.com/jg-rp/luoma-ruby/blob/main/lib/luoma/expression.rb) for a complete method reference and default implementations.
14
+
15
+ ## The default environment
16
+
17
+ The default Luoma environment and new instances of `Luoma::Environment` constructed without any arguments are equivalent to passing the following arguments to `Environment.new`.
18
+
19
+ ```ruby
20
+ env = Luoma::Environment.new(
21
+ auto_trim: nil,
22
+ globals: nil,
23
+ lexer: Luoma::UnifiedLexer,
24
+ loader: Luoma::HashLoader.new({}),
25
+ max_assign_score_cumulative: nil,
26
+ max_assign_score: nil,
27
+ max_context_depth: 30,
28
+ max_render_score_cumulative: nil,
29
+ max_render_score: nil,
30
+ max_render_size: nil,
31
+ parser: Luoma::UnifiedParser,
32
+ strict: false,
33
+ suppress_blank_control_flow_blocks: true,
34
+ undefined: Luoma::UndefinedDrop
35
+ )
36
+ ```
37
+
38
+ Top-level convenience methods `Luoma.parse` and `Luoma.render` always use the default environment.
39
+
40
+ ## Managing tags, filters and predicates
41
+
42
+ New instances of `Luoma::Environment` and the [default Luoma environment](#the-default-environment) have all standard tags, filters and predicates enabled by default. `Environment.tags`, `Environment.filters` and `Environment.predicates` are hashes mapping strings to `_Tag`, filter callables and predicate callables, respectively. You can add, remove, replace or alias tags, filters and predicates in an environment by updating these mappings after environment initialization.
43
+
44
+ This example removes th `{% include %}` tag and renames `downcase` to `lower` and `upcase` to `upper`.
45
+
46
+ ```ruby
47
+
48
+ require "luoma"
49
+
50
+ env = Luoma::Environment.new
51
+ env.tags.delete("include")
52
+ env.filters["lower"] = env.tags.delete("downcase")
53
+ env.filters["upper"] = env.tags.delete("upcase")
54
+ ```
55
+
56
+ Alternatively, you can extend `Luoma::Environment` and override `setup_tags_filters_and_predicates`.
57
+
58
+ ```ruby
59
+ require "luoma"
60
+
61
+ class MyLuomaEnv < Luoma::Environment
62
+ #: () -> void
63
+ def setup_tags_filters_and_predicates
64
+ super
65
+ @tags.delete("include")
66
+ end
67
+ end
68
+
69
+ env = MyLuomaEnv.new
70
+ # ...
71
+ ```
72
+
73
+ ## Auto trim
74
+
75
+ The `auto_trim` option sets the default whitespace trimming mode. Setting `auto_trim: "-"` or `auto_trim: "~"` is equivalent to adding `-` or `~` before every closing markup delimiter.
76
+
77
+ ```ruby
78
+ require "luoma"
79
+
80
+ source = <<~SOURCE.chomp
81
+ <ul>
82
+ {% for x in (1..4) %}
83
+ <li>{{ x }}</li>
84
+ {% endfor %}
85
+ </ul>
86
+ ---
87
+ SOURCE
88
+
89
+ puts Luoma::Environment.new.render(source)
90
+ puts Luoma::Environment.new(auto_trim: "~").render(source)
91
+ ```
92
+
93
+ ```html title="output"
94
+ <ul>
95
+ <li>1</li>
96
+
97
+ <li>2</li>
98
+
99
+ <li>3</li>
100
+
101
+ <li>4</li>
102
+ </ul>
103
+ ---
104
+ <ul>
105
+ <li>1</li>
106
+ <li>2</li>
107
+ <li>3</li>
108
+ <li>4</li>
109
+ </ul>
110
+ ---
111
+ ```
112
+
113
+ ## Global variables
114
+
115
+ Global template variables are those added by application developers, as opposed to local variables created by template authors with tags such as `{% assign %}` and `{% capture %}`. Globals can come from the following places, in order of highest to lowest priority.
116
+
117
+ 1. The _data_ argument to `Luoma.render`, `Luoma::Environment#render` or `Luoma::Template#render`.
118
+ 2. "overlay" or "matter" data provided by a template loader and bound to a `Luoma::Template` instance. This could be front matter parsed from the beginning of a template source file, or data from a database, for example.
119
+ 3. The `globals` argument to `Luoma.parse` or `Luoma::Environment#parse`. These variables are pinned to the resulting template.
120
+ 4. The `globals` argument when constructing a new `Luoma::Environment`. These variables are pinned to the environment and will be merged into other global data for every template rendered from the environment.
121
+
122
+ You can change the global variable source priority by extending and overriding `Luoma::Environment#makeGlobals` and/or `Luoma::Template#makeGlobals`.
123
+
124
+ ## Resource limits
125
+
126
+ For deployments where template authors are untrusted, you can set limits on some resources to avoid malicious templates from consuming too much memory or too many CPU cycles. If any limit is exceeded, a `Luoma::ResourceLimitError` is raised.
127
+
128
+ !!! note
129
+
130
+ The following "scores" are non-specific measures of usage modelled on Shopify/liquid resource limits.
131
+
132
+ These numbers are for illustration purposes. You'll need to do a bit of trial and error to find the limits that work best for you.
133
+
134
+ ```ruby
135
+ require "luoma"
136
+
137
+ env = Luoma::Environment.new(
138
+ # Maximum of 2000 "bytes" assigned with the assign/capture tags per template
139
+ # or partial template.
140
+ max_assign_score: 2000,
141
+
142
+ # Maximum of 10,000 "bytes" assigned with the assign/capture tags for the
143
+ # root template and all partial templates combined.
144
+ max_assign_score_cumulative: 10000,
145
+
146
+ # Maximum nesting of 30 `for` loops and/or `render` tags, for example.
147
+ max_context_depth: 30,
148
+
149
+ # Maximum of 1000 nodes (text and markup in the template syntax tree) rendered
150
+ # per template.
151
+ max_render_score: 1000,
152
+
153
+ # Maximum of 5000 nodes (text and markup in the template syntax tree) rendered
154
+ # for the root template and any rendered partial templates combined.
155
+ max_render_score_cumulative: 5000,
156
+
157
+ # Maximum of 15,000 bytes written to the output buffer.
158
+ max_render_size: 15000,
159
+ )
160
+ ```
161
+
162
+ ## Strict mode
163
+
164
+ When `strict: false` (the default) unknown filters, unknown predicates and filter argument errors are silently ignored at render time. When `strict: true`, `Luoma::FilterNotFoundError`, `Luoma::PredicateNotFoundError` or `Luoma::FilterArgumentError` is raised, all of which inherit from `Luoma::LuomaError`.
165
+
166
+ ```ruby
167
+ require "luoma"
168
+
169
+ source = "Hello, {{ you | title }}"
170
+
171
+ puts Luoma::Environment.new.render(source)
172
+ puts Luoma::Environment.new(strict: true).render(source)
173
+
174
+ # Hello,
175
+ # /home/james/projects/luoma-ruby/lib/luoma/expression.rb:195:in 'Luoma::FilteredExpression#evaluate_filter': Luoma::FilterNotFoundError: unknown filter "title"
176
+ # -> "Hello, {{ you | title }}":1:17
177
+ # |
178
+ # 1 | Hello, {{ you | title }}
179
+ # | ^^^^^ unknown filter "title"
180
+ ```
@@ -0,0 +1,3 @@
1
+ # Custom filters
2
+
3
+ TODO
@@ -0,0 +1,3 @@
1
+ # Custom tags
2
+
3
+ TODO:
@@ -0,0 +1,3 @@
1
+ # Expressions
2
+
3
+ TODO
@@ -0,0 +1,41 @@
1
+ # Extension types
2
+
3
+ A drop is a developer-defined type that plays nicely with Luoma tags, filters, predicates and operators. Drops are often used to implement lazy data retrieval or context sensitive logic.
4
+
5
+ The base drop, `Luoma::Drop`, is falsy, is an empty iterable/enumerable, is equal to nothing (including itself) and renders as an empty string.
6
+
7
+ TODO: document the complete Drop API.
8
+
9
+ ## Built-in drops
10
+
11
+ ### `BlockDrop`
12
+
13
+ A `Luoma::BlockDrop` encapsulates a block of Luoma markup. When an instance of `BlockDrop` is rendered, its internal markup is rendered to the output stream using variables from the current context, in the scope where it is "called".
14
+
15
+ Similarly, if a `BlockDrop` is coerced to a string - passed to a filter or tag that expects a string value - its internal markup is rendered into a new string using variables from the current scope.
16
+
17
+ Instances of `BlockDrop` are produced by the built-in `{% define %}` tag. When rendered, `{% define some_name %}...{% enddefine %}` binds a `BlockDrop` to the given name without writing to the output stream. This means blocks can be passed around and rendered multiple times in different contexts without loading a separate template.
18
+
19
+ By default, Luoma has special rules for rendering **arrays** of `BlockDrop`. Given an array of `BlockDrop` instances and no other types, Luoma will render each block to the output stream separated by a single newline character. If an array contains `BlockDrop` instances mixed with strings, those blocks and strings are rendered in order without a separating newline. Otherwise the array is considered data and is output in JSON.
20
+
21
+ ### `ExpressionDrop`
22
+
23
+ A `Luoma::ExpressionDrop` encapsulates a single Luoma expression for later evaluation. When rendered or coerced to a string, `ExpressionDrop` produces a textual representation of its expression.
24
+
25
+ Lambda literals (`(a, b) -> expression`) evaluate to an instance of `ExpressionDrop`, without capturing anything about the scope in which it is defined. Internally, Luoma's filter application operator (`|`) recognizes instances of `ExpressionDrop` as user-defined filters, and some built-in filters accept instances of `ExpressionDrop` as arguments.
26
+
27
+ ### `RangeDrop`
28
+
29
+ Range literals (`(1..5)`) evaluate to instances of `Luoma::RangeDrop`. When iterated, `RangeDrop` yields integers from its range of integer values.
30
+
31
+ When rendered or coerced to a string, `RangeDrop` produces a JSON-like array of integers.
32
+
33
+ ### `UndefinedDrop`
34
+
35
+ Undefined variables resolve to an instance of of `Luoma::UndefinedDrop`, or one of its subclasses. Undefined variable behavior is configured by passing a `Luoma::UndefinedDrop` singleton to `Luoma::Environment.new`.
36
+
37
+ See [Undefined variables](./undefined_variables.md) for a breakdown of the built-in `Undefined` types.
38
+
39
+ ## Custom drops
40
+
41
+ TODO: