PropelApi

A comprehensive Rails generator that creates complete API resources with models, controllers, tests, and realistic seed data. Supports both PropelFacets (JSON Facet) and Graphiti serialization engines with fixed route insertion, proper indentation, and comprehensive dynamic filtering system.

๐ŸŽ‰ NEW in v0.3.3: Dynamic Filtering System

PropelApi now includes a complete automatic URL query string filtering and scoping system using the has_scope gem. This provides powerful, database-agnostic filtering across all data types with zero configuration required.

Quick Filtering Examples

# String filtering
GET /api/v1/meetings?title_contains=Project&status_in=active,pending

# Numeric filtering  
GET /api/v1/meetings?max_participants_gte=50&max_participants_lte=200

# Boolean filtering
GET /api/v1/meetings?recording_enabled_eq=true&ai_research_enabled_eq=false

# DateTime filtering
GET /api/v1/meetings?start_time_after=2024-01-01T00:00:00Z&start_time_before=2024-12-31T23:59:59Z

# Range filtering
GET /api/v1/meetings?max_participants_range=150,350

# Combined filtering with sorting and pagination
GET /api/v1/meetings?title_contains=Project&max_participants_gte=50&order_by=start_time&page=1&limit=20

Supported Filter Operators

Data Type Operators Examples
String _eq, _contains, _starts_with, _ends_with, _in name_eq=Acme, title_contains=Project
Numeric _eq, _gt, _lt, _gte, _lte, _range, _in max_participants_gte=50, price_range=100,500
Boolean _eq (supports true/false, 1/0, yes/no, on/off) recording_enabled_eq=true
DateTime _before, _after, _year, _month, _day, _date start_time_after=2024-01-01, created_at_year=2024
Date Same as DateTime event_date_after=2024-01-01
Time Same as DateTime start_time_hour=14

Automatic Features

  • Zero Configuration: Filtering works automatically for all generated resources
  • Database Agnostic: Works with SQLite, PostgreSQL, and MySQL
  • Security Built-in: SQL injection protection and input validation
  • Multi-tenancy Ready: Automatic organization scoping
  • Performance Optimized: Efficient queries with proper indexing support

Installation

PropelApi is designed as a self-extracting generator gem. You install it temporarily, run the generators to extract the code into your application, then remove the gem dependency.

Step 1: Add to Gemfile as a Path Gem

# In your Gemfile
gem 'propel_api', path: 'propel_api'

Step 2: Bundle Install

bundle install

Step 3: Unpack the Generator (Optional)

If you want to customize the generator templates:

rails generate propel_api:unpack

This extracts the generator into lib/generators/propel_api/ for customization.

Step 4: Install PropelApi

rails generate propel_api:install --adapter=propel_facets
# or
rails generate propel_api:install --adapter=graphiti

This installs the API controller base class with your chosen serialization adapter.

Step 5: Remove Gem Dependency (Optional)

After installation, you can remove the gem from your Gemfile. All functionality remains in your application.

Usage

Generate Complete API Resources

# Basic resource with PropelFacets
rails generate propel_api:resource User name:string email:string role:string

# Resource with associations
rails generate propel_api:resource Article title:string content:text user:references category:references

# ๐ŸŽ‰ NEW: Polymorphic associations (v0.3.0) - REQUIRES --parents flag
rails generate propel_api:resource Comment content:text commentable:references{polymorphic} --parents commentable:Post,Video,Product

# Polymorphic with tenancy
rails generate propel_api:resource Review rating:integer comment:text review_parent:polymorphic --parents review_parent:Product,Agency

# With Graphiti adapter
rails generate propel_api Product name:string price:decimal --adapter=graphiti

This generates:

  • Model with associations and facets/resources
  • Migration with proper constraints
  • Controller with full CRUD operations
  • Routes with proper namespacing and correct indentation
  • Tests (model, controller, integration)
  • Fixtures with realistic test data
  • Seeds with Faker-generated sample data

๐ŸŽ‰ Polymorphic Association Support (v0.3.0)

PropelApi now provides comprehensive support for polymorphic associations with automatic test generation and intelligent fixture management.

Quick Start with Polymorphic Resources

# Generate a Comment that can belong to Posts, Videos, or Products
rails generate propel_api:resource Comment body:text commentable:references{polymorphic} --parents commentable:Post,Video,Product

# Generate a Review with full tenancy support  
rails generate propel_api:resource Review rating:integer comment:text review_parent:references{polymorphic} --parents review_parent:Product,Agency

Key Features

โœจ Simple, Intuitive Syntax

  • Additional support field_name:polymorphic instead of Rails' verbose field_name:references{polymorphic}
  • No need for complex quote escaping or nested syntax

๐ŸŽฏ Required Parent Specification

  • --parents field_name:Parent1,Parent2,Parent3 defines valid parent models (REQUIRED for polymorphic associations)
  • Field name must match the polymorphic association name (e.g., commentable, schedule_parent)
  • Automatically generates varied test data using different parent types
  • Generator will block with confirmation if --parents is not provided

๐Ÿงช Complete Test Coverage

  • Model Tests: Proper polymorphic association validation and parent type checking
  • Controller Tests: Automatic inclusion of both _id and _type parameters
  • Integration Tests: Full API workflow testing with polymorphic data
  • Fixtures: Uses Rails' clean fixture_name (ModelName) syntax

๐Ÿ”ง Generated Code Quality

# Generated Model
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
  # ... other associations and validations
end

# Generated Migration  
def change
  create_table :comments do |t|
    t.text :body
    t.references :commentable, polymorphic: true, null: false
    # ... other fields
  end
end

# Generated Fixtures (clean Rails syntax)
one:
  commentable: one (Post)
  body: "Great post!"

two:
  commentable: featured_video (Video)
  body: "Amazing video content!"

๐Ÿš€ API-Ready Controllers

Controllers automatically include proper parameter handling:

class Api::V1::CommentsController < Api::V1::ApiController
  permitted_params :body, :commentable_id, :commentable_type
end

--parents Flag Usage Guide

โš ๏ธ IMPORTANT: The --parents flag requires specific syntax to work correctly.

โœ… Correct Syntax:

# Single polymorphic field
--parents field_name:Parent1,Parent2,Parent3

# Real examples:
--parents commentable:Post,Photo,Video
--parents schedule_parent:User,Room,MeetingLink
--parents review_parent:Product,Agency

# Multiple polymorphic fields  
--parents commentable:Post,Photo schedule_parent:User,Room

โŒ Incorrect Syntax (Will Fail):

# Missing field name (Thor can't parse this)
--parents Post,Photo,Video

# Array brackets (not supported)
--parents commentable:[Post,Photo,Video]

# JSON-like syntax (not supported)  
--parents "{commentable: [Post,Photo,Video]}"

๐Ÿ”‘ Key Rules:

  1. Always include field name: field_name:Parent1,Parent2
  2. Field name must match polymorphic association: If your field is commentable:polymorphic, use --parents commentable:...
  3. Use commas to separate parent types: Post,Photo,Video not Post Photo Video
  4. Whitespace is automatically cleaned: Post, Photo, Video works fine

Complete Usage Examples

# Standard resource with polymorphic association
rails generate propel_api:resource Comment content:text commentable:polymorphic --parents commentable:Post,Photo,Video

# Multi-tenant polymorphic resource
rails generate propel_api:resource Review rating:integer organization:references agency:references review_parent:polymorphic --parents review_parent:Product,Agency,User

# Schedule system with polymorphic parent
rails generate propel_api:resource Schedule name:string start_time:datetime schedule_parent:polymorphic --parents schedule_parent:User,Room,MeetingLink

# Multiple polymorphic associations in one resource
rails generate propel_api:resource Attachment file_name:string attachable:polymorphic owner:polymorphic --parents attachable:Post,Comment,Email owner:User,Agency

Breaking Changes from v0.2.x

โš ๏ธ Migration Required: The polymorphic syntax and test generation has been completely rewritten for better reliability and Rails compatibility.

  • Old: rails generate propel_api:resource Comment body:text commentable:references{polymorphic}
  • New: rails generate propel_api:resource Comment body:text commentable:polymorphic --parents commentable:Post,Video

The new approach provides:

  • More reliable test generation
  • Better fixture management
  • Cleaner, more maintainable code
  • Full Rails compatibility

๐Ÿš€ API Explorer for Development

PropelApi includes a comprehensive test application with multiple API resources perfect for development and testing. The dummy app provides a fully functional API with authentication, multi-tenancy, and polymorphic associations.

Quick Start with the Test API

# Navigate to the test application
cd test/dummy

# Set up the database
rails db:setup

# Start the development server
rails server

# Your API is now running at http://localhost:3000

Available API Endpoints

The dummy app provides these fully functional endpoints:

Authentication

  • POST /api/v1/signup - User registration
  • POST /api/v1/login - User authentication
  • GET /api/v1/me - Current user info
  • DELETE /api/v1/logout - Logout
  • POST /api/v1/reset - Password reset request
  • PATCH /api/v1/reset - Password reset update

Core Resources

  • GET|POST /api/v1/users - User management
  • GET|POST /api/v1/organizations - Organization management
  • GET|POST|PATCH|DELETE /api/v1/products - Product CRUD
  • GET|POST|PATCH|DELETE /api/v1/comments - Comment CRUD
  • GET|POST|PATCH|DELETE /api/v1/attachments - File attachments
  • GET|POST|PATCH|DELETE /api/v1/reviews - Reviews (polymorphic!)

Setting Up API Explorer Tools

Option 1: Postman Collection

Create a Postman collection with these base settings:

{
  "info": {
    "name": "PropelApi Development",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "base_url",
      "value": "http://localhost:3000/api/v1",
      "type": "string"
    },
    {
      "key": "auth_token",
      "value": "",
      "type": "string"
    }
  ]
}

Option 2: Insomnia Workspace

  1. Create a new workspace in Insomnia
  2. Set base URL: http://localhost:3000/api/v1
  3. Create environment variables:
    • base_url: http://localhost:3000/api/v1
    • auth_token: (will be populated after login)

Option 3: cURL Scripts

# Login and get token
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{"data": {"email_address": "[email protected]", "password": "password123"}}' \
  | jq -r '.token')

# Use token for authenticated requests
curl -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     http://localhost:3000/api/v1/me

API Response Format

All PropelApi endpoints follow consistent JSON:API-inspired formatting:

{
  "data": {
    "id": 1,
    "name": "Example Product",
    "price": 99.99,
    "organization": {
      "id": 1,
      "name": "Test Organization"
    }
  },
  "meta": {
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Testing Polymorphic Associations

The Reviews resource demonstrates polymorphic associations:

# Create a review for a product
curl -X POST http://localhost:3000/api/v1/reviews \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "rating": 5,
      "comment": "Great product!",
      "reviewable_id": 1,
      "reviewable_type": "Product"
    }
  }'

# Create a review for an agency
curl -X POST http://localhost:3000/api/v1/reviews \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "rating": 4,
      "comment": "Good service!",
      "reviewable_id": 1,
      "reviewable_type": "Agency"
    }
  }'

Development Tips

  1. Seed Data: Run rails db:seed to populate with realistic test data
  2. Test User: Default login is [email protected] / password123
  3. Admin User: Admin login is [email protected] / password123
  4. Multi-tenancy: All resources are scoped to organizations automatically
  5. Error Handling: All endpoints return consistent error formats
  6. Validation: Try invalid data to see validation error responses

Generate Controllers for Existing Models

# Auto-detect model attributes (with warning)
rails generate propel_api:controller User

# Explicitly auto-detect all attributes
rails generate propel_api:controller User --all-attributes

# Specify specific attributes
rails generate propel_api:controller User name:string email:string

# Custom namespace and version
rails generate propel_api:controller User --namespace=admin_api --version=v2

API Controller Usage

PropelFacets Adapter

class Api::V1::UsersController < Api::V1::ApiController
  permitted_params :name, :email, :role

  connect_facet :short, actions: [:index]
  connect_facet :details, actions: [:show, :create, :update]
end

Graphiti Adapter

class Api::V1::UsersController < Api::V1::ApiController
  self.resource = UserResource
end

Configuration Options

Configure PropelApi defaults in config/initializers/propel_api.rb:

PropelApi.configure do |config|
  # Default serialization adapter: 'propel_facets' or 'graphiti'
  config.adapter = 'propel_facets'

  # Default API namespace: 'api', 'admin_api', etc.
  config.namespace = 'api'

  # Default API version: 'v1', 'v2', etc.
  config.version = 'v1'
end

Features

โœ… Fixed Route Insertion

  • Proper indentation - Routes are inserted with correct spacing
  • Namespace support - Works with nested namespaces (api/v1)
  • No duplicates - Prevents duplicate route insertion
  • Correct positioning - Routes inserted in the right location

Automatic Resource Generation

  • Smart associations - Automatically infers relationships from field types
  • Polymorphic support - Handles commentable:references patterns
  • Proper constraints - Makes organization required, others optional
  • Realistic test data - Uses Faker for meaningful sample data

Dual Serialization Support

  • PropelFacets - Flexible JSON views with inheritance and facet system
  • Graphiti - JSON:API compliant with automatic resource generation
  • Adapter switching - Change serialization strategy per project needs

Complete Test Coverage

  • Model tests - Validations, associations, business logic
  • Controller tests - CRUD operations, parameter handling
  • Integration tests - End-to-end API workflows
  • Fixtures - Realistic test data for reliable testing

Production-Ready Features

  • Pagination - Built-in with Pagy (PropelFacets) or Graphiti
  • Filtering - HasScope integration for PropelFacets
  • Authentication - Ready for PropelAuth integration
  • Error handling - Proper JSON error responses

Self-Extracting Architecture

PropelApi follows a self-extracting pattern that provides:

  • No runtime dependencies - all code lives in your application
  • Full control - modify any component after installation
  • No black boxes - transparent, readable Rails code
  • Easy maintenance - standard Rails patterns throughout

After installation, you can:

  • Remove the gem from your Gemfile
  • Customize all generated code
  • Maintain and extend functionality independently
  • Switch between PropelFacets and Graphiti adapters

Advanced Usage

Custom Namespaces and Versions

# Admin API with different namespace
rails generate propel_api:install --namespace=admin_api --version=v2

# No namespace/version
rails generate propel_api:install --namespace=none --version=none

Model Attribute Detection

PropelApi provides intelligent attribute detection with security filtering:

# Manual attributes (no filtering)
rails generate propel_api:controller User name:string email:string

# Auto-detect with explicit flag
rails generate propel_api:controller User --all-attributes

# Auto-detect with warning (when no attributes specified)
rails generate propel_api:controller User

Security Filtering: Sensitive attributes are automatically filtered:

  • Passwords, tokens, keys, secrets
  • Timestamps and Rails internals
  • Large content fields
  • Binary data

Customize filtering in config/initializers/propel_api.rb:

PropelApi.attribute_filter.add_sensitive_pattern(/private_.*/)
PropelApi.attribute_filter.add_excluded_pattern(/legacy_.*/)

Relationship Patterns

# Standard belongs_to/has_many
rails generate propel_api Article title:string user:references

# Polymorphic associations (commentable -> Comment belongs_to :commentable, polymorphic: true)
rails generate propel_api Comment content:text commentable:references

# Resource parent pattern (resource_parent -> Attachment belongs_to :resource, polymorphic: true)  
rails generate propel_api Attachment name:string resource_parent:references

Route Generation Examples

Nested Namespace (api/v1)

# Input routes.rb:
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      # Add your API routes here
    end
  end
end

# After: rails generate propel_api:controller User
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users  # โ† Correctly indented with 4 spaces
    end
  end
end

Single Namespace

# After: rails generate propel_api:controller User --namespace=api --version=none
Rails.application.routes.draw do
  namespace :api do
    resources :users  # โ† Correctly indented with 2 spaces
  end
end

Seed Data Generation

# Generated seeds use Faker for realistic data
User.create!([
  { name: Faker::Name.name, email: Faker::Internet.email, role: 'admin' },
  { name: Faker::Name.name, email: Faker::Internet.email, role: 'user' }
])

Dependencies

PropelFacets Adapter

  • propel_facets - JSON facet system (install separately)
  • pagy - Pagination
  • has_scope - Filtering
  • ransack - Advanced search

Graphiti Adapter

  • graphiti - JSON:API resource framework
  • graphiti-rails - Rails integration
  • vandal_ui - Interactive API documentation

Route Insertion Technical Details

PropelApi includes completely fixed route insertion that handles:

Indentation Rules

  • Nested namespaces (api/v1): 4 spaces for resources
  • Single namespaces (api): 2 spaces for resources
  • Rails route method: Automatically adds 2 spaces to each line

Insertion Logic

  • Detects existing namespace structures
  • Inserts at correct position (after namespace opening)
  • Prevents duplicate routes
  • Maintains proper Rails formatting

Error Prevention

  • Validates route placement before insertion
  • Shows warnings for duplicate routes
  • Maintains file structure integrity

Documentation

After installation:

  • API examples: doc/api_controller_example.rb
  • Configuration: config/initializers/propel_api.rb
  • Generated tests show usage patterns
  • Seed files demonstrate data creation

Development

# Run generator tests
cd propel_api
bundle exec rake test

# Test route insertion specifically
bundle exec ruby -Ilib:test test/generators/propel_api/route_insertion_test.rb

Contributing

Bug reports and pull requests are welcome on GitHub.

License

The gem is available as open source under the MIT License.