Skip to content

Instantly share code, notes, and snippets.

@bensheldon
Created August 27, 2025 21:30
Show Gist options
  • Save bensheldon/7642479bad80aa916322bfb9163e622a to your computer and use it in GitHub Desktop.
Save bensheldon/7642479bad80aa916322bfb9163e622a to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
# config/initializers/view_component.rb
#
# Instantiate a ViewComponents that is (optionally) serializable by Active Job
# but otherwise behaves like a normal ViewComponent. This allows it to be passed
# as a renderable into `broadcast_action_later_to`.
#
# To use, include the `ViewComponent::Serializable` concern:
#
# class ApplicationComponent < ViewComponent::Base
# include ViewComponent::Serializable
# end
#
# And then call `serializable` instead of `new` when instantiating:
#
# Turbo::StreamsChannel.broadcast_action_later_to(
# :admin, client, :messages,
# action: :update,
# target: ActionView::RecordIdentifier.dom_id(client, :messages),
# renderable: MessageComponent.serializable(message: message)
# )
#
module ViewComponent
module Serializable
extend ActiveSupport::Concern
included do
attr_reader :serializable_args
end
class_methods do
def serializable(*args)
new(*args).tap do |instance|
instance.instance_variable_set(:@serializable_args, args)
end
end
ruby2_keywords(:serializable)
end
end
end
class ViewComponentSerializer < ActiveJob::Serializers::ObjectSerializer
def serialize?(argument)
argument.is_a?(ViewComponent::Base) && argument.respond_to?(:serializable_args)
end
def serialize(view_component)
super(
"component" => view_component.class.name,
"arguments" => ActiveJob::Arguments.serialize(view_component.serializable_args),
)
end
def deserialize(hash)
hash["component"].safe_constantize&.new(*ActiveJob::Arguments.deserialize(hash["arguments"]))
end
ActiveJob::Serializers.add_serializers(self)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment