Class: Bundler::Spinel::Survey
- Inherits:
-
Object
- Object
- Bundler::Spinel::Survey
- Defined in:
- lib/bundler/spinel/survey.rb
Overview
Wholesale review: probe a large list of gems and aggregate the results. The point isn’t a pass/fail — it’s the *histogram of rejection reasons*, which directly prioritises what Spinel should support next (see RFC asks).
Embarrassingly parallel: each gem is an independent fetch + compile, both subprocess-bound (Open3 releases the GVL), so a thread pool scales across cores. Verdicts are cached in the ledger, so a survey is resumable and re-runnable; only ledger writes are serialised.
Constant Summary collapse
- METAPROG =
Metaprogramming / reflection constructs Spinel treats as out of scope. Mirrors Probe::RISK_TOKENS’ reason vocabulary: whether a construct shows up as a static risk (‘send`) or as an unresolved call (`unresolved:send`), it’s bucketed as metaprog and kept out of the candidate-call list — the point of that list is calls Spinel could plausibly learn, not ones it deliberately won’t.
%w[ eval instance_eval class_eval define_method method_missing respond_to_missing const_missing send public_send objectspace tracepoint binding ].freeze
- REPORT_CALLS =
How many candidate calls to show inline in the report. The full ranked list always goes to candidates.tsv; 0 shows the whole list inline too.
Integer(ENV.fetch("SPINEL_REPORT_CALLS", "200"))
Instance Method Summary collapse
-
#candidates_tsv(names) ⇒ Object
The full ranked candidate-call list as TSV (‘counttcall`, header first) — the long-tail companion to the report’s top-N inline table.
-
#initialize(engine: Engine.new, ledger: Ledger.new, jobs: 4, refresh: false, skip_known: nil) ⇒ Survey
constructor
A new instance of Survey.
-
#report(names) ⇒ Object
Aggregate a markdown report from ledger verdicts for ‘names` at this rev.
-
#run(names, progress: $stderr) ⇒ Object
names: Array<String>.
Constructor Details
#initialize(engine: Engine.new, ledger: Ledger.new, jobs: 4, refresh: false, skip_known: nil) ⇒ Survey
Returns a new instance of Survey.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/bundler/spinel/survey.rb', line 31 def initialize(engine: Engine.new, ledger: Ledger.new, jobs: 4, refresh: false, skip_known: nil) @engine = engine @ledger = ledger @jobs = jobs @refresh = refresh # Resume fast-path: when a gem already has *any* verdict at this rev, # short-circuit `probe_one` so we skip the per-gem `latest_version` # (a `gem list -r` HTTPS roundtrip × ~190k = hours of pure waste on a # restart). Defaults to !refresh — refresh means re-probe everything, # which by definition wants the fresh latest_version too. @skip_known = skip_known.nil? ? !refresh : skip_known @fetcher = GemFetcher.new @probe = Probe.new(@engine, @ledger) @mutex = Mutex.new end |
Instance Method Details
#candidates_tsv(names) ⇒ Object
The full ranked candidate-call list as TSV (‘counttcall`, header first) —the long-tail companion to the report’s top-N inline table. Written to candidates.tsv so the whole roadmap signal survives, not just the head.
91 92 93 94 95 |
# File 'lib/bundler/spinel/survey.rb', line 91 def candidates_tsv(names) _, _, reasons = aggregate(names) rows = candidate_calls(reasons).map { |call, c| "#{c}\t#{call}" } (["count\tcall"] + rows).join("\n") + "\n" end |
#report(names) ⇒ Object
Aggregate a markdown report from ledger verdicts for ‘names` at this rev.
Reads straight from the ledger — no network. The just-run probes already recorded a verdict (with its resolved version) per surveyed gem at this rev, so re-resolving each gem’s latest version online would only repeat work and serialise a 1k-name survey behind 1k ‘gem list -r` calls. We take the last current-rev entry per gem: append-only means a re-probe supersedes, and the survey probes a gem at one (latest) version per run.
83 84 85 86 |
# File 'lib/bundler/spinel/survey.rb', line 83 def report(names) n, counts, reasons = aggregate(names) render(n, counts, reasons) end |
#run(names, progress: $stderr) ⇒ Object
names: Array<String>. Probes each at its latest version (ledger-cached). Returns the Array<Ledger::Verdict> for the surveyed set.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/bundler/spinel/survey.rb', line 49 def run(names, progress: $stderr) @engine.ensure! queue = Queue.new names.each { |n| queue << n } results = [] done = 0 total = names.size workers = Array.new([@jobs, total].min) do Thread.new do until queue.empty? name = (queue.pop(true) rescue break) v = probe_one(name) @mutex.synchronize do results << v if v done += 1 progress&.print("\r[survey] #{done}/#{total} #{name.ljust(30)}") end end end end workers.each(&:join) progress&.puts("\r[survey] #{done}/#{total} done#{' ' * 30}") results.compact end |