A modern, slug-based blogging platform built with Rails 8.1 featuring rich text editing, internal linking, and a clean management interface.
- Clean, SEO-friendly URLs using post/page titles instead of numeric IDs
- Automatic slug generation from titles with duplicate handling
- Unique slug validation with automatic counter increments
- Database-indexed slugs for optimal performance
- Modern rich text editor powered by Lexical framework
- Syntax highlighting for code blocks
- Markdown support for quick formatting
- # Prompt Internal Linking: Type
#to search and link to other posts/pages - Server-side filtering for fast link discovery
- Bootstrap 5 with custom styling
- Bootstrap Icons integration
- Responsive design for all screen sizes
- Clean management interface with sidebar layouts
- Professional form designs with contextual help
- Single Table Inheritance (STI) with
Postablebase class - Unified handling of Posts and Pages
- Rich metadata support (descriptions, publishing dates, etc.)
- Flexible publishing workflow
- User authentication system
- Protected management interface
- Brakeman security scanning integration
- CSRF protection and secure headers
- 237 tests with 673 assertions
- Unit and integration tests
- 100% passing test suite
- Automated CI/CD ready
- Ruby 3.3.5+
- Node.js 18+ and Yarn
- SQLite3 (development) or PostgreSQL (production)
-
Clone the repository
git clone https://github.com/BillyRuffian/gutter_press.git cd gutter_press -
Install dependencies
# Ruby dependencies bundle install # Node.js dependencies yarn install
-
Setup database
bin/rails db:create bin/rails db:migrate bin/rails db:seed
-
Start the development server
bin/dev
-
Visit the application
- Main site: http://localhost:3000
- Management interface: http://localhost:3000/manage
- Navigate to
/manage/posts - Click "New Post"
- Fill in title and content using the rich text editor
- Use
#in the editor to link to other posts/pages - Configure slug in the sidebar (auto-generated if empty)
- Set publish date and status
- Save to publish
Similar to posts but accessible at /pages/slug-name for static content.
The # prompt feature allows you to easily link between content:
- Type
#in the rich text editor - Start typing the title of a post or page
- Select from the filtered results
- A properly formatted link is inserted automatically
- Posts:
/posts/my-great-post-title - Pages:
/pages/about-us - Management:
/manage/postsor/manage/pages
# All tests
bin/rails test
# Specific test file
bin/rails test test/models/postable_test.rb# Security scan
bin/brakeman
# Linting
bin/rubocop
# Bundle audit
bin/bundler-audit# Create and run migrations
bin/rails generate migration AddFeatureToPostables feature:string
bin/rails db:migrate
# Rollback
bin/rails db:rollback
# Reset database
bin/rails db:drop db:create db:migrate db:seed- Location:
app/models/postable.rb - Purpose: Base class for all content types
- Key Methods:
generate_slug: Automatic slug generation with duplicate handlingto_param: Returns slug for URL generationpublished?: Check if content is published
- Inheritance: Both inherit from
Postable - Differences: Different routing and display contexts
- Shared Features: All slug functionality, rich text, metadata
- Location:
app/controllers/manage/ - Purpose: Admin interface for content management
- Authentication: Required for all actions
- Features: Full CRUD operations with slug support
- Location:
app/controllers/manage/links_controller.rb - Purpose: Handles
#prompt searches for internal linking - Response: JSON data for Lexxy integration
- Framework: Bootstrap 5 with custom SCSS
- Templates: HAML for clean, readable markup
- Components: Reusable form partials and UI elements
- Editor: Lexxy with custom prompt configuration
- Styling: Custom CSS for proper spacing and icons
- Features: Syntax highlighting, markdown support, internal linking
-- Postables table (STI)
CREATE TABLE postables (
id INTEGER PRIMARY KEY,
type VARCHAR NOT NULL, -- 'Post' or 'Page'
title VARCHAR NOT NULL,
content TEXT,
description TEXT,
slug VARCHAR UNIQUE NOT NULL, -- Indexed for performance
user_id INTEGER NOT NULL,
publish BOOLEAN DEFAULT FALSE,
published_at DATETIME,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
CREATE UNIQUE INDEX index_postables_on_slug ON postables (slug);# Development
HOST=localhost
PORT=3000
RAILS_ENV=development
# Production
HOST=yourdomain.com
RAILS_ENV=production
SECRET_KEY_BASE=your_secret_key
DATABASE_URL=postgresql://...Edit config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }Edit config/environments/production.rb:
config.action_mailer.default_url_options = { host: 'yourdomain.com' }
config.hosts = [
"yourdomain.com",
/.*\.yourdomain\.com/
]# Dockerfile included in repository
FROM ruby:3.3.5
# ... (see Dockerfile for complete configuration)Build and run:
docker build -t gutterpress .
docker run -p 3000:3000 gutterpressConfiguration in config/deploy.yml:
service: gutterpress
image: gutterpress
servers:
- your-server.com
# ... (see deploy.yml for complete configuration)Deploy:
kamal setup
kamal deploy-
Server Setup
- Install Ruby, Node.js, and database
- Configure web server (Nginx + Passenger/Puma)
- Set up SSL certificates
-
Application Deployment
# On server git pull origin main bundle install --without development test yarn install --production bin/rails assets:precompile bin/rails db:migrate sudo systemctl restart your-app
Endpoint: GET /manage/links
Parameters:
query(string): Search term for filtering posts/pages
Response:
{
"results": [
{
"title": "My Great Post",
"slug": "my-great-post",
"type": "Post",
"url": "/posts/my-great-post"
}
]
}Usage: Automatically called by Lexxy editor when using # prompt.
test/
├── controllers/ # Controller unit tests
│ ├── manage/ # Management interface tests
│ └── ...
├── models/ # Model unit tests
├── integration/ # Integration tests
├── fixtures/ # Test data
└── test_helper.rb # Test configuration
test/models/postable_test.rb: Slug generation and validationtest/controllers/manage/links_controller_test.rb: Internal linking API
# Model tests
bin/rails test test/models/
# Controller tests
bin/rails test test/controllers/
# Single test
bin/rails test test/models/postable_test.rb:25- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Write tests for your changes
- Ensure tests pass:
bin/rails test - Check code quality:
bin/rubocop - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
- Follow Ruby community conventions
- Use RuboCop for linting
- Write descriptive commit messages
- Include tests for new features
- Update documentation as needed
- Check that
titleis present before save - Verify database has unique index on
slugcolumn - Review
generate_slugmethod inPostablemodel
- Ensure Yarn dependencies are installed:
yarn install - Check browser console for JavaScript errors
- Verify Lexxy CSS is properly compiled:
yarn build:css
- Confirm
LinksControlleris responding: check/manage/links?query=test - Verify route exists:
bin/rails routes | grep links - Check browser network tab for failed requests
- Reset test database:
RAILS_ENV=test bin/rails db:reset - Check for pending migrations:
bin/rails db:migrate:status - Review specific test failure output for details
- Enable caching in development:
bin/rails dev:cache - Check database queries in logs
- Consider adding database indexes for frequent queries
- Sass deprecation warnings are cosmetic (Bootstrap compatibility)
- Consider upgrading to Dart Sass when Bootstrap supports it
This project is licensed under the MIT License - see the LICENSE file for details.
- Rails Team for the excellent Rails 8.1 framework
- Lexxy for the modern rich text editing experience
- Bootstrap Team for the UI framework
- Community Contributors for various gems and tools used
- ✅ Complete slug-based URL system
- ✅ Lexxy rich text editor integration
- ✅ Internal linking with
#prompts - ✅ Bootstrap 5 UI with custom styling
- ✅ Comprehensive test suite (76 tests)
- ✅ Security hardening with Brakeman
- ✅ HAML templating throughout
- ✅ Single Table Inheritance architecture
Built with ❤️ using Rails 8.1 and modern web technologies.
For questions, issues, or contributions, please visit our GitHub repository.