diver_down 0.0.1.alpha13 → 0.0.1.alpha15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DiverDown
4
+ class Web
5
+ class DefinitionModuleDependencies
6
+ class ModuleDependency
7
+ attr_accessor :module
8
+ attr_reader :module_dependencies, :module_reverse_dependencies, :source_map, :source_reverse_dependency_map
9
+
10
+ def initialize(modulee)
11
+ @module = modulee
12
+ @module_dependencies = Set.new
13
+ @module_reverse_dependencies = Set.new
14
+ @source_map = {}
15
+ @source_reverse_dependency_map = {}
16
+ end
17
+
18
+ # @return [Array<Source>]
19
+ def sources
20
+ @source_map.keys.sort.map { @source_map.fetch(_1) }
21
+ end
22
+
23
+ # @return [Array<Source>]
24
+ def source_reverse_dependencies
25
+ @source_reverse_dependency_map.keys.sort.map { @source_reverse_dependency_map.fetch(_1) }
26
+ end
27
+ end
28
+
29
+ def initialize(metadata, definition)
30
+ @metadata = metadata
31
+ @definition = definition
32
+ end
33
+
34
+ # @return [Hash{ String => Hash{ String => Array<DiverDown::Definition::Source> } }]
35
+ def build_module_dependency_map
36
+ module_dependency_map = Hash.new { |h, k| h[k] = ModuleDependency.new(k) }
37
+
38
+ @definition.sources.each do |source|
39
+ source_module = @metadata.source(source.source_name).module
40
+ next if source_module.nil?
41
+
42
+ source_module_dependency = module_dependency_map[source_module]
43
+ source_module_dependency.source_map[source.source_name] ||= DiverDown::Definition::Source.new(source_name: source.source_name)
44
+
45
+ source.dependencies.each do |dependency|
46
+ dependency_module = @metadata.source(dependency.source_name).module
47
+
48
+ next if source_module == dependency_module
49
+ next if dependency_module.nil?
50
+
51
+ dependency_module_dependency = module_dependency_map[dependency_module]
52
+
53
+ # Add module dependencies
54
+ source_module_dependency.module_dependencies.add(dependency_module)
55
+
56
+ # Add module reverse dependencies
57
+ dependency_module_dependency.module_reverse_dependencies.add(source_module)
58
+
59
+ # Add source
60
+ definition_source = source_module_dependency.source_map[source.source_name]
61
+ definition_dependency = definition_source.find_or_build_dependency(dependency.source_name)
62
+ dependency.method_ids.each do |method_id|
63
+ definition_dependency.find_or_build_method_id(name: method_id.name, context: method_id.context).add_path(*method_id.paths)
64
+ end
65
+
66
+ # Add source reverse dependencies
67
+ definition_source = dependency_module_dependency.source_reverse_dependency_map[dependency.source_name] ||= DiverDown::Definition::Source.new(source_name: dependency.source_name)
68
+ definition_dependency = definition_source.find_or_build_dependency(source.source_name)
69
+ dependency.method_ids.each do |method_id|
70
+ definition_dependency.find_or_build_method_id(name: method_id.name, context: method_id.context).add_path(*method_id.paths)
71
+ end
72
+ end
73
+ end
74
+
75
+ module_dependency_map
76
+ end
77
+ end
78
+ end
79
+ end
@@ -5,12 +5,11 @@ module DiverDown
5
5
  class DefinitionStore
6
6
  include Enumerable
7
7
 
8
- attr_reader :bit_id
9
-
10
8
  def initialize
11
9
  # Hash{ Integer(unique bit flag) => DiverDown::Definition }
12
10
  @definitions = []
13
11
  @definition_group_store = Hash.new { |h, k| h[k] = [] }
12
+ @combined_definition = nil
14
13
  end
15
14
 
16
15
  # @param id [Integer]
@@ -31,14 +30,33 @@ module DiverDown
31
30
  raise(ArgumentError, 'definition already set') if _1.store_id
32
31
 
33
32
  _1.store_id = @definitions.size + 1
33
+ _1.freeze
34
34
 
35
35
  @definitions.push(_1)
36
36
  @definition_group_store[_1.definition_group] << _1
37
37
 
38
+ # Reset combined_definition
39
+ @combined_definition = nil
40
+
38
41
  _1.store_id
39
42
  end
40
43
  end
41
44
 
45
+ # @return [DiverDown::Definition]
46
+ def combined_definition
47
+ if @combined_definition.nil?
48
+ @combined_definition = DiverDown::Definition.combine(
49
+ definition_group: nil,
50
+ title: 'All Definitions',
51
+ definitions: @definitions
52
+ )
53
+
54
+ @combined_definition.freeze
55
+ end
56
+
57
+ @combined_definition
58
+ end
59
+
42
60
  # @return [Array<String, nil>]
43
61
  def definition_groups
44
62
  keys = @definition_group_store.keys
@@ -7,13 +7,12 @@ module DiverDown
7
7
  class Web
8
8
  class DefinitionToDot
9
9
  ATTRIBUTE_DELIMITER = ' '
10
- MODULE_DELIMITER = '::'
11
10
 
12
11
  # Between modules is prominently distanced
13
12
  MODULE_MINLEN = 3
14
13
 
15
- class MetadataStore
16
- Metadata = Data.define(:id, :type, :data, :module_store) do
14
+ class DotMetadataStore
15
+ DotMetadata = Data.define(:id, :type, :data, :metadata) do
17
16
  # @return [Hash]
18
17
  def to_h
19
18
  case type
@@ -31,18 +30,12 @@ module DiverDown
31
30
  private
32
31
 
33
32
  def source_to_h
34
- modules = module_store.get_modules(data.source_name).map do
35
- {
36
- module_name: _1,
37
- }
38
- end
39
-
40
33
  {
41
34
  id:,
42
35
  type: 'source',
43
36
  source_name: data.source_name,
44
- memo: module_store.get_memo(data.source_name),
45
- modules:,
37
+ memo: metadata.source(data.source_name).memo,
38
+ module: metadata.source(data.source_name).module,
46
39
  }
47
40
  end
48
41
 
@@ -68,20 +61,16 @@ module DiverDown
68
61
  {
69
62
  id:,
70
63
  type: 'module',
71
- modules: data.map do
72
- {
73
- module_name: _1,
74
- }
75
- end,
64
+ module: data,
76
65
  }
77
66
  end
78
67
  end
79
68
 
80
- def initialize(module_store)
69
+ def initialize(metadata)
81
70
  @prefix = 'graph_'
82
- @module_store = module_store
71
+ @metadata = metadata
83
72
 
84
- # Hash{ id => Metadata }
73
+ # Hash{ id => DotMetadata }
85
74
  @to_h = {}
86
75
  end
87
76
 
@@ -89,34 +78,34 @@ module DiverDown
89
78
  # @param record [DiverDown::Definition::Source]
90
79
  # @return [String]
91
80
  def issue_source_id(source)
92
- build_metadata_and_return_id(:source, source)
81
+ build_dot_metadata_and_return_id(:source, source)
93
82
  end
94
83
 
95
84
  # @param dependency [DiverDown::Definition::Dependency]
96
85
  # @return [String]
97
86
  def issue_dependency_id(dependency)
98
- build_metadata_and_return_id(:dependency, [dependency])
87
+ build_dot_metadata_and_return_id(:dependency, [dependency])
99
88
  end
100
89
 
101
90
  # @param module_names [Array<String>]
102
91
  # @return [String]
103
- def issue_modules_id(module_names)
104
- issued_metadata = @to_h.values.find { _1.type == :module && _1.data == module_names }
92
+ def issue_module_id(modulee)
93
+ issued_metadata = @to_h.values.find { _1.type == :module && _1.data == modulee }
105
94
 
106
95
  if issued_metadata
107
96
  issued_metadata.id
108
97
  else
109
- build_metadata_and_return_id(:module, module_names)
98
+ build_dot_metadata_and_return_id(:module, modulee)
110
99
  end
111
100
  end
112
101
 
113
102
  # @param id [String]
114
103
  # @param dependency [DiverDown::Definition::Dependency]
115
104
  def append_dependency(id, dependency)
116
- metadata = @to_h.fetch(id)
117
- dependencies = metadata.data
105
+ dot_metadata = @to_h.fetch(id)
106
+ dependencies = dot_metadata.data
118
107
  combined_dependencies = DiverDown::Definition::Dependency.combine(*dependencies, dependency)
119
- metadata.data.replace(combined_dependencies)
108
+ dot_metadata.data.replace(combined_dependencies)
120
109
  end
121
110
 
122
111
  # @return [Array<Hash>]
@@ -126,10 +115,10 @@ module DiverDown
126
115
 
127
116
  private
128
117
 
129
- def build_metadata_and_return_id(type, data)
118
+ def build_dot_metadata_and_return_id(type, data)
130
119
  id = "#{@prefix}#{length + 1}"
131
- metadata = Metadata.new(id:, type:, data:, module_store: @module_store)
132
- @to_h[id] = metadata
120
+ dot_metadata = DotMetadata.new(id:, type:, data:, metadata: @metadata)
121
+ @to_h[id] = dot_metadata
133
122
 
134
123
  id
135
124
  end
@@ -140,24 +129,24 @@ module DiverDown
140
129
  end
141
130
 
142
131
  # @param definition [DiverDown::Definition]
143
- # @param module_store [DiverDown::ModuleStore]
132
+ # @param metadata [DiverDown::Web::Metadata]
144
133
  # @param compound [Boolean]
145
134
  # @param concentrate [Boolean] https://graphviz.org/docs/attrs/concentrate/
146
- def initialize(definition, module_store, compound: false, concentrate: false, only_module: false)
135
+ def initialize(definition, metadata, compound: false, concentrate: false, only_module: false)
147
136
  @definition = definition
148
- @module_store = module_store
137
+ @metadata = metadata
149
138
  @io = DiverDown::Web::IndentedStringIo.new
150
139
  @indent = 0
151
140
  @compound = compound || only_module # When only-module is enabled, dependencies between modules are displayed as compound.
152
141
  @compound_map = Hash.new { |h, k| h[k] = {} } # Hash{ ltail => Hash{ lhead => issued id } }
153
142
  @concentrate = concentrate
154
143
  @only_module = only_module
155
- @metadata_store = MetadataStore.new(module_store)
144
+ @dot_metadata_store = DotMetadataStore.new(metadata)
156
145
  end
157
146
 
158
147
  # @return [Array<Hash>]
159
- def metadata
160
- @metadata_store.to_a
148
+ def dot_metadata
149
+ @dot_metadata_store.to_a
161
150
  end
162
151
 
163
152
  # @return [String]
@@ -179,77 +168,62 @@ module DiverDown
179
168
 
180
169
  private
181
170
 
182
- attr_reader :definition, :module_store, :io
171
+ attr_reader :definition, :metadata, :io
183
172
 
184
173
  def render_only_modules
185
174
  # Hash{ from_module => { to_module => Array<DiverDown::Definition::Dependency> } }
186
175
  dependency_map = Hash.new { |h, k| h[k] = Hash.new { |hi, ki| hi[ki] = [] } }
187
176
 
188
177
  definition.sources.sort_by(&:source_name).each do |source|
189
- source_modules = module_store.get_modules(source.source_name)
190
- next if source_modules.empty?
178
+ source_module = metadata.source(source.source_name).module
179
+ next if source_module.nil?
191
180
 
192
181
  source.dependencies.each do |dependency|
193
- dependency_modules = module_store.get_modules(dependency.source_name)
194
- next if dependency_modules.empty?
182
+ dependency_module = metadata.source(dependency.source_name).module
183
+ next if dependency_module.nil?
195
184
 
196
- dependency_map[source_modules][dependency_modules].push(dependency)
185
+ dependency_map[source_module][dependency_module].push(dependency)
197
186
  end
198
187
  end
199
188
 
200
189
  # Remove duplicated prefix modules
201
- # from [["A"], ["A", "B"]] to [["A", "B"]]
202
- uniq_modules = [*dependency_map.keys, *dependency_map.values.map(&:keys).flatten(1)].uniq
203
- uniq_modules.reject! do |modules|
204
- modules.empty? ||
205
- uniq_modules.any? { _1[0..modules.size - 1] == modules && _1.length > modules.size }
206
- end
207
-
208
- uniq_modules.each do |specific_module_names|
209
- buf = swap_io do
210
- indexes = (0..(specific_module_names.length - 1)).to_a
211
-
212
- chain_yield(indexes) do |index, next_proc|
213
- module_names = specific_module_names[0..index]
214
- module_name = specific_module_names[index]
215
-
216
- io.puts %(subgraph "#{escape_quote(module_label(module_names))}" {)
217
- io.indented do
218
- io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
219
- io.puts %(label="#{escape_quote(module_name)}")
220
- io.puts %("#{escape_quote(module_name)}" #{build_attributes(label: module_name, id: @metadata_store.issue_modules_id(module_names))})
221
-
222
- next_proc&.call
223
- end
224
- io.puts '}'
225
- end
190
+ uniq_modules = [*dependency_map.keys, *dependency_map.values.map(&:keys).flatten].uniq.sort
191
+ uniq_modules.reject!(&:nil?)
192
+
193
+ uniq_modules.each do |modulee|
194
+ io.puts %(subgraph "#{escape_quote(module_label(modulee))}" {)
195
+ io.indented do
196
+ io.puts %(id="#{@dot_metadata_store.issue_module_id(modulee)}")
197
+ io.puts %(label="#{escape_quote(modulee)}")
198
+ io.puts %("#{escape_quote(modulee)}" #{build_attributes(label: modulee, id: @dot_metadata_store.issue_module_id(modulee))})
226
199
  end
227
-
228
- io.write buf.string
200
+ io.puts '}'
229
201
  end
230
202
 
231
- dependency_map.each do |from_modules, h|
232
- h.each do |to_modules, all_dependencies|
203
+ dependency_map.keys.sort_by(&:to_s).each do |from_module|
204
+ dependency_map.fetch(from_module).keys.sort_by(&:to_s).each do |to_module|
205
+ all_dependencies = dependency_map.fetch(from_module).fetch(to_module)
206
+
233
207
  # Do not render standalone source
234
208
  # Do not render self-dependency
235
- next if from_modules.empty? || to_modules.empty? || from_modules == to_modules
209
+ next if from_module.nil? || to_module.empty? || from_module == to_module
236
210
 
237
211
  dependencies = DiverDown::Definition::Dependency.combine(*all_dependencies)
238
212
 
239
213
  dependencies.each do
240
214
  attributes = {}
241
- ltail = module_label(*from_modules)
242
- lhead = module_label(*to_modules)
215
+ ltail = module_label(from_module)
216
+ lhead = module_label(to_module)
243
217
 
244
218
  # Already rendered dependencies between modules
245
219
  # Add the dependency to the edge of the compound
246
220
  if @compound_map[ltail].include?(lhead)
247
221
  compound_id = @compound_map[ltail][lhead]
248
- @metadata_store.append_dependency(compound_id, _1)
222
+ @dot_metadata_store.append_dependency(compound_id, _1)
249
223
  next
250
224
  end
251
225
 
252
- compound_id = @metadata_store.issue_dependency_id(_1)
226
+ compound_id = @dot_metadata_store.issue_dependency_id(_1)
253
227
  @compound_map[ltail][lhead] = compound_id
254
228
 
255
229
  attributes.merge!(
@@ -259,7 +233,7 @@ module DiverDown
259
233
  minlen: MODULE_MINLEN
260
234
  )
261
235
 
262
- io.write(%("#{escape_quote(from_modules[-1])}" -> "#{escape_quote(to_modules[-1])}"))
236
+ io.write(%("#{escape_quote(from_module)}" -> "#{escape_quote(to_module)}"))
263
237
  io.write(%( #{build_attributes(**attributes)}), indent: false) unless attributes.empty?
264
238
  io.write("\n")
265
239
  end
@@ -268,67 +242,49 @@ module DiverDown
268
242
  end
269
243
 
270
244
  def render_sources
271
- by_modules = definition.sources.group_by do |source|
272
- module_store.get_modules(source.source_name)
273
- end
274
-
275
- # Remove duplicated prefix modules
276
- # from [["A"], ["A", "B"]] to [["A", "B"]]
277
- uniq_modules = by_modules.keys.uniq
278
- uniq_modules = uniq_modules.reject do |modules|
279
- uniq_modules.any? { _1[0..modules.size - 1] == modules && _1.length > modules.size }
245
+ # Hash{ module => sources }
246
+ # Hash{ String => Array<DiverDown::Definition::Source> }
247
+ by_module = definition.sources.group_by do |source|
248
+ metadata.source(source.source_name).module
280
249
  end
281
250
 
282
- uniq_modules.each do |full_modules|
283
- # Render module and source
284
- if full_modules.empty?
285
- sources = by_modules[full_modules].sort_by(&:source_name)
251
+ # Render subgraph for each module and its sources second
252
+ by_module.keys.sort_by(&:to_s).each do |modulee|
253
+ sources = by_module.fetch(modulee).sort_by(&:source_name)
286
254
 
255
+ if modulee.nil?
287
256
  sources.each do |source|
288
- insert_source(source)
257
+ io.puts build_source_node(source)
289
258
  end
290
259
  else
291
- buf = swap_io do
292
- indexes = (0..(full_modules.length - 1)).to_a
293
-
294
- chain_yield(indexes) do |index, next_proc|
295
- module_names = full_modules[0..index]
296
- module_name = module_names[-1]
297
-
298
- io.puts %(subgraph "#{escape_quote(module_label(module_names))}" {)
299
- io.indented do
300
- io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
301
- io.puts %(label="#{escape_quote(module_name)}")
302
-
303
- sources = (by_modules[module_names] || []).sort_by(&:source_name)
304
- sources.each do |source|
305
- insert_source(source)
306
- end
307
-
308
- next_proc&.call
309
- end
310
- io.puts '}'
260
+ io.puts %(subgraph "#{escape_quote(module_label(modulee))}" {)
261
+ io.indented do
262
+ io.puts %(id="#{@dot_metadata_store.issue_module_id(modulee)}")
263
+ io.puts %(label="#{escape_quote(modulee)}")
264
+
265
+ sources.each do |source|
266
+ io.puts build_source_node(source)
311
267
  end
312
268
  end
313
-
314
- io.write buf.string
269
+ io.puts '}'
315
270
  end
316
271
  end
317
272
 
273
+ # Render dependencies last
318
274
  definition.sources.sort_by(&:source_name).each do |source|
319
275
  insert_dependencies(source)
320
276
  end
321
277
  end
322
278
 
323
- def insert_source(source)
324
- io.puts %("#{escape_quote(source.source_name)}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
279
+ def build_source_node(source)
280
+ %("#{escape_quote(source.source_name)}" #{build_attributes(label: source.source_name, id: @dot_metadata_store.issue_source_id(source))})
325
281
  end
326
282
 
327
283
  def insert_dependencies(source)
328
284
  source.dependencies.each do
329
285
  attributes = {}
330
- ltail = module_label(*module_store.get_modules(source.source_name))
331
- lhead = module_label(*module_store.get_modules(_1.source_name))
286
+ ltail = module_label(metadata.source(source.source_name).module)
287
+ lhead = module_label(metadata.source(_1.source_name).module)
332
288
 
333
289
  if @compound && (ltail || lhead)
334
290
  # Rendering of dependencies between modules is done only once
@@ -338,11 +294,11 @@ module DiverDown
338
294
  # Add the dependency to the edge of the compound
339
295
  if between_modules && @compound_map[ltail].include?(lhead)
340
296
  compound_id = @compound_map[ltail][lhead]
341
- @metadata_store.append_dependency(compound_id, _1)
297
+ @dot_metadata_store.append_dependency(compound_id, _1)
342
298
  next
343
299
  end
344
300
 
345
- compound_id = @metadata_store.issue_dependency_id(_1)
301
+ compound_id = @dot_metadata_store.issue_dependency_id(_1)
346
302
  @compound_map[ltail][lhead] = compound_id
347
303
 
348
304
  attributes.merge!(
@@ -353,7 +309,8 @@ module DiverDown
353
309
  )
354
310
  else
355
311
  attributes.merge!(
356
- id: @metadata_store.issue_dependency_id(_1)
312
+ id: @dot_metadata_store.issue_dependency_id(_1),
313
+ minlen: MODULE_MINLEN
357
314
  )
358
315
  end
359
316
 
@@ -411,10 +368,10 @@ module DiverDown
411
368
  @io = old_io
412
369
  end
413
370
 
414
- def module_label(*modules)
415
- return if modules.empty?
371
+ def module_label(modulee)
372
+ return if modulee.nil?
416
373
 
417
- "cluster_#{modules.join(MODULE_DELIMITER)}"
374
+ "cluster_#{modulee}"
418
375
  end
419
376
 
420
377
  def escape_quote(string)
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DiverDown
4
+ class Web
5
+ class Metadata
6
+ class SourceAlias
7
+ class ConflictError < StandardError
8
+ attr_reader :source_name
9
+
10
+ def initialize(message, source_name:)
11
+ @source_name = source_name
12
+
13
+ super(message)
14
+ end
15
+ end
16
+
17
+ BLANK_RE = /\A\s*\z/
18
+
19
+ def initialize
20
+ # Hash{ alias_name => Set<source_name, ...> }
21
+ @alias_to_source_names = {}
22
+ @source_name_to_alias = {}
23
+ end
24
+
25
+ # @param alias_name [String]
26
+ # @param source_names [Array<String>]
27
+ # @return [void]
28
+ def update_alias(alias_name, source_names)
29
+ source_names = source_names.reject { BLANK_RE.match?(_1) }
30
+
31
+ if source_names.empty?
32
+ prev_source_names = @alias_to_source_names.delete(alias_name)
33
+ prev_source_names&.each do |prev_source_name|
34
+ @source_name_to_alias.delete(prev_source_name)
35
+ end
36
+ else
37
+ check_conflict(alias_name, source_names)
38
+ @alias_to_source_names[alias_name] = source_names.sort
39
+
40
+ source_names.each do |source_name|
41
+ @source_name_to_alias[source_name] = alias_name
42
+ end
43
+ end
44
+ end
45
+
46
+ # @param alias_name [String]
47
+ # @return [String]
48
+ def resolve_alias(source_name)
49
+ @source_name_to_alias[source_name] if @source_name_to_alias.key?(source_name)
50
+ end
51
+
52
+ # @param alias_name [String]
53
+ # @return [Array<String>]
54
+ def aliased_source_names(alias_name)
55
+ @alias_to_source_names[alias_name] if @alias_to_source_names.key?(alias_name)
56
+ end
57
+
58
+ # Rollback the changes made in the block if an conflict error occurs.
59
+ #
60
+ # @raise [ConflictError]
61
+ def transaction
62
+ alias_to_source_names = deep_dup(@alias_to_source_names)
63
+ source_name_to_alias = deep_dup(@source_name_to_alias)
64
+
65
+ yield
66
+ rescue ConflictError
67
+ @alias_to_source_names = alias_to_source_names
68
+ @source_name_to_alias = source_name_to_alias
69
+
70
+ raise
71
+ end
72
+
73
+ # @return [Hash]
74
+ def to_h
75
+ keys = @alias_to_source_names.keys.sort
76
+ keys.to_h { [_1, aliased_source_names(_1)] }
77
+ end
78
+
79
+ private
80
+
81
+ def deep_dup(hash)
82
+ hash.transform_values do
83
+ if _1.is_a?(Hash)
84
+ deep_dup(_1)
85
+ else
86
+ _1.dup
87
+ end
88
+ end
89
+ end
90
+
91
+ def added_source_names(excluding_alias_name)
92
+ source_names = Set.new
93
+
94
+ @alias_to_source_names.each_key do |name|
95
+ next if excluding_alias_name == name
96
+
97
+ source_names.add(name)
98
+
99
+ @alias_to_source_names[name].each do |source_name|
100
+ source_names.add(source_name)
101
+ end
102
+ end
103
+
104
+ source_names
105
+ end
106
+
107
+ def check_conflict(alias_name, source_names)
108
+ if source_names.any? { _1 == alias_name }
109
+ raise(ConflictError.new("Cannot create an alias for '#{alias_name}' that refers to itself.", source_name: alias_name))
110
+ end
111
+
112
+ registered_source_names = added_source_names(alias_name)
113
+
114
+ if registered_source_names.include?(alias_name)
115
+ raise ConflictError.new("Alias '#{alias_name}' is already aliased.", source_name: alias_name)
116
+ end
117
+
118
+ conflicted_source_name = source_names.find { registered_source_names.include?(_1) }
119
+
120
+ if conflicted_source_name
121
+ resolved_source_name = resolve_alias(conflicted_source_name)
122
+ raise ConflictError.new("Source '#{conflicted_source_name}' is already aliased to '#{resolved_source_name}'.", source_name: conflicted_source_name)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end