Class: WhittakerTech::Aeon::Resolver

Inherits:
Object
  • Object
show all
Defined in:
app/services/whittaker_tech/aeon/resolver.rb

Overview

Stateless read-path service that merges allocations, occurrences, overrides, and invalidations into a sorted array of frozen ResolvedOccurrence value objects.

The Resolver answers one question: “What actually happens within this window?”

It does NOT trigger projection, snapshot hosts, enforce domain rules, or format UI payloads.

Examples:

Resolve a 2-week window for a host

Resolver.between(
  schedulable: lesson,
  range: Time.current..(Time.current + 2.weeks),
  label: :time_slot
)
# => [#<ResolvedOccurrence ...>, ...]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schedulable:, range:, label:) ⇒ Resolver

Returns a new instance of Resolver.

Parameters:

  • schedulable (ActiveRecord::Base)

    host AR object

  • range (Range<Time>)

    query window

  • label (String, Symbol, nil)

    optional label filter



35
36
37
38
39
# File 'app/services/whittaker_tech/aeon/resolver.rb', line 35

def initialize(schedulable:, range:, label:)
  @schedulable = schedulable
  @range = range
  @label = label&.to_s
end

Class Method Details

.between(schedulable:, range:, label: nil) ⇒ Array<ResolvedOccurrence>

Returns frozen, sorted by +starts_at+.

Parameters:

  • schedulable (ActiveRecord::Base)

    host AR object

  • range (Range<Time>)

    query window

  • label (String, Symbol, nil) (defaults to: nil)

    optional schedulable_label filter; nil returns all labels

Returns:



28
29
30
# File 'app/services/whittaker_tech/aeon/resolver.rb', line 28

def self.between(schedulable:, range:, label: nil)
  new(schedulable: schedulable, range: range, label: label).call
end

Instance Method Details

#callArray<ResolvedOccurrence>

Fetches allocations and their active occurrences within the window, applies overrides, and returns frozen value objects.

Returns:



45
46
47
48
49
50
51
52
53
# File 'app/services/whittaker_tech/aeon/resolver.rb', line 45

def call
  allocations = fetch_allocations
  return [].freeze if allocations.empty?

  label_map = allocations.each_with_object({}) { |a, h| h[a.id] = a.schedulable_label }
  occurrences = fetch_occurrences(allocations)

  resolve(occurrences, label_map).sort_by(&:starts_at).freeze
end