diff --git a/bridgetown-core/lib/bridgetown-core.rb b/bridgetown-core/lib/bridgetown-core.rb
index 6e0db5c69..9dde66dc7 100644
--- a/bridgetown-core/lib/bridgetown-core.rb
+++ b/bridgetown-core/lib/bridgetown-core.rb
@@ -379,9 +379,7 @@ def build_errors_path
)
end
end
-end
-module Bridgetown
module CoreExt; end
module Model; end
@@ -395,6 +393,13 @@ def self.register_extension(mod)
end
end
end
+
+ # mixin for identity so Roda knows to call renderable objects
+ module RodaCallable
+ def self.===(other)
+ other.class < self
+ end
+ end
end
Zeitwerk::Loader.new.tap do |loader|
diff --git a/bridgetown-core/lib/bridgetown-core/component.rb b/bridgetown-core/lib/bridgetown-core/component.rb
index 1120db878..4dc4f2eb2 100644
--- a/bridgetown-core/lib/bridgetown-core/component.rb
+++ b/bridgetown-core/lib/bridgetown-core/component.rb
@@ -201,7 +201,7 @@ def render_in(view_context, &block)
# Subclasses can override this method to return a string from their own
# template handling.
def template
- call || _renderer.render(self)
+ (method(:call).arity.zero? ? call : nil) || _renderer.render(self)
end
# Typically not used but here as a compatibility nod toward ViewComponent.
diff --git a/bridgetown-core/lib/bridgetown-core/model/base.rb b/bridgetown-core/lib/bridgetown-core/model/base.rb
index fa3a164d0..909b8c3de 100644
--- a/bridgetown-core/lib/bridgetown-core/model/base.rb
+++ b/bridgetown-core/lib/bridgetown-core/model/base.rb
@@ -5,6 +5,7 @@
module Bridgetown
module Model
class Base
+ include Bridgetown::RodaCallable
include ActiveModel::Model
extend ActiveModel::Callbacks
define_model_callbacks :load, :save, :destroy
@@ -102,6 +103,11 @@ def render_as_resource
to_resource.read!.transform!
end
+ # Converts this model to a resource and returns the full output
+ #
+ # @return [String]
+ def call(*) = render_as_resource.output
+
# override if need be
# @return [Bridgetown::Site]
def site
diff --git a/bridgetown-core/lib/bridgetown-core/resource/base.rb b/bridgetown-core/lib/bridgetown-core/resource/base.rb
index 5344aa285..26e3d75b2 100644
--- a/bridgetown-core/lib/bridgetown-core/resource/base.rb
+++ b/bridgetown-core/lib/bridgetown-core/resource/base.rb
@@ -4,6 +4,7 @@ module Bridgetown
module Resource
class Base # rubocop:todo Metrics/ClassLength
include Comparable
+ include Bridgetown::RodaCallable
include Bridgetown::Publishable
include Bridgetown::LayoutPlaceable
include Bridgetown::LiquidRenderable
@@ -171,6 +172,11 @@ def transform! # rubocop:todo Metrics/CyclomaticComplexity
self
end
+ # Transforms the resource and returns the full output
+ #
+ # @return [String]
+ def call(*) = transform!.output
+
def trigger_hooks(hook_name, *args)
Bridgetown::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
Bridgetown::Hooks.trigger :resources, hook_name, self, *args
diff --git a/bridgetown-core/lib/roda/plugins/bridgetown_server.rb b/bridgetown-core/lib/roda/plugins/bridgetown_server.rb
index 900f89a60..986f0ea54 100644
--- a/bridgetown-core/lib/roda/plugins/bridgetown_server.rb
+++ b/bridgetown-core/lib/roda/plugins/bridgetown_server.rb
@@ -41,17 +41,6 @@ def self.load_dependencies(app) # rubocop:disable Metrics
"500 Internal Server Error"
end
- # This lets us return models or resources directly in Roda response blocks
- app.plugin :custom_block_results
-
- app.handle_block_result Bridgetown::Model::Base do |result|
- result.render_as_resource.output
- end
-
- app.handle_block_result Bridgetown::Resource::Base do |result|
- result.transform!.output
- end
-
# TODO: there may be a better way to do this, see `exception_page_css` instance method
ExceptionPage.class_eval do # rubocop:disable Metrics/BlockLength
def self.css
diff --git a/bridgetown-core/lib/roda/plugins/bridgetown_ssr.rb b/bridgetown-core/lib/roda/plugins/bridgetown_ssr.rb
index 3bf5a5779..97141c48a 100644
--- a/bridgetown-core/lib/roda/plugins/bridgetown_ssr.rb
+++ b/bridgetown-core/lib/roda/plugins/bridgetown_ssr.rb
@@ -9,6 +9,17 @@ module InstanceMethods
def bridgetown_site
self.class.opts[:bridgetown_site]
end
+
+ alias_method :site, :bridgetown_site
+ end
+
+ def self.load_dependencies(app)
+ app.plugin :custom_block_results
+
+ # This lets us return callable objects directly in Roda response blocks
+ app.handle_block_result(Bridgetown::RodaCallable) do |callable|
+ callable.(self)
+ end
end
def self.configure(app, _opts = {}, &)
diff --git a/bridgetown-core/test/ssr/config.ru b/bridgetown-core/test/ssr/config.ru
index 957991226..aaee05741 100644
--- a/bridgetown-core/test/ssr/config.ru
+++ b/bridgetown-core/test/ssr/config.ru
@@ -10,4 +10,6 @@ require "bridgetown-core/rack/boot"
Bridgetown::Rack.boot
+require_relative "src/_components/UseRoda" # normally Zeitwerk would take care of this for us
+
run RodaApp.freeze.app # see server/roda_app.rb
diff --git a/bridgetown-core/test/ssr/server/routes/hello.rb b/bridgetown-core/test/ssr/server/routes/hello.rb
index b737e655b..edebce028 100644
--- a/bridgetown-core/test/ssr/server/routes/hello.rb
+++ b/bridgetown-core/test/ssr/server/routes/hello.rb
@@ -4,7 +4,7 @@ class Routes::Hello < Bridgetown::Rack::Routes
priority :lowest
route do |r|
- saved_value = bridgetown_site.data.save_value
+ saved_value = site.data.save_value
# route: GET /hello/:name
r.get "hello", String do |name|
diff --git a/bridgetown-core/test/ssr/server/routes/render_resource.rb b/bridgetown-core/test/ssr/server/routes/render_resource.rb
index 07798b00e..4a49e27a2 100644
--- a/bridgetown-core/test/ssr/server/routes/render_resource.rb
+++ b/bridgetown-core/test/ssr/server/routes/render_resource.rb
@@ -5,12 +5,16 @@ class Routes::RenderResource < Bridgetown::Rack::Routes
# route: GET /render_resource
r.get "render_resource" do
# Roda should know how to autorender the resource
- bridgetown_site.collections.pages.resources.find { _1.id == "repo://pages.collection/index.md" }
+ site.collections.pages.find { _1.id == "repo://pages.collection/index.md" }
end
r.get "render_model" do
# Roda should know how to autorender the model as a resource
Bridgetown::Model::Base.find("repo://pages.collection/test_doc.md")
end
+
+ r.get "render_component", String do |title|
+ UseRoda.new(title:)
+ end
end
end
diff --git a/bridgetown-core/test/ssr/src/_components/UseRoda.rb b/bridgetown-core/test/ssr/src/_components/UseRoda.rb
new file mode 100644
index 000000000..14dd7fb84
--- /dev/null
+++ b/bridgetown-core/test/ssr/src/_components/UseRoda.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class UseRoda < Bridgetown::Component
+ include Bridgetown::RodaCallable
+
+ def initialize(title:) # rubocop:disable Lint/MissingSuper
+ @title = title.upcase
+ end
+
+ def template
+ "
THIS IS A TEST.
" end + + should "return rendered component" do + get "/render_component/wow" + + assert last_response.ok? + assert_equal "application/rss+xml", last_response["Content-Type"] + assert_equal "