tree_haver 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +52 -2
- data/README.md +32 -32
- data/lib/tree_haver/backend_registry.rb +230 -7
- data/lib/tree_haver/parser.rb +5 -3
- data/lib/tree_haver/rspec/dependency_tags.rb +112 -84
- data/lib/tree_haver/rspec/testable_node.rb +217 -0
- data/lib/tree_haver/rspec.rb +11 -1
- data/lib/tree_haver/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +5 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf24354cab86e375d66426a05aa6afa5d852bc1941a8fe1235f0647a89891e77
|
|
4
|
+
data.tar.gz: 25f91798a18044f1e0b1b5deb83cc61fe734bd05f7391225a36dd11dbb47a941
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dcd6d8d3e481bd1e5201a83441d63d41af9be368eb8c746e7d27a44dfa7cb76345a014f7a426557e40c1becfecf9f9e9f60176d8c6d53a57978077146137f13d
|
|
7
|
+
data.tar.gz: 5f1dc4d8844b5da005d5d1ef59ac3a74c1794ee95d25de1c5c67a13198f671a06ad45ce82d3530e875230374339c5a749f2a3e96ffe555b194a05b7a6cc4a4ef
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
3
|
[![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog]
|
|
4
4
|
|
|
@@ -30,6 +30,54 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [5.0.1] - 2026-01-11
|
|
34
|
+
|
|
35
|
+
- TAG: [v5.0.1][5.0.1t]
|
|
36
|
+
- COVERAGE: 90.79% -- 2308/2542 lines in 30 files
|
|
37
|
+
- BRANCH COVERAGE: 78.09% -- 930/1191 branches in 30 files
|
|
38
|
+
- 94.76% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `TreeHaver::RSpec::TestableNode` - A testable node class for creating mock TreeHaver::Node instances
|
|
43
|
+
in tests without requiring an actual parser backend. Available via `require "tree_haver/rspec/testable_node"`
|
|
44
|
+
or automatically when using `require "tree_haver/rspec"`.
|
|
45
|
+
- `TestableNode.create(type:, text:, ...)` - Create a single test node
|
|
46
|
+
- `TestableNode.create_list(...)` - Create multiple test nodes
|
|
47
|
+
- `MockInnerNode` - The underlying mock that simulates backend-specific nodes
|
|
48
|
+
- Top-level `TestableNode` constant for convenience in specs
|
|
49
|
+
- **Fully Dynamic Tag Registration** in `TreeHaver::BackendRegistry`:
|
|
50
|
+
- `register_tag(tag_name, category:, backend_name:, require_path:)` - Register a complete dependency tag
|
|
51
|
+
with lazy loading support. External gems can now get full RSpec tag support without any hardcoded
|
|
52
|
+
knowledge in tree_haver.
|
|
53
|
+
- `tag_available?(tag_name)` - Check if a tag's dependency is available, with automatic lazy loading
|
|
54
|
+
via the registered `require_path`
|
|
55
|
+
- `registered_tags` - Get all registered tag names
|
|
56
|
+
- `tags_by_category(category)` - Get tags filtered by category (:backend, :gem, :parsing, :grammar, :engine, :other)
|
|
57
|
+
- `tag_metadata(tag_name)` - Get full metadata for a registered tag
|
|
58
|
+
- `tag_summary` - Get availability status of all registered tags
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- **Fully Dynamic Backend Availability** in `BackendRegistry` and `DependencyTags`:
|
|
63
|
+
- `register_tag` now dynamically defines `*_available?` methods on `DependencyTags` at registration time
|
|
64
|
+
- External gems automatically get availability methods when they call `register_tag`
|
|
65
|
+
- No changes to tree_haver are needed for new external backend gems
|
|
66
|
+
- Built-in backends (prism, psych, citrus, parslet) retain explicit methods
|
|
67
|
+
- `summary` method dynamically includes registered backends from BackendRegistry
|
|
68
|
+
- `backend_availability_methods` and `backend_tags` hashes are built dynamically
|
|
69
|
+
- RSpec exclusion filters for backend tags are configured dynamically from BackendRegistry
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **`TreeHaver::Parser#unwrap_language` bug fix for MRI and Rust backends**
|
|
74
|
+
- `:mri` and `:rust` cases were not returning the unwrapped language value
|
|
75
|
+
- The code called `lang.to_language` / `lang.inner_language` / `lang.name` but didn't `return` the result
|
|
76
|
+
- Now properly returns the unwrapped language for all backend types
|
|
77
|
+
- `any_markdown_backend_available?` now uses `BackendRegistry.tag_available?` instead of calling
|
|
78
|
+
`markly_available?` and `commonmarker_available?` directly. This fixes `NoMethodError` when
|
|
79
|
+
the external markdown backend gems haven't registered their tags yet.
|
|
80
|
+
|
|
33
81
|
## [5.0.0] - 2026-01-11
|
|
34
82
|
|
|
35
83
|
- TAG: [v5.0.0][5.0.0t]
|
|
@@ -1211,7 +1259,9 @@ Despite the major version bump to 3.0.0 (following semver due to the breaking `L
|
|
|
1211
1259
|
|
|
1212
1260
|
- Initial release
|
|
1213
1261
|
|
|
1214
|
-
[Unreleased]: https://github.com/kettle-rb/tree_haver/compare/v5.0.
|
|
1262
|
+
[Unreleased]: https://github.com/kettle-rb/tree_haver/compare/v5.0.1...HEAD
|
|
1263
|
+
[5.0.1]: https://github.com/kettle-rb/tree_haver/compare/v5.0.0...v5.0.1
|
|
1264
|
+
[5.0.1t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.1
|
|
1215
1265
|
[5.0.0]: https://github.com/kettle-rb/tree_haver/compare/v4.0.5...v5.0.0
|
|
1216
1266
|
[5.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.0
|
|
1217
1267
|
[4.0.5]: https://github.com/kettle-rb/tree_haver/compare/v4.0.4...v4.0.5
|
data/README.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
[rubygems-maint-policy]: https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/rubygems/POLICIES.md?plain=1#L187-L196
|
|
32
32
|
[policy-fail]: https://www.reddit.com/r/ruby/comments/1ove9vp/rubycentral_hates_this_one_fact/
|
|
33
33
|
|
|
34
|
-
[![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]]
|
|
34
|
+
[![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]][🖼️galtzo-discord] [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5][🖼️ruby-lang-i]][🖼️ruby-lang] [![kettle-rb Logo by Aboling0, CC BY-SA 4.0][🖼️kettle-rb-i]][🖼️kettle-rb]
|
|
35
35
|
|
|
36
36
|
[🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
|
|
37
37
|
[🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
# 🌴 TreeHaver
|
|
44
44
|
|
|
45
|
-
[![Version][👽versioni]]
|
|
45
|
+
[![Version][👽versioni]][👽dl-rank] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
|
|
46
46
|
|
|
47
47
|
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
|
|
48
48
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
`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.
|
|
52
52
|
|
|
53
|
-
[![OpenCollective Backers][🖇osc-backers-i]]
|
|
53
|
+
[![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 at ko-fi.com][🖇kofi-img]][🖇kofi]
|
|
54
54
|
|
|
55
55
|
## 🌻 Synopsis
|
|
56
56
|
|
|
@@ -525,18 +525,18 @@ tree_haver supports multiple parsing backends, but not all backends work on all
|
|
|
525
525
|
|
|
526
526
|
## 💡 Info you can shake a stick at
|
|
527
527
|
|
|
528
|
-
| Tokens to Remember | [![Gem name][⛳️name-img]]
|
|
528
|
+
| Tokens to Remember | [![Gem name][⛳️name-img]][👽dl-rank] [![Gem namespace][⛳️namespace-img]][📜src-gh] |
|
|
529
529
|
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
530
|
-
| Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]]
|
|
531
|
-
| Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]]
|
|
532
|
-
| Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]]
|
|
533
|
-
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]]
|
|
534
|
-
| Source | [![Source on GitLab.com][📜src-gl-img]]
|
|
535
|
-
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]]
|
|
536
|
-
| Compliance | [![License: MIT][📄license-img]]
|
|
537
|
-
| Style | [![Enforced Code Style Linter][💎rlts-img]]
|
|
538
|
-
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]]
|
|
539
|
-
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]]
|
|
530
|
+
| Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
|
|
531
|
+
| Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
|
|
532
|
+
| Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
|
|
533
|
+
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
|
534
|
+
| 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] [][🧮kloc] |
|
|
535
|
+
| 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] |
|
|
536
|
+
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img][📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
|
537
|
+
| 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] |
|
|
538
|
+
| 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] |
|
|
539
|
+
| `...` 💖 | [![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] |
|
|
540
540
|
|
|
541
541
|
### Compatibility
|
|
542
542
|
|
|
@@ -556,13 +556,13 @@ Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRu
|
|
|
556
556
|
| 🧪 [kettle-rb/tree\_haver on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
|
|
557
557
|
| 🧊 [kettle-rb/tree\_haver on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
|
|
558
558
|
| 🐙 [kettle-rb/tree\_haver on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
|
|
559
|
-
| 🎮️ [Discord Server][🖼️galtzo-discord] | [![Live Chat on Discord][✉️discord-invite-img-ftb]]
|
|
559
|
+
| 🎮️ [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] |
|
|
560
560
|
|
|
561
561
|
</details>
|
|
562
562
|
|
|
563
563
|
[gh-discussions]: https://github.com/kettle-rb/tree_haver/discussions
|
|
564
564
|
|
|
565
|
-
### Enterprise Support []
|
|
565
|
+
### Enterprise Support [][🏙️entsup-tidelift]
|
|
566
566
|
|
|
567
567
|
Available as part of the Tidelift Subscription.
|
|
568
568
|
|
|
@@ -571,7 +571,7 @@ Available as part of the Tidelift Subscription.
|
|
|
571
571
|
|
|
572
572
|
The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
|
|
573
573
|
|
|
574
|
-
[![Get help from me on Tidelift][🏙️entsup-tidelift-img]]
|
|
574
|
+
[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
|
|
575
575
|
|
|
576
576
|
- 💡Subscribe for support guarantees covering *all* your FLOSS dependencies
|
|
577
577
|
|
|
@@ -580,11 +580,11 @@ The maintainers of this and thousands of other packages are working with Tidelif
|
|
|
580
580
|
- 💡Tidelift pays maintainers to maintain the software you depend on\!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
|
|
581
581
|
Alternatively:
|
|
582
582
|
|
|
583
|
-
- [![Live Chat on Discord][✉️discord-invite-img-ftb]]
|
|
583
|
+
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
|
|
584
584
|
|
|
585
|
-
- [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]]
|
|
585
|
+
- [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork]
|
|
586
586
|
|
|
587
|
-
- [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]]
|
|
587
|
+
- [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor]
|
|
588
588
|
|
|
589
589
|
</details>
|
|
590
590
|
|
|
@@ -2033,7 +2033,7 @@ I’m developing a new library, [floss\_funding][🖇floss-funding-gem], designe
|
|
|
2033
2033
|
|
|
2034
2034
|
**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
|
|
2035
2035
|
|
|
2036
|
-
[![OpenCollective Backers][🖇osc-backers-i]]
|
|
2036
|
+
[![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]
|
|
2037
2037
|
|
|
2038
2038
|
## 🔐 Security
|
|
2039
2039
|
|
|
@@ -2045,7 +2045,7 @@ If you need some ideas of where to help, you could work on adding more code cove
|
|
|
2045
2045
|
or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
|
|
2046
2046
|
or use the gem and think about how it could be better.
|
|
2047
2047
|
|
|
2048
|
-
We [![Keep A Changelog][📗keep-changelog-img]]
|
|
2048
|
+
We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
|
|
2049
2049
|
|
|
2050
2050
|
See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
|
|
2051
2051
|
|
|
@@ -2055,20 +2055,20 @@ See [CONTRIBUTING.md][🤝contributing].
|
|
|
2055
2055
|
|
|
2056
2056
|
### Code Coverage
|
|
2057
2057
|
|
|
2058
|
-
[![Coverage Graph][🏀codecov-g]]
|
|
2058
|
+
[![Coverage Graph][🏀codecov-g]][🏀codecov]
|
|
2059
2059
|
|
|
2060
|
-
[![Coveralls Test Coverage][🏀coveralls-img]]
|
|
2060
|
+
[![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
|
|
2061
2061
|
|
|
2062
|
-
[![QLTY Test Coverage][🏀qlty-covi]]
|
|
2062
|
+
[![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
|
|
2063
2063
|
|
|
2064
2064
|
### 🪇 Code of Conduct
|
|
2065
2065
|
|
|
2066
2066
|
Everyone interacting with this project's codebases, issue trackers,
|
|
2067
|
-
chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]]
|
|
2067
|
+
chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
|
|
2068
2068
|
|
|
2069
2069
|
## 🌈 Contributors
|
|
2070
2070
|
|
|
2071
|
-
[![Contributors][🖐contributors-img]]
|
|
2071
|
+
[![Contributors][🖐contributors-img]][🖐contributors]
|
|
2072
2072
|
|
|
2073
2073
|
Made with [contributors-img][🖐contrib-rocks].
|
|
2074
2074
|
|
|
@@ -2089,7 +2089,7 @@ Also see GitLab Contributors: <https://gitlab.com/kettle-rb/tree_haver/-/graphs/
|
|
|
2089
2089
|
|
|
2090
2090
|
## 📌 Versioning
|
|
2091
2091
|
|
|
2092
|
-
This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]]
|
|
2092
|
+
This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver].
|
|
2093
2093
|
Violations of this scheme should be reported as bugs.
|
|
2094
2094
|
Specifically, if a minor or patch version is released that breaks backward compatibility,
|
|
2095
2095
|
a new version should be immediately released that restores compatibility.
|
|
@@ -2127,7 +2127,7 @@ See [CHANGELOG.md][📌changelog] for a list of releases.
|
|
|
2127
2127
|
## 📄 License
|
|
2128
2128
|
|
|
2129
2129
|
The gem is available as open source under the terms of
|
|
2130
|
-
the [MIT License][📄license] [![License: MIT][📄license-img]]
|
|
2130
|
+
the [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref].
|
|
2131
2131
|
See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].
|
|
2132
2132
|
|
|
2133
2133
|
### © Copyright
|
|
@@ -2155,11 +2155,11 @@ Please consider sponsoring me or the project.
|
|
|
2155
2155
|
|
|
2156
2156
|
To join the community or get help 👇️ Join the Discord.
|
|
2157
2157
|
|
|
2158
|
-
[![Live Chat on Discord][✉️discord-invite-img-ftb]]
|
|
2158
|
+
[![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
|
|
2159
2159
|
|
|
2160
2160
|
To say "thanks\!" ☝️ Join the Discord or 👇️ send money.
|
|
2161
2161
|
|
|
2162
|
-
[![Sponsor kettle-rb/tree\_haver on Open Source Collective][🖇osc-all-bottom-img]]
|
|
2162
|
+
[![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]
|
|
2163
2163
|
|
|
2164
2164
|
### Please give the project a star ⭐ ♥.
|
|
2165
2165
|
|
|
@@ -2323,7 +2323,7 @@ Thanks for RTFM. ☺️
|
|
|
2323
2323
|
[📌gitmoji]: https://gitmoji.dev
|
|
2324
2324
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
2325
2325
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
2326
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.
|
|
2326
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.542-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
2327
2327
|
[🔐security]: SECURITY.md
|
|
2328
2328
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
2329
2329
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -22,27 +22,44 @@ module TreeHaver
|
|
|
22
22
|
# - **External backends** (commonmarker-merge, markly-merge, rbs-merge) register
|
|
23
23
|
# their checkers when their backend module is loaded
|
|
24
24
|
#
|
|
25
|
+
# == Full Tag Registration
|
|
26
|
+
#
|
|
27
|
+
# External gems can register complete tag support using {register_tag}:
|
|
28
|
+
# - Tag name (e.g., :commonmarker_backend)
|
|
29
|
+
# - Category (:backend, :gem, :parsing, :grammar)
|
|
30
|
+
# - Availability checker
|
|
31
|
+
# - Optional require path for lazy loading
|
|
32
|
+
#
|
|
33
|
+
# This enables tree_haver/rspec/dependency_tags to automatically configure
|
|
34
|
+
# RSpec exclusion filters for any registered tag without hardcoded knowledge.
|
|
35
|
+
#
|
|
25
36
|
# == Thread Safety
|
|
26
37
|
#
|
|
27
38
|
# All operations are thread-safe using a Mutex for synchronization.
|
|
28
39
|
# Results are cached after first check for performance.
|
|
29
40
|
#
|
|
30
|
-
# @example Registering a backend availability checker (
|
|
41
|
+
# @example Registering a backend availability checker (simple form)
|
|
31
42
|
# # In commonmarker-merge/lib/commonmarker/merge/backend.rb
|
|
32
43
|
# TreeHaver::BackendRegistry.register_availability_checker(:commonmarker) do
|
|
33
44
|
# available?
|
|
34
45
|
# end
|
|
35
46
|
#
|
|
47
|
+
# @example Registering a full tag with require path (preferred for external gems)
|
|
48
|
+
# TreeHaver::BackendRegistry.register_tag(
|
|
49
|
+
# :commonmarker_backend,
|
|
50
|
+
# category: :backend,
|
|
51
|
+
# backend_name: :commonmarker,
|
|
52
|
+
# require_path: "commonmarker/merge"
|
|
53
|
+
# ) { Commonmarker::Merge::Backend.available? }
|
|
54
|
+
#
|
|
36
55
|
# @example Checking backend availability
|
|
37
56
|
# TreeHaver::BackendRegistry.available?(:commonmarker) # => true/false
|
|
38
57
|
# TreeHaver::BackendRegistry.available?(:markly) # => true/false
|
|
39
58
|
# TreeHaver::BackendRegistry.available?(:rbs) # => true/false
|
|
40
59
|
#
|
|
41
|
-
# @example
|
|
42
|
-
# TreeHaver::BackendRegistry.
|
|
43
|
-
#
|
|
44
|
-
# @example Getting all registered backends
|
|
45
|
-
# TreeHaver::BackendRegistry.registered_backends # => [:mri, :rust, :ffi, ...]
|
|
60
|
+
# @example Getting all registered tags
|
|
61
|
+
# TreeHaver::BackendRegistry.registered_tags # => [:commonmarker_backend, :markly_backend, ...]
|
|
62
|
+
# TreeHaver::BackendRegistry.tags_by_category(:backend) # => [...]
|
|
46
63
|
#
|
|
47
64
|
# @see TreeHaver::RSpec::DependencyTags Uses BackendRegistry for dynamic backend detection
|
|
48
65
|
# @api public
|
|
@@ -50,15 +67,83 @@ module TreeHaver
|
|
|
50
67
|
@mutex = Mutex.new
|
|
51
68
|
@availability_checkers = {} # rubocop:disable ThreadSafety/MutableClassInstanceVariable
|
|
52
69
|
@availability_cache = {} # rubocop:disable ThreadSafety/MutableClassInstanceVariable
|
|
70
|
+
@tag_registry = {} # rubocop:disable ThreadSafety/MutableClassInstanceVariable
|
|
71
|
+
|
|
72
|
+
# Tag categories for organizing dependency tags
|
|
73
|
+
# @api private
|
|
74
|
+
CATEGORIES = %i[backend gem parsing grammar engine other].freeze
|
|
53
75
|
|
|
54
76
|
module_function
|
|
55
77
|
|
|
56
|
-
# Register
|
|
78
|
+
# Register a full dependency tag with all metadata
|
|
79
|
+
#
|
|
80
|
+
# This is the preferred method for external gems to register their availability
|
|
81
|
+
# with complete tag support. It registers both the availability checker and
|
|
82
|
+
# the tag metadata needed for RSpec configuration.
|
|
83
|
+
#
|
|
84
|
+
# When a tag is registered, this also dynamically defines a `*_available?` method
|
|
85
|
+
# on `TreeHaver::RSpec::DependencyTags` if it doesn't already exist.
|
|
86
|
+
#
|
|
87
|
+
# @param tag_name [Symbol] the RSpec tag name (e.g., :commonmarker_backend)
|
|
88
|
+
# @param category [Symbol] one of :backend, :gem, :parsing, :grammar, :engine, :other
|
|
89
|
+
# @param backend_name [Symbol, nil] the backend name for availability checks (defaults to tag without suffix)
|
|
90
|
+
# @param require_path [String, nil] optional require path to load before checking availability
|
|
91
|
+
# @param checker [#call, nil] a callable that returns true if available
|
|
92
|
+
# @yield Block form of checker (alternative to passing a callable)
|
|
93
|
+
# @yieldreturn [Boolean] true if the tag's dependency is available
|
|
94
|
+
# @return [void]
|
|
95
|
+
#
|
|
96
|
+
# @example Register a backend tag with require path
|
|
97
|
+
# TreeHaver::BackendRegistry.register_tag(
|
|
98
|
+
# :commonmarker_backend,
|
|
99
|
+
# category: :backend,
|
|
100
|
+
# require_path: "commonmarker/merge"
|
|
101
|
+
# ) { Commonmarker::Merge::Backend.available? }
|
|
102
|
+
#
|
|
103
|
+
# @example Register a gem tag
|
|
104
|
+
# TreeHaver::BackendRegistry.register_tag(
|
|
105
|
+
# :toml_gem,
|
|
106
|
+
# category: :gem,
|
|
107
|
+
# require_path: "toml"
|
|
108
|
+
# ) { defined?(TOML) }
|
|
109
|
+
def register_tag(tag_name, category:, backend_name: nil, require_path: nil, checker: nil, &block)
|
|
110
|
+
callable = checker || block
|
|
111
|
+
raise ArgumentError, "Must provide a checker callable or block" unless callable
|
|
112
|
+
raise ArgumentError, "Checker must respond to #call" unless callable.respond_to?(:call)
|
|
113
|
+
raise ArgumentError, "Invalid category: #{category}" unless CATEGORIES.include?(category)
|
|
114
|
+
|
|
115
|
+
tag_sym = tag_name.to_sym
|
|
116
|
+
# Derive backend_name from tag_name if not provided (e.g., :commonmarker_backend -> :commonmarker)
|
|
117
|
+
derived_backend = backend_name || tag_sym.to_s.sub(/_backend$/, "").to_sym
|
|
118
|
+
|
|
119
|
+
@mutex.synchronize do
|
|
120
|
+
@tag_registry[tag_sym] = {
|
|
121
|
+
category: category,
|
|
122
|
+
backend_name: derived_backend,
|
|
123
|
+
require_path: require_path,
|
|
124
|
+
checker: callable,
|
|
125
|
+
}
|
|
126
|
+
# Also register as availability checker for the backend name
|
|
127
|
+
@availability_checkers[derived_backend] = callable
|
|
128
|
+
# Clear caches
|
|
129
|
+
@availability_cache.delete(derived_backend)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Dynamically define the availability method on DependencyTags
|
|
133
|
+
# This happens outside the mutex to avoid potential deadlock
|
|
134
|
+
define_availability_method(derived_backend, tag_sym)
|
|
135
|
+
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Register an availability checker for a backend (simple form)
|
|
57
140
|
#
|
|
58
141
|
# The checker should be a callable (lambda/proc/block) that returns true if
|
|
59
142
|
# the backend is available and can be used. The checker is called lazily
|
|
60
143
|
# (only when {available?} is first called for this backend).
|
|
61
144
|
#
|
|
145
|
+
# For full tag support including require paths, use {register_tag} instead.
|
|
146
|
+
#
|
|
62
147
|
# @param backend_name [Symbol, String] the backend name (e.g., :commonmarker, :markly)
|
|
63
148
|
# @param checker [#call, nil] a callable that returns true if the backend is available
|
|
64
149
|
# @yield Block form of checker (alternative to passing a callable)
|
|
@@ -206,10 +291,118 @@ module TreeHaver
|
|
|
206
291
|
@mutex.synchronize do
|
|
207
292
|
@availability_checkers.clear
|
|
208
293
|
@availability_cache.clear
|
|
294
|
+
@tag_registry.clear
|
|
209
295
|
end
|
|
210
296
|
nil
|
|
211
297
|
end
|
|
212
298
|
|
|
299
|
+
# ============================================================
|
|
300
|
+
# Tag Registry Methods
|
|
301
|
+
# ============================================================
|
|
302
|
+
|
|
303
|
+
# Get all registered tag names
|
|
304
|
+
#
|
|
305
|
+
# @return [Array<Symbol>] list of registered tag names
|
|
306
|
+
#
|
|
307
|
+
# @example
|
|
308
|
+
# TreeHaver::BackendRegistry.registered_tags
|
|
309
|
+
# # => [:commonmarker_backend, :markly_backend, :toml_gem, ...]
|
|
310
|
+
def registered_tags
|
|
311
|
+
@mutex.synchronize do
|
|
312
|
+
@tag_registry.keys.dup
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Get tags filtered by category
|
|
317
|
+
#
|
|
318
|
+
# @param category [Symbol] one of :backend, :gem, :parsing, :grammar, :engine, :other
|
|
319
|
+
# @return [Array<Symbol>] list of tag names in that category
|
|
320
|
+
#
|
|
321
|
+
# @example
|
|
322
|
+
# TreeHaver::BackendRegistry.tags_by_category(:backend)
|
|
323
|
+
# # => [:commonmarker_backend, :markly_backend, :mri_backend, ...]
|
|
324
|
+
def tags_by_category(category)
|
|
325
|
+
@mutex.synchronize do
|
|
326
|
+
@tag_registry.select { |_, meta| meta[:category] == category }.keys
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Get tag metadata
|
|
331
|
+
#
|
|
332
|
+
# @param tag_name [Symbol] the tag name
|
|
333
|
+
# @return [Hash, nil] tag metadata or nil if not registered
|
|
334
|
+
#
|
|
335
|
+
# @example
|
|
336
|
+
# TreeHaver::BackendRegistry.tag_metadata(:commonmarker_backend)
|
|
337
|
+
# # => { category: :backend, backend_name: :commonmarker, require_path: "commonmarker/merge", checker: #<Proc> }
|
|
338
|
+
def tag_metadata(tag_name)
|
|
339
|
+
@mutex.synchronize do
|
|
340
|
+
@tag_registry[tag_name.to_sym]&.dup
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Check if a tag is registered
|
|
345
|
+
#
|
|
346
|
+
# @param tag_name [Symbol] the tag name
|
|
347
|
+
# @return [Boolean] true if the tag is registered
|
|
348
|
+
def tag_registered?(tag_name)
|
|
349
|
+
@mutex.synchronize do
|
|
350
|
+
@tag_registry.key?(tag_name.to_sym)
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Check if a tag's dependency is available
|
|
355
|
+
#
|
|
356
|
+
# This method handles require paths: if the tag has a require_path, it will
|
|
357
|
+
# attempt to load the gem before checking availability. This enables lazy
|
|
358
|
+
# loading of external gems.
|
|
359
|
+
#
|
|
360
|
+
# @param tag_name [Symbol] the tag name to check
|
|
361
|
+
# @return [Boolean] true if the tag's dependency is available
|
|
362
|
+
#
|
|
363
|
+
# @example
|
|
364
|
+
# TreeHaver::BackendRegistry.tag_available?(:commonmarker_backend) # => true/false
|
|
365
|
+
def tag_available?(tag_name)
|
|
366
|
+
tag_sym = tag_name.to_sym
|
|
367
|
+
|
|
368
|
+
# Get tag metadata
|
|
369
|
+
meta = @mutex.synchronize { @tag_registry[tag_sym] }
|
|
370
|
+
|
|
371
|
+
# If tag not registered, check if it's a backend name with _backend suffix
|
|
372
|
+
unless meta
|
|
373
|
+
# Try to derive backend name (e.g., :commonmarker_backend -> :commonmarker)
|
|
374
|
+
backend_name = tag_sym.to_s.sub(/_backend$/, "").to_sym
|
|
375
|
+
return available?(backend_name) if backend_name != tag_sym
|
|
376
|
+
return false
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Try to load the gem if require_path is specified
|
|
380
|
+
if meta[:require_path]
|
|
381
|
+
begin
|
|
382
|
+
require meta[:require_path]
|
|
383
|
+
rescue LoadError
|
|
384
|
+
# Gem not available
|
|
385
|
+
return false
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Check availability using the backend name
|
|
390
|
+
available?(meta[:backend_name])
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Get a summary of all registered tags and their availability
|
|
394
|
+
#
|
|
395
|
+
# @return [Hash{Symbol => Boolean}] map of tag name to availability
|
|
396
|
+
#
|
|
397
|
+
# @example
|
|
398
|
+
# TreeHaver::BackendRegistry.tag_summary
|
|
399
|
+
# # => { commonmarker_backend: true, markly_backend: false, ... }
|
|
400
|
+
def tag_summary
|
|
401
|
+
@mutex.synchronize { @tag_registry.keys.dup }.each_with_object({}) do |tag, result|
|
|
402
|
+
result[tag] = tag_available?(tag)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
213
406
|
# Check a built-in TreeHaver backend
|
|
214
407
|
#
|
|
215
408
|
# Attempts to find the backend module at `TreeHaver::Backends::<Name>` and
|
|
@@ -230,5 +423,35 @@ module TreeHaver
|
|
|
230
423
|
false
|
|
231
424
|
end
|
|
232
425
|
private_class_method :check_builtin_backend
|
|
426
|
+
|
|
427
|
+
# Dynamically define an availability method on DependencyTags
|
|
428
|
+
#
|
|
429
|
+
# This creates a `*_available?` method that checks tag_available? with
|
|
430
|
+
# memoization. The method is only defined if DependencyTags is loaded
|
|
431
|
+
# and doesn't already have a method with that name.
|
|
432
|
+
#
|
|
433
|
+
# @param backend_name [Symbol] the backend name (e.g., :commonmarker)
|
|
434
|
+
# @param tag_name [Symbol] the tag name (e.g., :commonmarker_backend)
|
|
435
|
+
# @return [void]
|
|
436
|
+
# @api private
|
|
437
|
+
def define_availability_method(backend_name, tag_name)
|
|
438
|
+
method_name = :"#{backend_name}_available?"
|
|
439
|
+
|
|
440
|
+
# Only define if DependencyTags is loaded
|
|
441
|
+
return unless defined?(TreeHaver::RSpec::DependencyTags)
|
|
442
|
+
|
|
443
|
+
deps = TreeHaver::RSpec::DependencyTags
|
|
444
|
+
|
|
445
|
+
# Don't override existing methods (built-in backends have explicit methods)
|
|
446
|
+
return if deps.respond_to?(method_name)
|
|
447
|
+
|
|
448
|
+
# Define the method dynamically
|
|
449
|
+
ivar = :"@#{backend_name}_available"
|
|
450
|
+
deps.define_singleton_method(method_name) do
|
|
451
|
+
return instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
|
452
|
+
instance_variable_set(ivar, TreeHaver::BackendRegistry.tag_available?(tag_name))
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
private_class_method :define_availability_method
|
|
233
456
|
end
|
|
234
457
|
end
|
data/lib/tree_haver/parser.rb
CHANGED
|
@@ -364,10 +364,12 @@ module TreeHaver
|
|
|
364
364
|
|
|
365
365
|
case lang.backend
|
|
366
366
|
when :mri
|
|
367
|
-
lang.to_language if lang.respond_to?(:to_language)
|
|
368
|
-
lang.inner_language if lang.respond_to?(:inner_language)
|
|
367
|
+
return lang.to_language if lang.respond_to?(:to_language)
|
|
368
|
+
return lang.inner_language if lang.respond_to?(:inner_language)
|
|
369
|
+
lang
|
|
369
370
|
when :rust
|
|
370
|
-
lang.name if lang.respond_to?(:name)
|
|
371
|
+
return lang.name if lang.respond_to?(:name)
|
|
372
|
+
lang
|
|
371
373
|
when :ffi
|
|
372
374
|
lang # FFI needs wrapper for to_ptr
|
|
373
375
|
when :java
|
|
@@ -162,7 +162,7 @@ require "set"
|
|
|
162
162
|
# ==== Language Parsing Capability Tags (*_parsing)
|
|
163
163
|
#
|
|
164
164
|
# [:toml_parsing]
|
|
165
|
-
# At least one TOML parser (tree-sitter-toml OR toml-rb/Citrus) is available.
|
|
165
|
+
# At least one TOML parser (tree-sitter-toml OR toml-rb/Citrus OR toml/Parslet) is available.
|
|
166
166
|
#
|
|
167
167
|
# [:markdown_parsing]
|
|
168
168
|
# At least one markdown parser (commonmarker OR markly) is available.
|
|
@@ -531,25 +531,26 @@ module TreeHaver
|
|
|
531
531
|
nil
|
|
532
532
|
end
|
|
533
533
|
|
|
534
|
-
#
|
|
534
|
+
# ============================================================
|
|
535
|
+
# Dynamic Backend Availability (via BackendRegistry)
|
|
536
|
+
# ============================================================
|
|
535
537
|
#
|
|
536
|
-
#
|
|
538
|
+
# External gems register tags with BackendRegistry.register_tag which
|
|
539
|
+
# dynamically defines *_available? methods on this module.
|
|
537
540
|
#
|
|
538
|
-
# @
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
# Check if markly gem is available
|
|
541
|
+
# @example External gem registers a tag
|
|
542
|
+
# TreeHaver::BackendRegistry.register_tag(
|
|
543
|
+
# :my_backend_backend,
|
|
544
|
+
# category: :backend,
|
|
545
|
+
# require_path: "my_backend/merge"
|
|
546
|
+
# ) { MyBackend::Merge::Backend.available? }
|
|
545
547
|
#
|
|
546
|
-
#
|
|
548
|
+
# # The registration automatically defines:
|
|
549
|
+
# TreeHaver::RSpec::DependencyTags.my_backend_available? # => true/false
|
|
547
550
|
#
|
|
548
|
-
#
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
@markly_available = TreeHaver::BackendRegistry.available?(:markly)
|
|
552
|
-
end
|
|
551
|
+
# Built-in backends (prism, psych, citrus, parslet) have explicit methods
|
|
552
|
+
# defined below. External backends get methods defined dynamically when
|
|
553
|
+
# their gem calls register_tag.
|
|
553
554
|
|
|
554
555
|
# Check if prism gem is available
|
|
555
556
|
#
|
|
@@ -738,9 +739,13 @@ module TreeHaver
|
|
|
738
739
|
|
|
739
740
|
# Check if at least one markdown backend is available
|
|
740
741
|
#
|
|
742
|
+
# Uses BackendRegistry.tag_available? to check external backends that may
|
|
743
|
+
# not have their methods defined yet (registered by external gems).
|
|
744
|
+
#
|
|
741
745
|
# @return [Boolean] true if any markdown backend works
|
|
742
746
|
def any_markdown_backend_available?
|
|
743
|
-
|
|
747
|
+
TreeHaver::BackendRegistry.tag_available?(:markly_backend) ||
|
|
748
|
+
TreeHaver::BackendRegistry.tag_available?(:commonmarker_backend)
|
|
744
749
|
end
|
|
745
750
|
|
|
746
751
|
def any_native_grammar_available?
|
|
@@ -812,46 +817,69 @@ module TreeHaver
|
|
|
812
817
|
# Use stored blocked_backends if available, otherwise compute dynamically
|
|
813
818
|
blocked = @blocked_backends || compute_blocked_backends
|
|
814
819
|
|
|
815
|
-
{
|
|
820
|
+
result = {
|
|
816
821
|
# Backend selection from environment variables
|
|
817
822
|
selected_backend: selected_backend,
|
|
818
823
|
allowed_native_backends: allowed_native_backends,
|
|
819
824
|
allowed_ruby_backends: allowed_ruby_backends,
|
|
820
|
-
# TreeHaver backends (*_backend) - skip blocked backends to avoid loading them
|
|
821
|
-
ffi_backend: blocked.include?(:ffi) ? :blocked : ffi_available?,
|
|
822
|
-
mri_backend: blocked.include?(:mri) ? :blocked : mri_backend_available?,
|
|
823
|
-
rust_backend: blocked.include?(:rust) ? :blocked : rust_backend_available?,
|
|
824
|
-
java_backend: blocked.include?(:java) ? :blocked : java_backend_available?,
|
|
825
|
-
prism_backend: blocked.include?(:prism) ? :blocked : prism_available?,
|
|
826
|
-
psych_backend: blocked.include?(:psych) ? :blocked : psych_available?,
|
|
827
|
-
commonmarker_backend: blocked.include?(:commonmarker) ? :blocked : commonmarker_available?,
|
|
828
|
-
markly_backend: blocked.include?(:markly) ? :blocked : markly_available?,
|
|
829
|
-
citrus_backend: blocked.include?(:citrus) ? :blocked : citrus_available?,
|
|
830
|
-
parslet_backend: blocked.include?(:parslet) ? :blocked : parslet_available?,
|
|
831
|
-
rbs_backend: blocked.include?(:rbs) ? :blocked : rbs_backend_available?,
|
|
832
|
-
# Ruby engines (*_engine)
|
|
833
|
-
ruby_engine: RUBY_ENGINE,
|
|
834
|
-
mri_engine: mri?,
|
|
835
|
-
jruby_engine: jruby?,
|
|
836
|
-
truffleruby_engine: truffleruby?,
|
|
837
|
-
# Tree-sitter grammars (*_grammar) - also respect blocked backends
|
|
838
|
-
# since grammar checks may load backends
|
|
839
|
-
libtree_sitter: libtree_sitter_available?,
|
|
840
|
-
bash_grammar: blocked.include?(:mri) ? :blocked : tree_sitter_bash_available?,
|
|
841
|
-
toml_grammar: blocked.include?(:mri) ? :blocked : tree_sitter_toml_available?,
|
|
842
|
-
json_grammar: blocked.include?(:mri) ? :blocked : tree_sitter_json_available?,
|
|
843
|
-
jsonc_grammar: blocked.include?(:mri) ? :blocked : tree_sitter_jsonc_available?,
|
|
844
|
-
rbs_grammar: blocked.include?(:mri) ? :blocked : tree_sitter_rbs_available?,
|
|
845
|
-
any_native_grammar: blocked.include?(:mri) ? :blocked : any_native_grammar_available?,
|
|
846
|
-
# Language parsing capabilities (*_parsing)
|
|
847
|
-
toml_parsing: any_toml_backend_available?,
|
|
848
|
-
markdown_parsing: any_markdown_backend_available?,
|
|
849
|
-
rbs_parsing: any_rbs_backend_available?,
|
|
850
|
-
# Specific libraries (*_gem)
|
|
851
|
-
toml_rb_gem: toml_rb_gem_available?,
|
|
852
|
-
toml_gem: toml_gem_available?,
|
|
853
|
-
rbs_gem: rbs_gem_available?,
|
|
854
825
|
}
|
|
826
|
+
|
|
827
|
+
# Built-in TreeHaver backends (*_backend) - skip blocked backends to avoid loading them
|
|
828
|
+
builtin_backends = {
|
|
829
|
+
ffi: :ffi_available?,
|
|
830
|
+
mri: :mri_backend_available?,
|
|
831
|
+
rust: :rust_backend_available?,
|
|
832
|
+
java: :java_backend_available?,
|
|
833
|
+
prism: :prism_available?,
|
|
834
|
+
psych: :psych_available?,
|
|
835
|
+
citrus: :citrus_available?,
|
|
836
|
+
parslet: :parslet_available?,
|
|
837
|
+
rbs: :rbs_backend_available?,
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
builtin_backends.each do |backend, method|
|
|
841
|
+
tag = :"#{backend}_backend"
|
|
842
|
+
result[tag] = blocked.include?(backend) ? :blocked : public_send(method)
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
# Dynamically registered backends from BackendRegistry
|
|
846
|
+
TreeHaver::BackendRegistry.registered_tags.each do |tag_name|
|
|
847
|
+
next if result.key?(tag_name) # Don't override built-ins
|
|
848
|
+
|
|
849
|
+
meta = TreeHaver::BackendRegistry.tag_metadata(tag_name)
|
|
850
|
+
next unless meta && meta[:category] == :backend
|
|
851
|
+
|
|
852
|
+
backend = meta[:backend_name]
|
|
853
|
+
result[tag_name] = blocked.include?(backend) ? :blocked : TreeHaver::BackendRegistry.tag_available?(tag_name)
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
# Ruby engines (*_engine)
|
|
857
|
+
result[:ruby_engine] = RUBY_ENGINE
|
|
858
|
+
result[:mri_engine] = mri?
|
|
859
|
+
result[:jruby_engine] = jruby?
|
|
860
|
+
result[:truffleruby_engine] = truffleruby?
|
|
861
|
+
|
|
862
|
+
# Tree-sitter grammars (*_grammar) - also respect blocked backends
|
|
863
|
+
# since grammar checks may load backends
|
|
864
|
+
result[:libtree_sitter] = libtree_sitter_available?
|
|
865
|
+
result[:bash_grammar] = blocked.include?(:mri) ? :blocked : tree_sitter_bash_available?
|
|
866
|
+
result[:toml_grammar] = blocked.include?(:mri) ? :blocked : tree_sitter_toml_available?
|
|
867
|
+
result[:json_grammar] = blocked.include?(:mri) ? :blocked : tree_sitter_json_available?
|
|
868
|
+
result[:jsonc_grammar] = blocked.include?(:mri) ? :blocked : tree_sitter_jsonc_available?
|
|
869
|
+
result[:rbs_grammar] = blocked.include?(:mri) ? :blocked : tree_sitter_rbs_available?
|
|
870
|
+
result[:any_native_grammar] = blocked.include?(:mri) ? :blocked : any_native_grammar_available?
|
|
871
|
+
|
|
872
|
+
# Language parsing capabilities (*_parsing)
|
|
873
|
+
result[:toml_parsing] = any_toml_backend_available?
|
|
874
|
+
result[:markdown_parsing] = any_markdown_backend_available?
|
|
875
|
+
result[:rbs_parsing] = any_rbs_backend_available?
|
|
876
|
+
|
|
877
|
+
# Specific libraries (*_gem)
|
|
878
|
+
result[:toml_rb_gem] = toml_rb_gem_available?
|
|
879
|
+
result[:toml_gem] = toml_gem_available?
|
|
880
|
+
result[:rbs_gem] = rbs_gem_available?
|
|
881
|
+
|
|
882
|
+
result
|
|
855
883
|
end
|
|
856
884
|
|
|
857
885
|
# Get environment variable summary for debugging
|
|
@@ -907,7 +935,7 @@ module TreeHaver
|
|
|
907
935
|
# @param test_source [String] sample source code to parse
|
|
908
936
|
# @return [Boolean] true if parsing works without errors
|
|
909
937
|
def grammar_works?(language, test_source)
|
|
910
|
-
debug = ENV
|
|
938
|
+
debug = !ENV.fetch("TREE_HAVER_DEBUG", "false").casecmp?("false")
|
|
911
939
|
env_var = "TREE_SITTER_#{language.to_s.upcase}_PATH"
|
|
912
940
|
env_value = ENV[env_var]
|
|
913
941
|
|
|
@@ -972,7 +1000,7 @@ RSpec.configure do |config|
|
|
|
972
1000
|
|
|
973
1001
|
config.before(:suite) do
|
|
974
1002
|
# Print dependency summary if TREE_HAVER_DEBUG is set
|
|
975
|
-
|
|
1003
|
+
unless ENV.fetch("TREE_HAVER_DEBUG", "false").casecmp?("false")
|
|
976
1004
|
puts "\n=== TreeHaver Environment Variables ==="
|
|
977
1005
|
deps.env_summary.each do |var, value|
|
|
978
1006
|
puts " #{var}: #{value.inspect}"
|
|
@@ -1063,35 +1091,35 @@ RSpec.configure do |config|
|
|
|
1063
1091
|
#
|
|
1064
1092
|
# This is dynamic based on TreeHaver::Backends::BLOCKED_BY configuration.
|
|
1065
1093
|
|
|
1066
|
-
#
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1094
|
+
# Build backend maps dynamically from BackendRegistry and built-in backends
|
|
1095
|
+
# This allows external gems to register and automatically get tag support
|
|
1096
|
+
backend_availability_methods = {}
|
|
1097
|
+
backend_tags = {}
|
|
1098
|
+
|
|
1099
|
+
# Built-in backends (always present in tree_haver)
|
|
1100
|
+
builtin_backends = %i[mri rust ffi java prism psych citrus parslet rbs]
|
|
1101
|
+
builtin_backends.each do |backend|
|
|
1102
|
+
# Special case for ffi which uses ffi_available? not ffi_backend_available?
|
|
1103
|
+
availability_method = (backend == :ffi) ? :ffi_available? : :"#{backend}_available?"
|
|
1104
|
+
# Special case for backends that use *_backend_available? naming
|
|
1105
|
+
availability_method = :"#{backend}_backend_available?" if %i[mri rust java rbs].include?(backend)
|
|
1106
|
+
|
|
1107
|
+
backend_availability_methods[backend] = availability_method
|
|
1108
|
+
backend_tags[backend] = :"#{backend}_backend"
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
# Add dynamically registered backends from BackendRegistry
|
|
1112
|
+
# This picks up external gems like commonmarker-merge, markly-merge, etc.
|
|
1113
|
+
TreeHaver::BackendRegistry.registered_tags.each do |tag_name|
|
|
1114
|
+
meta = TreeHaver::BackendRegistry.tag_metadata(tag_name)
|
|
1115
|
+
next unless meta && meta[:category] == :backend
|
|
1116
|
+
|
|
1117
|
+
backend_name = meta[:backend_name]
|
|
1118
|
+
next if backend_availability_methods.key?(backend_name) # Don't override built-ins
|
|
1119
|
+
|
|
1120
|
+
backend_availability_methods[backend_name] = :"#{backend_name}_available?"
|
|
1121
|
+
backend_tags[backend_name] = tag_name
|
|
1122
|
+
end
|
|
1095
1123
|
|
|
1096
1124
|
# Determine which backends should NOT have availability checked
|
|
1097
1125
|
# based on which *_backend_only tag is being run OR which backend is
|
|
@@ -1219,7 +1247,7 @@ RSpec.configure do |config|
|
|
|
1219
1247
|
# Language Parsing Capability Tags
|
|
1220
1248
|
# ============================================================
|
|
1221
1249
|
# Tags: *_parsing - require ANY parser for a language (any backend that can parse it)
|
|
1222
|
-
# :toml_parsing - any TOML parser (tree-sitter-toml OR toml-rb/Citrus)
|
|
1250
|
+
# :toml_parsing - any TOML parser (tree-sitter-toml OR toml-rb/Citrus OR toml/Parslet)
|
|
1223
1251
|
# :markdown_parsing - any Markdown parser (commonmarker OR markly)
|
|
1224
1252
|
# :rbs_parsing - any RBS parser (rbs gem OR tree-sitter-rbs)
|
|
1225
1253
|
# :native_parsing - any native tree-sitter backend + grammar
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ensure TreeHaver::Node and TreeHaver::Point are loaded
|
|
4
|
+
require "tree_haver"
|
|
5
|
+
|
|
6
|
+
module TreeHaver
|
|
7
|
+
module RSpec
|
|
8
|
+
# A mock inner node that provides the minimal interface TreeHaver::Node expects.
|
|
9
|
+
#
|
|
10
|
+
# This is what TreeHaver::Node wraps - it simulates the backend-specific node
|
|
11
|
+
# (like tree-sitter's Node, Markly::Node, etc.)
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class MockInnerNode
|
|
15
|
+
attr_reader :type, :start_byte, :end_byte, :children_data
|
|
16
|
+
|
|
17
|
+
def initialize(
|
|
18
|
+
type:,
|
|
19
|
+
text: nil,
|
|
20
|
+
start_byte: 0,
|
|
21
|
+
end_byte: nil,
|
|
22
|
+
start_row: 0,
|
|
23
|
+
start_column: 0,
|
|
24
|
+
end_row: nil,
|
|
25
|
+
end_column: nil,
|
|
26
|
+
children: []
|
|
27
|
+
)
|
|
28
|
+
@type = type.to_s
|
|
29
|
+
@text_content = text
|
|
30
|
+
@start_byte = start_byte
|
|
31
|
+
@end_byte = end_byte || (text ? start_byte + text.length : start_byte)
|
|
32
|
+
@start_row = start_row
|
|
33
|
+
@start_column = start_column
|
|
34
|
+
@end_row = end_row || start_row
|
|
35
|
+
@end_column = end_column || (text ? start_column + text.length : start_column)
|
|
36
|
+
@children_data = children
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def start_point
|
|
40
|
+
TreeHaver::Point.new(@start_row, @start_column)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def end_point
|
|
44
|
+
TreeHaver::Point.new(@end_row, @end_column)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def child_count
|
|
48
|
+
@children_data.length
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def child(index)
|
|
52
|
+
return if index.nil? || index < 0 || index >= @children_data.length
|
|
53
|
+
|
|
54
|
+
@children_data[index]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Return children array (for enumerable behavior)
|
|
58
|
+
def children
|
|
59
|
+
@children_data
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def first_child
|
|
63
|
+
@children_data.first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def last_child
|
|
67
|
+
@children_data.last
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Iterate over children
|
|
71
|
+
def each(&block)
|
|
72
|
+
return enum_for(:each) unless block
|
|
73
|
+
|
|
74
|
+
@children_data.each(&block)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def named?
|
|
78
|
+
true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Test nodes are always valid (no parse errors)
|
|
82
|
+
def has_error?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Test nodes are never missing (not error recovery insertions)
|
|
87
|
+
def missing?
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Some backends provide text directly
|
|
92
|
+
def text
|
|
93
|
+
@text_content
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# For backends that use string_content (like Markly/Commonmarker)
|
|
97
|
+
def string_content
|
|
98
|
+
@text_content
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A real TreeHaver::Node that wraps a MockInnerNode.
|
|
103
|
+
#
|
|
104
|
+
# This gives us full TreeHaver::Node behavior (#text, #type, #source_position, etc.)
|
|
105
|
+
# while allowing us to control the underlying data for testing.
|
|
106
|
+
#
|
|
107
|
+
# TestableNode is designed for testing code that works with TreeHaver nodes
|
|
108
|
+
# without requiring an actual parser backend. It creates real TreeHaver::Node
|
|
109
|
+
# instances with controlled, predictable data.
|
|
110
|
+
#
|
|
111
|
+
# @example Creating a testable node
|
|
112
|
+
# node = TreeHaver::RSpec::TestableNode.create(
|
|
113
|
+
# type: :heading,
|
|
114
|
+
# text: "## My Heading",
|
|
115
|
+
# start_line: 1
|
|
116
|
+
# )
|
|
117
|
+
# node.text # => "## My Heading"
|
|
118
|
+
# node.type # => "heading"
|
|
119
|
+
# node.start_line # => 1
|
|
120
|
+
#
|
|
121
|
+
# @example Creating with children
|
|
122
|
+
# parent = TreeHaver::RSpec::TestableNode.create(
|
|
123
|
+
# type: :document,
|
|
124
|
+
# text: "# Title\n\nParagraph",
|
|
125
|
+
# children: [
|
|
126
|
+
# { type: :heading, text: "# Title", start_line: 1 },
|
|
127
|
+
# { type: :paragraph, text: "Paragraph", start_line: 3 },
|
|
128
|
+
# ]
|
|
129
|
+
# )
|
|
130
|
+
#
|
|
131
|
+
# @example Using the convenience constant
|
|
132
|
+
# # After requiring tree_haver/rspec/testable_node, you can use:
|
|
133
|
+
# node = TestableNode.create(type: :paragraph, text: "Hello")
|
|
134
|
+
#
|
|
135
|
+
class TestableNode < TreeHaver::Node
|
|
136
|
+
class << self
|
|
137
|
+
# Create a TestableNode with the given attributes.
|
|
138
|
+
#
|
|
139
|
+
# @param type [Symbol, String] Node type (e.g., :heading, :paragraph)
|
|
140
|
+
# @param text [String] The text content of the node
|
|
141
|
+
# @param start_line [Integer] 1-based start line number (default: 1)
|
|
142
|
+
# @param end_line [Integer, nil] 1-based end line number (default: calculated from text)
|
|
143
|
+
# @param start_column [Integer] 0-based start column (default: 0)
|
|
144
|
+
# @param end_column [Integer, nil] 0-based end column (default: calculated from text)
|
|
145
|
+
# @param start_byte [Integer] Start byte offset (default: 0)
|
|
146
|
+
# @param end_byte [Integer, nil] End byte offset (default: calculated from text)
|
|
147
|
+
# @param children [Array<Hash>] Child node specifications
|
|
148
|
+
# @param source [String, nil] Full source text (default: uses text param)
|
|
149
|
+
# @return [TestableNode]
|
|
150
|
+
def create(
|
|
151
|
+
type:,
|
|
152
|
+
text: "",
|
|
153
|
+
start_line: 1,
|
|
154
|
+
end_line: nil,
|
|
155
|
+
start_column: 0,
|
|
156
|
+
end_column: nil,
|
|
157
|
+
start_byte: 0,
|
|
158
|
+
end_byte: nil,
|
|
159
|
+
children: [],
|
|
160
|
+
source: nil
|
|
161
|
+
)
|
|
162
|
+
# Convert 1-based line to 0-based row
|
|
163
|
+
start_row = start_line - 1
|
|
164
|
+
end_row = end_line ? end_line - 1 : start_row + text.count("\n")
|
|
165
|
+
|
|
166
|
+
# Calculate end_column if not provided
|
|
167
|
+
if end_column.nil?
|
|
168
|
+
lines = text.split("\n", -1)
|
|
169
|
+
end_column = lines.last&.length || 0
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Build children as MockInnerNodes
|
|
173
|
+
child_nodes = children.map do |child_spec|
|
|
174
|
+
MockInnerNode.new(**child_spec)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
inner = MockInnerNode.new(
|
|
178
|
+
type: type,
|
|
179
|
+
text: text,
|
|
180
|
+
start_byte: start_byte,
|
|
181
|
+
end_byte: end_byte,
|
|
182
|
+
start_row: start_row,
|
|
183
|
+
start_column: start_column,
|
|
184
|
+
end_row: end_row,
|
|
185
|
+
end_column: end_column,
|
|
186
|
+
children: child_nodes,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Create a real TreeHaver::Node wrapping our mock
|
|
190
|
+
# Pass source so TreeHaver::Node can extract text if needed
|
|
191
|
+
new(inner, source: source || text)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Create multiple nodes from an array of specifications.
|
|
195
|
+
#
|
|
196
|
+
# @param specs [Array<Hash>] Array of node specifications
|
|
197
|
+
# @return [Array<TestableNode>]
|
|
198
|
+
def create_list(*specs)
|
|
199
|
+
specs.flatten.map { |spec| create(**spec) }
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Additional test helper methods
|
|
204
|
+
|
|
205
|
+
# Check if this is a testable node (for test assertions)
|
|
206
|
+
#
|
|
207
|
+
# @return [Boolean] true
|
|
208
|
+
def testable?
|
|
209
|
+
true
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Make TestableNode available at top level for convenience in specs.
|
|
216
|
+
# This allows specs to use `TestableNode.create(...)` without the full namespace.
|
|
217
|
+
TestableNode = TreeHaver::RSpec::TestableNode
|
data/lib/tree_haver/rspec.rb
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#
|
|
10
10
|
# This will load:
|
|
11
11
|
# - Dependency tags for conditional test execution
|
|
12
|
-
# -
|
|
12
|
+
# - TestableNode for creating mock nodes in tests
|
|
13
13
|
#
|
|
14
14
|
# @example spec_helper.rb
|
|
15
15
|
# require "tree_haver/rspec"
|
|
@@ -18,6 +18,16 @@
|
|
|
18
18
|
# # Your additional configuration...
|
|
19
19
|
# end
|
|
20
20
|
#
|
|
21
|
+
# @example Using TestableNode
|
|
22
|
+
# node = TestableNode.create(
|
|
23
|
+
# type: :heading,
|
|
24
|
+
# text: "## My Heading",
|
|
25
|
+
# start_line: 1
|
|
26
|
+
# )
|
|
27
|
+
# expect(node.type).to eq("heading")
|
|
28
|
+
#
|
|
21
29
|
# @see TreeHaver::RSpec::DependencyTags
|
|
30
|
+
# @see TreeHaver::RSpec::TestableNode
|
|
22
31
|
|
|
23
32
|
require_relative "rspec/dependency_tags"
|
|
33
|
+
require_relative "rspec/testable_node"
|
data/lib/tree_haver/version.rb
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tree_haver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -288,6 +288,7 @@ files:
|
|
|
288
288
|
- lib/tree_haver/point.rb
|
|
289
289
|
- lib/tree_haver/rspec.rb
|
|
290
290
|
- lib/tree_haver/rspec/dependency_tags.rb
|
|
291
|
+
- lib/tree_haver/rspec/testable_node.rb
|
|
291
292
|
- lib/tree_haver/tree.rb
|
|
292
293
|
- lib/tree_haver/version.rb
|
|
293
294
|
- sig/tree_haver.rbs
|
|
@@ -299,10 +300,10 @@ licenses:
|
|
|
299
300
|
- MIT
|
|
300
301
|
metadata:
|
|
301
302
|
homepage_uri: https://tree-haver.galtzo.com/
|
|
302
|
-
source_code_uri: https://github.com/kettle-rb/tree_haver/tree/v5.0.
|
|
303
|
-
changelog_uri: https://github.com/kettle-rb/tree_haver/blob/v5.0.
|
|
303
|
+
source_code_uri: https://github.com/kettle-rb/tree_haver/tree/v5.0.1
|
|
304
|
+
changelog_uri: https://github.com/kettle-rb/tree_haver/blob/v5.0.1/CHANGELOG.md
|
|
304
305
|
bug_tracker_uri: https://github.com/kettle-rb/tree_haver/issues
|
|
305
|
-
documentation_uri: https://www.rubydoc.info/gems/tree_haver/5.0.
|
|
306
|
+
documentation_uri: https://www.rubydoc.info/gems/tree_haver/5.0.1
|
|
306
307
|
funding_uri: https://github.com/sponsors/pboling
|
|
307
308
|
wiki_uri: https://github.com/kettle-rb/tree_haver/wiki
|
|
308
309
|
news_uri: https://www.railsbling.com/tags/tree_haver
|
metadata.gz.sig
CHANGED
|
Binary file
|