diff --git a/app/graphql/types/template_entity_list_type.rb b/app/graphql/types/template_entity_list_type.rb index 0d1b1a56..1d296ff9 100644 --- a/app/graphql/types/template_entity_list_type.rb +++ b/app/graphql/types/template_entity_list_type.rb @@ -74,5 +74,9 @@ class TemplateEntityListType < Types::BaseObject The minimum hierarchical depth for the entities in the list (if known). TEXT end + + load_association! :entities + + load_association! :list_item_layout_instances, as: :list_item_layouts end end diff --git a/app/graphql/types/template_has_entity_list_type.rb b/app/graphql/types/template_has_entity_list_type.rb index 966206de..52c5d868 100644 --- a/app/graphql/types/template_has_entity_list_type.rb +++ b/app/graphql/types/template_has_entity_list_type.rb @@ -17,5 +17,13 @@ module TemplateHasEntityListType The list of entities to render as part of this template's content. TEXT end + + load_association! :cached_entity_list + + def entity_list + cached_entity_list.then do |cel| + cel.presence || ::Templates::EntityList.new + end + end end end diff --git a/app/jobs/entities/reprocess_layouts_job.rb b/app/jobs/entities/reprocess_layouts_job.rb new file mode 100644 index 00000000..bd0a6611 --- /dev/null +++ b/app/jobs/entities/reprocess_layouts_job.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Entities + class ReprocessLayoutsJob < ApplicationJob + queue_as :layouts + + queue_with_priority do + case arguments.first + when ::Item then 500 + when ::Collection then 600 + when ::Community then 700 + else + # :nocov: + 999 + # :nocov: + end + end + + # @param [HierarchicalEntity] entity + # @return [void] + def perform(entity) + call_operation!("entities.reprocess_layouts", entity) + end + end +end diff --git a/app/jobs/rendering/abstract_reprocess_entities_job.rb b/app/jobs/rendering/abstract_reprocess_entities_job.rb new file mode 100644 index 00000000..cd8ffd90 --- /dev/null +++ b/app/jobs/rendering/abstract_reprocess_entities_job.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Rendering + # @abstract + # Enqueues reprocessing jobs for all entities of a given class. + # @see Entities::ReprocessLayoutsJob + class AbstractReprocessEntitiesJob < ApplicationJob + extend Dry::Core::ClassAttributes + + include JobIteration::Iteration + + defines :entity_klass, type: Support::Types::Class + + entity_klass ApplicationRecord + + good_job_control_concurrency_with( + total_limit: 1, + key: -> { "#{self.class.name}-#{queue_name}" } + ) + + queue_as :default + + # @param [String] cursor + # @return [void] + def build_enumerator(cursor:) + enumerator_builder.active_record_on_records( + self.class.entity_klass.all, + cursor: + ) + end + + # @see Entities::ReprocessLayoutsJob + # @param [HierarchicalEntity] entity + # @return [void] + def each_iteration(entity) + Entities::ReprocessLayoutsJob.perform_later(entity) + end + end +end diff --git a/app/jobs/rendering/reprocess_all_collections_job.rb b/app/jobs/rendering/reprocess_all_collections_job.rb new file mode 100644 index 00000000..56ef1a46 --- /dev/null +++ b/app/jobs/rendering/reprocess_all_collections_job.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Rendering + # Enqueues reprocessing jobs for all {Collection} entities. + class ReprocessAllCollectionsJob < AbstractReprocessEntitiesJob + entity_klass Collection + + queue_with_priority 200 + end +end diff --git a/app/jobs/rendering/reprocess_all_communities_job.rb b/app/jobs/rendering/reprocess_all_communities_job.rb new file mode 100644 index 00000000..653a2c75 --- /dev/null +++ b/app/jobs/rendering/reprocess_all_communities_job.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Rendering + # Enqueues reprocessing jobs for all {Community} entities. + class ReprocessAllCommunitiesJob < AbstractReprocessEntitiesJob + entity_klass Community + + queue_with_priority 300 + end +end diff --git a/app/jobs/rendering/reprocess_all_entities_job.rb b/app/jobs/rendering/reprocess_all_entities_job.rb new file mode 100644 index 00000000..1c793404 --- /dev/null +++ b/app/jobs/rendering/reprocess_all_entities_job.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Rendering + # Enqueues reprocessing jobs for all entities. + class ReprocessAllEntitiesJob < ApplicationJob + queue_as :default + + # @return [void] + def perform + Rendering::ReprocessAllItemsJob.perform_later + Rendering::ReprocessAllCollectionsJob.perform_later + Rendering::ReprocessAllCommunitiesJob.perform_later + end + end +end diff --git a/app/jobs/rendering/reprocess_all_items_job.rb b/app/jobs/rendering/reprocess_all_items_job.rb new file mode 100644 index 00000000..705f30b1 --- /dev/null +++ b/app/jobs/rendering/reprocess_all_items_job.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Rendering + # Enqueues reprocessing jobs for all {Item} entities. + class ReprocessAllItemsJob < AbstractReprocessEntitiesJob + entity_klass Item + + queue_with_priority 100 + end +end diff --git a/app/jobs/system/shutdown_worker_job.rb b/app/jobs/system/shutdown_worker_job.rb new file mode 100644 index 00000000..56fc0011 --- /dev/null +++ b/app/jobs/system/shutdown_worker_job.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module System + class ShutdownWorkerJob < ApplicationJob + queue_as :default + + queue_with_priority(-10_000) + + # @return [void] + def perform + GOOD_JOB_KEEP_RUNNING.make_false + end + end +end diff --git a/app/models/concerns/entity_templating_support.rb b/app/models/concerns/entity_templating_support.rb index 4cf144e8..3187ed58 100644 --- a/app/models/concerns/entity_templating_support.rb +++ b/app/models/concerns/entity_templating_support.rb @@ -6,10 +6,12 @@ module EntityTemplatingSupport extend ActiveSupport::Concern extend DefinesMonadicOperation - include Dry::Effects.Reader(:layout_invalidation_disabled, default: false) include ModelMutationSupport included do + has_many :cached_entity_lists, as: :entity, class_name: "Templates::CachedEntityList", inverse_of: :entity, dependent: :delete_all + has_many :cached_entity_list_items, as: :entity, class_name: "Templates::CachedEntityListItem", inverse_of: :entity, dependent: :delete_all + has_many :entity_derived_layout_definitions, as: :entity, inverse_of: :entity, dependent: :delete_all has_many_readonly :missing_layout_instances, as: :entity, inverse_of: :entity, class_name: "EntityMissingLayoutInstance" @@ -82,9 +84,16 @@ def has_no_layout_definitions_derived? call_operation("entities.invalidate_related_layouts", self) end + # @see Layouts::Disabled + # @see Layouts::Disabler + # @return [void] + def layout_invalidation_currently_disabled? + Layouts::Disabled.currently? + end + # @see ModelMutationSupport#in_graphql_mutation? def layout_invalidation_disabled? - in_graphql_mutation? || layout_invalidation_disabled || force_disable_layout_invalidation + in_graphql_mutation? || layout_invalidation_currently_disabled? || force_disable_layout_invalidation end # @return [String] @@ -107,25 +116,23 @@ def render_lock_key call_operation("entities.render_layouts", self) end - def stale? - has_layout_invalidations? || has_missing_layouts? || has_missing_templates? || has_no_layout_definitions_derived? + # @see Entities::ReprocessLayout + # @see Entities::LayoutReprocessor + # @param [Layouts::Types::Kind] layout_kind + # @return [Dry::Monads::Success(HierarchicalEntity)] + def reprocess_layout(layout_kind) + call_operation("entities.reprocess_layout", self, layout_kind:) end - # @return [HierarchicalEntity] - def validated_layout_source - if has_invalid_layouts? - invalidation = layout_invalidations.latest - - invalidation.process! - - reload - elsif has_missing_layouts? - render_layouts! - - reload - end + # @see Entities::ReprocessLayouts + # @see Entities::LayoutsReprocessor + # @return [Dry::Monads::Success(HierarchicalEntity)] + monadic_matcher! def reprocess_layouts + call_operation("entities.reprocess_layouts", self) + end - return self + def stale? + has_layout_invalidations? || has_missing_layouts? || has_missing_templates? || has_no_layout_definitions_derived? end module ClassMethods diff --git a/app/models/concerns/layout_instance.rb b/app/models/concerns/layout_instance.rb index 953a0c1d..7950abef 100644 --- a/app/models/concerns/layout_instance.rb +++ b/app/models/concerns/layout_instance.rb @@ -21,6 +21,14 @@ module LayoutInstance template_instance_names [].freeze + if model_name == "Layouts::ListItemInstance" + has_many :cached_entity_list_items, + class_name: "Templates::CachedEntityListItem", + foreign_key: :list_item_layout_instance_id, + inverse_of: :list_item_layout_instance, + dependent: :delete_all + end + scope :root, -> { joins(:layout_definition).merge(layout_record.definition_klass.root) } scope :with_template_instances, -> { includes(*template_instance_names) } @@ -60,6 +68,9 @@ def build_digest_attributes layout_kind:, generation:, config:, + post_processed_at:, + all_hidden:, + all_slots_empty:, } end @@ -92,6 +103,18 @@ def template_instances @template_instances ||= fetch_template_instances end + monadic_operation! def post_process(**options) + call_operation("layouts.instances.post_process", self, **options) + end + + monadic_operation! def process(**options) + call_operation("layouts.instances.process", self, **options) + end + + monadic_operation! def reprocess(**options) + call_operation("layouts.instances.reprocess", self, **options) + end + monadic_operation! def upsert_layout_instance_digest call_operation("layouts.digests.instances.upsert", self) end diff --git a/app/models/concerns/template_instance.rb b/app/models/concerns/template_instance.rb index f2a6bee6..1265a740 100644 --- a/app/models/concerns/template_instance.rb +++ b/app/models/concerns/template_instance.rb @@ -7,7 +7,6 @@ module TemplateInstance include HasLayoutKind include HasTemplateKind include Renderable - include Support::Caching::Usage included do attribute :config, Templates::Instances::Config.to_type @@ -24,22 +23,6 @@ module TemplateInstance before_validation :infer_config! end - # @!attribute [r] all_slots_empty - # @see Templates::SlotMappings::AbstractInstanceSlots#all_empty? - # @return [Boolean] - def all_slots_empty - slots.all_empty? - end - - alias all_slots_empty? all_slots_empty - - # Boolean complement of {#force_show?}. - # - # Used in {#hidden} to bypass the hide logic. - def allow_hide? - !force_show? - end - # @see Templates::Instances::BuildConfig # @see Templates::Instances::ConfigBuilder monadic_operation! def build_config @@ -52,16 +35,11 @@ def allow_hide? call_operation("templates.instances.build_digest_attributes", self) end - # @api private - # @abstract - # @return [Boolean] - def force_show - false - end - - # @see #force_show - def force_show? - force_show + # Boolean complement of {#force_show?}. + # + # Used when calculating {#hidden} to bypass the hide logic. + def calculate_allow_hide? + !force_show? end # For most templates, it is just derived from from {#hidden_by_empty_slots}. @@ -74,27 +52,37 @@ def calculate_hidden hidden_by_empty_slots? end - # @!attribute [r] hidden - # Whether or not the template should be hidden in the frontend, derived from {#calculate_hidden}. - # - # Because it can be expensive to calculate at runtime, and we need to expose - # it on siblings, it uses {Support::Caching::Cache} to safely store the value across requests. + # @api private + # @abstract # @return [Boolean] - def hidden - vog_cache cache_key, :hidden do - allow_hide? && calculate_hidden - end + def force_show + false end - alias hidden? hidden + # @see #force_show + def force_show? + force_show + end - # @!attribute [r] hidden_by_empty_slots - # @return [Boolean] - def hidden_by_empty_slots - slots.hides_template? + # @see Templates::Instances::PostProcessor + # @return [Dry::Monads::Success(TemplateInstance)] + monadic_operation! def post_process + call_operation("templates.instances.post_process", self) + end + + # @see Templates::Instances::Processor + # @return [Dry::Monads::Success(TemplateInstance)] + monadic_operation! def process + call_operation("templates.instances.process", self) end - alias hidden_by_empty_slots? hidden_by_empty_slots + # @see Templates::Instances::Reprocessor + # @param [Hash] options + # @option options [Boolean] :update_digest (false) + # @return [Dry::Monads::Success(TemplateInstance)] + monadic_operation! def reprocess(**options) + call_operation("templates.instances.reprocess", self, **options) + end # @see Templates::Digests::Instances::TemplateUpserter monadic_operation! def upsert_instance_digests diff --git a/app/models/schema_version.rb b/app/models/schema_version.rb index 16bf917a..7756b21f 100644 --- a/app/models/schema_version.rb +++ b/app/models/schema_version.rb @@ -25,6 +25,8 @@ class SchemaVersion < ApplicationRecord belongs_to :schema_definition, inverse_of: :schema_versions + has_many :cached_entity_list_items, inverse_of: :schema_version, dependent: :delete_all, class_name: "Templates::CachedEntityListItem" + has_many :communities, dependent: :restrict_with_error, inverse_of: :schema_version has_many :collections, dependent: :restrict_with_error, inverse_of: :schema_version has_many :items, dependent: :restrict_with_error, inverse_of: :schema_version diff --git a/app/models/templates/cached_entity_list.rb b/app/models/templates/cached_entity_list.rb new file mode 100644 index 00000000..028b02e5 --- /dev/null +++ b/app/models/templates/cached_entity_list.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Templates + # @see Templates::CachedEntityListItem + # @see Templates::EntityList + # @see Templates::Instances::EntityListCacher + class CachedEntityList < ApplicationRecord + include HasEphemeralSystemSlug + include TimestampScopes + + belongs_to :template_instance, polymorphic: true + belongs_to :entity, polymorphic: true + + has_many :cached_entity_list_items, -> { in_default_order }, class_name: "Templates::CachedEntityListItem", inverse_of: :cached_entity_list, dependent: :delete_all + + has_many :entities, through: :cached_entity_list_items, source: :entity + has_many :list_item_layout_instances, through: :cached_entity_list_items, source: :list_item_layout_instance + + # @return [Integer] + def prune_items! + cached_entity_list_items.beyond(count).delete_all + end + end +end diff --git a/app/models/templates/cached_entity_list_item.rb b/app/models/templates/cached_entity_list_item.rb new file mode 100644 index 00000000..24fe1cc1 --- /dev/null +++ b/app/models/templates/cached_entity_list_item.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Templates + # @see Templates::CachedEntityList + # @see Templates::EntityList + # @see Templates::Instances::EntityListCacher + class CachedEntityListItem < ApplicationRecord + include HasEphemeralSystemSlug + include TimestampScopes + + belongs_to :cached_entity_list, class_name: "Templates::CachedEntityList", inverse_of: :cached_entity_list_items + belongs_to :entity, polymorphic: true + belongs_to :list_item_layout_instance, class_name: "Layouts::ListItemInstance", inverse_of: :cached_entity_list_items + belongs_to :schema_version, inverse_of: :cached_entity_list_items + + scope :beyond, ->(count) { reorder(nil).where(arel_beyond(count)) } + + scope :in_default_order, -> { order(position: :asc) } + + class << self + # @param [Integer] count + # @return [Arel::Nodes::GreaterThan] + def arel_beyond(count) + arel_table[:position].gt(count) + end + end + end +end diff --git a/app/models/templates/instance_digest.rb b/app/models/templates/instance_digest.rb index f2118331..302d06ca 100644 --- a/app/models/templates/instance_digest.rb +++ b/app/models/templates/instance_digest.rb @@ -16,5 +16,25 @@ class InstanceDigest < ApplicationRecord attribute :config, Templates::Digests::Instances::Config.to_type attribute :slots, Templates::Digests::Instances::Slots.to_type + + class << self + # @return [Hash] + def layout_instance_stats + all_hidden = arel_all_count(arel_table[:hidden].eq(true)).as("all_hidden") + all_slots_empty = arel_all_count(arel_table[:all_slots_empty].eq(true)).as("all_slots_empty") + + connection.select_one(reselect(all_hidden, all_slots_empty).to_sql).symbolize_keys + end + + # @param [Arel::Nodes::Node] condition + # @return [Arel::Nodes::Grouping] + def arel_all_count(condition) + count = Arel::Nodes::Count.new([Arel.star]) + + hidden_count = count.filter(condition) + + arel_grouping(hidden_count.eq(count)) + end + end end end diff --git a/app/models/templates/instance_sibling.rb b/app/models/templates/instance_sibling.rb index 26f6fdff..7213efc5 100644 --- a/app/models/templates/instance_sibling.rb +++ b/app/models/templates/instance_sibling.rb @@ -2,7 +2,6 @@ module Templates class InstanceSibling < ApplicationRecord - include Support::Caching::Usage include View self.primary_key = %i[template_instance_id sibling_instance_id] @@ -24,14 +23,7 @@ class InstanceSibling < ApplicationRecord scope :for_prev, -> { prev_sibling.with_sibling.in_prev_order } scope :for_next, -> { next_sibling.with_sibling.in_next_order } - # @!attribute [r] hidden - # @return [Boolean] - def hidden - vog_cache sibling_instance_id, :hidden do - sibling_instance.hidden? - end - end - - alias hidden? hidden + scope :hidden, -> { where(hidden: true) } + scope :visible, -> { where(hidden: false) } end end diff --git a/app/operations/entities/reprocess_layout.rb b/app/operations/entities/reprocess_layout.rb new file mode 100644 index 00000000..5d13c37c --- /dev/null +++ b/app/operations/entities/reprocess_layout.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Entities + # @see Entities::LayoutReprocessor + class ReprocessLayout < Support::SimpleServiceOperation + service_klass Entities::LayoutReprocessor + end +end diff --git a/app/operations/entities/reprocess_layouts.rb b/app/operations/entities/reprocess_layouts.rb new file mode 100644 index 00000000..c9002858 --- /dev/null +++ b/app/operations/entities/reprocess_layouts.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Entities + # @see Entities::LayoutsReprocessor + class ReprocessLayouts < Support::SimpleServiceOperation + service_klass Entities::LayoutsReprocessor + end +end diff --git a/app/operations/layouts/instances/post_process.rb b/app/operations/layouts/instances/post_process.rb new file mode 100644 index 00000000..d1c7ac68 --- /dev/null +++ b/app/operations/layouts/instances/post_process.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::PostProcessor + class PostProcess < Support::SimpleServiceOperation + service_klass Layouts::Instances::PostProcessor + end + end +end diff --git a/app/operations/layouts/instances/process.rb b/app/operations/layouts/instances/process.rb new file mode 100644 index 00000000..6b1fce1a --- /dev/null +++ b/app/operations/layouts/instances/process.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::Processor + class Process < Support::SimpleServiceOperation + service_klass Layouts::Instances::Processor + end + end +end diff --git a/app/operations/layouts/instances/reprocess.rb b/app/operations/layouts/instances/reprocess.rb new file mode 100644 index 00000000..8b3ec3eb --- /dev/null +++ b/app/operations/layouts/instances/reprocess.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::Reprocessor + class Reprocess < Support::SimpleServiceOperation + service_klass Layouts::Instances::Reprocessor + end + end +end diff --git a/app/operations/templates/instances/cache_entity_list.rb b/app/operations/templates/instances/cache_entity_list.rb new file mode 100644 index 00000000..04d4948e --- /dev/null +++ b/app/operations/templates/instances/cache_entity_list.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::EntityListCacher + class CacheEntityList < Support::SimpleServiceOperation + service_klass Templates::Instances::EntityListCacher + end + end +end diff --git a/app/operations/templates/instances/post_process.rb b/app/operations/templates/instances/post_process.rb new file mode 100644 index 00000000..a8bf220f --- /dev/null +++ b/app/operations/templates/instances/post_process.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::PostProcessor + class PostProcess < Support::SimpleServiceOperation + service_klass Templates::Instances::PostProcessor + end + end +end diff --git a/app/operations/templates/instances/process.rb b/app/operations/templates/instances/process.rb new file mode 100644 index 00000000..a4207f76 --- /dev/null +++ b/app/operations/templates/instances/process.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::Processor + class Process < Support::SimpleServiceOperation + service_klass Templates::Instances::Processor + end + end +end diff --git a/app/operations/templates/instances/reprocess.rb b/app/operations/templates/instances/reprocess.rb new file mode 100644 index 00000000..698d3e34 --- /dev/null +++ b/app/operations/templates/instances/reprocess.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::Reprocessor + class Reprocess < Support::SimpleServiceOperation + service_klass Templates::Instances::Reprocessor + end + end +end diff --git a/app/services/entities/layout_renderer.rb b/app/services/entities/layout_renderer.rb index 1789b83a..353ca4ef 100644 --- a/app/services/entities/layout_renderer.rb +++ b/app/services/entities/layout_renderer.rb @@ -16,6 +16,7 @@ class LayoutRenderer < Support::HookBased::Actor # @return [LayoutDefinition] attr_reader :layout_definition + # @return [Dry::Monads::Success(HierarchicalEntity)] def call run_callbacks :execute do yield prepare! diff --git a/app/services/entities/layout_reprocessor.rb b/app/services/entities/layout_reprocessor.rb new file mode 100644 index 00000000..8cdc39ab --- /dev/null +++ b/app/services/entities/layout_reprocessor.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Entities + # @see Entities::ReprocessLayout + class LayoutReprocessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :entity, Layouts::Types::Entity + + option :layout_kind, Layouts::Types::Kind + end + + standard_execution! + + # @return [LayoutInstance, nil] + attr_reader :layout_instance + + # @return [Dry::Monads::Success(HierarchicalEntity)] + def call + run_callbacks :execute do + yield prepare! + + yield reprocess! + end + + Success entity + end + + wrapped_hook! def prepare + @layout_instance = entity.__send__(:"#{layout_kind}_layout_instance") + + super + end + + wrapped_hook! def reprocess + # :nocov: + return super unless layout_instance + # :nocov: + + yield layout_instance.reprocess + + super + end + end +end diff --git a/app/services/entities/layouts_renderer.rb b/app/services/entities/layouts_renderer.rb index cdd7f409..d5aae53c 100644 --- a/app/services/entities/layouts_renderer.rb +++ b/app/services/entities/layouts_renderer.rb @@ -13,6 +13,7 @@ class LayoutsRenderer < Support::HookBased::Actor around_execute :acquire_render_lock! + # @return [Dry::Monads::Success(HierarchicalEntity)] def call run_callbacks :execute do yield derive_layout_definitions! diff --git a/app/services/entities/layouts_reprocessor.rb b/app/services/entities/layouts_reprocessor.rb new file mode 100644 index 00000000..fed94b40 --- /dev/null +++ b/app/services/entities/layouts_reprocessor.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Entities + # @see Entities::ReprocessLayouts + class LayoutsReprocessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :entity, Entities::Types::Entity + end + + standard_execution! + + # @return [Dry::Monads::Success(HierarchicalEntity)] + def call + run_callbacks :execute do + yield reprocess_each_layout! + end + + entity.asynchronously_revalidate_frontend_cache! + + Success entity + end + + wrapped_hook! def reprocess_each_layout + Layout.each do |layout| + yield entity.reprocess_layout(layout.kind) + end + + super + end + end +end diff --git a/app/services/journal_sources/parsers/abstract.rb b/app/services/journal_sources/parsers/abstract.rb index 503a2a78..f3371fe8 100644 --- a/app/services/journal_sources/parsers/abstract.rb +++ b/app/services/journal_sources/parsers/abstract.rb @@ -4,8 +4,6 @@ module JournalSources module Parsers # @abstract class Abstract - include Dry::Effects::Handler.Interrupt(:found_known, as: :catch_known) - include Dry::Effects.Interrupt(:found_known) include Dry::Monads[:maybe] include MeruAPI::Deps[ extract: "journal_sources.extract" @@ -24,15 +22,13 @@ class Abstract def call(*inputs) inputs.flatten! - caught, parsed = catch_known do + parsed = catch :found do try_parsing_inputs!(inputs) - end - if caught - parsed.to_monad - else None() end + + parsed.to_monad end # @param [] inputs @@ -65,7 +61,7 @@ def build_parsed(**attrs) def check_parsed!(**attrs) parsed = build_parsed(**attrs) - found_known(parsed) if parsed.known? + throw :found, parsed if parsed.known? rescue Dry::Struct::Error # :nocov: # intentionally left blank diff --git a/app/services/layouts/disabled.rb b/app/services/layouts/disabled.rb new file mode 100644 index 00000000..7592cae8 --- /dev/null +++ b/app/services/layouts/disabled.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Layouts + class Disabled < ActiveSupport::CurrentAttributes + attribute :currently, default: proc { false } + + alias currently? currently + end +end diff --git a/app/services/layouts/disabler.rb b/app/services/layouts/disabler.rb index d98348e6..bc12d191 100644 --- a/app/services/layouts/disabler.rb +++ b/app/services/layouts/disabler.rb @@ -1,15 +1,11 @@ # frozen_string_literal: true module Layouts + # @see Layouts::Disabled class Disabler - include Dry::Effects::Handler.Reader(:layout_invalidation_disabled) - include Dry::Effects.Reader(:layout_invalidation_disabled, default: false) - # @return [void] def disable! - return yield if layout_invalidation_disabled - - with_layout_invalidation_disabled(true) do + Layouts::Disabled.set(currently: true) do yield end end diff --git a/app/services/layouts/instances/post_processor.rb b/app/services/layouts/instances/post_processor.rb new file mode 100644 index 00000000..8e2c8867 --- /dev/null +++ b/app/services/layouts/instances/post_processor.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::PostProcess + class PostProcessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :layout_instance, ::Layouts::Types::LayoutInstance + end + + delegate :template_instance_digests, to: :layout_instance + + standard_execution! + + # @return [Dry::Monads::Success(LayoutInstance)] + def call + run_callbacks :execute do + yield derive_layout_stats! + end + + Success layout_instance + end + + wrapped_hook! def derive_layout_stats + stats = template_instance_digests.layout_instance_stats + + stats => { all_hidden:, all_slots_empty: } + + changes = { + post_processed_at: Time.current, + all_hidden:, + all_slots_empty:, + }.with_indifferent_access + + layout_instance.update_columns(changes) + + super + end + end + end +end diff --git a/app/services/layouts/instances/processor.rb b/app/services/layouts/instances/processor.rb new file mode 100644 index 00000000..936c5115 --- /dev/null +++ b/app/services/layouts/instances/processor.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::Process + class Processor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :layout_instance, ::Layouts::Types::LayoutInstance + end + + standard_execution! + + # @return [Dry::Monads::Success(LayoutInstance)] + def call + run_callbacks :execute do + yield upsert_template_digests! + + yield post_process! + + yield upsert_layout_digest! + end + + Success layout_instance + end + + wrapped_hook! def upsert_template_digests + yield layout_instance.upsert_template_instance_digests + + super + end + + wrapped_hook! def post_process + yield layout_instance.post_process + + super + end + + wrapped_hook! def upsert_layout_digest + yield layout_instance.upsert_layout_instance_digest + + super + end + end + end +end diff --git a/app/services/layouts/instances/reprocessor.rb b/app/services/layouts/instances/reprocessor.rb new file mode 100644 index 00000000..c0f1d9ca --- /dev/null +++ b/app/services/layouts/instances/reprocessor.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Layouts + module Instances + # @see Layouts::Instances::Reprocess + class Reprocessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :layout_instance, ::Layouts::Types::LayoutInstance + end + + standard_execution! + + # @return [Dry::Monads::Success(LayoutInstance)] + def call + run_callbacks :execute do + yield process! + end + + Success layout_instance + end + + wrapped_hook! def reprocess_templates + layout_instance.each_template_instance_association do |association| + association.find_each do |template_instance| + yield template_instance.reprocess(update_digest: false) + end + end + + super + end + + wrapped_hook! def process + yield layout_instance.process + + super + end + end + end +end diff --git a/app/services/layouts/renderer.rb b/app/services/layouts/renderer.rb index 91d8dbbf..b8864bd2 100644 --- a/app/services/layouts/renderer.rb +++ b/app/services/layouts/renderer.rb @@ -31,7 +31,7 @@ def call yield prune_removed_templates! - yield upsert_digests! + yield process! end Success entity @@ -74,10 +74,8 @@ def call super end - wrapped_hook! def upsert_digests - yield layout_instance.upsert_layout_instance_digest - - yield layout_instance.upsert_template_instance_digests + wrapped_hook! def process + yield layout_instance.process super end diff --git a/app/services/templates/entity_list.rb b/app/services/templates/entity_list.rb index a4a6cc40..9f8c9f9e 100644 --- a/app/services/templates/entity_list.rb +++ b/app/services/templates/entity_list.rb @@ -43,6 +43,8 @@ class EntityList # @return [] attr_reader :list_item_layouts + alias list_item_layout_instances list_item_layouts + # @return [Integer, nil] attr_reader :maximum_depth @@ -77,6 +79,17 @@ def initialize(...) @flat_depth = minimum_depth.present? && maximum_depth.present? && minimum_depth == maximum_depth end + def to_tuple + { + count:, + empty:, + fallback:, + flat_depth:, + maximum_depth:, + minimum_depth:, + } + end + private # @return [Array] @@ -89,6 +102,7 @@ def build_associations [ { + schema_version: [], list_item_layout_instance: [ :entity, :layout_definition, diff --git a/app/services/templates/instances/digest_attributes_builder.rb b/app/services/templates/instances/digest_attributes_builder.rb index 8ab470e4..be3f8b1b 100644 --- a/app/services/templates/instances/digest_attributes_builder.rb +++ b/app/services/templates/instances/digest_attributes_builder.rb @@ -21,6 +21,10 @@ class DigestAttributesBuilder < Support::HookBased::Actor :template_definition, :template_kind, :template_record, + :post_processed_at, + :all_slots_empty, + :allow_hide, + :hidden, to: :template_instance delegate :config, :slots, to: :template_instance, prefix: :template @@ -101,6 +105,10 @@ def build_initial_attributes width:, render_duration:, last_rendered_at:, + post_processed_at:, + all_slots_empty:, + allow_hide:, + hidden:, } end diff --git a/app/services/templates/instances/entity_list_cacher.rb b/app/services/templates/instances/entity_list_cacher.rb new file mode 100644 index 00000000..4924ae95 --- /dev/null +++ b/app/services/templates/instances/entity_list_cacher.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::CachedEntityList + # @see Templates::CachedEntityListItem + # @see Templates::EntityList + # @see Templates::Instances::CacheEntityList + class EntityListCacher < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :template_instance, Templates::Types::TemplateInstance + end + + standard_execution! + + delegate :id, to: :template_instance, prefix: true + + delegate :entity_id, :entity_type, :list_item_template?, to: :template_instance + + # @return [String] + attr_reader :advisory_lock_key + + # @return [Templates::CachedEntityList] + attr_reader :cached_entity_list + + # @return [Templates::EntityList] + attr_reader :entity_list + + # @return [Boolean] + attr_reader :hidden_by_entity_list + + # @return [String] + attr_reader :template_instance_type + + # @return [Dry::Monads::Result] + def call + run_callbacks :execute do + yield prepare! + + yield cache_entity_list! + + yield update_template! + end + + Success() + end + + wrapped_hook! def prepare + @advisory_lock_key = "templates/instances/entity_list_caching/#{template_instance.id}" + + @entity_list = template_instance.entity_list + + @hidden_by_entity_list = !list_item_template? && entity_list.empty? + + @template_instance_type = template_instance.class.name + + super + end + + wrapped_hook! def cache_entity_list + yield build_list! + + yield store_items! + + yield prune_items! + + super + end + + wrapped_hook! def update_template + template_instance.update_columns(hidden_by_entity_list:) + + super + end + + wrapped_hook! def build_list + tuple = { + template_instance_type:, + template_instance_id:, + entity_type:, + entity_id:, + **entity_list.to_tuple, + } + + result = Templates::CachedEntityList.upsert(tuple, unique_by: %i[template_instance_type template_instance_id], returning: :id) + + id = result.pick("id") + + @cached_entity_list = Templates::CachedEntityList.find(id) + + super + end + + wrapped_hook! def store_items + # :nocov: + return super if entity_list.empty? + # :nocov: + + base_tuple = { + cached_entity_list_id: cached_entity_list.id, + } + + tuples = entity_list.valid_entities.map.with_index do |entity, index| + position = index + 1 + + base_tuple.merge( + list_item_layout_instance_id: entity.list_item_layout_instance.id, + schema_version_id: entity.schema_version.id, + entity_type: entity.class.name, + entity_id: entity.id, + position:, + ) + end + + Templates::CachedEntityListItem.upsert_all(tuples, unique_by: %i[cached_entity_list_id position], returning: nil) + + Success() + end + + wrapped_hook! def prune_items + cached_entity_list.prune_items! + + super + end + end + end +end diff --git a/app/services/templates/instances/has_entity_list.rb b/app/services/templates/instances/has_entity_list.rb index ecaeec1e..cfeb3ba1 100644 --- a/app/services/templates/instances/has_entity_list.rb +++ b/app/services/templates/instances/has_entity_list.rb @@ -16,9 +16,15 @@ module HasEntityList include Templates::Instances::HasSelectionSource included do + has_one :cached_entity_list, as: :template_instance, class_name: "Templates::CachedEntityList", inverse_of: :template_instance, dependent: :delete + after_save :clear_entity_list! end + monadic_operation! def cache_entity_list + call_operation("templates.instances.cache_entity_list", self) + end + # @return [Templates::EntityList] def entity_list @entity_list ||= fetch_entity_list! @@ -32,14 +38,6 @@ def calculate_hidden super || hidden_by_entity_list? end - # @!attribute [r] hidden_by_entity_list - # @return [Boolean] - def hidden_by_entity_list - !list_item_template? && entity_list.empty? - end - - alias hidden_by_entity_list? hidden_by_entity_list - private # @return [void] diff --git a/app/services/templates/instances/post_processor.rb b/app/services/templates/instances/post_processor.rb new file mode 100644 index 00000000..6aa08e33 --- /dev/null +++ b/app/services/templates/instances/post_processor.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::PostProcess + class PostProcessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :template_instance, Templates::Types::TemplateInstance + end + + delegate :calculate_allow_hide?, :slots, to: :template_instance + + standard_execution! + + CHANGES = %w[ + all_slots_empty + allow_hide + hidden + hidden_by_empty_slots + ].freeze + + # @return [Boolean] + attr_reader :all_slots_empty + + # @return [Boolean] + attr_reader :allow_hide + + # @return [Boolean] + attr_reader :hidden_by_empty_slots + + # @return [Dry::Monads::Success(TemplateInstance)] + def call + run_callbacks :execute do + yield maybe_cache_entity_list! + + yield prepare! + + yield calculate_hidden! + end + + Success template_instance + end + + wrapped_hook! def maybe_cache_entity_list + return super unless template_instance.kind_of?(Templates::Instances::HasEntityList) + + yield template_instance.cache_entity_list + + super + end + + wrapped_hook! def prepare + @all_slots_empty = slots.all_empty? + @allow_hide = calculate_allow_hide? + @hidden_by_empty_slots = slots.hides_template? + + super + end + + wrapped_hook! def calculate_hidden + changes = { + all_slots_empty:, + allow_hide:, + hidden_by_empty_slots:, + }.with_indifferent_access + + template_instance.assign_attributes(changes) + + changes[:hidden] = allow_hide && template_instance.calculate_hidden + + changes[:post_processed_at] = Time.current + + template_instance.update_columns(changes) + + super + end + end + end +end diff --git a/app/services/templates/instances/processor.rb b/app/services/templates/instances/processor.rb new file mode 100644 index 00000000..ececea2e --- /dev/null +++ b/app/services/templates/instances/processor.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::Process + class Processor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + end + + standard_execution! + + # @return [Dry::Monads::Result] + def call + run_callbacks :execute do + yield prepare! + end + + Success() + end + + wrapped_hook! def prepare + super + end + end + end +end diff --git a/app/services/templates/instances/reprocessor.rb b/app/services/templates/instances/reprocessor.rb new file mode 100644 index 00000000..d5fd2645 --- /dev/null +++ b/app/services/templates/instances/reprocessor.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Templates + module Instances + # @see Templates::Instances::Reprocess + class Reprocessor < Support::HookBased::Actor + include Dry::Initializer[undefined: false].define -> do + param :template_instance, Templates::Types::TemplateInstance + + option :update_digest, Templates::Types::Bool, default: proc { false } + end + + standard_execution! + + # @return [Dry::Monads::Success(TemplateInstance)] + def call + run_callbacks :execute do + yield process! + + yield maybe_update_digest! + end + + Success template_instance + end + + wrapped_hook! def process + yield template_instance.process + + super + end + + wrapped_hook! def maybe_update_digest + # :nocov: + return super unless update_digest + + yield template_instance.upsert_instance_digests + + super + # :nocov: + end + end + end +end diff --git a/config/initializers/900_good_job.rb b/config/initializers/900_good_job.rb index f5871e6b..1967a0f8 100644 --- a/config/initializers/900_good_job.rb +++ b/config/initializers/900_good_job.rb @@ -9,7 +9,7 @@ "rendering:1", "+purging,hierarchies,entities,orderings,invalidations,layouts:2", "+harvest_pruning,extraction,harvesting,asset_fetching:2", - "default,mailers,ahoy:2", + "default,mailers,ahoy,processing,cache_warming:2", ].join(?;) config.good_job.preserve_job_records = :on_unhandled_error @@ -28,6 +28,12 @@ config.good_job.queue_select_limit = 1000 config.good_job.dashboard_live_poll_enabled = false config.good_job.inline_execution_respects_schedule = true + + config.cleanup_discarded_jobs = false + config.cleanup_preserved_jobs_before_seconds_ago = 1.hour + config.cleanup_interval_jobs = 500 + config.cleanup_interval_seconds = 300 + config.good_job.cron = { "access.enforce_assignments": { cron: "*/5 * * * *", @@ -174,3 +180,35 @@ GoodJob::Engine.middleware.use ActionDispatch::Flash GoodJob::Engine.middleware.use ActionDispatch::Cookies GoodJob::Engine.middleware.use ActionDispatch::Session::CookieStore + +GOOD_JOB_KEEP_RUNNING = Concurrent::AtomicBoolean.new(true) + +# :nocov: +if GoodJob.cli? + GOOD_JOB_STARTED_AT = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + GOOD_JOB_MAX_RUNTIME = 72_000.0 + + GOOD_JOB_MONITOR_THREAD = Thread.new do + loop do + now = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + elapsed = now - GOOD_JOB_STARTED_AT + + if elapsed >= GOOD_JOB_MAX_RUNTIME + warn "GoodJob workers have been running for over 20 hours, initiating shutdown..." + + GOOD_JOB_KEEP_RUNNING.make_false + end + + break unless GOOD_JOB_KEEP_RUNNING.value + + sleep 5 + end + + warn "Shutting down GoodJob workers..." + + GoodJob.shutdown(timeout: 25) + end +end +# :nocov: diff --git a/db/migrate/20251119033901_create_templates_cached_entity_lists.rb b/db/migrate/20251119033901_create_templates_cached_entity_lists.rb new file mode 100644 index 00000000..3bc125b0 --- /dev/null +++ b/db/migrate/20251119033901_create_templates_cached_entity_lists.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateTemplatesCachedEntityLists < ActiveRecord::Migration[7.2] + def change + create_table :templates_cached_entity_lists, id: :uuid do |t| + t.references :template_instance, polymorphic: true, null: false, type: :uuid, index: { unique: true, name: "index_cached_entity_list_uniqueness" } + t.references :entity, polymorphic: true, null: false, type: :uuid + + t.integer :count, null: false, default: 0 + t.boolean :empty, null: false, default: false + t.boolean :fallback, null: false, default: false + t.boolean :flat_depth, null: false, default: false + t.integer :maximum_depth, null: true + t.integer :minimum_depth, null: true + + t.timestamps null: false, default: -> { "CURRENT_TIMESTAMP" } + end + end +end diff --git a/db/migrate/20251119034349_create_templates_cached_entity_list_items.rb b/db/migrate/20251119034349_create_templates_cached_entity_list_items.rb new file mode 100644 index 00000000..1c49a006 --- /dev/null +++ b/db/migrate/20251119034349_create_templates_cached_entity_list_items.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateTemplatesCachedEntityListItems < ActiveRecord::Migration[7.2] + def change + create_table :templates_cached_entity_list_items, id: :uuid do |t| + t.references :cached_entity_list, null: false, foreign_key: { to_table: :templates_cached_entity_lists, on_delete: :cascade }, type: :uuid, index: false + t.references :list_item_layout_instance, null: false, foreign_key: { to_table: :layouts_list_item_instances, on_delete: :cascade }, type: :uuid + t.references :schema_version, null: false, foreign_key: { on_delete: :cascade }, type: :uuid + t.references :entity, polymorphic: true, null: false, type: :uuid + + t.bigint :position, null: false + + t.timestamps null: false, default: -> { "CURRENT_TIMESTAMP" } + + t.index %i[cached_entity_list_id position], name: "index_cached_entity_list_items_uniqueness", unique: true + end + end +end diff --git a/db/migrate/20251119040707_add_post_processing_to_template_instances.rb b/db/migrate/20251119040707_add_post_processing_to_template_instances.rb new file mode 100644 index 00000000..8900e7c8 --- /dev/null +++ b/db/migrate/20251119040707_add_post_processing_to_template_instances.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +class AddPostProcessingToTemplateInstances < ActiveRecord::Migration[7.2] + TEMPLATE_TABLES = %i[ + templates_blurb_instances + templates_contributor_list_instances + templates_descendant_list_instances + templates_detail_instances + templates_hero_instances + templates_link_list_instances + templates_list_item_instances + templates_metadata_instances + templates_navigation_instances + templates_ordering_instances + templates_page_list_instances + templates_supplementary_instances + ].freeze + + HAS_ENTITY_LIST_TABLES = %i[ + templates_descendant_list_instances + templates_link_list_instances + templates_list_item_instances + ].freeze + + def change + TEMPLATE_TABLES.each do |table_name| + change_table table_name do |t| + t.timestamp :post_processed_at + + t.boolean :all_slots_empty, null: false, default: false + + t.boolean :allow_hide, null: false, default: true + + t.boolean :hidden, null: false, default: false + + t.boolean :hidden_by_empty_slots, null: false, default: false + + if table_name.in?(HAS_ENTITY_LIST_TABLES) + t.boolean :hidden_by_entity_list, null: false, default: false + + t.timestamp :entity_list_cached_at + end + end + end + + change_table :templates_instance_digests do |t| + t.timestamp :post_processed_at + + t.boolean :all_slots_empty, null: false, default: false + + t.boolean :allow_hide, null: false, default: true + + t.boolean :hidden, null: false, default: false + end + + reversible do |dir| + dir.up do + say_with_time "Creating initial empty entity lists for existing template instances" do + exec_update(<<~SQL) + WITH raw_lists AS ( + SELECT 'Templates::DescendantListInstance'::text AS template_instance_type, id AS template_instance_id, + entity_type, entity_id + FROM templates_descendant_list_instances + UNION ALL + SELECT 'Templates::LinkListInstance'::text AS template_instance_type, id AS template_instance_id, + entity_type, entity_id + FROM templates_link_list_instances + UNION ALL + SELECT 'Templates::ListItemInstance'::text AS template_instance_type, id AS template_instance_id, + entity_type, entity_id + FROM templates_list_item_instances + ) + INSERT INTO templates_cached_entity_lists (template_instance_type, template_instance_id, entity_type, entity_id, created_at, updated_at) + SELECT template_instance_type, template_instance_id, entity_type, entity_id, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + FROM raw_lists; + SQL + end + + TEMPLATE_TABLES.each do |table_name| + say_with_time "Setting hidden flags on existing #{table_name} records" do + listy = table_name.in?(HAS_ENTITY_LIST_TABLES) && table_name != "templates_list_item_instances" + + exec_update(<<~SQL) + UPDATE #{table_name} + SET + post_processed_at = CURRENT_TIMESTAMP, + all_slots_empty = FALSE, + allow_hide = TRUE, + hidden_by_empty_slots = FALSE, + hidden = #{listy ? "TRUE" : "FALSE"} + #{", hidden_by_entity_list = TRUE, entity_list_cached_at = CURRENT_TIMESTAMP" if listy}; + SQL + end + end + + say_with_time "Setting hidden flags on existing entity list template instance digests" do + exec_update(<<~SQL) + UPDATE templates_instance_digests + SET + post_processed_at = CURRENT_TIMESTAMP, + all_slots_empty = FALSE, + allow_hide = TRUE, + hidden = TRUE + WHERE template_instance_type IN ('Templates::DescendantListInstance', 'Templates::LinkListInstance'); + SQL + end + end + end + end +end diff --git a/db/migrate/20251119042325_update_templates_instance_siblings_to_version_2.rb b/db/migrate/20251119042325_update_templates_instance_siblings_to_version_2.rb new file mode 100644 index 00000000..35c98c31 --- /dev/null +++ b/db/migrate/20251119042325_update_templates_instance_siblings_to_version_2.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class UpdateTemplatesInstanceSiblingsToVersion2 < ActiveRecord::Migration[7.2] + def change + update_view :templates_instance_siblings, version: 2, revert_to_version: 1 + end +end diff --git a/db/migrate/20251119062322_add_post_processing_to_layout_instances.rb b/db/migrate/20251119062322_add_post_processing_to_layout_instances.rb new file mode 100644 index 00000000..f4cc1eed --- /dev/null +++ b/db/migrate/20251119062322_add_post_processing_to_layout_instances.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class AddPostProcessingToLayoutInstances < ActiveRecord::Migration[7.2] + LAYOUT_TABLES = %i[ + layouts_hero_instances + layouts_list_item_instances + layouts_main_instances + layouts_metadata_instances + layouts_navigation_instances + layouts_supplementary_instances + ].freeze + + def change + LAYOUT_TABLES.each do |table_name| + change_table table_name do |t| + t.timestamp :post_processed_at + + t.boolean :all_hidden, null: false, default: false + + t.boolean :all_slots_empty, null: false, default: false + end + end + + change_table :layouts_instance_digests do |t| + t.timestamp :post_processed_at + + t.boolean :all_hidden, null: false, default: false + + t.boolean :all_slots_empty, null: false, default: false + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 1e44f92e..ba0de7b6 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -4966,7 +4966,10 @@ CREATE TABLE public.layouts_instance_digests ( generation uuid NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL + updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -5029,7 +5032,11 @@ CREATE TABLE public.templates_instance_digests ( render_duration numeric, last_rendered_at timestamp without time zone, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL + updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL ); @@ -6096,7 +6103,10 @@ CREATE TABLE public.layouts_hero_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6133,7 +6143,10 @@ CREATE TABLE public.layouts_list_item_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6170,7 +6183,10 @@ CREATE TABLE public.layouts_main_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6207,7 +6223,10 @@ CREATE TABLE public.layouts_metadata_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6244,7 +6263,10 @@ CREATE TABLE public.layouts_navigation_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6281,7 +6303,10 @@ CREATE TABLE public.layouts_supplementary_instances ( render_duration numeric, created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + config jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_hidden boolean DEFAULT false NOT NULL, + all_slots_empty boolean DEFAULT false NOT NULL ); @@ -6962,7 +6987,50 @@ CREATE TABLE public.templates_blurb_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL +); + + +-- +-- Name: templates_cached_entity_list_items; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.templates_cached_entity_list_items ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + cached_entity_list_id uuid NOT NULL, + list_item_layout_instance_id uuid NOT NULL, + schema_version_id uuid NOT NULL, + entity_type character varying NOT NULL, + entity_id uuid NOT NULL, + "position" bigint NOT NULL, + created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL +); + + +-- +-- Name: templates_cached_entity_lists; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.templates_cached_entity_lists ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + template_instance_type character varying NOT NULL, + template_instance_id uuid NOT NULL, + entity_type character varying NOT NULL, + entity_id uuid NOT NULL, + count integer DEFAULT 0 NOT NULL, + empty boolean DEFAULT false NOT NULL, + fallback boolean DEFAULT false NOT NULL, + flat_depth boolean DEFAULT false NOT NULL, + maximum_depth integer, + minimum_depth integer, + created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL ); @@ -7006,7 +7074,12 @@ CREATE TABLE public.templates_contributor_list_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7072,7 +7145,14 @@ CREATE TABLE public.templates_descendant_list_instances ( updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, slots jsonb DEFAULT '{}'::jsonb NOT NULL, - see_all_ordering_id uuid + see_all_ordering_id uuid, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL, + hidden_by_entity_list boolean DEFAULT false NOT NULL, + entity_list_cached_at timestamp without time zone ); @@ -7118,7 +7198,12 @@ CREATE TABLE public.templates_detail_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7173,7 +7258,12 @@ CREATE TABLE public.templates_hero_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7237,7 +7327,14 @@ CREATE TABLE public.templates_link_list_instances ( updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, slots jsonb DEFAULT '{}'::jsonb NOT NULL, - see_all_ordering_id uuid + see_all_ordering_id uuid, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL, + hidden_by_entity_list boolean DEFAULT false NOT NULL, + entity_list_cached_at timestamp without time zone ); @@ -7291,7 +7388,14 @@ CREATE TABLE public.templates_list_item_instances ( updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, slots jsonb DEFAULT '{}'::jsonb NOT NULL, - see_all_ordering_id uuid + see_all_ordering_id uuid, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL, + hidden_by_entity_list boolean DEFAULT false NOT NULL, + entity_list_cached_at timestamp without time zone ); @@ -7332,7 +7436,12 @@ CREATE TABLE public.templates_metadata_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7374,7 +7483,12 @@ CREATE TABLE public.templates_navigation_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7425,7 +7539,12 @@ CREATE TABLE public.templates_ordering_instances ( config jsonb DEFAULT '{}'::jsonb NOT NULL, slots jsonb DEFAULT '{}'::jsonb NOT NULL, ordering_id uuid, - ordering_entry_id uuid + ordering_entry_id uuid, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7467,7 +7586,12 @@ CREATE TABLE public.templates_page_list_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7508,7 +7632,12 @@ CREATE TABLE public.templates_supplementary_instances ( created_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp(6) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, - slots jsonb DEFAULT '{}'::jsonb NOT NULL + slots jsonb DEFAULT '{}'::jsonb NOT NULL, + post_processed_at timestamp without time zone, + all_slots_empty boolean DEFAULT false NOT NULL, + allow_hide boolean DEFAULT true NOT NULL, + hidden boolean DEFAULT false NOT NULL, + hidden_by_empty_slots boolean DEFAULT false NOT NULL ); @@ -7860,7 +7989,8 @@ CREATE VIEW public.templates_instance_siblings AS sibling.config, sibling.layout_kind, sibling.template_kind, - sibling.width + sibling.width, + sibling.hidden FROM (public.templates_instance_digests source JOIN public.templates_instance_digests sibling USING (layout_instance_type, layout_instance_id)) WHERE (source.template_instance_id <> sibling.template_instance_id); @@ -8903,6 +9033,22 @@ ALTER TABLE ONLY public.templates_blurb_instances ADD CONSTRAINT templates_blurb_instances_pkey PRIMARY KEY (id); +-- +-- Name: templates_cached_entity_list_items templates_cached_entity_list_items_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.templates_cached_entity_list_items + ADD CONSTRAINT templates_cached_entity_list_items_pkey PRIMARY KEY (id); + + +-- +-- Name: templates_cached_entity_lists templates_cached_entity_lists_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.templates_cached_entity_lists + ADD CONSTRAINT templates_cached_entity_lists_pkey PRIMARY KEY (id); + + -- -- Name: templates_contributor_list_definitions templates_contributor_list_definitions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -9291,6 +9437,13 @@ CREATE INDEX idx_layouts_navigation_instances_defn ON public.layouts_navigation_ CREATE INDEX idx_layouts_supplementary_instances_defn ON public.layouts_supplementary_instances USING btree (layout_definition_id); +-- +-- Name: idx_on_list_item_layout_instance_id_3e916a1bef; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_on_list_item_layout_instance_id_3e916a1bef ON public.templates_cached_entity_list_items USING btree (list_item_layout_instance_id); + + -- -- Name: idx_templates_blurb_definitions_layout; Type: INDEX; Schema: public; Owner: - -- @@ -9928,6 +10081,20 @@ CREATE UNIQUE INDEX index_cache_warmers_on_warmable ON public.cache_warmers USIN CREATE INDEX index_cache_warmings_on_cache_warmer_id ON public.cache_warmings USING btree (cache_warmer_id); +-- +-- Name: index_cached_entity_list_items_uniqueness; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_cached_entity_list_items_uniqueness ON public.templates_cached_entity_list_items USING btree (cached_entity_list_id, "position"); + + +-- +-- Name: index_cached_entity_list_uniqueness; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_cached_entity_list_uniqueness ON public.templates_cached_entity_lists USING btree (template_instance_type, template_instance_id); + + -- -- Name: index_collection_attributions_ordering; Type: INDEX; Schema: public; Owner: - -- @@ -12700,6 +12867,27 @@ CREATE INDEX index_templates_blurb_instances_on_last_rendered_at ON public.templ CREATE INDEX index_templates_blurb_instances_on_position ON public.templates_blurb_instances USING btree ("position"); +-- +-- Name: index_templates_cached_entity_list_items_on_entity; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_templates_cached_entity_list_items_on_entity ON public.templates_cached_entity_list_items USING btree (entity_type, entity_id); + + +-- +-- Name: index_templates_cached_entity_list_items_on_schema_version_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_templates_cached_entity_list_items_on_schema_version_id ON public.templates_cached_entity_list_items USING btree (schema_version_id); + + +-- +-- Name: index_templates_cached_entity_lists_on_entity; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_templates_cached_entity_lists_on_entity ON public.templates_cached_entity_lists USING btree (entity_type, entity_id); + + -- -- Name: index_templates_contributor_list_definitions_on_position; Type: INDEX; Schema: public; Owner: - -- @@ -13467,6 +13655,14 @@ ALTER TABLE ONLY public.harvest_mapping_record_links ADD CONSTRAINT fk_rails_040e19d13d FOREIGN KEY (harvest_record_id) REFERENCES public.harvest_records(id) ON DELETE CASCADE; +-- +-- Name: templates_cached_entity_list_items fk_rails_0411e3b4a6; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.templates_cached_entity_list_items + ADD CONSTRAINT fk_rails_0411e3b4a6 FOREIGN KEY (list_item_layout_instance_id) REFERENCES public.layouts_list_item_instances(id) ON DELETE CASCADE; + + -- -- Name: entity_search_documents fk_rails_0421c10e03; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -14139,6 +14335,14 @@ ALTER TABLE ONLY public.entity_links ADD CONSTRAINT fk_rails_6c08f572dd FOREIGN KEY (source_item_id) REFERENCES public.items(id) ON DELETE CASCADE; +-- +-- Name: templates_cached_entity_list_items fk_rails_6d36cabf22; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.templates_cached_entity_list_items + ADD CONSTRAINT fk_rails_6d36cabf22 FOREIGN KEY (cached_entity_list_id) REFERENCES public.templates_cached_entity_lists(id) ON DELETE CASCADE; + + -- -- Name: templates_hero_instances fk_rails_6d92b15d3f; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -14795,6 +14999,14 @@ ALTER TABLE ONLY public.schematic_texts ADD CONSTRAINT fk_rails_da1d1a5a10 FOREIGN KEY (schema_version_property_id) REFERENCES public.schema_version_properties(id) ON DELETE CASCADE; +-- +-- Name: templates_cached_entity_list_items fk_rails_db1b5d8ca2; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.templates_cached_entity_list_items + ADD CONSTRAINT fk_rails_db1b5d8ca2 FOREIGN KEY (schema_version_id) REFERENCES public.schema_versions(id) ON DELETE CASCADE; + + -- -- Name: ahoy_visits fk_rails_db648022ad; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -15050,6 +15262,11 @@ ALTER TABLE ONLY public.templates_ordering_instances SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES +('20251119062322'), +('20251119042325'), +('20251119040707'), +('20251119034349'), +('20251119033901'), ('20251114195823'), ('20251009055637'), ('20251009053907'), diff --git a/db/views/templates_instance_siblings_v02.sql b/db/views/templates_instance_siblings_v02.sql new file mode 100644 index 00000000..d9ea4ecf --- /dev/null +++ b/db/views/templates_instance_siblings_v02.sql @@ -0,0 +1,19 @@ +SELECT + source.template_instance_type, + source.template_instance_id, + sibling.template_instance_type AS sibling_instance_type, + sibling.template_instance_id AS sibling_instance_id, + sibling.position AS position, + CASE WHEN sibling.position > source.position THEN 'next'::sibling_kind ELSE 'prev'::sibling_kind END AS kind, + COALESCE( + sibling.config @> '{"dark": true}', + false + ) AS dark, + sibling.config, + sibling.layout_kind, + sibling.template_kind, + sibling.width, + sibling.hidden + FROM templates_instance_digests AS source + INNER JOIN templates_instance_digests AS sibling USING (layout_instance_type, layout_instance_id) + WHERE source.template_instance_id <> sibling.template_instance_id diff --git a/lib/frozen_record/static_cached_columns.yml b/lib/frozen_record/static_cached_columns.yml index ed1c9bfb..4b78361c 100644 --- a/lib/frozen_record/static_cached_columns.yml +++ b/lib/frozen_record/static_cached_columns.yml @@ -12439,6 +12439,45 @@ default_function: has_default: true virtual: false +- id: Layouts::HeroInstance#post_processed_at + model_name: Layouts::HeroInstance + table_name: layouts_hero_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::HeroInstance#all_hidden + model_name: Layouts::HeroInstance + table_name: layouts_hero_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::HeroInstance#all_slots_empty + model_name: Layouts::HeroInstance + table_name: layouts_hero_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::InstanceDigest#id model_name: Layouts::InstanceDigest table_name: layouts_instance_digests @@ -12597,6 +12636,45 @@ default_function: CURRENT_TIMESTAMP has_default: true virtual: false +- id: Layouts::InstanceDigest#post_processed_at + model_name: Layouts::InstanceDigest + table_name: layouts_instance_digests + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::InstanceDigest#all_hidden + model_name: Layouts::InstanceDigest + table_name: layouts_instance_digests + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::InstanceDigest#all_slots_empty + model_name: Layouts::InstanceDigest + table_name: layouts_instance_digests + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::ListItemDefinition#id model_name: Layouts::ListItemDefinition table_name: layouts_list_item_definitions @@ -12874,6 +12952,45 @@ default_function: has_default: true virtual: false +- id: Layouts::ListItemInstance#post_processed_at + model_name: Layouts::ListItemInstance + table_name: layouts_list_item_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::ListItemInstance#all_hidden + model_name: Layouts::ListItemInstance + table_name: layouts_list_item_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::ListItemInstance#all_slots_empty + model_name: Layouts::ListItemInstance + table_name: layouts_list_item_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::MainDefinition#id model_name: Layouts::MainDefinition table_name: layouts_main_definitions @@ -13151,6 +13268,45 @@ default_function: has_default: true virtual: false +- id: Layouts::MainInstance#post_processed_at + model_name: Layouts::MainInstance + table_name: layouts_main_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::MainInstance#all_hidden + model_name: Layouts::MainInstance + table_name: layouts_main_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::MainInstance#all_slots_empty + model_name: Layouts::MainInstance + table_name: layouts_main_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::MetadataDefinition#id model_name: Layouts::MetadataDefinition table_name: layouts_metadata_definitions @@ -13428,6 +13584,45 @@ default_function: has_default: true virtual: false +- id: Layouts::MetadataInstance#post_processed_at + model_name: Layouts::MetadataInstance + table_name: layouts_metadata_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::MetadataInstance#all_hidden + model_name: Layouts::MetadataInstance + table_name: layouts_metadata_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::MetadataInstance#all_slots_empty + model_name: Layouts::MetadataInstance + table_name: layouts_metadata_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::NavigationDefinition#id model_name: Layouts::NavigationDefinition table_name: layouts_navigation_definitions @@ -13705,6 +13900,45 @@ default_function: has_default: true virtual: false +- id: Layouts::NavigationInstance#post_processed_at + model_name: Layouts::NavigationInstance + table_name: layouts_navigation_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::NavigationInstance#all_hidden + model_name: Layouts::NavigationInstance + table_name: layouts_navigation_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::NavigationInstance#all_slots_empty + model_name: Layouts::NavigationInstance + table_name: layouts_navigation_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Layouts::SupplementaryDefinition#id model_name: Layouts::SupplementaryDefinition table_name: layouts_supplementary_definitions @@ -13982,6 +14216,45 @@ default_function: has_default: true virtual: false +- id: Layouts::SupplementaryInstance#post_processed_at + model_name: Layouts::SupplementaryInstance + table_name: layouts_supplementary_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Layouts::SupplementaryInstance#all_hidden + model_name: Layouts::SupplementaryInstance + table_name: layouts_supplementary_instances + name: all_hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Layouts::SupplementaryInstance#all_slots_empty + model_name: Layouts::SupplementaryInstance + table_name: layouts_supplementary_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: LinkTargetCandidate#source_type model_name: LinkTargetCandidate table_name: link_target_candidates @@ -19143,26 +19416,385 @@ default_function: has_default: true virtual: false -- id: Templates::ContributorListDefinition#id - model_name: Templates::ContributorListDefinition - table_name: templates_contributor_list_definitions - name: id - type: uuid - 'null': false +- id: Templates::BlurbInstance#post_processed_at + model_name: Templates::BlurbInstance + table_name: templates_blurb_instances + name: post_processed_at + type: datetime + 'null': true sql_type_metadata: - sql_type: uuid - type: uuid + sql_type: timestamp without time zone + type: datetime default: - default_function: gen_random_uuid() - has_default: true + default_function: + has_default: false virtual: false -- id: Templates::ContributorListDefinition#layout_definition_id - model_name: Templates::ContributorListDefinition - table_name: templates_contributor_list_definitions - name: layout_definition_id - type: uuid - 'null': false - sql_type_metadata: +- id: Templates::BlurbInstance#all_slots_empty + model_name: Templates::BlurbInstance + table_name: templates_blurb_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::BlurbInstance#allow_hide + model_name: Templates::BlurbInstance + table_name: templates_blurb_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::BlurbInstance#hidden + model_name: Templates::BlurbInstance + table_name: templates_blurb_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::BlurbInstance#hidden_by_empty_slots + model_name: Templates::BlurbInstance + table_name: templates_blurb_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::CachedEntityList#id + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: gen_random_uuid() + has_default: true + virtual: false +- id: Templates::CachedEntityList#template_instance_type + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: template_instance_type + type: string + 'null': false + sql_type_metadata: + sql_type: character varying + type: string + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#template_instance_id + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: template_instance_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#entity_type + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: entity_type + type: string + 'null': false + sql_type_metadata: + sql_type: character varying + type: string + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#entity_id + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: entity_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#count + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: count + type: integer + 'null': false + sql_type_metadata: + sql_type: integer + type: integer + limit: 4 + default: '0' + default_function: + has_default: true + virtual: false +- id: Templates::CachedEntityList#empty + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::CachedEntityList#fallback + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: fallback + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::CachedEntityList#flat_depth + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: flat_depth + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::CachedEntityList#maximum_depth + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: maximum_depth + type: integer + 'null': true + sql_type_metadata: + sql_type: integer + type: integer + limit: 4 + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#minimum_depth + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: minimum_depth + type: integer + 'null': true + sql_type_metadata: + sql_type: integer + type: integer + limit: 4 + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityList#created_at + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: created_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::CachedEntityList#updated_at + model_name: Templates::CachedEntityList + table_name: templates_cached_entity_lists + name: updated_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::CachedEntityListItem#id + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: gen_random_uuid() + has_default: true + virtual: false +- id: Templates::CachedEntityListItem#cached_entity_list_id + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: cached_entity_list_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#list_item_layout_instance_id + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: list_item_layout_instance_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#schema_version_id + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: schema_version_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#entity_type + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: entity_type + type: string + 'null': false + sql_type_metadata: + sql_type: character varying + type: string + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#entity_id + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: entity_id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#position + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: position + type: integer + 'null': false + sql_type_metadata: + sql_type: bigint + type: integer + limit: 8 + default: + default_function: + has_default: false + virtual: false +- id: Templates::CachedEntityListItem#created_at + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: created_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::CachedEntityListItem#updated_at + model_name: Templates::CachedEntityListItem + table_name: templates_cached_entity_list_items + name: updated_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::ContributorListDefinition#id + model_name: Templates::ContributorListDefinition + table_name: templates_contributor_list_definitions + name: id + type: uuid + 'null': false + sql_type_metadata: + sql_type: uuid + type: uuid + default: + default_function: gen_random_uuid() + has_default: true + virtual: false +- id: Templates::ContributorListDefinition#layout_definition_id + model_name: Templates::ContributorListDefinition + table_name: templates_contributor_list_definitions + name: layout_definition_id + type: uuid + 'null': false + sql_type_metadata: sql_type: uuid type: uuid default: @@ -19495,22 +20127,87 @@ type: jsonb 'null': false sql_type_metadata: - sql_type: jsonb - type: jsonb - default: "{}" + sql_type: jsonb + type: jsonb + default: "{}" + default_function: + has_default: true + virtual: false +- id: Templates::ContributorListInstance#slots + model_name: Templates::ContributorListInstance + table_name: templates_contributor_list_instances + name: slots + type: jsonb + 'null': false + sql_type_metadata: + sql_type: jsonb + type: jsonb + default: "{}" + default_function: + has_default: true + virtual: false +- id: Templates::ContributorListInstance#post_processed_at + model_name: Templates::ContributorListInstance + table_name: templates_contributor_list_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::ContributorListInstance#all_slots_empty + model_name: Templates::ContributorListInstance + table_name: templates_contributor_list_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::ContributorListInstance#allow_hide + model_name: Templates::ContributorListInstance + table_name: templates_contributor_list_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::ContributorListInstance#hidden + model_name: Templates::ContributorListInstance + table_name: templates_contributor_list_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' default_function: has_default: true virtual: false -- id: Templates::ContributorListInstance#slots +- id: Templates::ContributorListInstance#hidden_by_empty_slots model_name: Templates::ContributorListInstance table_name: templates_contributor_list_instances - name: slots - type: jsonb + name: hidden_by_empty_slots + type: boolean 'null': false sql_type_metadata: - sql_type: jsonb - type: jsonb - default: "{}" + sql_type: boolean + type: boolean + default: 'false' default_function: has_default: true virtual: false @@ -20580,6 +21277,97 @@ default_function: has_default: false virtual: false +- id: Templates::DescendantListInstance#post_processed_at + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::DescendantListInstance#all_slots_empty + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DescendantListInstance#allow_hide + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::DescendantListInstance#hidden + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DescendantListInstance#hidden_by_empty_slots + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DescendantListInstance#hidden_by_entity_list + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: hidden_by_entity_list + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DescendantListInstance#entity_list_cached_at + model_name: Templates::DescendantListInstance + table_name: templates_descendant_list_instances + name: entity_list_cached_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false - id: Templates::DetailDefinition#id model_name: Templates::DetailDefinition table_name: templates_detail_definitions @@ -20976,6 +21764,71 @@ default_function: has_default: true virtual: false +- id: Templates::DetailInstance#post_processed_at + model_name: Templates::DetailInstance + table_name: templates_detail_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::DetailInstance#all_slots_empty + model_name: Templates::DetailInstance + table_name: templates_detail_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DetailInstance#allow_hide + model_name: Templates::DetailInstance + table_name: templates_detail_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::DetailInstance#hidden + model_name: Templates::DetailInstance + table_name: templates_detail_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::DetailInstance#hidden_by_empty_slots + model_name: Templates::DetailInstance + table_name: templates_detail_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Templates::HeroDefinition#id model_name: Templates::HeroDefinition table_name: templates_hero_definitions @@ -21489,6 +22342,71 @@ default_function: has_default: true virtual: false +- id: Templates::HeroInstance#post_processed_at + model_name: Templates::HeroInstance + table_name: templates_hero_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::HeroInstance#all_slots_empty + model_name: Templates::HeroInstance + table_name: templates_hero_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::HeroInstance#allow_hide + model_name: Templates::HeroInstance + table_name: templates_hero_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::HeroInstance#hidden + model_name: Templates::HeroInstance + table_name: templates_hero_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::HeroInstance#hidden_by_empty_slots + model_name: Templates::HeroInstance + table_name: templates_hero_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Templates::InstanceDigest#id model_name: Templates::InstanceDigest table_name: templates_instance_digests @@ -21748,34 +22666,86 @@ type: datetime default: default_function: - has_default: false + has_default: false + virtual: false +- id: Templates::InstanceDigest#created_at + model_name: Templates::InstanceDigest + table_name: templates_instance_digests + name: created_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::InstanceDigest#updated_at + model_name: Templates::InstanceDigest + table_name: templates_instance_digests + name: updated_at + type: datetime + 'null': false + sql_type_metadata: + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::InstanceDigest#post_processed_at + model_name: Templates::InstanceDigest + table_name: templates_instance_digests + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::InstanceDigest#all_slots_empty + model_name: Templates::InstanceDigest + table_name: templates_instance_digests + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true virtual: false -- id: Templates::InstanceDigest#created_at +- id: Templates::InstanceDigest#allow_hide model_name: Templates::InstanceDigest table_name: templates_instance_digests - name: created_at - type: datetime + name: allow_hide + type: boolean 'null': false sql_type_metadata: - sql_type: timestamp(6) without time zone - type: datetime - precision: 6 - default: - default_function: CURRENT_TIMESTAMP + sql_type: boolean + type: boolean + default: 'true' + default_function: has_default: true virtual: false -- id: Templates::InstanceDigest#updated_at +- id: Templates::InstanceDigest#hidden model_name: Templates::InstanceDigest table_name: templates_instance_digests - name: updated_at - type: datetime + name: hidden + type: boolean 'null': false sql_type_metadata: - sql_type: timestamp(6) without time zone - type: datetime - precision: 6 - default: - default_function: CURRENT_TIMESTAMP + sql_type: boolean + type: boolean + default: 'false' + default_function: has_default: true virtual: false - id: Templates::InstanceSibling#template_instance_type @@ -21922,6 +22892,19 @@ default_function: has_default: false virtual: false +- id: Templates::InstanceSibling#hidden + model_name: Templates::InstanceSibling + table_name: templates_instance_siblings + name: hidden + type: boolean + 'null': true + sql_type_metadata: + sql_type: boolean + type: boolean + default: + default_function: + has_default: false + virtual: false - id: Templates::LinkListDefinition#id model_name: Templates::LinkListDefinition table_name: templates_link_list_definitions @@ -22553,6 +23536,97 @@ default_function: has_default: false virtual: false +- id: Templates::LinkListInstance#post_processed_at + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::LinkListInstance#all_slots_empty + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::LinkListInstance#allow_hide + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::LinkListInstance#hidden + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::LinkListInstance#hidden_by_empty_slots + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::LinkListInstance#hidden_by_entity_list + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: hidden_by_entity_list + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::LinkListInstance#entity_list_cached_at + model_name: Templates::LinkListInstance + table_name: templates_link_list_instances + name: entity_list_cached_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false - id: Templates::ListItemDefinition#id model_name: Templates::ListItemDefinition table_name: templates_list_item_definitions @@ -23054,6 +24128,97 @@ default_function: has_default: false virtual: false +- id: Templates::ListItemInstance#post_processed_at + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::ListItemInstance#all_slots_empty + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::ListItemInstance#allow_hide + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::ListItemInstance#hidden + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::ListItemInstance#hidden_by_empty_slots + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::ListItemInstance#hidden_by_entity_list + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: hidden_by_entity_list + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::ListItemInstance#entity_list_cached_at + model_name: Templates::ListItemInstance + table_name: templates_list_item_instances + name: entity_list_cached_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false - id: Templates::ManualList#id model_name: Templates::ManualList table_name: templates_manual_lists @@ -23617,36 +24782,101 @@ type: datetime 'null': false sql_type_metadata: - sql_type: timestamp(6) without time zone - type: datetime - precision: 6 - default: - default_function: CURRENT_TIMESTAMP + sql_type: timestamp(6) without time zone + type: datetime + precision: 6 + default: + default_function: CURRENT_TIMESTAMP + has_default: true + virtual: false +- id: Templates::MetadataInstance#config + model_name: Templates::MetadataInstance + table_name: templates_metadata_instances + name: config + type: jsonb + 'null': false + sql_type_metadata: + sql_type: jsonb + type: jsonb + default: "{}" + default_function: + has_default: true + virtual: false +- id: Templates::MetadataInstance#slots + model_name: Templates::MetadataInstance + table_name: templates_metadata_instances + name: slots + type: jsonb + 'null': false + sql_type_metadata: + sql_type: jsonb + type: jsonb + default: "{}" + default_function: + has_default: true + virtual: false +- id: Templates::MetadataInstance#post_processed_at + model_name: Templates::MetadataInstance + table_name: templates_metadata_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::MetadataInstance#all_slots_empty + model_name: Templates::MetadataInstance + table_name: templates_metadata_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::MetadataInstance#allow_hide + model_name: Templates::MetadataInstance + table_name: templates_metadata_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: has_default: true virtual: false -- id: Templates::MetadataInstance#config +- id: Templates::MetadataInstance#hidden model_name: Templates::MetadataInstance table_name: templates_metadata_instances - name: config - type: jsonb + name: hidden + type: boolean 'null': false sql_type_metadata: - sql_type: jsonb - type: jsonb - default: "{}" + sql_type: boolean + type: boolean + default: 'false' default_function: has_default: true virtual: false -- id: Templates::MetadataInstance#slots +- id: Templates::MetadataInstance#hidden_by_empty_slots model_name: Templates::MetadataInstance table_name: templates_metadata_instances - name: slots - type: jsonb + name: hidden_by_empty_slots + type: boolean 'null': false sql_type_metadata: - sql_type: jsonb - type: jsonb - default: "{}" + sql_type: boolean + type: boolean + default: 'false' default_function: has_default: true virtual: false @@ -23994,6 +25224,71 @@ default_function: has_default: true virtual: false +- id: Templates::NavigationInstance#post_processed_at + model_name: Templates::NavigationInstance + table_name: templates_navigation_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::NavigationInstance#all_slots_empty + model_name: Templates::NavigationInstance + table_name: templates_navigation_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::NavigationInstance#allow_hide + model_name: Templates::NavigationInstance + table_name: templates_navigation_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::NavigationInstance#hidden + model_name: Templates::NavigationInstance + table_name: templates_navigation_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::NavigationInstance#hidden_by_empty_slots + model_name: Templates::NavigationInstance + table_name: templates_navigation_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Templates::OrderingDefinition#id model_name: Templates::OrderingDefinition table_name: templates_ordering_definitions @@ -24455,6 +25750,71 @@ default_function: has_default: false virtual: false +- id: Templates::OrderingInstance#post_processed_at + model_name: Templates::OrderingInstance + table_name: templates_ordering_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::OrderingInstance#all_slots_empty + model_name: Templates::OrderingInstance + table_name: templates_ordering_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::OrderingInstance#allow_hide + model_name: Templates::OrderingInstance + table_name: templates_ordering_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::OrderingInstance#hidden + model_name: Templates::OrderingInstance + table_name: templates_ordering_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::OrderingInstance#hidden_by_empty_slots + model_name: Templates::OrderingInstance + table_name: templates_ordering_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Templates::PageListDefinition#id model_name: Templates::PageListDefinition table_name: templates_page_list_definitions @@ -24799,6 +26159,71 @@ default_function: has_default: true virtual: false +- id: Templates::PageListInstance#post_processed_at + model_name: Templates::PageListInstance + table_name: templates_page_list_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::PageListInstance#all_slots_empty + model_name: Templates::PageListInstance + table_name: templates_page_list_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::PageListInstance#allow_hide + model_name: Templates::PageListInstance + table_name: templates_page_list_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::PageListInstance#hidden + model_name: Templates::PageListInstance + table_name: templates_page_list_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::PageListInstance#hidden_by_empty_slots + model_name: Templates::PageListInstance + table_name: templates_page_list_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: Templates::SupplementaryDefinition#id model_name: Templates::SupplementaryDefinition table_name: templates_supplementary_definitions @@ -25130,6 +26555,71 @@ default_function: has_default: true virtual: false +- id: Templates::SupplementaryInstance#post_processed_at + model_name: Templates::SupplementaryInstance + table_name: templates_supplementary_instances + name: post_processed_at + type: datetime + 'null': true + sql_type_metadata: + sql_type: timestamp without time zone + type: datetime + default: + default_function: + has_default: false + virtual: false +- id: Templates::SupplementaryInstance#all_slots_empty + model_name: Templates::SupplementaryInstance + table_name: templates_supplementary_instances + name: all_slots_empty + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::SupplementaryInstance#allow_hide + model_name: Templates::SupplementaryInstance + table_name: templates_supplementary_instances + name: allow_hide + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'true' + default_function: + has_default: true + virtual: false +- id: Templates::SupplementaryInstance#hidden + model_name: Templates::SupplementaryInstance + table_name: templates_supplementary_instances + name: hidden + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false +- id: Templates::SupplementaryInstance#hidden_by_empty_slots + model_name: Templates::SupplementaryInstance + table_name: templates_supplementary_instances + name: hidden_by_empty_slots + type: boolean + 'null': false + sql_type_metadata: + sql_type: boolean + type: boolean + default: 'false' + default_function: + has_default: true + virtual: false - id: TunerMetric#id model_name: TunerMetric table_name: tuner_metrics diff --git a/lib/support/lib/hook_based/actor.rb b/lib/support/lib/hook_based/actor.rb index 571b3094..4f280b1f 100644 --- a/lib/support/lib/hook_based/actor.rb +++ b/lib/support/lib/hook_based/actor.rb @@ -11,8 +11,6 @@ class Actor include Support::CallsCommonOperation include Dry::Core::Constants - include Dry::Effects::Handler.Interrupt(:halt_actor, as: :catch_actor_halt) - include Dry::Effects.Interrupt(:halt_actor) include Dry::Monads[:result, :maybe, :try] @@ -25,14 +23,6 @@ class Actor benchmark_hooks false - defines :generic_process_error_key, type: Types::Symbol - - generic_process_error_key :process_error - - defines :halt_error_klass, type: Types::Class - - halt_error_klass Support::HookBased::Halted - # @return [Symbol] attr_reader :current_hook @@ -43,69 +33,13 @@ def inspect # :nocov: end - # @param [Dry::Monads::Result] result - # @return [Dry::Monads::Result] - def process(result) - Dry::Matcher::ResultMatcher.(result) do |m| - m.success do - on_success - end - - m.failure :actor_halt do |_, reason| - on_halt(reason) - end - - m.failure do |*args| - on_failure(*args) - end - end - end - - # @abstract - # @api private - # @return [Dry::Monads::Result] - def on_success - Success() - end - - # @param [Symbol] reason - # @raise [Support::HookBased::Halted] - # @return [void] - def on_halt(reason) - raise self.class.halt_error_klass, reason - end - - # @abstract - # @api private - # @return [Dry::Monads::Result] - def on_failure(*args) - # :nocov: - case args - in Exception => err, *_ - raise err - in Symbol => code, *rest - Failure[code, *rest] - else - Failure[self.class.generic_process_error_key, *args] - end - # :nocov: - end - # @api private # @yieldreturn [Dry::Monads::Result] # @return [Dry::Monads::Result] def enforce_monadic - halted, response = catch_actor_halt do - retval = yield + retval = yield - TO_RESULT.call(retval) - end - - if halted - Failure[:actor_halt, response] - else - response - end + TO_RESULT.call(retval) end def benchmark_hook! @@ -128,20 +62,6 @@ def benchmark_hooks! benchmark_hooks true end - # @param [Symbol] attr - # @return [void] - def simple_reader!(attribute, **options) - mod = SimpleReader.new(attribute, **options) - - include mod - end - - def stateful_counter!(attribute, **options) - mod = StatefulCounter.new(attribute, **options) - - include mod - end - def standard_execution! do_for! :call diff --git a/lib/support/lib/hook_based/halted.rb b/lib/support/lib/hook_based/halted.rb deleted file mode 100644 index 28e4d39a..00000000 --- a/lib/support/lib/hook_based/halted.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Support - module HookBased - # An error raised when a hook-based actor uses a special - # halt failure code. - class Halted < StandardError - DEFAULT = "Something went wrong." - - attr_reader :input - - # @return [Symbol] - attr_reader :reason - - def initialize(input) - @input = input - - case input - in Symbol => reason - @reason = reason - - message = I18n.t(reason, scope: "dry_validation.errors.rules", default:) - - super(message) - in String => message - super(message) - else - super(default) - end - end - - def default - DEFAULT - end - end - end -end diff --git a/lib/support/lib/hook_based/simple_reader.rb b/lib/support/lib/hook_based/simple_reader.rb deleted file mode 100644 index 4cd7f9fe..00000000 --- a/lib/support/lib/hook_based/simple_reader.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Support - module HookBased - class SimpleReader < AbstractProvider - def build_effect! - Dry::Effects::Handler.Reader(attribute) - end - - def define_provider! - class_eval <<~RUBY, __FILE__, __LINE__ + 1 - def #{provider_name} # def provide_foo! - with_#{attribute} #{attribute} do # with_foo foo do - yield # yield - end # end - end # end - RUBY - end - end - end -end diff --git a/lib/support/lib/hook_based/stateful_counter.rb b/lib/support/lib/hook_based/stateful_counter.rb deleted file mode 100644 index 49076a00..00000000 --- a/lib/support/lib/hook_based/stateful_counter.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Support - module HookBased - class StatefulCounter < AbstractProvider - option :initial_value, Types::Integer, default: proc { 0 } - - def build_effect! - Dry::Effects::Handler.State(attribute) - end - - def define_provider! - class_eval <<~RUBY, __FILE__, __LINE__ + 1 - def #{provider_name} # def provide_foo! - @#{attribute}, _ = with_#{attribute} #{attribute} || #{initial_value} do # with_foo foo || 0 do - yield # yield - end # end - end # end - RUBY - end - end - end -end diff --git a/lib/support/lib/requests/state.rb b/lib/support/lib/requests/state.rb index 8094d90a..7c042650 100644 --- a/lib/support/lib/requests/state.rb +++ b/lib/support/lib/requests/state.rb @@ -14,7 +14,7 @@ class State around_request :provide_current_state! - around_request :provide_vog_cache! + # around_request :provide_vog_cache! around_request :measure! @@ -53,9 +53,9 @@ def provide_current_state!(&) end # @return [void] - def provide_vog_cache!(&) - Support::Caching.with_vog_cache(&) - end + # def provide_vog_cache!(&) + # Support::Caching.with_vog_cache(&) + # end end end end diff --git a/lib/support/lib/requests/timer.rb b/lib/support/lib/requests/timer.rb index 4a1f840b..fb7095ad 100644 --- a/lib/support/lib/requests/timer.rb +++ b/lib/support/lib/requests/timer.rb @@ -33,6 +33,8 @@ def measure! @request_query ||= load_request_query! + @duration = 0.0 + update_current_request! run_callbacks :measure do diff --git a/spec/jobs/entities/reprocess_layouts_job_spec.rb b/spec/jobs/entities/reprocess_layouts_job_spec.rb new file mode 100644 index 00000000..7de96b12 --- /dev/null +++ b/spec/jobs/entities/reprocess_layouts_job_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +RSpec.describe Entities::ReprocessLayoutsJob, type: :job do + let!(:community) { FactoryBot.create(:community) } + + it_behaves_like "a pass-through operation job", "entities.reprocess_layouts" do + let(:job_arg) { community } + end +end diff --git a/spec/jobs/rendering/reprocess_all_collections_job_spec.rb b/spec/jobs/rendering/reprocess_all_collections_job_spec.rb new file mode 100644 index 00000000..b58e0cef --- /dev/null +++ b/spec/jobs/rendering/reprocess_all_collections_job_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe Rendering::ReprocessAllCollectionsJob, type: :job do + let!(:collection) { FactoryBot.create(:collection) } + + it "enqueues an Entities::ReprocessLayoutsJob for each record" do + expect do + described_class.perform_now + end.to have_enqueued_job(Entities::ReprocessLayoutsJob).once.with(collection) + end +end diff --git a/spec/jobs/rendering/reprocess_all_communities_job_spec.rb b/spec/jobs/rendering/reprocess_all_communities_job_spec.rb new file mode 100644 index 00000000..77fc6760 --- /dev/null +++ b/spec/jobs/rendering/reprocess_all_communities_job_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe Rendering::ReprocessAllCommunitiesJob, type: :job do + let!(:community) { FactoryBot.create(:community) } + + it "enqueues an Entities::ReprocessLayoutsJob for each record" do + expect do + described_class.perform_now + end.to have_enqueued_job(Entities::ReprocessLayoutsJob).once.with(community) + end +end diff --git a/spec/jobs/rendering/reprocess_all_entities_job_spec.rb b/spec/jobs/rendering/reprocess_all_entities_job_spec.rb new file mode 100644 index 00000000..c04503fa --- /dev/null +++ b/spec/jobs/rendering/reprocess_all_entities_job_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.describe Rendering::ReprocessAllEntitiesJob, type: :job do + pending "add some examples to (or delete) #{__FILE__}" + + it "enqueues jobs for all entities" do + expect do + described_class.perform_now + end.to have_enqueued_job(Rendering::ReprocessAllCommunitiesJob).once + .and have_enqueued_job(Rendering::ReprocessAllCollectionsJob).once + .and have_enqueued_job(Rendering::ReprocessAllItemsJob).once + end +end diff --git a/spec/jobs/rendering/reprocess_all_items_job_spec.rb b/spec/jobs/rendering/reprocess_all_items_job_spec.rb new file mode 100644 index 00000000..486214bc --- /dev/null +++ b/spec/jobs/rendering/reprocess_all_items_job_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe Rendering::ReprocessAllItemsJob, type: :job do + let!(:item) { FactoryBot.create(:item) } + + it "enqueues an Entities::ReprocessLayoutsJob for each record" do + expect do + described_class.perform_now + end.to have_enqueued_job(Entities::ReprocessLayoutsJob).once.with(item) + end +end diff --git a/spec/jobs/system/shutdown_worker_job_spec.rb b/spec/jobs/system/shutdown_worker_job_spec.rb new file mode 100644 index 00000000..04517ace --- /dev/null +++ b/spec/jobs/system/shutdown_worker_job_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +RSpec.describe System::ShutdownWorkerJob, type: :job do + before do + GOOD_JOB_KEEP_RUNNING.make_true + end + + after do + GOOD_JOB_KEEP_RUNNING.make_true + end + + it "signals GoodJob to stop processing new jobs" do + expect do + described_class.perform_now + end.to change(GOOD_JOB_KEEP_RUNNING, :true?).from(true).to(false) + end +end diff --git a/spec/services/layouts/disabler_spec.rb b/spec/services/layouts/disabler_spec.rb new file mode 100644 index 00000000..2adcfd41 --- /dev/null +++ b/spec/services/layouts/disabler_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +RSpec.describe Layouts::Disabler do + describe "#disable!" do + it "disables layout invalidation within the block" do + disabler = described_class.new + + expect(Layouts::Disabled).not_to be_currently + + disabler.disable! do + expect(Layouts::Disabled).to be_currently + end + + expect(Layouts::Disabled).not_to be_currently + end + end +end