Class: LcpRuby::VirtualFields::VirtualForm
- Inherits:
-
Object
- Object
- LcpRuby::VirtualFields::VirtualForm
- Defined in:
- lib/lcp_ruby/virtual_fields/virtual_form.rb
Overview
Per-request synthetic form-like object. Builds an anonymous AM::Model class on first ‘#to_model` access, declares attributes for each VirtualField, whitelists incoming params against the declared field set, and coerces values through AM::Attributes’ type system.
Designed as shared infrastructure (spec § 1 Dual goal): consumers include ‘Pages::FilterForm` (PR 5) plus future use cases (multi- step wizards, virtual show pages, PaaS config UI, custom-field designer).
Security invariants (spec § 3):
* Attributes are declared only from the boot-validated field
list — never from URL keys.
* Incoming params are SLICED against the union of every field's
`attribute_names` (`date_range` expands to `_from`/`_to`)
before assignment. `__send__`, `unknown`, and any other
attacker-supplied key is dropped silently.
* `ActiveModel::API` is included — it transitively brings in
`ActiveModel::Validations` (via AM::AttributeAssignment +
AM::Validations). The original spec feared this would retain
the anonymous class via `DescendantsTracker`; Rails 8+ uses
`ObjectSpace::WeakMap`, so synthetic classes ARE reclaimed by
GC. Pinned by spec/lib/lcp_ruby/virtual_fields/lifecycle_spec.rb.
* Hash / Parameters values for plain attributes coerce to `nil`
(so AM::Attributes applies the declared default). Array-typed
attributes (multi:) defer to `Type::ArrayOf#cast`, which
filters Hash elements out of arrays element-by-element.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#fields ⇒ Object
readonly
Returns the value of attribute fields.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#target_model ⇒ Object
readonly
Returns the value of attribute target_model.
Instance Method Summary collapse
- #any? ⇒ Boolean
- #attributes ⇒ Object
- #each_field(&block) ⇒ Object
- #field(name) ⇒ Object
-
#initialize(name:, fields:, target_model: nil, context: {}) ⇒ VirtualForm
constructor
A new instance of VirtualForm.
-
#model_name ⇒ Object
AM::Model contract for ‘form_with(model: filter_form.to_model)`.
- #persisted? ⇒ Boolean
- #to_model ⇒ Object
Constructor Details
#initialize(name:, fields:, target_model: nil, context: {}) ⇒ VirtualForm
Returns a new instance of VirtualForm.
37 38 39 40 41 42 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 37 def initialize(name:, fields:, target_model: nil, context: {}) @name = name @fields = fields @target_model = target_model @context = context end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
35 36 37 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 35 def context @context end |
#fields ⇒ Object (readonly)
Returns the value of attribute fields.
35 36 37 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 35 def fields @fields end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
35 36 37 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 35 def name @name end |
#target_model ⇒ Object (readonly)
Returns the value of attribute target_model.
35 36 37 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 35 def target_model @target_model end |
Instance Method Details
#any? ⇒ Boolean
68 69 70 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 68 def any? @fields.any? end |
#attributes ⇒ Object
72 73 74 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 72 def attributes to_model.attributes end |
#each_field(&block) ⇒ Object
60 61 62 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 60 def each_field(&block) @fields.each(&block) end |
#field(name) ⇒ Object
64 65 66 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 64 def field(name) @fields.find { |f| f.name.to_s == name.to_s } end |
#model_name ⇒ Object
AM::Model contract for ‘form_with(model: filter_form.to_model)`. Returning a stable, present name keeps Rails from triggering the “anonymous class” branch which falls back to the literal class name (something like `#<Class:0x…>`).
52 53 54 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 52 def model_name @model_name ||= ActiveModel::Name.new(self.class, nil, @name.to_s.camelize) end |
#persisted? ⇒ Boolean
56 57 58 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 56 def persisted? false end |
#to_model ⇒ Object
44 45 46 |
# File 'lib/lcp_ruby/virtual_fields/virtual_form.rb', line 44 def to_model @to_model ||= build_synthetic_instance end |