diver_down 0.0.1.alpha1

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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +132 -0
  5. data/exe/diver_down_web +55 -0
  6. data/lib/diver_down/definition/dependency.rb +107 -0
  7. data/lib/diver_down/definition/method_id.rb +83 -0
  8. data/lib/diver_down/definition/source.rb +90 -0
  9. data/lib/diver_down/definition.rb +112 -0
  10. data/lib/diver_down/helper.rb +81 -0
  11. data/lib/diver_down/trace/call_stack.rb +45 -0
  12. data/lib/diver_down/trace/ignored_method_ids.rb +136 -0
  13. data/lib/diver_down/trace/module_set/array_module_set.rb +31 -0
  14. data/lib/diver_down/trace/module_set/const_source_location_module_set.rb +28 -0
  15. data/lib/diver_down/trace/module_set.rb +78 -0
  16. data/lib/diver_down/trace/redefine_ruby_methods.rb +64 -0
  17. data/lib/diver_down/trace/tracer/session.rb +121 -0
  18. data/lib/diver_down/trace/tracer.rb +96 -0
  19. data/lib/diver_down/trace.rb +27 -0
  20. data/lib/diver_down/version.rb +5 -0
  21. data/lib/diver_down/web/action.rb +344 -0
  22. data/lib/diver_down/web/bit_id.rb +41 -0
  23. data/lib/diver_down/web/definition_enumerator.rb +54 -0
  24. data/lib/diver_down/web/definition_loader.rb +37 -0
  25. data/lib/diver_down/web/definition_store.rb +89 -0
  26. data/lib/diver_down/web/definition_to_dot.rb +399 -0
  27. data/lib/diver_down/web/dev_server_middleware.rb +72 -0
  28. data/lib/diver_down/web/indented_string_io.rb +59 -0
  29. data/lib/diver_down/web/module_store.rb +59 -0
  30. data/lib/diver_down/web.rb +101 -0
  31. data/lib/diver_down-trace.rb +4 -0
  32. data/lib/diver_down-web.rb +4 -0
  33. data/lib/diver_down.rb +14 -0
  34. data/web/assets/CjLq7LhZ.css +1 -0
  35. data/web/assets/bundle.js +978 -0
  36. data/web/index.html +13 -0
  37. metadata +122 -0
@@ -0,0 +1,399 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'cgi'
5
+
6
+ module DiverDown
7
+ class Web
8
+ class DefinitionToDot
9
+ ATTRIBUTE_DELIMITER = ' '
10
+ MODULE_DELIMITER = '::'
11
+
12
+ # Between modules is prominently distanced
13
+ MODULE_MINLEN = 3
14
+
15
+ class MetadataStore
16
+ Metadata = Data.define(:id, :type, :data, :module_store) do
17
+ # @return [Hash]
18
+ def to_h
19
+ case type
20
+ when :source
21
+ source_to_h
22
+ when :dependency
23
+ dependency_to_h
24
+ when :module
25
+ module_to_h
26
+ else
27
+ raise NotImplementedError, "not implemented yet #{type}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def source_to_h
34
+ modules = module_store.get(data.source_name).map do
35
+ {
36
+ module_name: _1,
37
+ }
38
+ end
39
+
40
+ {
41
+ id:,
42
+ type: 'source',
43
+ source_name: data.source_name,
44
+ modules:,
45
+ }
46
+ end
47
+
48
+ def dependency_to_h
49
+ {
50
+ id:,
51
+ type: 'dependency',
52
+ dependencies: data.map do |dependency|
53
+ {
54
+ source_name: dependency.source_name,
55
+ method_ids: dependency.method_ids.sort.map do
56
+ {
57
+ name: _1.name,
58
+ context: _1.context,
59
+ }
60
+ end,
61
+ }
62
+ end,
63
+ }
64
+ end
65
+
66
+ def module_to_h
67
+ {
68
+ id:,
69
+ type: 'module',
70
+ modules: data.map do
71
+ {
72
+ module_name: _1,
73
+ }
74
+ end,
75
+ }
76
+ end
77
+ end
78
+
79
+ def initialize(module_store)
80
+ @prefix = 'graph_'
81
+ @module_store = module_store
82
+
83
+ # Hash{ id => Metadata }
84
+ @to_h = {}
85
+ end
86
+
87
+ # @param type [Symbol]
88
+ # @param record [DiverDown::Definition::Source]
89
+ # @return [String]
90
+ def issue_source_id(source)
91
+ build_metadata_and_return_id(:source, source)
92
+ end
93
+
94
+ # @param dependency [DiverDown::Definition::Dependency]
95
+ # @return [String]
96
+ def issue_dependency_id(dependency)
97
+ build_metadata_and_return_id(:dependency, [dependency])
98
+ end
99
+
100
+ # @param module_names [Array<String>]
101
+ # @return [String]
102
+ def issue_modules_id(module_names)
103
+ issued_metadata = @to_h.values.find { _1.type == :module && _1.data == module_names }
104
+
105
+ if issued_metadata
106
+ issued_metadata.id
107
+ else
108
+ build_metadata_and_return_id(:module, module_names)
109
+ end
110
+ end
111
+
112
+ # @param id [String]
113
+ # @param dependency [DiverDown::Definition::Dependency]
114
+ def append_dependency(id, dependency)
115
+ metadata = @to_h.fetch(id)
116
+ dependencies = metadata.data
117
+ combined_dependencies = DiverDown::Definition::Dependency.combine(*dependencies, dependency)
118
+ metadata.data.replace(combined_dependencies)
119
+ end
120
+
121
+ # @return [Array<Hash>]
122
+ def to_a
123
+ @to_h.values.map(&:to_h)
124
+ end
125
+
126
+ private
127
+
128
+ def build_metadata_and_return_id(type, data)
129
+ id = "#{@prefix}#{length + 1}"
130
+ metadata = Metadata.new(id:, type:, data:, module_store: @module_store)
131
+ @to_h[id] = metadata
132
+
133
+ id
134
+ end
135
+
136
+ def length
137
+ @to_h.length
138
+ end
139
+ end
140
+
141
+ # @param definition [DiverDown::Definition]
142
+ # @param module_store [DiverDown::ModuleStore]
143
+ # @param compound [Boolean]
144
+ # @param concentrate [Boolean] https://graphviz.org/docs/attrs/concentrate/
145
+ def initialize(definition, module_store, compound: false, concentrate: false, only_module: false)
146
+ @definition = definition
147
+ @module_store = module_store
148
+ @io = DiverDown::Web::IndentedStringIo.new
149
+ @indent = 0
150
+ @compound = compound || only_module # When only-module is enabled, dependencies between modules are displayed as compound.
151
+ @compound_map = Hash.new { |h, k| h[k] = {} } # Hash{ ltail => Hash{ lhead => issued id } }
152
+ @concentrate = concentrate
153
+ @only_module = only_module
154
+ @metadata_store = MetadataStore.new(module_store)
155
+ end
156
+
157
+ # @return [Array<Hash>]
158
+ def metadata
159
+ @metadata_store.to_a
160
+ end
161
+
162
+ # @return [String]
163
+ def to_s
164
+ io.puts %(strict digraph "#{definition.title}" {)
165
+ io.indented do
166
+ io.puts('compound=true') if @compound
167
+ io.puts('concentrate=true') if @concentrate
168
+
169
+ if @only_module
170
+ render_only_modules
171
+ else
172
+ definition.sources.sort_by(&:source_name).each do
173
+ insert_source(_1)
174
+ end
175
+ end
176
+ end
177
+ io.puts '}'
178
+ io.string
179
+ end
180
+
181
+ private
182
+
183
+ attr_reader :definition, :module_store, :io
184
+
185
+ def render_only_modules
186
+ # Hash{ from_module => { to_module => Array<DiverDown::Definition::Dependency> } }
187
+ dependency_map = Hash.new { |h, k| h[k] = Hash.new { |hi, ki| hi[ki] = [] } }
188
+
189
+ definition.sources.sort_by(&:source_name).each do |source|
190
+ source_modules = module_store.get(source.source_name)
191
+ next if source_modules.empty?
192
+
193
+ source.dependencies.each do |dependency|
194
+ dependency_modules = module_store.get(dependency.source_name)
195
+ next if dependency_modules.empty?
196
+
197
+ dependency_map[source_modules][dependency_modules].push(dependency)
198
+ end
199
+ end
200
+
201
+ # Remove duplicated prefix modules
202
+ # from [["A"], ["A", "B"]] to [["A", "B"]]
203
+ uniq_modules = [*dependency_map.keys, *dependency_map.values.map(&:keys).flatten(1)].uniq
204
+ uniq_modules.reject! do |modules|
205
+ modules.empty? ||
206
+ uniq_modules.any? { _1[0..modules.size - 1] == modules && _1.length > modules.size }
207
+ end
208
+
209
+ uniq_modules.each do |specific_module_names|
210
+ buf = swap_io do
211
+ indexes = (0..(specific_module_names.length - 1)).to_a
212
+
213
+ chain_yield(indexes) do |index, next_proc|
214
+ module_names = specific_module_names[0..index]
215
+ module_name = specific_module_names[index]
216
+
217
+ io.puts %(subgraph "#{module_label(module_names)}" {)
218
+ io.indented do
219
+ io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
220
+ io.puts %(label="#{module_name}")
221
+ io.puts %("#{module_name}" #{build_attributes(label: module_name, id: @metadata_store.issue_modules_id(module_names))})
222
+
223
+ next_proc&.call
224
+ end
225
+ io.puts '}'
226
+ end
227
+ end
228
+
229
+ io.write buf.string
230
+ end
231
+
232
+ dependency_map.each do |from_modules, h|
233
+ h.each do |to_modules, all_dependencies|
234
+ # Do not render standalone source
235
+ # Do not render self-dependency
236
+ next if from_modules.empty? || to_modules.empty? || from_modules == to_modules
237
+
238
+ dependencies = DiverDown::Definition::Dependency.combine(*all_dependencies)
239
+
240
+ dependencies.each do
241
+ attributes = {}
242
+ ltail = module_label(*from_modules)
243
+ lhead = module_label(*to_modules)
244
+
245
+ # Already rendered dependencies between modules
246
+ # Add the dependency to the edge of the compound
247
+ if @compound_map[ltail].include?(lhead)
248
+ compound_id = @compound_map[ltail][lhead]
249
+ @metadata_store.append_dependency(compound_id, _1)
250
+ next
251
+ end
252
+
253
+ compound_id = @metadata_store.issue_dependency_id(_1)
254
+ @compound_map[ltail][lhead] = compound_id
255
+
256
+ attributes.merge!(
257
+ id: compound_id,
258
+ ltail:,
259
+ lhead:,
260
+ minlen: MODULE_MINLEN
261
+ )
262
+
263
+ io.write(%("#{from_modules[-1]}" -> "#{to_modules[-1]}"))
264
+ io.write(%( #{build_attributes(**attributes)}), indent: false) unless attributes.empty?
265
+ io.write("\n")
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ def insert_source(source)
272
+ if module_store.get(source.source_name).empty?
273
+ io.puts %("#{source.source_name}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
274
+ else
275
+ insert_modules(source)
276
+ end
277
+
278
+ source.dependencies.each do
279
+ attributes = {}
280
+ ltail = module_label(*module_store.get(source.source_name))
281
+ lhead = module_label(*module_store.get(_1.source_name))
282
+
283
+ if @compound && (ltail || lhead)
284
+ # Rendering of dependencies between modules is done only once
285
+ between_modules = ltail != lhead
286
+
287
+ # Already rendered dependencies between modules
288
+ # Add the dependency to the edge of the compound
289
+ if between_modules && @compound_map[ltail].include?(lhead)
290
+ compound_id = @compound_map[ltail][lhead]
291
+ @metadata_store.append_dependency(compound_id, _1)
292
+ next
293
+ end
294
+
295
+ compound_id = @metadata_store.issue_dependency_id(_1)
296
+ @compound_map[ltail][lhead] = compound_id
297
+
298
+ attributes.merge!(
299
+ id: compound_id,
300
+ ltail:,
301
+ lhead:,
302
+ minlen: MODULE_MINLEN
303
+ )
304
+ else
305
+ attributes.merge!(
306
+ id: @metadata_store.issue_dependency_id(_1)
307
+ )
308
+ end
309
+
310
+ io.write(%("#{source.source_name}" -> "#{_1.source_name}"))
311
+ io.write(%( #{build_attributes(**attributes)}), indent: false) unless attributes.empty?
312
+ io.write("\n")
313
+ end
314
+ end
315
+
316
+ def insert_modules(source)
317
+ buf = swap_io do
318
+ all_module_names = module_store.get(source.source_name)
319
+ indexes = (0..(all_module_names.length - 1)).to_a
320
+
321
+ chain_yield(indexes) do |index, next_proc|
322
+ module_names = all_module_names[0..index]
323
+ module_name = module_names[-1]
324
+
325
+ io.puts %(subgraph "#{module_label(module_names)}" {)
326
+ io.indented do
327
+ io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
328
+ io.puts %(label="#{module_name}")
329
+
330
+ if next_proc
331
+ next_proc.call
332
+ else
333
+ # last. equals indexes[-1] == index
334
+ io.puts %("#{source.source_name}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
335
+ end
336
+ end
337
+ io.puts '}'
338
+ end
339
+ end
340
+
341
+ io.write buf.string
342
+ end
343
+
344
+ def chain_yield(values, &block)
345
+ *head, tail = values
346
+
347
+ last_proc = proc do
348
+ block.call(tail, nil)
349
+ end
350
+
351
+ chain_proc = head.inject(last_proc) do |next_proc, value|
352
+ proc do
353
+ block.call(value, next_proc)
354
+ end
355
+ end
356
+
357
+ chain_proc.call
358
+ end
359
+
360
+ # rubocop:disable Lint/UnderscorePrefixedVariableName
361
+ # attrsの参考 https://qiita.com/rubytomato@github/items/51779135bc4b77c8c20d
362
+ def build_attributes(_wrap: '[]', **attrs)
363
+ attrs = attrs.reject { _2.nil? || _2 == '' }
364
+ return if attrs.empty?
365
+
366
+ attrs_str = attrs.map { %(#{_1}="#{_2}") }.join(ATTRIBUTE_DELIMITER)
367
+
368
+ if _wrap
369
+ "#{_wrap[0]}#{attrs_str}#{_wrap[1]}"
370
+ else
371
+ attrs_str
372
+ end
373
+ end
374
+ # rubocop:enable Lint/UnderscorePrefixedVariableName
375
+
376
+ def increase_indent
377
+ @indent += 1
378
+ yield
379
+ ensure
380
+ @indent -= 1
381
+ end
382
+
383
+ def swap_io
384
+ old_io = @io
385
+ @io = IndentedStringIo.new
386
+ yield
387
+ @io
388
+ ensure
389
+ @io = old_io
390
+ end
391
+
392
+ def module_label(*modules)
393
+ return if modules.empty?
394
+
395
+ "cluster_#{modules.join(MODULE_DELIMITER)}"
396
+ end
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/proxy'
4
+ require 'websocket/driver'
5
+ require 'eventmachine'
6
+
7
+ module DiverDown
8
+ class Web
9
+ # For vite
10
+ class DevServerMiddleware
11
+ class HttpProxy < ::Rack::Proxy
12
+ def initialize(_app = nil, host:, port:)
13
+ @host = host
14
+ @port = port
15
+
16
+ super(nil, backend: "http://#{@host}:#{@port}", proxy_host: @host, proxy_port: @port, proxy_scheme: 'http')
17
+ end
18
+ end
19
+
20
+ class WebSocketProxy
21
+ attr_reader :env, :url
22
+
23
+ def initialize(env, host:, port:)
24
+ @env = env
25
+ @url = "ws://#{host}:#{port}#{env['REQUEST_URI']}"
26
+ @driver = WebSocket::Driver.rack(self)
27
+
28
+ env['rack.hijack'].call
29
+ @io = env['rack.hijack_io']
30
+
31
+ EM.attach(@io, Reader) { |conn| conn.driver = @driver }
32
+
33
+ @driver.start
34
+ end
35
+
36
+ # @param string [String]
37
+ def write(string)
38
+ @io.write(string)
39
+ end
40
+
41
+ module Reader
42
+ attr_writer :driver
43
+
44
+ # @param string [String]
45
+ def receive_data(string)
46
+ @driver.parse(string)
47
+ end
48
+ end
49
+ end
50
+
51
+ def initialize(app, host:, port:)
52
+ @app = app
53
+ @host = host
54
+ @port = port
55
+ @http_proxy = HttpProxy.new(@app, host: @host, port: @port)
56
+ end
57
+
58
+ # @param env [Hash]
59
+ def call(env)
60
+ request = Rack::Request.new(env)
61
+
62
+ if WebSocket::Driver.websocket?(env)
63
+ WebSocketProxy.new(env, host: @host, port: @port)
64
+ elsif request.path.start_with?('/api')
65
+ @app.call(env)
66
+ else
67
+ @http_proxy.call(env)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require 'forwardable'
5
+
6
+ module DiverDown
7
+ class Web
8
+ class IndentedStringIo
9
+ extend ::Forwardable
10
+
11
+ def_delegators :@io, :rewind, :string
12
+
13
+ attr_accessor :indent
14
+
15
+ # @param tab [String]
16
+ def initialize(tab: ' ')
17
+ @io = StringIO.new
18
+ @indent = 0
19
+ @tab = tab
20
+ end
21
+
22
+ # @param contents [Array<String>]
23
+ # @param indent [Boolean] Enable or disable indentation
24
+ # @return [void]
25
+ def write(*contents, indent: true)
26
+ indent_string = if indent
27
+ @tab * @indent
28
+ else
29
+ ''
30
+ end
31
+
32
+ string = contents.join
33
+ lines = string.lines
34
+ lines.each do |line|
35
+ if line == "\n"
36
+ @io.write "\n"
37
+ else
38
+ @io.write "#{indent_string}#{line}"
39
+ end
40
+ end
41
+ end
42
+
43
+ # @param content [String]
44
+ # @return [void]
45
+ def puts(*contents, indent: true)
46
+ write("#{contents.join("\n")}\n", indent:)
47
+ nil
48
+ end
49
+
50
+ # increase the indent level for the block
51
+ def indented
52
+ @indent += 1
53
+ yield
54
+ ensure
55
+ @indent -= 1
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module DiverDown
6
+ class Web
7
+ class ModuleStore
8
+ BLANK_ARRAY = [].freeze
9
+ BLANK_RE = /\A\s*\z/
10
+
11
+ private_constant(:BLANK_RE)
12
+
13
+ def initialize(path)
14
+ @path = path
15
+ @store = load
16
+ end
17
+
18
+ # @param source_name [String]
19
+ # @param module_names [Array<String>]
20
+ def set(source_name, module_names)
21
+ @store[source_name] = module_names.dup.reject do
22
+ BLANK_RE.match?(_1)
23
+ end.freeze
24
+ end
25
+
26
+ # @param source_name [String]
27
+ # @return [Array<Module>]
28
+ def get(source_name)
29
+ @store[source_name] || BLANK_ARRAY
30
+ end
31
+
32
+ # @return [Hash]
33
+ def to_h
34
+ @store.dup
35
+ end
36
+
37
+ # Write store to file
38
+ # @return [void]
39
+ def flush
40
+ File.write(@path, to_h.to_yaml)
41
+ end
42
+
43
+ private
44
+
45
+ def load
46
+ store = {}
47
+
48
+ begin
49
+ loaded = YAML.load_file(@path)
50
+ store.merge!(loaded) if loaded
51
+ rescue StandardError
52
+ # Ignore error
53
+ end
54
+
55
+ store
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+ require 'yaml'
5
+
6
+ module DiverDown
7
+ class Web
8
+ WEB_DIR = File.expand_path('../../web', __dir__)
9
+
10
+ require 'diver_down/web/action'
11
+ require 'diver_down/web/definition_to_dot'
12
+ require 'diver_down/web/definition_enumerator'
13
+ require 'diver_down/web/bit_id'
14
+ require 'diver_down/web/module_store'
15
+ require 'diver_down/web/indented_string_io'
16
+ require 'diver_down/web/definition_store'
17
+ require 'diver_down/web/definition_loader'
18
+
19
+ # For development
20
+ autoload :DevServerMiddleware, 'diver_down/web/dev_server_middleware'
21
+
22
+ # @param definition_dir [String]
23
+ # @param module_store [DiverDown::ModuleStore]
24
+ # @param store [DiverDown::Web::DefinitionStore]
25
+ def initialize(definition_dir:, module_store:, store: DiverDown::Web::DefinitionStore.new)
26
+ @store = store
27
+ @module_store = module_store
28
+ @files_server = Rack::Files.new(File.join(WEB_DIR))
29
+
30
+ definition_files = ::Dir["#{definition_dir}/**/*.{yml,yaml,msgpack,json}"].sort
31
+ @total_definition_files_size = definition_files.size
32
+
33
+ load_definition_files_on_thread(definition_files)
34
+ end
35
+
36
+ # @param env [Hash]
37
+ # @return [Array[Integer, Hash, Array]]
38
+ def call(env)
39
+ request = Rack::Request.new(env)
40
+ action = DiverDown::Web::Action.new(store: @store, module_store: @module_store, request:)
41
+
42
+ case [request.request_method, request.path]
43
+ in ['GET', %r{\A/api/definitions\.json\z}]
44
+ action.definitions(
45
+ page: request.params['page']&.to_i || 1,
46
+ per: request.params['per']&.to_i || 100,
47
+ title: request.params['title'] || '',
48
+ source: request.params['source'] || ''
49
+ )
50
+ in ['GET', %r{\A/api/sources\.json\z}]
51
+ action.sources
52
+ in ['GET', %r{\A/api/modules\.json\z}]
53
+ action.modules
54
+ in ['GET', %r{\A/api/modules/(?<module_names>.+)\.json\z}]
55
+ module_names = Regexp.last_match[:module_names].split('/')
56
+ action.module(module_names)
57
+ in ['GET', %r{\A/api/definitions/(?<bit_id>\d+)\.json\z}]
58
+ bit_id = Regexp.last_match[:bit_id].to_i
59
+ compound = request.params['compound'] == '1'
60
+ concentrate = request.params['concentrate'] == '1'
61
+ only_module = request.params['only_module'] == '1'
62
+ action.combine_definitions(bit_id, compound, concentrate, only_module)
63
+ in ['GET', %r{\A/api/sources/(?<source>[^/]+)\.json\z}]
64
+ source = Regexp.last_match[:source]
65
+ action.source(source)
66
+ in ['POST', %r{\A/api/sources/(?<source>[^/]+)/modules.json\z}]
67
+ source = Regexp.last_match[:source]
68
+ modules = request.params['modules'] || []
69
+ action.set_modules(source, modules)
70
+ in ['GET', %r{\A/api/pid\.json\z}]
71
+ action.pid
72
+ in ['GET', %r{\A/api/initialization_status\.json\z}]
73
+ action.initialization_status(@total_definition_files_size)
74
+ in ['GET', %r{\A/assets/}]
75
+ @files_server.call(env)
76
+ in ['GET', /\.json\z/], ['POST', /\.json\z/]
77
+ action.not_found
78
+ else
79
+ @files_server.call(env.merge('PATH_INFO' => '/index.html'))
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def load_definition_files_on_thread(definition_files)
86
+ definition_loader = DiverDown::Web::DefinitionLoader.new
87
+
88
+ Thread.new do
89
+ loop do
90
+ break if definition_files.empty?
91
+
92
+ definition_file = definition_files.shift
93
+ definition = definition_loader.load_file(definition_file)
94
+
95
+ # No needed to synchronize because this is executed on a single thread.
96
+ @store.set(definition)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'diver_down'
4
+ require 'diver_down/trace'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'diver_down'
4
+ require 'diver_down/web'