BetterModel ๐
BetterModel is a Rails engine gem (Rails 8.1+) that provides powerful extensions for ActiveRecord models, including declarative status management, permissions, state machines, validations, archiving, change tracking, sorting, filtering, and unified search capabilities.
๐ฆ Installation
Add this line to your application's Gemfile:
gem "better_model"And then execute:
$ bundle installOr install it yourself as:
$ gem install better_modelโก Quick Start
Simply include BetterModel in your model to get all features:
class Article < ApplicationRecord
include BetterModel # Includes all BetterModel concerns
# 1. STATUSABLE - Define statuses with lambdas
is :draft, -> { status == "draft" }
is :published, -> { status == "published" && published_at.present? }
is :expired, -> { expires_at.present? && expires_at <= Time.current }
is :popular, -> { view_count >= 100 }
is :active, -> { is?(:published) && !is?(:expired) }
# 2. PERMISSIBLE - Define permissions based on statuses
permit :edit, -> { is?(:draft) || (is?(:published) && !is?(:expired)) }
permit :delete, -> { is?(:draft) }
permit :publish, -> { is?(:draft) }
permit :unpublish, -> { is?(:published) }
# 3. SORTABLE - Define sortable fields
sort :title, :view_count, :published_at, :created_at
# 4. PREDICABLE - Define searchable/filterable fields
predicates :title, :status, :view_count, :published_at, :created_at, :featured
# 5. ARCHIVABLE - Soft delete with tracking (opt-in)
archivable do
skip_archived_by_default true # Hide archived records by default
end
# 6. VALIDATABLE - Declarative validation system (opt-in)
register_complex_validation :published_requirements do
return unless status == "published"
errors.add(:published_at, "must be present for published articles") if published_at.blank?
end
validatable do
# Basic validations
check :title, :content, presence: true
# Conditional validations using Rails options
check :published_at, presence: true, if: -> { status == "published" }
# Complex validations for business rules
check_complex :published_requirements
# Validation groups (multi-step forms)
validation_group :step1, [:title, :content]
validation_group :step2, [:published_at]
end
# 7. STATEABLE - Declarative state machine (opt-in)
stateable do
# Define states
state :draft, initial: true
state :published
state :archived
# Define transitions with guards and callbacks
transition :publish, from: :draft, to: :published do
check { valid? }
check { title.present? && content.present? }
before_transition { self.published_at = Time.current }
after_transition { Rails.logger.info "Article #{id} published" }
end
transition :archive, from: [:draft, :published], to: :archived
end
# 8. TRACEABLE - Audit trail with time-travel (opt-in)
traceable do
track :title, :content, :status, :published_at
track :password_hash, sensitive: :full # Complete redaction
track :credit_card, sensitive: :partial # Pattern-based masking
track :api_token, sensitive: :hash # SHA256 hashing
versions_table :article_versions # Optional: custom table
end
# 9. SEARCHABLE - Configure unified search interface
searchable do
per_page 25
max_per_page 100
default_order [:sort_published_at_desc]
security :status_required, [:status_eq]
end
# 10. TAGGABLE - Tag management with statistics (opt-in)
taggable do
tag_field :tags
normalize true # Automatic lowercase
strip true # Remove whitespace
min_length 2 # Minimum tag length
max_length 30 # Maximum tag length
delimiter "," # CSV delimiter
validates_tags minimum: 1, maximum: 10
end
end๐ก Now you can use all the features:
# โ
Check statuses
article.is?(:draft) # => true/false
article.is_published? # => true/false
article.statuses # => { draft: true, published: false, ... }
# ๐ Check permissions
article.permit?(:edit) # => true/false
article.permit_delete? # => true/false
article.permissions # => { edit: true, delete: true, ... }
# โฌ๏ธ Sort
Article.sort_title_asc
Article.sort_view_count_desc
Article.sort_published_at_desc
# ๐ Filter with predicates
Article.status_eq("published")
Article.title_cont("Rails")
Article.view_count_gteq(100)
Article.published_at_present
# ๐๏ธ Archive records
article.archive!(by: current_user, reason: "Outdated")
article.archived? # => true
article.restore!
# ๐ Query archived records
Article.archived
Article.not_archived
Article.archived_recently(7.days)
# โ
Validate with groups (multi-step forms)
article.valid?(:step1) # Validate only step1 fields
article.valid?(:step2) # Validate only step2 fields
article.errors_for_group(:step1) # Get errors for step1 only
# ๐ State machine transitions
article.state # => "draft"
article.draft? # => true
article.can_publish? # => true (checks guards)
article.publish! # Executes transition with guards & callbacks
article.published? # => true
article.state_transitions # History of all transitions
article.transition_history # Formatted history array
# โฐ Time travel & rollback (Traceable)
article.audit_trail # Full change history
article.as_of(3.days.ago) # Reconstruct past state
article.rollback_to(version) # Restore to previous version
article.changes_for(:status) # Changes for specific field
# ๐ Query changes
Article.changed_by(user.id)
Article.changed_between(1.week.ago, Time.current)
Article.status_changed_from("draft").to("published")
# ๐ Unified search with filters, sorting, and pagination
Article.search(
{ status_eq: "published", view_count_gteq: 50 },
orders: [:sort_published_at_desc],
pagination: { page: 1, per_page: 25 }
)
# ๐ท๏ธ Manage tags
article.tag_with("ruby", "rails", "tutorial")
article.untag("tutorial")
article.tagged_with?("ruby") # => true
article.tag_list = "ruby, rails, api"
# ๐ Query with tags (via Predicable)
Article.tags_contains("ruby")
Article.tags_overlaps(["ruby", "python"])
Article.tags_contains_all(["ruby", "rails"])
# ๐ Tag statistics
Article.tag_counts # => {"ruby" => 45, "rails" => 38}
Article.popular_tags(limit: 10) # => [["ruby", 45], ["rails", 38]]
Article.related_tags("ruby", limit: 5) # => ["rails", "gem", "tutorial"]๐ฏ Including Individual Concerns (Advanced)
If you only need specific features, you can include individual concerns:
class Article < ApplicationRecord
include BetterModel::Statusable # Only status management
include BetterModel::Permissible # Only permissions
include BetterModel::Archivable # Only archiving
include BetterModel::Traceable # Only audit trail & time-travel
include BetterModel::Sortable # Only sorting
include BetterModel::Predicable # Only filtering
include BetterModel::Validatable # Only validations
include BetterModel::Stateable # Only state machine
include BetterModel::Searchable # Only search (requires Predicable & Sortable)
include BetterModel::Taggable # Only tag management
# Define your features...
end๐ ๏ธ Generators
Better Model provides Rails generators to help you quickly set up migrations for features that require database tables or columns.
Traceable Generator
Create migrations for audit trail version tables:
# Basic usage - shows setup instructions
rails g better_model:traceable Article
# Create migration for versions table
rails g better_model:traceable Article --create-table
# Custom table name
rails g better_model:traceable Article --create-table --table-name=audit_log
# Run migrations
rails db:migrateGenerated migration includes:
- Polymorphic association (
item_type,item_id) - Event tracking (
created,updated,destroyed) - Change tracking (
object_changesas JSON) - User attribution (
updated_by_id) - Change reason (
updated_reason) - Optimized indexes
Archivable Generator
Add soft-delete columns to existing models:
# Basic usage - shows setup instructions
rails g better_model:archivable Article
# Add archivable columns to articles table
rails g better_model:archivable Article --create-columns
# Run migrations
rails db:migrateGenerated migration adds:
-
archived_at(datetime) - when archived -
archived_by_id(integer) - who archived it -
archive_reason(string) - why archived - Index on
archived_at
Stateable Generator
Create state machine with state column and transitions tracking:
# Basic usage - shows setup instructions
rails g better_model:stateable Article
# Create both state column and transitions table
rails g better_model:stateable Article --create-tables
# Custom initial state (default: draft)
rails g better_model:stateable Article --create-tables --initial-state=pending
# Custom transitions table name
rails g better_model:stateable Article --create-tables --table-name=article_state_history
# Run migrations
rails db:migrateGenerated migrations include:
-
State column migration:
-
state(string) with default value and index
-
-
Transitions table migration:
- Polymorphic association (
transitionable_type,transitionable_id) - Event name and state tracking
- Optional metadata (JSON)
- Optimized indexes
- Polymorphic association (
Generator Options
All generators support these common options:
-
--pretend- Dry run, show what would be generated -
--skip-model- Only generate migrations, don't show model setup instructions -
--force- Overwrite existing files
Example workflow:
# 1. Generate migrations (dry-run first to preview)
rails g better_model:traceable Article --create-table --pretend
rails g better_model:archivable Article --create-columns --pretend
rails g better_model:stateable Article --create-tables --pretend
# 2. Generate for real
rails g better_model:traceable Article --create-table
rails g better_model:archivable Article --create-columns
rails g better_model:stateable Article --create-tables
# 3. Run migrations
rails db:migrate
# 4. Enable in your model (generators show you the code)
# See model setup instructions after running each generatorRepository Generator
Create repository classes that implement the Repository Pattern:
# Basic usage - creates model repository and ApplicationRepository
rails g better_model:repository Article
# Custom path
rails g better_model:repository Article --path app/services/repositories
# Skip ApplicationRepository creation
rails g better_model:repository Article --skip-base
# With namespace
rails g better_model:repository Article --namespace AdminGenerated repository includes:
- Repository class inheriting from
ApplicationRepository(orBetterModel::Repositable::BaseRepositorywith--skip-base) - Modern Ruby 3 endless method syntax:
def model_class = Article - Commented examples of custom query methods
- Integration with BetterModel's Searchable, Predicable, and Sortable features
- Auto-generated documentation of available predicates and sort scopes (if model uses BetterModel)
ApplicationRepository (generated once):
- Base class for all repositories in your application
- Inherits from
BetterModel::Repositable::BaseRepository - Can be customized with application-wide repository behaviors
Example usage:
# app/repositories/article_repository.rb
class ArticleRepository < ApplicationRepository
def model_class = Article
def published
search({ status_eq: "published" })
end
def recent(days: 7)
search({ created_at_gteq: days.days.ago }, order_scope: { field: :created_at, direction: :desc })
end
def popular(min_views: 100)
search({ view_count_gteq: min_views }, orders: [:sort_view_count_desc])
end
def search_text(query)
search({
or: [
{ title_i_cont: query },
{ content_i_cont: query }
]
})
end
end
# In your controllers/services
repo = ArticleRepository.new
articles = repo.published
popular = repo.popular(min_views: 200)
results = repo.search({ status_eq: "published" }, page: 1, per_page: 20)
article = repo.search({ id_eq: 1 }, limit: 1)BaseRepository features:
-
search(predicates, page:, per_page:, includes:, joins:, order:, order_scope:, limit:)- Main search method -
find(id),find_by(...)- Standard ActiveRecord finders -
create(attrs),create!(attrs)- Create records -
build(attrs)- Build new instances -
update(id, attrs)- Update records -
delete(id)- Delete records -
where(...),all,count,exists?- Basic ActiveRecord methods
Search method parameters:
-
predicates: Hash of BetterModel predicates (e.g.,
{ status_eq: "published", view_count_gt: 100 }) - page/per_page: Pagination (default: page 1, 20 per page)
- includes/joins: Eager loading and associations
- order: SQL order clause
-
order_scope: BetterModel sort scope (e.g.,
{ field: :published_at, direction: :desc }) -
limit: Result limit (Integer for limit,
:defaultfor pagination,nilfor all results)
๐ Features Overview
BetterModel provides ten powerful concerns that work seamlessly together:
Core Features (Always Available)
- โจ Statusable - Declarative status management with lambda-based conditions
- ๐ Permissible - State-based permission system
- โฌ๏ธ Sortable - Type-aware sorting scopes
- ๐ Predicable - Advanced filtering with rich predicate system
Opt-in Features (Require Activation)
- ๐ Searchable - Unified search interface (Predicable + Sortable)
- ๐๏ธ Archivable - Soft delete with tracking (by user, reason)
- โ Validatable - Declarative validation DSL with conditional rules
- ๐ Stateable - Declarative state machines with guards & callbacks
- โฐ Traceable - Complete audit trail with time-travel and rollback
- ๐ท๏ธ Taggable - Tag management with normalization, validation, and statistics
See all features in detail โ
โ๏ธ Requirements
- Ruby: 3.0 or higher
- Rails: 8.1 or higher
- ActiveRecord: Included with Rails
๐พ Database Compatibility
BetterModel works with all databases supported by ActiveRecord:
| Database | Status | Notes |
|---|---|---|
| PostgreSQL | โ Full support | Recommended. Includes array and JSONB predicates |
| MySQL/MariaDB | โ Full support | NULLS emulation for sorting |
| SQLite | โ Full support | Great for development and testing |
| SQL Server | โ Full support | Standard features work |
| Oracle | โ Full support | Standard features work |
PostgreSQL-Specific Features:
- Array predicates:
overlaps,contains,contained_by - JSONB predicates:
has_key,has_any_key,has_all_keys,jsonb_contains
๐๏ธ Database Requirements
Some opt-in features require database columns. Use the provided generators to add them:
| Feature | Database Requirement | Generator Command |
|---|---|---|
| Archivable |
archived_at column |
rails g better_model:archivable Model |
| Stateable |
state, transitions columns |
rails g better_model:stateable Model |
| Traceable |
version_records table |
rails g better_model:traceable Model |
| Taggable |
tags JSONB/text column |
rails g better_model:taggable Model |
Core features (Statusable, Permissible, Predicable, Sortable, Searchable, Validatable) require no database changes.
๐ Feature Details
BetterModel provides ten powerful concerns that work together seamlessly:
๐ Statusable - Declarative Status Management
Define derived statuses dynamically based on model attributes - no database columns needed!
๐ฏ Key Benefits:
- โจ Declarative DSL with clear, readable conditions
- โก Statuses calculated in real-time from model attributes
- ๐ Reference other statuses in conditions
- ๐ค Automatic method generation (
is_draft?,is_published?) - ๐ Thread-safe with immutable registry
๐ Permissible - Declarative Permission Management
Define permissions dynamically based on model state and statuses - perfect for authorization logic!
๐ฏ Key Benefits:
- โจ Declarative DSL following Statusable pattern
- โก Permissions calculated from model state
- ๐ Reference statuses in permission logic
- ๐ค Automatic method generation (
permit_edit?,permit_delete?) - ๐ Thread-safe with immutable registry
๐๏ธ Archivable - Soft Delete with Archive Management
Soft-delete records with archive tracking, audit trails, and restoration capabilities.
๐ฏ Key Benefits:
- ๐๏ธ Opt-in activation: only enabled when explicitly configured
- ๐ Archive and restore methods with optional tracking
- โ
Status methods:
archived?andactive? - ๐ Semantic scopes:
archived,not_archived,archived_only - ๐ ๏ธ Helper predicates:
archived_today,archived_this_week,archived_recently - ๐ป Optional default scope to hide archived records
- ๐ Migration generator with flexible options
- ๐ Thread-safe with immutable configuration
โ Validatable - Declarative Validation System
Define validations declaratively with support for conditional rules, cross-field validation, business rules, and validation groups.
๐ฏ Key Benefits:
- ๐๏ธ Opt-in activation: only enabled when explicitly configured
- โจ Declarative DSL with
checkmethod for basic validations - ๐ Complex validations: reusable validation blocks for cross-field and business logic
- ๐ Validation groups: partial validation for multi-step forms
- ๐ Conditional validations using Rails
if:/unless:options - ๐ Thread-safe with immutable configuration
๐ Stateable - Declarative State Machine
Define state machines declaratively with transitions, guards, validations, and callbacks for robust workflow management.
๐ฏ Key Benefits:
- ๐๏ธ Opt-in activation: only enabled when explicitly configured
- โจ Declarative DSL for states and transitions
- ๐ก๏ธ Guards: preconditions with lambda, methods, or Statusable predicates
- โ Validations: custom validation logic per transition
- ๐ Callbacks: before/after/around hooks for each transition
- ๐ State history tracking with customizable table names
- ๐ค Dynamic methods:
pending?,confirm!,can_confirm? - ๐ Integration with Statusable for complex guard logic
- ๐ Thread-safe with immutable configuration
โฐ Traceable - Audit Trail with Time-Travel
Track all changes to your records with complete audit trail, time-travel capabilities, and rollback support.
๐ฏ Key Benefits:
- ๐๏ธ Opt-in activation: only enabled when explicitly configured
- ๐ Automatic change tracking on create, update, and destroy
- ๐ Sensitive data protection: 3-level redaction system (full, partial, hash)
- ๐ค User attribution: track who made each change
- ๐ฌ Change reasons: optional context for changes
- โฐ Time-travel: reconstruct object state at any point in history
- โฉ๏ธ Rollback support: restore records to previous versions (with sensitive field protection)
- ๐ Rich query API: find changes by user, time, or field transitions
- ๐ Flexible table naming: per-model, shared, or custom tables
- ๐ Polymorphic association for efficient storage
- ๐พ Database adapter safety: PostgreSQL, MySQL, SQLite support
- ๐ Thread-safe dynamic class creation
โฌ๏ธ Sortable - Type-Aware Sorting Scopes
Generate intelligent sorting scopes automatically with database-specific optimizations and NULL handling.
๐ฏ Key Benefits:
- ๐ฏ Type-aware scope generation (string, numeric, datetime, boolean)
- ๐ค Case-insensitive sorting for strings
- ๐พ Database-specific NULLS FIRST/LAST support
- ๐ Sort by multiple fields with chaining
- โก Optimized queries with proper indexing support
๐ Predicable - Advanced Query Scopes
Generate comprehensive predicate scopes for filtering and searching with support for all data types.
๐ฏ Key Benefits:
- โ Complete coverage: string, numeric, datetime, boolean, null predicates
- ๐ Type-safe predicates based on column type
- ๐ค Case-insensitive string matching
- ๐ Range queries (between) for numerics and dates
- ๐ PostgreSQL array and JSONB support
- ๐ Chainable with standard ActiveRecord queries
- ๐งฉ Custom complex predicates for business logic
๐งฉ Complex Predicates
For queries that go beyond single-field filtering, you can register complex predicates - custom scopes that combine multiple conditions, work with associations, or encapsulate business logic.
Basic Example:
class Article < ApplicationRecord
include BetterModel
predicates :title, :view_count, :published_at
# Define a complex predicate with parameters
register_complex_predicate :trending do |days = 7, min_views = 100|
where("published_at >= ? AND view_count >= ?", days.days.ago, min_views)
end
# Define a complex predicate for association queries
register_complex_predicate :popular_author do |min_articles = 10|
joins(:author)
.group("articles.author_id")
.having("COUNT(articles.id) >= ?", min_articles)
end
endUsage:
# Use with default parameters
Article.trending
# => Articles from last 7 days with 100+ views
# Use with custom parameters
Article.trending(14, 200)
# => Articles from last 14 days with 200+ views
# Chain with standard predicates
Article.trending(7, 100)
.title_cont("Ruby")
.status_eq("published")
.sort_view_count_desc
# Association-based queries
Article.popular_author(5)
.published_at_within(30.days)When to Use Complex Predicates:
| Standard Predicates โ | Complex Predicates ๐งฉ |
|---|---|
| Single field filtering | Multi-field conditions |
title_eq("Ruby") |
trending(days, views) |
view_count_gt(100) |
Association queries |
published_at_within(7.days) |
Business logic encapsulation |
| Simple comparisons | Custom SQL expressions |
Check if Defined:
Article.complex_predicate?(:trending) # => true
Article.complex_predicates_registry # => { trending: #<Proc> }๐ Searchable - Unified Search Interface
Orchestrate Predicable and Sortable into a powerful, secure search interface with pagination and security.
๐ฏ Key Benefits:
- ๐ฏ Unified API: single
search()method for all operations - ๐ OR conditions for complex logic
- ๐ Built-in pagination with DoS protection (max_per_page)
- ๐ Security enforcement with required predicates
- โ๏ธ Default ordering configuration
- ๐ช Strong parameters integration
- โ Type-safe validation of all parameters
- ๐ Eager loading support with
includes:,preload:,eager_load:
๐ Eager Loading Associations
Optimize N+1 queries with built-in eager loading support:
# Single association (always use array syntax)
Article.search(
{ status_eq: "published" },
includes: [:author]
)
# Multiple associations
Article.search(
{ status_eq: "published" },
includes: [:author, :comments],
preload: [:tags]
)
# Nested associations
Article.search(
{ status_eq: "published" },
includes: [{ author: :profile }]
)
# Complex mix of associations
Article.search(
{ status_eq: "published" },
includes: [:tags, { author: :profile }, { comments: :user }]
)
# Combined with pagination and ordering
Article.search(
{ status_eq: "published" },
pagination: { page: 1, per_page: 25 },
orders: [:sort_view_count_desc],
includes: [:author, :comments]
)Strategies:
-
includes:- Smart loading (LEFT OUTER JOIN or separate queries) -
preload:- Separate queries (avoids JOIN ambiguity) -
eager_load:- Force LEFT OUTER JOIN (use with caution with default_order)
๐ท๏ธ Taggable - Tag Management with Statistics
Manage tags with automatic normalization, validation, and comprehensive statistics - integrated with Predicable for powerful searches.
๐ฏ Key Benefits:
- ๐๏ธ Opt-in activation: only enabled when explicitly configured
- ๐ค Automatic normalization (lowercase, strip, length limits)
- โ Validation (min/max count, whitelist, blacklist)
- ๐ Statistics (tag counts, popularity, co-occurrence)
- ๐ Automatic Predicable integration for searches
- ๐ CSV import/export with tag_list
- ๐ PostgreSQL arrays or serialized JSON for SQLite
- ๐ฏ Thread-safe configuration
๐ Full Documentation โ | ๐ Examples โ
๐ Changelog
See CHANGELOG.md for version history and release notes.
๐ฌ Support & Community
- ๐ Issues & Bugs: GitHub Issues
- ๐ป Source Code: GitHub Repository
- ๐ Documentation: This README and detailed docs in
docs/directory
๐ Complete Documentation
๐ Feature Guides
Detailed documentation for each BetterModel concern:
- Statusable - Status management with derived conditions
- Permissible - Permission system based on state
- Archivable - Soft delete with comprehensive tracking
- Traceable - Audit trail, time-travel, and rollback
- Sortable - Type-aware sorting system
- Predicable - Advanced filtering and predicates
- Searchable - Unified search interface
- Validatable - Declarative validation system
- Stateable - State machine with transitions
- Taggable - Flexible tag management with normalization
๐ก Quick Links
- Installation
- Quick Start
- Features Overview
- Requirements
- Contributing
๐ค Contributing
We welcome contributions! Here's how you can help:
๐ Reporting Bugs
- โ Check if the issue already exists in GitHub Issues
- ๐ Create a new issue with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Ruby/Rails versions
- Database adapter
๐ Submitting Pull Requests
- ๐ด Fork the repository
- ๐ฟ Create a feature branch (
git checkout -b feature/amazing-feature) - โ๏ธ Make your changes with tests
- ๐งช Run the test suite (
bundle exec rake test) - ๐
Ensure RuboCop passes (
bundle exec rubocop) - ๐พ Commit your changes (
git commit -m 'Add amazing feature') - ๐ค Push to the branch (
git push origin feature/amazing-feature) - ๐ Open a Pull Request
๐ง Development Setup
๐ณ Using Docker (Recommended)
The easiest way to get started is with Docker, which provides a consistent development environment:
# Clone your fork
git clone https://github.com/YOUR_USERNAME/better_model.git
cd better_model
# One-time setup: build image and install dependencies
bin/docker-setup
# Run tests
bin/docker-test
# Run RuboCop
bin/docker-rubocop
# Open interactive shell for debugging
docker compose run --rm app sh
# Run any command in the container
docker compose run --rm app bundle exec [command]๐ป Local Setup (Without Docker)
If you prefer to use your local Ruby installation:
# Clone your fork
git clone https://github.com/YOUR_USERNAME/better_model.git
cd better_model
# Install dependencies
bundle install
# Run tests
bundle exec rake test
# Run SimpleCov for coverage
bundle exec rake test # Coverage report in coverage/index.html
# Run RuboCop
bundle exec rubocopRequirements:
- Ruby 3.0+ (Ruby 3.3 recommended)
- Rails 8.1+
- SQLite3
๐ Test Coverage Notes
The test suite runs on SQLite for performance and portability. Current coverage: 94.5% (1891 / 2001 lines).
Database-Specific Features Not Covered:
-
Predicable: PostgreSQL array predicates (
_overlaps,_contains,_contained_by) and JSONB predicates (_has_key,_has_any_key,_has_all_keys,_jsonb_contains) - Traceable: PostgreSQL JSONB queries and MySQL JSON_EXTRACT queries for field-specific change tracking
- Sortable: MySQL NULLS emulation with CASE statements
- Taggable: PostgreSQL native array operations (covered by Predicable tests)
These features are fully implemented with proper SQL sanitization but require manual testing on PostgreSQL/MySQL:
# Test on PostgreSQL
RAILS_ENV=test DATABASE_URL=postgresql://user:pass@localhost/better_model_test rails console
# Test on MySQL
RAILS_ENV=test DATABASE_URL=mysql2://user:pass@localhost/better_model_test rails consoleAll code has inline comments marking database-specific sections for maintainability.
๐ Code Guidelines
- โจ Follow the existing code style (enforced by RuboCop Omakase)
- ๐งช Write tests for new features
- ๐ Update documentation (README) for user-facing changes
- ๐ฏ Keep pull requests focused (one feature/fix per PR)
๐ License
The gem is available as open source under the terms of the MIT License.
Made with โค๏ธ by Alessio Bussolari