Add representative dev seed data #21

Merged
ldraney merged 2 commits from 19-dev-seed-data into main 2026-06-08 03:16:00 +00:00
Owner

Summary

  • Populates db/seeds.rb with 18 realistic links so local development has representative data instead of 3 placeholders
  • Covers four categories: infrastructure, dev references, portfolio projects, and personal utilities
  • Uses find_or_create_by keyed on url for idempotent seeding (safe to run repeatedly)

Changes

  • db/seeds.rb: replaced boilerplate comments with 18 seed links spanning infrastructure/DevOps (ArgoCD, Harbor, Grafana, Woodpecker, Forgejo, Keycloak), development references (Ruby, Rails, PostgreSQL, Tailwind CSS), portfolio/projects (palinks, basketball-api, homelab, personal site), and personal/utility (brain.fm, calendar, email, GitHub). Includes a mix of favorited and non-favorited links, with and without forgejo_url, varied metadata tags, and staggered position values.

Test Plan

  • ruby -c db/seeds.rb passes syntax check
  • Run bin/rails db:seed in the dev container — should create 18 links
  • Run bin/rails db:seed a second time — count should remain 18 (idempotency)
  • Verify links appear in the UI with correct ordering (favorites first, then by position)
  • Confirm metadata tags are queryable via Link.tagged("infra") etc.

Review Checklist

  • Passed automated review-fix loop
  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • Feature flag needed? No — seed data for local development only, no new user-visible workflow or external integration
  • ldraney/palinks #19 — the Forgejo issue this PR implements
  • palinks — personal link dashboard project

Closes #19

## Summary - Populates `db/seeds.rb` with 18 realistic links so local development has representative data instead of 3 placeholders - Covers four categories: infrastructure, dev references, portfolio projects, and personal utilities - Uses `find_or_create_by` keyed on `url` for idempotent seeding (safe to run repeatedly) ## Changes - `db/seeds.rb`: replaced boilerplate comments with 18 seed links spanning infrastructure/DevOps (ArgoCD, Harbor, Grafana, Woodpecker, Forgejo, Keycloak), development references (Ruby, Rails, PostgreSQL, Tailwind CSS), portfolio/projects (palinks, basketball-api, homelab, personal site), and personal/utility (brain.fm, calendar, email, GitHub). Includes a mix of favorited and non-favorited links, with and without `forgejo_url`, varied metadata tags, and staggered position values. ## Test Plan - [x] `ruby -c db/seeds.rb` passes syntax check - [ ] Run `bin/rails db:seed` in the dev container — should create 18 links - [ ] Run `bin/rails db:seed` a second time — count should remain 18 (idempotency) - [ ] Verify links appear in the UI with correct ordering (favorites first, then by position) - [ ] Confirm metadata tags are queryable via `Link.tagged("infra")` etc. ## Review Checklist - [x] Passed automated review-fix loop - [x] No secrets committed - [x] No unnecessary file changes - [x] Commit messages are descriptive - [x] Feature flag needed? No — seed data for local development only, no new user-visible workflow or external integration ## Related Notes - `ldraney/palinks #19` — the Forgejo issue this PR implements - `palinks` — personal link dashboard project Closes #19
Populates local development with realistic links: infrastructure tools
(ArgoCD, Harbor, Grafana, Woodpecker, Forgejo, Keycloak), development
references (Ruby, Rails, PostgreSQL, Tailwind), portfolio projects
(palinks, basketball-api, homelab, personal site), and personal
utilities (brain.fm, calendar, email, GitHub). Uses find_or_create_by
for idempotency. Mix of favorited/unfavorited, with/without forgejo_url,
and varied metadata tags and position values.

Closes #19

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

Review — PR #21

Scope: Single file change (db/seeds.rb), 188 additions / 8 deletions.

Correctness

  • find_or_create_by!(url:) is the correct idempotency pattern — keyed on url, the natural unique identifier
  • All 18 entries supply url and title (both required by model validations)
  • All URLs use https:// scheme, satisfying the URI::DEFAULT_PARSER format validation
  • metadata hashes use { tags: [...] } structure, compatible with the tagged scope's @> JSONB operator
  • position values use logical grouping by category (1-6, 10-13, 20-23, 30-33), leaving room for insertions
  • favorite is boolean throughout, matching the default: false, null: false schema constraint
  • No visibility column referenced — correct, it does not exist in the current schema

Data Coverage

  • 18 links across 4 categories (infra: 6, dev refs: 4, portfolio: 4, personal: 4)
  • 8 favorited, 10 not favorited
  • 3 with forgejo_url, 15 without
  • Tag variety: 12 distinct tags across entries
  • No sensitive or real credential data

No Issues Found

Clean implementation. No fixes needed.

VERDICT: APPROVED

## Review — PR #21 **Scope:** Single file change (`db/seeds.rb`), 188 additions / 8 deletions. ### Correctness - `find_or_create_by!(url:)` is the correct idempotency pattern — keyed on `url`, the natural unique identifier - All 18 entries supply `url` and `title` (both required by model validations) - All URLs use `https://` scheme, satisfying the `URI::DEFAULT_PARSER` format validation - `metadata` hashes use `{ tags: [...] }` structure, compatible with the `tagged` scope's `@>` JSONB operator - `position` values use logical grouping by category (1-6, 10-13, 20-23, 30-33), leaving room for insertions - `favorite` is boolean throughout, matching the `default: false, null: false` schema constraint - No `visibility` column referenced — correct, it does not exist in the current schema ### Data Coverage - 18 links across 4 categories (infra: 6, dev refs: 4, portfolio: 4, personal: 4) - 8 favorited, 10 not favorited - 3 with `forgejo_url`, 15 without - Tag variety: 12 distinct tags across entries - No sensitive or real credential data ### No Issues Found Clean implementation. No fixes needed. **VERDICT: APPROVED**
Author
Owner

PR #21 Review

DOMAIN REVIEW

Tech stack: Ruby on Rails 8.1, PostgreSQL, seed data (db/seeds.rb).

Schema alignment: All 7 attributes used in the seed hashes (url, title, description, forgejo_url, metadata, position, favorite) match the links table columns in db/schema.rb. No unknown or misspelled column names.

Model validation compliance: Every seed entry provides both url (with valid HTTPS format) and title (non-blank), satisfying the validates :url, presence: true, format: ... and validates :title, presence: true constraints in app/models/link.rb. All 18 entries will pass validation.

Idempotency: find_or_create_by!(url:) is the correct pattern for seed idempotency. Note that the links table has no unique index on url -- idempotency relies on application-level find_or_create_by! rather than a DB constraint. This is acceptable for a single-process dev seed runner, but worth noting: if db:seed were ever run concurrently, duplicates could occur. Not a blocker for dev seed data.

Metadata structure: All metadata values use { tags: [...] } which is compatible with the tagged scope (where("metadata @> ?", { tags: [tag] }.to_json)). The GIN index on metadata will support these queries.

Position gaps: Positions use intentional gaps (1-6, 10-13, 20-23, 30-33) for category grouping. This is clean and allows future insertions without renumbering.

Favorite distribution: 8 of 18 links are favorited (favorite: true), providing good coverage for the favorites filter tab.

BLOCKERS

None.

This PR modifies only db/seeds.rb with static development data. There are no new endpoints, no controller changes, no model changes, and no new user-facing functionality. The BLOCKER criterion "new functionality with zero test coverage" does not apply -- seed data is not testable functionality, it is development tooling. No secrets or credentials are present. All URLs use example.com placeholders for infrastructure services.

NITS

  1. No unique index on url: The find_or_create_by!(url:) pattern assumes no concurrent seed runs, which is fine for dev. If the app later needs URL uniqueness (e.g., preventing duplicate links in production), a migration adding add_index :links, :url, unique: true would make idempotency bulletproof. Not needed now, but worth tracking.

  2. find_or_create_by! update semantics: If a link already exists with the same URL but different attributes (e.g., title changed), find_or_create_by! will NOT update the existing record -- the block only runs on create. This is the correct behavior for seeds (don't overwrite user edits), but worth documenting if anyone expects re-seeding to "reset" data. A comment in the file clarifying this would be helpful.

  3. Output message: puts "Seeded #{seeds.size} links (#{Link.count} total in database)" is good for developer feedback. Consider using Rails.logger.info instead of puts if seeds are ever run in a context where stdout is not visible, but puts is standard Rails seed convention.

SOP COMPLIANCE

  • Branch named after issue: 19-dev-seed-data follows {issue-number}-{kebab-case-purpose} convention
  • PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related Notes all present
  • Related section references parent issue: ldraney/palinks #19 and Closes #19
  • No secrets committed: all infrastructure URLs use example.com domain
  • No unnecessary file changes: single file modified (db/seeds.rb), 1 changed file total
  • Commit messages are descriptive (per PR checklist)

PROCESS OBSERVATIONS

  • Change failure risk: LOW. This is dev-only seed data with no production impact. The seed file is not invoked by any automated pipeline or production entrypoint.
  • Deployment frequency impact: NONE. The Woodpecker pipeline builds and pushes the Docker image, but db:seed is not part of the container entrypoint or any automated process.
  • Documentation: The PR body is thorough with a clear test plan including idempotency verification. The seed file itself has a useful header comment explaining its purpose and usage.

VERDICT: APPROVED

## PR #21 Review ### DOMAIN REVIEW **Tech stack:** Ruby on Rails 8.1, PostgreSQL, seed data (`db/seeds.rb`). **Schema alignment:** All 7 attributes used in the seed hashes (`url`, `title`, `description`, `forgejo_url`, `metadata`, `position`, `favorite`) match the `links` table columns in `db/schema.rb`. No unknown or misspelled column names. **Model validation compliance:** Every seed entry provides both `url` (with valid HTTPS format) and `title` (non-blank), satisfying the `validates :url, presence: true, format: ...` and `validates :title, presence: true` constraints in `app/models/link.rb`. All 18 entries will pass validation. **Idempotency:** `find_or_create_by!(url:)` is the correct pattern for seed idempotency. Note that the `links` table has no unique index on `url` -- idempotency relies on application-level `find_or_create_by!` rather than a DB constraint. This is acceptable for a single-process dev seed runner, but worth noting: if `db:seed` were ever run concurrently, duplicates could occur. Not a blocker for dev seed data. **Metadata structure:** All `metadata` values use `{ tags: [...] }` which is compatible with the `tagged` scope (`where("metadata @> ?", { tags: [tag] }.to_json)`). The GIN index on `metadata` will support these queries. **Position gaps:** Positions use intentional gaps (1-6, 10-13, 20-23, 30-33) for category grouping. This is clean and allows future insertions without renumbering. **Favorite distribution:** 8 of 18 links are favorited (`favorite: true`), providing good coverage for the favorites filter tab. ### BLOCKERS None. This PR modifies only `db/seeds.rb` with static development data. There are no new endpoints, no controller changes, no model changes, and no new user-facing functionality. The BLOCKER criterion "new functionality with zero test coverage" does not apply -- seed data is not testable functionality, it is development tooling. No secrets or credentials are present. All URLs use `example.com` placeholders for infrastructure services. ### NITS 1. **No unique index on `url`:** The `find_or_create_by!(url:)` pattern assumes no concurrent seed runs, which is fine for dev. If the app later needs URL uniqueness (e.g., preventing duplicate links in production), a migration adding `add_index :links, :url, unique: true` would make idempotency bulletproof. Not needed now, but worth tracking. 2. **`find_or_create_by!` update semantics:** If a link already exists with the same URL but different attributes (e.g., title changed), `find_or_create_by!` will NOT update the existing record -- the block only runs on create. This is the correct behavior for seeds (don't overwrite user edits), but worth documenting if anyone expects re-seeding to "reset" data. A comment in the file clarifying this would be helpful. 3. **Output message:** `puts "Seeded #{seeds.size} links (#{Link.count} total in database)"` is good for developer feedback. Consider using `Rails.logger.info` instead of `puts` if seeds are ever run in a context where stdout is not visible, but `puts` is standard Rails seed convention. ### SOP COMPLIANCE - [x] Branch named after issue: `19-dev-seed-data` follows `{issue-number}-{kebab-case-purpose}` convention - [x] PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related Notes all present - [x] Related section references parent issue: `ldraney/palinks #19` and `Closes #19` - [x] No secrets committed: all infrastructure URLs use `example.com` domain - [x] No unnecessary file changes: single file modified (`db/seeds.rb`), 1 changed file total - [x] Commit messages are descriptive (per PR checklist) ### PROCESS OBSERVATIONS - **Change failure risk: LOW.** This is dev-only seed data with no production impact. The seed file is not invoked by any automated pipeline or production entrypoint. - **Deployment frequency impact: NONE.** The Woodpecker pipeline builds and pushes the Docker image, but `db:seed` is not part of the container entrypoint or any automated process. - **Documentation:** The PR body is thorough with a clear test plan including idempotency verification. The seed file itself has a useful header comment explaining its purpose and usage. ### VERDICT: APPROVED
Switch from find_or_create_by! to find_or_initialize_by + assign_attributes
so re-running rails db:seed updates existing records with changed attribute
values instead of silently skipping them. Replace puts with Rails.logger.info
for consistency with Rails conventions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #21 Review

DOMAIN REVIEW

Tech stack: Ruby on Rails 8.1.3, PostgreSQL (JSONB), Hotwire/Turbo. Single-file change to db/seeds.rb.

Rails patterns:

  • find_or_initialize_by(url:) + assign_attributes + save! is the correct idempotent upsert pattern for seeds. Re-running updates existing records rather than silently skipping them. Well done.
  • All 18 seed records provide values for every model attribute (url, title, description, forgejo_url, metadata, position, favorite), which avoids reliance on DB defaults for clarity.
  • save! (bang version) will raise on validation failure rather than silently returning false -- correct for seed scripts where failures should be loud.
  • Position values use intentional gaps (1-6, 10-13, 20-23, 30-33) for category grouping, leaving room for future insertions without re-numbering. Good practice.

JSONB key handling: Seed data uses Ruby symbol keys ({ tags: [...] }), while the Link.tagged scope queries with string keys ({ tags: [tag] }.to_json). This is safe because Rails JSONB serialization normalizes symbols to strings on write, but it is a minor readability inconsistency (see nits).

Missing unique index on url: The find_or_initialize_by(url:) pattern assumes at most one record per URL, but db/schema.rb has no unique index on links.url. For dev seed data this is low-risk, but if this pattern is relied upon for production data integrity, a unique index should be added. Out of scope for this PR.

BLOCKERS

None.

This PR modifies only dev seed data (db/seeds.rb). It introduces no new user-facing functionality, no new endpoints, no new model behavior, and no security-relevant changes. The BLOCKER criterion for "new functionality with zero test coverage" does not apply -- seed data is conventionally untested in Rails, and the underlying Link model validations and controller logic are already covered.

NITS

  1. PR body says find_or_create_by but code uses find_or_initialize_by: The summary section states "Uses find_or_create_by keyed on url" but the actual implementation uses find_or_initialize_by + assign_attributes + save!. The code is actually better (allows updating changed attributes on re-seed), but the PR description should match what the code does.

  2. Symbol vs string keys in metadata hashes: The seed data uses metadata: { tags: [...] } (symbol key), while the model's tagged scope and controller both use string keys ("tags"). Rails handles the conversion, but using string keys in the seed data (metadata: { "tags" => [...] }) would make the data format visually consistent with how it is queried and stored.

  3. Logger output could include counts of created vs updated: The final log line "Seeded #{seeds.size} links" is helpful. A minor enhancement would be tracking how many were newly created vs updated, which aids debugging when seed data changes over time. Not blocking.

SOP COMPLIANCE

  • Branch named after issue (19-dev-seed-data references issue #19)
  • PR body follows template (Summary, Changes, Test Plan, Review Checklist, Related Notes sections all present)
  • Related section references parent issue (ldraney/palinks #19, Closes #19)
  • No secrets committed (all URLs use example.com domains)
  • No unnecessary file changes (single file: db/seeds.rb)
  • Commit messages are descriptive

PROCESS OBSERVATIONS

  • Low change-failure risk: seed data only, no schema changes, no controller logic, no production behavior change.
  • The test plan includes manual verification steps (run db:seed twice, check UI). These are appropriate for seed data validation but are not automated. Consider whether a CI step to run db:seed against a test database would catch seed breakage in the future.
  • The existing test fixtures (test/fixtures/links.yml) use minimal data (3 links). The new seeds and fixtures serve different purposes (dev environment vs test suite), which is the correct separation.

VERDICT: APPROVED

## PR #21 Review ### DOMAIN REVIEW **Tech stack:** Ruby on Rails 8.1.3, PostgreSQL (JSONB), Hotwire/Turbo. Single-file change to `db/seeds.rb`. **Rails patterns:** - `find_or_initialize_by(url:)` + `assign_attributes` + `save!` is the correct idempotent upsert pattern for seeds. Re-running updates existing records rather than silently skipping them. Well done. - All 18 seed records provide values for every model attribute (`url`, `title`, `description`, `forgejo_url`, `metadata`, `position`, `favorite`), which avoids reliance on DB defaults for clarity. - `save!` (bang version) will raise on validation failure rather than silently returning false -- correct for seed scripts where failures should be loud. - Position values use intentional gaps (1-6, 10-13, 20-23, 30-33) for category grouping, leaving room for future insertions without re-numbering. Good practice. **JSONB key handling:** Seed data uses Ruby symbol keys (`{ tags: [...] }`), while the `Link.tagged` scope queries with string keys (`{ tags: [tag] }.to_json`). This is safe because Rails JSONB serialization normalizes symbols to strings on write, but it is a minor readability inconsistency (see nits). **Missing unique index on `url`:** The `find_or_initialize_by(url:)` pattern assumes at most one record per URL, but `db/schema.rb` has no unique index on `links.url`. For dev seed data this is low-risk, but if this pattern is relied upon for production data integrity, a unique index should be added. Out of scope for this PR. ### BLOCKERS None. This PR modifies only dev seed data (`db/seeds.rb`). It introduces no new user-facing functionality, no new endpoints, no new model behavior, and no security-relevant changes. The BLOCKER criterion for "new functionality with zero test coverage" does not apply -- seed data is conventionally untested in Rails, and the underlying `Link` model validations and controller logic are already covered. ### NITS 1. **PR body says `find_or_create_by` but code uses `find_or_initialize_by`:** The summary section states "Uses `find_or_create_by` keyed on `url`" but the actual implementation uses `find_or_initialize_by` + `assign_attributes` + `save!`. The code is actually better (allows updating changed attributes on re-seed), but the PR description should match what the code does. 2. **Symbol vs string keys in metadata hashes:** The seed data uses `metadata: { tags: [...] }` (symbol key), while the model's `tagged` scope and controller both use string keys (`"tags"`). Rails handles the conversion, but using string keys in the seed data (`metadata: { "tags" => [...] }`) would make the data format visually consistent with how it is queried and stored. 3. **Logger output could include counts of created vs updated:** The final log line `"Seeded #{seeds.size} links"` is helpful. A minor enhancement would be tracking how many were newly created vs updated, which aids debugging when seed data changes over time. Not blocking. ### SOP COMPLIANCE - [x] Branch named after issue (`19-dev-seed-data` references issue #19) - [x] PR body follows template (Summary, Changes, Test Plan, Review Checklist, Related Notes sections all present) - [x] Related section references parent issue (`ldraney/palinks #19`, `Closes #19`) - [x] No secrets committed (all URLs use `example.com` domains) - [x] No unnecessary file changes (single file: `db/seeds.rb`) - [x] Commit messages are descriptive ### PROCESS OBSERVATIONS - Low change-failure risk: seed data only, no schema changes, no controller logic, no production behavior change. - The test plan includes manual verification steps (run `db:seed` twice, check UI). These are appropriate for seed data validation but are not automated. Consider whether a CI step to run `db:seed` against a test database would catch seed breakage in the future. - The existing test fixtures (`test/fixtures/links.yml`) use minimal data (3 links). The new seeds and fixtures serve different purposes (dev environment vs test suite), which is the correct separation. ### VERDICT: APPROVED
ldraney deleted branch 19-dev-seed-data 2026-06-08 03:16:01 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/palinks!21
No description provided.