utopia 2.25.0 → 2.26.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/utopia/environment.rb +16 -12
  4. data/bake/utopia/node.rb +5 -5
  5. data/bake/utopia/server.rb +8 -8
  6. data/bake/utopia/shell.rb +3 -3
  7. data/bake/utopia/site.rb +16 -16
  8. data/bake/utopia/static.rb +8 -8
  9. data/bake/utopia.rb +4 -4
  10. data/lib/utopia/content/document.rb +4 -4
  11. data/lib/utopia/content/link.rb +10 -10
  12. data/lib/utopia/content/links.rb +5 -5
  13. data/lib/utopia/content/markup.rb +5 -5
  14. data/lib/utopia/content/node.rb +8 -8
  15. data/lib/utopia/content/response.rb +5 -5
  16. data/lib/utopia/content/tags.rb +8 -8
  17. data/lib/utopia/content.rb +18 -18
  18. data/lib/utopia/controller/actions.rb +5 -5
  19. data/lib/utopia/controller/base.rb +2 -2
  20. data/lib/utopia/controller/respond.rb +6 -6
  21. data/lib/utopia/controller/rewrite.rb +3 -3
  22. data/lib/utopia/controller.rb +10 -10
  23. data/lib/utopia/exceptions/handler.rb +3 -3
  24. data/lib/utopia/exceptions/mailer.rb +31 -9
  25. data/lib/utopia/exceptions.rb +3 -3
  26. data/lib/utopia/extensions/date_comparisons.rb +2 -2
  27. data/lib/utopia/http.rb +36 -36
  28. data/lib/utopia/locale.rb +3 -3
  29. data/lib/utopia/localization.rb +7 -7
  30. data/lib/utopia/middleware.rb +5 -5
  31. data/lib/utopia/path/matcher.rb +2 -2
  32. data/lib/utopia/path.rb +21 -21
  33. data/lib/utopia/redirection.rb +4 -4
  34. data/lib/utopia/responder.rb +2 -2
  35. data/lib/utopia/session/serialization.rb +4 -4
  36. data/lib/utopia/session.rb +8 -8
  37. data/lib/utopia/setup.rb +7 -7
  38. data/lib/utopia/shell.rb +5 -5
  39. data/lib/utopia/static/local_file.rb +8 -8
  40. data/lib/utopia/static/mime_types.rb +2 -2
  41. data/lib/utopia/static.rb +11 -11
  42. data/lib/utopia/version.rb +1 -1
  43. data/lib/utopia.rb +7 -8
  44. data/license.md +1 -1
  45. data/readme.md +1 -1
  46. data/setup/server/git/hooks/post-receive +10 -9
  47. data/setup/site/Guardfile +3 -2
  48. data/setup/site/bake.rb +3 -3
  49. data/setup/site/config/environment.rb +3 -3
  50. data/setup/site/config/sus.rb +3 -3
  51. data/setup/site/config.ru +8 -8
  52. data/setup/site/fixtures/website.rb +6 -6
  53. data/setup/site/gems.rb +13 -13
  54. data/setup/site/test/website.rb +3 -3
  55. data.tar.gz.sig +0 -0
  56. metadata +3 -9
  57. metadata.gz.sig +0 -0
  58. 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-2024, by Samuel Williams.
4
+ # Copyright, 2014-2025, by Samuel Williams.
5
5
 
6
- require_relative '../http'
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 = '**'.freeze
48
+ WILDCARD_GREEDY = "**".freeze
49
49
 
50
50
  # Matches any 1 path component.
51
- WILDCARD = '*'.freeze
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 = ['**', first.to_s]
124
+ first = ["**", first.to_s]
125
125
  end
126
126
 
127
127
  actions.define(Path.split(first) + path, **options, &block)
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2014-2022, by Samuel Williams.
4
+ # Copyright, 2014-2025, by Samuel Williams.
5
5
 
6
- require_relative '../http'
6
+ require_relative "../http"
7
7
 
8
8
  module Utopia
9
9
  class Controller
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2016-2022, by Samuel Williams.
4
+ # Copyright, 2016-2025, by Samuel Williams.
5
5
 
6
- require_relative '../http'
7
- require_relative '../responder'
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('application', 'json').freeze
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['version']
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('*', '*').freeze
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-2022, by Samuel Williams.
4
+ # Copyright, 2015-2025, by Samuel Williams.
5
5
 
6
- require_relative '../http'
7
- require_relative '../path/matcher'
6
+ require_relative "../http"
7
+ require_relative "../path/matcher"
8
8
 
9
9
  module Utopia
10
10
  class Controller
@@ -1,25 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2022, by Samuel Williams.
4
+ # Copyright, 2009-2025, by Samuel Williams.
5
5
 
6
- require_relative 'path'
6
+ require_relative "path"
7
7
 
8
- require_relative 'middleware'
9
- require_relative 'controller/variables'
10
- require_relative 'controller/base'
8
+ require_relative "middleware"
9
+ require_relative "controller/variables"
10
+ require_relative "controller/base"
11
11
 
12
- require_relative 'controller/rewrite'
13
- require_relative 'controller/respond'
14
- require_relative 'controller/actions'
12
+ require_relative "controller/rewrite"
13
+ require_relative "controller/respond"
14
+ require_relative "controller/actions"
15
15
 
16
- require 'concurrent/map'
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 = 'controller.rb'.freeze
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-2022, by Samuel Williams.
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 = '/errors/exception')
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['rack.errors'] || $stderr
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-2022, by Samuel Williams.
4
+ # Copyright, 2016-2025, by Samuel Williams.
5
5
 
6
- require 'net/smtp'
7
- require 'mail'
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['USER'] || 'utopia').freeze
21
- DEFAULT_SUBJECT = '%{exception} [PID %{pid} : %{cwd}]'.freeze
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
- env.select{|key,_| key.start_with? 'HTTP_'}.each do |key, value|
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['body.bin'] = body
159
+ mail.attachments["body.bin"] = body
138
160
  end
139
161
 
140
162
  if @dump_environment
141
- mail.attachments['environment.yaml'] = YAML.dump(env)
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['rack.input']
181
+ if io = env["rack.input"]
160
182
  io.rewind if io.respond_to?(:rewind)
161
183
  io.read
162
184
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2016-2022, by Samuel Williams.
4
+ # Copyright, 2016-2025, by Samuel Williams.
5
5
 
6
- require_relative 'exceptions/handler'
7
- require_relative 'exceptions/mailer'
6
+ require_relative "exceptions/handler"
7
+ require_relative "exceptions/mailer"
8
8
 
9
9
  module Utopia
10
10
  # Middleware for handling exceptional situations.
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2010-2022, by Samuel Williams.
4
+ # Copyright, 2010-2025, by Samuel Williams.
5
5
 
6
- require 'date'
6
+ require "date"
7
7
 
8
8
  module Utopia
9
9
  module Extensions
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-2022, by Samuel Williams.
4
+ # Copyright, 2010-2025, by Samuel Williams.
5
5
 
6
- require 'rack'
6
+ require "rack"
7
7
 
8
- require 'http/accept'
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 => '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
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 = 'content-type'.freeze
77
- LOCATION = 'location'.freeze
78
- CACHE_CONTROL = 'cache-control'.freeze
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-2022, by Samuel Williams.
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('-', 3))
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
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2022, by Samuel Williams.
4
+ # Copyright, 2009-2025, by Samuel Williams.
5
5
 
6
- require_relative 'middleware'
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 = 'HTTP_ACCEPT_LANGUAGE'.freeze
52
- LOCALIZATION_KEY = 'utopia.localization'.freeze
53
- CURRENT_LOCALE_KEY = 'utopia.localization.current_locale'.freeze
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 << ['Vary', 'Accept-Language']
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['Content-Location'] = "/#{locale}" + env[Rack::PATH_INFO]
184
+ headers["Content-Location"] = "/#{locale}" + env[Rack::PATH_INFO]
185
185
  end
186
186
 
187
187
  return response
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2022, by Samuel Williams.
4
+ # Copyright, 2009-2025, by Samuel Williams.
5
5
 
6
- require_relative 'http'
7
- require_relative 'path'
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 = 'pages'.freeze
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 = 'utopia.variables'.freeze
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.
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2015-2022, by Samuel Williams.
4
+ # Copyright, 2015-2025, by Samuel Williams.
5
5
 
6
- require_relative '../path'
6
+ require_relative "../path"
7
7
 
8
8
  module Utopia
9
9
  class Path
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-2022, by Samuel Williams.
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('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) {
61
- [$1.delete('%')].pack('H*')
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([''] + @components)
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 << '' if directory?
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('.', 2)
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('.', 2)
268
+ _, extension = @components.last.split(".", 2)
269
269
 
270
270
  return extension
271
271
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2009-2024, by Samuel Williams.
4
+ # Copyright, 2009-2025, by Samuel Williams.
5
5
 
6
- require_relative 'middleware'
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: '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
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2022, by Samuel Williams.
4
+ # Copyright, 2020-2025, by Samuel Williams.
5
5
 
6
- require_relative 'middleware'
6
+ require_relative "middleware"
7
7
 
8
8
  module Utopia
9
9
  class Responder