Class: TypedFormModel::ModelLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/typed_form_model/model_loader.rb

Overview

Builds a form instance from one or more source objects (AR models, OpenStructs, Hashes).

Two entry points with intentionally different semantics:

from_model(record)   — single-source. The source segment of `from:` is
                     irrelevant (no ambiguity to dispatch on). Every
                     prop is read from `record` using its `from:`
                     rename or its own name.
from_models(sources) — multi-source. A dotted `from:` is required per
                     prop to pick the source; props without a dotted
                     `from:` are skipped.

In both cases the attribute segment of from: (or the Symbol form) renames the source attribute, and a prop is skipped if the source doesn't have the attribute at all (lets prop defaults fire). Edit-form semantics: a source that has the attribute with a nil value still wins — the form reflects the source.

Instance Method Summary collapse

Constructor Details

#initialize(form_class) ⇒ ModelLoader

Returns a new instance of ModelLoader.



23
24
25
# File 'lib/typed_form_model/model_loader.rb', line 23

def initialize(form_class)
  @form_class = form_class
end

Instance Method Details

#from_model(record, persisted: true, context: {}, props: nil) ⇒ Object

Single-source loader. Iterates every prop and reads it from record, ignoring the source segment of from:. The attribute segment (or a Symbol-form from:) is honoured for renames. props: (Array of Symbols) restricts which props are pulled from the source; non-listed props are skipped so their defaults fire.



32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/typed_form_model/model_loader.rb', line 32

def from_model(record, persisted: true, context: {}, props: nil)
  allowed = build_allowed_set(props)
  attrs = {}
  form_class.literal_properties.each do |prop|
    next if allowed && !allowed.include?(prop.name)
     = form_class.(prop.name)
    attr_name = (&.model_attribute(prop.name) || prop.name).to_sym
    next unless attribute_present?(record, attr_name)

    attrs[prop.name] = read_attribute(record, attr_name)
  end
  form_class.new(persisted: persisted, context: context, **attrs)
end

#from_models(sources, persisted: true, context: {}, props: nil) ⇒ Object

Multi-source loader. Each prop's metadata.model (parsed from the source segment of a dotted from:) picks its source from sources. Props with no source segment (or whose source isn't in sources) are skipped — there's no implicit fallback when the caller is explicit about having multiple sources. props: (Array of Symbols) restricts which props are pulled across sources; non-listed props are skipped so their defaults fire.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/typed_form_model/model_loader.rb', line 53

def from_models(sources, persisted: true, context: {}, props: nil)
  allowed = build_allowed_set(props)
  attrs = {}
  form_class.literal_properties.each do |prop|
    next if allowed && !allowed.include?(prop.name)
     = form_class.(prop.name)
    next unless 
    source = sources[.model]
    next unless source

    attr_name = .model_attribute(prop.name).to_sym
    next unless attribute_present?(source, attr_name)

    attrs[prop.name] = read_attribute(source, attr_name)
  end
  form_class.new(persisted: persisted, context: context, **attrs)
end