Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bake/utopia/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize(...)
SETUP_ROOT = File.expand_path("../../setup", __dir__)

# Configuration files which should be installed/updated:
CONFIGURATION_FILES = [".gitignore", "config.ru", "config/environment.rb", "falcon.rb", "gems.rb", "bake.rb", "test/website.rb", "fixtures/website.rb"]
CONFIGURATION_FILES = [".gitignore", "config/application.rb", "config/environment.rb", "falcon.rb", "gems.rb", "bake.rb", "test/website.rb", "fixtures/website.rb"]

# Directories that should exist:
DIRECTORIES = ["config", "lib", "pages", "public", "bake", "fixtures", "test"]
Expand Down
5 changes: 3 additions & 2 deletions bake/utopia/static.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ def generate(output_path: "static")
require "async/io"
require "async/http/endpoint"
require "async/container"
require "utopia/application"

config_path = File.join(Dir.pwd, "config.ru")
application_path = File.join(Dir.pwd, Utopia::Application::PATH)
container_class = Async::Container::Threaded
server_port = 9090

app, options = Rack::Builder.parse_file(config_path)
app = Utopia::Application.load(application_path)

container = container_class.run(count: 2) do
Async do
Expand Down
4 changes: 1 addition & 3 deletions config/external.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
utopia-project:
url: https://github.com/socketry/utopia-project.git
command: bundle exec bake test
www.codeotaku.com:
url: https://github.com/ioquatix/www.codeotaku.com.git
branch: v3-protocol-application
command: bundle exec bake test
6 changes: 3 additions & 3 deletions context/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This guide explains how to set up a `utopia` website for local development and d

## Installation

Utopia is built on Ruby and Rack. Therefore, Ruby (suggested 2.0+) should be installed and working. Then, to install `utopia` and all required dependencies, run:
Utopia is built on Ruby. Therefore, Ruby should be installed and working. Then, to install `utopia` and all required dependencies, run:

~~~ bash
$ gem install utopia
Expand Down Expand Up @@ -32,7 +32,7 @@ You will now have a basic template site running on `https://localhost:9292`.
Utopia includes a redirection middleware to redirect all root-level requests to a given URI. The default being `/welcome/index`:

```ruby
# in config.ru
# in config/application.rb

use Utopia::Redirection::Rewrite,
"/" => "/welcome/index"
Expand Down Expand Up @@ -84,7 +84,7 @@ website

Least Coverage:
pages/_page.xnode: 6 lines not executed!
config.ru: 4 lines not executed!
config/application.rb: 4 lines not executed!
pages/welcome/index.xnode: 2 lines not executed!
pages/_heading.xnode: 1 lines not executed!

Expand Down
2 changes: 1 addition & 1 deletion context/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ files:
and deployment.
- path: middleware.md
title: Middleware
description: This guide gives an overview of the different Rack middleware used
description: This guide gives an overview of the different middleware used
by Utopia.
- path: server-setup.md
title: Server Setup
Expand Down
14 changes: 10 additions & 4 deletions context/middleware.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Middleware

This guide gives an overview of the different Rack middleware used by Utopia.
This guide gives an overview of the different middleware used by Utopia.

## Static

The {ruby Utopia::Static} middleware services static files efficiently. By default, it works with `Rack::Sendfile` and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.
The {ruby Utopia::Static} middleware services static files efficiently and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.

~~~ ruby
use Utopia::Static,
Expand Down Expand Up @@ -90,7 +90,7 @@ def passthrough(request, path)

# Succeed the request and immediately respond.
# def succeed!(status: 200, headers: {}, **options)
# options may include content: string or body: Enumerable (as per Rack specifications
# options may include content: String or body: Enumerable.

suceed!
end
Expand All @@ -108,7 +108,7 @@ end

on "edit" do |request, path|
if request.post?
@user.update_attributes(request[:user])
@user.update_attributes(request.arguments["user"])
end
end

Expand Down Expand Up @@ -155,3 +155,9 @@ use Utopia::Session,
```

All session data is stored on the client, but it's encrypted with a salt and the secret key. It is impossible for the client to decrypt the data without the secret stored on the server.

When the middleware is installed, the session is available on the request:

```ruby
request.session[:user_id] = user.id
```
14 changes: 0 additions & 14 deletions fixtures/a_rack_application.rb

This file was deleted.

3 changes: 0 additions & 3 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

group :development do
gem "json"
gem "rackula"
end

group :test do
Expand All @@ -40,6 +39,4 @@
gem "bake-test-external"

gem "benchmark-ips"

gem "rack-test"
end
6 changes: 3 additions & 3 deletions guides/getting-started/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This guide explains how to set up a `utopia` website for local development and d

## Installation

Utopia is built on Ruby and Rack. Therefore, Ruby (suggested 2.0+) should be installed and working. Then, to install `utopia` and all required dependencies, run:
Utopia is built on Ruby. Therefore, Ruby should be installed and working. Then, to install `utopia` and all required dependencies, run:

~~~ bash
$ gem install utopia
Expand Down Expand Up @@ -32,7 +32,7 @@ You will now have a basic template site running on `https://localhost:9292`.
Utopia includes a redirection middleware to redirect all root-level requests to a given URI. The default being `/welcome/index`:

```ruby
# in config.ru
# in config/application.rb

use Utopia::Redirection::Rewrite,
"/" => "/welcome/index"
Expand Down Expand Up @@ -84,7 +84,7 @@ website

Least Coverage:
pages/_page.xnode: 6 lines not executed!
config.ru: 4 lines not executed!
config/application.rb: 4 lines not executed!
pages/welcome/index.xnode: 2 lines not executed!
pages/_heading.xnode: 1 lines not executed!

Expand Down
14 changes: 10 additions & 4 deletions guides/middleware/readme.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Middleware

This guide gives an overview of the different Rack middleware used by Utopia.
This guide gives an overview of the different middleware used by Utopia.

## Static

The {ruby Utopia::Static} middleware services static files efficiently. By default, it works with `Rack::Sendfile` and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.
The {ruby Utopia::Static} middleware services static files efficiently and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.

~~~ ruby
use Utopia::Static,
Expand Down Expand Up @@ -90,7 +90,7 @@ def passthrough(request, path)

# Succeed the request and immediately respond.
# def succeed!(status: 200, headers: {}, **options)
# options may include content: string or body: Enumerable (as per Rack specifications
# options may include content: String or body: Enumerable.

suceed!
end
Expand All @@ -108,7 +108,7 @@ end

on "edit" do |request, path|
if request.post?
@user.update_attributes(request[:user])
@user.update_attributes(request.arguments["user"])
end
end

Expand Down Expand Up @@ -155,3 +155,9 @@ use Utopia::Session,
```

All session data is stored on the client, but it's encrypted with a salt and the secret key. It is impossible for the client to decrypt the data without the secret stored on the server.

When the middleware is installed, the session is available on the request:

```ruby
request.session[:user_id] = user.id
```
1 change: 1 addition & 0 deletions lib/utopia.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

require_relative "utopia/version"

require_relative "utopia/application"
require_relative "utopia/import_map"
require_relative "utopia/content"
require_relative "utopia/controller"
Expand Down
84 changes: 84 additions & 0 deletions lib/utopia/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "protocol/http/middleware"
require "protocol/http/middleware/builder"

require_relative "request"
require_relative "response"

module Utopia
# The protocol-facing entrypoint for a Utopia application.
#
# This object accepts {Protocol::HTTP::Request} instances, dispatches to the
# Utopia application stack, and normalizes the result back to a
# {Protocol::HTTP::Response}.
class Application < Protocol::HTTP::Middleware
PATH = "config/application.rb".freeze

# Build a Utopia application stack using the protocol HTTP middleware builder.
# @parameter default_app [Interface(:call)] The terminal application used when the block does not call `run`.
# @parameter block [Proc] The middleware builder block.
# @returns [Application] The protocol-facing Utopia application.
def self.build(default_app = Response::NotFound, &block)
builder = Protocol::HTTP::Middleware::Builder.new(default_app)

if block
if block.arity.zero?
builder.instance_exec(&block)
else
block.call(builder)
end
end

return self.new(builder.to_app)
end

# Build the default Utopia application.
# @parameter options [Hash] Options passed to the application constructor.
# @returns [Application] The default protocol-facing Utopia application.
def self.default(**options)
self.build(**options)
end

# Load a Utopia application from a conventional configuration file.
#
# If the file defines an `Application` constant, it will be returned
# directly. If the constant is a class, it will be instantiated.
# If the file does not exist, or does not define `Application`, the default
# application is returned.
#
# @parameter path [String] The application configuration path.
# @parameter options [Hash] Options passed to the application constructor.
# @returns [Interface(:call)] The loaded protocol-facing application.
def self.load(path = PATH, **options)
if File.exist?(path)
top = Module.new
top.class_eval(File.read(path), path)

if top.const_defined?(:Application, false)
application = top.const_get(:Application)

if application.is_a?(Class)
return application.new(**options)
else
return application
end
end
end

return self.default(**options)
end

# Process a protocol HTTP request.
# @parameter request [Protocol::HTTP::Request] The incoming protocol request.
# @returns [Protocol::HTTP::Response] The normalized protocol response.
def call(request)
request = Request.new(request)

return Response.wrap(super(request))
end
end
end
9 changes: 5 additions & 4 deletions lib/utopia/content/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative "response"
require_relative "markup"
require_relative "builder"
require_relative "../request"

module Utopia
module Content
Expand All @@ -27,15 +28,15 @@ def initialize(tag)
class Document < Response
# Render a content node into a new document.
# @parameter node [Utopia::Content::Node] The content node.
# @parameter request [Rack::Request] The request.
# @parameter request [Utopia::Request] The application request.
# @parameter attributes [Hash] The attributes.
# @returns [Document] The rendered document.
def self.render(node, request, attributes)
self.new(request, attributes).render!(node, attributes)
end

# Initialize a document for a protocol request.
# @parameter request [Rack::Request] The request.
# @parameter request [Utopia::Request] The application request.
# @parameter attributes [Hash] The attributes.
def initialize(request, attributes = {})
@request = request
Expand All @@ -51,7 +52,7 @@ def initialize(request, attributes = {})

# @returns [Path] The original request path, if known.
def request_path
Path[request.env["REQUEST_PATH"]]
Path[request.request_path]
end

protected def current_base_uri_path
Expand Down Expand Up @@ -113,7 +114,7 @@ def parse_markup(markup)
MarkupParser.parse(markup, self)
end

# The Rack::Request for this document.
# The request for this document.
attr :request

# Per-document global attributes.
Expand Down
Loading
Loading