Module: Otto::Core::StaticMounts
- Included in:
- Otto
- Defined in:
- lib/otto/core/static_mounts.rb
Overview
Explicit static-file registration (issue #267).
A static mount binds a URL prefix to one directory on disk:
otto.mount_static('/assets', root: 'public/assets')
Requests for GET /assets/public: directory (Otto::Core::FileSafety): the candidate is
canonicalized with File.realpath and must land inside the mount's
canonical root, be a regular readable file, and be owned by the process
user or group. A mount never authorizes anything outside its own root,
so several mounts can point into unrelated directories without exposing
their parents or siblings.
Registration is a boot-time operation. The root is canonicalized once, when the mount is registered, and every failure mode (missing, unreadable, not a directory, escaping symlink, malformed prefix, duplicate prefix) raises ArgumentError immediately so a misconfigured application does not start. Mounts participate in configuration freezing: mount_static raises FrozenError after freeze_configuration!, and the mount table is an immutable, sorted snapshot that dispatch reads without any per-request mutation.
Dispatch precedence is fixed: literal routes, then static mounts (longest
prefix first), then the implicit public: directory, then dynamic
routes. A mount claims files, not the prefix: when no mount root
contains the requested file the request falls through to the next
stage exactly as an unregistered path would. See Otto::Core::Router.
Defined Under Namespace
Classes: StaticMount
Instance Method Summary collapse
-
#mount_static(prefix, root:) ⇒ StaticMount
Serve the files under
rootat URLs beneathprefix. -
#static_mounts ⇒ Array<StaticMount>
Registered mounts, longest prefix first.
Instance Method Details
#mount_static(prefix, root:) ⇒ StaticMount
Serve the files under root at URLs beneath prefix.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/otto/core/static_mounts.rb', line 83 def mount_static(prefix, root:) ensure_not_frozen! clean_prefix = normalize_mount_prefix(prefix) if @static_mounts.any? { |mount| mount.prefix == clean_prefix } raise ArgumentError, "Static mount prefix #{display_mount_prefix(clean_prefix).inspect} is already registered" end canonical_root = canonicalize_mount_root(clean_prefix, root) mount = StaticMount.new(clean_prefix, canonical_root, Rack::Files.new(canonical_root).freeze).freeze # Longest prefix first so an overlay ('/assets/vendor') is consulted # before the mount that contains it ('/assets'). Ties cannot happen: # prefixes are unique. Rebuild rather than mutate so in-flight readers # keep their snapshot. @static_mounts = (@static_mounts + [mount]).sort_by { |m| -m.prefix.length }.freeze Otto.structured_log(:debug, 'Static mount registered', { prefix: mount.display_prefix, root: mount.root }) mount end |
#static_mounts ⇒ Array<StaticMount>
Registered mounts, longest prefix first. Frozen snapshot; a new array replaces it on every registration so readers never observe a partial update.
68 69 70 |
# File 'lib/otto/core/static_mounts.rb', line 68 def static_mounts @static_mounts end |