{"id":31718216,"url":"https://github.com/mundanecodes/rails-starter","last_synced_at":"2026-04-11T04:31:35.535Z","repository":{"id":318387901,"uuid":"1071089547","full_name":"mundanecodes/rails-starter","owner":"mundanecodes","description":"Production-ready Rails 8.1 template with UUID v7, RSpec, Sidekiq, Docker, and modern tooling","archived":false,"fork":false,"pushed_at":"2025-10-06T21:26:00.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-06T23:22:36.827Z","etag":null,"topics":["docker","postgresql","production-ready","rails","rails8","rspec","ruby","sidekiq","starter-template","template","uuid"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mundanecodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-06T21:11:02.000Z","updated_at":"2025-10-06T21:22:34.000Z","dependencies_parsed_at":"2025-10-06T23:23:32.219Z","dependency_job_id":"a80613f9-c28b-4c52-8588-bfa98f41266a","html_url":"https://github.com/mundanecodes/rails-starter","commit_stats":null,"previous_names":["mundanecodes/rails-starter"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mundanecodes/rails-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mundanecodes%2Frails-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mundanecodes%2Frails-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mundanecodes%2Frails-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mundanecodes%2Frails-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mundanecodes","download_url":"https://codeload.github.com/mundanecodes/rails-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mundanecodes%2Frails-starter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000780,"owners_count":26082906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","postgresql","production-ready","rails","rails8","rspec","ruby","sidekiq","starter-template","template","uuid"],"created_at":"2025-10-09T02:33:04.267Z","updated_at":"2025-12-30T21:13:10.476Z","avatar_url":"https://github.com/mundanecodes.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rails Starter Template\n\n[![Ruby](https://img.shields.io/badge/Ruby-3.3+-red.svg)](https://www.ruby-lang.org)\n[![Rails](https://img.shields.io/badge/Rails-8.1-red.svg)](https://rubyonrails.org)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Hacktoberfest](https://img.shields.io/badge/Hacktoberfest-friendly-blueviolet.svg)](https://hacktoberfest.com)\n\nA production-ready Rails 8.1 application template with modern tooling, UUID v7 primary keys, comprehensive testing setup, and Docker support.\n\n## Table of Contents\n\n- [Features](#features)\n- [Usage](#usage)\n- [Environment Variables](#environment-variables)\n- [UUID v7 Primary Keys](#uuid-v7-primary-keys)\n- [SimpleState](#simplestate)\n- [Testing](#testing)\n- [Sidekiq Web UI](#sidekiq-web-ui)\n- [Development URLs](#development-urls)\n- [Robot Blocking](#robot-blocking)\n- [CI/CD](#cicd)\n- [Customization](#customization)\n- [Tech Stack Summary](#tech-stack-summary)\n- [License](#license)\n\n## Features\n\n### Core Stack\n- **Rails 8.1.0.beta1** - Latest edge version\n- **PostgreSQL** with UUID v7 primary keys (time-ordered, sortable)\n- **Redis** for caching and job queue\n- **Memcached** with Dalli for distributed caching\n- **Puma** web server (2 workers, 5 threads)\n\n### Background Jobs\n- **Sidekiq** for background job processing\n- Authenticated web UI at `/wera` with HTTP Basic Auth\n\n### Testing\n- **RSpec** testing framework\n- **FactoryBot** for test data\n- **Shoulda Matchers** for Rails-specific assertions\n- **WebMock** for HTTP request stubbing\n- **DatabaseCleaner** with transaction strategy\n- Performance optimizations (GC management, eager loading)\n\n### HTTP \u0026 Storage\n- **HTTPX** - modern HTTP/2 client\n- **Active Storage** with AWS S3 support\n- Multi-environment storage configuration (local/test/S3)\n\n### Development\n- **dotenv-rails** for environment variables\n- **Ngrok** tunnel support for webhooks\n- **.localhost** domain support (no /etc/hosts editing needed)\n- Port 3007 by default (avoids common conflicts)\n\n### Security \u0026 SEO\n- Robot blocking middleware (SEO and AI crawlers)\n- Comprehensive robots.txt\n- Constant-time authentication comparison (prevents timing attacks on password checks)\n- No default credentials (environment-based)\n\n### DevOps\n- **Docker** and **docker-compose** ready\n- **GitHub Actions** CI/CD pipeline\n- Health check endpoint silencing\n- **Kamal** deployment configuration\n- **Brakeman** security scanning\n- **RuboCop** and **StandardRb** code linting\n\n### Configuration\n- **UTC timezone** (standard for Rails applications)\n- PostgreSQL connection pooling and statement timeouts\n- Request specs only (no controller/view specs)\n- No helper file generation\n- Clean, organized Gemfile\n\n## Usage\n\n### Create a new application\n\n```bash\nrails new myapp -d postgresql -m https://raw.githubusercontent.com/mundanecodes/rails-starter/main/template.rb\n```\n\nThe template will:\n1. Install and configure all gems\n2. Set up RSpec with all testing tools\n3. Configure PostgreSQL with UUID v7 support\n4. Set up Sidekiq with authenticated web UI\n5. Configure Docker and docker-compose\n6. Create GitHub Actions workflow\n7. Set up robot blocking\n8. Generate `.env.example` with all required variables\n9. Create initial git commit\n\n### Post-installation steps\n\n1. **Configure environment variables:**\n   ```bash\n   cp .env.example .env\n   # Edit .env with your values\n   ```\n\n2. **Set up database:**\n   ```bash\n   bin/rails db:create db:migrate\n   ```\n\n3. **Start the server:**\n   ```bash\n   bin/rails server -p 3007\n   ```\n\n4. **Access your app:**\n   - Main app: `http://myapp.localhost:3007`\n   - Sidekiq UI: `http://myapp.localhost:3007/wera`\n\n### Using Docker\n\n```bash\ndocker-compose up\n```\n\nServices included:\n- Web (Rails app on port 3007)\n- PostgreSQL 16\n- Redis 7\n- Memcached 1.6\n- Sidekiq worker\n\n## Environment Variables\n\nRequired variables (see `.env.example`):\n\n```bash\n# Database\nDATABASE_URL=postgres://postgres:postgres@localhost:5432/myapp_development\n\n# Redis \u0026 Sidekiq\nREDIS_URL=redis://localhost:6379/0\nSIDEKIQ_USERNAME=your_username\nSIDEKIQ_PASSWORD=your_secure_password\n\n# Memcached\nMEMCACHED_SERVERS=localhost:11211\n\n# AWS S3 (production)\nAWS_ACCESS_KEY_ID=your_access_key\nAWS_SECRET_ACCESS_KEY=your_secret_key\nAWS_REGION=us-east-1\nAWS_BUCKET=your-bucket-name\n\n# Server\nPORT=3007\n```\n\n## UUID v7 Primary Keys\n\nAll models automatically use UUID v7 as primary keys:\n\n```ruby\n# No configuration needed - it's automatic!\nclass User \u003c ApplicationRecord\n  # id will be UUID v7\nend\n\n# In migrations\ncreate_table :users do |t|\n  # id is automatically UUID v7\n  t.string :email\n  t.timestamps\nend\n```\n\nUUID v7 benefits:\n- Time-ordered (sortable like auto-incrementing IDs)\n- Globally unique\n- Better for distributed systems\n- Database index-friendly\n\n## SimpleState\n\nA lightweight state machine module included with this template. Perfect for modeling workflows like user onboarding, order processing\n\n```ruby\nclass Employee \u003c ApplicationRecord\n  include SimpleState\n\n  state_column :status\n\n  enum status: {\n    created: \"created\",\n    invited: \"invited\",\n    enrolled: \"enrolled\",\n    suspended: \"suspended\",\n    terminated: \"terminated\"\n  }\n\n  # Basic transition\n  transition :invite, from: :created, to: :invited, timestamp: :invited_at\n\n  # Transition with guard\n  transition :enroll, from: :invited, to: :enrolled, timestamp: true,\n             guard: :valid_invitation?\n\n  # Transition with callback\n  transition :suspend, from: [:enrolled], to: :suspended do\n    NotificationMailer.suspension_notice(self).deliver_later\n  end\n\n  # Multiple source states\n  transition :reactivate, from: [:suspended, :terminated], to: :enrolled\n\n  private\n\n  def valid_invitation?\n    invited_at.present? \u0026\u0026 invited_at \u003e 30.days.ago\n  end\nend\n```\n\n**Usage:**\n\n```ruby\nemployee = Employee.create!(status: :created)\nemployee.invite  # =\u003e transitions to :invited, sets invited_at\nemployee.enroll  # =\u003e transitions to :enrolled if guard passes\n\n# Check if transition is allowed\nemployee.can_transition?(:suspend)  # =\u003e true\n\n# Error handling\nbegin\n  employee.invite  # raises SimpleState::TransitionError if invalid\nrescue SimpleState::TransitionError =\u003e e\n  # Handle invalid transition\nend\n```\n\n**Features:**\n- **Guards**: Prevent transitions unless conditions are met\n- **Timestamps**: Auto-update columns on transition (e.g., `invited_at`)\n- **Callbacks**: Execute code after successful transitions\n- **Events**: Publishes `ActiveSupport::Notifications` for all transitions\n- **Validation**: Ensures states exist in your enum at class load time\n\n**Events published:**\n\nEvents follow the pattern: `{model_name}.{transition_name}.{outcome}`\n\nFor example, the `invite` transition on `Employee` publishes:\n- `employee.invite.success` - Transition succeeded\n- `employee.invite.failed` - Update failed (validation, etc.)\n- `employee.invite.invalid` - Transition not allowed or guard failed\n\nSubscribe to events for logging, metrics, or side effects:\n\n```ruby\nActiveSupport::Notifications.subscribe(/employee\\..*\\.success/) do |name, start, finish, id, payload|\n  Rails.logger.info \"Employee #{payload[:record_id]} transitioned to #{payload[:to_state]}\"\nend\n```\n\n## Testing\n\nRun the full test suite:\n```bash\nbundle exec rspec\n```\n\nTesting features:\n- Request specs generated by default\n- FactoryBot syntax methods included\n- WebMock prevents external HTTP calls\n- Transaction-based database cleaning (fast)\n- GC disabled during tests for 20-30% speed improvement\n\n## Sidekiq Web UI\n\nAccess at `/wera` with HTTP Basic Authentication.\n\nSet credentials via environment variables:\n```bash\nSIDEKIQ_USERNAME=admin\nSIDEKIQ_PASSWORD=secure_password_here\n```\n\nAuthentication uses constant-time comparison to prevent timing attacks.\n\n## Development URLs\n\nThe template configures support for:\n- `http://myapp.localhost:3007` (no /etc/hosts editing needed)\n- Ngrok tunnels: `https://abc123.ngrok-free.app`\n- Standard localhost: `http://localhost:3007`\n\n### Starting an Ngrok tunnel\n\nTo expose your local server for webhook testing:\n```bash\nngrok http 3007\n```\n\nThis creates a public URL like `https://abc123.ngrok-free.app` that forwards to your local port 3007.\n\n## Robot Blocking\n\nAll robots are blocked by default via:\n1. `public/robots.txt` - Blocks all user agents including GPTBot, CCBot, etc.\n2. Middleware - Adds `X-Robots-Tag` headers to all responses\n3. Multiple layers ensure comprehensive blocking\n\nRemove or modify in production if you want SEO indexing.\n\n## CI/CD\n\nGitHub Actions workflow includes:\n- RSpec test suite\n- Brakeman security scanning\n- RuboCop and StandardRb linting\n- PostgreSQL and Redis services\n\n## Customization\n\n### Change timezone\nEdit `config/application.rb`:\n```ruby\nconfig.time_zone = \"Your/Timezone\"\n```\n\n### Change port\nEdit `.env`:\n```bash\nPORT=3000\n```\n\nAnd `docker-compose.yml`:\n```yaml\nports:\n  - \"3000:3000\"\n```\n\n### Disable robot blocking\nRemove from `config/application.rb`:\n```ruby\nconfig.middleware.use BlockRobots\n```\n\n## Tech Stack Summary\n\n| Category | Technology |\n|----------|-----------|\n| Framework | Rails 8.1.0.beta1 |\n| Database | PostgreSQL 16 + UUID v7 |\n| Cache | Redis 7 + Memcached 1.6 + Dalli |\n| Background Jobs | Sidekiq |\n| Testing | RSpec + FactoryBot + Shoulda + WebMock |\n| HTTP Client | HTTPX |\n| Storage | Active Storage + AWS S3 |\n| Web Server | Puma (2 workers, 5 threads) |\n| Containerization | Docker + docker-compose |\n| CI/CD | GitHub Actions |\n| Deployment | Kamal |\n| Security | Brakeman |\n| Linting | RuboCop + StandardRb |\n\n## License\n\nMIT\n\n## Credits\n\nBuilt by [mundanecodes](https://github.com/mundanecodes) for rapid Rails application development.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmundanecodes%2Frails-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmundanecodes%2Frails-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmundanecodes%2Frails-starter/lists"}