Class: Ibex::Configuration::Report

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/configuration/explanation.rb,
sig/ibex/configuration/explanation.rbs

Overview

Deterministic, static-no-user-code explanation of every registered setting.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, cli: {}) ⇒ Report

Returns a new instance of Report.

RBS:

  • (Input input, ?cli: Hash[String, config_value]) -> void

Parameters:

  • input (Input)
  • cli: (Hash[String, config_value]) (defaults to: {})


301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/ibex/configuration/explanation.rb', line 301

def initialize(input, cli: {})
  raise ArgumentError, "configuration report input must be a Configuration::Input" unless input.is_a?(Input)

  cli.each_key { |name| Registry.fetch(name) }

  @input = input
  conflicts = [] #: Array[ConflictRecord]
  @explanations = Registry.keys.sort_by(&:name).map do |key|
    value = resolve_key(key, cli)
    explanation(value, cli)
  rescue Conflict => e
    record = ConflictRecord.new(e)
    conflicts << record
    explanation(e.declared, cli, conflict: record)
  end.freeze
  @conflicts = conflicts.freeze
  freeze
end

Instance Attribute Details

#conflictsArray[ConflictRecord] (readonly)

Signature:

  • Array[ConflictRecord]

Returns:



298
299
300
# File 'lib/ibex/configuration/explanation.rb', line 298

def conflicts
  @conflicts
end

#explanationsArray[Explanation] (readonly)

Signature:

  • Array[Explanation]

Returns:



297
298
299
# File 'lib/ibex/configuration/explanation.rb', line 297

def explanations
  @explanations
end

#inputInput (readonly)

Signature:

  • Input

Returns:



296
297
298
# File 'lib/ibex/configuration/explanation.rb', line 296

def input
  @input
end

Instance Method Details

#cli_evidence(key, raw, conflicting:) ⇒ Evidence

RBS:

  • (Key key, config_value raw, conflicting: bool) -> Evidence

Parameters:

  • key (Key)
  • raw (config_value)
  • conflicting: (Boolean)

Returns:



380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/ibex/configuration/explanation.rb', line 380

def cli_evidence(key, raw, conflicting:)
  value = key.validate(raw)
  grammar = @input.grammar_values
  status = conflicting ? :conflicting : :accepted
  reason = if conflicting
             "conflicts with the fixed grammar contract and was not selected"
           elsif grammar.key?(key.name) && grammar.fetch(key.name) == value
             "matches the fixed grammar contract"
           else
             "selected canonical command-line request"
           end
  Evidence.new(key, source: :cli, value: value, status: status, reason: reason)
end

#display(value) ⇒ String

RBS:

  • (config_value value) -> String

Parameters:

  • value (config_value)

Returns:

  • (String)


395
396
397
# File 'lib/ibex/configuration/explanation.rb', line 395

def display(value)
  value.nil? ? "null" : value.to_s
end

#dumpString

Deterministic JSON independent of caller hash insertion order.

RBS:

  • () -> String

Returns:

  • (String)


340
341
342
# File 'lib/ibex/configuration/explanation.rb', line 340

def dump
  "#{JSON.generate(to_h)}\n"
end

#explanation(value, cli, conflict: nil) ⇒ Explanation

RBS:

  • (Value value, Hash[String, config_value] cli, ?conflict: ConflictRecord?) -> Explanation

Parameters:

  • value (Value)
  • cli (Hash[String, config_value])
  • conflict: (ConflictRecord, nil) (defaults to: nil)

Returns:



368
369
370
371
372
373
374
375
376
377
# File 'lib/ibex/configuration/explanation.rb', line 368

def explanation(value, cli, conflict: nil)
  key = value.key
  evidence = @input.evidence.fetch(key.name, []).dup
  evidence << cli_evidence(key, cli.fetch(key.name), conflicting: !conflict.nil?) if cli.key?(key.name)
  recording = @input.recordings.fetch(
    key.name,
    Recording.new(:not_applicable, "this setting has no historical source contract in this input")
  )
  Explanation.new(value, evidence: evidence, recording: recording)
end

#location_label(location) ⇒ String

RBS:

  • (Location location) -> String

Parameters:

Returns:

  • (String)


400
401
402
# File 'lib/ibex/configuration/explanation.rb', line 400

def location_label(location)
  "#{location.file || '(source)'}:#{location.line}:#{location.column}"
end

#render_explanation(entry) ⇒ Array[String]

RBS:

  • (Explanation entry) -> Array[String]

Parameters:

Returns:

  • (Array[String])


405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/ibex/configuration/explanation.rb', line 405

def render_explanation(entry)
  value = entry.value
  lines = [
    "#{value.key.name}=#{display(value.value)} owner=#{value.key.owner_name} " \
    "origin=#{value.origin.label} policy=#{value.key.policy} " \
    "#{value.explicit ? 'explicit' : 'implicit'} #{value.canonical ? 'canonical' : 'noncanonical'}",
    "  recording=#{entry.recording.state}: #{entry.recording.reason}"
  ]
  entry.evidence.each do |evidence|
    location = evidence.location ? " at #{location_label(evidence.location)}" : ""
    lines << "  #{evidence.status} #{evidence.source}=#{display(evidence.value)}#{location}: #{evidence.reason}"
  end
  lines
end

#render_textString

RBS:

  • () -> String

Returns:

  • (String)


345
346
347
348
349
350
# File 'lib/ibex/configuration/explanation.rb', line 345

def render_text
  lines = ["configuration status=#{success? ? 'ok' : 'conflict'} (static-no-user-code): #{@input.path}"]
  @explanations.each { |entry| lines.concat(render_explanation(entry)) }
  @conflicts.each { |conflict| lines << "conflict: #{conflict.message}" }
  "#{lines.join("\n")}\n"
end

#resolve_key(key, cli) ⇒ Value

RBS:

  • (Key key, Hash[String, config_value] cli) -> Value

Parameters:

  • key (Key)
  • cli (Hash[String, config_value])

Returns:



355
356
357
358
359
360
361
362
363
364
365
# File 'lib/ibex/configuration/explanation.rb', line 355

def resolve_key(key, cli)
  name = key.name
  grammar = {} #: Hash[String, config_value]
  grammar[name] = @input.grammar_values.fetch(name) if @input.grammar_values.key?(name)
  request = {} #: Hash[String, config_value]
  request[name] = cli.fetch(name) if cli.key?(name)
  locations = {} #: Hash[Symbol, Hash[String, Location]]
  locations[:grammar] = { name => @input.grammar_locations.fetch(name) } \
    if @input.grammar_locations.key?(name)
  Resolver.new(keys: [key], grammar: grammar, cli: request, locations: locations).values.fetch(0)
end

#success?Boolean

RBS:

  • () -> bool

Returns:

  • (Boolean)


321
322
323
# File 'lib/ibex/configuration/explanation.rb', line 321

def success?
  @conflicts.empty?
end

#to_hHash[String, json_value]

RBS:

  • () -> Hash[String, json_value]

Returns:

  • (Hash[String, json_value])


326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ibex/configuration/explanation.rb', line 326

def to_h
  {
    "ibex_report" => "configuration",
    "schema_version" => 1,
    "trust" => "static-no-user-code",
    "status" => success? ? "ok" : "conflict",
    "input" => @input.to_h,
    "configuration" => @explanations.map(&:to_h),
    "conflicts" => @conflicts.map(&:to_h)
  }
end