# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Parsley is a personal recipe manager and meal planning app. It is a monolithic Rails 7.2 application with a Vue 3 SPA frontend.

**Backend:** Ruby 4.0.2, Rails 8.1.3, SQLite (dev/test), PostgreSQL (production)
**Frontend:** Vue 3 (Composition API / `<script setup>`), Pinia, Vue Router 4, Bulma CSS
**Asset bundling:** Shakapacker 8 (Webpack 5 + SWC)
**Testing:** RSpec, FactoryBot, Guard

## Common Commands

```bash
# Development
foreman start                          # Start Rails + Shakapacker dev server together

# Dependencies
bundle install && yarn install

# Database
bundle exec rake db:create db:migrate db:seed
bundle exec rake db:reset              # Drop, recreate, seed

# Tests
bundle exec rspec                      # All tests
bundle exec rspec spec/models/recipe_spec.rb        # Single file
bundle exec rspec spec/models/recipe_spec.rb:42     # Single test by line
bundle exec guard                      # Watch mode

# Build
bundle exec rake shakapacker:compile   # Compile assets for production
```

**Test environment note:** Set `FAST=true` to skip FactoryBot linting on startup.

## Architecture

### Backend

**Models follow an `Ingredient` abstraction:** `Recipe` and `Food` both inherit from an abstract `Ingredient` base class. This allows recipes to contain both foods and other recipes as ingredients (`RecipeIngredient` is a polymorphic join). Polymorphic ingredient IDs are encoded as strings: `F{id}` for foods, `R{id}` for recipes.

**Custom unit conversion library** (`lib/unit_conversion/`): The `UnitConversion` module handles all quantity parsing and conversion. It is density-aware (can convert volume ↔ mass if food density is known) and supports custom per-food units defined in `FoodUnit` records. Key entry points: `UnitConversion.parse`, `UnitConversion.auto_unit`, `UnitConversion.with_custom_units`.

**Recipe scaling:** Recipes can be scaled by a factor and converted between unit systems (standard/metric) and unit types (mass/volume). Cache keys incorporate scale and unit system: `recipes/{id}/{scale}/{system}/{unit}`. Memcache is optional; enable with `RAILS_USE_MEMCACHE=true`.

**USDA nutrition data:** The `UsdaImporter` (lib/) imports from `vendor/data/usda/` during `db:seed`. `UsdaFood`/`UsdaFoodWeight` tables are a read-only cache; `Food` records link to them and inherit nutritional data.

**Serializers** (`app/serializers/`) handle all JSON API responses.

### Frontend

Vue 3 components live in `app/javascript/components/`. They use `<script setup>` Composition API style. Pinia stores (`app/javascript/stores/`) hold global state. Vue Router (`app/javascript/router/`) manages navigation.

Page-level components are named `The*` (e.g., `TheRecipeList`, `TheFood`, `TheCalculator`). Shared UI primitives are named `App*` (e.g., `AppModal`, `AppAutocomplete`, `AppIcon`).

### Key Environment Variables

| Variable | Purpose |
|----------|---------|
| `RAILS_USE_MEMCACHE` | Enable Memcache caching |
| `RAILS_MEMCACHE_HOST` | Memcache host (default: `memcache`) |
| `PARSLEY_DB_HOST/USER/PG_PASSWORD/DB_NAME` | Production PostgreSQL config |

### Docker

`docker-compose up` starts PostgreSQL, Memcached, Nginx, and two Rails service instances. See `docker-compose.yml` and `Dockerfile`.

### Seed Data

Default dev user: username `dan`, password `qwerty`. Comes with sample recipes and foods.
