class Middleware < Protocol::HTTP::Middleware
- Inherits from
Protocol::HTTP::Middleware- Includes
Localization::Resolver
A middleware which serves dynamically generated content based on markup files.
Definitions
def initialize(app, root: Utopia::default_root, namespaces: {})
Implementation
def initialize(app, root: Utopia::default_root, namespaces: {})
super(app)
@root = root
@template_cache = Concurrent::Map.new
@node_cache = Concurrent::Map.new
@links = Links.new(@root)
@namespaces = namespaces
# Default content namespace for dynamic path based lookup:
@namespaces[CONTENT_NAMESPACE] ||= self.method(:content_tag)
# Resolve content relative to the logical invocation path:
@namespaces[RELATIVE_NAMESPACE] ||= self.method(:relative_tag)
# The core namespace for utopia specific functionality:
@namespaces[UTOPIA_NAMESPACE] ||= Tags
end
def freeze
Freeze this object and its internal state.
Signature
-
returns
self This object.
Implementation
def freeze
return self if frozen?
@root.freeze
@namespaces.values.each(&:freeze)
@namespaces.freeze
super
end
def links(path, **options)
TODO we should remove this method and expose @links directly.
Implementation
def links(path, **options)
@links.index(path, **options)
end
def fetch_template(path)
Load and cache a content template.
Signature
-
parameter
pathUtopia::Path | String The path.
-
returns
XRB::Template The parsed template.
Implementation
def fetch_template(path)
@template_cache.fetch_or_store(path.to_s) do
XRB::Template.load_file(path)
end
end
def lookup_tag(qualified_name, node)
Look up a named tag such as <entry /> or <content:page>...
Implementation
def lookup_tag(qualified_name, node)
namespace, name = XRB::Tag.split(qualified_name)
if library = @namespaces[namespace]
library.call(name, node)
end
end
def lookup_node(path, locale = nil)
Implementation
def lookup_node(path, locale = nil)
resolve_link(
@links.for(path, locale)
)
end
def resolve_link(link)
Resolve a link to an existing content node.
Signature
-
parameter
linkUtopia::Content::Link The content link.
-
returns
Node | Nil The content node when its backing file exists.
Implementation
def resolve_link(link)
if full_path = link&.full_path(@root)
if File.exist?(full_path)
return Node.new(self, link.path, link.path, full_path)
end
end
end
def respond(link, request, localization: request.localization)
Respond.
Signature
-
parameter
linkUtopia::Content::Link The content link.
-
parameter
requestUtopia::Request The application request.
-
parameter
localizationUtopia::Localization::Preferences | Nil The selected localization.
-
returns
Protocol::HTTP::Response The response.
Implementation
def respond(link, request, localization: request.localization)
if node = resolve_link(link)
attributes = request.variables&.to_hash || {}
return node.process!(request, attributes, localization: localization)
elsif redirect_uri = link[:uri]
return Utopia::Response[307, {HTTP::LOCATION => redirect_uri}, []]
end
end
def call(request)
Serve or redirect filesystem-backed content, otherwise invoke the application.
Signature
-
parameter
requestUtopia::Request The request.
-
returns
Protocol::HTTP::Response The content, redirect, or downstream response.
Implementation
def call(request)
path = request.path
# Check if the request is to a non-specific index. This only works for requests with a given name:
basename = path.basename
directory_path = File.join(@root, path.dirname.components, basename)
# If the request for /foo/bar is actually a directory, rewrite it to /foo/bar/index:
if File.directory? directory_path
index_path = [basename, INDEX]
location = path.dirname.join(index_path).to_url_path.encoded
return Utopia::Response[307, {HTTP::LOCATION => location}, []]
end
response = resolve_localized(request) do |localization|
locale = localization&.locale
if link = @links.for(path, locale, fallback: false)
self.respond(link, request, localization: localization)
end
end
if response
return response
end
return @delegate.call(request)
end