Rage Ruby Web Framework

repository·main·Indexed 23 days ago

https://github.com/rage-rb/rage

An API-first Ruby web framework utilizing fiber-based concurrency to unify APIs, background jobs, and WebSockets into a single runtime. Rage features a lean execution model with pre-compiled controller actions, built-in OpenAPI documentation generation via controller comments, and in-process durable background jobs using Rage::Deferred::Task. It can be used as a standalone framework or integrated into existing Rails applications.

Tokens
6.5K
Snippets
17
Records
46
Agent score
77%

What's inside Rage

  1. Understand Rage's execution model and design principles

    main

    Rage is built on several core architectural principles that affect how you write and expect your application to behave:

    • Single-Threaded Fiber-Based Approach: Every request is processed in its own isolated Fiber. When the code encounters blocking I/O, the Fiber pauses, allowing the single thread to handle other tasks. This eliminates thread synchronization overhead and simplifies concurrency.
    • Lean Happy Path: The framework performs as much work as possible during server initialization (boot time) to ensure the request-processing path is as fast as possible.
    • Rails Compatibility: The Controller and Cable APIs are designed to feel familiar to Rails developers, though they are unique implementations.
    • Idiomatic Ruby: The framework avoids heavy abstractions in favor of standard Ruby syntax and patterns. You are encouraged to use idiomatic Ruby rather than relying on framework-specific DSLs where standard Ruby suffices.
  2. How Rage executes controller actions

    main
    Rage optimizes request-response performance by pre-compiling controller actions during application boot. Instead of resolving callbacks and exception handlers dynamically for every incoming request, Rage resolves the full chain of these components at boot time to build a single, optimized procedure. When a request arrives, Rage executes this pre-compiled procedure directly, minimizing runtime overhead.
  3. Two ways to use Rage: Standalone vs Rails Integration

    main

    Rage can be used in two primary ways depending on your project needs:

    1. Standalone: Use rage new to create a fresh project. This provides a clean structure, CLI tools, and everything needed to build production-ready APIs from scratch.
    2. Rails Integration: Add Rage to an existing Rails application. This allows for a gradual migration where you can use Rage for new endpoints or high-traffic routes while keeping the rest of your Rails application unchanged.
  4. How Rage::OpenAPI generates API specifications

    main
    Rage::OpenAPI automates the creation of OpenAPI 3.0 specifications. It works by parsing specific comments within your controller files during the application boot process. The resulting specification is built and stored in memory, allowing it to be served efficiently for API documentation purposes.
  5. Wildcard segments in Rage routing

    main

    Rage supports wildcard segments using the * syntax. However, there are two strict rules for using them:

    1. A wildcard segment can only be in the last section of the path.
    2. Wildcard segments cannot be named (unlike standard parameters like :id).
    get "*", to: ->(env) { [404, {}, [{ message: "Not Found" }.to_json]] }
  6. How Rage::Cable handles real-time communication

    main

    Rage::Cable is the component used for real-time, bidirectional messaging over WebSockets. The workflow follows a standard pattern:

    1. Authenticate connections: Verify the identity of the client connecting via WebSocket.
    2. Subscribe to channels: Allow authenticated connections to subscribe to specific channels to receive and send messages.
  7. How Rage handles concurrency with Fibers

    main

    Rage uses a fiber-based architecture to handle I/O operations (such as HTTP requests, database queries, or file reads) without requiring threads, locks, or async/await syntax.

    When your code performs an I/O operation, the current fiber automatically pauses, allowing Rage to process other requests in the same process. Once the I/O operation completes, the fiber resumes execution exactly where it left off. This allows you to write standard, synchronous-looking Ruby code that runs concurrently.

  8. Install and set up a new Rage application

    main

    To start a new project with Rage, install the gem, use the rage new CLI command to scaffold your application, and then install the dependencies. You can also optionally install agent skills.

    Installation Steps

    1. Install the gem:

      gem install rage-rb
    2. Create a new app:

      rage new my_app
    3. Install dependencies:

      cd my_app
      bundle install
    4. Optional: Install agent skills:

      rage skills install
    5. Start the server:

      rage s

      The server will be available at http://localhost:3000.

    gem install rage-rb
    rage new my_app
    cd my_app
    bundle install
    rage skills install
    rage s
  9. Define routes using Rage.routes.draw

    main

    You can define your application's routing logic using the Rage.routes.draw block. This DSL allows you to map HTTP methods and paths to specific controllers/actions or lambda handlers.

    Key features include:

    • Named routes: Use :id style syntax for path parameters.
    • Scopes: Group routes under a common path and module prefix.
    • Root route: Define a base route using root.
    • Wildcard segments: Use * to catch all remaining paths. Note that wildcard segments must be the last segment in a path and cannot be named.
    • Constraints: Currently, only host constraints are supported (e.g., using a Regexp to match a specific host).
    Rage.routes.draw do
      get "photos/:id", to: "photos#show", constraints: { host: /myhost/ }
    
      scope path: "api/v1", module: "api/v1" do
        get "photos/:id", to: "photos#show"
      end
    
      root to: "photos#index"
    
      get "*", to: ->(env) { [404, {}, [{ message: "Not Found" }.to_json]] }
    end
  10. How Rage::Cable works

    main

    Rage::Cable provides built-in WebSocket support for Rage apps, similar to Action Cable in Rails. It allows you to:

    • Mount a separate WebSocket application.
    • Define channels and connections.
    • Subscribe clients to named streams.
    • Broadcast messages in real time.

    It uses a protocol-based system (configured via Rage.config.cable.protocol) to handle message encoding and decoding, and integrates with the project's Pub/Sub adapter for broadcasting across processes.

  11. How to use the Rage::Events system

    main

    The Rage::Events module provides a lightweight event-driven system. To use it, you follow three steps:

    1. Define an event as a data structure (e.g., using Ruby's Data.define).
    2. Define a subscriber by including Rage::Events::Subscriber and using the subscribe_to macro to register it for a specific event class.
    3. Publish the event using Rage::Events.publish.

    Subscribers implement a call method to process the event. They can optionally accept a context argument for additional metadata.

    # 1. Define an event
    UserRegistered = Data.define(:user_id)
    
    # 2. Define a subscriber
    class SendWelcomeEmail
      include Rage::Events::Subscriber
      subscribe_to UserRegistered
    
      def call(event, context: nil)
        puts "Sending welcome email to user #{event.user_id}"
      end
    end
    
    # 3. Publish an event
    Rage::Events.publish(UserRegistered.new(user_id: 1))
  12. Test WebSocket/Cable channels with RageCableHelpers

    main

    When writing channel specs (using type: :channel), RageCableHelpers allows you to simulate WebSocket connections and subscriptions.

    Workflow:

    1. Connect: Use connect(url, headers: {}) to establish a mock connection.
    2. Subscribe: Use subscribe(params) to subscribe to a channel. This requires the test class to inherit from Rage::Cable::Channel (or the appropriate connection class if specified via described_class).
    3. Perform Actions: Use perform(action, data) to trigger actions on the subscribed channel.
    4. Verify Transmissions: Use transmissions to retrieve an array of messages sent from the channel to the client.

    Key Methods:

    • connection: Returns the current mock connection.
    • subscription: Returns the current mock subscription.
    • stub_connection(identified_by: {}): Manually stubs a connection with specific identity data.
    • cookies: Provides access to a mock cookie jar for testing session-based authentication in Cable.