racer-rb 0.1.0

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.
@@ -0,0 +1,645 @@
1
+ require "rbs"
2
+ require "fileutils"
3
+ require "yaml"
4
+
5
+ module Racer::Collectors
6
+ class RBSCollector
7
+ module EnsureValidConstantName
8
+ def self.valid_name(name)
9
+ return unless name
10
+
11
+ name.split("::").map do |fragment|
12
+ fragment[0].upcase.concat(fragment[1..])
13
+ end.join("::")
14
+ end
15
+
16
+ def ensure_valid_name!
17
+ return if defined?(@original_name)
18
+ @original_name = @name
19
+ @name = EnsureValidConstantName.valid_name(@name)
20
+ end
21
+
22
+ refine Racer::Trace::Constant do
23
+ import_methods EnsureValidConstantName
24
+
25
+ def ensure_valid_names!
26
+ ensure_valid_name!
27
+
28
+ @superclass = EnsureValidConstantName.valid_name(@superclass)
29
+ @included_modules = @included_modules.map { EnsureValidConstantName.valid_name(_1) }
30
+ @prepended_modules = @prepended_modules.map { EnsureValidConstantName.valid_name(_1) }
31
+ @extended_modules = @extended_modules.map { EnsureValidConstantName.valid_name(_1) }
32
+ end
33
+ end
34
+
35
+ refine Racer::Trace::ConstantInstance do
36
+ import_methods EnsureValidConstantName
37
+ end
38
+ end
39
+ using EnsureValidConstantName
40
+
41
+ module EnsureValidMethodName
42
+ refine Racer::Trace do
43
+ def valid_method_name?
44
+ method_name.match?(
45
+ /\A([a-zA-Z_][a-zA-Z0-9_]*[!?=]?|\+|\-|\*|\/|%|\*\*|==|!=|===|<=>|<=|>=|<|>|\<\<|\>\>|\&|\||\^|~|!|`|=~|!~|\[\]|\[\]=)\z/
46
+ )
47
+ end
48
+ end
49
+ end
50
+ using EnsureValidMethodName
51
+
52
+ module FilterMultiEntryMembers
53
+ refine RBS::Environment::MultiEntry do
54
+ def members(of:)
55
+ decls.flat_map do |decl|
56
+ decl.decl.members.select { _1.is_a?(of) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ using FilterMultiEntryMembers
62
+
63
+ def initialize(libraries: [])
64
+ @results = {}
65
+
66
+ loader = RBS::EnvironmentLoader.new
67
+ libraries.each { loader.add(library: _1) }
68
+
69
+ rbs_collection_config = Pathname.new("rbs_collection.yaml")
70
+ if rbs_collection_config.exist?
71
+ lockfile_path = RBS::Collection::Config.to_lockfile_path(rbs_collection_config)
72
+ lockfile_content = YAML.load_file(lockfile_path)
73
+ lockfile = RBS::Collection::Config::Lockfile.from_lockfile(lockfile_path:, data: lockfile_content)
74
+ loader.add_collection(lockfile)
75
+ end
76
+
77
+ @environment = RBS::Environment.from_loader(loader).resolve_type_names
78
+ @definition_builder = RBS::DefinitionBuilder.new(env: @environment)
79
+
80
+ @existing_types = {}
81
+ # Modules that are included or prepended to the Object class
82
+ # need to have a self type that is not Object (for example BasicObject)
83
+ @modules_in_object_class = Set.new
84
+ end
85
+
86
+ def collect(trace)
87
+ trace.constant_updates.each do |constant|
88
+ constant.ensure_valid_names!
89
+
90
+ push_constant_to_results(constant)
91
+ end
92
+
93
+ unless trace.valid_method_name?
94
+ return
95
+ end
96
+
97
+ method_type_key =
98
+ case trace.method_kind
99
+ when :instance
100
+ :instance_methods
101
+ when :singleton
102
+ :singleton_methods
103
+ end
104
+
105
+ owner = trace.method_callee || trace.method_owner
106
+
107
+ @results[owner.name][method_type_key][trace.method_name] ||= []
108
+
109
+ @results[owner.name][method_type_key][trace.method_name].each do |traces|
110
+ if traces.params == trace.params && traces.return_type == trace.return_type && traces.block_param == trace.block_param
111
+ return
112
+ end
113
+ end
114
+
115
+ @results[owner.name][method_type_key][trace.method_name] << trace
116
+ end
117
+
118
+ def stop(path: "sig/generated")
119
+ FileUtils.rm_r(path) if File.directory?(path)
120
+ @results.each do |owner_name, owner|
121
+ owner => { constant:, instance_methods:, singleton_methods: }
122
+
123
+ declarations =
124
+ [
125
+ case constant.type
126
+ when :class
127
+ to_class_declaration(constant, instance_methods, singleton_methods)
128
+ when :module
129
+ to_module_declaration(constant, instance_methods, singleton_methods)
130
+ else
131
+ puts "Unknown owner type #{type} (#{instance_methods}, #{singleton_methods})"
132
+ end
133
+ ].compact
134
+
135
+ # Skip writing for invalid owners and if the owner has no members and already exists as RBS
136
+ if declarations.empty? || (declarations.first.members.empty? && @existing_types.key?(owner_name))
137
+ next
138
+ end
139
+
140
+ filename = "#{path}/#{owner_name.split("::").map { underscore(_1) }.join("/")}.rbs"
141
+ dirname = File.dirname(filename)
142
+ unless File.directory?(dirname)
143
+ FileUtils.mkdir_p(dirname)
144
+ end
145
+
146
+ io = File.open(filename, "w")
147
+ writer = RBS::Writer.new(out: io)
148
+ writer.write(declarations)
149
+ io.close
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def underscore(camel_cased_word)
156
+ return camel_cased_word.to_s.dup unless /[A-Z-]|::/.match?(camel_cased_word)
157
+ word = camel_cased_word.to_s.gsub("::", "/")
158
+ word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
159
+ word.tr!("-", "_")
160
+ word.downcase!
161
+ word
162
+ end
163
+
164
+ def push_constant_to_results(constant)
165
+ return if @results.key?(constant.name)
166
+ return if @existing_types.key?(constant.name)
167
+
168
+ type_name = to_type_name(constant.name)
169
+
170
+ class_decl = @environment.class_decls[type_name]
171
+ if class_decl
172
+ @existing_types[constant.name] = { class_decl:, type_name: }
173
+
174
+ included_modules = Set.new(class_decl.members(of: RBS::AST::Members::Include).map(&:name))
175
+ constant.included_modules.reject! do |module_name|
176
+ included_modules.include?(to_type_name(module_name))
177
+ end
178
+
179
+ prepended_modules = Set.new(class_decl.members(of: RBS::AST::Members::Prepend).map(&:name))
180
+ constant.prepended_modules.reject! do |module_name|
181
+ prepended_modules.include?(to_type_name(module_name))
182
+ end
183
+
184
+ extended_modules = Set.new(class_decl.members(of: RBS::AST::Members::Extend).map(&:name))
185
+ constant.extended_modules.reject! do |module_name|
186
+ extended_modules.include?(to_type_name(module_name))
187
+ end
188
+ end
189
+
190
+ if constant.name == "Object"
191
+ constant.included_modules.each do |module_name|
192
+ @modules_in_object_class.add(module_name)
193
+ end
194
+
195
+ constant.prepended_modules.each do |module_name|
196
+ @modules_in_object_class.add(module_name)
197
+ end
198
+ end
199
+
200
+ @results[constant.name] = { constant:, instance_methods: {}, singleton_methods: {} }
201
+ end
202
+
203
+ def find_self_types(owner)
204
+ self_types = []
205
+ if @modules_in_object_class.include?(owner.name)
206
+ self_types << RBS::AST::Declarations::Module::Self.new(name: to_type_name("BasicObject"), args: [], location: nil)
207
+ end
208
+
209
+ owner.included_modules.each do |module_name|
210
+ constant = @results[module_name][:constant]
211
+ if (existing_type = @existing_types[module_name])
212
+ unless existing_type[:class_decl].self_types.empty?
213
+ self_types.concat(existing_types[:class_decl].self_types)
214
+ end
215
+ end
216
+
217
+ self_types.concat(find_self_types(constant))
218
+ end
219
+
220
+ self_types
221
+ end
222
+
223
+ def to_module_declaration(owner, instance_methods, singleton_methods)
224
+ self_types = find_self_types(owner)
225
+
226
+ RBS::AST::Declarations::Module.new(
227
+ name: to_type_name(owner.name),
228
+ type_params: type_params_of_existing_class(owner.name),
229
+ members: to_module_members(owner, instance_methods, singleton_methods),
230
+ annotations: [],
231
+ self_types:,
232
+ location: nil,
233
+ comment: nil
234
+ )
235
+ end
236
+
237
+ def to_class_declaration(owner, instance_methods, singleton_methods)
238
+ super_class =
239
+ if owner.superclass
240
+ existing_type = @existing_types[owner.name]
241
+
242
+ if existing_type
243
+ existing_type[:class_decl].primary.decl.super_class
244
+ else
245
+ RBS::AST::Declarations::Class::Super.new(
246
+ name: to_type_name(owner.superclass),
247
+ args: generic_arguments_of_class(owner.superclass),
248
+ location: nil
249
+ )
250
+ end
251
+ end
252
+
253
+ RBS::AST::Declarations::Class.new(
254
+ name: to_type_name(owner.name),
255
+ type_params: type_params_of_existing_class(owner.name),
256
+ super_class:,
257
+ members: to_module_members(owner, instance_methods, singleton_methods),
258
+ annotations: [],
259
+ location: nil,
260
+ comment: nil
261
+ )
262
+ end
263
+
264
+ def to_module_members(constant, instance_methods, singleton_methods)
265
+ [
266
+ *to_mixin_definitions(constant.extended_modules, :extend),
267
+ *to_mixin_definitions(constant.prepended_modules, :prepend),
268
+ *to_mixin_definitions(constant.included_modules, :include),
269
+ *to_method_definitions(instance_methods, singleton_methods),
270
+ *required_interface_definitions(constant, instance_methods, singleton_methods)
271
+ ]
272
+ end
273
+
274
+ def required_interface_definitions(constant, instance_methods, singleton_methods)
275
+ required_interface_members = ->(modules, existing_methods, kind) do
276
+ needed_methods = {}
277
+
278
+ existing_type = @existing_types[constant.name]
279
+
280
+ instance =
281
+ if existing_type
282
+ case kind
283
+ when :instance
284
+ @definition_builder.build_instance(existing_type[:type_name])
285
+ when :singleton
286
+ @definition_builder.build_singleton(existing_type[:type_name])
287
+ end
288
+ end
289
+
290
+ modules.each do |module_name|
291
+ module_existing_type = @existing_types[module_name]
292
+ next unless module_existing_type
293
+
294
+ type_name = module_existing_type[:type_name]
295
+
296
+ ancestors = @definition_builder.ancestor_builder.one_instance_ancestors(type_name)
297
+ ancestors.each_self_type do |self_type|
298
+ next unless @environment.interface_name?(self_type.name)
299
+
300
+ interface_definition = @definition_builder.build_interface(self_type.name)
301
+ needed_methods.merge!(interface_definition.methods)
302
+ end
303
+ end
304
+
305
+ needed_methods.filter_map do |name, definition|
306
+ next if existing_methods.key?(name.to_s)
307
+ next if instance && instance.methods[name]&.implemented_in
308
+
309
+ defined_member = definition.defs.first.member
310
+ overload = defined_member.overloads.first
311
+ method_type = overload.method_type.map_type { RBS::Types::Bases::Any.new(location: nil) }
312
+
313
+ defined_member.update(kind:, overloads: [overload.update(method_type:)])
314
+ end
315
+ end
316
+
317
+ [
318
+ *required_interface_members.([*constant.prepended_modules, *constant.included_modules], instance_methods, :instance),
319
+ *required_interface_members.(constant.extended_modules, singleton_methods, :singleton)
320
+ ]
321
+ end
322
+
323
+ def to_method_definitions(instance_methods, singleton_methods)
324
+ instance_methods.map do |name, overloads|
325
+ to_method_definition(name, :instance, overloads)
326
+ end.concat(
327
+ singleton_methods.map do |name, overloads|
328
+ to_method_definition(name, :singleton, overloads)
329
+ end
330
+ ).compact
331
+ end
332
+
333
+ def method_defined?(method_name, method_kind, owner_name)
334
+ existing_type = @existing_types[owner_name]
335
+
336
+ if existing_type
337
+ methods =
338
+ case method_kind
339
+ when :instance
340
+ existing_type[:instance] ||= @definition_builder.build_instance(existing_type[:type_name])
341
+ when :singleton
342
+ existing_type[:singleton] ||= @definition_builder.build_singleton(existing_type[:type_name])
343
+ end.methods
344
+
345
+ methods.key?(method_name.to_sym)
346
+ else
347
+ false
348
+ end
349
+ end
350
+
351
+ def to_method_definition(name, kind, traces)
352
+ first_trace = traces.first
353
+
354
+ # This is not ideal as we miss redefined core methods (for example if redefining Integer#+).
355
+ # Howver in most cases this does not happend and we instead want to keep the original type definition to have more
356
+ # correct signatures (for example Object#tap returns self instead of a chain of union types).
357
+ return if method_defined?(name, kind, first_trace.method_owner.name)
358
+
359
+ # We add methods to the method callee if present. In this case we still check if the callee
360
+ # defines this method already. As the callee is not equal to the owner, the method signature might
361
+ # have changed from the implemented method on the callee, so we overload in this case.
362
+ overloading =
363
+ if first_trace.method_callee
364
+ method_defined?(name, kind, first_trace.method_callee.name)
365
+ else
366
+ false
367
+ end
368
+
369
+ # It could totally be that a method was public when called the first time and private
370
+ # the next time. We cannot depict such a case using RBS.
371
+ visibility = traces.last.method_visibility
372
+
373
+ overloads = {}
374
+
375
+ traces.map do |trace|
376
+ key = [trace.params]
377
+ if trace.block_param
378
+ params = trace.block_param.traces.first&.params || []
379
+ key << params.map { [_1.name, _1.type] }
380
+ end
381
+
382
+ overloads[key] ||= []
383
+ overloads[key] << trace
384
+ end
385
+
386
+ return_type =
387
+ if name == "initialize"
388
+ RBS::Types::Bases::Void.new(location: nil)
389
+ end
390
+
391
+ RBS::AST::Members::MethodDefinition.new(
392
+ name: name.to_sym,
393
+ kind:,
394
+ overloads: overloads.map do |(params, *), overload_traces|
395
+ block_params = overload_traces.filter_map(&:block_param)
396
+
397
+ unless block_params.empty?
398
+ block_traces = block_params.flat_map(&:traces)
399
+ param_sets = block_traces.map(&:params)
400
+ unless param_sets.empty?
401
+ size = param_sets.first.size
402
+ if param_sets.any? { _1.size != size }
403
+ warn "block params different for #{overload_traces}"
404
+ end
405
+ end
406
+ end
407
+
408
+ RBS::AST::Members::MethodDefinition::Overload.new(
409
+ method_type: RBS::MethodType.new(
410
+ type_params: [],
411
+ type: RBS::Types::Function.new(
412
+ **method_parameters(params),
413
+ return_type: return_type || to_rbs_type(*overload_traces.map(&:return_type))
414
+ ),
415
+ block: block_params.empty? ? nil : to_block(block_params),
416
+ location: nil
417
+ ),
418
+ annotations: []
419
+ )
420
+ end,
421
+ annotations: [],
422
+ overloading:,
423
+ location: nil,
424
+ comment: nil,
425
+ # We do not use visibility sections so declare all methods that are not private
426
+ # without visibility to mark them as "public".
427
+ # Protected methods are not supported by RBS yet.
428
+ visibility: visibility == :private ? :private : nil
429
+ )
430
+ end
431
+
432
+ def method_parameters(*param_sets)
433
+ {
434
+ required_positionals: [],
435
+ optional_positionals: [],
436
+ trailing_positionals: [],
437
+ rest_positionals: nil,
438
+ required_keywords: {},
439
+ optional_keywords: {},
440
+ rest_keywords: nil
441
+ }.tap do |parameters|
442
+ size = param_sets.first.size
443
+ if param_sets.any? { _1.size != size }
444
+ warn "Received param sets with different sizes #{param_sets}"
445
+ next
446
+ end
447
+
448
+ size.times do |n|
449
+ # TODO-Racer: Rethink the data structure here...
450
+ type = param_sets.first[n].type
451
+ name = param_sets.first[n].name
452
+ types = param_sets.map { _1[n].type_name }
453
+ generic_arguments = types.first.generic_arguments
454
+
455
+ case type
456
+ when :required, :optional
457
+ rbs_param =
458
+ RBS::Types::Function::Param.new(
459
+ type: to_rbs_type(*types),
460
+ name:
461
+ )
462
+
463
+ if type == :required
464
+ if parameters[:rest_positionals]
465
+ parameters[:trailing_positionals] << rbs_param
466
+ else
467
+ parameters[:required_positionals] << rbs_param
468
+ end
469
+ else
470
+ parameters[:optional_positionals] << rbs_param
471
+ end
472
+ when :rest
473
+ type =
474
+ if generic_arguments.size == 1
475
+ to_rbs_type(*generic_arguments[0])
476
+ else
477
+ RBS::Types::Bases::Any.new(location: nil)
478
+ end
479
+
480
+ parameters[:rest_positionals] =
481
+ RBS::Types::Function::Param.new(
482
+ type:,
483
+ name: name == :* ? nil : name
484
+ )
485
+ when :keyword_required, :keyword_optional
486
+ rbs_param =
487
+ RBS::Types::Function::Param.new(
488
+ type: to_rbs_type(*types),
489
+ name: nil
490
+ )
491
+
492
+ if type == :keyword_required
493
+ parameters[:required_keywords][name] = rbs_param
494
+ else
495
+ parameters[:optional_keywords][name] = rbs_param
496
+ end
497
+ when :keyword_rest
498
+ type =
499
+ if generic_arguments.size == 2
500
+ to_rbs_type(*generic_arguments[1])
501
+ else
502
+ RBS::Types::Bases::Any.new(location: nil)
503
+ end
504
+
505
+ parameters[:rest_keywords] =
506
+ RBS::Types::Function::Param.new(
507
+ type:,
508
+ name: name == :** ? nil : name
509
+ )
510
+ end
511
+ end
512
+ end
513
+ end
514
+
515
+ # Converts block parameters of a method to a block type
516
+ # Note: RBS does not support blocks that get other blocks passed so we cannot document
517
+ # "nested" block params.
518
+ def to_block(block_params)
519
+ required = block_params.any? { !_1.traces.empty? }
520
+
521
+ traces = block_params.flat_map(&:traces)
522
+
523
+ function =
524
+ if traces.empty?
525
+ RBS::Types::UntypedFunction.new(return_type: RBS::Types::Bases::Any.new(location: nil))
526
+ else
527
+ RBS::Types::Function.new(
528
+ **method_parameters(*traces.map(&:params)),
529
+ return_type: to_rbs_type(*traces.map(&:return_type))
530
+ )
531
+ end
532
+
533
+ self_types = traces.filter_map(&:self_type)
534
+
535
+ RBS::Types::Block.new(
536
+ type: function,
537
+ self_type: self_types.empty? ? nil : to_rbs_type(*self_types),
538
+ required:
539
+ )
540
+ end
541
+
542
+ def to_mixin_definitions(modules, type)
543
+ klass =
544
+ case type
545
+ when :extend
546
+ RBS::AST::Members::Extend
547
+ when :prepend
548
+ RBS::AST::Members::Prepend
549
+ when :include
550
+ RBS::AST::Members::Include
551
+ end
552
+
553
+ modules.map do |module_name|
554
+ klass.new(
555
+ name: to_type_name(module_name),
556
+ args: generic_arguments_of_class(module_name),
557
+ annotations: [],
558
+ location: nil,
559
+ comment: nil
560
+ )
561
+ end
562
+ end
563
+
564
+ def to_type_name(type_name_str)
565
+ path = type_name_str.split("::").map(&:to_sym)
566
+
567
+ RBS::TypeName.new(
568
+ name: path.pop,
569
+ namespace: RBS::Namespace.new(path:, absolute: true)
570
+ )
571
+ end
572
+
573
+ def to_rbs_type(*constants)
574
+ constants.uniq!
575
+
576
+ has_boolean = false
577
+ constants.each do |constant|
578
+ if constant.name == "TrueClass" || constant.name == "FalseClass"
579
+ has_boolean = true
580
+ end
581
+ end
582
+
583
+ if has_boolean
584
+ constants.delete_if { _1.name == "TrueClass" || _1.name == "FalseClass" }
585
+ constants.push(Racer::Trace::ConstantInstance.new(name: "bool", singleton: false, generic_arguments: []))
586
+ end
587
+
588
+ if constants.size > 1
589
+ constants.each do |constant|
590
+ unless constant.generic_arguments.empty?
591
+ constants.delete_if { _1.name == constant.name && _1.generic_arguments.empty? }
592
+ end
593
+ end
594
+
595
+ return RBS::Types::Union.new(types: constants.map { |type| to_rbs_type(type) }, location: nil)
596
+ end
597
+
598
+ constant = constants.first
599
+
600
+ case constant.name
601
+ when "bool"
602
+ RBS::Types::Bases::Bool.new(location: nil)
603
+ when "NilClass"
604
+ RBS::Types::Bases::Nil.new(location: nil)
605
+ else
606
+ constant.ensure_valid_name!
607
+ type_name = to_type_name(constant.name)
608
+
609
+ if constant.singleton
610
+ RBS::Types::ClassSingleton.new(
611
+ name: type_name,
612
+ location: nil
613
+ )
614
+ else
615
+ RBS::Types::ClassInstance.new(
616
+ name: type_name,
617
+ args: generic_arguments_of_class(constant.name, constant.generic_arguments),
618
+ location: nil
619
+ )
620
+ end
621
+ end
622
+ end
623
+
624
+ def generic_arguments_of_class(name, existing_generic_arguments = [])
625
+ return [] unless @existing_types.key?(name)
626
+
627
+ existing_type = @existing_types[name][:class_decl]
628
+ type_params = existing_type&.type_params || []
629
+
630
+ if existing_generic_arguments.size == type_params.size
631
+ existing_generic_arguments.map do |union_types|
632
+ to_rbs_type(*union_types)
633
+ end
634
+ else
635
+ type_params.map { |param| RBS::Types::Bases::Any.new(location: nil) }
636
+ end
637
+ end
638
+
639
+ def type_params_of_existing_class(owner)
640
+ return [] unless @existing_types.key?(owner)
641
+
642
+ @existing_types[owner][:class_decl].type_params
643
+ end
644
+ end
645
+ end
@@ -0,0 +1,20 @@
1
+ # This ensures that racer can inject it's railtie even if rails was not fully required yet
2
+ #
3
+ # This happens if running a test with a path argument because the test:prepare task is not being executed and rails did not fully initialize
4
+ # when requiring racer/minitest.
5
+ #
6
+ # If no path argument was passed the rails:prepare rake task is being executed. In this case racer is being required during the rails boot process
7
+ # and installs the railtie accordingly.
8
+ begin
9
+ require "rails"
10
+ rescue LoadError
11
+ # no-op rails not available
12
+ end
13
+
14
+ require "racer"
15
+ require_relative "minitest_plugin"
16
+
17
+ Racer::MinitestPlugin.agent_pid = Racer.start_agent(stop_at_exit: false, collectors: [Racer::Collectors::RBSCollector.new(libraries: ["json", "minitest", "tempfile", "base64", "pathname", "logger", "uri", "erb", "date", "ipaddr", "securerandom"])])
18
+ puts "Agent started with pid #{Racer::MinitestPlugin.agent_pid}"
19
+
20
+ Minitest.register_plugin Racer::MinitestPlugin
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Racer
4
+ class MinitestPlugin
5
+ class << self
6
+ attr_accessor :agent_pid
7
+ end
8
+
9
+ def self.minitest_plugin_init(options)
10
+ Minitest.reporter << reporter(**options)
11
+ end
12
+
13
+ def self.reporter(**options)
14
+ Reporter.new(pid: agent_pid, **options)
15
+ end
16
+
17
+ # Do not subclass Minitest::AbstractReporter to be compatible with minitest-reporters
18
+ # (they expect an #io= method)
19
+ class Reporter < Minitest::Reporter
20
+ def initialize(pid:, **)
21
+ @pid = pid
22
+ super()
23
+ end
24
+
25
+ def report
26
+ Racer.flush
27
+ Racer.stop_agent(@pid)
28
+ end
29
+
30
+ # Compatibility with minitest-reporters gem
31
+ def before_test(*); end
32
+ def after_test(*); end
33
+ end
34
+ end
35
+ end