Class: Ibex::Configuration::Resolver

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

Overview

Applies fixed/minimum override algebra and exposes deterministic evidence.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(keys: Registry.keys, grammar: {}, project: {}, cli: {}, analysis_overrides: {}, locations: {}) ⇒ Resolver

Returns a new instance of Resolver.

RBS:

  • (?keys: Array[Key], ?grammar: Hash[String, config_value], ?project: Hash[String, config_value], ?cli: Hash[String, config_value], ?analysis_overrides: Hash[String, config_value], ?locations: Hash[Symbol, Hash[String, Location]]) -> void

Parameters:

  • keys: (Array[Key]) (defaults to: Registry.keys)
  • grammar: (Hash[String, config_value]) (defaults to: {})
  • project: (Hash[String, config_value]) (defaults to: {})
  • cli: (Hash[String, config_value]) (defaults to: {})
  • analysis_overrides: (Hash[String, config_value]) (defaults to: {})
  • locations: (Hash[Symbol, Hash[String, Location]]) (defaults to: {})


331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/ibex/configuration.rb', line 331

def initialize(keys: Registry.keys, grammar: {}, project: {}, cli: {}, analysis_overrides: {}, locations: {})
  @keys = keys.sort_by(&:name).freeze
  @key_index = @keys.to_h { |key| [key.name, key] }.freeze
  raise ArgumentError, "duplicate configuration key definition" unless @key_index.length == @keys.length

  sources = { grammar: grammar, project: project, cli: cli, analysis_override: analysis_overrides }
  sources.each { |kind, entries| validate_source!(kind, entries) }
  validate_locations!(locations, sources)
  @values = @keys.map do |key|
    resolve_key(key, sources, locations)
  end.freeze
  @value_index = @values.to_h { |entry| [entry.key.name, entry] }.freeze
  freeze
end

Instance Attribute Details

#valuesArray[Value] (readonly)

Signature:

  • Array[Value]

Returns:



326
327
328
# File 'lib/ibex/configuration.rb', line 326

def values
  @values
end

Instance Method Details

#dumpString

Deterministic JSON independent of caller hash insertion order.

RBS:

  • () -> String

Returns:

  • (String)


363
364
365
# File 'lib/ibex/configuration.rb', line 363

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

#fetch(name) ⇒ Value

RBS:

  • (String name) -> Value

Parameters:

  • name (String)

Returns:



347
348
349
# File 'lib/ibex/configuration.rb', line 347

def fetch(name)
  @value_index.fetch(name) { raise ArgumentError, "unknown configuration key: #{name.inspect}" }
end

#origin(kind, name, locations) ⇒ Origin

RBS:

  • (Symbol kind, String name, Hash[Symbol, Hash[String, Location]] locations) -> Origin

Parameters:

  • kind (Symbol)
  • name (String)
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



491
492
493
# File 'lib/ibex/configuration.rb', line 491

def origin(kind, name, locations)
  Origin.new(kind, location: locations.dig(kind, name))
end

#reject_fixed_conflicts!(key, grammar, declared, requested) ⇒ void

This method returns an undefined value.

RBS:

  • (Key key, Value? grammar, Value declared, Array[Value] requested) -> void

Parameters:



430
431
432
433
434
435
436
437
# File 'lib/ibex/configuration.rb', line 430

def reject_fixed_conflicts!(key, grammar, declared, requested)
  if grammar
    conflict = requested.find { |selection| selection.value != declared.value }
    raise Conflict.new(key, declared, conflict) if conflict
  elsif requested.map(&:value).uniq.length > 1
    raise Conflict.new(key, requested.fetch(0), requested.fetch(1))
  end
end

#resolve_fixed(key, builtin, sources, locations) ⇒ Value

RBS:

  • (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources, Hash[Symbol, Hash[String, Location]] locations) -> Value

Parameters:

  • key (Key)
  • builtin (Value)
  • sources (Hash[Symbol, Hash[String, config_value]])
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



420
421
422
423
424
425
426
427
# File 'lib/ibex/configuration.rb', line 420

def resolve_fixed(key, builtin, sources, locations)
  grammar = source_value(key, :grammar, sources, locations)
  declared = grammar || builtin
  requested = %i[project cli].filter_map { |kind| source_value(key, kind, sources, locations) }
  reject_fixed_conflicts!(key, grammar, declared, requested)

  grammar || requested.last || builtin
end

#resolve_key(key, sources, locations) ⇒ Value

RBS:

  • (Key key, Hash[Symbol, Hash[String, config_value]] sources, Hash[Symbol, Hash[String, Location]] locations) -> Value

Parameters:

  • key (Key)
  • sources (Hash[Symbol, Hash[String, config_value]])
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/ibex/configuration.rb', line 396

def resolve_key(key, sources, locations)
  builtin = value_from(key, key.default, :builtin, locations, explicit: false)
  selected = case key.policy
             when :fixed then resolve_fixed(key, builtin, sources, locations)
             when :minimum then resolve_minimum(key, builtin, sources, locations)
             when :build, :invocation then resolve_override(key, builtin, sources, locations)
             else raise ArgumentError, "unsupported configuration policy: #{key.policy.inspect}"
             end
  override = sources.fetch(:analysis_override)
  return selected unless override.key?(key.name)
  raise ArgumentError, "analysis overrides are only valid for fixed configuration" unless key.policy == :fixed
  if key.validate(override.fetch(key.name)) == selected.value
    raise ArgumentError, "analysis override for #{key.name} must differ from the canonical value"
  end

  selected_origin = origin(:analysis_override, key.name, locations)
  Value.new(
    key, override.fetch(key.name), origin: selected_origin,
                                   explicit: true, canonical: false, declared_value: selected.value
  )
end

#resolve_minimum(key, builtin, sources, locations) ⇒ Value

RBS:

  • (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources, Hash[Symbol, Hash[String, Location]] locations) -> Value

Parameters:

  • key (Key)
  • builtin (Value)
  • sources (Hash[Symbol, Hash[String, config_value]])
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/ibex/configuration.rb', line 441

def resolve_minimum(key, builtin, sources, locations)
  grammar = source_value(key, :grammar, sources, locations)
  floor = grammar || builtin
  selected = floor
  %i[project cli].each do |kind|
    request = source_value(key, kind, sources, locations)
    next unless request

    request_value = request.value #: Integer
    floor_value = floor.value #: Integer
    raise Conflict.new(key, floor, request) if request_value < floor_value

    selected_value = selected.value #: Integer
    selected = request if request_value > selected_value
  end
  selected
end

#resolve_override(key, builtin, sources, locations) ⇒ Value

RBS:

  • (Key key, Value builtin, Hash[Symbol, Hash[String, config_value]] sources, Hash[Symbol, Hash[String, Location]] locations) -> Value

Parameters:

  • key (Key)
  • builtin (Value)
  • sources (Hash[Symbol, Hash[String, config_value]])
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



461
462
463
464
465
466
467
468
469
470
471
# File 'lib/ibex/configuration.rb', line 461

def resolve_override(key, builtin, sources, locations)
  if sources.fetch(:grammar).key?(key.name)
    raise ArgumentError, "#{key.name} is #{key.policy} configuration and cannot be grammar-owned"
  end
  if key.policy == :invocation && sources.fetch(:project).key?(key.name)
    raise ArgumentError, "#{key.name} is invocation configuration and cannot be project-owned"
  end

  source_value(key, :cli, sources, locations) ||
    source_value(key, :project, sources, locations) || builtin
end

#source_value(key, kind, sources, locations) ⇒ Value?

RBS:

  • (Key key, Symbol kind, Hash[Symbol, Hash[String, config_value]] sources, Hash[Symbol, Hash[String, Location]] locations) -> Value?

Parameters:

  • key (Key)
  • kind (Symbol)
  • sources (Hash[Symbol, Hash[String, config_value]])
  • locations (Hash[Symbol, Hash[String, Location]])

Returns:



475
476
477
478
479
480
# File 'lib/ibex/configuration.rb', line 475

def source_value(key, kind, sources, locations)
  entries = sources.fetch(kind)
  return unless entries.key?(key.name)

  value_from(key, entries.fetch(key.name), kind, locations, explicit: true)
end

#to_hHash[String, Array[Hash[String, json_value]]]

RBS:

  • () -> Hash[String, Array[Hash[String, json_value]]]

Returns:

  • (Hash[String, Array[Hash[String, json_value]]])


357
358
359
# File 'lib/ibex/configuration.rb', line 357

def to_h
  { "configuration" => @values.map(&:to_h) }
end

#validate_locations!(locations, sources) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[Symbol, Hash[String, Location]] locations, Hash[Symbol, Hash[String, config_value]] sources) -> void

Parameters:

  • locations (Hash[Symbol, Hash[String, Location]])
  • sources (Hash[Symbol, Hash[String, config_value]])


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

def validate_locations!(locations, sources)
  locations.each do |kind, entries|
    unless %i[grammar project].include?(kind)
      raise ArgumentError, "#{kind} configuration cannot have source locations"
    end

    entries.each do |name, location|
      raise ArgumentError, "unknown #{kind} configuration location: #{name.inspect}" unless @key_index.key?(name)
      raise ArgumentError, "configuration location must be an Ibex::Location" unless location.is_a?(Location)
      next if sources.fetch(kind).key?(name)

      raise ArgumentError, "configuration location for #{name} has no #{kind} value"
    end
  end
end

#validate_source!(kind, entries) ⇒ void

This method returns an undefined value.

RBS:

  • (Symbol kind, Hash[String, config_value] entries) -> void

Parameters:

  • kind (Symbol)
  • entries (Hash[String, config_value])


370
371
372
373
374
# File 'lib/ibex/configuration.rb', line 370

def validate_source!(kind, entries)
  entries.each_key do |name|
    raise ArgumentError, "unknown #{kind} configuration key: #{name.inspect}" unless @key_index.key?(name)
  end
end

#value(name) ⇒ config_value

RBS:

  • (String name) -> config_value

Parameters:

  • name (String)

Returns:

  • (config_value)


352
353
354
# File 'lib/ibex/configuration.rb', line 352

def value(name)
  fetch(name).value
end

#value_from(key, raw, kind, locations, explicit:) ⇒ Value

RBS:

  • (Key key, config_value raw, Symbol kind, Hash[Symbol, Hash[String, Location]] locations, explicit: bool) -> Value

Parameters:

  • key (Key)
  • raw (config_value)
  • kind (Symbol)
  • locations (Hash[Symbol, Hash[String, Location]])
  • explicit: (Boolean)

Returns:



484
485
486
487
488
# File 'lib/ibex/configuration.rb', line 484

def value_from(key, raw, kind, locations, explicit:)
  Value.new(
    key, raw, origin: origin(kind, key.name, locations), explicit: explicit, canonical: true
  )
end