# PropertyWebBuilder Seed Packs System: Scenario-Based Site Setup Guide

> Effortlessly set up multi-tenant property websites with PropertyWebBuilder's Seed Packs system. Deploy sites from YAML definitions using one command or API call.

- Repository: [Ed Tee/property_web_builder](https://github.com/etewiah/property_web_builder)
- Tags: how-to-guide
- Published: 2026-03-01

---

**PropertyWebBuilder's Seed Packs system enables developers to provision complete multi-tenant property websites from YAML scenario definitions using a single command or API call.**

The **Seed Packs** system in PropertyWebBuilder (PWB) provides an extensible, scenario-driven approach to tenant provisioning. By bundling configuration, content, and assets into self-contained YAML definitions, developers can instantiate fully-featured real estate websites—complete with themes, properties, translations, and user accounts—without manual database seeding.

## What Are Seed Packs?

A **seed pack** is a self-contained directory structure that describes every aspect of a property website. Stored under `db/seeds/packs/`, each pack contains a central [`pack.yml`](https://github.com/etewiah/property_web_builder/blob/main/pack.yml) manifest alongside subdirectories for properties, pages, translations, and images. When applied, the pack creates or updates a `Pwb::Website` tenant with the specified theme, agency data, navigation links, and content translations.

Unlike traditional Rails database seeds, seed packs support **scenario-based inheritance**, allowing base configurations to be extended for specific markets (e.g., luxury Spanish properties vs. standard residential listings).

## Core Architecture and Components

The seed packs architecture consists of four primary components that orchestrate the loading, validation, and application of scenario definitions.

### Pwb::SeedPack Core Class

The `Pwb::SeedPack` class in [`lib/pwb/seed_pack.rb`](https://github.com/etewiah/property_web_builder/blob/main/lib/pwb/seed_pack.rb) serves as the primary interface for pack operations. It handles:

- **Path resolution**: Locates packs within `PACKS_PATHS` directories
- **Schema validation**: Ensures required keys (`display_name`, `website`) exist
- **Inheritance resolution**: Loads parent packs defined in `inherits_from`
- **Seeding orchestration**: Executes the ten-step application flow

The class provides both destructive (`apply!`) and introspection (`preview`) methods for safe deployment workflows.

### Rake Task Interface

CLI operations are exposed through `lib/tasks/seed_packs.rake` under the `pwb:seed_packs` namespace:

- `pwb:seed_packs:list` – Enumerates available packs
- `pwb:seed_packs:preview[pack_name]` – Generates a dry-run summary
- `pwb:seed_packs:apply[pack_name,subdomain]` – Provisions a new tenant

These tasks instantiate the core `SeedPack` class and manage the target `Pwb::Website` lifecycle.

### Pack Directory Structure

Each pack follows a standardized layout within `db/seeds/packs/`:

```text
spain_luxury/
├── pack.yml              # Manifest and configuration

├── properties/           # YAML property definitions

├── pages/               # Page content files

├── field_keys.yml       # Custom attribute definitions

├── links.yml            # Navigation structure

├── translations/        # I18n locale files

└── images/              # Binary assets or URL manifests

```

The [`pack.yml`](https://github.com/etewiah/property_web_builder/blob/main/pack.yml) file defines metadata, inheritance relationships, and configuration hashes for the website, agency, and user accounts.

### Helper Seeder Integration

When a pack omits specific asset types, the system falls back to existing infrastructure:

- `Pwb::PagesSeeder` – Supplies default page layouts when `pages/` is empty
- `Pwb::ContentsSeeder` – Populates translation keys when `content/` is missing
- `Pwb::SeedImages` – Generates external image URLs via `attach_property_image` to avoid storage bloat

## Step-by-Step Seeding Execution Flow

When `Pwb::SeedPack#apply!` is invoked, the system executes a deterministic twelve-step process:

1. **Initialize** – Loads [`pack.yml`](https://github.com/etewiah/property_web_builder/blob/main/pack.yml) and validates required keys
2. **Inheritance** – Recursively applies parent packs via `apply_parent_pack!` with `skip_website` and `skip_agency` flags to prevent overwrites
3. **Website Configuration** – Executes `seed_website` to set theme, locales, palette, and search configuration
4. **Agency Provisioning** – Creates or updates the agency record and address via `seed_agency`
5. **Field Keys** – Loads [`field_keys.yml`](https://github.com/etewiah/property_web_builder/blob/main/field_keys.yml) (supporting legacy list or nested hash formats)
6. **Navigation Links** – Seeds navigation items from [`links.yml`](https://github.com/etewiah/property_web_builder/blob/main/links.yml) with slug-based deduplication
7. **Page Structure** – Loads YAML files from `pages/` directory
8. **Page Parts** – Applies pack-specific page parts or falls back to `PagesSeeder`
9. **Property Import** – Creates `Pwb::RealtyAsset` records and sale/rental listings from `properties/`
10. **Content Population** – Seeds `Website.contents` and translations via `seed_content`
11. **User Creation** – Generates admin and member accounts with `UserMembership` associations
12. **Materialized View Refresh** – Calls `Pwb::ListedProperty.refresh` to update the search index

The method returns `true` on completion or raises a detailed error if validation fails.

## CLI Operations for Pack Management

### Listing Available Packs

Enumerate all valid seed packs in the load path:

```bash
rails pwb:seed_packs:list

```

Internally, this invokes `Pwb::SeedPack.available`, which scans `PACKS_PATHS` directories for valid [`pack.yml`](https://github.com/etewiah/property_web_builder/blob/main/pack.yml) files.

### Previewing Pack Contents

Generate a JSON summary of what would be created without modifying the database:

```bash
rails pwb:seed_packs:preview[spain_luxury]

```

Output includes pack metadata, inheritance chain, entity counts, and supported locales:

```json
{
  "pack_name": "spain_luxury",
  "display_name": "Spanish Luxury Real Estate",
  "inherits_from": "base",
  "website": { "theme_name": "bristol" },
  "properties": 7,
  "locales": ["es", "en", "de"],
  "users": 1
}

```

### Applying a Pack to a Tenant

Provision a new website using a specific pack:

```bash
rails pwb:seed_packs:apply[spain_luxury,costa-luxury]

```

This command:
1. Locates or creates a `Pwb::Website` with subdomain `costa-luxury`
2. Assigns the theme specified in the pack configuration
3. Executes the full seeding flow via `Pwb::SeedPack.find('spain_luxury').apply!(website: website)`

## Programmatic API Usage

For automated provisioning workflows, interact with seed packs directly in Ruby:

```ruby

# Load a pack definition

pack = Pwb::SeedPack.find('spain_luxury')

# Create a tenant website

website = Pwb::Website.create!(
  subdomain: 'costa-demo',
  theme_name: 'bristol'
)

# Apply the pack with options

pack.apply!(website: website, options: { dry_run: false })

```

The `apply!` method accepts a `website` instance and optional configuration hashes, enabling integration with background job processors like Sidekiq for automated tenant onboarding.

## Creating Custom Packs with Inheritance

Extend existing packs using the `inherits_from` directive to avoid duplication of common data (field keys, base pages, standard translations).

Define a premium variant of an existing pack:

```yaml

# db/seeds/packs/spain_luxury_premium/pack.yml

name: spain_luxury_premium
display_name: "Spanish Luxury – Premium"
inherits_from: spain_luxury

website:
  theme_name: bristol
  default_client_locale: es
  supported_locales: [es, en, de, fr]

```

When applied, the system first executes the parent `spain_luxury` pack (skipping website and agency creation if already present), then overlays the premium-specific configuration. Add supplementary property files under `properties/` or override specific content keys to customize the scenario.

## Summary

- **Seed packs** are self-contained YAML bundles in `db/seeds/packs/` that define complete website scenarios including themes, properties, and translations.
- The **`Pwb::SeedPack`** class in [`lib/pwb/seed_pack.rb`](https://github.com/etewiah/property_web_builder/blob/main/lib/pwb/seed_pack.rb) orchestrates validation, inheritance, and the twelve-step seeding process.
- CLI tools in `lib/tasks/seed_packs.rake` provide `list`, `preview`, and `apply` commands for pack management.
- **Inheritance** via `inherits_from` enables pack specialization without duplication, applying parent configurations first with selective skipping of website and agency data.
- The system integrates with existing seeders (`PagesSeeder`, `ContentsSeeder`) and supports external image URLs via `Pwb::SeedImages` to minimize storage overhead.

## Frequently Asked Questions

### How do I create a new seed pack from scratch?

Create a directory under `db/seeds/packs/` containing a [`pack.yml`](https://github.com/etewiah/property_web_builder/blob/main/pack.yml) file with required keys (`name`, `display_name`, `website`). Add subdirectories for `properties/`, `pages/`, or `translations/` as needed. Validate your structure by running `rails pwb:seed_packs:list` to ensure the pack appears in the registry.

### What happens if a pack references missing images?

When `Pwb::SeedImages.enabled?` returns true, the `attach_property_image` method generates external URLs on-the-fly rather than storing binary data. If image files are missing from the pack's `images/` directory, the seeder logs a warning but continues processing the property record, ensuring partial seeding does not fail.

### Can I apply multiple packs to the same website?

Yes. Invoke `apply!` sequentially with different pack instances. The system updates existing records (e.g., agency data) or creates missing entities (e.g., new properties) based on unique identifiers like slugs. Use inheritance instead of sequential application if packs represent logical extensions of the same scenario.

### How do I override specific pages without copying the entire pack?

Create a child pack using `inherits_from` pointing to the base pack. Place only the modified YAML files in the child pack's `pages/` or `content/` directories. The seeding process prioritizes pack-specific files over parent definitions and default seeders, applying your overrides while retaining base configuration.