Moduler MIT GitHub

Open-source Rails engine

Stop rebuilding the boring half of every Rails app.

Accounts, sign-in, an admin panel, notifications, settings. Every application needs them. None of them are your product. Moduler ships them as modules you mount — not code you fork. MIT-licensed, developed in the open.

Version
0.1.0
Rails
8.1+
Ruby
3.2+
Getting started
# Gemfile
gem "moduler", github: "tkowalewski/moduler"

# config/routes.rb
moduler "/application" do
  root "home#index"
end

$ bin/rails moduler:install:migrations
$ bin/rails db:migrate

Sign-up and sign-in, working, in four steps.

01  /  Problem

The first three weeks are always the same three weeks.

You have an idea worth building. What you write first is a registration form, a session store, a users table in some admin screen you also have to build. Work that is identical in every project, and finished before you have written a single line of the thing you actually set out to make.

Copy-paste archaeology

You lift the auth code out of your last project. It carries that project's assumptions, its naming, and the bug you never got around to fixing.

Generators walk away

A scaffold or a boilerplate template writes into your app once and leaves. From that moment the code is yours to maintain, and upstream improvements never reach you.

Forks don't merge back

Starting from a fork means every app drifts on its own branch. Five apps later you are maintaining five slightly different account systems.

All or nothing

You decide up front what the app will need. Adding surveys or an audit log in month six means going back to the generator, by hand, against code you have already changed.

02  /  Solution

A dependency, not a starting point.

Moduler is a mountable Rails engine. The modules live in the gem and stay there. Your repository holds your product — and a list of the modules you turned on.

  1. 01

    Pick the modules you need

    Each module is self-contained: its models, migrations, controllers and views arrive together, namespaced under Moduler::, and work on their own.

  2. 02

    Add the rest whenever you want

    Turning on a module in month six is a bundle update and a migration — not a re-run of a generator over code you have since rewritten.

  3. 03

    Fixes reach every app at once

    A correction to the session logic lands in every application that depends on Moduler. There is no downstream copy to patch, because there is no downstream copy.

  4. 04

    Override anything, by name

    Your own view, controller or model at the same path wins. Replace the parts that need to be yours and leave the rest on the defaults.

  5. 05

    Nothing is a black box

    It is MIT-licensed and the whole engine is on GitHub. Read the authentication concern before you trust it with your users — that is the point of shipping it this way.

03  /  Modules

Two modules are in. Fourteen more are specified.

In the repository today

Users

Sign-up with an email address as the identity, passwords hashed by has_secure_password, UUID primary keys, and the record everything else hangs off.

Sessions

Sign in and out over a signed, http-only cookie. One row per sign-in with its IP and user agent, a Current.session, and allow_unauthenticated_access where you need a way past the door.

Next on the list

These are not a wishlist. Every one of them is code I have written by hand before, more than once, and is being generalised into a module rather than designed from scratch.

Password reset

A signed, expiring token and the two screens around it.

Accounts

Multi-user accounts with memberships and owner, administrator and member roles.

Admin panel

A back office over every module: lists, filters, search and impersonation.

Recovery codes

Single-use backup codes, hashed at rest, for when the second factor is gone.

Notifications

In-app messages with per-user read state and a kind for each one.

Emails

Editable transactional templates and a log of everything actually sent.

Conversations

A support inbox inside the app, with unread state on both sides.

Feedback

A small form anywhere in the app that lands in the admin panel.

Surveys

Forms with ordered questions, public slugs and collected responses.

Articles

A blog or changelog with clean paths, images and full-text search.

Boards & tasks

Ordered columns, assignable tasks and comments — internal or client-facing.

Documents

Versioned terms and privacy policies, with a record of who accepted what.

Preferences

Typed settings — string, boolean, integer — editable without a deploy.

Audit log

Who did what to which record, kept as an append-only trail.

Something you need that isn't here?

The order is not fixed. Open an issue and say which module you want first.

04  /  How-to

From an empty directory to a working app.

A fresh Rails application on PostgreSQL, with Moduler handling sign-up and sign-in. You need Ruby and Docker installed — everything else is below.

  1. 01

    Create the project directory

    Make a directory for the app and install Rails.

    Requires Ruby 3.2+ — installed system-wide or through a version manager such as rbenv, asdf or mise.

    $ mkdir example && cd example
    $ gem install rails
  2. 02

    Start PostgreSQL

    Add a docker-compose.yml and bring the database up.

    Requires Docker — install it first if you haven't already.

    # docker-compose.yml
    services:
      postgres:
        image: postgres:18
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD:
          POSTGRES_HOST_AUTH_METHOD: "trust"
        ports:
          - "5432:5432"
        volumes:
          - postgres:/var/lib/postgresql
    
    volumes:
      postgres:
    
    $ docker-compose up -d
  3. 03

    Generate the Rails application

    $ rails new . --name=Example --database=postgresql \
        --javascript=importmap --css=tailwind
  4. 04

    Add the Moduler gem

    # Gemfile
    gem "moduler", github: "tkowalewski/moduler"
    
    $ bundle install
  5. 05

    Point the database at Docker

    Add the connection details to the default block, so development and test both use the container.

    # config/database.yml
    default: &default
      adapter: postgresql
      encoding: unicode
      max_connections: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
      host: localhost
      port: 5432
      user: postgres
      password:
  6. 06

    Create the database and install migrations

    $ bin/rails db:create
    $ bin/rails moduler:install:migrations
    $ bin/rails db:migrate
  7. 07

    Define your routes inside moduler

    The block mounts the engine for you. Everything inside it is your application — plain Rails routing, namespaces included.

    # config/routes.rb
    moduler "/application" do
      root "home#index"
    end
  8. 08

    Create the controller for root

    "home#index" points at a controller in your app. Skip the generated route — it is already inside the moduler block — then make the controller inherit from ::Moduler::ApplicationController.

    $ bin/rails generate controller Home index --skip-routes
    
    # app/controllers/home_controller.rb
    class HomeController < ::Moduler::ApplicationController
      def index
      end
    end
  9. 09

    Start the application server

    Runs Rails together with the Tailwind watcher. Open http://localhost:3000/application in your browser.

    $ bin/dev

05  /  Open source

Early days, and honest about it.

Users and sessions work. The rest arrives module by module, each one generalised from code that has already had to hold up. It is MIT-licensed, so read it, use it, fork it if you disagree with me — and if a module you need is missing, a module is exactly the right size for a pull request.

License

MIT. No open-core tier, no license that changes later.

Install

From GitHub for now — it is not on RubyGems until the API settles.

Built on

Rails 8.1, Ruby 3.2+, Tailwind, importmap and Stimulus. No other dependencies to adopt.

Stability

0.1.0. Expect the configuration to move before 1.0.