ast-merge 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +29 -1
- data/README.md +32 -9
- data/lib/ast/merge/emitter_base.rb +123 -0
- data/lib/ast/merge/freeze_node_base.rb +9 -0
- data/lib/ast/merge/node_typing.rb +21 -0
- data/lib/ast/merge/version.rb +1 -1
- data/lib/ast/merge.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +9 -8
- 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: 4ce2b23f374b6f178709f84a66ccbed7b997a05d170d7b1f63ede714fc844469
|
|
4
|
+
data.tar.gz: d0ae1bc694c669108b5ddaac372424c02f8528f3c59642ced6fb7bb1af67dcf4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41981f4b37122e224ecc1b3047156ec15871742fbca09b8203acb91c27df7acf61164871c2ee7eac07db32a12acccc7c8dda3b83b8cd7dafd8f86ade604a82a6
|
|
7
|
+
data.tar.gz: 29c07de568c15bc8a78b6363c438e6c7902af5f24d8d441aaa9f9417bceeedb62107f40e95a226864be3492b16373ae4493efa0ed956c780448bf4a3168980b9
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,32 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [3.1.0] - 2026-01-08
|
|
34
|
+
|
|
35
|
+
- TAG: [v3.1.0][3.1.0t]
|
|
36
|
+
- COVERAGE: 96.89% -- 2465/2544 lines in 47 files
|
|
37
|
+
- BRANCH COVERAGE: 89.62% -- 794/886 branches in 47 files
|
|
38
|
+
- 98.75% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `Ast::Merge::EmitterBase` - Abstract base class for format-specific emitters
|
|
43
|
+
- Provides common infrastructure for converting AST structures back to text
|
|
44
|
+
- Tracks indentation level with configurable `indent_size` (default: 2 spaces)
|
|
45
|
+
- Manages output lines collection with `#lines` accessor
|
|
46
|
+
- `#emit_blank_line` - Emit an empty line
|
|
47
|
+
- `#emit_leading_comments` - Emit comments from CommentTracker
|
|
48
|
+
- `#emit_raw_lines` - Emit lines without modification (preserves exact formatting)
|
|
49
|
+
- `#to_s` - Get output as a single string with trailing newline
|
|
50
|
+
- `#clear` - Reset emitter state
|
|
51
|
+
- `#indent` / `#dedent` - Increase/decrease indentation level
|
|
52
|
+
- Subclass hooks: `#initialize_subclass_state`, `#clear_subclass_state`, `#emit_tracked_comment`
|
|
53
|
+
- Used by jsonc-merge, json-merge, bash-merge, toml-merge, and psych-merge emitters
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- tree_haver v4.0.0
|
|
58
|
+
|
|
33
59
|
## [3.0.0] - 2026-01-05
|
|
34
60
|
|
|
35
61
|
- TAG: [v3.0.0][3.0.0t]
|
|
@@ -507,7 +533,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
507
533
|
|
|
508
534
|
- Initial release
|
|
509
535
|
|
|
510
|
-
[Unreleased]: https://github.com/kettle-rb/ast-merge/compare/v3.
|
|
536
|
+
[Unreleased]: https://github.com/kettle-rb/ast-merge/compare/v3.1.0...HEAD
|
|
537
|
+
[3.1.0]: https://github.com/kettle-rb/ast-merge/compare/v3.0.0...v3.1.0
|
|
538
|
+
[3.1.0t]: https://github.com/kettle-rb/ast-merge/releases/tag/v3.1.0
|
|
511
539
|
[3.0.0]: https://github.com/kettle-rb/ast-merge/compare/v2.0.10...v3.0.0
|
|
512
540
|
[3.0.0t]: https://github.com/kettle-rb/ast-merge/releases/tag/v3.0.0
|
|
513
541
|
[2.0.10]: https://github.com/kettle-rb/ast-merge/compare/v2.0.9...v2.0.10
|
data/README.md
CHANGED
|
@@ -75,12 +75,36 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
|
|
|
75
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
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 |
|
|
77
77
|
|
|
78
|
+
#### Backend Platform Compatibility
|
|
79
|
+
|
|
80
|
+
tree_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:
|
|
81
|
+
|
|
82
|
+
| Platform 👉️<br> TreeHaver Backend 👇️ | MRI | JRuby | TruffleRuby | Notes |
|
|
83
|
+
|------------------------------------------------|:---:|:-----:|:-----------:|-----------------------------------------------------|
|
|
84
|
+
| **MRI** ([ruby_tree_sitter][ruby_tree_sitter]) | ✅ | ❌ | ❌ | C extension, MRI only |
|
|
85
|
+
| **Rust** ([tree_stump][tree_stump]) | ✅ | ❌ | ❌ | Rust extension via magnus/rb-sys, MRI only |
|
|
86
|
+
| **FFI** | ✅ | ✅ | ❌ | TruffleRuby's FFI doesn't support `STRUCT_BY_VALUE` |
|
|
87
|
+
| **Java** ([jtreesitter][jtreesitter]) | ❌ | ✅ | ❌ | JRuby only, requires grammar JARs |
|
|
88
|
+
| **Prism** | ✅ | ✅ | ✅ | Ruby parsing, stdlib in Ruby 3.4+ |
|
|
89
|
+
| **Psych** | ✅ | ✅ | ✅ | YAML parsing, stdlib |
|
|
90
|
+
| **Citrus** | ✅ | ✅ | ✅ | Pure Ruby, no native dependencies |
|
|
91
|
+
| **Commonmarker** | ✅ | ❌ | ❓ | Rust extension for Markdown |
|
|
92
|
+
| **Markly** | ✅ | ❌ | ❓ | C extension for Markdown |
|
|
93
|
+
|
|
94
|
+
**Legend**: ✅ = Works, ❌ = Does not work, ❓ = Untested
|
|
95
|
+
|
|
96
|
+
**Why some backends don't work on certain platforms**:
|
|
97
|
+
|
|
98
|
+
- **JRuby**: Runs on the JVM; cannot load native C/Rust extensions (`.so` files)
|
|
99
|
+
- **TruffleRuby**: Has C API emulation via Sulong/LLVM, but it doesn't expose all MRI internals that native extensions require (e.g., `RBasic.flags`, `rb_gc_writebarrier`)
|
|
100
|
+
- **FFI on TruffleRuby**: TruffleRuby's FFI implementation doesn't support returning structs by value, which tree-sitter's C API requires
|
|
101
|
+
|
|
78
102
|
**Example implementations** for the gem templating use case:
|
|
79
103
|
|
|
80
|
-
| Gem
|
|
81
|
-
|
|
82
|
-
| [kettle-dev]
|
|
83
|
-
| [kettle-jem]
|
|
104
|
+
| Gem | Purpose | Description |
|
|
105
|
+
|--------------------------|-----------------|-----------------------------------------------|
|
|
106
|
+
| [kettle-dev][kettle-dev] | Gem Development | Gem templating tool using `*-merge` gems |
|
|
107
|
+
| [kettle-jem][kettle-jem] | Gem Templating | Gem template library with smart merge support |
|
|
84
108
|
|
|
85
109
|
[tree_haver]: https://github.com/kettle-rb/tree_haver
|
|
86
110
|
[ast-merge]: https://github.com/kettle-rb/ast-merge
|
|
@@ -100,8 +124,11 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
|
|
|
100
124
|
[prism]: https://github.com/ruby/prism
|
|
101
125
|
[psych]: https://github.com/ruby/psych
|
|
102
126
|
[ts-json]: https://github.com/tree-sitter/tree-sitter-json
|
|
127
|
+
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
|
|
103
128
|
[ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
|
|
129
|
+
[ts-rbs]: https://github.com/joker1007/tree-sitter-rbs
|
|
104
130
|
[ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
|
|
131
|
+
[dotenv]: https://github.com/bkeepers/dotenv
|
|
105
132
|
[rbs]: https://github.com/ruby/rbs
|
|
106
133
|
[toml-rb]: https://github.com/emancu/toml-rb
|
|
107
134
|
[markly]: https://github.com/ioquatix/markly
|
|
@@ -110,10 +137,6 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
|
|
|
110
137
|
[tree_stump]: https://github.com/joker1007/tree_stump
|
|
111
138
|
[jtreesitter]: https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
|
|
112
139
|
|
|
113
|
-
|
|
114
|
-
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
|
|
115
|
-
[dotenv]: https://github.com/bkeepers/dotenv
|
|
116
|
-
|
|
117
140
|
#### Backend Platform Compatibility
|
|
118
141
|
|
|
119
142
|
tree\_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:
|
|
@@ -1033,7 +1056,7 @@ Thanks for RTFM. ☺️
|
|
|
1033
1056
|
[📌gitmoji]: https://gitmoji.dev
|
|
1034
1057
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1035
1058
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1036
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.
|
|
1059
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-2.544-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1037
1060
|
[🔐security]: SECURITY.md
|
|
1038
1061
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1039
1062
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ast
|
|
4
|
+
module Merge
|
|
5
|
+
# Base class for emitters that convert AST structures back to text.
|
|
6
|
+
# Provides common functionality for tracking indentation, managing output lines,
|
|
7
|
+
# and handling comments.
|
|
8
|
+
#
|
|
9
|
+
# Subclasses implement format-specific emission methods (e.g., emit_pair for JSON,
|
|
10
|
+
# emit_variable_assignment for Bash, etc.)
|
|
11
|
+
#
|
|
12
|
+
# @example Implementing a custom emitter
|
|
13
|
+
# class MyEmitter < Ast::Merge::EmitterBase
|
|
14
|
+
# def emit_my_construct(data)
|
|
15
|
+
# add_comma_if_needed if @needs_separator
|
|
16
|
+
# @lines << "#{current_indent}my_syntax: #{data}"
|
|
17
|
+
# @needs_separator = true
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
class EmitterBase
|
|
21
|
+
# @return [Array<String>] Output lines
|
|
22
|
+
attr_reader :lines
|
|
23
|
+
|
|
24
|
+
# @return [Integer] Current indentation level
|
|
25
|
+
attr_reader :indent_level
|
|
26
|
+
|
|
27
|
+
# @return [Integer] Spaces per indent level
|
|
28
|
+
attr_reader :indent_size
|
|
29
|
+
|
|
30
|
+
# Initialize a new emitter
|
|
31
|
+
#
|
|
32
|
+
# @param indent_size [Integer] Number of spaces per indent level
|
|
33
|
+
# @param options [Hash] Additional options for subclasses
|
|
34
|
+
def initialize(indent_size: 2, **options)
|
|
35
|
+
@lines = []
|
|
36
|
+
@indent_level = 0
|
|
37
|
+
@indent_size = indent_size
|
|
38
|
+
initialize_subclass_state(**options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Hook for subclasses to initialize their own state
|
|
42
|
+
# @param options [Hash] Additional options
|
|
43
|
+
def initialize_subclass_state(**options)
|
|
44
|
+
# Override in subclasses if needed
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Emit a blank line
|
|
48
|
+
def emit_blank_line
|
|
49
|
+
@lines << ""
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Emit leading comments from CommentTracker
|
|
53
|
+
#
|
|
54
|
+
# @param comments [Array<Hash>] Comment hashes with :text, :indent, etc.
|
|
55
|
+
def emit_leading_comments(comments)
|
|
56
|
+
comments.each do |comment|
|
|
57
|
+
emit_tracked_comment(comment)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Emit a comment from CommentTracker hash
|
|
62
|
+
# Subclasses should override this to handle format-specific comment syntax
|
|
63
|
+
#
|
|
64
|
+
# @param comment [Hash] Comment hash with :text, :indent, :block, etc.
|
|
65
|
+
def emit_tracked_comment(comment)
|
|
66
|
+
raise NotImplementedError, "Subclasses must implement emit_tracked_comment"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Emit raw lines as-is (for preserving exact formatting)
|
|
70
|
+
#
|
|
71
|
+
# @param raw_lines [Array<String>] Lines to emit without modification
|
|
72
|
+
def emit_raw_lines(raw_lines)
|
|
73
|
+
raw_lines.each { |line| @lines << line.chomp }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get the output as a single string
|
|
77
|
+
# Subclasses may override to customize output format (e.g., to_json, to_yaml)
|
|
78
|
+
#
|
|
79
|
+
# @return [String]
|
|
80
|
+
def to_s
|
|
81
|
+
content = @lines.join("\n")
|
|
82
|
+
content += "\n" unless content.empty? || content.end_with?("\n")
|
|
83
|
+
content
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Clear the emitter state
|
|
87
|
+
def clear
|
|
88
|
+
@lines = []
|
|
89
|
+
@indent_level = 0
|
|
90
|
+
clear_subclass_state
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Hook for subclasses to clear their own state
|
|
94
|
+
def clear_subclass_state
|
|
95
|
+
# Override in subclasses if needed
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Increase indentation level
|
|
99
|
+
def indent
|
|
100
|
+
@indent_level += 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Decrease indentation level
|
|
104
|
+
def dedent
|
|
105
|
+
@indent_level -= 1 if @indent_level > 0
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
protected
|
|
109
|
+
|
|
110
|
+
# Get the current indentation string
|
|
111
|
+
# @return [String]
|
|
112
|
+
def current_indent
|
|
113
|
+
" " * (@indent_level * @indent_size)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Add a line with current indentation
|
|
117
|
+
# @param content [String] Line content
|
|
118
|
+
def add_indented_line(content)
|
|
119
|
+
@lines << "#{current_indent}#{content}"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -354,6 +354,15 @@ module Ast
|
|
|
354
354
|
true
|
|
355
355
|
end
|
|
356
356
|
|
|
357
|
+
# Node type for merge classification
|
|
358
|
+
# @return [Symbol] :freeze_block
|
|
359
|
+
def merge_type
|
|
360
|
+
:freeze_block
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Alias for compatibility
|
|
364
|
+
alias_method :type, :merge_type
|
|
365
|
+
|
|
357
366
|
# Returns a stable signature for this freeze block.
|
|
358
367
|
# Override in subclasses for file-type-specific normalization.
|
|
359
368
|
# @return [Array] Signature array
|
|
@@ -13,6 +13,27 @@ module Ast
|
|
|
13
13
|
# The `merge_type` attribute can then be used by other merge tools like
|
|
14
14
|
# `signature_generator`, `match_refiner`, and per-node-type `preference` settings.
|
|
15
15
|
#
|
|
16
|
+
# ## Important: Two Uses of merge_type
|
|
17
|
+
#
|
|
18
|
+
# The `merge_type` method serves two complementary purposes in the codebase:
|
|
19
|
+
#
|
|
20
|
+
# ### 1. NodeTyping-specific (gated by typed_node?)
|
|
21
|
+
# Wrapped nodes (Wrapper/FrozenWrapper) with custom type tagging for:
|
|
22
|
+
# - Per-node-type preferences (e.g., `:lint_gem` → `:template`)
|
|
23
|
+
# - Match refinement based on custom categories
|
|
24
|
+
# - Only applies when `typed_node?` returns true
|
|
25
|
+
# - Accessed via `NodeTyping.merge_type_for(node)`
|
|
26
|
+
#
|
|
27
|
+
# ### 2. General node classification (any node)
|
|
28
|
+
# Any node can implement `merge_type` for category identification:
|
|
29
|
+
# - FreezeNodeBase has `merge_type` → `:freeze_block`
|
|
30
|
+
# - GapLineNode has `merge_type` → `:gap_line`
|
|
31
|
+
# - Used by systems like MarkdownStructure for structural spacing rules
|
|
32
|
+
# - These nodes are NOT "typed nodes" (typed_node? returns false)
|
|
33
|
+
#
|
|
34
|
+
# The key distinction: **typed_node? is the gate** for NodeTyping wrapper
|
|
35
|
+
# semantics. A node can have `merge_type` without being a NodeTyping wrapper.
|
|
36
|
+
#
|
|
16
37
|
# @example Basic node typing for different gem types
|
|
17
38
|
# node_typing = {
|
|
18
39
|
# CallNode: ->(node) {
|
data/lib/ast/merge/version.rb
CHANGED
data/lib/ast/merge.rb
CHANGED
|
@@ -142,6 +142,7 @@ module Ast
|
|
|
142
142
|
autoload :ConflictResolverBase, "ast/merge/conflict_resolver_base"
|
|
143
143
|
autoload :ContentMatchRefiner, "ast/merge/content_match_refiner"
|
|
144
144
|
autoload :DebugLogger, "ast/merge/debug_logger"
|
|
145
|
+
autoload :EmitterBase, "ast/merge/emitter_base"
|
|
145
146
|
autoload :FileAnalyzable, "ast/merge/file_analyzable"
|
|
146
147
|
autoload :Freezable, "ast/merge/freezable"
|
|
147
148
|
autoload :FreezeNodeBase, "ast/merge/freeze_node_base"
|
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: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -63,20 +63,20 @@ dependencies:
|
|
|
63
63
|
requirements:
|
|
64
64
|
- - "~>"
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '
|
|
66
|
+
version: '4.0'
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version:
|
|
69
|
+
version: 4.0.0
|
|
70
70
|
type: :runtime
|
|
71
71
|
prerelease: false
|
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
73
|
requirements:
|
|
74
74
|
- - "~>"
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
76
|
+
version: '4.0'
|
|
77
77
|
- - ">="
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
|
-
version:
|
|
79
|
+
version: 4.0.0
|
|
80
80
|
- !ruby/object:Gem::Dependency
|
|
81
81
|
name: kettle-dev
|
|
82
82
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -309,6 +309,7 @@ files:
|
|
|
309
309
|
- lib/ast/merge/detector/mergeable.rb
|
|
310
310
|
- lib/ast/merge/detector/toml_frontmatter.rb
|
|
311
311
|
- lib/ast/merge/detector/yaml_frontmatter.rb
|
|
312
|
+
- lib/ast/merge/emitter_base.rb
|
|
312
313
|
- lib/ast/merge/file_analyzable.rb
|
|
313
314
|
- lib/ast/merge/freezable.rb
|
|
314
315
|
- lib/ast/merge/freeze_node_base.rb
|
|
@@ -356,10 +357,10 @@ licenses:
|
|
|
356
357
|
- MIT
|
|
357
358
|
metadata:
|
|
358
359
|
homepage_uri: https://ast-merge.galtzo.com/
|
|
359
|
-
source_code_uri: https://github.com/kettle-rb/ast-merge/tree/v3.
|
|
360
|
-
changelog_uri: https://github.com/kettle-rb/ast-merge/blob/v3.
|
|
360
|
+
source_code_uri: https://github.com/kettle-rb/ast-merge/tree/v3.1.0
|
|
361
|
+
changelog_uri: https://github.com/kettle-rb/ast-merge/blob/v3.1.0/CHANGELOG.md
|
|
361
362
|
bug_tracker_uri: https://github.com/kettle-rb/ast-merge/issues
|
|
362
|
-
documentation_uri: https://www.rubydoc.info/gems/ast-merge/3.
|
|
363
|
+
documentation_uri: https://www.rubydoc.info/gems/ast-merge/3.1.0
|
|
363
364
|
funding_uri: https://github.com/sponsors/pboling
|
|
364
365
|
wiki_uri: https://github.com/kettle-rb/ast-merge/wiki
|
|
365
366
|
news_uri: https://www.railsbling.com/tags/ast-merge
|
metadata.gz.sig
CHANGED
|
Binary file
|