scenic-views/scenic
Versioned database views for Rails
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 6mo ago
- ✓33+ active contributors
- ✓Distributed ownership (top contributor 43% of recent commits)
Show 4 more →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
- ⚠Slowing — last commit 6mo ago
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/scenic-views/scenic)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/scenic-views/scenic on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: scenic-views/scenic
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/scenic-views/scenic shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 6mo ago
- 33+ active contributors
- Distributed ownership (top contributor 43% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Slowing — last commit 6mo ago
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live scenic-views/scenic
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/scenic-views/scenic.
What it runs against: a local clone of scenic-views/scenic — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in scenic-views/scenic | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 206 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of scenic-views/scenic. If you don't
# have one yet, run these first:
#
# git clone https://github.com/scenic-views/scenic.git
# cd scenic
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of scenic-views/scenic and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "scenic-views/scenic(\\.git)?\\b" \\
&& ok "origin remote is scenic-views/scenic" \\
|| miss "origin remote is not scenic-views/scenic (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "lib/scenic.rb" \\
&& ok "lib/scenic.rb" \\
|| miss "missing critical file: lib/scenic.rb"
test -f "lib/scenic/statements.rb" \\
&& ok "lib/scenic/statements.rb" \\
|| miss "missing critical file: lib/scenic/statements.rb"
test -f "lib/scenic/adapters/postgres.rb" \\
&& ok "lib/scenic/adapters/postgres.rb" \\
|| miss "missing critical file: lib/scenic/adapters/postgres.rb"
test -f "lib/scenic/definition.rb" \\
&& ok "lib/scenic/definition.rb" \\
|| miss "missing critical file: lib/scenic/definition.rb"
test -f "lib/scenic/view.rb" \\
&& ok "lib/scenic/view.rb" \\
|| miss "missing critical file: lib/scenic/view.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 206 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~176d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/scenic-views/scenic"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Scenic is a Rails gem that enables versioned database views with a convention-based workflow: developers define SQL in db/views/ files, generators create migrations automatically, and running migrations applies view versions without duplicating SQL. It brings PostgreSQL views into Rails migrations with full reversibility and schema.rb support—solving the problem of managing complex SQL views alongside ActiveRecord code without abandoning Rails conventions. Modular generator + adapter pattern: lib/generators/scenic/ contains Rails generators (view, model) that scaffold migrations and SQL files; lib/scenic/adapters/postgres/ contains all database logic (connection, views, indexes, refresh_dependencies); lib/scenic/command_recorder/ handles Rails migration DSL recording. The entry point is lib/scenic.rb which loads configuration and adapters.
👥Who it's for
Rails developers building data-heavy applications (dashboards, reporting, analytics) who need to leverage PostgreSQL views for query optimization, data aggregation, or denormalization but want to keep view management integrated with Rails migrations rather than raw SQL files.
🌱Maturity & risk
Production-ready and actively maintained. The gem has CI/CD via GitHub Actions (.github/workflows/ci.yml), comprehensive documentation (CONTRIBUTING.md, SECURITY.md, CHANGELOG.md), and uses Ruby/RSpec conventions. The repo shows steady stewardship with clear versioning (CHANGELOG.md present) and a Docker dev environment (.devcontainer/) indicating professional standards.
Low risk for PostgreSQL users; the adapter is tightly coupled to Postgres (lib/scenic/adapters/postgres/ is the only adapter). The codebase is relatively small (~120KB Ruby) with clear isolation, reducing dependency surface area. Risk increases if you need MySQL/SQLite support—third-party adapters are mentioned in the README but not bundled, creating a potential version-sync problem.
Active areas of work
Unable to determine from static repo snapshot alone, but the presence of .devcontainer/ (recent dev tooling), well-organized lib/scenic/adapters/postgres/ subdirectories (indexes, refresh_dependencies, side_by_side), and bin/standardrb indicate active maintenance. No breaking changes visible in the file structure.
🚀Get running
git clone https://github.com/scenic-views/scenic.git && cd scenic && bundle install && bin/setup && bin/rspec
Daily commands: bin/setup (standard Rails setup), bin/rspec (run tests), bin/rake (run Rake tasks), bin/standardrb (lint Ruby). For development: .devcontainer/docker-compose.yml provides PostgreSQL and a Ruby environment.
🗺️Map of the codebase
lib/scenic.rb— Entry point and main API surface that loads all Scenic components and exposes the public DSL.lib/scenic/statements.rb— Core DSL methods (create_view, update_view, drop_view) that integrate with ActiveRecord::Migration.lib/scenic/adapters/postgres.rb— Primary database adapter that implements view lifecycle management and SQL-level operations.lib/scenic/definition.rb— Loads and parses SQL view definitions from versioned files in db/views directory.lib/scenic/view.rb— Core View model abstraction representing a single database view with metadata and lifecycle.lib/scenic/schema_dumper.rb— Integrates with Rails schema dumper to persist view definitions in schema.rb for consistency.lib/scenic/railtie.rb— Rails framework integration hook that registers migrations and generators on app boot.
🛠️How to make changes
Create a new versioned database view
- Run generator to scaffold view migration and SQL file (
lib/generators/scenic/view/view_generator.rb) - Write SQL view definition in db/views/view_name_v01.sql (
lib/scenic/definition.rb) - Generator creates migration in db/migrate/*_create_view_name.rb with create_view call (
lib/generators/scenic/view/templates/db/migrate/create_view.erb) - Run rake db:migrate; Scenic loads SQL via Definition, calls adapter.create_view() (
lib/scenic/statements.rb)
Update an existing view with zero downtime
- Create new versioned SQL file: db/views/view_name_v02.sql with updated definition (
lib/scenic/definition.rb) - Run generator to create update migration in db/migrate/*_update_view_name.rb (
lib/generators/scenic/view/templates/db/migrate/update_view.erb) - Migration calls update_view(name, materialized: bool) which delegates to adapter (
lib/scenic/statements.rb) - Adapter uses side_by_side strategy: create temp view, swap names atomically, drop old (
lib/scenic/adapters/postgres/side_by_side.rb) - Run rake db:migrate; old queries remain uninterrupted until swap is complete (
lib/scenic/adapters/postgres.rb)
Add indexes to a materialized view and manage them in migrations
- In migration, call add_index(:view_name, [:col1, :col2], unique: true) (
lib/scenic/adapters/postgres/index_migration.rb) - Adapter queries pg_indexes to detect current indexes on view (
lib/scenic/adapters/postgres/indexes.rb) - If view is updated via side_by_side, index reapplication logic recreates indexes on new view (
lib/scenic/adapters/postgres/index_reapplication.rb) - Schema dumper persists index definitions in schema.rb alongside view (
lib/scenic/schema_dumper.rb)
Support a new database adapter (e.g., MySQL, SQLite)
- Create lib/scenic/adapters/mysql.rb inheriting from Scenic::Adapters::Base (
lib/scenic/adapters/postgres.rb) - Implement required methods: create_view, update_view, drop_view, current_views, refresh_materialized_view (
lib/scenic/adapters/postgres/views.rb) - Register adapter in lib/scenic/configuration.rb and update adapter lookup logic (
lib/scenic/configuration.rb) - Add integration tests in spec/scenic/adapters/mysql_spec.rb (
spec/scenic/adapters/postgres_spec.rb)
🔧Why these technologies
- Rails ActiveRecord::Migration — Provides versioned, reversible migration framework that Scenic extends with create_view/update_view DSL methods; allows developers to treat views as first-class schema objects.
- PostgreSQL catalog tables (pg_views, pg_indexes) — Allows runtime discovery of existing views and indexes to validate state before migrations; enables idempotent operations and dependency resolution.
- SQL files in db/views/*.sql — Separates view logic from migration code; enables full SQL syntax highlighting in editors and eases testing SQL in psql console without code recompilation.
- Adapter pattern (Scenic::Adapters::*) — Isolates database-specific logic (PostgreSQL) from core domain; makes adding MySQL/SQLite support non-invasive and testable in isolation.
- Side-by-side view swapping strategy — Enables zero-downtime updates: create new view with temp name, swap via transaction, drop old view; queries in-flight continue against old view until swap completes.
⚖️Trade-offs already made
-
Only PostgreSQL adapter implemented; others configurable but not bundled
- Why: PostgreSQL has mature schema introspection APIs (pg_views, pg_indexes) and transactional DDL; other databases lack equivalent tooling or have edge cases.
- Consequence: MySQL/SQLite users must write their own adapter; scope is smaller and more maintainable but limits portability of existing Scenic projects.
-
Views stored in separate SQL files rather than inline in migration
- Why: Better syntax highlighting, easier testing, avoids duplication if view is used in multiple migrations, simpler to audit
- Consequence: undefined
🪤Traps & gotchas
- PostgreSQL-only: The gem currently only supports PostgreSQL; attempting to use with MySQL/SQLite will fail silently or throw adapter errors. 2. View naming and versioning: Views must follow the naming convention (name_vXX.sql); deviations break the generator's version detection. 3. Materialized view dependencies: lib/scenic/adapters/postgres/refresh_dependencies.rb handles cascading refreshes—circular dependencies between materialized views can cause deadlocks. 4. Migration reversibility: Drop migrations are only safe if no other views/tables depend on the dropped view; Scenic doesn't validate this automatically. 5. SQL file location: Views must live in db/views/ and be picked up by Scenic's file discovery; non-standard paths will be ignored.
🏗️Architecture
💡Concepts to learn
- Database Views (Logical) — Scenic's entire purpose is to manage these; understanding the difference between views (virtual, no storage) and materialized views (cached queries) is critical to using Scenic correctly
- View Versioning & Migration History — Scenic's core innovation is treating views like schema objects with version history (v01, v02); requires understanding how migrations are reversible and how old view definitions are preserved
- Adapter Pattern — Scenic uses adapters (Scenic::Adapters::Postgres) to abstract database operations; extending Scenic to MySQL/SQLite means implementing a new adapter, not modifying core code
- Schema Dumping & Reversible Migrations — Scenic integrates with Rails' schema.rb via command_recorder.rb; understanding how Rails captures DSL calls and replays them is essential to debugging or extending Scenic
- Side-by-Side View Updates — lib/scenic/adapters/postgres/side_by_side.rb implements atomic view updates by creating a temporary view and swapping—avoiding locks and read disruption during updates
- Rails Generators — Scenic extends Rails::Generators (lib/generators/scenic/) to create boilerplate; understanding generator hooks (create_file, template) is needed to customize code generation
- Materialized View Refresh Dependencies — lib/scenic/adapters/postgres/refresh_dependencies.rb detects cascading refreshes; required knowledge if your views depend on other materialized views and need atomic updates
🔗Related repos
thoughtbot/scenic— This IS the official scenic repository—listed here for clarity on provenance and GitHub organization (scenic-views/scenic)ankane/strong_migrations— Complementary Rails migration tool that validates view/table changes for safety; often used alongside Scenic to catch dependency issuesrails/rails— The ActiveRecord::Migration base that Scenic extends; understanding Rails migration hooks is essential to using Scenicrkrage/pg_query— PostgreSQL query parser for Ruby; could be integrated into Scenic to auto-detect view dependencies and prevent orphaningankane/pgsync— PostgreSQL sync tool; useful for developers who test Scenic views against production-like data
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for MySQL/MariaDB adapter
The repo currently has extensive PostgreSQL adapter support (lib/scenic/adapters/postgres/) with full test coverage via spec/acceptance/user_manages_views_spec.rb, but there's no evidence of MySQL/MariaDB adapter implementation or tests. This is a high-value addition since many Rails apps use MySQL. A new contributor could implement a MySQL adapter following the PostgreSQL pattern, including views.rb, indexes.rb, connection.rb, and corresponding spec files.
- [ ] Create lib/scenic/adapters/mysql/connection.rb following PostgreSQL adapter pattern
- [ ] Create lib/scenic/adapters/mysql/views.rb to handle MySQL view introspection
- [ ] Create lib/scenic/adapters/mysql/indexes.rb for MySQL index support
- [ ] Add MySQL-specific error handling in lib/scenic/adapters/mysql/errors.rb
- [ ] Create spec/acceptance tests for MySQL (mirror of PostgreSQL acceptance tests)
- [ ] Update lib/scenic.rb to register MySQL adapter
- [ ] Update CI workflow (.github/workflows/ci.yml) to test against MySQL service
Add unit tests for Scenic::CommandRecorder and statement argument handling
The lib/scenic/command_recorder/ and lib/scenic/command_recorder/statement_arguments.rb files exist but there's no dedicated spec file for testing command recording during migrations (e.g., reversible up/down migrations with views and indexes). This is critical for ensuring migrations are properly reversible and the feature works as documented.
- [ ] Create spec/unit/command_recorder_spec.rb to test record_create_view, record_drop_view, record_update_view, record_create_index, and record_drop_index methods
- [ ] Create spec/unit/statement_arguments_spec.rb to test argument parsing and handling for all statement types
- [ ] Add tests for reversibility of create_view and update_view migrations
- [ ] Test edge cases like circular view dependencies in CommandRecorder
- [ ] Verify statement arguments correctly handle all adapter-specific syntax variations
Add missing documentation and YARD examples for lib/scenic/view.rb and lib/scenic/index.rb
The .yardopts file exists and YARD documentation is configured (Inch CI badge in README), but core classes like Scenic::View and Scenic::Index lack detailed method documentation and usage examples. New contributors could add comprehensive YARD comments following Rails conventions, improving discoverability and reducing support burden.
- [ ] Add YARD documentation to lib/scenic/view.rb with examples for accessing view definition, properties, and managing view versions
- [ ] Add YARD documentation to lib/scenic/index.rb with examples for index creation on views
- [ ] Add YARD documentation to lib/scenic/definition.rb explaining how view SQL is loaded and versioned
- [ ] Document the public API in lib/scenic/statements.rb (create_view, update_view, drop_view methods)
- [ ] Regenerate YARD docs and verify Inch CI score improvement (currently shown in README badge)
🌿Good first issues
- Add MySQL adapter skeleton (lib/scenic/adapters/mysql.rb) with basic connection + views.rb structure to enable third-party contributors to fill in dialect-specific SQL
- Write integration tests for lib/scenic/adapters/postgres/refresh_dependencies.rb against a real PostgreSQL instance with interdependent materialized views to prevent regression
- Document the adapter interface (Scenic::Adapters::Postgres contract) in a new ADAPTER_DEVELOPMENT.md file with concrete examples of required methods (current_views, create_view, update_view, drop_view, indexes) for external adapter authors
⭐Top contributors
Click to expand
Top contributors
- @derekprior — 43 commits
- @calebhearth — 20 commits
- @drnic — 2 commits
- @edwardloveall — 2 commits
- @petergoldstein — 2 commits
📝Recent commits
Click to expand
Recent commits
f2162db— ensure multi-schema dumping in Rails 8.1 doesn't dump the same objects for all schemas (agrobbin)635fff5— Lint (calebhearth)88e387a— Clarify where to use the cascade option for materialized views (Joeventures)6cdf4e9— v1.9.0 (derekprior)3766dd5— Update link to MySQL adapter (#435) (ralph)a9f2cff— Refactor side_by_side materialized view creation (derekprior)7295d5b— Add side_by_side mode to update_materialized_view (Roguelazer)a828baa— Only refresh concurrently if view is populated (drnic)1b70bf7— Fix issues with sorting views in the adapter (derekprior)736dee8— Move SchemaDumper sorting behavior to adapter (calebhearth)
🔒Security observations
Scenic is a well-structured Rails gem for database view management with generally sound architecture. Primary concerns involve SQL injection risk vectors inherent to SQL view handling, potential information disclosure through error messages, and a restrictive security maintenance policy. The library would benefit from enhanced input validation in generators, comprehensive security testing, and improved documentation of security best practices. The codebase shows no evidence of hardcoded secrets or critical misconfigurations. Dependency verification was not possible without Gemfile.lock analysis.
- High · SQL Injection Risk in View Management —
lib/scenic/adapters/postgres/views.rb, lib/generators/scenic/view/templates/db/migrate/. The codebase handles raw SQL queries for database view creation and management. Files like lib/scenic/adapters/postgres/views.rb and view generation templates (lib/generators/scenic/view/templates/db/migrate/) process SQL directly. While Rails migrations provide some protection, improper handling of view definitions or user-supplied input in view SQL could lead to SQL injection attacks. Fix: Ensure all SQL input is properly sanitized and parameterized. Implement input validation for view definitions. Add security tests for SQL injection vectors in view creation and updates. - Medium · Potential Information Disclosure via Error Messages —
lib/scenic/adapters/postgres/errors.rb. The codebase includes error handling in lib/scenic/adapters/postgres/errors.rb. Database error messages and stack traces could potentially expose sensitive information about the database schema or internal structure if not properly filtered before being shown to users. Fix: Implement sanitized error messages for user-facing errors. Ensure database error details are only logged internally and not exposed to end users. Review all error handling paths. - Medium · Dependency Management and Update Strategy —
SECURITY.md, Gemfile. The SECURITY.md policy states that only the most recently released version is supported and no backported fixes are provided. This creates a security maintenance gap for users unable to immediately upgrade. No dependency file (Gemfile.lock) was provided in the analysis, preventing verification of known vulnerable dependencies. Fix: Establish a policy for at least one prior version receiving critical security patches. Maintain an updated Gemfile.lock and regularly audit dependencies using tools like bundler-audit. Document dependency requirements clearly. - Medium · Lack of Input Validation in Generators —
lib/generators/scenic/model/model_generator.rb, lib/generators/scenic/view/view_generator.rb, lib/generators/scenic/generators.rb. The code generators in lib/generators/scenic/ create migration files and models based on user input. If class names or view names are not properly validated, this could lead to code injection or unexpected behavior in generated files. Fix: Implement strict validation for generated class names and view identifiers. Ensure all user inputs are validated against whitelists of allowed characters. Add tests for edge cases and malicious inputs. - Low · Missing Security Headers Documentation —
SECURITY.md, README.md. While not directly applicable to a gem (library), the documentation and SECURITY.md file lack guidance on security best practices for applications using Scenic, such as database access controls and view permission management. Fix: Add a security best practices section to README.md covering: proper database role management, view-level permissions, query complexity considerations, and recommendations for production deployments. - Low · Limited Security Testing Evidence —
spec/acceptance/user_manages_views_spec.rb. The spec directory structure suggests unit and acceptance tests exist, but no evidence of dedicated security testing, fuzzing, or SQL injection test cases for view management operations. Fix: Add comprehensive security-focused test suite covering: SQL injection attempts, malicious input in view definitions, permission boundary testing, and race conditions in view refresh operations.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.