diver_down 0.0.1.alpha14 → 0.0.1.alpha15

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.
@@ -7,7 +7,6 @@ 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
@@ -31,18 +30,12 @@ module DiverDown
31
30
  private
32
31
 
33
32
  def source_to_h
34
- modules = metadata.source(data.source_name).modules.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
37
  memo: metadata.source(data.source_name).memo,
45
- modules:,
38
+ module: metadata.source(data.source_name).module,
46
39
  }
47
40
  end
48
41
 
@@ -68,11 +61,7 @@ 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
@@ -100,13 +89,13 @@ module DiverDown
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_dot_metadata_and_return_id(:module, module_names)
98
+ build_dot_metadata_and_return_id(:module, modulee)
110
99
  end
111
100
  end
112
101
 
@@ -186,60 +175,45 @@ module DiverDown
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 = metadata.source(source.source_name).modules
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 = metadata.source(dependency.source_name).modules
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"], ["A", "C"], ["D"]] to { "A" => { "B" => {}, "C" => {} }, "D" => {} }
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="#{@dot_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: @dot_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
@@ -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,46 +242,38 @@ module DiverDown
268
242
  end
269
243
 
270
244
  def render_sources
271
- # Hash{ modules => sources }
272
- # Hash{ Array<String> => Array<DiverDown::Definition::Source> }
273
- by_modules = definition.sources.group_by do |source|
274
- metadata.source(source.source_name).modules
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
275
249
  end
276
250
 
277
251
  # Render subgraph for each module and its sources second
278
- nested_modules = array_to_hash(by_modules.keys.uniq.sort)
279
- render_nested_modules_sources(by_modules, nested_modules)
280
-
281
- # Render dependencies last
282
- definition.sources.sort_by(&:source_name).each do |source|
283
- insert_dependencies(source)
284
- end
285
- end
286
-
287
- def render_nested_modules_sources(by_modules, nested_modules, prefix = [])
288
- nested_modules.each do |module_name, next_nested_modules|
289
- module_names = prefix + [module_name].compact
290
- sources = (by_modules[module_names] || []).sort_by(&:source_name)
252
+ by_module.keys.sort_by(&:to_s).each do |modulee|
253
+ sources = by_module.fetch(modulee).sort_by(&:source_name)
291
254
 
292
- if module_name.nil?
255
+ if modulee.nil?
293
256
  sources.each do |source|
294
257
  io.puts build_source_node(source)
295
258
  end
296
259
  else
297
- io.puts %(subgraph "#{escape_quote(module_label(module_names))}" {)
260
+ io.puts %(subgraph "#{escape_quote(module_label(modulee))}" {)
298
261
  io.indented do
299
- io.puts %(id="#{@dot_metadata_store.issue_modules_id(module_names)}")
300
- io.puts %(label="#{escape_quote(module_name)}")
262
+ io.puts %(id="#{@dot_metadata_store.issue_module_id(modulee)}")
263
+ io.puts %(label="#{escape_quote(modulee)}")
301
264
 
302
265
  sources.each do |source|
303
266
  io.puts build_source_node(source)
304
267
  end
305
-
306
- render_nested_modules_sources(by_modules, next_nested_modules, module_names)
307
268
  end
308
269
  io.puts '}'
309
270
  end
310
271
  end
272
+
273
+ # Render dependencies last
274
+ definition.sources.sort_by(&:source_name).each do |source|
275
+ insert_dependencies(source)
276
+ end
311
277
  end
312
278
 
313
279
  def build_source_node(source)
@@ -317,8 +283,8 @@ module DiverDown
317
283
  def insert_dependencies(source)
318
284
  source.dependencies.each do
319
285
  attributes = {}
320
- ltail = module_label(*metadata.source(source.source_name).modules)
321
- lhead = module_label(*metadata.source(_1.source_name).modules)
286
+ ltail = module_label(metadata.source(source.source_name).module)
287
+ lhead = module_label(metadata.source(_1.source_name).module)
322
288
 
323
289
  if @compound && (ltail || lhead)
324
290
  # Rendering of dependencies between modules is done only once
@@ -343,7 +309,8 @@ module DiverDown
343
309
  )
344
310
  else
345
311
  attributes.merge!(
346
- id: @dot_metadata_store.issue_dependency_id(_1)
312
+ id: @dot_metadata_store.issue_dependency_id(_1),
313
+ minlen: MODULE_MINLEN
347
314
  )
348
315
  end
349
316
 
@@ -401,37 +368,15 @@ module DiverDown
401
368
  @io = old_io
402
369
  end
403
370
 
404
- def module_label(*modules)
405
- return if modules.empty?
371
+ def module_label(modulee)
372
+ return if modulee.nil?
406
373
 
407
- "cluster_#{modules.join(MODULE_DELIMITER)}"
374
+ "cluster_#{modulee}"
408
375
  end
409
376
 
410
377
  def escape_quote(string)
411
378
  string.to_s.gsub(/"/, '\"')
412
379
  end
413
-
414
- # from [["A"], ["A", "B"], ["A", "C"], ["D"]] to { "A" => { "B" => {}, "C" => {} }, "D" => {} }
415
- def array_to_hash(array)
416
- hash = {}
417
- array.each do |sub_array|
418
- current_hash = hash
419
-
420
- if sub_array.empty?
421
- current_hash[nil] = {}
422
- else
423
- sub_array.each_with_index do |element, index|
424
- if index == sub_array.length - 1
425
- current_hash[element] = {}
426
- else
427
- current_hash[element] ||= {}
428
- current_hash = current_hash[element]
429
- end
430
- end
431
- end
432
- end
433
- hash
434
- end
435
380
  end
436
381
  end
437
382
  end
@@ -6,17 +6,16 @@ module DiverDown
6
6
  class SourceMetadata
7
7
  BLANK_MEMO = ''
8
8
  BLANK_RE = /\A\s*\z/
9
- BLANK_ARRAY = [].freeze
10
9
  private_constant(:BLANK_MEMO)
11
10
  private_constant(:BLANK_RE)
12
- private_constant(:BLANK_ARRAY)
13
11
 
14
- attr_reader :memo, :modules
12
+ attr_reader :memo, :module
15
13
 
16
14
  # @param source_name [String]
17
- def initialize(memo: BLANK_MEMO, modules: BLANK_ARRAY)
15
+ # NOTE: `module` is a reserved keyword in Ruby
16
+ def initialize(memo: BLANK_MEMO, modulee: nil)
18
17
  @memo = memo
19
- @modules = modules
18
+ @module = modulee
20
19
  end
21
20
 
22
21
  # @param memo [String]
@@ -25,17 +24,19 @@ module DiverDown
25
24
  @memo = memo.strip
26
25
  end
27
26
 
28
- # @param module_names [Array<String>]
27
+ # @param modulee [String, nil]
29
28
  # @return [void]
30
- def modules=(module_names)
31
- @modules = module_names.reject do
32
- BLANK_RE.match?(_1)
33
- end.freeze
29
+ def module=(modulee)
30
+ @module = if modulee.nil? || BLANK_RE.match?(modulee)
31
+ nil
32
+ else
33
+ modulee.strip
34
+ end
34
35
  end
35
36
 
36
37
  # @return [Boolean]
37
- def modules?
38
- @modules.any?
38
+ def module?
39
+ !@module.nil?
39
40
  end
40
41
 
41
42
  # @return [Hash]
@@ -43,7 +44,7 @@ module DiverDown
43
44
  hash = {}
44
45
 
45
46
  hash[:memo] = memo unless memo == BLANK_MEMO
46
- hash[:modules] = modules unless modules.empty?
47
+ hash[:module] = @module unless @module.nil?
47
48
 
48
49
  hash
49
50
  end
@@ -54,7 +54,12 @@ module DiverDown
54
54
  # NOTE: This is for backward compatibility. It will be removed in the future.
55
55
  (loaded[:sources] || loaded || []).each do |source_name, source_hash|
56
56
  source(source_name).memo = source_hash[:memo] if source_hash[:memo]
57
- source(source_name).modules = source_hash[:modules] if source_hash[:modules]
57
+
58
+ if source_hash[:modules].is_a?(Array)
59
+ source(source_name).module = source_hash[:modules][0]
60
+ elsif source_hash[:module]
61
+ source(source_name).module = source_hash[:module]
62
+ end
58
63
  end
59
64
 
60
65
  loaded[:source_alias]&.each do |alias_name, source_names|
@@ -15,22 +15,28 @@ module DiverDown
15
15
  require 'diver_down/web/indented_string_io'
16
16
  require 'diver_down/web/definition_store'
17
17
  require 'diver_down/web/definition_loader'
18
+ require 'diver_down/web/definition_module_dependencies'
18
19
  require 'diver_down/web/source_alias_resolver'
19
- require 'diver_down/web/module_sources_filter'
20
+ require 'diver_down/web/definition_filter'
20
21
 
21
22
  # For development
22
23
  autoload :DevServerMiddleware, 'diver_down/web/dev_server_middleware'
23
24
 
25
+ # @return [DiverDown::Web::DefinitionStore]
26
+ def self.store
27
+ @store ||= DiverDown::Web::DefinitionStore.new
28
+ end
29
+
24
30
  # @param definition_dir [String]
25
31
  # @param metadata [DiverDown::Web::Metadata]
26
32
  # @param store [DiverDown::Web::DefinitionStore]
27
- def initialize(definition_dir:, metadata:, store: DiverDown::Web::DefinitionStore.new)
28
- @store = store
33
+ def initialize(definition_dir:, metadata:)
29
34
  @metadata = metadata
30
35
  @files_server = Rack::Files.new(File.join(WEB_DIR))
31
36
 
32
37
  definition_files = ::Dir["#{definition_dir}/**/*.{yml,yaml,msgpack,json}"].sort
33
38
  @total_definition_files_size = definition_files.size
39
+ @action = nil
34
40
 
35
41
  load_definition_files_on_thread(definition_files)
36
42
  end
@@ -39,11 +45,14 @@ module DiverDown
39
45
  # @return [Array[Integer, Hash, Array]]
40
46
  def call(env)
41
47
  request = Rack::Request.new(env)
42
- action = DiverDown::Web::Action.new(store: @store, metadata: @metadata, request:)
48
+
49
+ if @action&.store.object_id != self.class.store.object_id
50
+ @action = DiverDown::Web::Action.new(store: self.class.store, metadata: @metadata)
51
+ end
43
52
 
44
53
  case [request.request_method, request.path]
45
54
  in ['GET', %r{\A/api/definitions\.json\z}]
46
- action.definitions(
55
+ @action.definitions(
47
56
  page: request.params['page']&.to_i || 1,
48
57
  per: request.params['per']&.to_i || 100,
49
58
  title: request.params['title'] || '',
@@ -51,51 +60,48 @@ module DiverDown
51
60
  definition_group: request.params['definition_group'] || ''
52
61
  )
53
62
  in ['GET', %r{\A/api/sources\.json\z}]
54
- action.sources
63
+ @action.sources
55
64
  in ['GET', %r{\A/api/modules\.json\z}]
56
- action.modules
57
- in ['GET', %r{\A/api/modules/(?<module_names>.+)\.json\z}]
58
- module_names = CGI.unescape(Regexp.last_match[:module_names]).split('/')
59
- action.module(module_names)
60
- in ['GET', %r{\A/api/module_definitions/(?<modules>.+)\.json\z}]
61
- modules = CGI.unescape(Regexp.last_match[:modules]).split('/')
62
- compound = request.params['compound'] == '1'
63
- concentrate = request.params['concentrate'] == '1'
64
- only_module = request.params['only_module'] == '1'
65
- action.module_definition(compound, concentrate, only_module, modules)
65
+ @action.modules
66
+ in ['GET', %r{\A/api/modules/(?<modulee>[^/]+)\.json\z}]
67
+ modulee = CGI.unescape(Regexp.last_match[:modulee])
68
+ @action.module(modulee)
66
69
  in ['GET', %r{\A/api/definitions/(?<bit_id>\d+)\.json\z}]
67
70
  bit_id = Regexp.last_match[:bit_id].to_i
68
71
  compound = request.params['compound'] == '1'
69
72
  concentrate = request.params['concentrate'] == '1'
70
73
  only_module = request.params['only_module'] == '1'
71
- action.combine_definitions(bit_id, compound, concentrate, only_module)
74
+ remove_internal_sources = request.params['remove_internal_sources'] == '1'
75
+ focus_modules = request.params['focus_modules'] || []
76
+ modules = request.params['modules'] || []
77
+ @action.combine_definitions(bit_id, compound, concentrate, only_module, remove_internal_sources, focus_modules, modules)
72
78
  in ['GET', %r{\A/api/sources/(?<source>[^/]+)\.json\z}]
73
79
  source = Regexp.last_match[:source]
74
- action.source(source)
75
- in ['POST', %r{\A/api/sources/(?<source>[^/]+)/modules.json\z}]
80
+ @action.source(source)
81
+ in ['POST', %r{\A/api/sources/(?<source>[^/]+)/module.json\z}]
76
82
  source = Regexp.last_match[:source]
77
- modules = request.params['modules'] || []
78
- action.set_modules(source, modules)
83
+ modulee = request.params['module'] || ''
84
+ @action.set_module(source, modulee)
79
85
  in ['POST', %r{\A/api/sources/(?<source>[^/]+)/memo.json\z}]
80
86
  source = Regexp.last_match[:source]
81
87
  memo = request.params['memo'] || ''
82
- action.set_memo(source, memo)
88
+ @action.set_memo(source, memo)
83
89
  in ['GET', %r{\A/api/pid\.json\z}]
84
- action.pid
90
+ @action.pid
85
91
  in ['GET', %r{\A/api/initialization_status\.json\z}]
86
- action.initialization_status(@total_definition_files_size)
92
+ @action.initialization_status(@total_definition_files_size)
87
93
  in ['GET', %r{\A/api/source_aliases\.json\z}]
88
- action.source_aliases
94
+ @action.source_aliases
89
95
  in ['POST', %r{\A/api/source_aliases\.json\z}]
90
96
  alias_name = request.params['alias_name']
91
97
  old_alias_name = request.params['old_alias_name'] # NOTE: nillable
92
98
  source_names = request.params['source_names'] || []
93
99
 
94
- action.update_source_alias(alias_name, old_alias_name, source_names)
100
+ @action.update_source_alias(alias_name, old_alias_name, source_names)
95
101
  in ['GET', %r{\A/assets/}]
96
102
  @files_server.call(env)
97
103
  in ['GET', /\.json\z/], ['POST', /\.json\z/]
98
- action.not_found
104
+ @action.not_found
99
105
  else
100
106
  @files_server.call(env.merge('PATH_INFO' => '/index.html'))
101
107
  end
@@ -105,20 +111,29 @@ module DiverDown
105
111
 
106
112
  def load_definition_files_on_thread(definition_files)
107
113
  definition_loader = DiverDown::Web::DefinitionLoader.new
114
+ store = self.class.store
108
115
 
109
116
  Thread.new do
110
117
  loop do
111
118
  break if definition_files.empty?
112
119
 
120
+ # If store is changed in test, stop loading.
121
+ break if store != self.class.store
122
+
113
123
  definition_file = definition_files.shift
114
124
  definition = definition_loader.load_file(definition_file)
115
125
 
116
126
  # No needed to synchronize because this is executed on a single thread.
117
- @store.set(definition)
127
+ store.set(definition)
118
128
  end
119
129
 
120
130
  # Cache combined_definition
121
- @store.combined_definition
131
+ store.combined_definition
132
+
133
+ # For development
134
+ if ENV['DIVER_DOWN_DIR']
135
+ File.write(File.expand_path('../../tmp/combined_definition.yml', __dir__), store.combined_definition.to_h.to_yaml)
136
+ end
122
137
  end
123
138
  end
124
139
  end