Module: LcpRuby::Authorization::IncludesHint

Defined in:
lib/lcp_ruby/authorization/includes_hint.rb

Overview

Builds an ‘ActiveRecord::Relation#includes` argument that preloads the parent associations along an `inherits_from` cascade. Composes recursively so a 3-tier chain `grade → student → school_class` yields `{ student: { school_class: {} } }` — preloading just the immediate parent is not enough when the parent’s own scope traverses further.

The hint is purely additive: AR’s ‘.includes` is associative and idempotent, so callers may chain their own `.includes(…)` on top (e.g. presenter-driven `IncludesResolver`) and the result is one consolidated preload graph.

See ‘docs/design/hierarchical_record_permissions.md` § 6.7 (Increment 3 of hierarchical record permissions).

Class Method Summary collapse

Class Method Details

.build(scope_spec, model_class, roles, depth = 0) ⇒ Object

Returns one of:

nil          — scope has no inheritance to preload
Hash         — `{ assoc: <child> }` per parent (always a Hash for
               the `inherits` branch)
Array        — top-level `union` of multiple branches; AR accepts
               mixed `[:a, { b: :c }]`.

‘model_class` is the AR class the scope spec applies to (needed to resolve the `belongs_to` reflection name from the parent model name — handles `via:` overrides via `AssociationLookup`).

‘roles` is the list already computed by the caller (`PermissionEvaluator#roles`) — passed through so we don’t re-walk the role resolver per parent and so the unit specs can drive the helper without a real ‘User`.

Bounded by ‘max_inheritance_depth` (also enforced by `ConfigurationValidator#detect_inheritance_cycle` at boot) so a malformed cycle here cannot loop forever.



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/lcp_ruby/authorization/includes_hint.rb', line 39

def build(scope_spec, model_class, roles, depth = 0)
  return nil unless scope_spec.is_a?(Hash)
  return nil if depth > LcpRuby.configuration.max_inheritance_depth

  case scope_spec["type"]
  when "inherits"
    build_inherits(scope_spec, model_class, roles, depth)
  when "union"
    build_union(scope_spec, model_class, roles, depth)
  end
end