tree_haver 5.0.5 → 7.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +299 -660
- data/lib/tree_haver/backend_api.rb +78 -35
- data/lib/tree_haver/backend_context.rb +28 -0
- data/lib/tree_haver/backend_registry.rb +119 -382
- data/lib/tree_haver/backends/citrus.rb +55 -53
- data/lib/tree_haver/backends/ffi.rb +105 -101
- data/lib/tree_haver/backends/java.rb +92 -76
- data/lib/tree_haver/backends/mri.rb +44 -39
- data/lib/tree_haver/backends/parslet.rb +53 -48
- data/lib/tree_haver/backends/prism.rb +140 -43
- data/lib/tree_haver/backends/psych.rb +30 -26
- data/lib/tree_haver/backends/rust.rb +21 -17
- data/lib/tree_haver/backends/tslp.rb +274 -0
- data/lib/tree_haver/base/comment.rb +320 -0
- data/lib/tree_haver/base/language.rb +2 -2
- data/lib/tree_haver/base/node.rb +39 -31
- data/lib/tree_haver/base/parser.rb +4 -0
- data/lib/tree_haver/base/point.rb +3 -3
- data/lib/tree_haver/base/tree.rb +1 -1
- data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
- data/lib/tree_haver/contracts.rb +1025 -0
- data/lib/tree_haver/grammar_finder.rb +125 -70
- data/lib/tree_haver/kaitai_backend.rb +30 -0
- data/lib/tree_haver/language.rb +36 -37
- data/lib/tree_haver/language_pack.rb +41 -0
- data/lib/tree_haver/language_registry.rb +34 -3
- data/lib/tree_haver/library_path_utils.rb +7 -7
- data/lib/tree_haver/node.rb +40 -31
- data/lib/tree_haver/parser.rb +44 -37
- data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
- data/lib/tree_haver/path_validator.rb +39 -36
- data/lib/tree_haver/peg_backends.rb +76 -0
- data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
- data/lib/tree_haver/rspec.rb +1 -31
- data/lib/tree_haver/tree.rb +16 -7
- data/lib/tree_haver/version.rb +5 -14
- data/lib/tree_haver.rb +536 -1240
- data/sig/tree_haver.rbs +1 -229
- data.tar.gz.sig +0 -0
- metadata +154 -70
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -1393
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -359
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/tree_haver/base.rb +0 -12
- data/lib/tree_haver/compat.rb +0 -43
- data/lib/tree_haver/rspec/testable_node.rb +0 -217
- data/sig/tree_haver/backends.rbs +0 -352
- data/sig/tree_haver/grammar_finder.rbs +0 -29
- data/sig/tree_haver/path_validator.rbs +0 -32
data/README.md
CHANGED
|
@@ -1,32 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
[🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
|
|
4
|
-
[🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
|
|
5
|
-
[🖼️ruby-lang-i]: https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg
|
|
6
|
-
[🖼️ruby-lang]: https://www.ruby-lang.org/
|
|
7
|
-
[🖼️kettle-rb-i]: https://logos.galtzo.com/assets/images/kettle-rb/avatar-192px.svg
|
|
8
|
-
[🖼️kettle-rb]: https://github.com/kettle-rb
|
|
1
|
+
<a href="https://github.com/structuredmerge"><img alt="structuredmerge Logo by GitHub" src="https://github.com/structuredmerge.png?size=192" width="12%" align="right"/></a> <a href="https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/tree_haver"><img alt="tree_haver Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/structuredmerge/structuredmerge-ruby/tree_haver/avatar-128px.svg" width="12%" align="right"/></a>
|
|
9
2
|
|
|
10
3
|
# 🌴 TreeHaver
|
|
11
4
|
|
|
12
|
-
[![Version][👽versioni]][👽
|
|
5
|
+
[![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Total downloads][👽dl-ranki]][👽dl-rank] [![CI Current][🚎11-c-wfi]][🚎11-c-wf]
|
|
13
6
|
|
|
14
|
-
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][
|
|
7
|
+
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know on Discord][✉️discord-invite] or [RubyForum][✉️ruby-forum], as I may have missed the notification.
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
---
|
|
17
10
|
|
|
18
11
|
`if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
|
|
19
12
|
|
|
20
|
-
[![
|
|
13
|
+
[![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
|
|
21
14
|
|
|
22
|
-
<details>
|
|
23
|
-
|
|
15
|
+
<details markdown="1">
|
|
16
|
+
<summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>
|
|
24
17
|
|
|
25
18
|
I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo).
|
|
26
19
|
|
|
27
20
|
</details>
|
|
28
21
|
|
|
29
|
-
## 🌻 Synopsis
|
|
22
|
+
## 🌻 Synopsis <a href="https://discord.gg/3qme4XHNKN"><img alt="Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px.svg" width="8%" align="right"/></a> <a href="https://ruby-toolbox.com"><img alt="ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5" src="https://logos.galtzo.com/assets/images/ruby-lang/avatar-128px.svg" width="8%" align="right"/></a>
|
|
30
23
|
|
|
31
24
|
TreeHaver is a cross-Ruby adapter for the [tree-sitter](https://tree-sitter.github.io/tree-sitter/), [Citrus][citrus], and [Parslet][parslet] parsing libraries and other dedicated parsing tools that works seamlessly across MRI Ruby, JRuby, and TruffleRuby. It provides a unified API for parsing source code using grammars, regardless of your Ruby implementation.
|
|
32
25
|
|
|
@@ -39,13 +32,13 @@ If you've used [Faraday](https://github.com/lostisland/faraday), [multi\_json](h
|
|
|
39
32
|
| **Faraday** | HTTP requests | Net::HTTP, Typhoeus, Patron, Excon |
|
|
40
33
|
| **multi\_json** | JSON parsing | Oj, Yajl, JSON gem |
|
|
41
34
|
| **multi\_xml** | XML parsing | Nokogiri, LibXML, Ox |
|
|
42
|
-
| **TreeHaver** | Code parsing | MRI, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus, Parslet |
|
|
35
|
+
| **TreeHaver** | Code parsing | TSLP, MRI, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, RBS, Citrus, Parslet, Kaitai |
|
|
43
36
|
|
|
44
37
|
**Learn once, write anywhere.**
|
|
45
38
|
|
|
46
39
|
**Write once, run anywhere.**
|
|
47
40
|
|
|
48
|
-
Just as Faraday lets you swap HTTP adapters without changing your code, TreeHaver lets
|
|
41
|
+
Just as Faraday lets you swap HTTP adapters without changing your code, TreeHaver lets merge providers report and select parsing backends through one registry. The high-level StructuredMerge providers default to the tree-sitter language-pack path when it supports the requested language, while the lower-level `TreeHaver::Parser` facade can still use native tree-sitter adapters and Ruby parser backends directly.
|
|
49
42
|
|
|
50
43
|
```ruby
|
|
51
44
|
# Your code stays the same regardless of backend
|
|
@@ -53,20 +46,20 @@ parser = TreeHaver::Parser.new
|
|
|
53
46
|
parser.language = TreeHaver::Language.from_library("/path/to/grammar.so")
|
|
54
47
|
tree = parser.parse(source_code)
|
|
55
48
|
|
|
56
|
-
# TreeHaver automatically picks the best available backend:
|
|
57
|
-
# - MRI: ruby_tree_sitter, tree_stump, ffi, prism, psych,
|
|
58
|
-
# - JRuby:
|
|
59
|
-
# - TruffleRuby: prism, psych,
|
|
60
|
-
# (tree-sitter
|
|
49
|
+
# TreeHaver::Parser automatically picks the best available parser facade backend:
|
|
50
|
+
# - MRI: ruby_tree_sitter, tree_stump, ffi, prism, psych, citrus, parslet
|
|
51
|
+
# - JRuby: java-tree-sitter / jtreesitter, ffi, prism, psych, citrus, parslet
|
|
52
|
+
# - TruffleRuby: prism, psych, citrus, parslet
|
|
53
|
+
# (the FFI tree-sitter adapter is not used on TruffleRuby because of struct-by-value limits)
|
|
61
54
|
```
|
|
62
55
|
|
|
63
56
|
### Key Features
|
|
64
57
|
|
|
65
58
|
- **Universal Ruby Support**: Works on MRI Ruby, JRuby, and TruffleRuby
|
|
66
|
-
- **
|
|
59
|
+
- **Backend Registry** - Choose the right backend for your needs:
|
|
67
60
|
- **Tree-sitter Backends** (high-performance, incremental parsing):
|
|
61
|
+
- **Tree-sitter Language Pack (TSLP)**: Default provider path for StructuredMerge language-family gems that use `tree-sitter-language-pack`
|
|
68
62
|
- **MRI Backend**: Leverages [`ruby_tree_sitter`][ruby_tree_sitter] gem (C extension, fastest on MRI)
|
|
69
|
-
- **Note**: `ruby_tree_sitter` currently requires unreleased fixes in the `pboling` fork, `tree_haver` branch.
|
|
70
63
|
- **Rust Backend**: Uses [`tree_stump`][tree_stump] gem (Rust with precompiled binaries)
|
|
71
64
|
- **Note**: Use `tree_stump` v0.2.0 or newer (fixes are released).
|
|
72
65
|
- **FFI Backend**: Pure Ruby FFI bindings to `libtree-sitter` (JRuby only; TruffleRuby's FFI doesn't support tree-sitter's struct-by-value returns)
|
|
@@ -76,12 +69,15 @@ tree = parser.parse(source_code)
|
|
|
76
69
|
- **Psych Backend**: Ruby's YAML parser ([Psych][psych], stdlib)
|
|
77
70
|
- **Commonmarker Backend**: Fast Markdown parser ([Commonmarker][commonmarker], comrak Rust)
|
|
78
71
|
- **Markly Backend**: GitHub Flavored Markdown ([Markly][markly], cmark-gfm C)
|
|
79
|
-
|
|
72
|
+
- **RBS Backend**: Official RBS parser integration registered by `rbs-merge`
|
|
73
|
+
- **Pure Ruby Provider Backends**:
|
|
80
74
|
- **Citrus Backend**: Pure Ruby PEG parsing via [`citrus`][citrus] (no native dependencies)
|
|
81
75
|
- **Parslet Backend**: Pure Ruby PEG parsing via [`parslet`][parslet] (no native dependencies)
|
|
76
|
+
- **Binary Schema Support**:
|
|
77
|
+
- **Kaitai Struct Backend**: Backend reference and capability profile for binary schema analysis
|
|
82
78
|
- **Automatic Backend Selection**: Intelligently selects the best backend for your Ruby implementation
|
|
83
79
|
- **Language Agnostic**: Parse any language - Ruby, Markdown, YAML, JSON, Bash, TOML, JavaScript, etc.
|
|
84
|
-
- **Grammar Discovery**: Built-in `GrammarFinder` utility for
|
|
80
|
+
- **Grammar Discovery**: Built-in `GrammarFinder` utility for registration-first tree-sitter grammar resolution
|
|
85
81
|
- **Unified Position API**: Consistent `start_line`, `end_line`, `source_position` across all backends
|
|
86
82
|
- **Thread-Safe**: Built-in language registry with thread-safe caching
|
|
87
83
|
- **Minimal API Surface**: Simple, focused API that covers the most common use cases
|
|
@@ -94,9 +90,9 @@ TreeHaver has minimal dependencies and automatically selects the best backend fo
|
|
|
94
90
|
|
|
95
91
|
**Requires `ruby_tree_sitter` v2.0+**
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
TreeHaver normalizes backend failures behind `TreeHaver::Error` subclasses, which inherit from `StandardError`.
|
|
98
94
|
|
|
99
|
-
**Exception Mapping**: TreeHaver catches `TreeSitter::TreeSitterError` and its subclasses, converting them to `TreeHaver::NotAvailable` while preserving the original error message. This provides a consistent exception API across
|
|
95
|
+
**Exception Mapping**: TreeHaver catches `TreeSitter::TreeSitterError` and its subclasses where the MRI adapter exposes them, converting them to `TreeHaver::NotAvailable` while preserving the original error message. This provides a consistent exception API across backends:
|
|
100
96
|
|
|
101
97
|
| ruby\_tree\_sitter Exception | TreeHaver Exception | When It Occurs |
|
|
102
98
|
|-----------------------------------|---------------------------|----------------------------------------------|
|
|
@@ -107,11 +103,8 @@ In ruby\_tree\_sitter v2.0, all TreeSitter exceptions were changed to inherit fr
|
|
|
107
103
|
| `TreeSitter::QueryCreationError` | `TreeHaver::NotAvailable` | Query creation fails |
|
|
108
104
|
|
|
109
105
|
```ruby
|
|
110
|
-
# MRI tree-sitter
|
|
111
|
-
gem "ruby_tree_sitter",
|
|
112
|
-
github: "pboling/ruby-tree-sitter",
|
|
113
|
-
branch: "tree_haver",
|
|
114
|
-
require: false # DO NOT LOAD, because conflicts with FFI
|
|
106
|
+
# MRI tree-sitter backend
|
|
107
|
+
gem "ruby_tree_sitter", "~> 2.0", require: false
|
|
115
108
|
```
|
|
116
109
|
|
|
117
110
|
#### Rust Backend (tree\_stump)
|
|
@@ -292,153 +285,6 @@ tree-sitter is a powerful parser generator that creates incremental parsers for
|
|
|
292
285
|
- Managing different backends for different Ruby implementations is cumbersome
|
|
293
286
|
TreeHaver solves these problems by providing a unified API that automatically selects the appropriate backend for your Ruby implementation, allowing you to write code once and run it anywhere.
|
|
294
287
|
|
|
295
|
-
### The `*-merge` Gem Family
|
|
296
|
-
|
|
297
|
-
The `*-merge` gem family provides intelligent, AST-based merging for various file formats. At the foundation is [tree_haver][tree_haver], which provides a unified cross-Ruby parsing API that works seamlessly across MRI, JRuby, and TruffleRuby.
|
|
298
|
-
|
|
299
|
-
| Gem | Version / CI | Language<br>/ Format | Parser Backend(s) | Description |
|
|
300
|
-
|------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------:|----------------------|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
|
|
301
|
-
| [tree_haver][tree_haver] | [![Version][tree_haver-gem-i]][tree_haver-gem] <br/> [![CI][tree_haver-ci-i]][tree_haver-ci] | Multi | Supported Backends: MRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus, Parslet | **Foundation**: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP) |
|
|
302
|
-
| [ast-merge][ast-merge] | [![Version][ast-merge-gem-i]][ast-merge-gem] <br/> [![CI][ast-merge-ci-i]][ast-merge-ci] | Text | internal | **Infrastructure**: Shared base classes and merge logic for all `*-merge` gems |
|
|
303
|
-
| [bash-merge][bash-merge] | [![Version][bash-merge-gem-i]][bash-merge-gem] <br/> [![CI][bash-merge-ci-i]][bash-merge-ci] | Bash | [tree-sitter-bash][ts-bash] (via tree_haver) | Smart merge for Bash scripts |
|
|
304
|
-
| [commonmarker-merge][commonmarker-merge] | [![Version][commonmarker-merge-gem-i]][commonmarker-merge-gem] <br/> [![CI][commonmarker-merge-ci-i]][commonmarker-merge-ci] | Markdown | [Commonmarker][commonmarker] (via tree_haver) | Smart merge for Markdown (CommonMark via comrak Rust) |
|
|
305
|
-
| [dotenv-merge][dotenv-merge] | [![Version][dotenv-merge-gem-i]][dotenv-merge-gem] <br/> [![CI][dotenv-merge-ci-i]][dotenv-merge-ci] | Dotenv | internal | Smart merge for `.env` files |
|
|
306
|
-
| [json-merge][json-merge] | [![Version][json-merge-gem-i]][json-merge-gem] <br/> [![CI][json-merge-ci-i]][json-merge-ci] | JSON | [tree-sitter-json][ts-json] (via tree_haver) | Smart merge for JSON files |
|
|
307
|
-
| [jsonc-merge][jsonc-merge] | [![Version][jsonc-merge-gem-i]][jsonc-merge-gem] <br/> [![CI][jsonc-merge-ci-i]][jsonc-merge-ci] | JSONC | [tree-sitter-jsonc][ts-jsonc] (via tree_haver) | ⚠️ Proof of concept; Smart merge for JSON with Comments |
|
|
308
|
-
| [markdown-merge][markdown-merge] | [![Version][markdown-merge-gem-i]][markdown-merge-gem] <br/> [![CI][markdown-merge-ci-i]][markdown-merge-ci] | Markdown | [Commonmarker][commonmarker] / [Markly][markly] (via tree_haver), [Parslet][parslet] | **Foundation**: Shared base for Markdown mergers with inner code block merging |
|
|
309
|
-
| [markly-merge][markly-merge] | [![Version][markly-merge-gem-i]][markly-merge-gem] <br/> [![CI][markly-merge-ci-i]][markly-merge-ci] | Markdown | [Markly][markly] (via tree_haver) | Smart merge for Markdown (CommonMark via cmark-gfm C) |
|
|
310
|
-
| [prism-merge][prism-merge] | [![Version][prism-merge-gem-i]][prism-merge-gem] <br/> [![CI][prism-merge-ci-i]][prism-merge-ci] | Ruby | [Prism][prism] (`prism` std lib gem) | Smart merge for Ruby source files |
|
|
311
|
-
| [psych-merge][psych-merge] | [![Version][psych-merge-gem-i]][psych-merge-gem] <br/> [![CI][psych-merge-ci-i]][psych-merge-ci] | YAML | [Psych][psych] (`psych` std lib gem) | Smart merge for YAML files |
|
|
312
|
-
| [rbs-merge][rbs-merge] | [![Version][rbs-merge-gem-i]][rbs-merge-gem] <br/> [![CI][rbs-merge-ci-i]][rbs-merge-ci] | RBS | [tree-sitter-bash][ts-rbs] (via tree_haver), [RBS][rbs] (`rbs` std lib gem) | Smart merge for Ruby type signatures |
|
|
313
|
-
| [toml-merge][toml-merge] | [![Version][toml-merge-gem-i]][toml-merge-gem] <br/> [![CI][toml-merge-ci-i]][toml-merge-ci] | TOML | [Parslet + toml][toml], [Citrus + toml-rb][toml-rb], [tree-sitter-toml][ts-toml] (all via tree_haver) | Smart merge for TOML files |
|
|
314
|
-
|
|
315
|
-
#### Backend Platform Compatibility
|
|
316
|
-
|
|
317
|
-
tree_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:
|
|
318
|
-
|
|
319
|
-
| Platform 👉️<br> TreeHaver Backend 👇️ | MRI | JRuby | TruffleRuby | Notes |
|
|
320
|
-
|-------------------------------------------------|:---:|:-----:|:-----------:|----------------------------------------------------------------------------|
|
|
321
|
-
| **MRI** ([ruby_tree_sitter][ruby_tree_sitter]) | ✅ | ❌ | ❌ | C extension, MRI only |
|
|
322
|
-
| **Rust** ([tree_stump][tree_stump]) | ✅ | ❌ | ❌ | Rust extension via magnus/rb-sys, MRI only |
|
|
323
|
-
| **FFI** ([ffi][ffi]) | ✅ | ✅ | ❌ | TruffleRuby's FFI doesn't support `STRUCT_BY_VALUE` |
|
|
324
|
-
| **Java** ([jtreesitter][jtreesitter]) | ❌ | ✅ | ❌ | JRuby only, requires grammar JARs |
|
|
325
|
-
| **Prism** ([prism][prism]) | ✅ | ✅ | ✅ | Ruby parsing, stdlib in Ruby 3.4+ |
|
|
326
|
-
| **Psych** ([psych][psych]) | ✅ | ✅ | ✅ | YAML parsing, stdlib |
|
|
327
|
-
| **Citrus** ([citrus][citrus]) | ✅ | ✅ | ✅ | Pure Ruby PEG parser, no native dependencies |
|
|
328
|
-
| **Parslet** ([parslet][parslet]) | ✅ | ✅ | ✅ | Pure Ruby PEG parser, no native dependencies |
|
|
329
|
-
| **Commonmarker** ([commonmarker][commonmarker]) | ✅ | ❌ | ❓ | Rust extension for Markdown (via [commonmarker-merge][commonmarker-merge]) |
|
|
330
|
-
| **Markly** ([markly][markly]) | ✅ | ❌ | ❓ | C extension for Markdown (via [markly-merge][markly-merge]) |
|
|
331
|
-
|
|
332
|
-
**Legend**: ✅ = Works, ❌ = Does not work, ❓ = Untested
|
|
333
|
-
|
|
334
|
-
**Why some backends don't work on certain platforms**:
|
|
335
|
-
|
|
336
|
-
- **JRuby**: Runs on the JVM; cannot load native C/Rust extensions (`.so` files)
|
|
337
|
-
- **TruffleRuby**: Has C API emulation via Sulong/LLVM, but it doesn't expose all MRI internals that native extensions require (e.g., `RBasic.flags`, `rb_gc_writebarrier`)
|
|
338
|
-
- **FFI on TruffleRuby**: TruffleRuby's FFI implementation doesn't support returning structs by value, which tree-sitter's C API requires
|
|
339
|
-
|
|
340
|
-
**Example implementations** for the gem templating use case:
|
|
341
|
-
|
|
342
|
-
| Gem | Purpose | Description |
|
|
343
|
-
|--------------------------|-----------------|-----------------------------------------------|
|
|
344
|
-
| [kettle-dev][kettle-dev] | Gem Development | Gem templating tool using `*-merge` gems |
|
|
345
|
-
| [kettle-jem][kettle-jem] | Gem Templating | Gem template library with smart merge support |
|
|
346
|
-
|
|
347
|
-
[tree_haver]: https://github.com/kettle-rb/tree_haver
|
|
348
|
-
[ast-merge]: https://github.com/kettle-rb/ast-merge
|
|
349
|
-
[prism-merge]: https://github.com/kettle-rb/prism-merge
|
|
350
|
-
[psych-merge]: https://github.com/kettle-rb/psych-merge
|
|
351
|
-
[json-merge]: https://github.com/kettle-rb/json-merge
|
|
352
|
-
[jsonc-merge]: https://github.com/kettle-rb/jsonc-merge
|
|
353
|
-
[bash-merge]: https://github.com/kettle-rb/bash-merge
|
|
354
|
-
[rbs-merge]: https://github.com/kettle-rb/rbs-merge
|
|
355
|
-
[dotenv-merge]: https://github.com/kettle-rb/dotenv-merge
|
|
356
|
-
[toml-merge]: https://github.com/kettle-rb/toml-merge
|
|
357
|
-
[markdown-merge]: https://github.com/kettle-rb/markdown-merge
|
|
358
|
-
[markly-merge]: https://github.com/kettle-rb/markly-merge
|
|
359
|
-
[commonmarker-merge]: https://github.com/kettle-rb/commonmarker-merge
|
|
360
|
-
[kettle-dev]: https://github.com/kettle-rb/kettle-dev
|
|
361
|
-
[kettle-jem]: https://github.com/kettle-rb/kettle-jem
|
|
362
|
-
[tree_haver-gem]: https://bestgems.org/gems/tree_haver
|
|
363
|
-
[ast-merge-gem]: https://bestgems.org/gems/ast-merge
|
|
364
|
-
[prism-merge-gem]: https://bestgems.org/gems/prism-merge
|
|
365
|
-
[psych-merge-gem]: https://bestgems.org/gems/psych-merge
|
|
366
|
-
[json-merge-gem]: https://bestgems.org/gems/json-merge
|
|
367
|
-
[jsonc-merge-gem]: https://bestgems.org/gems/jsonc-merge
|
|
368
|
-
[bash-merge-gem]: https://bestgems.org/gems/bash-merge
|
|
369
|
-
[rbs-merge-gem]: https://bestgems.org/gems/rbs-merge
|
|
370
|
-
[dotenv-merge-gem]: https://bestgems.org/gems/dotenv-merge
|
|
371
|
-
[toml-merge-gem]: https://bestgems.org/gems/toml-merge
|
|
372
|
-
[markdown-merge-gem]: https://bestgems.org/gems/markdown-merge
|
|
373
|
-
[markly-merge-gem]: https://bestgems.org/gems/markly-merge
|
|
374
|
-
[commonmarker-merge-gem]: https://bestgems.org/gems/commonmarker-merge
|
|
375
|
-
[kettle-dev-gem]: https://bestgems.org/gems/kettle-dev
|
|
376
|
-
[kettle-jem-gem]: https://bestgems.org/gems/kettle-jem
|
|
377
|
-
[tree_haver-gem-i]: https://img.shields.io/gem/v/tree_haver.svg
|
|
378
|
-
[ast-merge-gem-i]: https://img.shields.io/gem/v/ast-merge.svg
|
|
379
|
-
[prism-merge-gem-i]: https://img.shields.io/gem/v/prism-merge.svg
|
|
380
|
-
[psych-merge-gem-i]: https://img.shields.io/gem/v/psych-merge.svg
|
|
381
|
-
[json-merge-gem-i]: https://img.shields.io/gem/v/json-merge.svg
|
|
382
|
-
[jsonc-merge-gem-i]: https://img.shields.io/gem/v/jsonc-merge.svg
|
|
383
|
-
[bash-merge-gem-i]: https://img.shields.io/gem/v/bash-merge.svg
|
|
384
|
-
[rbs-merge-gem-i]: https://img.shields.io/gem/v/rbs-merge.svg
|
|
385
|
-
[dotenv-merge-gem-i]: https://img.shields.io/gem/v/dotenv-merge.svg
|
|
386
|
-
[toml-merge-gem-i]: https://img.shields.io/gem/v/toml-merge.svg
|
|
387
|
-
[markdown-merge-gem-i]: https://img.shields.io/gem/v/markdown-merge.svg
|
|
388
|
-
[markly-merge-gem-i]: https://img.shields.io/gem/v/markly-merge.svg
|
|
389
|
-
[commonmarker-merge-gem-i]: https://img.shields.io/gem/v/commonmarker-merge.svg
|
|
390
|
-
[kettle-dev-gem-i]: https://img.shields.io/gem/v/kettle-dev.svg
|
|
391
|
-
[kettle-jem-gem-i]: https://img.shields.io/gem/v/kettle-jem.svg
|
|
392
|
-
[tree_haver-ci-i]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml/badge.svg
|
|
393
|
-
[ast-merge-ci-i]: https://github.com/kettle-rb/ast-merge/actions/workflows/current.yml/badge.svg
|
|
394
|
-
[prism-merge-ci-i]: https://github.com/kettle-rb/prism-merge/actions/workflows/current.yml/badge.svg
|
|
395
|
-
[psych-merge-ci-i]: https://github.com/kettle-rb/psych-merge/actions/workflows/current.yml/badge.svg
|
|
396
|
-
[json-merge-ci-i]: https://github.com/kettle-rb/json-merge/actions/workflows/current.yml/badge.svg
|
|
397
|
-
[jsonc-merge-ci-i]: https://github.com/kettle-rb/jsonc-merge/actions/workflows/current.yml/badge.svg
|
|
398
|
-
[bash-merge-ci-i]: https://github.com/kettle-rb/bash-merge/actions/workflows/current.yml/badge.svg
|
|
399
|
-
[rbs-merge-ci-i]: https://github.com/kettle-rb/rbs-merge/actions/workflows/current.yml/badge.svg
|
|
400
|
-
[dotenv-merge-ci-i]: https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml/badge.svg
|
|
401
|
-
[toml-merge-ci-i]: https://github.com/kettle-rb/toml-merge/actions/workflows/current.yml/badge.svg
|
|
402
|
-
[markdown-merge-ci-i]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml/badge.svg
|
|
403
|
-
[markly-merge-ci-i]: https://github.com/kettle-rb/markly-merge/actions/workflows/current.yml/badge.svg
|
|
404
|
-
[commonmarker-merge-ci-i]: https://github.com/kettle-rb/commonmarker-merge/actions/workflows/current.yml/badge.svg
|
|
405
|
-
[kettle-dev-ci-i]: https://github.com/kettle-rb/kettle-dev/actions/workflows/current.yml/badge.svg
|
|
406
|
-
[kettle-jem-ci-i]: https://github.com/kettle-rb/kettle-jem/actions/workflows/current.yml/badge.svg
|
|
407
|
-
[tree_haver-ci]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml
|
|
408
|
-
[ast-merge-ci]: https://github.com/kettle-rb/ast-merge/actions/workflows/current.yml
|
|
409
|
-
[prism-merge-ci]: https://github.com/kettle-rb/prism-merge/actions/workflows/current.yml
|
|
410
|
-
[psych-merge-ci]: https://github.com/kettle-rb/psych-merge/actions/workflows/current.yml
|
|
411
|
-
[json-merge-ci]: https://github.com/kettle-rb/json-merge/actions/workflows/current.yml
|
|
412
|
-
[jsonc-merge-ci]: https://github.com/kettle-rb/jsonc-merge/actions/workflows/current.yml
|
|
413
|
-
[bash-merge-ci]: https://github.com/kettle-rb/bash-merge/actions/workflows/current.yml
|
|
414
|
-
[rbs-merge-ci]: https://github.com/kettle-rb/rbs-merge/actions/workflows/current.yml
|
|
415
|
-
[dotenv-merge-ci]: https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml
|
|
416
|
-
[toml-merge-ci]: https://github.com/kettle-rb/toml-merge/actions/workflows/current.yml
|
|
417
|
-
[markdown-merge-ci]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml
|
|
418
|
-
[markly-merge-ci]: https://github.com/kettle-rb/markly-merge/actions/workflows/current.yml
|
|
419
|
-
[commonmarker-merge-ci]: https://github.com/kettle-rb/commonmarker-merge/actions/workflows/current.yml
|
|
420
|
-
[kettle-dev-ci]: https://github.com/kettle-rb/kettle-dev/actions/workflows/current.yml
|
|
421
|
-
[kettle-jem-ci]: https://github.com/kettle-rb/kettle-jem/actions/workflows/current.yml
|
|
422
|
-
[prism]: https://github.com/ruby/prism
|
|
423
|
-
[psych]: https://github.com/ruby/psych
|
|
424
|
-
[ffi]: https://github.com/ffi/ffi
|
|
425
|
-
[ts-json]: https://github.com/tree-sitter/tree-sitter-json
|
|
426
|
-
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
|
|
427
|
-
[ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
|
|
428
|
-
[ts-rbs]: https://github.com/joker1007/tree-sitter-rbs
|
|
429
|
-
[ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
|
|
430
|
-
[dotenv]: https://github.com/bkeepers/dotenv
|
|
431
|
-
[rbs]: https://github.com/ruby/rbs
|
|
432
|
-
[toml-rb]: https://github.com/emancu/toml-rb
|
|
433
|
-
[toml]: https://github.com/jm/toml
|
|
434
|
-
[markly]: https://github.com/ioquatix/markly
|
|
435
|
-
[commonmarker]: https://github.com/gjtorikian/commonmarker
|
|
436
|
-
[ruby_tree_sitter]: https://github.com/Faveod/ruby-tree-sitter
|
|
437
|
-
[tree_stump]: https://github.com/joker1007/tree_stump
|
|
438
|
-
[jtreesitter]: https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
|
|
439
|
-
[citrus]: https://github.com/mjackson/citrus
|
|
440
|
-
[parslet]: https://github.com/kschiess/parslet
|
|
441
|
-
|
|
442
288
|
### Comparison with Other Ruby AST / Parser Bindings
|
|
443
289
|
|
|
444
290
|
| Feature | [tree\_haver][📜src-gh] (this gem) | [ruby\_tree\_sitter][ruby_tree_sitter] | [tree\_stump][tree_stump] | [citrus][citrus] | [parslet][parslet] |
|
|
@@ -495,69 +341,58 @@ tree_haver supports multiple parsing backends, but not all backends work on all
|
|
|
495
341
|
- Performance is less critical than portability
|
|
496
342
|
- You don't need TreeHaver's unified API
|
|
497
343
|
|
|
344
|
+
[citrus]: https://github.com/mjackson/citrus
|
|
345
|
+
[parslet]: https://github.com/kschiess/parslet
|
|
346
|
+
[ruby_tree_sitter]: https://github.com/Faveod/ruby-tree-sitter
|
|
347
|
+
[tree_stump]: https://github.com/joker1007/tree_stump
|
|
348
|
+
[jtreesitter]: https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
|
|
349
|
+
[prism]: https://github.com/ruby/prism
|
|
350
|
+
[psych]: https://github.com/ruby/psych
|
|
351
|
+
[commonmarker]: https://github.com/gjtorikian/commonmarker
|
|
352
|
+
[markly]: https://github.com/ioquatix/markly
|
|
353
|
+
[ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
|
|
354
|
+
[ts-json]: https://github.com/tree-sitter/tree-sitter-json
|
|
355
|
+
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
|
|
356
|
+
[ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
|
|
357
|
+
|
|
498
358
|
## 💡 Info you can shake a stick at
|
|
499
359
|
|
|
500
|
-
| Tokens to Remember
|
|
501
|
-
|
|
502
|
-
| Works with
|
|
503
|
-
|
|
|
504
|
-
|
|
|
505
|
-
|
|
|
506
|
-
|
|
|
507
|
-
|
|
|
508
|
-
|
|
|
509
|
-
|
|
|
510
|
-
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
|
|
511
|
-
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
|
512
|
-
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
|
|
360
|
+
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
|
|
361
|
+
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
362
|
+
| Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf]|
|
|
363
|
+
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Get help from RubyForum][✉️ruby-forum-img]][✉️ruby-forum] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
|
364
|
+
| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on GitHub.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
|
|
365
|
+
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
|
|
366
|
+
| Compliance | [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Apache license compatibility: Category X][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
|
367
|
+
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
|
|
368
|
+
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
|
369
|
+
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
|
|
513
370
|
|
|
514
371
|
### Compatibility
|
|
515
372
|
|
|
516
|
-
Compatible with MRI Ruby
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
### Federated DVCS
|
|
523
|
-
|
|
524
|
-
<details markdown="1">
|
|
525
|
-
<summary>Find this repo on federated forges (Coming soon!)</summary>
|
|
526
|
-
|
|
527
|
-
| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
|
|
528
|
-
|--------------------------------------------------|------------------------------------------------------------------------------------|----------------------------|---------------------------|----------------------------|---------------------------|--------------------------------|
|
|
529
|
-
| 🧪 [kettle-rb/tree\_haver on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
|
|
530
|
-
| 🧊 [kettle-rb/tree\_haver on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
|
|
531
|
-
| 🐙 [kettle-rb/tree\_haver on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
|
|
532
|
-
| 🎮️ [Discord Server][🖼️galtzo-discord] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord] | [Let's][🖼️galtzo-discord] | [talk][🖼️galtzo-discord] | [about][🖼️galtzo-discord] | [this][🖼️galtzo-discord] | [library\!][🖼️galtzo-discord] |
|
|
533
|
-
|
|
534
|
-
</details>
|
|
535
|
-
|
|
536
|
-
[gh-discussions]: https://github.com/kettle-rb/tree_haver/discussions
|
|
373
|
+
Compatible with MRI Ruby 4.0.0+, and concordant releases of JRuby, and TruffleRuby.
|
|
374
|
+
CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
|
|
375
|
+
This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
|
|
376
|
+
may be higher than the gem's runtime compatibility floor when legacy Rubies are
|
|
377
|
+
not practical for the current toolchain.
|
|
537
378
|
|
|
538
|
-
|
|
379
|
+
<a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/kettle-dev/avatar-128px.svg" width="14%" align="right"/></a>
|
|
539
380
|
|
|
540
|
-
|
|
381
|
+
The _amazing_ test matrix is powered by the kettle-dev stack.
|
|
541
382
|
|
|
542
383
|
<details markdown="1">
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
[
|
|
548
|
-
|
|
549
|
-
-
|
|
550
|
-
|
|
551
|
-
-
|
|
552
|
-
|
|
553
|
-
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
|
|
557
|
-
|
|
558
|
-
- [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork]
|
|
559
|
-
|
|
560
|
-
- [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor]
|
|
384
|
+
<summary>How kettle-dev manages complexity in tests</summary>
|
|
385
|
+
|
|
386
|
+
| Gem | Source | Role | Total downloads |
|
|
387
|
+
|-----|--------|------|---------------------|
|
|
388
|
+
| [appraisal2](https://clickgems.clickhouse.com/dashboard/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [](https://clickgems.clickhouse.com/dashboard/appraisal2) |
|
|
389
|
+
| [appraisal2-rubocop](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) |
|
|
390
|
+
| [kettle-dev](https://clickgems.clickhouse.com/dashboard/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [](https://clickgems.clickhouse.com/dashboard/kettle-dev) |
|
|
391
|
+
| [kettle-jem](https://clickgems.clickhouse.com/dashboard/kettle-jem) | [GitHub](https://github.com/kettle-dev/kettle-jem) | Appraisals & CI workflow templates | [](https://clickgems.clickhouse.com/dashboard/kettle-jem) |
|
|
392
|
+
| [kettle-soup-cover](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) |
|
|
393
|
+
| [kettle-test](https://clickgems.clickhouse.com/dashboard/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [](https://clickgems.clickhouse.com/dashboard/kettle-test) |
|
|
394
|
+
| [rubocop-lts](https://clickgems.clickhouse.com/dashboard/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [](https://clickgems.clickhouse.com/dashboard/rubocop-lts) |
|
|
395
|
+
| [turbo_tests2](https://clickgems.clickhouse.com/dashboard/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [](https://clickgems.clickhouse.com/dashboard/turbo_tests2) |
|
|
561
396
|
|
|
562
397
|
</details>
|
|
563
398
|
|
|
@@ -575,69 +410,50 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
575
410
|
gem install tree_haver
|
|
576
411
|
```
|
|
577
412
|
|
|
578
|
-
### 🔒 Secure Installation
|
|
579
|
-
|
|
580
|
-
<details markdown="1">
|
|
581
|
-
<summary>For Medium or High Security Installations</summary>
|
|
582
|
-
|
|
583
|
-
This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
|
|
584
|
-
[stone\_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with
|
|
585
|
-
by following the instructions below.
|
|
586
|
-
|
|
587
|
-
Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:
|
|
588
|
-
|
|
589
|
-
```console
|
|
590
|
-
gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
You only need to do that once. Then proceed to install with:
|
|
594
|
-
|
|
595
|
-
```console
|
|
596
|
-
gem install tree_haver -P HighSecurity
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
The `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies.
|
|
600
|
-
|
|
601
|
-
If you want to up your security game full-time:
|
|
602
|
-
|
|
603
|
-
```console
|
|
604
|
-
bundle config set --global trust-policy MediumSecurity
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
`MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed.
|
|
608
|
-
|
|
609
|
-
NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.
|
|
610
|
-
|
|
611
|
-
</details>
|
|
612
|
-
|
|
613
413
|
## ⚙️ Configuration
|
|
614
414
|
|
|
615
415
|
### Available Backends
|
|
616
416
|
|
|
617
|
-
TreeHaver
|
|
417
|
+
TreeHaver exposes a backend registry for parser facades, language-family providers, and binary schema support. The high-level StructuredMerge providers default to `tslp` / `kreuzberg-language-pack` when the language pack supports the requested format; the low-level `TreeHaver::Parser` facade uses `auto` to select among parser modules that expose the Parser/Language/Tree API.
|
|
618
418
|
|
|
619
419
|
#### Tree-sitter Backends (Universal Parsing)
|
|
620
420
|
|
|
621
|
-
| Backend
|
|
622
|
-
|
|
623
|
-
| **
|
|
624
|
-
| **
|
|
625
|
-
| **
|
|
626
|
-
| **
|
|
627
|
-
| **
|
|
421
|
+
| Backend | Description | Performance | Portability |
|
|
422
|
+
|---------|-------------|-------------|-------------|
|
|
423
|
+
| **TSLP** | `tree-sitter-language-pack`; default StructuredMerge provider path | Fast | Universal where the gem supports the language |
|
|
424
|
+
| **Kreuzberg Language Pack** | Stable provider id alias used by StructuredMerge family gems | Fast | Universal where `tree-sitter-language-pack` is available |
|
|
425
|
+
| **Auto** | Auto-selects the best `TreeHaver::Parser` facade backend | Varies | Universal |
|
|
426
|
+
| **MRI** | C extension via ruby\_tree\_sitter | Fastest | MRI only |
|
|
427
|
+
| **Rust** | Precompiled via tree\_stump | Very fast | MRI only |
|
|
428
|
+
| **FFI** | Dynamic linking via FFI | Fast | MRI/JRuby where `libtree-sitter` is available |
|
|
429
|
+
| **Java** | JNI bindings (jtreesitter \>= 0.26.0) | Very fast | JRuby only |
|
|
628
430
|
|
|
629
431
|
#### Language-Specific Backends (Native Parser Integration)
|
|
630
432
|
|
|
631
|
-
| Backend
|
|
632
|
-
|
|
633
|
-
| **Prism**
|
|
634
|
-
| **Psych**
|
|
635
|
-
| **Commonmarker** | Markdown via comrak (Rust)
|
|
636
|
-
| **Markly**
|
|
637
|
-
| **
|
|
638
|
-
| **
|
|
639
|
-
|
|
640
|
-
|
|
433
|
+
| Backend | Description | Performance | Portability |
|
|
434
|
+
|---------|-------------|-------------|-------------|
|
|
435
|
+
| **Prism** | Ruby's official parser | Very fast | Universal |
|
|
436
|
+
| **Psych** | Ruby's YAML parser (stdlib) | Very fast | Universal |
|
|
437
|
+
| **Commonmarker** | Markdown via comrak (Rust) | Very fast | MRI/JRuby/TruffleRuby where the gem is available |
|
|
438
|
+
| **Markly** | GFM via cmark-gfm (C) | Very fast | MRI/JRuby/TruffleRuby where the gem is available |
|
|
439
|
+
| **RBS** | Official RBS parser integration | Fast | Universal where the `rbs` gem is available |
|
|
440
|
+
| **Citrus** | Pure Ruby parsing | Slower | Universal |
|
|
441
|
+
| **Parslet** | Pure Ruby parsing | Slower | Universal |
|
|
442
|
+
|
|
443
|
+
#### Binary Schema Support
|
|
444
|
+
|
|
445
|
+
| Backend | Description | Performance | Portability |
|
|
446
|
+
|---------|-------------|-------------|-------------|
|
|
447
|
+
| **Kaitai Struct** | Backend reference and feature profile for binary schema analysis | Varies | Universal once a schema adapter is supplied |
|
|
448
|
+
|
|
449
|
+
**`TreeHaver::Parser` Auto-selection contract:** `:auto` never means
|
|
450
|
+
unregistered parser discovery. It selects the first registered TreeHaver backend
|
|
451
|
+
module whose availability check passes. Built-in tree-sitter facade priority is
|
|
452
|
+
MRI/Rust/FFI on MRI and Java/FFI on JRuby, followed by registered provider
|
|
453
|
+
modules such as Prism, Psych, Citrus, and Parslet when those providers have been
|
|
454
|
+
registered for the requested language. Explicit backend requests fail closed if
|
|
455
|
+
that backend is unavailable or has no parser registered for the requested
|
|
456
|
+
language.
|
|
641
457
|
|
|
642
458
|
**Known Issues:**
|
|
643
459
|
|
|
@@ -647,6 +463,7 @@ TreeHaver supports 10 parsing backends, each with different trade-offs. The `aut
|
|
|
647
463
|
|
|
648
464
|
```ruby
|
|
649
465
|
# Tree-sitter backends
|
|
466
|
+
gem "tree-sitter-language-pack" # TSLP / Kreuzberg language-pack provider
|
|
650
467
|
gem "ruby_tree_sitter", "~> 2.0" # MRI backend
|
|
651
468
|
gem "tree_stump" # Rust backend
|
|
652
469
|
gem "ffi", ">= 1.15", "< 2.0" # FFI backend
|
|
@@ -657,6 +474,7 @@ gem "prism", "~> 1.0" # Ruby parsing (stdlib in Ruby 3.4+)
|
|
|
657
474
|
# Psych: no gem required (Ruby stdlib)
|
|
658
475
|
gem "commonmarker", ">= 0.23" # Markdown parsing (comrak)
|
|
659
476
|
gem "markly", "~> 0.11" # GFM parsing (cmark-gfm)
|
|
477
|
+
gem "rbs" # RBS parsing
|
|
660
478
|
|
|
661
479
|
# Pure Ruby fallbacks
|
|
662
480
|
gem "citrus", "~> 3.0" # Citrus backend
|
|
@@ -668,6 +486,7 @@ gem "parslet", "~> 2.0" # Parslet backend
|
|
|
668
486
|
|
|
669
487
|
```ruby
|
|
670
488
|
# Tree-sitter backends
|
|
489
|
+
TreeHaver.backend = :tslp # Force tree-sitter-language-pack where supported
|
|
671
490
|
TreeHaver.backend = :mri # Force MRI backend (ruby_tree_sitter)
|
|
672
491
|
TreeHaver.backend = :rust # Force Rust backend (tree_stump)
|
|
673
492
|
TreeHaver.backend = :ffi # Force FFI backend
|
|
@@ -678,6 +497,7 @@ TreeHaver.backend = :prism # Force Prism (Ruby parsing)
|
|
|
678
497
|
TreeHaver.backend = :psych # Force Psych (YAML parsing)
|
|
679
498
|
TreeHaver.backend = :commonmarker # Force Commonmarker (Markdown)
|
|
680
499
|
TreeHaver.backend = :markly # Force Markly (GFM Markdown)
|
|
500
|
+
TreeHaver.backend = :rbs # Force RBS parser integration
|
|
681
501
|
TreeHaver.backend = :citrus # Force Citrus (Pure Ruby PEG)
|
|
682
502
|
TreeHaver.backend = :parslet # Force Parslet (Pure Ruby PEG)
|
|
683
503
|
|
|
@@ -721,7 +541,7 @@ This is particularly useful for:
|
|
|
721
541
|
|
|
722
542
|
- **Testing**: Test the same code with different backends
|
|
723
543
|
- **Performance comparison**: Benchmark different backends
|
|
724
|
-
- **
|
|
544
|
+
- **Backend-selection scenarios**: Exercise each registered backend explicitly
|
|
725
545
|
- **Thread isolation**: Each thread can use a different backend safely
|
|
726
546
|
|
|
727
547
|
```ruby
|
|
@@ -743,7 +563,7 @@ TreeHaver.backend_module # => TreeHaver::Backends::FFI
|
|
|
743
563
|
TreeHaver.capabilities # => { backend: :ffi, parse: true, query: false, ... }
|
|
744
564
|
```
|
|
745
565
|
|
|
746
|
-
|
|
566
|
+
For runnable scenario examples, see the implementation-level [examples directory](../../examples/). Those examples are user-level scripts rather than backend-count smoke tests.
|
|
747
567
|
|
|
748
568
|
### Security Considerations
|
|
749
569
|
|
|
@@ -828,7 +648,8 @@ export TREE_SITTER_TOML_PATH=~/.local/share/mise/installs/lua/5.4.8/luarocks/lib
|
|
|
828
648
|
|
|
829
649
|
### Backend Selection
|
|
830
650
|
|
|
831
|
-
TreeHaver
|
|
651
|
+
TreeHaver can select an available registered backend for you, but you can
|
|
652
|
+
override this behavior:
|
|
832
653
|
|
|
833
654
|
```ruby
|
|
834
655
|
# Automatic backend selection (default)
|
|
@@ -839,7 +660,7 @@ TreeHaver.backend = :mri # Use ruby_tree_sitter (MRI only, C extension)
|
|
|
839
660
|
TreeHaver.backend = :rust # Use tree_stump (MRI, Rust extension with precompiled binaries)
|
|
840
661
|
# Note: Use tree_stump v0.2.0 or newer (fixes are released).
|
|
841
662
|
TreeHaver.backend = :ffi # Use FFI bindings (works on MRI and JRuby)
|
|
842
|
-
TreeHaver.backend = :java # Use Java bindings (JRuby only
|
|
663
|
+
TreeHaver.backend = :java # Use Java bindings (JRuby only)
|
|
843
664
|
TreeHaver.backend = :citrus # Use Citrus pure Ruby parser
|
|
844
665
|
# NOTE: Portable, all Ruby implementations
|
|
845
666
|
# CAVEAT: few major language grammars, but many esoteric grammars
|
|
@@ -848,7 +669,13 @@ TreeHaver.backend = :parslet # Use Parslet pure Ruby parser
|
|
|
848
669
|
# CAVEAT: few major language grammars, but many esoteric grammars
|
|
849
670
|
```
|
|
850
671
|
|
|
851
|
-
**Auto-selection
|
|
672
|
+
**Auto-selection rule:** `:auto` uses the registered backend list and chooses
|
|
673
|
+
the first backend module that is allowed by environment configuration and reports
|
|
674
|
+
itself available. It does not call parser libraries directly, search for
|
|
675
|
+
language-specific fallback parsers outside the registry, or hide an explicit
|
|
676
|
+
backend failure by silently switching to a different backend.
|
|
677
|
+
|
|
678
|
+
**Built-in facade priority on MRI:** MRI → Rust → FFI → Citrus → Parslet
|
|
852
679
|
|
|
853
680
|
You can also set the backend via environment variable:
|
|
854
681
|
|
|
@@ -1023,7 +850,12 @@ language = TreeHaver::Language.toml(
|
|
|
1023
850
|
|
|
1024
851
|
### Grammar Discovery with GrammarFinder
|
|
1025
852
|
|
|
1026
|
-
For libraries that need to automatically locate tree-sitter grammars (like the
|
|
853
|
+
For libraries that need to automatically locate tree-sitter grammars (like the
|
|
854
|
+
`*-merge` family of gems), TreeHaver provides the `GrammarFinder` utility
|
|
855
|
+
class. It resolves explicit registrations first and then uses
|
|
856
|
+
`tree_sitter_language_pack` as the normalized on-demand provisioning path for
|
|
857
|
+
tree-sitter grammars. Parser-specific non-tree-sitter backends should be
|
|
858
|
+
registered by the owning merge gem rather than hardcoded in TreeHaver.
|
|
1027
859
|
|
|
1028
860
|
```ruby
|
|
1029
861
|
# Create a finder for any language
|
|
@@ -1034,7 +866,7 @@ if finder.available?
|
|
|
1034
866
|
puts "TOML grammar found at: #{finder.find_library_path}"
|
|
1035
867
|
else
|
|
1036
868
|
puts finder.not_found_message
|
|
1037
|
-
# => "tree-sitter toml grammar not found. Searched:
|
|
869
|
+
# => "tree-sitter toml grammar not found. Searched: /.../libtree_sitter_toml.so, ..."
|
|
1038
870
|
end
|
|
1039
871
|
|
|
1040
872
|
# Register the language if available
|
|
@@ -1044,47 +876,109 @@ finder.register! if finder.available?
|
|
|
1044
876
|
language = TreeHaver::Language.toml
|
|
1045
877
|
```
|
|
1046
878
|
|
|
879
|
+
#### Registration Bootstrap
|
|
880
|
+
|
|
881
|
+
TreeHaver is the shared registry. It is not the owner of parser-family policy.
|
|
882
|
+
|
|
883
|
+
- Tree-sitter grammars should be normalized through `GrammarFinder` and
|
|
884
|
+
`tree_sitter_language_pack` or an explicit registration.
|
|
885
|
+
- Non-tree-sitter backends should be registered by the merge gem that owns
|
|
886
|
+
that parser family.
|
|
887
|
+
- Tools that load multiple merge gems should invoke each gem's registration
|
|
888
|
+
bootstrap so TreeHaver sees the full set of available grammars before
|
|
889
|
+
`parser_for` is called.
|
|
890
|
+
|
|
891
|
+
```ruby
|
|
892
|
+
# In a tool that uses several merge gems
|
|
893
|
+
require "tree_haver"
|
|
894
|
+
require "toml-merge"
|
|
895
|
+
require "markdown-merge"
|
|
896
|
+
|
|
897
|
+
Toml::Merge.register_backend!
|
|
898
|
+
Markdown::Merge.register_backend!
|
|
899
|
+
|
|
900
|
+
parser = TreeHaver.parser_for(:toml)
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
Once those registrations have run, `TreeHaver.parser_for` can resolve any
|
|
904
|
+
registered tree-sitter grammar plus any registered backend-specific grammar for
|
|
905
|
+
the active backend mode. If a merge depends on a grammar that has not been
|
|
906
|
+
registered and cannot be provisioned through `tree_sitter_language_pack`,
|
|
907
|
+
TreeHaver raises `TreeHaver::NotAvailable`.
|
|
908
|
+
|
|
1047
909
|
#### GrammarFinder Automatic Derivation
|
|
1048
910
|
|
|
1049
911
|
Given just the language name, `GrammarFinder` automatically derives:
|
|
1050
912
|
|
|
1051
|
-
| Property | Derived Value (for `:toml`)
|
|
1052
|
-
|
|
1053
|
-
| ENV var | `TREE_SITTER_TOML_PATH`
|
|
1054
|
-
| Library filename | `
|
|
1055
|
-
| Symbol name | `tree_sitter_toml`
|
|
913
|
+
| Property | Derived Value (for `:toml`) |
|
|
914
|
+
|------------------|-----------------------------------------------------|
|
|
915
|
+
| ENV var | `TREE_SITTER_TOML_PATH` |
|
|
916
|
+
| Library filename | `libtree_sitter_toml.so` (Linux) or `.dylib` (macOS) |
|
|
917
|
+
| Symbol name | `tree_sitter_toml` |
|
|
1056
918
|
|
|
1057
919
|
#### Search Order
|
|
1058
920
|
|
|
1059
921
|
`GrammarFinder` searches for grammars in this order:
|
|
1060
922
|
|
|
1061
923
|
1. **Environment variable**: `TREE_SITTER_<LANG>_PATH` (highest priority)
|
|
1062
|
-
2. **
|
|
1063
|
-
3. **
|
|
924
|
+
2. **Existing TreeHaver registration**: previously-registered tree-sitter grammar path
|
|
925
|
+
3. **Extra paths**: explicit paths provided at initialization
|
|
926
|
+
4. **`tree_sitter_language_pack`**: parser API availability through its on-demand grammar loader
|
|
1064
927
|
|
|
1065
|
-
####
|
|
928
|
+
#### TSLP Cold-Cache Troubleshooting
|
|
1066
929
|
|
|
1067
|
-
|
|
930
|
+
`tree_sitter_language_pack` loads bundled grammars on demand. A cold install may
|
|
931
|
+
not have a populated shared-library cache before the first parse, so
|
|
932
|
+
`GrammarFinder` must not treat cache contents as the source of truth for TSLP
|
|
933
|
+
availability.
|
|
934
|
+
|
|
935
|
+
For TSLP-backed languages, check availability through the TSLP parser API and
|
|
936
|
+
then register the language with the TreeHaver TSLP backend:
|
|
1068
937
|
|
|
1069
938
|
```ruby
|
|
1070
|
-
# In toml-merge
|
|
1071
939
|
finder = TreeHaver::GrammarFinder.new(:toml)
|
|
1072
940
|
finder.register! if finder.available?
|
|
1073
941
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
942
|
+
TreeHaver.with_backend("kreuzberg-language-pack") do
|
|
943
|
+
TreeHaver.parser_for(:toml).parse("title = \"example\"\n")
|
|
944
|
+
end
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
If this fails from a cold start, inspect `finder.search_info` and confirm that
|
|
948
|
+
the `tree-sitter-language-pack` gem is in the bundle being used. Do not add a
|
|
949
|
+
merge-gem parser fallback to hide the failure; fix the TreeHaver registration
|
|
950
|
+
or the TSLP grammar binding instead.
|
|
951
|
+
|
|
952
|
+
#### Usage in \*-merge Gems
|
|
953
|
+
|
|
954
|
+
The `GrammarFinder` pattern enables clean integration in language-specific
|
|
955
|
+
merge gems:
|
|
1077
956
|
|
|
1078
|
-
|
|
1079
|
-
|
|
957
|
+
```ruby
|
|
958
|
+
# In a substrate merge gem
|
|
959
|
+
finder = TreeHaver::GrammarFinder.new(:toml)
|
|
1080
960
|
finder.register! if finder.available?
|
|
1081
961
|
```
|
|
1082
962
|
|
|
1083
|
-
|
|
963
|
+
Parser-specific gems should register their own TreeHaver backend wrappers:
|
|
964
|
+
|
|
965
|
+
```ruby
|
|
966
|
+
# In a TOML parser provider gem
|
|
967
|
+
TreeHaver.register_language(
|
|
968
|
+
:toml,
|
|
969
|
+
backend_module: Citrus::Toml::Merge::Backend,
|
|
970
|
+
backend_type: :citrus,
|
|
971
|
+
gem_name: "toml-rb",
|
|
972
|
+
)
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
Each gem uses the same API. TreeHaver owns the shared registration surface;
|
|
976
|
+
merge gems own parser-specific backend registrations and any explicit bootstrap
|
|
977
|
+
hook they expose to register them.
|
|
1084
978
|
|
|
1085
979
|
#### Adding Custom Search Paths
|
|
1086
980
|
|
|
1087
|
-
For non-standard
|
|
981
|
+
For non-standard standalone grammar builds, provide extra search paths:
|
|
1088
982
|
|
|
1089
983
|
```ruby
|
|
1090
984
|
finder = TreeHaver::GrammarFinder.new(:toml, extra_paths: [
|
|
@@ -1105,9 +999,9 @@ puts finder.search_info
|
|
|
1105
999
|
# env_var: "TREE_SITTER_TOML_PATH",
|
|
1106
1000
|
# env_value: nil,
|
|
1107
1001
|
# symbol: "tree_sitter_toml",
|
|
1108
|
-
# library_filename: "
|
|
1109
|
-
# search_paths: ["/
|
|
1110
|
-
# found_path: "
|
|
1002
|
+
# library_filename: "libtree_sitter_toml.so",
|
|
1003
|
+
# search_paths: ["/custom/lib/libtree_sitter_toml.so", "/.../tree-sitter-language-pack/..."],
|
|
1004
|
+
# found_path: "/.../libtree_sitter_toml.so",
|
|
1111
1005
|
# available: true
|
|
1112
1006
|
# }
|
|
1113
1007
|
```
|
|
@@ -1127,80 +1021,20 @@ TreeHaver.capabilities
|
|
|
1127
1021
|
# => { backend: :parslet, parse: true, query: false, bytes_field: false }
|
|
1128
1022
|
```
|
|
1129
1023
|
|
|
1130
|
-
###
|
|
1131
|
-
|
|
1132
|
-
For codebases migrating from `ruby_tree_sitter`, TreeHaver provides a compatibility shim:
|
|
1133
|
-
|
|
1134
|
-
```ruby
|
|
1135
|
-
require "tree_haver/compat"
|
|
1136
|
-
|
|
1137
|
-
# Now TreeSitter constants map to TreeHaver
|
|
1138
|
-
parser = TreeSitter::Parser.new # Actually creates TreeHaver::Parser
|
|
1139
|
-
```
|
|
1140
|
-
|
|
1141
|
-
This is safe and idempotent—if the real `TreeSitter` module is already loaded, the shim does nothing.
|
|
1142
|
-
|
|
1143
|
-
#### ⚠️ Important: Exception Hierarchy
|
|
1144
|
-
|
|
1145
|
-
**Both ruby\_tree\_sitter v2+ and TreeHaver exceptions inherit from `Exception` (not `StandardError`).**
|
|
1146
|
-
|
|
1147
|
-
This design decision follows ruby\_tree\_sitter's lead for thread-safety and signal handling reasons. See [ruby\_tree\_sitter PR \#83](https://github.com/Faveod/ruby-tree-sitter/pull/83) for the rationale.
|
|
1024
|
+
### Error Handling Model
|
|
1148
1025
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
```ruby
|
|
1152
|
-
# ⚠️ This will NOT catch TreeHaver errors
|
|
1153
|
-
begin
|
|
1154
|
-
TreeHaver::Language.from_library("/nonexistent.so")
|
|
1155
|
-
rescue => e
|
|
1156
|
-
puts "Caught!" # Never reached - TreeHaver::Error inherits Exception
|
|
1157
|
-
end
|
|
1158
|
-
|
|
1159
|
-
# ✅ Explicit rescue is required
|
|
1160
|
-
begin
|
|
1161
|
-
TreeHaver::Language.from_library("/nonexistent.so")
|
|
1162
|
-
rescue TreeHaver::Error => e
|
|
1163
|
-
puts "Caught!" # This works
|
|
1164
|
-
end
|
|
1165
|
-
|
|
1166
|
-
# ✅ Or rescue specific exceptions
|
|
1167
|
-
begin
|
|
1168
|
-
TreeHaver::Language.from_library("/nonexistent.so")
|
|
1169
|
-
rescue TreeHaver::NotAvailable => e
|
|
1170
|
-
puts "Grammar not available: #{e.message}"
|
|
1171
|
-
end
|
|
1172
|
-
```
|
|
1026
|
+
TreeHaver does not ship a `TreeSitter::*` compatibility namespace. Use the `TreeHaver::*` API directly, and rescue `TreeHaver::Error` or a more specific subclass when parser setup can fail.
|
|
1173
1027
|
|
|
1174
1028
|
**TreeHaver Exception Hierarchy:**
|
|
1175
1029
|
|
|
1176
|
-
|
|
1030
|
+
StandardError
|
|
1177
1031
|
└── TreeHaver::Error # Base error class
|
|
1178
1032
|
├── TreeHaver::NotAvailable # Backend/grammar not available
|
|
1179
1033
|
└── TreeHaver::BackendConflict # Backend incompatibility detected
|
|
1180
1034
|
|
|
1181
|
-
**Compatibility Mode Behavior:**
|
|
1182
|
-
|
|
1183
|
-
The compat mode (`require "tree_haver/compat"`) creates aliases but **does not change the exception hierarchy**:
|
|
1184
|
-
|
|
1185
|
-
```ruby
|
|
1186
|
-
require "tree_haver/compat"
|
|
1187
|
-
|
|
1188
|
-
# TreeSitter constants are now aliases to TreeHaver
|
|
1189
|
-
TreeSitter::Error # => TreeHaver::Error (still inherits Exception)
|
|
1190
|
-
TreeSitter::Parser # => TreeHaver::Parser
|
|
1191
|
-
TreeSitter::Language # => TreeHaver::Language
|
|
1192
|
-
|
|
1193
|
-
# Exception handling remains the same
|
|
1194
|
-
begin
|
|
1195
|
-
TreeSitter::Language.load("missing", "/nonexistent.so")
|
|
1196
|
-
rescue TreeSitter::Error => e # Still requires explicit rescue
|
|
1197
|
-
puts "Error: #{e.message}"
|
|
1198
|
-
end
|
|
1199
|
-
```
|
|
1200
|
-
|
|
1201
1035
|
**Best Practices:**
|
|
1202
1036
|
|
|
1203
|
-
1. **
|
|
1037
|
+
1. **Rescue TreeHaver errors explicitly when backend setup can fail:**
|
|
1204
1038
|
|
|
1205
1039
|
```ruby
|
|
1206
1040
|
begin
|
|
@@ -1209,29 +1043,24 @@ end
|
|
|
1209
1043
|
language = TreeHaver::Language.toml
|
|
1210
1044
|
rescue TreeHaver::NotAvailable => e
|
|
1211
1045
|
warn("TOML grammar not available: #{e.message}")
|
|
1212
|
-
#
|
|
1046
|
+
# Select another registered TreeHaver backend explicitly, or fail gracefully
|
|
1213
1047
|
end
|
|
1214
1048
|
```
|
|
1215
1049
|
|
|
1216
|
-
2. **
|
|
1217
|
-
**Why inherit from Exception?**
|
|
1218
|
-
Following ruby\_tree\_sitter's reasoning:
|
|
1219
|
-
|
|
1220
|
-
- **Thread safety**: Prevents accidental catching in thread cleanup code
|
|
1221
|
-
- **Signal handling**: Ensures parsing errors don't interfere with SIGTERM/SIGINT
|
|
1222
|
-
- **Intentional handling**: Forces developers to explicitly handle parsing errors
|
|
1223
|
-
See `lib/tree_haver/compat.rb` for compatibility layer documentation.
|
|
1050
|
+
2. **Prefer specific handling** for `TreeHaver::NotAvailable` when a backend or grammar is optional.
|
|
1224
1051
|
|
|
1225
1052
|
## 🔧 Basic Usage
|
|
1226
1053
|
|
|
1227
1054
|
### Quick Start
|
|
1228
1055
|
|
|
1229
|
-
The simplest way to parse code is with `TreeHaver.parser_for`, which handles
|
|
1056
|
+
The simplest way to parse code is with `TreeHaver.parser_for`, which handles
|
|
1057
|
+
language loading, grammar resolution, and backend selection:
|
|
1230
1058
|
|
|
1231
1059
|
```ruby
|
|
1232
1060
|
require "tree_haver"
|
|
1233
1061
|
|
|
1234
|
-
# Parse TOML -
|
|
1062
|
+
# Parse TOML - resolves any registered tree-sitter grammar and any registered
|
|
1063
|
+
# non-tree-sitter backend for the active backend mode
|
|
1235
1064
|
parser = TreeHaver.parser_for(:toml)
|
|
1236
1065
|
tree = parser.parse("[package]\nname = \"my-app\"")
|
|
1237
1066
|
|
|
@@ -1246,7 +1075,7 @@ tree = parser.parse("#!/bin/bash\necho hello")
|
|
|
1246
1075
|
# With explicit library path
|
|
1247
1076
|
parser = TreeHaver.parser_for(:toml, library_path: "/custom/path/libtree-sitter-toml.so")
|
|
1248
1077
|
|
|
1249
|
-
# With Citrus
|
|
1078
|
+
# With explicit Citrus provider configuration
|
|
1250
1079
|
parser = TreeHaver.parser_for(
|
|
1251
1080
|
:toml,
|
|
1252
1081
|
citrus_config: {gem_name: "toml-rb", grammar_const: "TomlRB::Document"},
|
|
@@ -1257,7 +1086,7 @@ parser = TreeHaver.parser_for(
|
|
|
1257
1086
|
|
|
1258
1087
|
1. Checking if the language is already registered
|
|
1259
1088
|
2. Auto-discovering tree-sitter grammar via `GrammarFinder`
|
|
1260
|
-
3.
|
|
1089
|
+
3. Using any registered backend-specific grammar for the active backend
|
|
1261
1090
|
4. Creating and configuring the parser
|
|
1262
1091
|
5. Raising `NotAvailable` with a helpful message if nothing works
|
|
1263
1092
|
|
|
@@ -1265,7 +1094,7 @@ parser = TreeHaver.parser_for(
|
|
|
1265
1094
|
|
|
1266
1095
|
For more control, you can create parsers manually:
|
|
1267
1096
|
|
|
1268
|
-
TreeHaver works with
|
|
1097
|
+
TreeHaver works with many languages through its registry of tree-sitter adapters, language-pack providers, native parsers, PEG parsers, and binary schema support. Here are examples for different parsing needs:
|
|
1269
1098
|
|
|
1270
1099
|
#### Parsing with Tree-sitter (Universal Languages)
|
|
1271
1100
|
|
|
@@ -1529,9 +1358,7 @@ tree.edit(
|
|
|
1529
1358
|
new_tree = parser.parse_string(tree, "x = 42")
|
|
1530
1359
|
```
|
|
1531
1360
|
|
|
1532
|
-
**Note:** Incremental parsing requires the MRI (`ruby_tree_sitter`), Rust (`tree_stump`), or Java (`java-tree-sitter` / `jtreesitter`) backend. The FFI, Citrus, and Parslet backends do not
|
|
1533
|
-
|
|
1534
|
-
**Note:** `tree_stump` currently requires unreleased fixes in the `main` branch.
|
|
1361
|
+
**Note:** Incremental parsing requires the MRI (`ruby_tree_sitter`), Rust (`tree_stump`), or Java (`java-tree-sitter` / `jtreesitter`) backend. The FFI, Citrus, and Parslet backends do not support incremental parsing. You can check support with:
|
|
1535
1362
|
|
|
1536
1363
|
```ruby
|
|
1537
1364
|
tree.supports_editing? # => true if edit() is available
|
|
@@ -1549,7 +1376,7 @@ end
|
|
|
1549
1376
|
# Check if a backend is available
|
|
1550
1377
|
if TreeHaver.backend_module.nil?
|
|
1551
1378
|
puts "No TreeHaver backend is available!"
|
|
1552
|
-
puts "Install ruby_tree_sitter
|
|
1379
|
+
puts "Install tree-sitter-language-pack, ruby_tree_sitter, tree_stump, ffi with libtree-sitter, or a Ruby parser backend such as prism, psych, citrus, or parslet"
|
|
1553
1380
|
end
|
|
1554
1381
|
```
|
|
1555
1382
|
|
|
@@ -1595,8 +1422,8 @@ parser = TreeHaver::Parser.new
|
|
|
1595
1422
|
```bash
|
|
1596
1423
|
# 1. Download java-tree-sitter JAR from Maven Central
|
|
1597
1424
|
mkdir -p vendor/jars
|
|
1598
|
-
curl -fSL -o vendor/jars/jtreesitter-0.
|
|
1599
|
-
"https://repo1.maven.org/maven2/io/github/tree-sitter/jtreesitter/0.
|
|
1425
|
+
curl -fSL -o vendor/jars/jtreesitter-0.26.0.jar \
|
|
1426
|
+
"https://repo1.maven.org/maven2/io/github/tree-sitter/jtreesitter/0.26.0/jtreesitter-0.26.0.jar"
|
|
1600
1427
|
|
|
1601
1428
|
# 2. Set environment variables
|
|
1602
1429
|
export CLASSPATH="$(pwd)/vendor/jars:$CLASSPATH"
|
|
@@ -1692,16 +1519,16 @@ end
|
|
|
1692
1519
|
|
|
1693
1520
|
#### TruffleRuby
|
|
1694
1521
|
|
|
1695
|
-
TruffleRuby can use
|
|
1522
|
+
TruffleRuby can use Ruby-native parser backends such as Prism, Psych, Citrus, or Parslet. The FFI tree-sitter backend is not selected on TruffleRuby because tree-sitter's struct-by-value API is incompatible with the current FFI path.
|
|
1696
1523
|
|
|
1697
1524
|
```ruby
|
|
1698
|
-
# Use
|
|
1699
|
-
TreeHaver.backend = :
|
|
1525
|
+
# Use Prism for Ruby source
|
|
1526
|
+
TreeHaver.backend = :prism
|
|
1700
1527
|
|
|
1701
|
-
# Or
|
|
1702
|
-
TreeHaver.backend = :
|
|
1528
|
+
# Or use Psych for YAML
|
|
1529
|
+
TreeHaver.backend = :psych
|
|
1703
1530
|
|
|
1704
|
-
#
|
|
1531
|
+
# Use Citrus backend for zero native dependencies
|
|
1705
1532
|
TreeHaver.backend = :citrus
|
|
1706
1533
|
|
|
1707
1534
|
# Or use Parslet backend for zero native dependencies
|
|
@@ -1791,23 +1618,15 @@ TreeHaver.with_backend(:rust) do
|
|
|
1791
1618
|
end
|
|
1792
1619
|
```
|
|
1793
1620
|
|
|
1794
|
-
####
|
|
1621
|
+
#### Explicit Backend Selection Pattern
|
|
1795
1622
|
|
|
1796
|
-
Try
|
|
1623
|
+
Try registered TreeHaver backends explicitly when you want controlled provider
|
|
1624
|
+
selection. Structured merge gems should fail closed when no registered TreeHaver
|
|
1625
|
+
backend can parse the requested language.
|
|
1797
1626
|
|
|
1798
1627
|
```ruby
|
|
1799
|
-
def
|
|
1800
|
-
TreeHaver.with_backend(
|
|
1801
|
-
TreeHaver::Parser.new.tap { |p| p.language = load_language }.parse(source)
|
|
1802
|
-
end
|
|
1803
|
-
rescue TreeHaver::NotAvailable
|
|
1804
|
-
# Fall back to Citrus if MRI backend unavailable
|
|
1805
|
-
TreeHaver.with_backend(:citrus) do
|
|
1806
|
-
TreeHaver::Parser.new.tap { |p| p.language = load_language }.parse(source)
|
|
1807
|
-
end
|
|
1808
|
-
rescue TreeHaver::NotAvailable
|
|
1809
|
-
# Fall back to Parslet if Citrus backend unavailable
|
|
1810
|
-
TreeHaver.with_backend(:parslet) do
|
|
1628
|
+
def parse_with_backend(source, backend_name)
|
|
1629
|
+
TreeHaver.with_backend(backend_name) do
|
|
1811
1630
|
TreeHaver::Parser.new.tap { |p| p.language = load_language }.parse(source)
|
|
1812
1631
|
end
|
|
1813
1632
|
end
|
|
@@ -1897,7 +1716,7 @@ end
|
|
|
1897
1716
|
|
|
1898
1717
|
Tags follow a naming convention:
|
|
1899
1718
|
|
|
1900
|
-
- `*_backend` = TreeHaver
|
|
1719
|
+
- `*_backend` = TreeHaver backend availability checks (tslp, mri, rust, ffi, java, prism, psych, commonmarker, markly, citrus, parslet, rbs)
|
|
1901
1720
|
- `*_engine` = Ruby engines (mri, jruby, truffleruby)
|
|
1902
1721
|
- `*_grammar` = tree-sitter grammar files (.so)
|
|
1903
1722
|
- `*_parsing` = any parsing capability for a language (combines multiple backends/grammars)
|
|
@@ -1906,7 +1725,7 @@ Tags follow a naming convention:
|
|
|
1906
1725
|
| Tag | Description |
|
|
1907
1726
|
|-------------------------|---------------------------------------------------------------------------|
|
|
1908
1727
|
| **Backend Tags** | |
|
|
1909
|
-
| `:ffi_backend` | FFI backend available (dynamic check
|
|
1728
|
+
| `:ffi_backend` | FFI backend available (dynamic check) |
|
|
1910
1729
|
| `:ffi_backend_only` | FFI backend in isolation (won't trigger MRI check) |
|
|
1911
1730
|
| `:mri_backend` | ruby\_tree\_sitter gem available |
|
|
1912
1731
|
| `:mri_backend_only` | MRI backend in isolation (won't trigger FFI check) |
|
|
@@ -1918,7 +1737,8 @@ Tags follow a naming convention:
|
|
|
1918
1737
|
| `:markly_backend` | markly gem available |
|
|
1919
1738
|
| `:citrus_backend` | Citrus gem available |
|
|
1920
1739
|
| `:parslet_backend` | Parslet gem available |
|
|
1921
|
-
| `:
|
|
1740
|
+
| `:tslp_backend` | tree-sitter-language-pack available |
|
|
1741
|
+
| `:rbs_backend` | RBS gem available (official RBS parser) |
|
|
1922
1742
|
| **Engine Tags** | |
|
|
1923
1743
|
| `:mri_engine` | Running on MRI (CRuby) |
|
|
1924
1744
|
| `:jruby_engine` | Running on JRuby |
|
|
@@ -1950,63 +1770,6 @@ Set `TREE_HAVER_DEBUG=1` to print a dependency summary at the start of your test
|
|
|
1950
1770
|
TREE_HAVER_DEBUG=1 bundle exec rspec
|
|
1951
1771
|
```
|
|
1952
1772
|
|
|
1953
|
-
## 🦷 FLOSS Funding
|
|
1954
|
-
|
|
1955
|
-
While kettle-rb tools are free software and will always be, the project would benefit immensely from some funding.
|
|
1956
|
-
Raising a monthly budget of... "dollars" would make the project more sustainable.
|
|
1957
|
-
|
|
1958
|
-
We welcome both individual and corporate sponsors\! We also offer a
|
|
1959
|
-
wide array of funding channels to account for your preferences
|
|
1960
|
-
(although currently [Open Collective][🖇osc] is our preferred funding platform).
|
|
1961
|
-
|
|
1962
|
-
**If you're working in a company that's making significant use of kettle-rb tools we'd
|
|
1963
|
-
appreciate it if you suggest to your company to become a kettle-rb sponsor.**
|
|
1964
|
-
|
|
1965
|
-
You can support the development of kettle-rb tools via
|
|
1966
|
-
[GitHub Sponsors][🖇sponsor],
|
|
1967
|
-
[Liberapay][⛳liberapay],
|
|
1968
|
-
[PayPal][🖇paypal],
|
|
1969
|
-
[Open Collective][🖇osc]
|
|
1970
|
-
and [Tidelift][🏙️entsup-tidelift].
|
|
1971
|
-
|
|
1972
|
-
| 📍 NOTE |
|
|
1973
|
-
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
1974
|
-
| If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
|
|
1975
|
-
|
|
1976
|
-
### Open Collective for Individuals
|
|
1977
|
-
|
|
1978
|
-
Support us with a monthly donation and help us continue our activities. \[[Become a backer][🖇osc-backers]\]
|
|
1979
|
-
|
|
1980
|
-
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
1981
|
-
|
|
1982
|
-
<!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
|
|
1983
|
-
No backers yet. Be the first!
|
|
1984
|
-
<!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
|
|
1985
|
-
|
|
1986
|
-
### Open Collective for Organizations
|
|
1987
|
-
|
|
1988
|
-
Become a sponsor and get your logo on our README on GitHub with a link to your site. \[[Become a sponsor][🖇osc-sponsors]\]
|
|
1989
|
-
|
|
1990
|
-
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
1991
|
-
|
|
1992
|
-
<!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
|
|
1993
|
-
No sponsors yet. Be the first!
|
|
1994
|
-
<!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
|
|
1995
|
-
|
|
1996
|
-
[kettle-readme-backers]: https://github.com/kettle-rb/tree_haver/blob/main/exe/kettle-readme-backers
|
|
1997
|
-
|
|
1998
|
-
### Another way to support open-source
|
|
1999
|
-
|
|
2000
|
-
I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈 cats).
|
|
2001
|
-
|
|
2002
|
-
If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
|
|
2003
|
-
|
|
2004
|
-
I’m developing a new library, [floss\_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
|
|
2005
|
-
|
|
2006
|
-
**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
|
|
2007
|
-
|
|
2008
|
-
[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS efforts using Patreon][🖇patreon-img]][🖇patreon]
|
|
2009
|
-
|
|
2010
1773
|
## 🔐 Security
|
|
2011
1774
|
|
|
2012
1775
|
See [SECURITY.md][🔐security].
|
|
@@ -2014,78 +1777,43 @@ See [SECURITY.md][🔐security].
|
|
|
2014
1777
|
## 🤝 Contributing
|
|
2015
1778
|
|
|
2016
1779
|
If you need some ideas of where to help, you could work on adding more code coverage,
|
|
2017
|
-
or if it is already 💯 (see [below](#code-coverage)) check [
|
|
1780
|
+
or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
|
|
2018
1781
|
or use the gem and think about how it could be better.
|
|
2019
1782
|
|
|
2020
1783
|
We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
|
|
2021
1784
|
|
|
2022
1785
|
See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
|
|
2023
1786
|
|
|
2024
|
-
### 🚀 Release Instructions
|
|
2025
|
-
|
|
2026
|
-
See [CONTRIBUTING.md][🤝contributing].
|
|
2027
|
-
|
|
2028
1787
|
### Code Coverage
|
|
2029
1788
|
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
[![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
|
|
1789
|
+
Run the test suite with coverage enabled:
|
|
2033
1790
|
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
Everyone interacting with this project's codebases, issue trackers,
|
|
2039
|
-
chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
|
|
2040
|
-
|
|
2041
|
-
## 🌈 Contributors
|
|
2042
|
-
|
|
2043
|
-
[![Contributors][🖐contributors-img]][🖐contributors]
|
|
2044
|
-
|
|
2045
|
-
Made with [contributors-img][🖐contrib-rocks].
|
|
2046
|
-
|
|
2047
|
-
Also see GitLab Contributors: <https://gitlab.com/kettle-rb/tree_haver/-/graphs/main>
|
|
2048
|
-
|
|
2049
|
-
<details>
|
|
2050
|
-
<summary>⭐️ Star History</summary>
|
|
1791
|
+
```bash
|
|
1792
|
+
bundle exec kettle-test
|
|
1793
|
+
```
|
|
2051
1794
|
|
|
2052
|
-
|
|
2053
|
-
<picture>
|
|
2054
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kettle-rb/tree_haver&type=Date&theme=dark" />
|
|
2055
|
-
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kettle-rb/tree_haver&type=Date" />
|
|
2056
|
-
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kettle-rb/tree_haver&type=Date" />
|
|
2057
|
-
</picture>
|
|
2058
|
-
</a>
|
|
1795
|
+
Inspect the generated report with:
|
|
2059
1796
|
|
|
2060
|
-
|
|
1797
|
+
```bash
|
|
1798
|
+
kettle-soup-cover -d
|
|
1799
|
+
```
|
|
2061
1800
|
|
|
2062
1801
|
## 📌 Versioning
|
|
2063
1802
|
|
|
2064
|
-
This
|
|
2065
|
-
|
|
2066
|
-
Specifically, if a minor or patch version is released that breaks backward compatibility,
|
|
2067
|
-
a new version should be immediately released that restores compatibility.
|
|
2068
|
-
Breaking changes to the public API will only be introduced with new major versions.
|
|
2069
|
-
|
|
2070
|
-
> dropping support for a platform is both obviously and objectively a breaking change <br/>
|
|
2071
|
-
> —Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
|
|
2072
|
-
|
|
2073
|
-
I understand that policy doesn't work universally ("exceptions to every rule\!"),
|
|
2074
|
-
but it is the policy here.
|
|
2075
|
-
As such, in many cases it is good to specify a dependency on this library using
|
|
2076
|
-
the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
|
|
1803
|
+
This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
|
|
1804
|
+
For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
|
|
2077
1805
|
|
|
2078
1806
|
For example:
|
|
2079
1807
|
|
|
2080
1808
|
```ruby
|
|
2081
|
-
spec.add_dependency("tree_haver", "~>
|
|
1809
|
+
spec.add_dependency("tree_haver", "~> 7.0")
|
|
2082
1810
|
```
|
|
2083
1811
|
|
|
2084
1812
|
<details markdown="1">
|
|
2085
1813
|
<summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
|
|
2086
1814
|
|
|
2087
|
-
|
|
2088
|
-
|
|
1815
|
+
Dropping support for a platform can be a breaking change for affected users.
|
|
1816
|
+
If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
|
|
2089
1817
|
|
|
2090
1818
|
To get a better understanding of how SemVer is intended to work over a project's lifetime,
|
|
2091
1819
|
read this article from the creator of SemVer:
|
|
@@ -2098,68 +1826,19 @@ See [CHANGELOG.md][📌changelog] for a list of releases.
|
|
|
2098
1826
|
|
|
2099
1827
|
## 📄 License
|
|
2100
1828
|
|
|
2101
|
-
The gem is available
|
|
2102
|
-
|
|
2103
|
-
See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].
|
|
2104
|
-
|
|
2105
|
-
### © Copyright
|
|
2106
|
-
|
|
2107
|
-
<ul>
|
|
2108
|
-
<li>
|
|
2109
|
-
Copyright (c) 2025-2026 Peter H. Boling, of
|
|
2110
|
-
<a href="https://discord.gg/3qme4XHNKN">
|
|
2111
|
-
Galtzo.com
|
|
2112
|
-
<picture>
|
|
2113
|
-
<img src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px-blank.svg" alt="Galtzo.com Logo (Wordless) by Aboling0, CC BY-SA 4.0" width="24">
|
|
2114
|
-
</picture>
|
|
2115
|
-
</a>, and tree_haver contributors.
|
|
2116
|
-
</li>
|
|
2117
|
-
</ul>
|
|
2118
|
-
|
|
2119
|
-
## 🤑 A request for help
|
|
2120
|
-
|
|
2121
|
-
Maintainers have teeth and need to pay their dentists.
|
|
2122
|
-
After getting laid off in an RIF in March, and encountering difficulty finding a new one,
|
|
2123
|
-
I began spending most of my time building open source tools.
|
|
2124
|
-
I'm hoping to be able to pay for my kids' health insurance this month,
|
|
2125
|
-
so if you value the work I am doing, I need your support.
|
|
2126
|
-
Please consider sponsoring me or the project.
|
|
2127
|
-
|
|
2128
|
-
To join the community or get help 👇️ Join the Discord.
|
|
2129
|
-
|
|
2130
|
-
[![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
|
|
2131
|
-
|
|
2132
|
-
To say "thanks\!" ☝️ Join the Discord or 👇️ send money.
|
|
2133
|
-
|
|
2134
|
-
[![Sponsor kettle-rb/tree\_haver on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
|
|
2135
|
-
|
|
2136
|
-
### Please give the project a star ⭐ ♥.
|
|
1829
|
+
The gem is available under the following licenses: [AGPL-3.0-only](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/AGPL-3.0-only.md), [PolyForm-Small-Business-1.0.0](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/PolyForm-Small-Business-1.0.0.md).
|
|
1830
|
+
See [LICENSE.md][📄license] for details.
|
|
2137
1831
|
|
|
2138
|
-
|
|
1832
|
+
If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
|
|
2139
1833
|
|
|
2140
1834
|
[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
|
|
2141
1835
|
[⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
|
|
2142
1836
|
[⛳liberapay]: https://liberapay.com/pboling/donate
|
|
2143
|
-
[🖇osc-all-img]: https://img.shields.io/opencollective/all/kettle-rb
|
|
2144
|
-
[🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/kettle-rb
|
|
2145
|
-
[🖇osc-backers-img]: https://img.shields.io/opencollective/backers/kettle-rb
|
|
2146
|
-
[🖇osc-backers]: https://opencollective.com/kettle-rb#backer
|
|
2147
|
-
[🖇osc-backers-i]: https://opencollective.com/kettle-rb/backers/badge.svg?style=flat
|
|
2148
|
-
[🖇osc-sponsors]: https://opencollective.com/kettle-rb#sponsor
|
|
2149
|
-
[🖇osc-sponsors-i]: https://opencollective.com/kettle-rb/sponsors/badge.svg?style=flat
|
|
2150
|
-
[🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/kettle-rb?style=for-the-badge
|
|
2151
|
-
[🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/kettle-rb?style=for-the-badge
|
|
2152
|
-
[🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/kettle-rb?style=for-the-badge
|
|
2153
|
-
[🖇osc]: https://opencollective.com/kettle-rb
|
|
2154
1837
|
[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
|
|
2155
1838
|
[🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
|
|
2156
1839
|
[🖇sponsor]: https://github.com/sponsors/pboling
|
|
2157
|
-
[🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat
|
|
2158
|
-
[🖇polar]: https://polar.sh/pboling
|
|
2159
1840
|
[🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
|
|
2160
|
-
[🖇kofi]: https://ko-fi.com/
|
|
2161
|
-
[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat
|
|
2162
|
-
[🖇patreon]: https://patreon.com/galtzo
|
|
1841
|
+
[🖇kofi]: https://ko-fi.com/pboling
|
|
2163
1842
|
[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
|
|
2164
1843
|
[🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
|
|
2165
1844
|
[🖇buyme]: https://www.buymeacoffee.com/pboling
|
|
@@ -2172,19 +1851,22 @@ Thanks for RTFM. ☺️
|
|
|
2172
1851
|
[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
|
|
2173
1852
|
[✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
|
|
2174
1853
|
[✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
|
|
1854
|
+
[✉️ruby-forum-top-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=flat&logo=discourse&label=Ruby%20Users%20Forum
|
|
1855
|
+
[✉️ruby-forum-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=for-the-badge&logo=discourse&label=Ruby%20Users%20Forum
|
|
1856
|
+
[✉️ruby-forum]: https://www.rubyforum.org/tag/tree_haver
|
|
2175
1857
|
[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
|
|
2176
|
-
[⛳️gem-namespace]: https://github.com/
|
|
1858
|
+
[⛳️gem-namespace]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/tree_haver
|
|
2177
1859
|
[⛳️namespace-img]: https://img.shields.io/badge/namespace-TreeHaver-3C2D2D.svg?style=square&logo=ruby&logoColor=white
|
|
2178
|
-
[⛳️gem-name]: https://
|
|
1860
|
+
[⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/tree_haver
|
|
2179
1861
|
[⛳️name-img]: https://img.shields.io/badge/name-tree__haver-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
|
|
2180
|
-
[⛳️tag-img]: https://img.shields.io/github/tag/
|
|
2181
|
-
[⛳️tag]:
|
|
1862
|
+
[⛳️tag-img]: https://img.shields.io/github/tag/structuredmerge/structuredmerge-ruby.svg
|
|
1863
|
+
[⛳️tag]: https://github.com/structuredmerge/structuredmerge-ruby/releases
|
|
2182
1864
|
[🚂maint-blog]: http://www.railsbling.com/tags/tree_haver
|
|
2183
1865
|
[🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
|
|
2184
1866
|
[🚂maint-contact]: http://www.railsbling.com/contact
|
|
2185
1867
|
[🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
|
|
2186
1868
|
[💖🖇linkedin]: http://www.linkedin.com/in/peterboling
|
|
2187
|
-
[💖🖇linkedin-img]: https://img.shields.io/badge/
|
|
1869
|
+
[💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
|
|
2188
1870
|
[💖✌️wellfound]: https://wellfound.com/u/peter-boling
|
|
2189
1871
|
[💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
|
|
2190
1872
|
[💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
|
|
@@ -2213,106 +1895,63 @@ Thanks for RTFM. ☺️
|
|
|
2213
1895
|
[💁🏼♂️peterboling]: http://www.peterboling.com
|
|
2214
1896
|
[🚂railsbling]: http://www.railsbling.com
|
|
2215
1897
|
[📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
|
|
2216
|
-
[📜src-gl]: https://gitlab.com/
|
|
1898
|
+
[📜src-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/gems/tree_haver
|
|
2217
1899
|
[📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
|
|
2218
|
-
[📜src-cb]: https://codeberg.org/
|
|
2219
|
-
[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=
|
|
2220
|
-
[📜src-gh]: https://github.com/
|
|
1900
|
+
[📜src-cb]: https://codeberg.org/structuredmerge/structuredmerge-ruby/src/branch/main/gems/tree_haver
|
|
1901
|
+
[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=GitHub&logoColor=green
|
|
1902
|
+
[📜src-gh]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/tree_haver
|
|
2221
1903
|
[📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
|
|
2222
1904
|
[📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
|
|
2223
|
-
[📜gl-wiki]: https://gitlab.com/
|
|
2224
|
-
[📜gh-wiki]: https://github.com/
|
|
2225
|
-
[📜gl-wiki-img]: https://img.shields.io/badge/wiki-
|
|
2226
|
-
[📜gh-wiki-img]: https://img.shields.io/badge/wiki-
|
|
2227
|
-
[👽dl-rank]: https://
|
|
2228
|
-
[👽dl-ranki]: https://img.shields.io/gem/
|
|
2229
|
-
[👽
|
|
2230
|
-
[👽oss-helpi]: https://www.codetriage.com/kettle-rb/tree_haver/badges/users.svg
|
|
2231
|
-
[👽version]: https://bestgems.org/gems/tree_haver
|
|
1905
|
+
[📜gl-wiki]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/wikis/home
|
|
1906
|
+
[📜gh-wiki]: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
1907
|
+
[📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
|
|
1908
|
+
[📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
|
|
1909
|
+
[👽dl-rank]: https://clickgems.clickhouse.com/dashboard/tree_haver
|
|
1910
|
+
[👽dl-ranki]: https://img.shields.io/gem/dt/tree_haver.svg
|
|
1911
|
+
[👽version]: https://clickgems.clickhouse.com/dashboard/tree_haver
|
|
2232
1912
|
[👽versioni]: https://img.shields.io/gem/v/tree_haver.svg
|
|
2233
|
-
[
|
|
2234
|
-
[
|
|
2235
|
-
[🏀qlty-cov]: https://qlty.sh/gh/kettle-rb/projects/tree_haver/metrics/code?sort=coverageRating
|
|
2236
|
-
[🏀qlty-covi]: https://qlty.sh/gh/kettle-rb/projects/tree_haver/coverage.svg
|
|
2237
|
-
[🏀codecov]: https://codecov.io/gh/kettle-rb/tree_haver
|
|
2238
|
-
[🏀codecovi]: https://codecov.io/gh/kettle-rb/tree_haver/graph/badge.svg
|
|
2239
|
-
[🏀coveralls]: https://coveralls.io/github/kettle-rb/tree_haver?branch=main
|
|
2240
|
-
[🏀coveralls-img]: https://coveralls.io/repos/github/kettle-rb/tree_haver/badge.svg?branch=main
|
|
2241
|
-
[🖐codeQL]: https://github.com/kettle-rb/tree_haver/security/code-scanning
|
|
2242
|
-
[🖐codeQL-img]: https://github.com/kettle-rb/tree_haver/actions/workflows/codeql-analysis.yml/badge.svg
|
|
2243
|
-
[🚎2-cov-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/coverage.yml
|
|
2244
|
-
[🚎2-cov-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/coverage.yml/badge.svg
|
|
2245
|
-
[🚎3-hd-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/heads.yml
|
|
2246
|
-
[🚎3-hd-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/heads.yml/badge.svg
|
|
2247
|
-
[🚎5-st-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/style.yml
|
|
2248
|
-
[🚎5-st-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/style.yml/badge.svg
|
|
2249
|
-
[🚎6-s-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/supported.yml
|
|
2250
|
-
[🚎6-s-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/supported.yml/badge.svg
|
|
2251
|
-
[🚎9-t-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/truffle.yml
|
|
2252
|
-
[🚎9-t-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/truffle.yml/badge.svg
|
|
2253
|
-
[🚎11-c-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml
|
|
2254
|
-
[🚎11-c-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml/badge.svg
|
|
2255
|
-
[🚎12-crh-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/dep-heads.yml
|
|
2256
|
-
[🚎12-crh-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/dep-heads.yml/badge.svg
|
|
2257
|
-
[🚎13-🔒️-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/locked_deps.yml
|
|
2258
|
-
[🚎13-🔒️-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/locked_deps.yml/badge.svg
|
|
2259
|
-
[🚎14-🔓️-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/unlocked_deps.yml
|
|
2260
|
-
[🚎14-🔓️-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/unlocked_deps.yml/badge.svg
|
|
2261
|
-
[🚎15-🪪-wf]: https://github.com/kettle-rb/tree_haver/actions/workflows/license-eye.yml
|
|
2262
|
-
[🚎15-🪪-wfi]: https://github.com/kettle-rb/tree_haver/actions/workflows/license-eye.yml/badge.svg
|
|
2263
|
-
[💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
2264
|
-
[💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
2265
|
-
[💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
|
1913
|
+
[🚎11-c-wf]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml
|
|
1914
|
+
[🚎11-c-wfi]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml/badge.svg
|
|
2266
1915
|
[💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
|
|
2267
|
-
[
|
|
2268
|
-
[
|
|
2269
|
-
[
|
|
2270
|
-
[
|
|
2271
|
-
[
|
|
2272
|
-
[
|
|
2273
|
-
[💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
|
|
2274
|
-
[💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
|
|
2275
|
-
[🤝gh-issues]: https://github.com/kettle-rb/tree_haver/issues
|
|
2276
|
-
[🤝gh-pulls]: https://github.com/kettle-rb/tree_haver/pulls
|
|
2277
|
-
[🤝gl-issues]: https://gitlab.com/kettle-rb/tree_haver/-/issues
|
|
2278
|
-
[🤝gl-pulls]: https://gitlab.com/kettle-rb/tree_haver/-/merge_requests
|
|
2279
|
-
[🤝cb-issues]: https://codeberg.org/kettle-rb/tree_haver/issues
|
|
2280
|
-
[🤝cb-pulls]: https://codeberg.org/kettle-rb/tree_haver/pulls
|
|
1916
|
+
[🤝gh-issues]: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
1917
|
+
[🤝gh-pulls]: https://github.com/structuredmerge/structuredmerge-ruby/pulls
|
|
1918
|
+
[🤝gl-issues]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/issues
|
|
1919
|
+
[🤝gl-pulls]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/merge_requests
|
|
1920
|
+
[🤝cb-issues]: https://codeberg.org/structuredmerge/structuredmerge-ruby/issues
|
|
1921
|
+
[🤝cb-pulls]: https://codeberg.org/structuredmerge/structuredmerge-ruby/pulls
|
|
2281
1922
|
[🤝cb-donate]: https://donate.codeberg.org/
|
|
2282
|
-
[🤝contributing]: CONTRIBUTING.md
|
|
2283
|
-
[🏀codecov-g]: https://codecov.io/gh/kettle-rb/tree_haver/graphs/tree.svg
|
|
1923
|
+
[🤝contributing]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CONTRIBUTING.md
|
|
2284
1924
|
[🖐contrib-rocks]: https://contrib.rocks
|
|
2285
|
-
[🖐contributors]: https://github.com/
|
|
2286
|
-
[🖐contributors-img]: https://contrib.rocks/image?repo=
|
|
2287
|
-
[
|
|
2288
|
-
[🪇conduct]: CODE_OF_CONDUCT.md
|
|
1925
|
+
[🖐contributors]: https://github.com/structuredmerge/structuredmerge-ruby/graphs/contributors
|
|
1926
|
+
[🖐contributors-img]: https://contrib.rocks/image?repo=structuredmerge/structuredmerge-ruby
|
|
1927
|
+
[🪇conduct]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CODE_OF_CONDUCT.md
|
|
2289
1928
|
[🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
|
|
2290
1929
|
[📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
|
|
2291
1930
|
[📌semver]: https://semver.org/spec/v2.0.0.html
|
|
2292
1931
|
[📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
|
|
2293
1932
|
[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
|
|
2294
1933
|
[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
|
|
2295
|
-
[📌changelog]: CHANGELOG.md
|
|
1934
|
+
[📌changelog]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CHANGELOG.md
|
|
2296
1935
|
[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
|
|
2297
1936
|
[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
|
|
2298
1937
|
[📌gitmoji]: https://gitmoji.dev
|
|
2299
1938
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
2300
1939
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
2301
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-
|
|
2302
|
-
[🔐security]: SECURITY.md
|
|
1940
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-5.053-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1941
|
+
[🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
|
|
2303
1942
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
2304
1943
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
2305
|
-
[📄license]: LICENSE.
|
|
2306
|
-
[📄license-ref]:
|
|
2307
|
-
[📄license-img]: https://img.shields.io/badge/License-
|
|
2308
|
-
[📄license-compat]: https://
|
|
2309
|
-
[📄license-compat-img]: https://img.shields.io/badge/
|
|
1944
|
+
[📄license]: LICENSE.md
|
|
1945
|
+
[📄license-ref]: LICENSE.md
|
|
1946
|
+
[📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only_OR_PolyForm--Small--Business--1.0.0-259D6C.svg
|
|
1947
|
+
[📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
|
|
1948
|
+
[📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
|
|
2310
1949
|
[📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
|
|
2311
1950
|
[📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
|
|
2312
1951
|
[🚎yard-current]: http://rubydoc.info/gems/tree_haver
|
|
2313
1952
|
[🚎yard-head]: https://tree-haver.galtzo.com
|
|
2314
1953
|
[💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
|
|
2315
|
-
[💎SHA_checksums]: https://gitlab.com/
|
|
1954
|
+
[💎SHA_checksums]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/checksums
|
|
2316
1955
|
[💎rlts]: https://github.com/rubocop-lts/rubocop-lts
|
|
2317
1956
|
[💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
|
|
2318
1957
|
[💎appraisal2]: https://github.com/appraisal-rb/appraisal2
|