diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 69d7c6b..931339f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,7 +18,6 @@ jobs: - macos ruby: - - "3.2" - "3.3" - "3.4" diff --git a/bake/utopia/project.rb b/bake/utopia/project.rb index 27df3bc..efa121d 100644 --- a/bake/utopia/project.rb +++ b/bake/utopia/project.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2020-2025, by Samuel Williams. +# Copyright, 2020-2026, by Samuel Williams. def initialize(context) super @@ -35,7 +35,7 @@ def update # @parameter port [Integer] The port to bind to. # @parameter bind [String] The URL to bind to, e.g. `http://localhost:80`. def serve(port: nil, bind: nil) - config_path = File.expand_path("../../template/config.ru", __dir__) + config_path = File.expand_path("../../template/config/serve.rb", __dir__) preload_path = File.expand_path("../../template/preload.rb", __dir__) options = [] @@ -55,22 +55,15 @@ def serve(port: nil, bind: nil) # @parameter output_path [String] The output path for the static site. # @parameter force [Boolean] Remove the output directory before generating the static content. def static(output_path: "docs", force: true) - require "rackula/command" - - config_path = File.expand_path("../../template/config.ru", __dir__) + application_path = File.expand_path("../../template/config/application.rb", __dir__) public_path = File.expand_path("../../public", __dir__) - arguments = [] - - if force - arguments << "--force" - end - - Rackula::Command::Top["generate", *arguments, - "--config", config_path, - "--public", public_path, - "--output-path", output_path - ].call + context["utopia:static:generate"].call( + output_path: output_path, + application_path: application_path, + public_path: public_path, + force: force, + ) FileUtils.touch File.expand_path(".nojekyll", output_path) end @@ -94,4 +87,3 @@ def description(root: context.root) end end end - diff --git a/gems.rb b/gems.rb index 4049ae0..6295b9f 100644 --- a/gems.rb +++ b/gems.rb @@ -18,6 +18,7 @@ group :test do gem "sus" + gem "sus-fixtures-protocol-http", "~> 0.1" gem "covered" gem "decode" @@ -25,7 +26,5 @@ gem "rubocop-md" gem "rubocop-socketry" - gem "rack-test" - gem "bake-test" end diff --git a/lib/utopia/project.rb b/lib/utopia/project.rb index 3d4175b..e031021 100644 --- a/lib/utopia/project.rb +++ b/lib/utopia/project.rb @@ -1,13 +1,19 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2020-2025, by Samuel Williams. +# Copyright, 2020-2026, by Samuel Williams. require "utopia/project/version" require "variant" +require "utopia/application" +require "utopia/content" +require "utopia/controller" +require "utopia/exceptions" require "utopia/localization" +require "utopia/redirection" +require "utopia/static" require_relative "project/base" require_relative "project/import_map" @@ -23,9 +29,9 @@ module Project # The root directory for static assets. PUBLIC_ROOT = File.expand_path("public", SITE_ROOT) - # Appends a project application to the rack builder. + # Appends a project application to the protocol HTTP middleware builder. # - # @parameter builder [Rack::Builder] + # @parameter builder [Protocol::HTTP::Middleware::Builder] # @parameter root [String] The file-system root path of the project/gem. # @parameter locales [Array(String)] an array of locales to support, e.g. `['en', 'ja']`. def self.call(builder, root = Dir.pwd, locales: nil) @@ -33,9 +39,6 @@ def self.call(builder, root = Dir.pwd, locales: nil) # Handle exceptions in production with a error page and send an email notification: builder.use Utopia::Exceptions::Handler builder.use Utopia::Exceptions::Mailer - else - # We want to propate exceptions up when running tests: - builder.use Rack::ShowExceptions unless UTOPIA.testing? end # We serve static files from the project root: @@ -46,7 +49,6 @@ def self.call(builder, root = Dir.pwd, locales: nil) builder.use Utopia::Redirection::Rewrite, { "/" => "/index" } - builder.use Utopia::Redirection::DirectoryIndex builder.use Utopia::Redirection::Errors, { @@ -67,7 +69,6 @@ def self.call(builder, root = Dir.pwd, locales: nil) # 'gallery' => Utopia::Gallery::Tags.new } - builder.run lambda {|env| [404, {}, []]} end end end diff --git a/lib/utopia/project/guides.rb b/lib/utopia/project/guides.rb index 91ee54c..73a04b1 100644 --- a/lib/utopia/project/guides.rb +++ b/lib/utopia/project/guides.rb @@ -45,14 +45,14 @@ def to_a # @parameter name [String] The guide name. # @returns [Guide | Nil] def [](name) - to_a.find { |guide| guide.name == name } + to_a.find{|guide| guide.name == name} end # Get the related guides (previous and next) for the given guide. # @parameter guide [Guide] The current guide. # @returns [Array(Guide | Nil, Guide | Nil)] A two-element array containing the previous and next guides. def related(guide) - index = to_a.index { |g| g.name == guide.name } + index = to_a.index{|g| g.name == guide.name} return [nil, nil] unless index previous_guide = index > 0 ? to_a[index - 1] : nil @@ -63,4 +63,3 @@ def related(guide) end end end - diff --git a/pages/_header.xnode b/pages/_header.xnode index e11ff7b..7a16750 100644 --- a/pages/_header.xnode +++ b/pages/_header.xnode @@ -6,7 +6,7 @@ path = path + "index" end - if link = links(path.dirname, name: path.last, locale: localization.current_locale, indices: true).first + if link = links(path.dirname, name: path.last, locale: localization&.locale, indices: true).first if replace = link[:replace]&.to_sym and base = controller[:base] ?> › #{link.to_href(content: base.public_send(replace))}#{guide.title} • (#{index.zero? ? '' : ' '}#{locale}) \ No newline at end of file +?> diff --git a/pages/_page.xnode b/pages/_page.xnode index 44c9002..55f1d29 100644 --- a/pages/_page.xnode +++ b/pages/_page.xnode @@ -15,7 +15,7 @@ - #{Utopia::Project::IMPORT_MAP.relative_to(request.env["REQUEST_PATH"]).to_html} + #{Utopia::Project::IMPORT_MAP.relative_to(request.request_path).to_html} diff --git a/releases.md b/releases.md index 204702f..2089837 100644 --- a/releases.md +++ b/releases.md @@ -3,6 +3,7 @@ ## Unreleased - Don't render empty signature block when there are only examples. + - Use protocol HTTP middleware for serving and testing project documentation. ## v0.40.0 diff --git a/template/config.ru b/template/config.ru deleted file mode 100644 index 8f2be7f..0000000 --- a/template/config.ru +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require "utopia/setup" -UTOPIA ||= Utopia.setup(Dir.pwd) - -require "utopia/project" -Utopia::Project.call(self) diff --git a/template/config/application.rb b/template/config/application.rb new file mode 100644 index 0000000..02b0e09 --- /dev/null +++ b/template/config/application.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2026, by Samuel Williams. + +require_relative "environment" + +require "utopia/project" + +Application = Utopia::Application.build do |builder| + Utopia::Project.call(builder) +end diff --git a/template/config/environment.rb b/template/config/environment.rb new file mode 100644 index 0000000..d3dda4f --- /dev/null +++ b/template/config/environment.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2026, by Samuel Williams. + +require "utopia/setup" + +UTOPIA ||= Utopia.setup(File.expand_path("..", __dir__)) diff --git a/template/config/serve.rb b/template/config/serve.rb new file mode 100644 index 0000000..8ea867b --- /dev/null +++ b/template/config/serve.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2026, by Samuel Williams. + +require "utopia/application" + +run Utopia::Application.load(File.expand_path("application.rb", __dir__)) diff --git a/test/utopia/project/serve.rb b/test/utopia/project/serve.rb index 4c873a7..bb95797 100644 --- a/test/utopia/project/serve.rb +++ b/test/utopia/project/serve.rb @@ -1,52 +1,37 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2020-2025, by Samuel Williams. +# Copyright, 2020-2026, by Samuel Williams. require "utopia/project" -require "rack/builder" -require "rack/test" +require "protocol/http/middleware/builder" +require "sus/fixtures/protocol/http/middleware_context" describe Utopia::Project do - include Rack::Test::Methods + include Sus::Fixtures::Protocol::HTTP::MiddlewareContext let(:template_root) {File.expand_path("../../../template", __dir__)} - let(:rackup_path) {File.expand_path("config.ru", template_root)} - let(:rackup_directory) {File.dirname(rackup_path)} + let(:configuration_path) {File.expand_path("config/serve.rb", template_root)} - let(:app) do - inner_app = Rack::Builder.parse_file(rackup_path) - - # Middleware to set REQUEST_PATH from PATH_INFO - lambda do |env| - env["REQUEST_PATH"] ||= env["PATH_INFO"] - inner_app.call(env) - end + let(:middleware) do + Protocol::HTTP::Middleware.load(configuration_path) end it "has root page" do - get "/index" - - expect(last_response.body).to be(:include?, "Project") + expect(client.get("/index").read).to be(:include?, "Project") end it "has guide page" do - get "/guides/getting-started/index" - - expect(last_response.body).to be(:include?, "Getting Started") + expect(client.get("/guides/getting-started/index").read).to be(:include?, "Getting Started") end it "has source code index" do - get "/source/index" - - expect(last_response.body).to be(:include?, "module Utopia") + expect(client.get("/source/index").read).to be(:include?, "module Utopia") end it "has source code file" do - get "/source/Utopia/Project/Base/index" - - expect(last_response.body).to be(:include?, "def initialize") + expect(client.get("/source/Utopia/Project/Base/index").read).to be(:include?, "def initialize") end end diff --git a/utopia-project.gemspec b/utopia-project.gemspec index ebcdfc8..80be5d8 100644 --- a/utopia-project.gemspec +++ b/utopia-project.gemspec @@ -23,12 +23,11 @@ Gem::Specification.new do |spec| spec.files = Dir.glob(["{bake,context,lib,pages,public,template}/**/*", "*.md"], File::FNM_DOTMATCH, base: __dir__) - spec.required_ruby_version = ">= 3.2" + spec.required_ruby_version = ">= 3.3" spec.add_dependency "decode", "~> 0.26" spec.add_dependency "falcon" spec.add_dependency "markly", "~> 0.15" - spec.add_dependency "rackula", "~> 1.3" spec.add_dependency "thread-local" - spec.add_dependency "utopia", "~> 2.32" + spec.add_dependency "utopia", "~> 3.0" end