discourse/lib/topic_view/post_dependent_cache.rb
Mark VanLandingham ff700ad8c3
DEV: Various changes to support nested posts experiment (#37785)
This PR adds several reusable pieces to Core that the
discourse-nested-replies
plugin needs, while keeping them general-purpose enough to be useful to
other
  plugins and future Core work.

  ## Summary

  ### TopicMetadata component extraction
  - Extracts the category chooser, tag chooser, plugin outlets, and
    save/cancel controls from `topic.gjs` into a standalone
    `<TopicCategoryTagEditor>` component
  - The nested-replies plugin reuses this component in its own topic
    header, avoiding duplication of ~55 lines of template code
  - No behavioral change to the existing topic page

  ### Value transformers for URL routing
  - **`route-to-url`** — applied in `DiscourseURL.routeTo()`, allows
    plugins to intercept and rewrite navigation URLs before routing
    occurs. Returns early if the transformer nullifies the path.
- **`topic-url-for-post-number`** — applied in `Topic#urlForPostNumber`,
    allows plugins to rewrite per-post URLs (e.g. for nested views)
- **`post-share-url`** 

  ### TopicView `PostDependentCache` concern
  - Introduces `memoize_for_posts` — a declarative way to register
    instance variables that should be cleared when posts are replaced
  - Provides `reset_post_collection(posts:)` — an explicit method for
    swapping the post collection on a TopicView after initialization,
    automatically clearing all registered caches
  - Adds `skip_post_loading` initializer option so plugins that supply
    their own posts can skip the default post-loading SQL entirely
  - 4 specs covering replacement, cache clearing, preload hooks, and
    skip_post_loading

---------

Co-authored-by: Isaac Janzen <isaac.janzen@discourse.org>
2026-03-09 15:47:03 -05:00

43 lines
1.7 KiB
Ruby

# frozen_string_literal: true
# Extracted from TopicView to make post-dependent memoization self-maintaining.
#
# Any method in TopicView (or a plugin) that caches data derived from @posts
# can register itself with `memoize_for_posts`. When @posts is replaced via
# `reset_post_collection`, all registered caches are automatically cleared —
# no hardcoded ivar list required.
module TopicView::PostDependentCache
extend ActiveSupport::Concern
included do
# Stores ivar names (as symbols like :@all_post_actions) that should
# be cleared whenever @posts is replaced.
class_attribute :post_dependent_ivars, instance_writer: false, default: []
end
class_methods do
# Register a memoized ivar as post-dependent.
#
# memoize_for_posts :all_post_actions
# memoize_for_posts :primary_group_names, :@group_names
#
# The ivar name defaults to `@<method_name>` but can be overridden
# for cases where the ivar doesn't match the method name.
def memoize_for_posts(method_name, ivar_name = nil)
ivar_name ||= :"@#{method_name}"
# Use += to avoid mutating a parent class's array
self.post_dependent_ivars = post_dependent_ivars + [ivar_name]
end
end
# Replaces @posts with a new collection and clears all registered
# post-dependent caches. Use this instead of writing to @posts directly
# when you need to swap in a different set of posts (e.g. a plugin that
# builds its own post tree) after the TopicView has been initialized.
def reset_post_collection(posts:)
@posts = posts
self.class.post_dependent_ivars.each do |ivar|
remove_instance_variable(ivar) if instance_variable_defined?(ivar)
end
end
end