Class: HasHelpers::Cards::CardDataCommand

Inherits:
Object
  • Object
show all
Defined in:
app/commands/has_helpers/cards/card_data_command.rb

Overview

Command object that resolves the effective card configuration (card + fields) for a given role/resource/identifier/record combo and returns serialized data for GraphQL or other consumers.

Usage:

result = HasHelpers::Cards::CardDataCommand.call(
role_id:       role_id,
resource_id:   resource_id,
identifier_id: identifier_id,
record_id:     record_id
)

Usage (optimized API, receives a preloaded record to avoid N+1):

result = HasHelpers::Cards::CardDataCommand.call_with_record(
role_id:       role_id,
resource_id:   resource,        # HasHelpers::Resource instance or id
identifier_id: identifier,      # HasHelpers::Cards::CardIdentifier instance or id
record:        record           # ActiveRecord instance
)

result.card    # => Hash
result.fields  # => Array<Hash>
result.entries # => Array<Hash>

Defined Under Namespace

Classes: Response

Constant Summary collapse

MAPPER_NOT_APPLIED =
Object.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(role_id:, resource_id:, identifier_id:, record_id: nil, record: nil, card_definition: nil) ⇒ CardDataCommand

Constructor supports both styles:

- record_id only (classic)
- record only (preloaded)
- or both, but record takes precedence when present.


71
72
73
74
75
76
77
78
79
80
# File 'app/commands/has_helpers/cards/card_data_command.rb', line 71

def initialize(role_id:, resource_id:, identifier_id:, record_id: nil, record: nil, card_definition: nil)
  @role            = ::HasHelpers::Role.find_by(id: role_id)
  @resource        = find_resource(resource_id)
  # Hover-only definitions are passed in-memory; we don't look up the CardIdentifier
  # option in the DB for those (the identifier may just be a plain string).
  @identifier      = card_definition ? identifier_id : find_identifier(identifier_id)
  @record_id       = record_id
  @record          = record
  @card_definition = card_definition
end

Class Method Details

.call(role_id:, resource_id:, identifier_id:, record_id:) ⇒ Object

Public entry point: original API, keeps backward compatibility. It receives record_id and will load the record internally.



34
35
36
37
38
39
40
41
# File 'app/commands/has_helpers/cards/card_data_command.rb', line 34

def self.call(role_id:, resource_id:, identifier_id:, record_id:)
  new(
    role_id:       role_id,
    resource_id:   resource_id,
    identifier_id: identifier_id,
    record_id:     record_id
  ).call
end

.call_with_definition(resource_id:, card_definition:, record:) ⇒ Object

Public entry point: hover-only cards live entirely in-memory. Caller supplies a pre-built (unpersisted) CardDefinition + preloaded record; no DB lookup for the definition is performed, and role customization is skipped.



57
58
59
60
61
62
63
64
65
# File 'app/commands/has_helpers/cards/card_data_command.rb', line 57

def self.call_with_definition(resource_id:, card_definition:, record:)
  new(
    role_id:         nil,
    resource_id:     resource_id,
    identifier_id:   card_definition.identifier,
    record:          record,
    card_definition: card_definition
  ).call
end

.call_with_record(role_id:, resource_id:, identifier_id:, record:) ⇒ Object

Public entry point: optimized variant that receives a preloaded record. This avoids doing resource.klass.find(record_id) for each card.



45
46
47
48
49
50
51
52
# File 'app/commands/has_helpers/cards/card_data_command.rb', line 45

def self.call_with_record(role_id:, resource_id:, identifier_id:, record:)
  new(
    role_id:       role_id,
    resource_id:   resource_id,
    identifier_id: identifier_id,
    record:        record
  ).call
end

Instance Method Details

#callObject



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/commands/has_helpers/cards/card_data_command.rb', line 82

def call
  card_definition    = @card_definition || find_card_definition!
  registry_card      = registry_card_for(card_definition)
  role_resource_card = @card_definition ? nil : find_role_resource_card(card_definition)

  card_visible  = resolve_card_visibility(card_definition, role_resource_card)
  card_position = resolve_card_position(card_definition, role_resource_card)

  # Build field configs with all metadata in memory.
  field_configs = build_field_configs(card_definition, role_resource_card, registry_card)

  # If the card is hidden for this role, we only return the card metadata.
  return Response.new(
    card:    serialize_card(card_definition, card_visible, card_position),
    fields:  [],
    entries: []
  ) unless card_visible

  # In hover-only mode the parent record IS the target — no relationship navigation.
  relationship = @card_definition ? nil : relationship_for(card_definition, registry_card)

  targets = target_records(
    load_record,
    relationship,
    scope_for(card_definition, registry_card),
    expansion_scope_for(card_definition, registry_card),
    registry_card
  )

  Response.new(
    card:    serialize_card(card_definition, card_visible, card_position),
    fields:  serialize_fields(field_configs),
    entries: build_entries(targets, field_configs, registry_card)
  )
end