Moduler MIT GitHub

Open-source Rails engine

Stop rebuilding the boring half of every Rails app.

Sign-up, sign-in, a profile, an admin panel, a blog, the terms your users accept. 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.5.0
Rails
8.1+
Ruby
3.2+
Getting started
# Gemfile
gem "moduler", github: "tkowalewski/moduler"

# config/routes.rb
moduler "/application"

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

Accounts, sessions, an admin panel, a blog and legal documents, working, in five 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

    Plug your own screens in

    Add routes inside the engine with a block on moduler "/path", list them in config/moduler.yml, and they appear in the app and admin sidebars — built from the same form, list and page components the modules use.

  6. 06

    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

Five modules are in. Eleven more are specified.

In the repository today

Users

Sign-up with a normalised email address as the identity, passwords hashed by has_secure_password, member and administrator roles, UUID primary keys, and a profile page where users change their password. Accounts are active, waitlisted or inactive — and only active ones can sign in.

Sessions

Sign in over a signed, http-only cookie that expires after 30 days, with rate limits against brute force. Users see every sign-in with its IP and user agent, and revoke one or all of them.

Admin panel

A back office behind Pundit policies: search, create and edit users, set who is waitlisted, revoke anyone's sessions, manage articles and documents — and add your own resources to its sidebar from config/moduler.yml.

Articles

A blog or changelog written in Markdown, with a live-preview editor, image uploads through Active Storage, clean /articles/:path URLs and title, description and keywords meta tags.

Documents

Privacy policy, terms and conditions and data processing agreements, uploaded as PDFs in the admin panel. Signed-in users accept every document before they get into the app — and again the moment you publish a new one — with a record of who accepted what. The profile, sign-out and the admin panel stay open meanwhile.

Underneath, the view components the modules are built from — form fields, a Markdown editor, page headers, lists, filters, badges and buttons — are yours to use in your own screens.

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.

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.

Boards & tasks

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

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

    Articles store their images and documents their PDFs with Active Storage, so install its tables alongside Moduler's.

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

    Configure Moduler

    The resources listed here get a link in the app and admin sidebars. Start with empty lists and add your own as you build them.

    # config/moduler.yml
    shared:
      resources: []
      administration:
        resources: []
  8. 08

    Mount Moduler in your routes

    moduler mounts the engine at the path you give it. Routes in the optional block are drawn inside the engine, next to its own.

    # config/routes.rb
    moduler "/application"
    
    # or, with routes of your own
    moduler "/application" do
      get "/reports", to: "reports#index"
    end
  9. 09

    Start the application server

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

    $ bin/dev
  10. 10

    Make yourself an administrator

    New users are members. Promote your own account and the admin panel opens at /application/administration. Upload your privacy policy and terms under Documents, and every user accepts them before using the app.

    $ bin/rails runner \
        'Moduler::User.find_by!(email_address: "you@example.com").administrator!'

05  /  Open source

Early days, and honest about it.

Users, sessions, the admin panel, articles and documents 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 on PostgreSQL, Ruby 3.2+, Tailwind, importmap, Turbo and Stimulus, with ViewComponent, Pundit and Kaminari inside. No JavaScript build step to adopt.

Stability

0.5.0. Expect the configuration to move before 1.0 — the changelog says what changed and what to run.