Skip to content

Commit 9ab5abf

Browse files
Document Utopia fiber state APIs
Assisted-By: devx/166ed168-1c4d-4c63-a5f6-8d0d9cbff13f
1 parent 80141ee commit 9ab5abf

4 files changed

Lines changed: 60 additions & 1 deletion

File tree

lib/utopia/context.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
module Utopia
77
# Accessors for request-scoped Utopia state stored directly in fiber storage.
88
module Context
9+
# The fiber storage keys used by Utopia.
910
KEYS = {
1011
request: :utopia_request,
1112
request_path: :utopia_request_path,
@@ -16,14 +17,17 @@ module Context
1617
exception: :utopia_exception
1718
}.freeze
1819

20+
# Fetch a Utopia fiber state value.
1921
def self.[] key
2022
Fiber[KEYS.fetch(key)]
2123
end
2224

25+
# Assign a Utopia fiber state value.
2326
def self.[]= key, value
2427
Fiber[KEYS.fetch(key)] = value
2528
end
2629

30+
# Temporarily assign Utopia fiber state values for the duration of the block.
2731
def self.with(**values)
2832
previous = {}
2933

@@ -39,21 +43,25 @@ def self.with(**values)
3943
end
4044
end
4145

46+
# Clear all Utopia fiber state values from the current fiber.
4247
def self.clear
4348
KEYS.each_value do |key|
4449
Fiber[key] = nil
4550
end
4651
end
4752

53+
# Convert the current Utopia fiber state to a hash.
4854
def self.to_hash
4955
KEYS.transform_values{|key| Fiber[key]}
5056
end
5157

5258
KEYS.each_key do |name|
59+
# Fetch a named Utopia fiber state value.
5360
define_singleton_method(name) do
5461
self[name]
5562
end
5663

64+
# Assign a named Utopia fiber state value.
5765
define_singleton_method("#{name}=") do |value|
5866
self[name] = value
5967
end

lib/utopia/request.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,56 @@
1010

1111
require_relative "context"
1212

13+
# Protocol namespaces extended with Utopia request helpers.
1314
module Protocol
15+
# HTTP protocol types extended with Utopia request helpers.
1416
module HTTP
17+
# Convenience methods used by Utopia middleware.
1518
class Request
19+
# The HTTP request method.
1620
alias request_method method
1721

22+
# Whether the request method is GET.
1823
def get?
1924
self.method == "GET"
2025
end
2126

27+
# Whether the request method is HEAD.
2228
def head?
2329
self.method == "HEAD"
2430
end
2531

32+
# Whether the request method is POST.
2633
def post?
2734
self.method == "POST"
2835
end
2936

37+
# Whether the request method is PUT.
3038
def put?
3139
self.method == "PUT"
3240
end
3341

42+
# Whether the request method is PATCH.
3443
def patch?
3544
self.method == "PATCH"
3645
end
3746

47+
# Whether the request method is DELETE.
3848
def delete?
3949
self.method == "DELETE"
4050
end
4151

52+
# Whether the request method is OPTIONS.
4253
def options?
4354
self.method == "OPTIONS"
4455
end
4556

57+
# The request path without the query string.
4658
def path_info
4759
self.path&.split("?", 2)&.first
4860
end
4961

62+
# Set the request path while preserving the query string.
5063
def path_info=(value)
5164
if query = self.query
5265
self.path = "#{value}?#{query}"
@@ -57,28 +70,34 @@ def path_info=(value)
5770
@utopia_arguments = nil
5871
end
5972

73+
# The query string without the leading question mark.
6074
def query
6175
self.path&.split("?", 2)&.last if self.path&.include?("?")
6276
end
6377

78+
# Decoded query arguments.
6479
def arguments
6580
@utopia_arguments ||= decode_arguments(self.query)
6681
end
6782
alias params arguments
6883

84+
# Decoded request cookies.
6985
def cookies
7086
@utopia_cookies ||= parse_cookies(self.headers["cookie"])
7187
end
7288

89+
# The request host with optional port.
7390
def host
7491
self.authority || self.headers["host"]
7592
end
7693
alias host_with_port host
7794

95+
# Whether the request uses HTTPS.
7896
def ssl?
7997
self.scheme == "https"
8098
end
8199

100+
# The base URL for the request.
82101
def base_url
83102
if self.scheme && self.host
84103
"#{self.scheme}://#{self.host}"
@@ -87,23 +106,28 @@ def base_url
87106
end
88107
end
89108

109+
# The request user agent.
90110
def user_agent
91111
self.headers["user-agent"]
92112
end
93113

114+
# The request referrer.
94115
def referrer
95116
self.headers["referer"]
96117
end
97118
alias referer referrer
98119

120+
# The current Utopia session, if installed.
99121
def session
100122
Utopia::Context.session
101123
end
102124

125+
# The remote peer IP address, if available.
103126
def ip
104127
self.peer&.ip_address
105128
end
106129

130+
# The full request URL, if scheme and host are available.
107131
def url
108132
base_url = self.base_url
109133

@@ -114,6 +138,7 @@ def url
114138
end
115139
end
116140

141+
# Build a derived request with updated protocol fields.
117142
def with(method: self.method, path: self.path, path_info: nil)
118143
request = self.dup
119144
request.method = method
@@ -134,6 +159,7 @@ def with(method: self.method, path: self.path, path_info: nil)
134159
return request
135160
end
136161

162+
# Fetch a Rack-style compatibility value or query argument.
137163
def [] key
138164
case key
139165
when "REQUEST_METHOD"

lib/utopia/session.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
require_relative "context"
88

99
module Utopia
10+
# Session access helpers and middleware constructor.
1011
module Session
12+
# Base class for Utopia session errors.
1113
class Error < StandardError
1214
end
1315

16+
# Raised when session access requires installed session middleware.
1417
class MissingError < Error
1518
end
1619
end
@@ -20,27 +23,32 @@ class MissingError < Error
2023

2124
module Utopia
2225
module Session
23-
26+
# Build a session middleware instance.
2427
def self.new(...)
2528
Middleware.new(...)
2629
end
2730

31+
# The current session, if session middleware is installed.
2832
def self.current
2933
Context.session
3034
end
3135

36+
# The current session, or raise a clear error if sessions are unavailable.
3237
def self.required
3338
self.current or raise MissingError, "No current Utopia session!"
3439
end
3540

41+
# Fetch a value from the current session.
3642
def self.[] key
3743
self.required[key]
3844
end
3945

46+
# Assign a value in the current session.
4047
def self.[]= key, value
4148
self.required[key] = value
4249
end
4350

51+
# Delete a value from the current session.
4452
def self.delete(key)
4553
self.required.delete(key)
4654
end

lib/utopia/session/lazy_hash.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ module Utopia
77
module Session
88
# A simple hash table which fetches it's values only when required.
99
class LazyHash
10+
# Base class for session mutation errors.
1011
class MutationError < Session::Error
1112
end
1213

14+
# Raised when mutating a session after it has been committed.
1315
class AlreadyCommittedError < MutationError
1416
end
1517

18+
# Raised when mutating a session from a non-owning fiber.
1619
class WrongFiberError < MutationError
1720
end
1821

22+
# Initialize a lazy hash with a block for loading values.
1923
def initialize(&block)
2024
@changed = false
2125
@values = nil
@@ -25,13 +29,18 @@ def initialize(&block)
2529
@loader = block
2630
end
2731

32+
# The loaded session values, if already loaded.
2833
attr :values
34+
35+
# The fiber which owns session mutation.
2936
attr :owner
3037

38+
# Fetch a session value.
3139
def [] key
3240
load![key]
3341
end
3442

43+
# Assign a session value.
3544
def []= key, value
3645
check_mutable!
3746

@@ -45,10 +54,12 @@ def []= key, value
4554
return value
4655
end
4756

57+
# Check whether the session includes the specified key.
4858
def include?(key)
4959
load!.include?(key)
5060
end
5161

62+
# Delete a session value.
5263
def delete(key)
5364
check_mutable!
5465
load!
@@ -58,27 +69,33 @@ def delete(key)
5869
@values.delete(key)
5970
end
6071

72+
# Whether the session has changed since it was loaded.
6173
def changed?
6274
@changed
6375
end
6476

77+
# Whether the session has already been committed.
6578
def committed?
6679
@committed
6780
end
6881

82+
# Mark the session as committed.
6983
def commit!
7084
check_owner!
7185
@committed = true
7286
end
7387

88+
# Load the session values if they have not been loaded yet.
7489
def load!
7590
@values ||= @loader.call
7691
end
7792

93+
# Whether the session values have been loaded.
7894
def loaded?
7995
!@values.nil?
8096
end
8197

98+
# Whether the session should be committed to the response.
8299
def needs_update?(timeout = nil)
83100
# If data has changed, we need update:
84101
return true if @changed

0 commit comments

Comments
 (0)