Class: Ecoportal::API::Common::GraphQL::Model::Diffable::LeafDiffService

Inherits:
DiffService show all
Defined in:
lib/ecoportal/api/common/graphql/model/diffable/leaf_diff_service.rb

Overview

Diff strategy for models whose array attributes are leaf DATA, not nested GraphQL::Model children.

Array attributes declared with passarray (tag lists, id lists, config lists) — and embeds_many id-collections written as a whole set (e.g. page locations) — are registered as cascaded attributes. The default DiffService:

1. strips cascaded keys from the flat classic diff
 (`doc_with_non_cascaded_attributes`), on the assumption that a later
 `diff_reduce` pass re-adds them by cascading into nested `GraphQL::Model`
 children; and
2. in `diff_reduce`, deletes the flat placeholder for every cascaded key and
 only re-adds it when the child object `is_a?(GraphQL::Model)`.

A passarray is wrapped as a v2 ArrayModel and a whole-set embeds_many as a CollectionModel — neither is a GraphQL::Model, so the re-add never happens and the array change becomes invisible to as_update (scalar passthrough fields are unaffected). This is the systemic "array/leaf fields don't diff" root cause (see .ai-assistance/code/model_input_mapping/SYNTHESIS.md §3.1).

This service fixes it for leaf models by:

1. keeping cascaded keys in the flat classic diff (`doc_with_non_cascaded_attributes`
 is a no-op), and
2. overriding `diff_reduce` so the flat-diff placeholder for a NON-model leaf
 child is preserved (only real `GraphQL::Model` children are cascade-diffed;
 `root!` children are still dropped as independent entities), and
3. treating a leaf array as an OPAQUE value: a change replaces the whole array
 (a full-set replace), rather than the id-keyed nested update/delete/create
 patch-ops the default array diff would emit. This matches how the write inputs
 consume these arrays (`PageInput.otherTags`, `PageInput.locations = [{ id: }]`,
 register `filterTags`, PresetView `fieldConfigurations`).

A model can either behave as a full leaf (call dirty?/as_update with flat: true, as Base::Page::DataField does — it has no nested GraphQL::Model children at all) or as a mixed model with both leaf arrays and real nested model children (page / register / preset view — the default cascaded as_update still recurses into the model children while the leaf arrays now diff inline).

Constant Summary

Constants included from HashDiffNesting

HashDiffNesting::META_KEYS, HashDiffNesting::NO_CHANGES

Instance Attribute Summary

Attributes inherited from ClassicDiffService

#subject

Method Summary

Methods inherited from DiffService

#diff, #flat?, #initialize

Methods included from HashHelpers

included

Methods included from HashHelpers::InstanceMethods

#deep_dup, #except_keys, #keys_to_s_deep, #keys_to_sym_deep, #remove_nil_keys_deep

Methods included from HashDiffNesting

included

Methods inherited from ClassicDiffService

#diff, #initialize

Constructor Details

This class inherits a constructor from Ecoportal::API::Common::GraphQL::Model::Diffable::DiffService