### Setup preload.rb for Rails Application Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Creates the `preload.rb` file, which is responsible for loading the Rails application environment before worker processes are forked. This optimizes memory usage. ```ruby # frozen_string_literal: true require_relative "config/environment" ``` -------------------------------- ### Run Production Server with Falcon Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Starts the Falcon production server using the configured `falcon.rb` file. Alternatively, the `falcon host` command can be used directly. ```bash bundle exec ./falcon.rb # Or: bundle exec falcon host ``` -------------------------------- ### Install Falcon-Rails Gem Source: https://github.com/socketry/falcon-rails/blob/main/context/getting-started.md Adds the Falcon Rails gem to your project's Gemfile. This is the initial step to integrate Falcon with your Rails application. ```bash bundle add falcon-rails ``` -------------------------------- ### Install Falcon Rails Gem Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Adds the falcon-rails gem to your Rails application's Gemfile. It's recommended to remove other web servers like Puma after installation. ```bash bundle add falcon-rails bundle remove puma ``` -------------------------------- ### Serve Rails Application with Falcon Source: https://github.com/socketry/falcon-rails/blob/main/context/getting-started.md Starts your Rails application using the Falcon web server. This command is used to run your application in development or production environments. ```bash > bundle exec falcon serve ``` -------------------------------- ### Configure Falcon for Production Deployment Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Sets up the `falcon.rb` configuration file for production deployments. It includes Rack environment setup, preloading the Rails application, and defining the network endpoint. ```ruby #!/usr/bin/env -S falcon-host # frozen_string_literal: true require "falcon/environment/rack" hostname = File.basename(__dir__) service hostname do include Falcon::Environment::Rack # This file will be loaded in the main process before forking. preload "preload.rb" # Default to port 3000 unless otherwise specified. port {ENV.fetch("PORT", 3000).to_i} # Default to HTTP/1.1 for compatibility with proxies: endpoint do Async::HTTP::Endpoint .parse("http://0.0.0.0:#{port}") .with(protocol: Async::HTTP::Protocol::HTTP11) end end ``` -------------------------------- ### Make falcon.rb Executable Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Grants execute permissions to the `falcon.rb` configuration file, allowing it to be run directly as a script for starting the production server. ```bash chmod +x falcon.rb ``` -------------------------------- ### Install Development TLS Certificates Source: https://github.com/socketry/falcon-rails/blob/main/context/getting-started.md Installs local development TLS certificates required for running Falcon in development mode. This enables secure connections during local testing. ```bash > bundle exec bake localhost:install ``` -------------------------------- ### Start Rails Application with Falcon (HTTP) Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Starts your Rails application with Falcon in development mode, explicitly binding to an HTTP endpoint. This is useful if you prefer not to use HTTPS during development. ```bash bundle exec falcon serve -b http://localhost:3000 ``` -------------------------------- ### Install Live::View JavaScript Packages Source: https://context7.com/socketry/falcon-rails/llms.txt Installs the necessary JavaScript packages for Live::View using `importmap`. This command pins the `@socketry/live` package for use in the application. ```bash # Install Live::View JavaScript packages bin/importmap pin @socketry/live ``` -------------------------------- ### Initialize Live::View Client Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Initializes the Live::View client by importing the Live class and starting the connection. This JavaScript code makes the Live connection globally available as `window.live`. ```javascript import {Live} from "@socketry/live" window.live = Live.start() ``` -------------------------------- ### Install Live::View JavaScript Packages Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Installs the necessary JavaScript packages for Live::View and morphdom using the importmap tool. This ensures the client-side components are available for real-time functionality. ```bash > bin/importmap pin @socketry/live Pinning "@socketry/live" to vendor/javascript/@socketry/live.js via download from https://ga.jspm.io/npm:@socketry/live@0.14.0/Live.js Pinning "morphdom" to vendor/javascript/morphdom.js via download from https://ga.jspm.io/npm:morphdom@2.7.7/dist/morphdom-esm.js ``` -------------------------------- ### Development Server with HTTPS Source: https://context7.com/socketry/falcon-rails/llms.txt Start the Falcon development server with automatic HTTPS using self-signed certificates. This involves an initial setup for TLS certificates and then starting the server. ```bash # Install development TLS certificates (one-time setup) bundle exec bake localhost:install # Start Falcon with HTTPS (default) bundle exec falcon serve # => Application available at https://localhost:9292 # Or start with HTTP on custom port bundle exec falcon serve -b http://localhost:3000 ``` -------------------------------- ### Start Rails Application with Falcon (HTTPS) Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Launches your Rails application using the Falcon web server in development mode, defaulting to HTTPS. The application will be accessible at https://localhost:9292. ```bash bundle exec falcon serve ``` -------------------------------- ### Configure Worker Processes in falcon.rb Source: https://github.com/socketry/falcon-rails/blob/main/guides/getting-started/readme.md Demonstrates how to configure the number of worker processes for Falcon in the `falcon.rb` file. This setting is controlled by the `WEB_CONCURRENCY` environment variable, defaulting to 2. ```ruby # In your falcon.rb service hostname do include Falcon::Environment::Rack # Set the number of worker processes count ENV.fetch("WEB_CONCURRENCY", 2).to_i # ... rest of configuration end ``` -------------------------------- ### Initialize Live::View in JavaScript Source: https://context7.com/socketry/falcon-rails/llms.txt Initializes the Live::View library in the browser's JavaScript environment. This makes the `Live` object globally available for starting live views. ```javascript // app/javascript/live.js import { Live } from "@socketry/live" window.live = Live.start() ``` -------------------------------- ### Rails Routing for SSE Source: https://github.com/socketry/falcon-rails/blob/main/context/server-sent-events.md Configures the routes for the Server-Sent Events example in a Rails application. It maps GET requests to the `sse#index` action for the page displaying the JavaScript client and to the `sse#events` action for the SSE endpoint. ```ruby Rails.application.routes.draw do # SSE Example: get "sse/index" get "sse/events" end ``` -------------------------------- ### Install Falcon::Rails Gem Source: https://context7.com/socketry/falcon-rails/llms.txt Add the falcon-rails gem to your Rails application's Gemfile and optionally remove other web servers like Puma. ```bash # Add falcon-rails gem bundle add falcon-rails # Optionally remove Puma bundle remove puma ``` -------------------------------- ### Rails Routing Configuration for WebSockets Source: https://github.com/socketry/falcon-rails/blob/main/context/websockets.md Defines the necessary routes in `config/routes.rb` to handle WebSocket connections for a chat application. It maps GET requests to the index action and WebSocket connections to the connect action. ```ruby Rails.application.routes.draw do get "chat/index" connect "chat/connect" end ``` -------------------------------- ### Client-Side Fetch API for HTTP Streaming Source: https://github.com/socketry/falcon-rails/blob/main/guides/http-streaming/readme.md This JavaScript code demonstrates how to consume an HTTP stream on the client-side using the Fetch API and ReadableStream. It includes event listeners for starting and stopping the stream, displays streamed data in a terminal-like div, and uses an AbortController for stream cancellation. The code decodes the stream chunks using TextDecoder and handles stream completion or errors. ```javascript
``` -------------------------------- ### Rails Routing for HTTP Streaming Source: https://github.com/socketry/falcon-rails/blob/main/guides/http-streaming/readme.md This Ruby code snippet shows how to configure routes in a Rails application's `config/routes.rb` file to support HTTP streaming. It defines GET routes for 'streaming/index' to serve the page with the client-side streaming JavaScript, and 'streaming/stream' to act as the endpoint for the actual data stream. ```ruby Rails.application.routes.draw do # Streaming Example: get "streaming/index" # Page with streaming JavaScript get "streaming/stream" # HTTP streaming endpoint end ``` -------------------------------- ### Make Live.js Importable in Rails Views Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Configures the import map to make the local `live.js` file importable within Rails view templates. This allows easy inclusion of the Live::View client-side setup. ```ruby pin "live" ``` -------------------------------- ### Remove Puma Gem Source: https://github.com/socketry/falcon-rails/blob/main/context/getting-started.md Removes the Puma gem from your project. This is an optional step to ensure Falcon is the sole web server. ```bash bundle remove puma ``` -------------------------------- ### Client-side HTTP Stream Consumption Source: https://context7.com/socketry/falcon-rails/llms.txt Consume HTTP streams on the client-side using JavaScript's Fetch API and the ReadableStream interface. This example demonstrates reading and decoding streamed text data. ```javascript // Client-side: Consume HTTP stream fetch('/streaming/stream') .then(response => { const reader = response.body.getReader(); const decoder = new TextDecoder(); function readStream() { reader.read().then(({ done, value }) => { if (done) return; console.log(decoder.decode(value, { stream: true })); readStream(); }); } readStream(); }); ``` -------------------------------- ### Rails WebSocket Routing Source: https://context7.com/socketry/falcon-rails/llms.txt Configures WebSocket routes in a Rails application. It defines a standard GET route and a specific `connect` route for WebSocket connections. ```ruby # config/routes.rb Rails.application.routes.draw do get "chat/index" connect "chat/connect" # Rails 8 WebSocket route helper end ``` -------------------------------- ### Live::View WebSocket Controller in Ruby Source: https://context7.com/socketry/falcon-rails/llms.txt Sets up a Rails controller to handle Live::View WebSocket connections. It uses `Async::WebSocket::Adapters::Rails` and `Live::Page` to manage the live view lifecycle. ```ruby # app/controllers/clock_controller.rb require "async/websocket/adapters/rails" class ClockController < ApplicationController RESOLVER = Live::Resolver.allow(ClockTag) def index @tag = ClockTag.new("clock") end skip_before_action :verify_authenticity_token, only: :live def live self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| Live::Page.new(RESOLVER).run(connection) end end end ``` -------------------------------- ### Rails Routing for Live::View WebSocket Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Configures the routes in `config/routes.rb` to handle both the standard HTTP request for the clock index and the WebSocket connection for Live::View. Uses the `connect` helper for WebSocket routes. ```ruby Rails.application.routes.draw do # Live Clock Example: get "clock/index" connect "clock/live" end ``` -------------------------------- ### Rails Controller for Live::View WebSocket Source: https://github.com/socketry/falcon-rails/blob/main/guides/real-time-views/readme.md Handles incoming WebSocket connections for Live::View. It sets up a resolver to allow `ClockTag` and uses `Async::WebSocket::Adapters::Rails` to manage the connection lifecycle. ```ruby require "async/websocket/adapters/rails" class ClockController < ApplicationController RESOLVER = Live::Resolver.allow(ClockTag) def index @tag = ClockTag.new("clock") end skip_before_action :verify_authenticity_token, only: :live def live self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| Live::Page.new(RESOLVER).run(connection) end end end ``` -------------------------------- ### Live::View Clock Component Implementation Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Defines a `Live::View` class for a real-time clock. It uses `Async` for background updates, `self.update!` to trigger re-renders, and `forward_event` for handling interactions. The `render` method structures the HTML output. ```ruby require "live" class ClockTag < Live::View def initialize(...) super end def bind(page) @task ||= start_clock end def close if task = @task @task = nil task.stop end end def start_clock Async do while true sleep 1 self.update! end end def forward_event(name) "event.preventDefault(); live.forwardEvent(#JSON.dump(@id)}, event, {name: #{name.inspect}})" end render(builder) builder.tag(:div, class: "clock-container") do builder.tag(:h2) {builder.text("Live Clock")} builder.tag(:div, id: "clock", class: "clock-display") do builder.text(Time.now.strftime("%H:%M:%S")) end end end end ``` -------------------------------- ### Rails Controller for Live::View WebSocket Connection Source: https://github.com/socketry/falcon-rails/blob/main/context/real-time-views.md Sets up a Rails controller to handle Live::View connections. It whitelists the `ClockTag` using `Live::Resolver`, skips CSRF verification for the WebSocket endpoint, and uses `Async::WebSocket::Adapters::Rails` to manage the connection. ```ruby require "async/websocket/adapters/rails" class ClockController < ApplicationController RESOLVER = Live::Resolver.allow(ClockTag) def index @tag = ClockTag.new("clock") end skip_before_action :verify_authenticity_token, only: :live live self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| Live::Page.new(RESOLVER).run(connection) end end end ``` -------------------------------- ### Live::View Counter Component Implementation (Ruby) Source: https://context7.com/socketry/falcon-rails/llms.txt This Ruby code defines a CounterTag class that inherits from Live::View. It manages a counter's state, handles 'click' events for incrementing and decrementing, and updates the UI dynamically using `self.replace`. It also includes methods for forwarding events and rendering the component's HTML structure with buttons. ```ruby class CounterTag < Live::View def initialize(id, count: 0) super(id) @data["count"] = @data.fetch("count", count).to_i end def handle(event) case event[:type] when "click" case event.dig(:detail, :name) when "increment" then @data["count"] += 1 when "decrement" then @data["count"] -= 1 end update_counter end end def update_counter self.replace("#counter") do |builder| builder.tag(:div, id: "counter") { builder.text(@data["count"].to_s) } end end def forward_event(name) "event.preventDefault(); live.forwardEvent(#JSON.dump(@id)), event, {name: #{name.inspect}})" end def render(builder) builder.tag(:div, class: "counter") do builder.tag(:div, id: "counter") { builder.text(@data["count"].to_s) } builder.tag(:button, onclick: forward_event("decrement")) { builder.text("-") } builder.tag(:button, onclick: forward_event("increment")) { builder.text("+") } end end end ``` -------------------------------- ### Configure async-job Queues in Rails Initializer Source: https://github.com/socketry/falcon-rails/blob/main/context/job-processing.md This Ruby code configures async-job queues within a Rails initializer. It defines queues like 'default' (using Redis for persistence) and 'local' (using inline processing for higher throughput). It demonstrates how to specify different processors for enqueueing and dequeueing jobs. ```ruby require "async/job" require "async/job/processor/aggregate" require "async/job/processor/redis" require "async/job/processor/inline" Rails.application.configure do config.async_job.define_queue "default" do # Double-buffers incoming jobs to avoid submission latency: enqueue Async::Job::Processor::Aggregate dequeue Async::Job::Processor::Redis end config.async_job.define_queue "local" do dequeue Async::Job::Processor::Inline end end ``` -------------------------------- ### Configure Live::View Importmap Source: https://context7.com/socketry/falcon-rails/llms.txt Pins the 'live' JavaScript file in the Rails importmap configuration. This makes the Live::View JavaScript accessible to the application. ```ruby # config/importmap.rb pin "live" ``` -------------------------------- ### Production Configuration with falcon.rb Source: https://context7.com/socketry/falcon-rails/llms.txt Configure Falcon for production deployment using a `falcon.rb` file. This includes setting up worker processes, custom endpoints, and preloading the Rails environment for efficiency. ```ruby #!/usr/bin/env -S falcon-host # frozen_string_literal: true require "falcon/environment/rack" hostname = File.basename(__dir__) service hostname do include Falcon::Environment::Rack # Preload Rails before forking workers (reduces memory usage) preload "preload.rb" # Number of worker processes count ENV.fetch("WEB_CONCURRENCY", 2).to_i # Port configuration port { ENV.fetch("PORT", 3000).to_i } # HTTP/1.1 endpoint for proxy compatibility endpoint do Async::HTTP::Endpoint .parse("http://0.0.0.0:#{port}") .with(protocol: Async::HTTP::Protocol::HTTP11) end end ``` ```ruby # preload.rb - Load Rails before forking # frozen_string_literal: true require_relative "config/environment" ``` ```bash # Make executable and run chmod +x falcon.rb bundle exec ./falcon.rb # Or use: bundle exec falcon host ``` -------------------------------- ### Live::View Clock Component in Ruby Source: https://context7.com/socketry/falcon-rails/llms.txt Defines a Live::View component that displays the current time, updating every second. It uses `Async` for background tasks and `self.update!` to trigger re-renders. ```ruby # app/views/clock_tag.rb require "live" class ClockTag < Live::View def bind(page) @task ||= start_clock end def close @task&.stop @task = nil end def start_clock Async do loop do sleep 1 self.update! # Queue full re-render end end end def render(builder) builder.tag(:div, class: "clock-container") do builder.tag(:div, class: "clock-display") do builder.text(Time.now.strftime("%H:%M:%S")) end end end end ``` -------------------------------- ### Rails Server-Side WebSocket Implementation Source: https://github.com/socketry/falcon-rails/blob/main/context/websockets.md Implements a Rails controller to handle incoming WebSocket connections using the `async-websocket` adapter. It listens for messages, echoes them back to the client, and handles connection closures. Requires the `async-websocket` gem. ```ruby require "async/websocket/adapters/rails" class ChatController < ApplicationController def index # Render the page with WebSocket JavaScript end skip_before_action :verify_authenticity_token, only: :connect def connect self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| Sync do # Perpetually read incoming messages from client: while message = connection.read # Echo message back to client: response_data = { text: "Echo: #{JSON.parse(message.buffer)['text']}" } connection.send_text(response_data.to_json) connection.flush end rescue Protocol::WebSocket::ClosedError # Connection closed by client. end end end end ``` -------------------------------- ### Client-Side SSE with EventSource API Source: https://context7.com/socketry/falcon-rails/llms.txt Demonstrates how to consume Server-Sent Events on the client-side using the native EventSource API in JavaScript. It connects to the SSE endpoint and listens for different event types. ```javascript // Client-side: EventSource API const eventSource = new EventSource("/sse/events"); eventSource.addEventListener("open", () => console.log("Connected")); eventSource.addEventListener("message", (e) => console.log("Data:", e.data)); eventSource.addEventListener("user_joined", (e) => { const user = JSON.parse(e.data); console.log("User joined:", user); }); eventSource.addEventListener("error", () => console.log("Connection error")); ``` -------------------------------- ### Rails Controller for HTTP Streaming Source: https://github.com/socketry/falcon-rails/blob/main/guides/http-streaming/readme.md This Ruby code defines a Rails controller with two actions: 'index' to render the page with streaming JavaScript, and 'stream' which implements the server-side logic for HTTP streaming. It uses Rack::Response to send data progressively to the client. The 'stream' action writes lines of text with a delay, simulating a long-running task. ```ruby class StreamingController < ApplicationController def index # Render the page with streaming JavaScript end def stream body = proc do |stream| 10.downto(1) do |i| stream.write "#{i} bottles of beer on the wall\n" sleep 1 stream.write "#{i} bottles of beer\n" sleep 1 stream.write "Take one down, pass it around\n" sleep 1 stream.write "#{i - 1} bottles of beer on the wall\n" sleep 1 end end self.response = Rack::Response[200, {"content-type" => "text/plain"}, body] end end ``` -------------------------------- ### JavaScript Client for SSE Source: https://github.com/socketry/falcon-rails/blob/main/guides/server-sent-events/readme.md Consumes Server-Sent Events from the server. It establishes a connection using `EventSource`, listens for 'open', 'message', and 'error' events, and updates the UI accordingly. It also includes logic to limit the number of displayed messages. ```javascriptThis clock updates every second using Live::View.
<%= javascript_import_module_tag "live" %>