Skip to content

How It Works

Braden Keith edited this page Sep 21, 2025 · 2 revisions

How It Works

Understanding the Romega Software - Availability evaluation engine.

Core Concept

The availability engine uses a "last matching rule wins" evaluation strategy. Rules are processed in priority order, and the final matching rule determines the availability state.

Initial State (from default) → Rule 1 → Rule 2 → Rule 3 → Final State

Evaluation Flow

1. Start with Default State

Every resource has a default availability state. You may define this per model by creating a availability_default field that casts to Effect, or if it's not set on the model, it'll default to the config file's value.

$resource->availability_default = Effect::Allow; // or Effect::Deny

This is your starting point before any rules are evaluated.

2. Process Rules by Priority

Rules are evaluated in ascending priority order (lowest to highest):

Priority 10 → Priority 20 → Priority 30 → Priority 40

3. Apply Matching Rules

When a rule matches the given moment, it updates the current state:

// Initial: Deny
// Rule Priority 10 (weekdays): Matches Monday → State becomes Allow
// Rule Priority 20 (time_of_day): Matches 10 AM → State remains Allow
// Rule Priority 30 (blackout): Matches holiday → State becomes Deny
// Final: Deny

4. Return Final State

The state after all rules have been evaluated is the final availability.

Visual Example

Let's trace through an evaluation:

// Resource Configuration (or if not defined, inherit from config file)
$room->availability_default = Effect::Deny;
$room->availability_timezone = 'America/New_York';

// Rules
1. Priority 10: Weekdays (Mon-Fri) → Allow
2. Priority 20: Business hours (9-5) → Allow
3. Priority 30: Lunch break (12-1) → Deny
4. Priority 40: Holiday blackout → Deny

Checking availability for Monday at 12:30 PM (not a holiday):

Start:        Deny (default)
↓
Priority 10:  Matches (Monday is weekday) → Allow
↓
Priority 20:  Matches (12:30 is within 9-5) → Allow (no change)
↓
Priority 30:  Matches (12:30 is within 12-1) → Deny
↓
Priority 40:  Doesn't match (not a holiday) → Deny (no change)
↓
Result:       Deny (unavailable during lunch)

Checking availability for Monday at 10:00 AM (not a holiday):

Start:        Deny (default)
↓
Priority 10:  Matches (Monday is weekday) → Allow
↓
Priority 20:  Matches (10:00 is within 9-5) → Allow (no change)
↓
Priority 30:  Doesn't match (10:00 not in 12-1) → Allow (no change)
↓
Priority 40:  Doesn't match (not a holiday) → Allow (no change)
↓
Result:       Allow (available)

Key Principles

1. Rules Are Stateless

Each rule evaluation is independent. Rules don't know about other rules or previous evaluations.

2. Only Matching Rules Change State

If a rule doesn't match, the current state is preserved:

// Current state: Allow
// Rule: time_of_day (9-5) checking at 6 PM
// Doesn't match → State remains Allow

3. Priority Determines Order, Not Importance

Higher priority numbers are evaluated later, giving them "final say":

// Priority 10: General rule
// Priority 50: Specific override
// Priority 100: Emergency override (evaluated last, wins if matches)

4. Timezone Conversion

All evaluations happen in the resource's timezone:

// Resource timezone: America/New_York
// User checks: 3 PM UTC
// Evaluated as: 10 AM EST

Rule Combination Logic

AND Logic

To require multiple conditions, both rules must match:

// Both must match for Allow
Priority 10: Weekdays → Allow
Priority 20: Business hours → Allow
// Result: Available only on weekdays during business hours

OR Logic

To allow multiple options, use same priority or separate effect groups:

// Either matching allows access
Priority 10: Weekdays → Allow
Priority 10: Special event dates → Allow
// Result: Available on weekdays OR special events

NOT Logic

Use Deny effects to create exclusions:

Priority 10: All December → Allow
Priority 20: December 25 → Deny
// Result: All of December except Christmas

Disabled Rules

Disabled rules are skipped entirely:

$rule->enabled = false; // This rule is ignored during evaluation

Edge Cases

No Rules

When no rules exist, the default effect is used:

// No rules defined
// Result: Uses $resource->availability_default

All Rules Disabled

Same as having no rules - falls back to default:

// All rules have enabled = false
// Result: Uses $resource->availability_default

Unknown Rule Types

Rules with unregistered types are skipped:

// Rule type: 'custom_type' (not registered)
// Result: Skipped, continues to next rule

Null Configuration

Rules with invalid configurations typically don't match:

// Rule config: null or invalid
// Most evaluators return false (doesn't match)

Performance Considerations

Rule Loading

Rules are loaded once per evaluation:

// Single query with eager loading
$resource->availabilityRules()
    ->where('enabled', true)
    ->orderBy('priority')
    ->get();

Evaluation Order

Rules are always processed in order, no short-circuiting:

// Even if Priority 10 denies access,
// Priority 20 could allow it again
// All rules must be evaluated

Caching Strategies

Consider caching for high-traffic scenarios:

Cache::remember("availability.{$resource->id}.{$moment}", 60, function() {
    return $engine->isAvailable($resource, $moment);
});

Next Steps

Getting Started

Installation
Set up the package in your Laravel app

Quick Start
Get running in 5 minutes

Basic Usage
Common patterns and examples


Core Concepts

How It Works
Understanding the evaluation engine

Rule Types
Available rule types and configurations

Priority System
How rule priority affects evaluation


Advanced Topics

Inventory Gates
Dynamic availability based on stock

Custom Evaluators
Build your own rule types

Complex Scenarios
Real-world implementation patterns

Performance Tips
Optimization strategies


API Reference

Configuration
Package configuration options

Models & Traits
Available models and traits

Testing
Testing your availability rules

Clone this wiki locally