rename project from SimpleshopTheme to Berrypod

All modules, configs, paths, and references updated.
836 tests pass, zero warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-18 21:23:15 +00:00
parent c65e777832
commit 9528700862
300 changed files with 23932 additions and 1349 deletions

View File

@@ -126,9 +126,9 @@ For a production-quality system, Oban's durability is worth the small complexity
```elixir
# config/config.exs
config :simpleshop_theme, Oban,
config :berrypod, Oban,
engine: Oban.Engines.Lite, # SQLite support
repo: SimpleshopTheme.Repo,
repo: Berrypod.Repo,
plugins: [
# Prune completed jobs after 60 seconds - keeps DB lean
{Oban.Plugins.Pruner, max_age: 60}
@@ -144,7 +144,7 @@ With `max_age: 60`, the Oban tables typically contain:
### Job Worker
```elixir
defmodule SimpleshopTheme.Workers.ImageVariants do
defmodule Berrypod.Workers.ImageVariants do
use Oban.Worker, queue: :images, max_attempts: 3
@impl Oban.Worker
@@ -168,7 +168,7 @@ def upload_image(attrs) do
{:ok, image} ->
# Enqueue async variant generation
%{image_id: image.id}
|> SimpleshopTheme.Workers.ImageVariants.new()
|> Berrypod.Workers.ImageVariants.new()
|> Oban.insert()
{:ok, image}
@@ -195,7 +195,7 @@ defp ensure_all_variants do
|> Enum.each(fn image ->
# Re-enqueue for processing
%{image_id: image.id}
|> SimpleshopTheme.Workers.ImageVariants.new()
|> Berrypod.Workers.ImageVariants.new()
|> Oban.insert()
end)
end
@@ -223,7 +223,7 @@ end
**File:** `priv/repo/migrations/YYYYMMDDHHMMSS_add_image_metadata.exs`
```elixir
defmodule SimpleshopTheme.Repo.Migrations.AddImageMetadata do
defmodule Berrypod.Repo.Migrations.AddImageMetadata do
use Ecto.Migration
def change do
@@ -244,10 +244,10 @@ end
### Step 3: Image Optimizer Module
**File:** `lib/simpleshop_theme/images/optimizer.ex`
**File:** `lib/berrypod/images/optimizer.ex`
```elixir
defmodule SimpleshopTheme.Images.Optimizer do
defmodule Berrypod.Images.Optimizer do
@moduledoc """
Generates optimized image variants. Only creates sizes ≤ source dimensions.
"""
@@ -290,7 +290,7 @@ defmodule SimpleshopTheme.Images.Optimizer do
Called by Oban worker.
"""
def process_for_image(image_id) do
alias SimpleshopTheme.{Repo, Media.Image}
alias Berrypod.{Repo, Media.Image}
case Repo.get(Image, image_id) do
nil ->
@@ -387,16 +387,16 @@ end
### Step 4: Oban Worker
**File:** `lib/simpleshop_theme/workers/image_variants.ex`
**File:** `lib/berrypod/workers/image_variants.ex`
```elixir
defmodule SimpleshopTheme.Workers.ImageVariants do
defmodule Berrypod.Workers.ImageVariants do
use Oban.Worker,
queue: :images,
max_attempts: 3,
unique: [period: 60] # Prevent duplicate jobs
alias SimpleshopTheme.Images.Optimizer
alias Berrypod.Images.Optimizer
@impl Oban.Worker
def perform(%Oban.Job{args: %{"image_id" => image_id}}) do
@@ -410,11 +410,11 @@ end
### Step 5: Update Media Module
**File:** `lib/simpleshop_theme/media.ex`
**File:** `lib/berrypod/media.ex`
```elixir
alias SimpleshopTheme.Images.Optimizer
alias SimpleshopTheme.Workers.ImageVariants
alias Berrypod.Images.Optimizer
alias Berrypod.Workers.ImageVariants
def upload_image(attrs) do
# Convert to lossless WebP before storing
@@ -449,10 +449,10 @@ end
### Step 6: Startup Recovery GenServer
**File:** `lib/simpleshop_theme/images/variant_cache.ex`
**File:** `lib/berrypod/images/variant_cache.ex`
```elixir
defmodule SimpleshopTheme.Images.VariantCache do
defmodule Berrypod.Images.VariantCache do
@moduledoc """
Ensures all image variants exist on startup.
Enqueues Oban jobs for any missing variants.
@@ -461,10 +461,10 @@ defmodule SimpleshopTheme.Images.VariantCache do
use GenServer
require Logger
alias SimpleshopTheme.Repo
alias SimpleshopTheme.Media.Image, as: ImageSchema
alias SimpleshopTheme.Images.Optimizer
alias SimpleshopTheme.Workers.ImageVariants
alias Berrypod.Repo
alias Berrypod.Media.Image, as: ImageSchema
alias Berrypod.Images.Optimizer
alias Berrypod.Workers.ImageVariants
import Ecto.Query
def start_link(_opts) do
@@ -521,7 +521,7 @@ end
### Step 7: Responsive Image Component
**File:** `lib/simpleshop_theme_web/components/shop_components.ex`
**File:** `lib/berrypod_web/components/shop_components.ex`
```elixir
@doc """
@@ -547,7 +547,7 @@ attr :height, :integer, default: nil
attr :priority, :boolean, default: false
def responsive_image(assigns) do
alias SimpleshopTheme.Images.Optimizer
alias Berrypod.Images.Optimizer
# Compute available widths from source dimensions
available = Optimizer.applicable_widths(assigns.source_width)
@@ -596,7 +596,7 @@ end
### Step 8: Update Thumbnail Serving
**File:** `lib/simpleshop_theme_web/controllers/image_controller.ex`
**File:** `lib/berrypod_web/controllers/image_controller.ex`
```elixir
def thumbnail(conn, %{"id" => id}) do
@@ -750,20 +750,20 @@ end
## File Changes Summary
### Create:
- `lib/simpleshop_theme/images/optimizer.ex` - Core optimization logic
- `lib/simpleshop_theme/images/variant_cache.ex` - Startup recovery GenServer
- `lib/simpleshop_theme/workers/image_variants.ex` - Oban worker
- `lib/berrypod/images/optimizer.ex` - Core optimization logic
- `lib/berrypod/images/variant_cache.ex` - Startup recovery GenServer
- `lib/berrypod/workers/image_variants.ex` - Oban worker
- `lib/mix/tasks/optimize_images.ex` - Mockup batch processing
- `priv/repo/migrations/*_add_image_metadata.exs` - Schema + Oban tables
### Modify:
- `mix.exs` - Add Oban dependency
- `config/config.exs` - Oban configuration
- `lib/simpleshop_theme/media/image.ex` - Add source_width/height, remove thumbnail_data
- `lib/simpleshop_theme/media.ex` - Convert to WebP, enqueue Oban job
- `lib/simpleshop_theme/application.ex` - Add Oban + VariantCache to supervision
- `lib/simpleshop_theme_web/components/shop_components.ex` - Responsive image component
- `lib/simpleshop_theme_web/controllers/image_controller.ex` - Serve thumbnails from disk
- `lib/berrypod/media/image.ex` - Add source_width/height, remove thumbnail_data
- `lib/berrypod/media.ex` - Convert to WebP, enqueue Oban job
- `lib/berrypod/application.ex` - Add Oban + VariantCache to supervision
- `lib/berrypod_web/components/shop_components.ex` - Responsive image component
- `lib/berrypod_web/controllers/image_controller.ex` - Serve thumbnails from disk
- `.gitignore` - Add `/priv/static/image_cache/`
---
@@ -832,14 +832,14 @@ mix phx.server
**Files to create/modify:**
- `priv/repo/migrations/*_add_image_metadata.exs` - New migration
- `lib/simpleshop_theme/media/image.ex` - Add source_width/height, variants_status; remove thumbnail_data
- `lib/berrypod/media/image.ex` - Add source_width/height, variants_status; remove thumbnail_data
**Tests:** `mix ecto.migrate && mix test test/simpleshop_theme/media_test.exs` (if exists)
**Tests:** `mix ecto.migrate && mix test test/berrypod/media_test.exs` (if exists)
**Manual verification:**
```bash
# Check migration applied
sqlite3 simpleshop_theme_dev.db ".schema images"
sqlite3 berrypod_dev.db ".schema images"
# Should show new columns: source_width, source_height, variants_status
# Should NOT have: thumbnail_data
```
@@ -851,17 +851,17 @@ sqlite3 simpleshop_theme_dev.db ".schema images"
### Phase 3: Optimizer Module
**Files to create:**
- `lib/simpleshop_theme/images/optimizer.ex`
- `test/simpleshop_theme/images/optimizer_test.exs`
- `lib/berrypod/images/optimizer.ex`
- `test/berrypod/images/optimizer_test.exs`
- `test/support/fixtures/image_fixtures.ex`
- `test/fixtures/sample_1200x800.jpg` (test image)
**Tests:** `mix test test/simpleshop_theme/images/optimizer_test.exs`
**Tests:** `mix test test/berrypod/images/optimizer_test.exs`
**Manual verification:**
```elixir
# In iex -S mix
alias SimpleshopTheme.Images.Optimizer
alias Berrypod.Images.Optimizer
Optimizer.applicable_widths(1500) # Should return [400, 800, 1200]
Optimizer.applicable_widths(300) # Should return [300]
```
@@ -873,13 +873,13 @@ Optimizer.applicable_widths(300) # Should return [300]
### Phase 4: Oban Worker
**Files to create:**
- `lib/simpleshop_theme/workers/image_variants.ex`
- `test/simpleshop_theme/workers/image_variants_test.exs`
- `lib/berrypod/workers/image_variants.ex`
- `test/berrypod/workers/image_variants_test.exs`
**Files to modify:**
- `lib/simpleshop_theme/application.ex` - Add Oban to supervision tree
- `lib/berrypod/application.ex` - Add Oban to supervision tree
**Tests:** `mix test test/simpleshop_theme/workers/image_variants_test.exs`
**Tests:** `mix test test/berrypod/workers/image_variants_test.exs`
**Manual verification:**
```bash
@@ -894,15 +894,15 @@ mix phx.server
### Phase 5: Media Module Integration
**Files to modify:**
- `lib/simpleshop_theme/media.ex` - Update upload_image to use optimizer + enqueue worker
- `lib/berrypod/media.ex` - Update upload_image to use optimizer + enqueue worker
**Tests:** `mix test test/simpleshop_theme/media_test.exs` (existing tests should pass)
**Tests:** `mix test test/berrypod/media_test.exs` (existing tests should pass)
**Manual verification:**
1. Go to `/admin/theme`
2. Upload a new logo image
3. Check `priv/static/image_cache/` for generated variants
4. Check database: `sqlite3 simpleshop_theme_dev.db "SELECT id, source_width, variants_status FROM images ORDER BY inserted_at DESC LIMIT 1"`
4. Check database: `sqlite3 berrypod_dev.db "SELECT id, source_width, variants_status FROM images ORDER BY inserted_at DESC LIMIT 1"`
**Commit:** `feat: integrate optimizer with image uploads`
@@ -911,10 +911,10 @@ mix phx.server
### Phase 6: VariantCache GenServer
**Files to create:**
- `lib/simpleshop_theme/images/variant_cache.ex`
- `lib/berrypod/images/variant_cache.ex`
**Files to modify:**
- `lib/simpleshop_theme/application.ex` - Add VariantCache to supervision tree
- `lib/berrypod/application.ex` - Add VariantCache to supervision tree
**Tests:** Manual (startup behavior)
@@ -934,12 +934,12 @@ mix phx.server
### Phase 7: Responsive Image Component
**Files to create:**
- `test/simpleshop_theme_web/components/shop_components_test.exs`
- `test/berrypod_web/components/shop_components_test.exs`
**Files to modify:**
- `lib/simpleshop_theme_web/components/shop_components.ex` - Add responsive_image component
- `lib/berrypod_web/components/shop_components.ex` - Add responsive_image component
**Tests:** `mix test test/simpleshop_theme_web/components/shop_components_test.exs`
**Tests:** `mix test test/berrypod_web/components/shop_components_test.exs`
**Manual verification:**
```elixir
@@ -954,7 +954,7 @@ mix phx.server
### Phase 8: ImageController Disk Serving
**Files to modify:**
- `lib/simpleshop_theme_web/controllers/image_controller.ex` - Update thumbnail to serve from disk
- `lib/berrypod_web/controllers/image_controller.ex` - Update thumbnail to serve from disk
**Tests:** Existing controller tests should pass
@@ -1040,8 +1040,8 @@ ls -la priv/static/mockups/
6. **Run tests:**
```bash
mix test test/simpleshop_theme/images/
mix test test/simpleshop_theme/workers/
mix test test/berrypod/images/
mix test test/berrypod/workers/
```
7. **Re-run Lighthouse:**
@@ -1063,12 +1063,12 @@ ls -la priv/static/mockups/
```
test/
├── simpleshop_theme/
├── berrypod/
│ ├── images/
│ │ └── optimizer_test.exs # Core optimization logic
│ └── workers/
│ └── image_variants_test.exs # Oban worker
├── simpleshop_theme_web/
├── berrypod_web/
│ └── components/
│ └── shop_components_test.exs # responsive_image component
├── mix/
@@ -1085,16 +1085,16 @@ test/
**`test/support/fixtures/image_fixtures.ex`:**
```elixir
defmodule SimpleshopTheme.ImageFixtures do
alias SimpleshopTheme.Repo
alias SimpleshopTheme.Media.Image
defmodule Berrypod.ImageFixtures do
alias Berrypod.Repo
alias Berrypod.Media.Image
@sample_jpeg File.read!("test/fixtures/sample_1200x800.jpg")
def sample_jpeg, do: @sample_jpeg
def image_fixture(attrs \\ %{}) do
{:ok, webp, w, h} = SimpleshopTheme.Images.Optimizer.to_lossless_webp(@sample_jpeg)
{:ok, webp, w, h} = Berrypod.Images.Optimizer.to_lossless_webp(@sample_jpeg)
defaults = %{
image_type: "product",
@@ -1132,23 +1132,23 @@ defmodule SimpleshopTheme.ImageFixtures do
def cache_path(id, width, format) do
ext = if format == :jpg, do: "jpg", else: Atom.to_string(format)
Path.join(SimpleshopTheme.Images.Optimizer.cache_dir(), "#{id}-#{width}.#{ext}")
Path.join(Berrypod.Images.Optimizer.cache_dir(), "#{id}-#{width}.#{ext}")
end
def cleanup_cache do
cache_dir = SimpleshopTheme.Images.Optimizer.cache_dir()
cache_dir = Berrypod.Images.Optimizer.cache_dir()
if File.exists?(cache_dir), do: File.rm_rf!(cache_dir)
end
end
```
**`test/simpleshop_theme/images/optimizer_test.exs`:**
**`test/berrypod/images/optimizer_test.exs`:**
```elixir
defmodule SimpleshopTheme.Images.OptimizerTest do
use SimpleshopTheme.DataCase, async: false
defmodule Berrypod.Images.OptimizerTest do
use Berrypod.DataCase, async: false
alias SimpleshopTheme.Images.Optimizer
import SimpleshopTheme.ImageFixtures
alias Berrypod.Images.Optimizer
import Berrypod.ImageFixtures
setup do
cleanup_cache()
@@ -1256,15 +1256,15 @@ defmodule SimpleshopTheme.Images.OptimizerTest do
end
```
**`test/simpleshop_theme/workers/image_variants_test.exs`:**
**`test/berrypod/workers/image_variants_test.exs`:**
```elixir
defmodule SimpleshopTheme.Workers.ImageVariantsTest do
use SimpleshopTheme.DataCase, async: false
use Oban.Testing, repo: SimpleshopTheme.Repo
defmodule Berrypod.Workers.ImageVariantsTest do
use Berrypod.DataCase, async: false
use Oban.Testing, repo: Berrypod.Repo
alias SimpleshopTheme.Workers.ImageVariants
alias SimpleshopTheme.Media.Image
import SimpleshopTheme.ImageFixtures
alias Berrypod.Workers.ImageVariants
alias Berrypod.Media.Image
import Berrypod.ImageFixtures
setup do
cleanup_cache()
@@ -1297,13 +1297,13 @@ defmodule SimpleshopTheme.Workers.ImageVariantsTest do
end
```
**`test/simpleshop_theme_web/components/shop_components_test.exs`:**
**`test/berrypod_web/components/shop_components_test.exs`:**
```elixir
defmodule SimpleshopThemeWeb.ShopComponentsTest do
use SimpleshopThemeWeb.ConnCase, async: true
defmodule BerrypodWeb.ShopComponentsTest do
use BerrypodWeb.ConnCase, async: true
import Phoenix.LiveViewTest
alias SimpleshopThemeWeb.ShopComponents
alias BerrypodWeb.ShopComponents
describe "responsive_image/1" do
test "builds srcset with all widths for 1200px source" do