rigortype 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- data/README.md +40 -13
- data/lib/rigor/analysis/fact_store.rb +15 -3
- data/lib/rigor/analysis/result.rb +11 -3
- data/lib/rigor/analysis/run_stats.rb +193 -0
- data/lib/rigor/analysis/runner.rb +387 -12
- data/lib/rigor/analysis/worker_session.rb +327 -0
- data/lib/rigor/builtins/imported_refinements.rb +6 -2
- data/lib/rigor/builtins/regex_refinement.rb +17 -12
- data/lib/rigor/cache/rbs_descriptor.rb +3 -1
- data/lib/rigor/cache/store.rb +40 -7
- data/lib/rigor/cli.rb +52 -2
- data/lib/rigor/configuration.rb +131 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
- data/lib/rigor/environment/class_registry.rb +12 -3
- data/lib/rigor/environment/lockfile_resolver.rb +125 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +126 -0
- data/lib/rigor/environment/rbs_coverage_report.rb +112 -0
- data/lib/rigor/environment/rbs_loader.rb +194 -6
- data/lib/rigor/environment/reflection.rb +152 -0
- data/lib/rigor/environment.rb +78 -6
- data/lib/rigor/inference/acceptance.rb +35 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
- data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
- data/lib/rigor/inference/expression_typer.rb +12 -2
- data/lib/rigor/inference/macro_block_self_type.rb +96 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -29
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +18 -1
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -40
- data/lib/rigor/inference/method_dispatcher.rb +128 -3
- data/lib/rigor/inference/method_parameter_binder.rb +21 -11
- data/lib/rigor/inference/narrowing.rb +127 -8
- data/lib/rigor/inference/synthetic_method.rb +86 -0
- data/lib/rigor/inference/synthetic_method_index.rb +82 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +521 -0
- data/lib/rigor/plugin/blueprint.rb +60 -0
- data/lib/rigor/plugin/loader.rb +3 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +131 -0
- data/lib/rigor/plugin/macro/external_file.rb +143 -0
- data/lib/rigor/plugin/macro/heredoc_template.rb +201 -0
- data/lib/rigor/plugin/macro/trait_registry.rb +198 -0
- data/lib/rigor/plugin/macro.rb +31 -0
- data/lib/rigor/plugin/manifest.rb +78 -7
- data/lib/rigor/plugin/registry.rb +32 -2
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/trinary.rb +15 -11
- data/lib/rigor/type/bot.rb +6 -3
- data/lib/rigor/type/combinator.rb +12 -1
- data/lib/rigor/type/integer_range.rb +7 -7
- data/lib/rigor/type/refined.rb +18 -12
- data/lib/rigor/type/top.rb +4 -3
- data/lib/rigor/type_node/generic.rb +7 -1
- data/lib/rigor/type_node/identifier.rb +9 -1
- data/lib/rigor/type_node/string_literal.rb +4 -1
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/environment.rbs +5 -2
- data/sig/rigor/plugin/blueprint.rbs +7 -0
- data/sig/rigor/plugin/manifest.rbs +1 -1
- data/sig/rigor/plugin/registry.rbs +14 -1
- data/sig/rigor.rbs +35 -2
- metadata +39 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5960ec17b35768103e97d752f8cc6fd78fcb3f12e12fc43dfa41be07ec5317b
|
|
4
|
+
data.tar.gz: e79c9b25c973c8938e9b2f0a2741cca5195342619827b320ca521ec09e54321e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af1e033a25410c0f87943f12d43ab18a3a0d2a79c01307c2117c2fc15be4c9db3cb28e6fec10ce598ef6a5bfa063227f280c023a0f7e9025b06c69946df4654d
|
|
7
|
+
data.tar.gz: 351b3275dd35f37a11d30a696627e23a6cdca31bfb94fa3eacae762d2de624e4a914c6f8f4eebdd7df0bd17fd9fac13fe55ac434957509b742771a00d352a981
|
data/README.md
CHANGED
|
@@ -421,11 +421,14 @@ analyzer guarantees live under
|
|
|
421
421
|
with the [ADR-9](docs/adr/9-cross-plugin-api.md) cross-plugin
|
|
422
422
|
fact channel (one plugin publishes a fact like `:model_index`,
|
|
423
423
|
another consumes it), [ADR-11](docs/adr/11-sorbet-input-adapter.md)
|
|
424
|
-
Sorbet ingestion,
|
|
425
|
-
plugin-supplied type-vocabulary resolvers
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
424
|
+
Sorbet ingestion, [ADR-13](docs/adr/13-typenode-resolver-plugin.md)
|
|
425
|
+
plugin-supplied type-vocabulary resolvers, and
|
|
426
|
+
[ADR-16](docs/adr/16-macro-expansion.md) macro / DSL expansion
|
|
427
|
+
substrate (declarative Tier A block-as-method / Tier B
|
|
428
|
+
trait-inlining-registry / Tier C heredoc-template / Tier D
|
|
429
|
+
external-file inclusion). **Twenty-four worked examples** ship
|
|
430
|
+
under [`examples/`](examples/) — each is a fully-shaped plugin
|
|
431
|
+
gem with a runnable demo and an end-to-end integration spec.
|
|
429
432
|
|
|
430
433
|
**Plugin-contract teaching examples** (focus on a single
|
|
431
434
|
extension-point):
|
|
@@ -448,6 +451,25 @@ extension-point):
|
|
|
448
451
|
(`Pick` / `Omit` / `Partial` / `Required` / `Readonly`) onto
|
|
449
452
|
Rigor's shape-projection type functions.
|
|
450
453
|
|
|
454
|
+
**Macro expansion substrate consumers** (ADR-16 — declarative
|
|
455
|
+
manifest entries, no walker code):
|
|
456
|
+
|
|
457
|
+
- [`rigor-sinatra`](examples/rigor-sinatra/) — **Tier A**
|
|
458
|
+
block-as-method. Recognises Sinatra's nine class-level HTTP
|
|
459
|
+
verb methods and narrows the route block's `self_type` so
|
|
460
|
+
bare `params` / `redirect` / `halt` resolve through
|
|
461
|
+
`Sinatra::Base`'s RBS.
|
|
462
|
+
- [`rigor-dry-struct`](examples/rigor-dry-struct/) — **Tier C**
|
|
463
|
+
heredoc-template. Synthesises a reader on every `Dry::Struct`
|
|
464
|
+
subclass for each `attribute :name, T` / `attribute? :name, T`
|
|
465
|
+
call.
|
|
466
|
+
- [`rigor-devise`](examples/rigor-devise/) — **Tier B**
|
|
467
|
+
trait-inlining registry mirroring `lib/devise/modules.rb`.
|
|
468
|
+
Each `devise :strategy_a, :strategy_b` call explodes the
|
|
469
|
+
included module's RBS instance methods onto the calling model
|
|
470
|
+
class (Devise's `user.valid_password?` returns the module's
|
|
471
|
+
authored `bool`).
|
|
472
|
+
|
|
451
473
|
**Rails ecosystem plugins** (Tier 1 + Tier 2 + Tier 3 + Sorbet):
|
|
452
474
|
|
|
453
475
|
- Tier 1: [`rigor-rails-routes`](examples/rigor-rails-routes/),
|
|
@@ -510,15 +532,15 @@ Common knobs the file exposes:
|
|
|
510
532
|
|
|
511
533
|
## Status
|
|
512
534
|
|
|
513
|
-
Current released version: **`v0.1.
|
|
535
|
+
Current released version: **`v0.1.4`**. The analyzer is usable
|
|
514
536
|
on real Ruby code today; the rule catalogue is deliberately
|
|
515
537
|
narrow — Rigor's stance is to surface zero false positives
|
|
516
|
-
while the inference surface stabilises.
|
|
517
|
-
in
|
|
518
|
-
|
|
538
|
+
while the inference surface stabilises. Forward-looking commitments
|
|
539
|
+
(in-flight cycle + queued work) live in
|
|
540
|
+
[`docs/ROADMAP.md`](docs/ROADMAP.md); the release-by-release
|
|
541
|
+
"what shipped" record is [`CHANGELOG.md`](CHANGELOG.md).
|
|
519
542
|
|
|
520
|
-
`v0.1.4`
|
|
521
|
-
delivers:
|
|
543
|
+
`v0.1.4` (released 2026-05-14) delivered:
|
|
522
544
|
|
|
523
545
|
- **[ADR-10](docs/adr/10-dependency-source-inference.md) closed
|
|
524
546
|
end-to-end** — opt-in gem-source inference, per-gem budget,
|
|
@@ -553,10 +575,15 @@ delivers:
|
|
|
553
575
|
`rigor-activerecord` publishing `:model_index` via the
|
|
554
576
|
ADR-9 cross-plugin fact channel.
|
|
555
577
|
|
|
556
|
-
|
|
578
|
+
Twenty-four worked plugin examples now ship under
|
|
557
579
|
[`examples/`](examples/) — see
|
|
558
580
|
[`examples/README.md`](examples/README.md) for the comparison
|
|
559
|
-
table.
|
|
581
|
+
table. The current `[Unreleased]` cycle on `master` (release
|
|
582
|
+
pending) also delivered the [ADR-16](docs/adr/16-macro-expansion.md)
|
|
583
|
+
macro / DSL expansion substrate (four-tier declarative
|
|
584
|
+
manifest contract + engine integration + Tier B/C precision
|
|
585
|
+
promotion); see `CHANGELOG.md` `[Unreleased]` for the full
|
|
586
|
+
landing notes.
|
|
560
587
|
|
|
561
588
|
## Contributing
|
|
562
589
|
|
|
@@ -47,9 +47,14 @@ module Rigor
|
|
|
47
47
|
attr_reader :facts
|
|
48
48
|
|
|
49
49
|
class << self
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
# ADR-15 Phase 4b.x — return the eagerly-loaded
|
|
51
|
+
# singleton-class `@empty` ivar. Lazy `@empty ||= new`
|
|
52
|
+
# would write to a class/module ivar from non-main
|
|
53
|
+
# Ractors and trip `Ractor::IsolationError`. The
|
|
54
|
+
# `@empty = new.freeze` at module body below
|
|
55
|
+
# pre-populates the ivar on the main Ractor at load
|
|
56
|
+
# time.
|
|
57
|
+
attr_reader :empty
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
def initialize(facts: [])
|
|
@@ -136,6 +141,13 @@ module Rigor
|
|
|
136
141
|
|
|
137
142
|
[target]
|
|
138
143
|
end
|
|
144
|
+
|
|
145
|
+
# ADR-15 Phase 4b.x — eager-load the singleton `@empty`
|
|
146
|
+
# on the main Ractor at module-load time. Workers then
|
|
147
|
+
# READ the populated ivar without ever attempting a
|
|
148
|
+
# class/module ivar WRITE (which non-main Ractors are
|
|
149
|
+
# forbidden from doing).
|
|
150
|
+
@empty = new.freeze
|
|
139
151
|
end
|
|
140
152
|
end
|
|
141
153
|
end
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
5
|
class Result
|
|
6
|
-
attr_reader :diagnostics
|
|
6
|
+
attr_reader :diagnostics, :stats
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# @param stats [Rigor::Analysis::RunStats, nil] end-of-run
|
|
9
|
+
# telemetry (target file count, RBS class breakdown,
|
|
10
|
+
# wall + RSS) collected by the Runner. Nil when stats
|
|
11
|
+
# collection wasn't requested or wasn't applicable
|
|
12
|
+
# (early-exit paths like `validate_target_ruby` failure).
|
|
13
|
+
def initialize(diagnostics: [], stats: nil)
|
|
9
14
|
@diagnostics = diagnostics
|
|
15
|
+
@stats = stats
|
|
10
16
|
end
|
|
11
17
|
|
|
12
18
|
def success?
|
|
@@ -18,11 +24,13 @@ module Rigor
|
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
def to_h
|
|
21
|
-
{
|
|
27
|
+
hash = {
|
|
22
28
|
"success" => success?,
|
|
23
29
|
"error_count" => error_count,
|
|
24
30
|
"diagnostics" => diagnostics.map(&:to_h)
|
|
25
31
|
}
|
|
32
|
+
hash["stats"] = @stats.to_h if @stats
|
|
33
|
+
hash
|
|
26
34
|
end
|
|
27
35
|
end
|
|
28
36
|
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Analysis
|
|
7
|
+
# End-of-run telemetry for the `rigor check` CLI's `--stats`
|
|
8
|
+
# output. Captures four cheap-to-measure groups:
|
|
9
|
+
#
|
|
10
|
+
# - **Check targets** — the Ruby files the analyser actually
|
|
11
|
+
# walks for diagnostics (`expand_paths` output).
|
|
12
|
+
# - **Type universe** — RBS class/module declarations the
|
|
13
|
+
# analyser had visibility of, broken down by source:
|
|
14
|
+
# `project_sig` (declarations whose source file lives under
|
|
15
|
+
# the configured `signature_paths`) vs `bundled` (RBS core,
|
|
16
|
+
# stdlib libraries, gem-bundled RBS — everything outside
|
|
17
|
+
# the project's own `sig/` tree).
|
|
18
|
+
# - **Gem source-walk** — the ADR-10
|
|
19
|
+
# `dependencies.source_inference` catalogue. Reports the
|
|
20
|
+
# class count and the number of opt-in gems contributing.
|
|
21
|
+
# - **Process** — wall-clock seconds + peak resident set size.
|
|
22
|
+
#
|
|
23
|
+
# The split between "check targets" and "type universe" makes
|
|
24
|
+
# explicit that the analyser's diagnostic surface is bounded
|
|
25
|
+
# by the user-controlled `paths:` configuration; the (typically
|
|
26
|
+
# much larger) RBS class universe is symbol-discovery, not a
|
|
27
|
+
# diagnostic surface.
|
|
28
|
+
#
|
|
29
|
+
# Stats collection is intentionally cheap: wall + RSS are
|
|
30
|
+
# single syscalls, target file count is already in
|
|
31
|
+
# `expand_paths`, gem source-walk uses
|
|
32
|
+
# `Index#class_to_gem.size`, and the RBS class breakdown
|
|
33
|
+
# walks `class_decl_paths` (a frozen `Hash<String, String>`
|
|
34
|
+
# populated once per environment by the RBS loader; ~1000-2000
|
|
35
|
+
# entries × one `String#start_with?`).
|
|
36
|
+
class RunStats
|
|
37
|
+
attr_reader :wall_seconds, :peak_rss_bytes,
|
|
38
|
+
:target_files,
|
|
39
|
+
:rbs_classes_total, :rbs_classes_project_sig, :rbs_classes_bundled,
|
|
40
|
+
:gem_walk_classes, :gem_walk_gems, :rbs_attribution_available
|
|
41
|
+
|
|
42
|
+
def initialize(wall_seconds:, peak_rss_bytes:, # rubocop:disable Metrics/ParameterLists
|
|
43
|
+
target_files:,
|
|
44
|
+
rbs_classes_total:, rbs_classes_project_sig:, rbs_classes_bundled:,
|
|
45
|
+
gem_walk_classes:, gem_walk_gems:,
|
|
46
|
+
rbs_attribution_available: true)
|
|
47
|
+
@wall_seconds = wall_seconds
|
|
48
|
+
@peak_rss_bytes = peak_rss_bytes
|
|
49
|
+
@target_files = target_files
|
|
50
|
+
@rbs_classes_total = rbs_classes_total
|
|
51
|
+
@rbs_classes_project_sig = rbs_classes_project_sig
|
|
52
|
+
@rbs_classes_bundled = rbs_classes_bundled
|
|
53
|
+
@gem_walk_classes = gem_walk_classes
|
|
54
|
+
@gem_walk_gems = gem_walk_gems
|
|
55
|
+
@rbs_attribution_available = rbs_attribution_available
|
|
56
|
+
freeze
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Reports the process's resident set size in bytes. Source
|
|
60
|
+
# ordering: `/proc/self/status` (Linux — reads `VmHWM:`,
|
|
61
|
+
# the peak RSS the kernel records) first; otherwise
|
|
62
|
+
# `ps -o rss= -p <pid>` (macOS / BSD — reports CURRENT
|
|
63
|
+
# RSS, the closest universally-available proxy). Returns
|
|
64
|
+
# nil when neither route works so the formatter can render
|
|
65
|
+
# `unavailable` instead of misleading zero.
|
|
66
|
+
def self.peak_rss_bytes
|
|
67
|
+
from_proc = read_vmhwm_from_proc
|
|
68
|
+
return from_proc unless from_proc.nil?
|
|
69
|
+
|
|
70
|
+
from_ps = read_rss_via_ps
|
|
71
|
+
return from_ps unless from_ps.nil?
|
|
72
|
+
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.read_vmhwm_from_proc
|
|
77
|
+
return nil unless File.readable?("/proc/self/status")
|
|
78
|
+
|
|
79
|
+
File.foreach("/proc/self/status") do |line|
|
|
80
|
+
next unless line.start_with?("VmHWM:")
|
|
81
|
+
|
|
82
|
+
kb_token = line.split.find { |token| token.match?(/\A\d+\z/) }
|
|
83
|
+
return Integer(kb_token) * 1024 if kb_token
|
|
84
|
+
end
|
|
85
|
+
nil
|
|
86
|
+
rescue StandardError
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.read_rss_via_ps
|
|
91
|
+
out = `ps -o rss= -p #{Process.pid} 2>/dev/null`.strip
|
|
92
|
+
return nil if out.empty?
|
|
93
|
+
|
|
94
|
+
Integer(out) * 1024
|
|
95
|
+
rescue StandardError
|
|
96
|
+
nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Source-attribution sentinel produced by `RBS::Environment`
|
|
100
|
+
# entries restored from a cached blob (Marshal-loaded
|
|
101
|
+
# `RBS::Environment` loses real file-path attribution; every
|
|
102
|
+
# buffer reports `"<cached>"`). When every entry carries
|
|
103
|
+
# this sentinel the partition_classes routine returns
|
|
104
|
+
# `[0, total]` AND `attribution_available: false`, which
|
|
105
|
+
# the format routine consumes to suppress the misleading
|
|
106
|
+
# breakdown row.
|
|
107
|
+
CACHED_SENTINEL = "<cached>"
|
|
108
|
+
|
|
109
|
+
# Computes `(project_sig, bundled)` counts from a frozen
|
|
110
|
+
# `Hash<class_name => source_path>` snapshot and the
|
|
111
|
+
# configured `signature_paths`. `project_sig` is the count
|
|
112
|
+
# of classes whose source path begins with any of the
|
|
113
|
+
# signature path prefixes (after expansion to absolute
|
|
114
|
+
# paths); `bundled` is the remainder.
|
|
115
|
+
def self.partition_classes(class_decl_paths:, signature_paths:)
|
|
116
|
+
prefixes = Array(signature_paths).map { |p| File.expand_path(p.to_s) }
|
|
117
|
+
return [0, class_decl_paths.size] if prefixes.empty?
|
|
118
|
+
|
|
119
|
+
project = 0
|
|
120
|
+
class_decl_paths.each_value do |path|
|
|
121
|
+
expanded = File.expand_path(path)
|
|
122
|
+
project += 1 if prefixes.any? { |prefix| expanded.start_with?("#{prefix}/") || expanded == prefix }
|
|
123
|
+
end
|
|
124
|
+
[project, class_decl_paths.size - project]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# True when at least one entry in `class_decl_paths` carries
|
|
128
|
+
# a real source file path (i.e. not the cached-sentinel
|
|
129
|
+
# marker). Used by callers to decide whether the
|
|
130
|
+
# `project_sig` / `bundled` split is meaningful.
|
|
131
|
+
def self.attribution_available?(class_decl_paths:)
|
|
132
|
+
return false if class_decl_paths.empty?
|
|
133
|
+
|
|
134
|
+
class_decl_paths.each_value.any? { |path| path != CACHED_SENTINEL }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Writes a human-facing rendering of the stats to `out`
|
|
138
|
+
# (typically `$stderr` from the CLI). Format is intentionally
|
|
139
|
+
# plain text — JSON consumers should parse the structured
|
|
140
|
+
# output of `rigor check --format=json` and consult `stats`
|
|
141
|
+
# there.
|
|
142
|
+
def format(out, prefix: "")
|
|
143
|
+
out.puts("#{prefix}Check targets")
|
|
144
|
+
out.puts("#{prefix} Ruby source files: #{@target_files}")
|
|
145
|
+
out.puts("#{prefix}Type universe (symbol discovery; not analyzed for diagnostics)")
|
|
146
|
+
out.puts("#{prefix} RBS classes available: #{@rbs_classes_total}")
|
|
147
|
+
if @rbs_attribution_available
|
|
148
|
+
out.puts("#{prefix} project sig/: #{@rbs_classes_project_sig}")
|
|
149
|
+
out.puts("#{prefix} bundled (core+stdlib+gems): #{@rbs_classes_bundled}")
|
|
150
|
+
elsif @rbs_classes_total.positive?
|
|
151
|
+
out.puts("#{prefix} (source attribution unavailable on cache-hit runs; --no-cache surfaces it)")
|
|
152
|
+
end
|
|
153
|
+
if @gem_walk_gems.positive?
|
|
154
|
+
out.puts("#{prefix} Gem source-walk classes: #{@gem_walk_classes} " \
|
|
155
|
+
"(across #{@gem_walk_gems} #{@gem_walk_gems == 1 ? 'gem' : 'gems'} " \
|
|
156
|
+
"via dependencies.source_inference)")
|
|
157
|
+
end
|
|
158
|
+
out.puts("#{prefix}Process")
|
|
159
|
+
out.puts("#{prefix} Wall time: #{Kernel.format('%.2fs', @wall_seconds)}")
|
|
160
|
+
out.puts("#{prefix} Memory peak: #{format_bytes(@peak_rss_bytes)}")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def to_h
|
|
164
|
+
{
|
|
165
|
+
target_files: @target_files,
|
|
166
|
+
rbs_classes_total: @rbs_classes_total,
|
|
167
|
+
rbs_classes_project_sig: @rbs_classes_project_sig,
|
|
168
|
+
rbs_classes_bundled: @rbs_classes_bundled,
|
|
169
|
+
rbs_attribution_available: @rbs_attribution_available,
|
|
170
|
+
gem_walk_classes: @gem_walk_classes,
|
|
171
|
+
gem_walk_gems: @gem_walk_gems,
|
|
172
|
+
wall_seconds: @wall_seconds,
|
|
173
|
+
peak_rss_bytes: @peak_rss_bytes
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
def format_bytes(bytes)
|
|
180
|
+
return "unavailable" if bytes.nil?
|
|
181
|
+
|
|
182
|
+
units = %w[B KB MB GB TB]
|
|
183
|
+
size = bytes.to_f
|
|
184
|
+
index = 0
|
|
185
|
+
while size >= 1024 && index < units.size - 1
|
|
186
|
+
size /= 1024
|
|
187
|
+
index += 1
|
|
188
|
+
end
|
|
189
|
+
Kernel.format("%<size>.1f %<unit>s", size: size, unit: units[index])
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|