Class: Menuconform::UcpExporter

Inherits:
Object
  • Object
show all
Defined in:
lib/menuconform/ucp_exporter.rb

Overview

Exports a Menu IR document to the shopping catalog shapes of the pinned UCP spec (product / variant / product_option / price), reporting what the export loses as EXPORT- findings.

The pinned draft's catalog model is retail-shaped: a product carries enumerable fixed-price variants defined by option combinations. A food menu maps onto that only where an item's price is fully determined by required single-select groups (sizes) — those become product options and variants. Everything else has no UCP home today:

  • optional / multi-select / quantity-bearing / nested modifier groups, conditional prices, included quantities -> EXPORT-002 (dropped; the modifier model is stashed in the non-standard metadata extension)
  • half/half slots, allergens, min_age, dietary (degraded to free tags), availability windows, timed suspensions -> EXPORT-002
  • items whose price depends on a group that can't be a variant axis, or whose variant space exceeds the enumeration cap -> EXPORT-001 (the item cannot exist under the pinned draft)

EXPORT- findings surface only through this stage (menuconform export), never through check: the check score judges the menu data, not the current gaps of the UCP draft.

Constant Summary collapse

UCP_SPEC_PIN =
"2026-08-25"
DEFAULT_VARIANT_CAP =
50

Instance Method Summary collapse

Constructor Details

#initialize(catalog:, variant_cap: DEFAULT_VARIANT_CAP) ⇒ UcpExporter

Returns a new instance of UcpExporter.



31
32
33
34
# File 'lib/menuconform/ucp_exporter.rb', line 31

def initialize(catalog:, variant_cap: DEFAULT_VARIANT_CAP)
  @catalog = catalog
  @variant_cap = variant_cap
end

Instance Method Details

#call(doc, reference_time: Time.now.utc) ⇒ Object

Returns { artifact: Hash, findings: [Finding] } (severities stamped).



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/menuconform/ucp_exporter.rb', line 37

def call(doc, reference_time: Time.now.utc)
  menu = Menu.new(doc, reference_time: reference_time)
  findings = []
  products = []

  findings << finding("EXPORT-002", "menu", doc["name"] || "(unnamed)",
                      "menu-level service windows have no UCP catalog construct; agents will not know the ordering hours") if present?(doc["availability"])
  findings << finding("EXPORT-002", "menu", doc["name"] || "(unnamed)",
                      "special_hours (holiday/date exceptions) have no UCP catalog construct") if present?(doc["special_hours"])
  menu.categories_by_id.each_value do |c|
    next unless present?(c["availability"])
    findings << finding("EXPORT-002", "category", c["id"],
                        "category availability windows (dayparts) have no UCP catalog construct")
  end

  menu.items_by_id.each_value do |item|
    product, item_findings = export_item(menu, item)
    findings.concat(item_findings)
    products << product if product
  end

  artifact = {
    "ucp_spec_version" => UCP_SPEC_PIN,
    "generated_by" => "menuconform #{VERSION}",
    "catalog" => { "products" => products }
  }
  { artifact: artifact, findings: findings.each { |f| f.severity = @catalog.severity(f.rule) } }
end