markdown-merge 7.0.0 → 7.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +673 -0
- data/lib/markdown/merge/backend_support.rb +200 -0
- data/lib/markdown/merge/cleanse/block_spacing.rb +248 -0
- data/lib/markdown/merge/cleanse/code_fence_spacing.rb +294 -0
- data/lib/markdown/merge/cleanse/condensed_link_refs.rb +411 -0
- data/lib/markdown/merge/cleanse/list_marker_duplication.rb +66 -0
- data/lib/markdown/merge/cleanse/templating_corruption.rb +86 -0
- data/lib/markdown/merge/cleanse.rb +44 -0
- data/lib/markdown/merge/code_block_match_refiner.rb +111 -0
- data/lib/markdown/merge/code_block_merger.rb +742 -0
- data/lib/markdown/merge/comment_tracker.rb +42 -0
- data/lib/markdown/merge/conflict_resolver.rb +199 -0
- data/lib/markdown/merge/debug_logger.rb +26 -0
- data/lib/markdown/merge/document_problems.rb +190 -0
- data/lib/markdown/merge/file_aligner.rb +496 -0
- data/lib/markdown/merge/file_analysis.rb +689 -0
- data/lib/markdown/merge/file_analysis_base.rb +766 -0
- data/lib/markdown/merge/freeze_node.rb +93 -0
- data/lib/markdown/merge/gap_line_node.rb +142 -0
- data/lib/markdown/merge/link_definition_formatter.rb +49 -0
- data/lib/markdown/merge/link_definition_node.rb +157 -0
- data/lib/markdown/merge/link_parser.rb +421 -0
- data/lib/markdown/merge/link_reference_rehydrator.rb +320 -0
- data/lib/markdown/merge/list_match_refiner.rb +98 -0
- data/lib/markdown/merge/list_merger.rb +322 -0
- data/lib/markdown/merge/markdown_structure.rb +123 -0
- data/lib/markdown/merge/merge_result.rb +483 -0
- data/lib/markdown/merge/node_type_normalizer.rb +126 -0
- data/lib/markdown/merge/output_builder.rb +248 -0
- data/lib/markdown/merge/partial_template_merger.rb +555 -0
- data/lib/markdown/merge/preservation_support.rb +291 -0
- data/lib/markdown/merge/rspec/shared_examples/source_preserving_provider.rb +338 -0
- data/lib/markdown/merge/smart_merger.rb +269 -0
- data/lib/markdown/merge/smart_merger_base.rb +1490 -0
- data/lib/markdown/merge/source_preserving_provider.rb +814 -0
- data/lib/markdown/merge/table_match_algorithm.rb +499 -0
- data/lib/markdown/merge/table_match_refiner.rb +132 -0
- data/lib/markdown/merge/version.rb +5 -3
- data/lib/markdown/merge/whitespace_normalizer.rb +243 -0
- data/lib/markdown/merge/wrapper_support.rb +194 -0
- data/lib/markdown/merge.rb +271 -87
- data/lib/markdown-merge.rb +7 -1
- data/sig/markdown/merge.rbs +62 -0
- data.tar.gz.sig +0 -0
- metadata +289 -15
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markdown
|
|
4
|
+
module Merge
|
|
5
|
+
# Normalizes whitespace in markdown documents.
|
|
6
|
+
#
|
|
7
|
+
# Supports multiple normalization modes:
|
|
8
|
+
# - `:basic` (or `true`) - Collapse excessive blank lines (3+ → 2)
|
|
9
|
+
# - `:link_refs` - Also remove blank lines between consecutive link reference definitions
|
|
10
|
+
# - `:strict` - All of the above normalizations
|
|
11
|
+
#
|
|
12
|
+
# Uses {LinkParser} for detecting link reference definitions, which supports:
|
|
13
|
+
# - Standard definitions: `[label]: url`
|
|
14
|
+
# - Definitions with titles: `[label]: url "title"`
|
|
15
|
+
# - Angle-bracketed URLs: `[label]: <url>`
|
|
16
|
+
# - Emoji in labels: `[🎨logo]: url`
|
|
17
|
+
#
|
|
18
|
+
# @example Basic normalization (default)
|
|
19
|
+
# content = "Hello\n\n\n\nWorld"
|
|
20
|
+
# normalized = WhitespaceNormalizer.normalize(content)
|
|
21
|
+
# # => "Hello\n\nWorld"
|
|
22
|
+
#
|
|
23
|
+
# @example With link_refs mode
|
|
24
|
+
# content = "[link1]: url1\n\n[link2]: url2"
|
|
25
|
+
# normalized = WhitespaceNormalizer.normalize(content, mode: :link_refs)
|
|
26
|
+
# # => "[link1]: url1\n[link2]: url2"
|
|
27
|
+
#
|
|
28
|
+
# @example With problem tracking
|
|
29
|
+
# normalizer = WhitespaceNormalizer.new(content, mode: :link_refs)
|
|
30
|
+
# result = normalizer.normalize
|
|
31
|
+
# normalizer.problems.by_category(:link_ref_spacing)
|
|
32
|
+
#
|
|
33
|
+
class WhitespaceNormalizer
|
|
34
|
+
# Valid normalization modes
|
|
35
|
+
MODES = %i[basic link_refs strict].freeze
|
|
36
|
+
|
|
37
|
+
# @return [String] The original content
|
|
38
|
+
attr_reader :content
|
|
39
|
+
|
|
40
|
+
# @return [Symbol] The normalization mode
|
|
41
|
+
attr_reader :mode
|
|
42
|
+
|
|
43
|
+
# @return [DocumentProblems] Problems found during normalization
|
|
44
|
+
attr_reader :problems
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
# Normalize whitespace in content (class method for convenience).
|
|
48
|
+
#
|
|
49
|
+
# @param content [String] Content to normalize
|
|
50
|
+
# @param mode [Symbol, Boolean] Normalization mode (:basic, :link_refs, :strict, or true for :basic)
|
|
51
|
+
# @return [String] Normalized content
|
|
52
|
+
def normalize(content, mode: :basic)
|
|
53
|
+
new(content, mode: mode).normalize
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Initialize a new normalizer.
|
|
58
|
+
#
|
|
59
|
+
# @param content [String] Content to normalize
|
|
60
|
+
# @param mode [Symbol, Boolean] Normalization mode (:basic, :link_refs, :strict, or true for :basic)
|
|
61
|
+
def initialize(content, mode: :basic)
|
|
62
|
+
@content = content
|
|
63
|
+
@mode = normalize_mode(mode)
|
|
64
|
+
@problems = DocumentProblems.new
|
|
65
|
+
@link_parser = LinkParser.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Normalize whitespace based on the configured mode.
|
|
69
|
+
#
|
|
70
|
+
# @return [String] Normalized content
|
|
71
|
+
def normalize
|
|
72
|
+
result = content.dup
|
|
73
|
+
|
|
74
|
+
# Always collapse excessive blank lines (3+ → 2)
|
|
75
|
+
result = collapse_excessive_blank_lines(result)
|
|
76
|
+
|
|
77
|
+
# Remove blank lines between link refs if mode requires it
|
|
78
|
+
result = remove_blank_lines_between_link_refs(result) if @mode == :link_refs || @mode == :strict
|
|
79
|
+
|
|
80
|
+
result
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Check if normalization made any changes.
|
|
84
|
+
#
|
|
85
|
+
# @return [Boolean] true if content had whitespace issues
|
|
86
|
+
def changed?
|
|
87
|
+
!@problems.empty?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Get count of normalizations performed.
|
|
91
|
+
#
|
|
92
|
+
# @return [Integer] Number of whitespace issues fixed
|
|
93
|
+
def normalization_count
|
|
94
|
+
@problems.count
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
# Normalize mode parameter to a symbol.
|
|
100
|
+
#
|
|
101
|
+
# @param mode [Symbol, Boolean] Input mode
|
|
102
|
+
# @return [Symbol] Normalized mode
|
|
103
|
+
def normalize_mode(mode)
|
|
104
|
+
case mode
|
|
105
|
+
when true
|
|
106
|
+
:basic
|
|
107
|
+
when false
|
|
108
|
+
:basic # Still do basic normalization
|
|
109
|
+
when Symbol
|
|
110
|
+
raise ArgumentError, "Unknown mode: #{mode}. Valid modes: #{MODES.join(', ')}" unless MODES.include?(mode)
|
|
111
|
+
|
|
112
|
+
mode
|
|
113
|
+
else
|
|
114
|
+
raise ArgumentError, "Mode must be a Symbol or Boolean, got: #{mode.class}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Collapse 3+ consecutive newlines to 2.
|
|
119
|
+
#
|
|
120
|
+
# This detects runs of blank lines (empty lines) and collapses them.
|
|
121
|
+
# Note: A blank line is a line containing only whitespace.
|
|
122
|
+
# 3+ consecutive newlines means 2+ blank lines.
|
|
123
|
+
#
|
|
124
|
+
# @param text [String] Text to process
|
|
125
|
+
# @return [String] Processed text
|
|
126
|
+
def collapse_excessive_blank_lines(text)
|
|
127
|
+
lines = text.lines
|
|
128
|
+
result = []
|
|
129
|
+
consecutive_blank_count = 0
|
|
130
|
+
problem_start_line = nil
|
|
131
|
+
line_number = 0
|
|
132
|
+
|
|
133
|
+
lines.each do |line|
|
|
134
|
+
line_number += 1
|
|
135
|
+
|
|
136
|
+
if line.chomp.empty?
|
|
137
|
+
consecutive_blank_count += 1
|
|
138
|
+
# The problem starts at the line BEFORE the first blank line
|
|
139
|
+
# (i.e., the line that ends with the first \n of the excessive sequence)
|
|
140
|
+
problem_start_line ||= line_number - 1
|
|
141
|
+
|
|
142
|
+
# Only add up to 1 blank line (which creates the standard paragraph gap)
|
|
143
|
+
result << line if consecutive_blank_count <= 1
|
|
144
|
+
# Skip adding lines when consecutive_blank_count >= 2
|
|
145
|
+
else
|
|
146
|
+
# Record problem if we had 2+ blank lines (which means 3+ newlines)
|
|
147
|
+
# consecutive_blank_count is the count of blank lines, so >= 2 means excessive
|
|
148
|
+
if consecutive_blank_count >= 2
|
|
149
|
+
@problems.add(
|
|
150
|
+
:excessive_whitespace,
|
|
151
|
+
severity: :warning,
|
|
152
|
+
line: problem_start_line,
|
|
153
|
+
newline_count: consecutive_blank_count + 1, # +1 because first line ends with \n too
|
|
154
|
+
collapsed_to: 2
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
consecutive_blank_count = 0
|
|
159
|
+
problem_start_line = nil
|
|
160
|
+
result << line
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Handle trailing blank lines
|
|
165
|
+
if consecutive_blank_count >= 2
|
|
166
|
+
@problems.add(
|
|
167
|
+
:excessive_whitespace,
|
|
168
|
+
severity: :warning,
|
|
169
|
+
line: problem_start_line,
|
|
170
|
+
newline_count: consecutive_blank_count + 1,
|
|
171
|
+
collapsed_to: 2
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
result.join
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Remove blank lines between consecutive link reference definitions.
|
|
179
|
+
#
|
|
180
|
+
# Uses {LinkParser} to detect link definitions, supporting:
|
|
181
|
+
# - Standard: `[label]: url`
|
|
182
|
+
# - With title: `[label]: url "title"`
|
|
183
|
+
# - Angle-bracketed: `[label]: <url>`
|
|
184
|
+
# - Emoji labels: `[🎨logo]: url`
|
|
185
|
+
#
|
|
186
|
+
# @param text [String] Text to process
|
|
187
|
+
# @return [String] Processed text
|
|
188
|
+
def remove_blank_lines_between_link_refs(text)
|
|
189
|
+
lines = text.lines
|
|
190
|
+
result = []
|
|
191
|
+
i = 0
|
|
192
|
+
|
|
193
|
+
while i < lines.length
|
|
194
|
+
line = lines[i]
|
|
195
|
+
result << line
|
|
196
|
+
|
|
197
|
+
# Check if current line is a link ref definition using LinkParser
|
|
198
|
+
if link_definition_line?(line)
|
|
199
|
+
# Look ahead for blank lines followed by another link ref
|
|
200
|
+
j = i + 1
|
|
201
|
+
while j < lines.length
|
|
202
|
+
next_line = lines[j]
|
|
203
|
+
break unless next_line.chomp.empty?
|
|
204
|
+
|
|
205
|
+
# Check if there's a link ref definition after the blank line(s)
|
|
206
|
+
k = j + 1
|
|
207
|
+
k += 1 while k < lines.length && lines[k].chomp.empty?
|
|
208
|
+
break unless k < lines.length && link_definition_line?(lines[k])
|
|
209
|
+
|
|
210
|
+
# Skip all blank lines between link refs
|
|
211
|
+
blanks_skipped = k - j
|
|
212
|
+
@problems.add(
|
|
213
|
+
:link_ref_spacing,
|
|
214
|
+
severity: :info,
|
|
215
|
+
line: j + 1,
|
|
216
|
+
blank_lines_removed: blanks_skipped
|
|
217
|
+
)
|
|
218
|
+
j = k
|
|
219
|
+
|
|
220
|
+
# Not followed by a link ref, keep the blank line
|
|
221
|
+
|
|
222
|
+
end
|
|
223
|
+
i = j
|
|
224
|
+
else
|
|
225
|
+
i += 1
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
result.join
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Check if a line is a link reference definition using LinkParser.
|
|
233
|
+
#
|
|
234
|
+
# @param line [String] Line to check
|
|
235
|
+
# @return [Boolean] true if line is a link definition
|
|
236
|
+
def link_definition_line?(line)
|
|
237
|
+
# Use LinkParser to attempt parsing the line as a definition
|
|
238
|
+
result = @link_parser.parse_definition_line(line.chomp)
|
|
239
|
+
!result.nil?
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markdown
|
|
4
|
+
module Merge
|
|
5
|
+
# Shared bootstrap helpers for backend-specific Markdown wrapper gems.
|
|
6
|
+
module WrapperSupport
|
|
7
|
+
SHARED_REEXPORTS = {
|
|
8
|
+
FileAligner: Markdown::Merge::FileAligner,
|
|
9
|
+
ConflictResolver: Markdown::Merge::ConflictResolver,
|
|
10
|
+
MergeResult: Markdown::Merge::MergeResult,
|
|
11
|
+
TableMatchAlgorithm: Markdown::Merge::TableMatchAlgorithm,
|
|
12
|
+
TableMatchRefiner: Markdown::Merge::TableMatchRefiner,
|
|
13
|
+
CodeBlockMerger: Markdown::Merge::CodeBlockMerger,
|
|
14
|
+
NodeTypeNormalizer: Markdown::Merge::NodeTypeNormalizer
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
WRAPPER_AUTOLOADS = {
|
|
18
|
+
DebugLogger: 'debug_logger',
|
|
19
|
+
CommentTracker: 'comment_tracker',
|
|
20
|
+
FreezeNode: 'freeze_node',
|
|
21
|
+
FileAnalysis: 'file_analysis',
|
|
22
|
+
PartialTemplateMerger: 'partial_template_merger',
|
|
23
|
+
SmartMerger: 'smart_merger',
|
|
24
|
+
Backend: 'backend'
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def install!(
|
|
30
|
+
wrapper_module:,
|
|
31
|
+
require_prefix:,
|
|
32
|
+
default_freeze_token:,
|
|
33
|
+
default_inner_merge_code_blocks:,
|
|
34
|
+
registry_tag:,
|
|
35
|
+
merger_class:,
|
|
36
|
+
test_source: "# Test\n\nParagraph",
|
|
37
|
+
category: :markdown
|
|
38
|
+
)
|
|
39
|
+
define_error_classes!(wrapper_module)
|
|
40
|
+
define_constant_unless_present(wrapper_module, :DEFAULT_FREEZE_TOKEN, default_freeze_token)
|
|
41
|
+
define_constant_unless_present(wrapper_module, :DEFAULT_INNER_MERGE_CODE_BLOCKS,
|
|
42
|
+
default_inner_merge_code_blocks)
|
|
43
|
+
|
|
44
|
+
SHARED_REEXPORTS.each do |name, value|
|
|
45
|
+
define_constant_unless_present(wrapper_module, name, value)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
WRAPPER_AUTOLOADS.each do |name, suffix|
|
|
49
|
+
next if wrapper_module.const_defined?(name, false) || wrapper_module.autoload?(name)
|
|
50
|
+
|
|
51
|
+
wrapper_module.send(:autoload, name, "#{require_prefix}/#{suffix}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
install_backend_loader!(wrapper_module)
|
|
55
|
+
register_merge_gem!(
|
|
56
|
+
registry_tag: registry_tag,
|
|
57
|
+
require_path: require_prefix,
|
|
58
|
+
merger_class: merger_class,
|
|
59
|
+
test_source: test_source,
|
|
60
|
+
category: category
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def configure_debug_logger!(debug_logger_module:, env_var_name:, log_prefix:)
|
|
65
|
+
debug_logger_module.extend(Ast::Merge::DebugLogger)
|
|
66
|
+
debug_logger_module.env_var_name = env_var_name
|
|
67
|
+
debug_logger_module.log_prefix = log_prefix
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def configure_file_analysis_subclass!(klass, default_backend:, default_parser_options: nil)
|
|
71
|
+
install_singleton_value_method!(klass, :default_backend, default_backend)
|
|
72
|
+
return unless default_parser_options
|
|
73
|
+
|
|
74
|
+
install_singleton_value_method!(klass, :default_parser_options,
|
|
75
|
+
default_parser_options)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def configure_smart_merger_subclass!(
|
|
79
|
+
klass,
|
|
80
|
+
default_backend:,
|
|
81
|
+
default_freeze_token: nil,
|
|
82
|
+
default_inner_merge_code_blocks: nil,
|
|
83
|
+
default_parser_options: nil,
|
|
84
|
+
file_analysis_class: nil,
|
|
85
|
+
template_parse_error_class: nil,
|
|
86
|
+
destination_parse_error_class: nil
|
|
87
|
+
)
|
|
88
|
+
install_singleton_value_method!(klass, :default_backend, default_backend)
|
|
89
|
+
install_singleton_value_method!(klass, :default_freeze_token, default_freeze_token) if default_freeze_token
|
|
90
|
+
unless default_inner_merge_code_blocks.nil?
|
|
91
|
+
install_singleton_value_method!(klass, :default_inner_merge_code_blocks,
|
|
92
|
+
default_inner_merge_code_blocks)
|
|
93
|
+
end
|
|
94
|
+
if default_parser_options
|
|
95
|
+
install_singleton_value_method!(klass, :default_parser_options,
|
|
96
|
+
default_parser_options)
|
|
97
|
+
end
|
|
98
|
+
install_singleton_value_method!(klass, :file_analysis_class, file_analysis_class) if file_analysis_class
|
|
99
|
+
if template_parse_error_class
|
|
100
|
+
install_singleton_value_method!(klass, :template_parse_error_class,
|
|
101
|
+
template_parse_error_class)
|
|
102
|
+
end
|
|
103
|
+
return unless destination_parse_error_class
|
|
104
|
+
|
|
105
|
+
install_singleton_value_method!(klass, :destination_parse_error_class,
|
|
106
|
+
destination_parse_error_class)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def configure_partial_template_merger_subclass!(klass, default_backend:, file_analysis_class:,
|
|
110
|
+
smart_merger_class:)
|
|
111
|
+
install_singleton_value_method!(klass, :default_backend, default_backend)
|
|
112
|
+
install_singleton_value_method!(klass, :file_analysis_class, file_analysis_class)
|
|
113
|
+
install_singleton_value_method!(klass, :smart_merger_class, smart_merger_class)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def install_backend_loader!(wrapper_module)
|
|
117
|
+
return if wrapper_module.respond_to?(:ensure_backend_loaded!)
|
|
118
|
+
|
|
119
|
+
wrapper_module.singleton_class.class_eval do
|
|
120
|
+
define_method(:ensure_backend_loaded!) do
|
|
121
|
+
const_get(:Backend)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
private_class_method :install_backend_loader!
|
|
126
|
+
|
|
127
|
+
def define_error_classes!(wrapper_module)
|
|
128
|
+
define_constant_unless_present(wrapper_module, :Error, Class.new(Markdown::Merge::Error))
|
|
129
|
+
define_constant_unless_present(wrapper_module, :ParseError, Class.new(Markdown::Merge::ParseError))
|
|
130
|
+
define_constant_unless_present(
|
|
131
|
+
wrapper_module,
|
|
132
|
+
:TemplateParseError,
|
|
133
|
+
Class.new(wrapper_module.const_get(:ParseError))
|
|
134
|
+
)
|
|
135
|
+
define_constant_unless_present(
|
|
136
|
+
wrapper_module,
|
|
137
|
+
:DestinationParseError,
|
|
138
|
+
Class.new(wrapper_module.const_get(:ParseError))
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
private_class_method :define_error_classes!
|
|
142
|
+
|
|
143
|
+
def install_singleton_value_method!(klass, method_name, value)
|
|
144
|
+
klass.singleton_class.class_eval do
|
|
145
|
+
define_method(method_name) do
|
|
146
|
+
WrapperSupport.send(:resolve_config_value, value, context: self)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
private_class_method :install_singleton_value_method!
|
|
151
|
+
|
|
152
|
+
def resolve_config_value(value, context:)
|
|
153
|
+
return context.instance_exec(&value) if value.respond_to?(:call)
|
|
154
|
+
|
|
155
|
+
duplicate_config_value(value)
|
|
156
|
+
end
|
|
157
|
+
private_class_method :resolve_config_value
|
|
158
|
+
|
|
159
|
+
def duplicate_config_value(value)
|
|
160
|
+
case value
|
|
161
|
+
when Hash
|
|
162
|
+
value.each_with_object({}) do |(key, nested_value), result|
|
|
163
|
+
result[key] = duplicate_config_value(nested_value)
|
|
164
|
+
end
|
|
165
|
+
when Array
|
|
166
|
+
value.map { |nested_value| duplicate_config_value(nested_value) }
|
|
167
|
+
else
|
|
168
|
+
value
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
private_class_method :duplicate_config_value
|
|
172
|
+
|
|
173
|
+
def register_merge_gem!(registry_tag:, require_path:, merger_class:, test_source:, category:)
|
|
174
|
+
return unless defined?(Ast::Merge::RSpec::MergeGemRegistry)
|
|
175
|
+
|
|
176
|
+
Ast::Merge::RSpec::MergeGemRegistry.register(
|
|
177
|
+
registry_tag,
|
|
178
|
+
require_path: require_path,
|
|
179
|
+
merger_class: merger_class,
|
|
180
|
+
test_source: test_source,
|
|
181
|
+
category: category
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
private_class_method :register_merge_gem!
|
|
185
|
+
|
|
186
|
+
def define_constant_unless_present(wrapper_module, name, value)
|
|
187
|
+
return if wrapper_module.const_defined?(name, false)
|
|
188
|
+
|
|
189
|
+
wrapper_module.const_set(name, value)
|
|
190
|
+
end
|
|
191
|
+
private_class_method :define_constant_unless_present
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|