Class: Heatwave::TrackingNumber::Catalog

Inherits:
Object
  • Object
show all
Defined in:
app/lib/heatwave/tracking_number/catalog.rb

Overview

Loads every courier JSON file and answers parse / detect / spec queries.

One directory, one load, no generated per-barcode classes. Add a carrier
by dropping a JSON file next to the others and adding a real test number.

Collision ties (same checksum weight and length) keep the first spec in
filename order — fedex.json before purolator.json — so a 12-digit PIN
that satisfies both stays FedEx unless a caller disambiguates.

Constant Summary collapse

DATA_DIR =

Owned courier JSON. lib/heatwave is not Zeitwerk-loaded (it would
collide with Heatwave::TrackingNumber in app/lib).

Rails.root.join('lib/heatwave/tracking_number/data/couriers')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(directory: DATA_DIR) ⇒ Catalog

Returns a new instance of Catalog.

Parameters:

  • directory (String, Pathname) (defaults to: DATA_DIR)

    courier JSON directory



40
41
42
43
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 40

def initialize(directory: DATA_DIR)
  @specs = load_specs(directory)
  @by_courier = @specs.group_by(&:courier_code)
end

Instance Attribute Details

#specsArray<Spec> (readonly)

Returns:



37
38
39
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 37

def specs
  @specs
end

Class Method Details

.instanceCatalog

Process-wide catalog. Specs are immutable after load.

Returns:



24
25
26
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 24

def instance
  @instance ||= new
end

.reset!void

This method returns an undefined value.

Drop the memoized catalog (tests that point at a temp directory).



31
32
33
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 31

def reset!
  @instance = nil
end

Instance Method Details

#match(value) ⇒ Result

Highest-confidence valid spec, or an unmatched result.

Parameters:

  • value (String, nil)

Returns:



49
50
51
52
53
54
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 49

def match(value)
  normalized = Spec.normalize(value)
  return Result.new(tracking_number: normalized) if normalized.empty?

  matches(normalized).max_by(&:confidence) || Result.new(tracking_number: normalized)
end

#matches(value) ⇒ Array<Result>

Every spec that validates the number (used for collision disambiguation).

Parameters:

  • value (String, nil)

Returns:



60
61
62
63
64
65
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 60

def matches(value)
  normalized = Spec.normalize(value)
  return [] if normalized.empty?

  @specs.filter_map { |spec| spec.result(normalized) }
end

#specs_for(courier_code) ⇒ Array<Spec>

Parameters:

  • courier_code (Symbol, String, nil)

Returns:



69
70
71
72
73
# File 'app/lib/heatwave/tracking_number/catalog.rb', line 69

def specs_for(courier_code)
  return [] if courier_code.nil?

  @by_courier.fetch(courier_code.to_sym, [])
end