diver_down 0.0.1.alpha14 → 0.0.1.alpha15

Sign up to get free protection for your applications and to get access to all the features.
@@ -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