utopia 2.25.0 → 2.26.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bake/utopia/environment.rb +16 -12
- data/bake/utopia/node.rb +5 -5
- data/bake/utopia/server.rb +8 -8
- data/bake/utopia/shell.rb +3 -3
- data/bake/utopia/site.rb +16 -16
- data/bake/utopia/static.rb +8 -8
- data/bake/utopia.rb +4 -4
- data/lib/utopia/content/document.rb +4 -4
- data/lib/utopia/content/link.rb +10 -10
- data/lib/utopia/content/links.rb +5 -5
- data/lib/utopia/content/markup.rb +5 -5
- data/lib/utopia/content/node.rb +8 -8
- data/lib/utopia/content/response.rb +5 -5
- data/lib/utopia/content/tags.rb +8 -8
- data/lib/utopia/content.rb +18 -18
- data/lib/utopia/controller/actions.rb +5 -5
- data/lib/utopia/controller/base.rb +2 -2
- data/lib/utopia/controller/respond.rb +6 -6
- data/lib/utopia/controller/rewrite.rb +3 -3
- data/lib/utopia/controller.rb +10 -10
- data/lib/utopia/exceptions/handler.rb +3 -3
- data/lib/utopia/exceptions/mailer.rb +31 -9
- data/lib/utopia/exceptions.rb +3 -3
- data/lib/utopia/extensions/date_comparisons.rb +2 -2
- data/lib/utopia/http.rb +36 -36
- data/lib/utopia/locale.rb +3 -3
- data/lib/utopia/localization.rb +7 -7
- data/lib/utopia/middleware.rb +5 -5
- data/lib/utopia/path/matcher.rb +2 -2
- data/lib/utopia/path.rb +21 -21
- data/lib/utopia/redirection.rb +4 -4
- data/lib/utopia/responder.rb +2 -2
- data/lib/utopia/session/serialization.rb +4 -4
- data/lib/utopia/session.rb +8 -8
- data/lib/utopia/setup.rb +7 -7
- data/lib/utopia/shell.rb +5 -5
- data/lib/utopia/static/local_file.rb +8 -8
- data/lib/utopia/static/mime_types.rb +2 -2
- data/lib/utopia/static.rb +11 -11
- data/lib/utopia/version.rb +1 -1
- data/lib/utopia.rb +7 -8
- data/license.md +1 -1
- data/readme.md +1 -1
- data/setup/server/git/hooks/post-receive +10 -9
- data/setup/site/Guardfile +3 -2
- data/setup/site/bake.rb +3 -3
- data/setup/site/config/environment.rb +3 -3
- data/setup/site/config/sus.rb +3 -3
- data/setup/site/config.ru +8 -8
- data/setup/site/fixtures/website.rb +6 -6
- data/setup/site/gems.rb +13 -13
- data/setup/site/test/website.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +3 -9
- metadata.gz.sig +0 -0
- data/lib/utopia/content_length.rb +0 -33
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2014-
|
4
|
+
# Copyright, 2014-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "../http"
|
7
7
|
|
8
8
|
module Utopia
|
9
9
|
class Controller
|
@@ -45,10 +45,10 @@ module Utopia
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Matches 0 or more path components.
|
48
|
-
WILDCARD_GREEDY =
|
48
|
+
WILDCARD_GREEDY = "**".freeze
|
49
49
|
|
50
50
|
# Matches any 1 path component.
|
51
|
-
WILDCARD =
|
51
|
+
WILDCARD = "*".freeze
|
52
52
|
|
53
53
|
# Given a path, iterate over all actions that match. Actions match from most specific to most general.
|
54
54
|
# @return nil if nothing matched, or true if something matched.
|
@@ -121,7 +121,7 @@ module Utopia
|
|
121
121
|
|
122
122
|
def on(first, *path, **options, &block)
|
123
123
|
if first.is_a? Symbol
|
124
|
-
first = [
|
124
|
+
first = ["**", first.to_s]
|
125
125
|
end
|
126
126
|
|
127
127
|
actions.define(Path.split(first) + path, **options, &block)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "../http"
|
7
|
+
require_relative "../responder"
|
8
8
|
|
9
9
|
module Utopia
|
10
10
|
class Controller
|
@@ -16,14 +16,14 @@ module Utopia
|
|
16
16
|
|
17
17
|
module Handlers
|
18
18
|
module JSON
|
19
|
-
APPLICATION_JSON = HTTP::Accept::ContentType.new(
|
19
|
+
APPLICATION_JSON = HTTP::Accept::ContentType.new("application", "json").freeze
|
20
20
|
|
21
21
|
def self.split(*arguments)
|
22
22
|
APPLICATION_JSON.split(*arguments)
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.call(context, request, media_range, object, **options)
|
26
|
-
if version = media_range.parameters[
|
26
|
+
if version = media_range.parameters["version"]
|
27
27
|
options[:version] = version.to_s
|
28
28
|
end
|
29
29
|
|
@@ -32,7 +32,7 @@ module Utopia
|
|
32
32
|
end
|
33
33
|
|
34
34
|
module Passthrough
|
35
|
-
WILDCARD = HTTP::Accept::MediaTypes::MediaRange.new(
|
35
|
+
WILDCARD = HTTP::Accept::MediaTypes::MediaRange.new("*", "*").freeze
|
36
36
|
|
37
37
|
def self.split(*arguments)
|
38
38
|
WILDCARD.split(*arguments)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2015-
|
4
|
+
# Copyright, 2015-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "../http"
|
7
|
+
require_relative "../path/matcher"
|
8
8
|
|
9
9
|
module Utopia
|
10
10
|
class Controller
|
data/lib/utopia/controller.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2009-
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "path"
|
7
7
|
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
8
|
+
require_relative "middleware"
|
9
|
+
require_relative "controller/variables"
|
10
|
+
require_relative "controller/base"
|
11
11
|
|
12
|
-
require_relative
|
13
|
-
require_relative
|
14
|
-
require_relative
|
12
|
+
require_relative "controller/rewrite"
|
13
|
+
require_relative "controller/respond"
|
14
|
+
require_relative "controller/actions"
|
15
15
|
|
16
|
-
require
|
16
|
+
require "concurrent/map"
|
17
17
|
|
18
18
|
module Utopia
|
19
19
|
# A middleware which loads controller classes and invokes functionality based on the requested path.
|
20
20
|
class Controller
|
21
21
|
# The controller filename.
|
22
|
-
CONTROLLER_RB =
|
22
|
+
CONTROLLER_RB = "controller.rb".freeze
|
23
23
|
|
24
24
|
def self.[] request
|
25
25
|
request.env[VARIABLES_KEY]
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2014-
|
4
|
+
# Copyright, 2014-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Utopia
|
7
7
|
module Exceptions
|
8
8
|
# A middleware which catches exceptions and performs an internal redirect.
|
9
9
|
class Handler
|
10
10
|
# @param location [String] Peform an internal redirect to this location when an exception is raised.
|
11
|
-
def initialize(app, location =
|
11
|
+
def initialize(app, location = "/errors/exception")
|
12
12
|
@app = app
|
13
13
|
|
14
14
|
@location = location
|
@@ -34,7 +34,7 @@ module Utopia
|
|
34
34
|
|
35
35
|
def log_exception(env, exception)
|
36
36
|
# An error has occurred, log it:
|
37
|
-
output = env[
|
37
|
+
output = env["rack.errors"] || $stderr
|
38
38
|
write_exception_to_stream(output, env, exception, true)
|
39
39
|
end
|
40
40
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require "net/smtp"
|
7
|
+
require "mail"
|
8
8
|
|
9
9
|
module Utopia
|
10
10
|
module Exceptions
|
@@ -17,8 +17,8 @@ module Utopia
|
|
17
17
|
:enable_starttls_auto => false
|
18
18
|
}]
|
19
19
|
|
20
|
-
DEFAULT_FROM = (ENV[
|
21
|
-
DEFAULT_SUBJECT =
|
20
|
+
DEFAULT_FROM = (ENV["USER"] || "utopia").freeze
|
21
|
+
DEFAULT_SUBJECT = "%{exception} [PID %{pid} : %{cwd}]".freeze
|
22
22
|
|
23
23
|
# @param to [String] The address to email error reports to.
|
24
24
|
# @param from [String] The from address for error reports.
|
@@ -66,6 +66,21 @@ module Utopia
|
|
66
66
|
:user_agent,
|
67
67
|
]
|
68
68
|
|
69
|
+
ENV_KEYS = [
|
70
|
+
"PATH_INFO",
|
71
|
+
"REQUEST_METHOD",
|
72
|
+
"REQUEST_PATH",
|
73
|
+
"REQUEST_URI",
|
74
|
+
"SCRIPT_NAME",
|
75
|
+
"QUERY_STRING",
|
76
|
+
"SERVER_PROTOCOL",
|
77
|
+
"SERVER_NAME",
|
78
|
+
"SERVER_PORT",
|
79
|
+
"REMOTE_ADDR",
|
80
|
+
"CONTENT_TYPE",
|
81
|
+
"CONTENT_LENGTH",
|
82
|
+
]
|
83
|
+
|
69
84
|
def generate_backtrace(io, exception, prefix: "Exception")
|
70
85
|
io.puts "#{prefix} #{exception.class.name}: #{exception.to_s}"
|
71
86
|
|
@@ -104,7 +119,14 @@ module Utopia
|
|
104
119
|
|
105
120
|
io.puts
|
106
121
|
|
107
|
-
|
122
|
+
ENV_KEYS.each do |key|
|
123
|
+
value = env[key]
|
124
|
+
io.puts "env[#{key.inspect}]: #{value.inspect}"
|
125
|
+
end
|
126
|
+
|
127
|
+
io.puts
|
128
|
+
|
129
|
+
env.select{|key,_| key.start_with? "HTTP_"}.each do |key, value|
|
108
130
|
io.puts "#{key}: #{value.inspect}"
|
109
131
|
end
|
110
132
|
|
@@ -134,11 +156,11 @@ module Utopia
|
|
134
156
|
mail.text_part.body = generate_body(exception, env)
|
135
157
|
|
136
158
|
if body = extract_body(env) and body.size > 0
|
137
|
-
mail.attachments[
|
159
|
+
mail.attachments["body.bin"] = body
|
138
160
|
end
|
139
161
|
|
140
162
|
if @dump_environment
|
141
|
-
mail.attachments[
|
163
|
+
mail.attachments["environment.yaml"] = YAML.dump(env)
|
142
164
|
end
|
143
165
|
|
144
166
|
return mail
|
@@ -156,7 +178,7 @@ module Utopia
|
|
156
178
|
end
|
157
179
|
|
158
180
|
def extract_body(env)
|
159
|
-
if io = env[
|
181
|
+
if io = env["rack.input"]
|
160
182
|
io.rewind if io.respond_to?(:rewind)
|
161
183
|
io.read
|
162
184
|
end
|
data/lib/utopia/exceptions.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2016-
|
4
|
+
# Copyright, 2016-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "exceptions/handler"
|
7
|
+
require_relative "exceptions/mailer"
|
8
8
|
|
9
9
|
module Utopia
|
10
10
|
# Middleware for handling exceptional situations.
|
data/lib/utopia/http.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2010-
|
4
|
+
# Copyright, 2010-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "rack"
|
7
7
|
|
8
|
-
require
|
8
|
+
require "http/accept"
|
9
9
|
|
10
10
|
module Utopia
|
11
11
|
# HTTP protocol implementation.
|
@@ -41,41 +41,41 @@ module Utopia
|
|
41
41
|
# A list of human readable descriptions for a given status code.
|
42
42
|
# For a more detailed description, see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
43
43
|
STATUS_DESCRIPTIONS = {
|
44
|
-
200 =>
|
45
|
-
201 =>
|
46
|
-
202 =>
|
47
|
-
203 =>
|
48
|
-
204 =>
|
49
|
-
205 =>
|
50
|
-
206 =>
|
51
|
-
300 =>
|
52
|
-
301 =>
|
53
|
-
302 =>
|
54
|
-
303 =>
|
55
|
-
304 =>
|
56
|
-
305 =>
|
57
|
-
307 =>
|
58
|
-
308 =>
|
59
|
-
400 =>
|
60
|
-
401 =>
|
61
|
-
402 =>
|
62
|
-
403 =>
|
63
|
-
404 =>
|
64
|
-
405 =>
|
65
|
-
406 =>
|
66
|
-
408 =>
|
67
|
-
409 =>
|
68
|
-
410 =>
|
69
|
-
416 =>
|
70
|
-
422 =>
|
71
|
-
500 =>
|
72
|
-
501 =>
|
73
|
-
503 =>
|
44
|
+
200 => "OK".freeze,
|
45
|
+
201 => "Created".freeze,
|
46
|
+
202 => "Accepted".freeze,
|
47
|
+
203 => "Non-Authoritive Information".freeze,
|
48
|
+
204 => "No Content".freeze,
|
49
|
+
205 => "Reset Content".freeze,
|
50
|
+
206 => "Partial Content".freeze,
|
51
|
+
300 => "Multiple Choices".freeze,
|
52
|
+
301 => "Moved Permanently".freeze,
|
53
|
+
302 => "Found".freeze,
|
54
|
+
303 => "See Other".freeze,
|
55
|
+
304 => "Not Modified".freeze,
|
56
|
+
305 => "Use Proxy".freeze,
|
57
|
+
307 => "Temporary Redirect".freeze,
|
58
|
+
308 => "Permanent Redirect".freeze,
|
59
|
+
400 => "Bad Request".freeze,
|
60
|
+
401 => "Permission Denied".freeze,
|
61
|
+
402 => "Payment Required".freeze,
|
62
|
+
403 => "Access Forbidden".freeze,
|
63
|
+
404 => "Resource Not Found".freeze,
|
64
|
+
405 => "Unsupported Method".freeze,
|
65
|
+
406 => "Not Acceptable".freeze,
|
66
|
+
408 => "Request Timeout".freeze,
|
67
|
+
409 => "Request Conflict".freeze,
|
68
|
+
410 => "Resource Removed".freeze,
|
69
|
+
416 => "Byte range unsatisfiable".freeze,
|
70
|
+
422 => "Unprocessible Entity".freeze,
|
71
|
+
500 => "Internal Server Error".freeze,
|
72
|
+
501 => "Not Implemented".freeze,
|
73
|
+
503 => "Service Unavailable".freeze
|
74
74
|
}.merge(Rack::Utils::HTTP_STATUS_CODES)
|
75
75
|
|
76
|
-
CONTENT_TYPE =
|
77
|
-
LOCATION =
|
78
|
-
CACHE_CONTROL =
|
76
|
+
CONTENT_TYPE = "content-type".freeze
|
77
|
+
LOCATION = "location".freeze
|
78
|
+
CACHE_CONTROL = "cache-control".freeze
|
79
79
|
|
80
80
|
# A small HTTP status wrapper that verifies the status code within a given range.
|
81
81
|
class Status
|
data/lib/utopia/locale.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2015-
|
4
|
+
# Copyright, 2015-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Utopia
|
7
7
|
# A structured representation of locale based on RFC3066.
|
8
8
|
Locale = Struct.new(:language, :country, :variant) do
|
9
9
|
def to_s
|
10
|
-
to_a.compact.join(
|
10
|
+
to_a.compact.join("-")
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.dump(instance)
|
@@ -18,7 +18,7 @@ module Utopia
|
|
18
18
|
|
19
19
|
def self.load(instance)
|
20
20
|
if instance.is_a? String
|
21
|
-
self.new(*instance.split(
|
21
|
+
self.new(*instance.split("-", 3))
|
22
22
|
elsif instance.is_a? Array
|
23
23
|
return self.new(*instance)
|
24
24
|
elsif instance.is_a? self
|
data/lib/utopia/localization.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2009-
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "middleware"
|
7
7
|
|
8
8
|
module Utopia
|
9
9
|
# A middleware which attempts to find localized content.
|
@@ -48,9 +48,9 @@ module Utopia
|
|
48
48
|
|
49
49
|
RESOURCE_NOT_FOUND = [400, {}, []].freeze
|
50
50
|
|
51
|
-
HTTP_ACCEPT_LANGUAGE =
|
52
|
-
LOCALIZATION_KEY =
|
53
|
-
CURRENT_LOCALE_KEY =
|
51
|
+
HTTP_ACCEPT_LANGUAGE = "HTTP_ACCEPT_LANGUAGE".freeze
|
52
|
+
LOCALIZATION_KEY = "utopia.localization".freeze
|
53
|
+
CURRENT_LOCALE_KEY = "utopia.localization.current_locale".freeze
|
54
54
|
|
55
55
|
# @param locales [Array<String>] An array of all supported locales.
|
56
56
|
# @param default_locale [String] The default locale if none is provided.
|
@@ -176,12 +176,12 @@ module Utopia
|
|
176
176
|
headers = response[1].to_a
|
177
177
|
|
178
178
|
# This response was based on the Accept-Language header:
|
179
|
-
headers << [
|
179
|
+
headers << ["Vary", "Accept-Language"]
|
180
180
|
|
181
181
|
# Althought this header is generally not supported, we supply it anyway as it is useful for debugging:
|
182
182
|
if locale = env[CURRENT_LOCALE_KEY]
|
183
183
|
# Set the Content-Location to point to the localized URI as requested:
|
184
|
-
headers[
|
184
|
+
headers["Content-Location"] = "/#{locale}" + env[Rack::PATH_INFO]
|
185
185
|
end
|
186
186
|
|
187
187
|
return response
|
data/lib/utopia/middleware.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2009-
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "http"
|
7
|
+
require_relative "path"
|
8
8
|
|
9
9
|
module Utopia
|
10
10
|
# The default pages path for {Utopia::Content} middleware.
|
11
|
-
PAGES_PATH =
|
11
|
+
PAGES_PATH = "pages".freeze
|
12
12
|
|
13
13
|
# This is used for shared controller variables which get consumed by the content middleware.
|
14
|
-
VARIABLES_KEY =
|
14
|
+
VARIABLES_KEY = "utopia.variables".freeze
|
15
15
|
|
16
16
|
# The default root directory for middleware to operate within, e.g. the web-site directory. Convention over configuration.
|
17
17
|
# @param subdirectory [String] Appended to the default root to make a more specific path.
|
data/lib/utopia/path/matcher.rb
CHANGED
data/lib/utopia/path.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2009-
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Utopia
|
7
7
|
# Represents a path as an array of path components. Useful for efficient URL manipulation.
|
8
8
|
class Path
|
9
9
|
include Comparable
|
10
10
|
|
11
|
-
SEPARATOR =
|
11
|
+
SEPARATOR = "/"
|
12
12
|
|
13
13
|
def initialize(components = [])
|
14
14
|
@components = components
|
@@ -29,7 +29,7 @@ module Utopia
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.root
|
32
|
-
self.new([
|
32
|
+
self.new([""])
|
33
33
|
end
|
34
34
|
|
35
35
|
# Returns the length of the prefix which is shared by two strings.
|
@@ -57,8 +57,8 @@ module Utopia
|
|
57
57
|
|
58
58
|
# Converts '+' into whitespace and hex encoded characters into their equivalent characters.
|
59
59
|
def self.unescape(string)
|
60
|
-
string.tr(
|
61
|
-
[$1.delete(
|
60
|
+
string.tr("+", " ").gsub(/((?:%[0-9a-fA-F]{2})+)/n) {
|
61
|
+
[$1.delete("%")].pack("H*")
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
@@ -116,34 +116,34 @@ module Utopia
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def directory?
|
119
|
-
return @components.last ==
|
119
|
+
return @components.last == ""
|
120
120
|
end
|
121
121
|
|
122
122
|
def file?
|
123
|
-
return @components.last !=
|
123
|
+
return @components.last != ""
|
124
124
|
end
|
125
125
|
|
126
126
|
def to_directory
|
127
127
|
if directory?
|
128
128
|
return self
|
129
129
|
else
|
130
|
-
return self.class.new(@components + [
|
130
|
+
return self.class.new(@components + [""])
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
134
|
def relative?
|
135
|
-
@components.first !=
|
135
|
+
@components.first != ""
|
136
136
|
end
|
137
137
|
|
138
138
|
def absolute?
|
139
|
-
@components.first ==
|
139
|
+
@components.first == ""
|
140
140
|
end
|
141
141
|
|
142
142
|
def to_absolute
|
143
143
|
if absolute?
|
144
144
|
return self
|
145
145
|
else
|
146
|
-
return self.class.new([
|
146
|
+
return self.class.new([""] + @components)
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
@@ -152,7 +152,7 @@ module Utopia
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def to_str
|
155
|
-
if @components == [
|
155
|
+
if @components == [""]
|
156
156
|
SEPARATOR
|
157
157
|
else
|
158
158
|
@components.join(SEPARATOR)
|
@@ -168,7 +168,7 @@ module Utopia
|
|
168
168
|
# @parameter other [Array(String)] The path components to append.
|
169
169
|
def join(other)
|
170
170
|
# Check whether other is an absolute path:
|
171
|
-
if other.first ==
|
171
|
+
if other.first == ""
|
172
172
|
self.class.new(other)
|
173
173
|
else
|
174
174
|
self.class.new(@components + other).simplify
|
@@ -215,17 +215,17 @@ module Utopia
|
|
215
215
|
end
|
216
216
|
|
217
217
|
def simplify
|
218
|
-
result = absolute? ? [
|
218
|
+
result = absolute? ? [""] : []
|
219
219
|
|
220
220
|
@components.each do |bit|
|
221
221
|
if bit == ".."
|
222
222
|
result.pop
|
223
|
-
elsif bit != "." && bit !=
|
223
|
+
elsif bit != "." && bit != ""
|
224
224
|
result << bit
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
result <<
|
228
|
+
result << "" if directory?
|
229
229
|
|
230
230
|
return self.class.new(result)
|
231
231
|
end
|
@@ -241,7 +241,7 @@ module Utopia
|
|
241
241
|
|
242
242
|
# Returns the last path component.
|
243
243
|
def last
|
244
|
-
if @components != [
|
244
|
+
if @components != [""]
|
245
245
|
@components.last
|
246
246
|
end
|
247
247
|
end
|
@@ -251,21 +251,21 @@ module Utopia
|
|
251
251
|
# Pops the last path component.
|
252
252
|
def pop
|
253
253
|
# We don't want to convert an absolute path to a relative path.
|
254
|
-
if @components != [
|
254
|
+
if @components != [""]
|
255
255
|
@components.pop
|
256
256
|
end
|
257
257
|
end
|
258
258
|
|
259
259
|
# @return [String] the last path component without any file extension.
|
260
260
|
def basename
|
261
|
-
basename, _ = @components.last.split(
|
261
|
+
basename, _ = @components.last.split(".", 2)
|
262
262
|
|
263
|
-
return basename ||
|
263
|
+
return basename || ""
|
264
264
|
end
|
265
265
|
|
266
266
|
# @return [String] the last path component's file extension.
|
267
267
|
def extension
|
268
|
-
_, extension = @components.last.split(
|
268
|
+
_, extension = @components.last.split(".", 2)
|
269
269
|
|
270
270
|
return extension
|
271
271
|
end
|
data/lib/utopia/redirection.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2009-
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "middleware"
|
7
7
|
|
8
8
|
module Utopia
|
9
9
|
# A middleware which assists with redirecting from one path to another.
|
@@ -114,7 +114,7 @@ module Utopia
|
|
114
114
|
|
115
115
|
# Redirect urls that end with a `/`, e.g. directories.
|
116
116
|
class DirectoryIndex < ClientRedirect
|
117
|
-
def initialize(app, index:
|
117
|
+
def initialize(app, index: "index")
|
118
118
|
@app = app
|
119
119
|
@index = index
|
120
120
|
|
@@ -122,7 +122,7 @@ module Utopia
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def [] path
|
125
|
-
if path.end_with?(
|
125
|
+
if path.end_with?("/")
|
126
126
|
return redirect(path + @index)
|
127
127
|
end
|
128
128
|
end
|
data/lib/utopia/responder.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "middleware"
|
7
7
|
|
8
8
|
module Utopia
|
9
9
|
class Responder
|