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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user