solid-rails-app icon indicating copy to clipboard operation
solid-rails-app copied to clipboard

Twelve versions (gradually implemented) of a Web and REST API to demonstrate how Solid::Process can add value to a Ruby on Rails application.

✨ Solid Rails App

Web and REST API application made with Ruby on Rails + solid-process.

🙌 Repository branches

This repository has three branches:

  1. vanilla-rails: 100% Rails way + 0% solid-process.
  2. main: 95% Rails way + 5% solid-process. (📍 you are here)
  3. solid-process: 20% Rails way + 80% solid-process.

📊 Rails stats and code quality

Branch LOC Rubycritic Tests coverage
vanilla-rails 1407 94.26 98.5%
main 1517 94.30 98.57%
solid-process 1883 93.93 97.13%

Use:

  • bin/rails test to generate the tests coverage report.
  • bin/rails stats to generate the LOC report.
  • bin/rails rubycritic to generate the rubycritic (code quality) report.

📢 Disclaimer

The goal of this branch is to show how the solid-process can be progressively introduced into a Rails application (check out the User::Registration).

You can use it only where you see fit, and you don't need to choose between one approach (Rails Way) or another (solid-process), as both can coexist in a complementary and friendly way.

🌟 Highlights of what solid-process can bring to you

  1. The solid-process uses Rails's known components, such as ActiveModel attributes, validations, callbacks, and more. This way, you can use the same tools you are already familiar with.

  2. A way for representing/writing critical system operations. It feels like having code that documents itself. You can see the operation's steps, inputs, outputs, side effects, and more in one place.

  3. A less coupled codebase, given that this structure encourages the creation of cohesive operations (with a specific purpose), thus reducing the concentration of logic in ActiveRecord models.

    e.g., several callbacks from the User model were replaced by the User::Registration process.

  4. Standardization of instrumentation and observability of what occurs within each process (Implement a listener to do this automatically and transparently for the developer [1]). This will help you better understand what is happening within the system.

    User::Registration event logs sample:
     #0 User::Registration
     * Given(email:, password:, password_confirmation:)
     * Continue(user:) from method: create_user
     * Continue(account:) from method: create_user_account
     * Continue() from method: create_user_inbox
     * Continue() from method: create_user_token
     * Continue() from method: send_email_confirmation
     * Success(:user_registered, user:)
     
  5. The file structure reveals the system's critical processes, making it easier to understand its behavior and find where to make changes. Check out the app/models directory.

    app/models file structure (checkout the solid-process branch to see a more complete example):
     app/models/user
     └── registration.rb
     

🤔 How do we be aware of the system's critical processes?

Use the following command to generate a list of all processes in the system:

bin/rails solid:processes
Lines:
      62 ./app/models/user/registration.rb

Files: 1

📚 Table of contents

  • System dependencies
  • Setup
  • How to run the test suite
  • How to run the application locally
  • API Documentation (cURL examples)
    • User
      • Registration
      • Authentication
      • Account deletion
      • Access token updating
      • Password updating
      • Password resetting - Link to change the password
      • Password resetting - Change the password
    • Task List
      • Listing
      • Creation
      • Updating
      • Deletion
    • Task
      • Listing
      • Creation
      • Updating
      • Deletion
      • Marking as completed
      • Marking as incomplete
  • Web app screenshots

System dependencies

  • SQLite3
  • Ruby 3.2.3
    • bundler >= 2.5.6

Setup

  1. Install system dependencies
  2. Create a config/master.key file with the following content:
echo 'a061933f96843c82342fb8ab9e9db503' > config/master.key

chmod 600 config/master.key
  1. Run bin/setup

How to run the test suite

  • bin/rails test

How to run the application locally

  1. bin/rails s
  2. Open in your browser: http://localhost:3000

API Documentation (cURL examples)

Set the following environment variables to use the examples below:

export API_HOST="http://localhost:3000"
export API_TOKEN="MY_ACCESS_TOKEN"

You can get the API_TOKEN by:

  1. Using the below User / Registration request.
  2. or performing the below User / Authentication request.
  3. or copying the access_token from Sign In >> Settings >> API page.

User

Registration

curl -X POST "$API_HOST/api/v1/user/registrations" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "[email protected]",
      "password": "123123123",
      "password_confirmation": "123123123"
    }
  }'

Authentication

curl -X POST "$API_HOST/api/v1/user/sessions" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "[email protected]",
      "password": "123123123"
    }
  }'

Account deletion

curl -X DELETE "$API_HOST/api/v1/user/registrations" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Access token updating

curl -X PUT "$API_HOST/api/v1/user/tokens" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Password updating

curl -X PUT "$API_HOST/api/v1/user/passwords" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d '{
    "user": {
      "current_password": "123123123",
      "password": "321321321",
      "password_confirmation": "321321321"
    }
  }'

Password resetting - Link to change the password

curl -X POST "$API_HOST/api/v1/user/passwords/reset" \
  -H "Content-Type: application/json" \
  -d '{"user": {"email": "[email protected]"}}'

Password resetting - Change the password

curl -X PUT "$API_HOST/api/v1/user/passwords/reset" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "token": "TOKEN_RETRIEVED_BY_EMAIL",
      "password": "123123123",
      "password_confirmation": "123123123"
    }
  }'

Task List

Listing

curl -X GET "$API_HOST/api/v1/task/lists" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Creation

curl -X POST "$API_HOST/api/v1/task/lists" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d '{"task_list": {"name": "My Task List"}}'

Updating

curl -X PUT "$API_HOST/api/v1/task/lists/2" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d '{"task_list": {"name": "My List"}}'

Deletion

curl -X DELETE "$API_HOST/api/v1/task/lists/2" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Task

Listing

# ?filter=completed | incomplete

curl -X GET "$API_HOST/api/v1/task/lists/1/items" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Creation

curl -X POST "$API_HOST/api/v1/task/lists/1/items" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d '{"task": {"name": "My Task"}}'

Updating

# "completed": true | 1 | false | 0

curl -X PUT "$API_HOST/api/v1/task/lists/1/items/1" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN" \
  -d '{"task": {"name": "My Task", "completed": true}}'

Deletion

curl -X DELETE "$API_HOST/api/v1/task/lists/1/items/1" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Marking as completed

curl -X PUT "$API_HOST/api/v1/task/lists/1/items/1/complete" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Marking as incomplete

curl -X PUT "$API_HOST/api/v1/task/lists/1/items/1/incomplete" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_TOKEN"

Web app screenshots

Sign in

Forgot password

Sign up

Tasks

Task Lists

Settings

Sign out