Class: Menuconform::Engine

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

Overview

Runs a Menu IR document through schema validation and all implemented rules. Severities always come from the rule catalog. reference_time is the only time input (AVAIL-003 suspension horizon) — inject it for reproducible runs.

Constant Summary collapse

ROOT =
File.expand_path("../..", __dir__)
DEFAULT_SCHEMA =
File.join(ROOT, "schema", "menu_ir.schema.json")
DEFAULT_CATALOG =
File.join(ROOT, "rules", "catalog.json")
RULES =
[
  Rules::StructRules,
  Rules::ConflictRules,
  Rules::PriceRules,
  Rules::AvailRules,
  Rules::NameRules,
  Rules::AllergenRules,
  Rules::AgeRules,
  Rules::CartRules
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema_path: DEFAULT_SCHEMA, catalog_path: DEFAULT_CATALOG) ⇒ Engine

Returns a new instance of Engine.



29
30
31
32
# File 'lib/menuconform/engine.rb', line 29

def initialize(schema_path: DEFAULT_SCHEMA, catalog_path: DEFAULT_CATALOG)
  @schemer = JSONSchemer.schema(JSON.parse(File.read(schema_path, encoding: "UTF-8")))
  @catalog = Catalog.load(catalog_path)
end

Instance Attribute Details

#catalogObject (readonly)

Returns the value of attribute catalog.



27
28
29
# File 'lib/menuconform/engine.rb', line 27

def catalog
  @catalog
end

Instance Method Details

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

Full analysis: findings + score + enriched report.



50
51
52
53
54
55
56
57
# File 'lib/menuconform/engine.rb', line 50

def analyze(doc, reference_time: Time.now.utc)
  Report.new(
    doc: doc,
    findings: run(doc, reference_time: reference_time),
    catalog: @catalog,
    reference_time: reference_time
  )
end

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

doc: parsed IR document (Hash). Returns [Finding].



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/menuconform/engine.rb', line 35

def run(doc, reference_time: Time.now.utc)
  schema_errors = @schemer.validate(doc).to_a
  unless schema_errors.empty?
    detail = schema_errors.first(3).map { |e| "#{e['data_pointer']} #{e['error']}" }.join("; ")
    return [with_severity(Finding.new("STRUCT-001", "menu", doc["name"] || "(unnamed)",
                                      "document fails IR schema validation: #{detail}"))]
  end

  menu = Menu.new(doc, reference_time: reference_time)
  RULES.flat_map { |rule| rule.call(menu) }
       .map { |f| with_severity(f) }
       .sort_by { |f| [f.rule, f.entity_type, f.entity_id.to_s] }
end