ast-merge 2.0.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +68 -1
- data/README.md +21 -20
- data/lib/ast/merge/content_match_refiner.rb +6 -17
- data/lib/ast/merge/file_analyzable.rb +4 -0
- data/lib/ast/merge/navigable_statement.rb +3 -11
- data/lib/ast/merge/node_typing.rb +4 -1
- data/lib/ast/merge/{partial_template_merger.rb → partial_template_merger_base.rb} +87 -173
- data/lib/ast/merge/recipe/preset.rb +13 -0
- data/lib/ast/merge/recipe/runner.rb +23 -3
- data/lib/ast/merge/rspec/dependency_tags.rb +15 -1
- data/lib/ast/merge/version.rb +1 -1
- data/lib/ast/merge.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +7 -7
- 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: 79e0b486476d9494e6e1065d71b13ef8b940bd7d5db8caffd4784231ce239ad2
|
|
4
|
+
data.tar.gz: 941f9604854c537b087b76a253945f35dbc81f8f5b701f60dcad0ab1b9a35026
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8b51479340dd11352faac1e7ca2933481dcbf012b1fbff8176a5f425e07717848dce99f4b631cdc5562fc897e4ae2bb7af06d9380d105a6f25ab612473cd231
|
|
7
|
+
data.tar.gz: d45e550372da739a549ba78c27fe15836a8ca17b674c88dcee958f0f58732f54b7d52d6d9b99a22ee635f2ad1862c9a2631e8cc15fb4fa3c38aca528d58142cc
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,69 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [3.0.0] - 2026-01-05
|
|
34
|
+
|
|
35
|
+
- TAG: [v3.0.0][3.0.0t]
|
|
36
|
+
- COVERAGE: 96.93% -- 2462/2540 lines in 47 files
|
|
37
|
+
- BRANCH COVERAGE: 89.62% -- 794/886 branches in 47 files
|
|
38
|
+
- 98.72% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `TestableNode` spec helper class that wraps a mock in a real `TreeHaver::Node`, providing consistent API testing without relying on fragile mocks
|
|
43
|
+
- `Recipe::Preset#match_refiner` accessor method (was missing, causing errors in Recipe::Runner)
|
|
44
|
+
- Minimal reproduction specs for `to_commonmark` normalization behavior:
|
|
45
|
+
- `spec/integration/link_reference_preservation_spec.rb` - tests link ref preservation
|
|
46
|
+
- `spec/integration/table_formatting_preservation_spec.rb` - tests table padding preservation
|
|
47
|
+
- `Ast::Merge::PartialTemplateMergerBase` - Abstract base class for parser-agnostic partial template merging
|
|
48
|
+
- `#build_position_based_signature_generator` - Creates signature generators that match elements by position
|
|
49
|
+
- Position counters reset per document key, enabling tables at same position to match regardless of structure
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **BREAKING**: `NavigableStatement#text` now requires nodes to conform to TreeHaver Node API (must have `#text` method)
|
|
54
|
+
- Removed conditional fallbacks for `to_plaintext`, `to_commonmark`, `slice`
|
|
55
|
+
- Nodes must now implement `#text` directly (all TreeHaver backends already do)
|
|
56
|
+
- **BREAKING**: `ContentMatchRefiner#extract_content` now requires nodes to conform to TreeHaver Node API
|
|
57
|
+
- Removed conditional fallbacks for `text_content`, `string_content`, `content`, `to_s`
|
|
58
|
+
- Custom `content_extractor` proc still supported for non-standard nodes
|
|
59
|
+
- Signature generators and typing scripts now receive TreeHaver nodes directly (no NavigableStatement wrapping)
|
|
60
|
+
- Removed NavigableStatement wrapping from `FileAnalyzable#generate_signature` and `NodeTyping.process`
|
|
61
|
+
|
|
62
|
+
### Removed
|
|
63
|
+
|
|
64
|
+
- **BREAKING**: `Ast::Merge::PartialTemplateMerger` removed. Use `Markdown::Merge::PartialTemplateMerger` directly.
|
|
65
|
+
- The base class `Ast::Merge::PartialTemplateMergerBase` remains for other parsers to extend
|
|
66
|
+
- Migration: change `Ast::Merge::PartialTemplateMerger.new(parser: :markly, ...)` to
|
|
67
|
+
`Markdown::Merge::PartialTemplateMerger.new(backend: :markly, ...)`
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- **Source-based rendering**: `Markdown::Merge::PartialTemplateMerger#node_to_text` now prefers extracting
|
|
72
|
+
original source text using `analysis.source_range` instead of `to_commonmark`. This preserves:
|
|
73
|
+
- Link reference definitions (no conversion to inline links)
|
|
74
|
+
- Table column padding/alignment
|
|
75
|
+
- Original formatting exactly as written
|
|
76
|
+
|
|
77
|
+
## [2.0.10] - 2026-01-04
|
|
78
|
+
|
|
79
|
+
- TAG: [v2.0.10][2.0.10t]
|
|
80
|
+
- COVERAGE: 97.10% -- 2642/2721 lines in 48 files
|
|
81
|
+
- BRANCH COVERAGE: 89.57% -- 893/997 branches in 48 files
|
|
82
|
+
- 98.72% documented
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- Dependency tags for `rbs_merge` and `not_rbs_merge`
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- Upgraded to `tree_haver` v3.2.4 (major new features, and bug fixes, see [release notes](https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.4))
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- `PartialTemplateMerger#build_merged_content` previously always injected an extra newline between parts, now join is context-aware
|
|
95
|
+
|
|
33
96
|
## [2.0.9] - 2026-01-02
|
|
34
97
|
|
|
35
98
|
- TAG: [v2.0.9][2.0.9t]
|
|
@@ -444,7 +507,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
444
507
|
|
|
445
508
|
- Initial release
|
|
446
509
|
|
|
447
|
-
[Unreleased]: https://github.com/kettle-rb/ast-merge/compare/
|
|
510
|
+
[Unreleased]: https://github.com/kettle-rb/ast-merge/compare/v3.0.0...HEAD
|
|
511
|
+
[3.0.0]: https://github.com/kettle-rb/ast-merge/compare/v2.0.10...v3.0.0
|
|
512
|
+
[3.0.0t]: https://github.com/kettle-rb/ast-merge/releases/tag/v3.0.0
|
|
513
|
+
[2.0.10]: https://github.com/kettle-rb/ast-merge/compare/v2.0.9...v2.0.10
|
|
514
|
+
[2.0.10t]: https://github.com/kettle-rb/ast-merge/releases/tag/v2.0.10
|
|
448
515
|
[2.0.9]: https://github.com/kettle-rb/ast-merge/compare/v2.0.8...v2.0.9
|
|
449
516
|
[2.0.9t]: https://github.com/kettle-rb/ast-merge/releases/tag/v2.0.9
|
|
450
517
|
[2.0.8]: https://github.com/kettle-rb/ast-merge/compare/v2.0.7...v2.0.8
|
data/README.md
CHANGED
|
@@ -55,26 +55,25 @@
|
|
|
55
55
|
|
|
56
56
|
Ast::Merge is **not typically used directly** - instead, use one of the format-specific gems built on top of it.
|
|
57
57
|
|
|
58
|
-
|
|
59
58
|
### The `*-merge` Gem Family
|
|
60
59
|
|
|
61
60
|
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.
|
|
62
61
|
|
|
63
|
-
| Gem | Format
|
|
64
|
-
|
|
65
|
-
| [tree_haver][tree_haver] | Multi
|
|
66
|
-
| [ast-merge][ast-merge] | Text
|
|
67
|
-
| [
|
|
68
|
-
| [
|
|
69
|
-
| [
|
|
70
|
-
| [
|
|
71
|
-
| [
|
|
72
|
-
| [
|
|
73
|
-
| [
|
|
74
|
-
| [
|
|
75
|
-
| [
|
|
76
|
-
| [
|
|
77
|
-
| [
|
|
62
|
+
| Gem | Language<br>/ Format | Parser Backend(s) | Description |
|
|
63
|
+
|------------------------------------------|----------------------|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
|
|
64
|
+
| [tree_haver][tree_haver] | Multi | MRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus | **Foundation**: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP) |
|
|
65
|
+
| [ast-merge][ast-merge] | Text | internal | **Infrastructure**: Shared base classes and merge logic for all `*-merge` gems |
|
|
66
|
+
| [bash-merge][bash-merge] | Bash | [tree-sitter-bash][ts-bash] (via tree_haver) | Smart merge for Bash scripts |
|
|
67
|
+
| [commonmarker-merge][commonmarker-merge] | Markdown | [Commonmarker][commonmarker] (via tree_haver) | Smart merge for Markdown (CommonMark via comrak Rust) |
|
|
68
|
+
| [dotenv-merge][dotenv-merge] | Dotenv | internal | Smart merge for `.env` files |
|
|
69
|
+
| [json-merge][json-merge] | JSON | [tree-sitter-json][ts-json] (via tree_haver) | Smart merge for JSON files |
|
|
70
|
+
| [jsonc-merge][jsonc-merge] | JSONC | [tree-sitter-jsonc][ts-jsonc] (via tree_haver) | ⚠️ Proof of concept; Smart merge for JSON with Comments |
|
|
71
|
+
| [markdown-merge][markdown-merge] | Markdown | [Commonmarker][commonmarker] / [Markly][markly] (via tree_haver) | **Foundation**: Shared base for Markdown mergers with inner code block merging |
|
|
72
|
+
| [markly-merge][markly-merge] | Markdown | [Markly][markly] (via tree_haver) | Smart merge for Markdown (CommonMark via cmark-gfm C) |
|
|
73
|
+
| [prism-merge][prism-merge] | Ruby | [Prism][prism] (`prism` std lib gem) | Smart merge for Ruby source files |
|
|
74
|
+
| [psych-merge][psych-merge] | YAML | [Psych][psych] (`psych` std lib gem) | Smart merge for YAML files |
|
|
75
|
+
| [rbs-merge][rbs-merge] | RBS | [tree-sitter-bash][ts-rbs] (via tree_haver), [RBS][rbs] (`rbs` std lib gem) | Smart merge for Ruby type signatures |
|
|
76
|
+
| [toml-merge][toml-merge] | TOML | [Citrus + toml-rb][toml-rb] (default, via tree_haver), [tree-sitter-toml][ts-toml] (via tree_haver) | Smart merge for TOML files |
|
|
78
77
|
|
|
79
78
|
**Example implementations** for the gem templating use case:
|
|
80
79
|
|
|
@@ -381,12 +380,16 @@ The maintainers of this and thousands of other packages are working with Tidelif
|
|
|
381
380
|
[](https://tidelift.com/subscription/pkg/rubygems-ast-merge?utm_source=rubygems-ast-merge&utm_medium=referral&utm_campaign=readme)
|
|
382
381
|
|
|
383
382
|
- 💡Subscribe for support guarantees covering *all* your FLOSS dependencies
|
|
383
|
+
|
|
384
384
|
- 💡Tidelift is part of [Sonar](https://blog.tidelift.com/tidelift-joins-sonar)
|
|
385
|
+
|
|
385
386
|
- 💡Tidelift pays maintainers to maintain the software you depend on\!<br/>📊`@`Pointy Haired Boss: An [enterprise support](https://tidelift.com/subscription/pkg/rubygems-ast-merge?utm_source=rubygems-ast-merge&utm_medium=referral&utm_campaign=readme) subscription is "[never gonna let you down](https://www.youtube.com/watch?v=dQw4w9WgXcQ)", and *supports* open source maintainers
|
|
386
|
-
Alternatively:
|
|
387
|
+
Alternatively:
|
|
387
388
|
|
|
388
389
|
- [](https://discord.gg/3qme4XHNKN)
|
|
390
|
+
|
|
389
391
|
- [](https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share)
|
|
392
|
+
|
|
390
393
|
- [](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github)
|
|
391
394
|
</details>
|
|
392
395
|
|
|
@@ -547,7 +550,6 @@ A freeze block consists of:
|
|
|
547
550
|
- A **start marker** comment (e.g., `# mytoken:freeze`)
|
|
548
551
|
- The protected content
|
|
549
552
|
- An **end marker** comment (e.g., `# mytoken:unfreeze`)
|
|
550
|
-
|
|
551
553
|
<!-- end list -->
|
|
552
554
|
``` ruby
|
|
553
555
|
# In a Ruby file with prism-merge:
|
|
@@ -599,7 +601,6 @@ preferences for different types of nodes (e.g., prefer template for linter confi
|
|
|
599
601
|
|
|
600
602
|
- `:default` key for the fallback preference
|
|
601
603
|
- Custom keys matching the `merge_type` values from your `node_typing`
|
|
602
|
-
|
|
603
604
|
<!-- end list -->
|
|
604
605
|
``` ruby
|
|
605
606
|
# Example: Prefer template for lint gem configs, destination for everything else
|
|
@@ -1032,7 +1033,7 @@ Thanks for RTFM. ☺️
|
|
|
1032
1033
|
[📌gitmoji]: https://gitmoji.dev
|
|
1033
1034
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1034
1035
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1035
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.
|
|
1036
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.540-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1036
1037
|
[🔐security]: SECURITY.md
|
|
1037
1038
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1038
1039
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -140,28 +140,17 @@ module Ast
|
|
|
140
140
|
|
|
141
141
|
# Extract text content from a node.
|
|
142
142
|
#
|
|
143
|
-
# Uses the custom content_extractor if provided, otherwise
|
|
144
|
-
#
|
|
143
|
+
# Uses the custom content_extractor if provided, otherwise uses the
|
|
144
|
+
# standard #text method that all TreeHaver nodes provide.
|
|
145
145
|
#
|
|
146
|
-
# @param node [Object] The node
|
|
146
|
+
# @param node [Object] The node (must conform to TreeHaver Node API)
|
|
147
147
|
# @return [String] The text content
|
|
148
148
|
def extract_content(node)
|
|
149
149
|
return @content_extractor.call(node) if @content_extractor
|
|
150
150
|
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
elsif node.respond_to?(:string_content)
|
|
155
|
-
node.string_content.to_s
|
|
156
|
-
elsif node.respond_to?(:content)
|
|
157
|
-
node.content.to_s
|
|
158
|
-
elsif node.respond_to?(:text)
|
|
159
|
-
node.text.to_s
|
|
160
|
-
elsif node.respond_to?(:to_s)
|
|
161
|
-
node.to_s
|
|
162
|
-
else
|
|
163
|
-
""
|
|
164
|
-
end
|
|
151
|
+
# TreeHaver nodes (and any node conforming to the unified API) provide #text.
|
|
152
|
+
# No conditional fallbacks - nodes must conform to the API.
|
|
153
|
+
node.text.to_s
|
|
165
154
|
end
|
|
166
155
|
|
|
167
156
|
# Compute similarity score between two nodes based on content.
|
|
@@ -224,6 +224,10 @@ module Ast
|
|
|
224
224
|
# - Type checks work (e.g., `node.is_a?(Prism::CallNode)`)
|
|
225
225
|
# - The generator sees the real AST structure
|
|
226
226
|
# - Frozen nodes match by their underlying identity
|
|
227
|
+
#
|
|
228
|
+
# NOTE: For TreeHaver-based backends, the node already has a unified API
|
|
229
|
+
# with #text, #type, #source_position methods. For other backends, they
|
|
230
|
+
# must conform to the same API (either via TreeHaver or equivalent adapter).
|
|
227
231
|
custom_result = signature_generator.call(actual_node)
|
|
228
232
|
case custom_result
|
|
229
233
|
when Array, nil
|
|
@@ -270,17 +270,9 @@ module Ast
|
|
|
270
270
|
|
|
271
271
|
# @return [String] Node text content
|
|
272
272
|
def text
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
node.to_commonmark.to_s
|
|
277
|
-
elsif node.respond_to?(:slice)
|
|
278
|
-
node.slice.to_s
|
|
279
|
-
elsif node.respond_to?(:text)
|
|
280
|
-
node.text.to_s
|
|
281
|
-
else
|
|
282
|
-
node.to_s
|
|
283
|
-
end
|
|
273
|
+
# TreeHaver nodes (and any node conforming to the unified API) provide #text.
|
|
274
|
+
# No conditional fallbacks - nodes must conform to the API.
|
|
275
|
+
node.text.to_s
|
|
284
276
|
end
|
|
285
277
|
|
|
286
278
|
# @return [Hash, nil] Source position info
|
|
@@ -139,7 +139,10 @@ module Ast
|
|
|
139
139
|
callable = find_typing_callable(typing_config, type_key, node)
|
|
140
140
|
return node unless callable
|
|
141
141
|
|
|
142
|
-
# Call the typing callable with the node
|
|
142
|
+
# Call the typing callable with the node.
|
|
143
|
+
# NOTE: For TreeHaver-based backends, the node already has a unified API
|
|
144
|
+
# with #text, #type, #source_position methods. For other backends, they
|
|
145
|
+
# must conform to the same API (either via TreeHaver or equivalent adapter).
|
|
143
146
|
callable.call(node)
|
|
144
147
|
end
|
|
145
148
|
|
|
@@ -2,36 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module Ast
|
|
4
4
|
module Merge
|
|
5
|
-
#
|
|
5
|
+
# Base class for merging a partial template into a specific section of a destination document.
|
|
6
6
|
#
|
|
7
|
-
# Unlike the full SmartMerger which merges entire documents,
|
|
7
|
+
# Unlike the full SmartMerger which merges entire documents, PartialTemplateMergerBase:
|
|
8
8
|
# 1. Finds a specific section in the destination (using InjectionPoint)
|
|
9
9
|
# 2. Replaces/merges only that section with the template
|
|
10
10
|
# 3. Leaves the rest of the destination unchanged
|
|
11
11
|
#
|
|
12
|
-
# This is
|
|
13
|
-
#
|
|
12
|
+
# This is an abstract base class. Subclasses must implement:
|
|
13
|
+
# - #create_analysis(content) - Create a FileAnalysis for the given content
|
|
14
|
+
# - #create_smart_merger(template, section) - Create a SmartMerger for the section merge
|
|
15
|
+
# - #find_section_end(statements, injection_point) - Find where the section ends
|
|
16
|
+
# - #node_to_text(node, analysis) - Convert a node to source text
|
|
14
17
|
#
|
|
15
|
-
# @
|
|
16
|
-
#
|
|
17
|
-
# template: template_content,
|
|
18
|
-
# destination: destination_content,
|
|
19
|
-
# anchor: { type: :heading, text: /Gem Family/ },
|
|
20
|
-
# parser: :markly
|
|
21
|
-
# )
|
|
22
|
-
# result = merger.merge
|
|
23
|
-
# puts result.content
|
|
18
|
+
# @abstract Subclass and implement parser-specific methods
|
|
19
|
+
# @see Markdown::Merge::PartialTemplateMerger For markdown implementation
|
|
24
20
|
#
|
|
25
|
-
|
|
26
|
-
# merger = PartialTemplateMerger.new(
|
|
27
|
-
# template: template_content,
|
|
28
|
-
# destination: destination_content,
|
|
29
|
-
# anchor: { type: :heading, text: /Installation/ },
|
|
30
|
-
# boundary: { type: :heading }, # Stop at next heading
|
|
31
|
-
# parser: :markly
|
|
32
|
-
# )
|
|
33
|
-
#
|
|
34
|
-
class PartialTemplateMerger
|
|
21
|
+
class PartialTemplateMergerBase
|
|
35
22
|
# Result of a partial template merge
|
|
36
23
|
class Result
|
|
37
24
|
# @return [String] The merged content
|
|
@@ -79,9 +66,6 @@ module Ast
|
|
|
79
66
|
# @return [Hash, nil] Boundary matcher configuration
|
|
80
67
|
attr_reader :boundary
|
|
81
68
|
|
|
82
|
-
# @return [Symbol] Parser to use (:markly, :commonmarker, etc.)
|
|
83
|
-
attr_reader :parser
|
|
84
|
-
|
|
85
69
|
# @return [Symbol, Hash] Merge preference (:template, :destination, or per-type hash)
|
|
86
70
|
attr_reader :preference
|
|
87
71
|
|
|
@@ -97,43 +81,46 @@ module Ast
|
|
|
97
81
|
# @return [Hash, nil] Node typing configuration for per-type preferences
|
|
98
82
|
attr_reader :node_typing
|
|
99
83
|
|
|
100
|
-
#
|
|
84
|
+
# @return [Object, nil] Match refiner for fuzzy matching unmatched nodes
|
|
85
|
+
attr_reader :match_refiner
|
|
86
|
+
|
|
87
|
+
# Initialize a PartialTemplateMergerBase.
|
|
101
88
|
#
|
|
102
89
|
# @param template [String] The template content (the section to merge in)
|
|
103
90
|
# @param destination [String] The destination content
|
|
104
91
|
# @param anchor [Hash] Anchor matcher: { type: :heading, text: /pattern/ }
|
|
105
92
|
# @param boundary [Hash, nil] Boundary matcher (defaults to same type as anchor)
|
|
106
|
-
# @param parser [Symbol] Parser to use (:markly, :commonmarker, :prism, :psych)
|
|
107
93
|
# @param preference [Symbol, Hash] Which content wins (:template, :destination, or per-type hash)
|
|
108
94
|
# @param add_missing [Boolean, Proc] Whether to add template nodes not in destination
|
|
109
95
|
# @param when_missing [Symbol] What to do if section not found (:skip, :append, :prepend)
|
|
110
96
|
# @param replace_mode [Boolean] If true, template replaces section entirely (no merge)
|
|
111
97
|
# @param signature_generator [Proc, nil] Custom signature generator for SmartMerger
|
|
112
98
|
# @param node_typing [Hash, nil] Node typing configuration for per-type preferences
|
|
99
|
+
# @param match_refiner [Object, nil] Match refiner for fuzzy matching (e.g., ContentMatchRefiner)
|
|
113
100
|
def initialize(
|
|
114
101
|
template:,
|
|
115
102
|
destination:,
|
|
116
103
|
anchor:,
|
|
117
104
|
boundary: nil,
|
|
118
|
-
parser: :markly,
|
|
119
105
|
preference: :template,
|
|
120
106
|
add_missing: true,
|
|
121
107
|
when_missing: :skip,
|
|
122
108
|
replace_mode: false,
|
|
123
109
|
signature_generator: nil,
|
|
124
|
-
node_typing: nil
|
|
110
|
+
node_typing: nil,
|
|
111
|
+
match_refiner: nil
|
|
125
112
|
)
|
|
126
113
|
@template = template
|
|
127
114
|
@destination = destination
|
|
128
115
|
@anchor = normalize_matcher(anchor)
|
|
129
116
|
@boundary = boundary ? normalize_matcher(boundary) : nil
|
|
130
|
-
@parser = parser
|
|
131
117
|
@preference = preference
|
|
132
118
|
@add_missing = add_missing
|
|
133
119
|
@when_missing = when_missing
|
|
134
120
|
@replace_mode = replace_mode
|
|
135
121
|
@signature_generator = signature_generator
|
|
136
122
|
@node_typing = node_typing
|
|
123
|
+
@match_refiner = match_refiner
|
|
137
124
|
end
|
|
138
125
|
|
|
139
126
|
# Perform the partial template merge.
|
|
@@ -161,6 +148,47 @@ module Ast
|
|
|
161
148
|
perform_section_merge(d_analysis, d_statements, injection_point)
|
|
162
149
|
end
|
|
163
150
|
|
|
151
|
+
protected
|
|
152
|
+
|
|
153
|
+
# Create a FileAnalysis for the given content.
|
|
154
|
+
#
|
|
155
|
+
# @abstract Subclasses must implement this method
|
|
156
|
+
# @param content [String] The content to analyze
|
|
157
|
+
# @return [Object] A FileAnalysis instance
|
|
158
|
+
def create_analysis(content)
|
|
159
|
+
raise NotImplementedError, "#{self.class} must implement #create_analysis"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Create a SmartMerger for merging the section.
|
|
163
|
+
#
|
|
164
|
+
# @abstract Subclasses must implement this method
|
|
165
|
+
# @param template_content [String] The template content
|
|
166
|
+
# @param destination_content [String] The destination section content
|
|
167
|
+
# @return [Object] A SmartMerger instance
|
|
168
|
+
def create_smart_merger(template_content, destination_content)
|
|
169
|
+
raise NotImplementedError, "#{self.class} must implement #create_smart_merger"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Find where the section ends.
|
|
173
|
+
#
|
|
174
|
+
# @abstract Subclasses must implement this method
|
|
175
|
+
# @param statements [Array<NavigableStatement>] All statements
|
|
176
|
+
# @param injection_point [InjectionPoint] The injection point
|
|
177
|
+
# @return [Integer] Index of the last statement in the section
|
|
178
|
+
def find_section_end(statements, injection_point)
|
|
179
|
+
raise NotImplementedError, "#{self.class} must implement #find_section_end"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Convert a node to its source text.
|
|
183
|
+
#
|
|
184
|
+
# @abstract Subclasses must implement this method
|
|
185
|
+
# @param node [Object] The node to convert
|
|
186
|
+
# @param analysis [Object, nil] The analysis object for source lookup
|
|
187
|
+
# @return [String] The source text
|
|
188
|
+
def node_to_text(node, analysis = nil)
|
|
189
|
+
raise NotImplementedError, "#{self.class} must implement #node_to_text"
|
|
190
|
+
end
|
|
191
|
+
|
|
164
192
|
private
|
|
165
193
|
|
|
166
194
|
def normalize_matcher(matcher)
|
|
@@ -187,17 +215,10 @@ module Ast
|
|
|
187
215
|
end
|
|
188
216
|
end
|
|
189
217
|
|
|
190
|
-
def handle_missing_section(
|
|
218
|
+
def handle_missing_section(d_analysis)
|
|
191
219
|
case when_missing
|
|
192
|
-
when :skip
|
|
193
|
-
Result.new(
|
|
194
|
-
content: destination,
|
|
195
|
-
has_section: false,
|
|
196
|
-
changed: false,
|
|
197
|
-
message: "Section not found, skipping",
|
|
198
|
-
)
|
|
199
220
|
when :append
|
|
200
|
-
# Append template
|
|
221
|
+
# Append template to end of destination
|
|
201
222
|
new_content = destination.chomp + "\n\n" + template
|
|
202
223
|
Result.new(
|
|
203
224
|
content: new_content,
|
|
@@ -206,7 +227,7 @@ module Ast
|
|
|
206
227
|
message: "Section not found, appended template",
|
|
207
228
|
)
|
|
208
229
|
when :prepend
|
|
209
|
-
# Prepend template
|
|
230
|
+
# Prepend template to beginning of destination
|
|
210
231
|
new_content = template + "\n\n" + destination
|
|
211
232
|
Result.new(
|
|
212
233
|
content: new_content,
|
|
@@ -219,12 +240,12 @@ module Ast
|
|
|
219
240
|
content: destination,
|
|
220
241
|
has_section: false,
|
|
221
242
|
changed: false,
|
|
222
|
-
message: "Section not found,
|
|
243
|
+
message: "Section not found, skipping",
|
|
223
244
|
)
|
|
224
245
|
end
|
|
225
246
|
end
|
|
226
247
|
|
|
227
|
-
def perform_section_merge(
|
|
248
|
+
def perform_section_merge(d_analysis, d_statements, injection_point)
|
|
228
249
|
# Determine section boundaries in destination
|
|
229
250
|
section_start_idx = injection_point.anchor.index
|
|
230
251
|
section_end_idx = find_section_end(d_statements, injection_point)
|
|
@@ -235,12 +256,12 @@ module Ast
|
|
|
235
256
|
after_statements = d_statements[(section_end_idx + 1)..]
|
|
236
257
|
|
|
237
258
|
# Determine the merged section content
|
|
238
|
-
section_content = statements_to_content(section_statements)
|
|
259
|
+
section_content = statements_to_content(section_statements, d_analysis)
|
|
239
260
|
merged_section, stats = merge_section_content(section_content)
|
|
240
261
|
|
|
241
|
-
# Reconstruct the document
|
|
242
|
-
before_content = statements_to_content(before_statements)
|
|
243
|
-
after_content = statements_to_content(after_statements)
|
|
262
|
+
# Reconstruct the document using source-based extraction
|
|
263
|
+
before_content = statements_to_content(before_statements, d_analysis)
|
|
264
|
+
after_content = statements_to_content(after_statements, d_analysis)
|
|
244
265
|
|
|
245
266
|
new_content = build_merged_content(before_content, merged_section, after_content)
|
|
246
267
|
|
|
@@ -280,153 +301,46 @@ module Ast
|
|
|
280
301
|
@replace_mode == true
|
|
281
302
|
end
|
|
282
303
|
|
|
283
|
-
def
|
|
284
|
-
# If boundary was specified and found, use it (exclusive - section ends before boundary)
|
|
285
|
-
if injection_point.boundary
|
|
286
|
-
return injection_point.boundary.index - 1
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# Otherwise, find the next node of same type (for headings, same or higher level)
|
|
290
|
-
anchor = injection_point.anchor
|
|
291
|
-
anchor_type = anchor.type
|
|
292
|
-
|
|
293
|
-
# For headings, find next heading of same or higher level
|
|
294
|
-
if heading_type?(anchor_type)
|
|
295
|
-
anchor_level = get_heading_level(anchor)
|
|
296
|
-
|
|
297
|
-
((anchor.index + 1)...statements.length).each do |idx|
|
|
298
|
-
stmt = statements[idx]
|
|
299
|
-
if heading_type?(stmt.type)
|
|
300
|
-
stmt_level = get_heading_level(stmt)
|
|
301
|
-
if stmt_level && anchor_level && stmt_level <= anchor_level
|
|
302
|
-
# Found next heading of same or higher level - section ends before it
|
|
303
|
-
return idx - 1
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
else
|
|
308
|
-
# For non-headings, find next node of same type
|
|
309
|
-
((anchor.index + 1)...statements.length).each do |idx|
|
|
310
|
-
stmt = statements[idx]
|
|
311
|
-
if stmt.type == anchor_type
|
|
312
|
-
return idx - 1
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
# Section extends to end of document
|
|
318
|
-
statements.length - 1
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def heading_type?(type)
|
|
322
|
-
type.to_s == "heading" || type == :heading || type == :header
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
def get_heading_level(stmt)
|
|
326
|
-
inner = stmt.respond_to?(:unwrapped_node) ? stmt.unwrapped_node : stmt.node
|
|
327
|
-
|
|
328
|
-
if inner.respond_to?(:header_level)
|
|
329
|
-
inner.header_level
|
|
330
|
-
elsif inner.respond_to?(:level)
|
|
331
|
-
inner.level
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
def statements_to_content(statements)
|
|
304
|
+
def statements_to_content(statements, analysis = nil)
|
|
336
305
|
return "" if statements.nil? || statements.empty?
|
|
337
306
|
|
|
338
307
|
statements.map do |stmt|
|
|
339
308
|
node = stmt.respond_to?(:node) ? stmt.node : stmt
|
|
340
|
-
node_to_text(node)
|
|
309
|
+
node_to_text(node, analysis)
|
|
341
310
|
end.join
|
|
342
311
|
end
|
|
343
312
|
|
|
344
|
-
def node_to_text(node)
|
|
345
|
-
# Unwrap if needed
|
|
346
|
-
inner = node
|
|
347
|
-
while inner.respond_to?(:inner_node) && inner.inner_node != inner
|
|
348
|
-
inner = inner.inner_node
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
if inner.respond_to?(:to_commonmark)
|
|
352
|
-
inner.to_commonmark.to_s
|
|
353
|
-
elsif inner.respond_to?(:to_s)
|
|
354
|
-
inner.to_s
|
|
355
|
-
else
|
|
356
|
-
""
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
|
|
360
313
|
def build_merged_content(before, section, after)
|
|
361
|
-
|
|
314
|
+
result = +""
|
|
362
315
|
|
|
363
316
|
# Before content
|
|
364
317
|
unless before.nil? || before.strip.empty?
|
|
365
|
-
|
|
318
|
+
result << before.chomp("\n")
|
|
366
319
|
end
|
|
367
320
|
|
|
368
|
-
# Merged section
|
|
321
|
+
# Merged section - ensure exactly one blank line before it if there's content before
|
|
369
322
|
unless section.nil? || section.strip.empty?
|
|
370
|
-
|
|
323
|
+
unless result.empty?
|
|
324
|
+
# Ensure exactly one blank line between before and section
|
|
325
|
+
result << "\n" unless result.end_with?("\n")
|
|
326
|
+
result << "\n" unless result.end_with?("\n\n")
|
|
327
|
+
end
|
|
328
|
+
result << section.chomp("\n")
|
|
371
329
|
end
|
|
372
330
|
|
|
373
|
-
# After content
|
|
331
|
+
# After content - ensure exactly one blank line before it if there's content before
|
|
374
332
|
unless after.nil? || after.strip.empty?
|
|
375
|
-
|
|
333
|
+
unless result.empty?
|
|
334
|
+
# Ensure exactly one blank line between section and after
|
|
335
|
+
result << "\n" unless result.end_with?("\n")
|
|
336
|
+
result << "\n" unless result.end_with?("\n\n")
|
|
337
|
+
end
|
|
338
|
+
result << after.chomp("\n")
|
|
376
339
|
end
|
|
377
340
|
|
|
378
|
-
result
|
|
379
|
-
result += "\n" unless result.end_with?("\n")
|
|
341
|
+
result << "\n" unless result.empty? || result.end_with?("\n")
|
|
380
342
|
result
|
|
381
343
|
end
|
|
382
|
-
|
|
383
|
-
def create_analysis(content)
|
|
384
|
-
case parser
|
|
385
|
-
when :markly
|
|
386
|
-
require "markly/merge" unless defined?(Markly::Merge)
|
|
387
|
-
Markly::Merge::FileAnalysis.new(content)
|
|
388
|
-
when :commonmarker
|
|
389
|
-
require "commonmarker/merge" unless defined?(Commonmarker::Merge)
|
|
390
|
-
Commonmarker::Merge::FileAnalysis.new(content)
|
|
391
|
-
when :prism
|
|
392
|
-
require "prism/merge" unless defined?(Prism::Merge)
|
|
393
|
-
Prism::Merge::FileAnalysis.new(content)
|
|
394
|
-
when :psych
|
|
395
|
-
require "psych/merge" unless defined?(Psych::Merge)
|
|
396
|
-
Psych::Merge::FileAnalysis.new(content)
|
|
397
|
-
else
|
|
398
|
-
raise ArgumentError, "Unknown parser: #{parser}"
|
|
399
|
-
end
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def create_smart_merger(template_content, destination_content)
|
|
403
|
-
merger_class = case parser
|
|
404
|
-
when :markly
|
|
405
|
-
require "markly/merge" unless defined?(Markly::Merge)
|
|
406
|
-
Markly::Merge::SmartMerger
|
|
407
|
-
when :commonmarker
|
|
408
|
-
require "commonmarker/merge" unless defined?(Commonmarker::Merge)
|
|
409
|
-
Commonmarker::Merge::SmartMerger
|
|
410
|
-
when :prism
|
|
411
|
-
require "prism/merge" unless defined?(Prism::Merge)
|
|
412
|
-
Prism::Merge::SmartMerger
|
|
413
|
-
when :psych
|
|
414
|
-
require "psych/merge" unless defined?(Psych::Merge)
|
|
415
|
-
Psych::Merge::SmartMerger
|
|
416
|
-
else
|
|
417
|
-
raise ArgumentError, "Unknown parser: #{parser}"
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
# Build options hash, only including non-nil values
|
|
421
|
-
options = {
|
|
422
|
-
preference: preference,
|
|
423
|
-
add_template_only_nodes: add_missing,
|
|
424
|
-
}
|
|
425
|
-
options[:signature_generator] = signature_generator if signature_generator
|
|
426
|
-
options[:node_typing] = node_typing if node_typing
|
|
427
|
-
|
|
428
|
-
merger_class.new(template_content, destination_content, **options)
|
|
429
|
-
end
|
|
430
344
|
end
|
|
431
345
|
end
|
|
432
346
|
end
|
|
@@ -124,6 +124,17 @@ module Ast
|
|
|
124
124
|
script_loader.load_callable_hash(value)
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
+
# Get the match_refiner callable, loading from script if needed.
|
|
128
|
+
#
|
|
129
|
+
# @return [Object, nil] Match refiner instance or callable
|
|
130
|
+
def match_refiner
|
|
131
|
+
value = merge_config[:match_refiner]
|
|
132
|
+
return if value.nil?
|
|
133
|
+
return value if value.respond_to?(:call) || value.is_a?(Ast::Merge::MatchRefinerBase)
|
|
134
|
+
|
|
135
|
+
script_loader.load_callable(value)
|
|
136
|
+
end
|
|
137
|
+
|
|
127
138
|
# Convert preset to a hash suitable for SmartMerger options.
|
|
128
139
|
#
|
|
129
140
|
# @return [Hash]
|
|
@@ -133,6 +144,7 @@ module Ast
|
|
|
133
144
|
add_template_only_nodes: add_missing,
|
|
134
145
|
signature_generator: signature_generator,
|
|
135
146
|
node_typing: node_typing,
|
|
147
|
+
match_refiner: match_refiner,
|
|
136
148
|
freeze_token: freeze_token,
|
|
137
149
|
}.compact
|
|
138
150
|
end
|
|
@@ -155,6 +167,7 @@ module Ast
|
|
|
155
167
|
deep: config["deep"] == true,
|
|
156
168
|
signature_generator: config["signature_generator"],
|
|
157
169
|
node_typing: config["node_typing"],
|
|
170
|
+
match_refiner: config["match_refiner"],
|
|
158
171
|
}
|
|
159
172
|
end
|
|
160
173
|
|
|
@@ -144,19 +144,19 @@ module Ast
|
|
|
144
144
|
begin
|
|
145
145
|
destination_content = File.read(target_path)
|
|
146
146
|
|
|
147
|
-
# Use
|
|
148
|
-
merger =
|
|
147
|
+
# Use the appropriate PartialTemplateMerger based on parser
|
|
148
|
+
merger = create_partial_template_merger(
|
|
149
149
|
template: template_content,
|
|
150
150
|
destination: destination_content,
|
|
151
151
|
anchor: recipe.injection[:anchor] || {},
|
|
152
152
|
boundary: recipe.injection[:boundary],
|
|
153
|
-
parser: parser,
|
|
154
153
|
preference: recipe.preference,
|
|
155
154
|
add_missing: recipe.add_missing,
|
|
156
155
|
when_missing: recipe.when_missing,
|
|
157
156
|
replace_mode: recipe.replace_mode?,
|
|
158
157
|
signature_generator: recipe.signature_generator,
|
|
159
158
|
node_typing: recipe.node_typing,
|
|
159
|
+
match_refiner: recipe.match_refiner,
|
|
160
160
|
)
|
|
161
161
|
|
|
162
162
|
result = merger.merge
|
|
@@ -179,6 +179,26 @@ module Ast
|
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
+
# Create the appropriate PartialTemplateMerger based on parser type.
|
|
183
|
+
#
|
|
184
|
+
# @param options [Hash] Merger options
|
|
185
|
+
# @return [Object] A PartialTemplateMerger instance
|
|
186
|
+
def create_partial_template_merger(**options)
|
|
187
|
+
case parser.to_sym
|
|
188
|
+
when :markly, :commonmarker
|
|
189
|
+
require "markdown/merge" unless defined?(Markdown::Merge)
|
|
190
|
+
Markdown::Merge::PartialTemplateMerger.new(backend: parser, **options)
|
|
191
|
+
when :prism
|
|
192
|
+
require "prism/merge" unless defined?(Prism::Merge)
|
|
193
|
+
raise NotImplementedError, "Prism PartialTemplateMerger not yet implemented"
|
|
194
|
+
when :psych
|
|
195
|
+
require "psych/merge" unless defined?(Psych::Merge)
|
|
196
|
+
raise NotImplementedError, "Psych PartialTemplateMerger not yet implemented"
|
|
197
|
+
else
|
|
198
|
+
raise ArgumentError, "Unknown parser: #{parser}. Supported: :markly, :commonmarker"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
182
202
|
def create_result_from_merge(target_path, relative_path, _destination_content, merge_result)
|
|
183
203
|
changed = merge_result.changed
|
|
184
204
|
|
|
@@ -49,6 +49,9 @@
|
|
|
49
49
|
# [:psych_merge]
|
|
50
50
|
# psych-merge gem is available and functional.
|
|
51
51
|
#
|
|
52
|
+
# [:rbs_merge]
|
|
53
|
+
# rbs-merge gem is available and functional.
|
|
54
|
+
#
|
|
52
55
|
# [:any_markdown_merge]
|
|
53
56
|
# At least one markdown merge gem (markly-merge or commonmarker-merge) is available.
|
|
54
57
|
#
|
|
@@ -57,7 +60,7 @@
|
|
|
57
60
|
# All positive tags have negated versions prefixed with `not_`:
|
|
58
61
|
# - :not_markly_merge, :not_commonmarker_merge, :not_markdown_merge
|
|
59
62
|
# - :not_prism_merge, :not_json_merge, :not_jsonc_merge
|
|
60
|
-
# - :not_toml_merge, :not_bash_merge, :not_psych_merge
|
|
63
|
+
# - :not_toml_merge, :not_bash_merge, :not_psych_merge, :not_rbs_merge
|
|
61
64
|
# - :not_any_markdown_merge
|
|
62
65
|
|
|
63
66
|
module Ast
|
|
@@ -142,6 +145,14 @@ module Ast
|
|
|
142
145
|
return @psych_merge_available if defined?(@psych_merge_available)
|
|
143
146
|
@psych_merge_available = merge_gem_works?("psych/merge", "Psych::Merge::SmartMerger", "key: value")
|
|
144
147
|
end
|
|
148
|
+
|
|
149
|
+
# Check if rbs-merge is available and functional
|
|
150
|
+
#
|
|
151
|
+
# @return [Boolean] true if rbs-merge works
|
|
152
|
+
def rbs_merge_available?
|
|
153
|
+
return @rbs_merge_available if defined?(@rbs_merge_available)
|
|
154
|
+
@rbs_merge_available = merge_gem_works?("rbs/merge", "Rbs::Merge::SmartMerger", "class Foo end")
|
|
155
|
+
end
|
|
145
156
|
# rubocop:enable ThreadSafety/ClassInstanceVariable
|
|
146
157
|
|
|
147
158
|
# Check if at least one markdown merge gem is available
|
|
@@ -169,6 +180,7 @@ module Ast
|
|
|
169
180
|
toml_merge: toml_merge_available?,
|
|
170
181
|
bash_merge: bash_merge_available?,
|
|
171
182
|
psych_merge: psych_merge_available?,
|
|
183
|
+
rbs_merge: rbs_merge_available?,
|
|
172
184
|
any_markdown_merge: any_markdown_merge_available?,
|
|
173
185
|
}
|
|
174
186
|
end
|
|
@@ -233,6 +245,7 @@ RSpec.configure do |config|
|
|
|
233
245
|
config.filter_run_excluding(toml_merge: true) unless deps.toml_merge_available?
|
|
234
246
|
config.filter_run_excluding(bash_merge: true) unless deps.bash_merge_available?
|
|
235
247
|
config.filter_run_excluding(psych_merge: true) unless deps.psych_merge_available?
|
|
248
|
+
config.filter_run_excluding(rbs_merge: true) unless deps.rbs_merge_available?
|
|
236
249
|
config.filter_run_excluding(any_markdown_merge: true) unless deps.any_markdown_merge_available?
|
|
237
250
|
|
|
238
251
|
# ============================================================
|
|
@@ -248,5 +261,6 @@ RSpec.configure do |config|
|
|
|
248
261
|
config.filter_run_excluding(not_toml_merge: true) if deps.toml_merge_available?
|
|
249
262
|
config.filter_run_excluding(not_bash_merge: true) if deps.bash_merge_available?
|
|
250
263
|
config.filter_run_excluding(not_psych_merge: true) if deps.psych_merge_available?
|
|
264
|
+
config.filter_run_excluding(not_rbs_merge: true) if deps.rbs_merge_available?
|
|
251
265
|
config.filter_run_excluding(not_any_markdown_merge: true) if deps.any_markdown_merge_available?
|
|
252
266
|
end
|
data/lib/ast/merge/version.rb
CHANGED
data/lib/ast/merge.rb
CHANGED
|
@@ -154,7 +154,7 @@ module Ast
|
|
|
154
154
|
autoload :NavigableStatement, "ast/merge/navigable_statement"
|
|
155
155
|
autoload :NodeTyping, "ast/merge/node_typing"
|
|
156
156
|
autoload :NodeWrapperBase, "ast/merge/node_wrapper_base"
|
|
157
|
-
autoload :
|
|
157
|
+
autoload :PartialTemplateMergerBase, "ast/merge/partial_template_merger_base"
|
|
158
158
|
autoload :SectionTyping, "ast/merge/section_typing"
|
|
159
159
|
autoload :SmartMergerBase, "ast/merge/smart_merger_base"
|
|
160
160
|
autoload :Text, "ast/merge/text"
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ast-merge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -66,7 +66,7 @@ dependencies:
|
|
|
66
66
|
version: '3.2'
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: 3.2.
|
|
69
|
+
version: 3.2.5
|
|
70
70
|
type: :runtime
|
|
71
71
|
prerelease: false
|
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -76,7 +76,7 @@ dependencies:
|
|
|
76
76
|
version: '3.2'
|
|
77
77
|
- - ">="
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
|
-
version: 3.2.
|
|
79
|
+
version: 3.2.5
|
|
80
80
|
- !ruby/object:Gem::Dependency
|
|
81
81
|
name: kettle-dev
|
|
82
82
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -322,7 +322,7 @@ files:
|
|
|
322
322
|
- lib/ast/merge/node_typing/normalizer.rb
|
|
323
323
|
- lib/ast/merge/node_typing/wrapper.rb
|
|
324
324
|
- lib/ast/merge/node_wrapper_base.rb
|
|
325
|
-
- lib/ast/merge/
|
|
325
|
+
- lib/ast/merge/partial_template_merger_base.rb
|
|
326
326
|
- lib/ast/merge/recipe.rb
|
|
327
327
|
- lib/ast/merge/recipe/config.rb
|
|
328
328
|
- lib/ast/merge/recipe/preset.rb
|
|
@@ -356,10 +356,10 @@ licenses:
|
|
|
356
356
|
- MIT
|
|
357
357
|
metadata:
|
|
358
358
|
homepage_uri: https://ast-merge.galtzo.com/
|
|
359
|
-
source_code_uri: https://github.com/kettle-rb/ast-merge/tree/
|
|
360
|
-
changelog_uri: https://github.com/kettle-rb/ast-merge/blob/
|
|
359
|
+
source_code_uri: https://github.com/kettle-rb/ast-merge/tree/v3.0.0
|
|
360
|
+
changelog_uri: https://github.com/kettle-rb/ast-merge/blob/v3.0.0/CHANGELOG.md
|
|
361
361
|
bug_tracker_uri: https://github.com/kettle-rb/ast-merge/issues
|
|
362
|
-
documentation_uri: https://www.rubydoc.info/gems/ast-merge/
|
|
362
|
+
documentation_uri: https://www.rubydoc.info/gems/ast-merge/3.0.0
|
|
363
363
|
funding_uri: https://github.com/sponsors/pboling
|
|
364
364
|
wiki_uri: https://github.com/kettle-rb/ast-merge/wiki
|
|
365
365
|
news_uri: https://www.railsbling.com/tags/ast-merge
|
metadata.gz.sig
CHANGED
|
Binary file
|