# typed: strict
# frozen_string_literal: true
require_relative "d3"
require "erb"
module Spoom
module Coverage
class Template
extend T::Sig
extend T::Helpers
abstract!
# Create a new template from an Erb file path
sig { params(template: String).void }
def initialize(template:)
@template = template
end
sig { returns(String) }
def erb
File.read(@template)
end
sig { returns(String) }
def html
ERB.new(erb).result(get_binding)
end
sig { returns(Binding) }
def get_binding # rubocop:disable Naming/AccessorMethodName
binding
end
end
class Page < Template
extend T::Sig
extend T::Helpers
abstract!
TEMPLATE = T.let("#{Spoom::SPOOM_PATH}/templates/page.erb", String)
sig { returns(String) }
attr_reader :title
sig { returns(D3::ColorPalette) }
attr_reader :palette
sig { params(title: String, palette: D3::ColorPalette, template: String).void }
def initialize(title:, palette:, template: TEMPLATE)
super(template: template)
@title = title
@palette = palette
end
sig { returns(String) }
def header_style
D3.header_style
end
sig { returns(String) }
def header_script
D3.header_script(palette)
end
sig { returns(String) }
def header_html
"
#{title}
"
end
sig { returns(String) }
def body_html
cards.map(&:html).join("\n")
end
sig { abstract.returns(T::Array[Cards::Card]) }
def cards; end
sig { returns(String) }
def footer_html
"Generated by spoom on #{Time.now.utc}."
end
end
module Cards
class Card < Template
extend T::Sig
TEMPLATE = T.let("#{Spoom::SPOOM_PATH}/templates/card.erb", String)
sig { returns(T.nilable(String)) }
attr_reader :title, :body
sig { params(template: String, title: T.nilable(String), body: T.nilable(String)).void }
def initialize(template: TEMPLATE, title: nil, body: nil)
super(template: template)
@title = title
@body = body
end
end
class Erb < Card
extend T::Sig
extend T::Helpers
abstract!
sig { void }
def initialize; end
sig { override.returns(String) }
def html
ERB.new(erb).result(get_binding)
end
sig { abstract.returns(String) }
def erb; end
end
class Snapshot < Card
extend T::Sig
TEMPLATE = T.let("#{Spoom::SPOOM_PATH}/templates/card_snapshot.erb", String)
sig { returns(Coverage::Snapshot) }
attr_reader :snapshot
sig { params(snapshot: Coverage::Snapshot, title: String).void }
def initialize(snapshot:, title: "Snapshot")
super(template: TEMPLATE, title: title)
@snapshot = snapshot
end
sig { returns(D3::Pie::Sigils) }
def pie_sigils
D3::Pie::Sigils.new('pie_sigils', 'Sigils', snapshot)
end
sig { returns(D3::Pie::Calls) }
def pie_calls
D3::Pie::Calls.new('pie_calls', 'Calls', snapshot)
end
sig { returns(D3::Pie::Sigs) }
def pie_sigs
D3::Pie::Sigs.new('pie_sigs', 'Sigs', snapshot)
end
end
class Map < Card
extend T::Sig
sig { params(sigils_tree: FileTree, title: String).void }
def initialize(sigils_tree:, title: "Strictness Map")
super(title: title, body: D3::CircleMap::Sigils.new("map_sigils", sigils_tree).html)
end
end
class Timeline < Card
extend T::Sig
sig { params(title: String, timeline: D3::Timeline).void }
def initialize(title:, timeline:)
super(title: title, body: timeline.html)
end
class Sigils < Timeline
extend T::Sig
sig { params(snapshots: T::Array[Coverage::Snapshot], title: String).void }
def initialize(snapshots:, title: "Sigils Timeline")
super(title: title, timeline: D3::Timeline::Sigils.new("timeline_sigils", snapshots))
end
end
class Calls < Timeline
extend T::Sig
sig { params(snapshots: T::Array[Coverage::Snapshot], title: String).void }
def initialize(snapshots:, title: "Calls Timeline")
super(title: title, timeline: D3::Timeline::Calls.new("timeline_calls", snapshots))
end
end
class Sigs < Timeline
extend T::Sig
sig { params(snapshots: T::Array[Coverage::Snapshot], title: String).void }
def initialize(snapshots:, title: "Signatures Timeline")
super(title: title, timeline: D3::Timeline::Sigs.new("timeline_sigs", snapshots))
end
end
class Versions < Timeline
extend T::Sig
sig { params(snapshots: T::Array[Coverage::Snapshot], title: String).void }
def initialize(snapshots:, title: "Sorbet Versions Timeline")
super(title: title, timeline: D3::Timeline::Versions.new("timeline_versions", snapshots))
end
end
class Runtimes < Timeline
extend T::Sig
sig { params(snapshots: T::Array[Coverage::Snapshot], title: String).void }
def initialize(snapshots:, title: "Sorbet Typechecking Time")
super(title: title, timeline: D3::Timeline::Runtimes.new("timeline_runtimes", snapshots))
end
end
end
class SorbetIntro < Erb
extend T::Sig
sig { params(sorbet_intro_commit: T.nilable(String), sorbet_intro_date: T.nilable(Time)).void }
def initialize(sorbet_intro_commit: nil, sorbet_intro_date: nil)
@sorbet_intro_commit = sorbet_intro_commit
@sorbet_intro_date = sorbet_intro_date
end
sig { override.returns(String) }
def erb
<<~ERB
Typchecked by Sorbet since #{@sorbet_intro_date&.strftime('%F')}
(commit #{@sorbet_intro_commit}).
ERB
end
end
end
class Report < Page
extend T::Sig
sig { returns(String) }
attr_reader :project_name
sig { returns(T.nilable(String)) }
attr_reader :sorbet_intro_commit
sig { returns(T.nilable(Time)) }
attr_reader :sorbet_intro_date
sig { returns(T::Array[Snapshot]) }
attr_reader :snapshots
sig { returns(FileTree) }
attr_reader :sigils_tree
sig do
params(
project_name: String,
palette: D3::ColorPalette,
snapshots: T::Array[Snapshot],
sigils_tree: FileTree,
sorbet_intro_commit: T.nilable(String),
sorbet_intro_date: T.nilable(Time),
).void
end
def initialize(
project_name:,
palette:,
snapshots:,
sigils_tree:,
sorbet_intro_commit: nil,
sorbet_intro_date: nil
)
super(title: project_name, palette: palette)
@project_name = project_name
@snapshots = snapshots
@sigils_tree = sigils_tree
@sorbet_intro_commit = sorbet_intro_commit
@sorbet_intro_date = sorbet_intro_date
end
sig { override.returns(String) }
def header_html
last = T.must(snapshots.last)
<<~ERB
#{project_name}
#{last.commit_sha}
ERB
end
sig { override.returns(T::Array[Cards::Card]) }
def cards
last = T.must(snapshots.last)
cards = []
cards << Cards::Snapshot.new(snapshot: last)
cards << Cards::Map.new(sigils_tree: sigils_tree)
cards << Cards::Timeline::Sigils.new(snapshots: snapshots)
cards << Cards::Timeline::Calls.new(snapshots: snapshots)
cards << Cards::Timeline::Sigs.new(snapshots: snapshots)
cards << Cards::Timeline::Versions.new(snapshots: snapshots)
cards << Cards::Timeline::Runtimes.new(snapshots: snapshots)
cards << Cards::SorbetIntro.new(sorbet_intro_commit: sorbet_intro_commit, sorbet_intro_date: sorbet_intro_date)
cards
end
end
end
end