Class: HasHelpers::Cards::CardBuilder::CardDefinitionBuilder

Inherits:
Object
  • Object
show all
Defined in:
app/models/has_helpers/cards/card_builder/card_definition_builder.rb

Defined Under Namespace

Classes: ValidationError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, relationship:, relationship_klass:, identifier:, layout:, resources:, variant:, virtual:, hover_only:, is_customizable:, default_visibility:, default_position:, scope: nil, icon_name: nil, icon_color: nil, owner_class: nil) ⇒ CardDefinitionBuilder

Returns a new instance of CardDefinitionBuilder.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 30

def initialize(name:, relationship:, relationship_klass:, identifier:, layout:, resources:, variant:, virtual:, hover_only:, is_customizable:, default_visibility:, default_position:, scope: nil, icon_name: nil, icon_color: nil, owner_class: nil)
  @owner_class  = owner_class
  @identifier   = identifier || derive_identifier!
  @name         = name
  @relationship = relationship
  @relationship_klass = (relationship_klass && (relationship_klass.is_a?(Class) ? relationship_klass.name : relationship_klass.to_s)) || nil
  @scope = scope
  @layout_type = normalize_enum(layout)
  @virtual = !!virtual
  @hover_only = hover_only ? true : nil
  @variant = variant && normalize_enum(variant)

  # Optional, non-persisted presentation hints at the card level.
  # They live in the registry (in-memory) only and are never written to
  # card_definitions; consumers read them from the DSL registry.
  @icon_name  = icon_name.presence
  @icon_color = icon_color.presence

  # Defaults at card level
  @description        = nil
  @is_customizable    = is_customizable
  @default_visibility = default_visibility.nil? ? true : default_visibility
  @default_position   = default_position

  @resources = Array(resources).map { |r| r.respond_to?(:to_h) ? r.to_h : r }
  @fields    = []
  @data_sources = []
  @expansions = []

  @next_field_position = 10
end

Instance Attribute Details

#custom_labelObject (readonly)

Returns the value of attribute custom_label.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def custom_label
  @custom_label
end

#custom_label_keyObject (readonly)

Returns the value of attribute custom_label_key.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def custom_label_key
  @custom_label_key
end

#data_sourcesObject (readonly)

Returns the value of attribute data_sources.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def data_sources
  @data_sources
end

#default_positionObject (readonly)

Returns the value of attribute default_position.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def default_position
  @default_position
end

#default_visibilityObject (readonly)

Returns the value of attribute default_visibility.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def default_visibility
  @default_visibility
end

#expansion_scopeObject (readonly)

Returns the value of attribute expansion_scope.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def expansion_scope
  @expansion_scope
end

#fieldsObject (readonly)

Returns the value of attribute fields.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def fields
  @fields
end

#hover_onlyObject (readonly)

Returns the value of attribute hover_only.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def hover_only
  @hover_only
end

#icon_colorObject (readonly)

Returns the value of attribute icon_color.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def icon_color
  @icon_color
end

#icon_nameObject (readonly)

Returns the value of attribute icon_name.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def icon_name
  @icon_name
end

#identifierObject (readonly)

Returns the value of attribute identifier.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def identifier
  @identifier
end

#is_customizableObject (readonly)

Returns the value of attribute is_customizable.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def is_customizable
  @is_customizable
end

#layout_typeObject (readonly)

Returns the value of attribute layout_type.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def layout_type
  @layout_type
end

#nameObject (readonly)

Returns the value of attribute name.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def name
  @name
end

#relationshipObject (readonly)

Returns the value of attribute relationship.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def relationship
  @relationship
end

#relationship_klassObject (readonly)

Returns the value of attribute relationship_klass.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def relationship_klass
  @relationship_klass
end

#resourcesObject (readonly)

Returns the value of attribute resources.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def resources
  @resources
end

#scopeObject (readonly)

Returns the value of attribute scope.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def scope
  @scope
end

#variantObject (readonly)

Returns the value of attribute variant.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def variant
  @variant
end

#virtualObject (readonly)

Returns the value of attribute virtual.



9
10
11
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 9

def virtual
  @virtual
end

Instance Method Details

#as_jsonObject



335
336
337
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 335

def as_json(*)
  to_h
end

#buildObject

Builds base card plus any expansions defined.



330
331
332
333
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 330

def build
  base_hash = to_h
  [base_hash] + build_expansions(base_hash)
end

#data_source(name, &block) ⇒ Object

DSL:

data_source :details do |record, ctx| { ... } end

Raises:

  • (ArgumentError)


231
232
233
234
235
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 231

def data_source(name, &block)
  raise ArgumentError, "data_source block required" unless block

  @data_sources << { name: name.to_sym, resolver: block }
end

#description(text = nil) ⇒ Object

DSL:

description "Basic details"



65
66
67
68
69
70
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 65

def description(text = nil)
  # setter
  @description = text.to_s if text
  # getter
  @description
end

#expand(name, identifier: nil, expansion_scope: nil, default_visibility: nil, default_position: nil, description: nil, resources: nil) ⇒ Object

DSL:

expand "Primary Emails", identifier: :emails_primary, expansion_scope: ->(rel) { rel.where(is_primary: true) }



240
241
242
243
244
245
246
247
248
249
250
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 240

def expand(name, identifier: nil, expansion_scope: nil, default_visibility: nil, default_position: nil, description: nil, resources: nil)
  @expansions << {
    name:               name,
    identifier:         identifier,
    expansion_scope:    expansion_scope,
    default_visibility: default_visibility,
    default_position:   default_position,
    description:        description,
    resources:          resources
  }
end

#expand_each(_label, values:, &block) ⇒ Object

DSL:

expand_each "Emails by Location", values: HasContactInfo::Location::ALL do |loc| { name: "Emails (#HasHelpers::Cards::CardBuilder::CardDefinitionBuilder.locloc.name)", identifier: :"emails_#HasHelpers::Cards::CardBuilder::CardDefinitionBuilder.locloc.id", expansion_scope: ->(rel) { rel.where(location_id: loc.id) }, default_visibility: false, default_position: 40 + loc.id.to_i } end

Raises:

  • (ArgumentError)


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 263

def expand_each(_label, values:, &block)
  raise ArgumentError, "values required" if values.nil?
  raise ArgumentError, "expand_each block required" unless block

  Array(resources).each_with_index do |res, res_idx|
    res_values =
      if values.respond_to?(:call)
        case values.arity
        when 0 then values.call
        when 1 then values.call(res)
        else values.call(res, res_idx)
        end
      else
        values
      end

    Array(res_values).each_with_index do |value, idx|
      attrs =
        case block.arity
        when 1 then block.call(value)
        when 2 then block.call(value, res)
        else block.call(value, res, idx)
        end

      expand(
        attrs[:name] || value.to_s,
        identifier:         attrs[:identifier],
        expansion_scope:    attrs[:expansion_scope],
        default_visibility: attrs[:default_visibility],
        default_position:   attrs[:default_position] || (@default_position.to_i + (idx + 1)),
        description:        attrs[:description],
        resources:          [res]
      )
    end
  end
end

#field(ref, data_type:, name: nil, **opts) ⇒ Object

DSL:

field :age, data_type: :integer

Defaults:

  • default_visibility: true
  • is_required: false
  • default_position: inferred (10, 20, 30...)


80
81
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
117
118
119
120
121
122
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 80

def field(ref, data_type:, name: nil, **opts)
  ensure_unique_field!(kind: :resource, ref: ref, key: nil)

  position   = opts[:default_position] || next_field_position!
  visibility = opts.key?(:default_visibility) ? !!opts[:default_visibility] : true
  required   = opts.fetch(:is_required, false)
  url_proc   = opts[:url]
  badge_color = opts[:badge_color]
  source = opts[:source]
  custom_label = opts[:custom_label] ? true : false
  custom_label_key = opts[:custom_label_key]
  hover_card, hover_card_source =
    split_static_or_source(opts[:hover_card], opts[:hover_card_source])
  hover_card ||= derive_hover_card(ref, data_type) unless hover_card_source
  hover_record_source = opts[:hover_record_source]
  hover_resource, hover_resource_source =
    split_static_or_source(opts[:hover_resource], opts[:hover_resource_source])
  hover_resource ||= derive_hover_resource(ref, data_type) unless hover_resource_source
  icon = opts[:icon]

  @fields << FieldBuilder.new(
    kind:                  :resource,
    ref:                   ref,
    key:                   nil,
    name:                  name,
    data_type:             data_type,
    mapper:                nil,
    source:                source,
    url:                   url_proc,
    badge_color:           badge_color,
    hover_card:            hover_card,
    hover_card_source:     hover_card_source,
    hover_record_source:   hover_record_source,
    hover_resource:        hover_resource,
    hover_resource_source: hover_resource_source,
    icon:                  icon,
    default_visibility: visibility,
    default_position:   position,
    is_required:        required,
    custom_label:       custom_label,
    custom_label_key:   custom_label_key
  )
end

#field_custom(legacy_key = nil, name:, data_type:, mapper:, **opts) ⇒ Object

DSL:

field_custom name: "Full Name", data_type: HasHelpers::Cards::CardDataType::String, mapper: "CardsFieldMapper::Demographics::FullName"

Defaults:

  • default_visibility: true
  • is_required: false
  • default_position: inferred (10, 20, 30...) Legacy API: first positional argument accepted for backward compatibility but ignored.


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 135

def field_custom(legacy_key = nil, name:, data_type:, mapper:, **opts)
  if legacy_key
    ActiveSupport::Deprecation.warn(
      "Passing a CardCustomField constant as the first argument to field_custom is deprecated and will be removed in future versions."
    )
  end

  key = name.to_s

  ensure_unique_field!(kind: :custom, ref: key, key: key)

  position   = opts[:default_position] || next_field_position!
  visibility = opts.key?(:default_visibility) ? !!opts[:default_visibility] : true
  required   = opts.fetch(:is_required, false)
  url_proc   = opts[:url]
  badge_color = opts[:badge_color]
  source = opts[:source]
  hover_card, hover_card_source =
    split_static_or_source(opts[:hover_card], opts[:hover_card_source])
  hover_record_source = opts[:hover_record_source]
  hover_resource, hover_resource_source =
    split_static_or_source(opts[:hover_resource], opts[:hover_resource_source])
  icon = opts[:icon]

  @fields << FieldBuilder.new(
    kind:                  :custom,
    ref:                   key,
    key:                   key,
    name:                  name,
    data_type:             data_type,
    mapper:                mapper,
    source:                source,
    url:                   url_proc,
    badge_color:           badge_color,
    hover_card:            hover_card,
    hover_card_source:     hover_card_source,
    hover_record_source:   hover_record_source,
    hover_resource:        hover_resource,
    hover_resource_source: hover_resource_source,
    icon:                  icon,
    default_visibility: visibility,
    default_position:   position,
    is_required:        required
  )
end

#field_virtual(ref, name:, data_type:, source:, **opts) ⇒ Object

DSL:

field_virtual :foo, name: "Foo", data_type: :string, source: { from: :details, key: :foo }



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 184

def field_virtual(ref, name:, data_type:, source:, **opts)
  ensure_unique_field!(kind: :virtual, ref: ref, key: nil)

  position   = opts[:default_position] || next_field_position!
  visibility = opts.key?(:default_visibility) ? !!opts[:default_visibility] : true
  required   = opts.fetch(:is_required, false)
  url_proc   = opts[:url]
  badge_color = opts[:badge_color]
  hover_card, hover_card_source =
    split_static_or_source(opts[:hover_card], opts[:hover_card_source])
  hover_record_source = opts[:hover_record_source]
  hover_resource, hover_resource_source =
    split_static_or_source(opts[:hover_resource], opts[:hover_resource_source])
  icon = opts[:icon]

  custom_label     = opts[:custom_label] ? true : false
  custom_label_key = opts[:custom_label_key]

  @fields << FieldBuilder.new(
    kind:                  :virtual,
    ref:                   ref,
    key:                   nil,
    name:                  name,
    data_type:             data_type,
    mapper:                nil,
    source:                source,
    url:                   url_proc,
    badge_color:           badge_color,
    hover_card:            hover_card,
    hover_card_source:     hover_card_source,
    hover_record_source:   hover_record_source,
    hover_resource:        hover_resource,
    hover_resource_source: hover_resource_source,
    icon:                  icon,
    default_visibility: visibility,
    default_position:   position,
    is_required:        required,
    custom_label:       custom_label,
    custom_label_key:   custom_label_key
  )
end

#to_hObject

Normalized representation that the Importer will consume later.



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'app/models/has_helpers/cards/card_builder/card_definition_builder.rb', line 301

def to_h
  validate!

  {
    name:               name,
    identifier:         identifier,
    relationship:       relationship,
    relationship_klass: relationship_klass,
    layout_type:        layout_type,
    virtual:            virtual,
    hover_only:         hover_only,
    variant:            variant,
    description:        description,
    is_customizable:    is_customizable,
    default_visibility: default_visibility,
    default_position:   default_position,
    expansion_scope:    nil,
    scope:              scope,
    data_sources:       data_sources,
    resources:          resources,
    fields:             fields.map(&:to_h),
    custom_label:       custom_label,
    custom_label_key:   custom_label_key,
    icon_name:          icon_name,
    icon_color:         icon_color
  }
end