Class: Forms::Base

Inherits:
Form
  • Object
show all
Defined in:
lib/forms/base.rb

Overview

Declarative form classes: subclass, declare the fields in #fields, render. Inside #fields, self IS the form — the whole builder surface (field, row, group, Input, submit, fields_for, ...) is available as bare calls.

class UserForm < Forms::Base
form_options :spaced

def fields
  field :email
  row { field :first_name; field :last_name }
  submit :primary
end
end

render UserForm.new(model: @user)

A render-time block appends after the declared fields:

render UserForm.new(model: @user) { |f| f.Hidden(:token) }

Constant Summary

Constants included from PhlexForms::Builder

PhlexForms::Builder::INPUT_TYPE_INFERENCE, PhlexForms::Builder::INPUT_TYPE_MODIFIERS

Instance Attribute Summary

Attributes inherited from Form

#errors, #method, #model, #scope, #theme, #url, #validate

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Form

#collection_check_boxes, #collection_select, #default_field_variants, #field_id, #field_name, #field_object, #field_value, #fields_for, #rich_textarea, #submit, #time_zone_select, #validations_introspector

Methods included from PhlexForms::Builder

#Checkbox, #Control, #FileInput, #Hidden, #Input, #Label, #Radio, #Select, #Textarea, #Toggle, #field, #group, #row

Constructor Details

#initialize(*modifiers, **options) ⇒ Base

Returns a new instance of Base.



80
81
82
# File 'lib/forms/base.rb', line 80

def initialize(*modifiers, **options)
  super(*(self.class.form_modifiers + modifiers).uniq, **self.class.form_defaults.merge(options))
end

Class Method Details

.form_defaultsObject



39
40
41
42
# File 'lib/forms/base.rb', line 39

def form_defaults
  inherited = superclass.respond_to?(:form_defaults) ? superclass.form_defaults : {}
  inherited.merge(@form_defaults || {})
end

.form_modifiersObject



34
35
36
37
# File 'lib/forms/base.rb', line 34

def form_modifiers
  inherited = superclass.respond_to?(:form_modifiers) ? superclass.form_modifiers : []
  (inherited + (@form_modifiers || [])).uniq
end

.form_options(*modifiers, **defaults) ⇒ Object

Class-level defaults, inherited and merged down the subclass chain (instance args beat class defaults; subclass defaults beat parents'):

form_options :spaced, url: "/signup", validate: true


29
30
31
32
# File 'lib/forms/base.rb', line 29

def form_options(*modifiers, **defaults)
  @form_modifiers = modifiers
  @form_defaults = defaults
end

.live(model:, scope: nil, debounce: 300) ⇒ Object

Server-truth live validation via phlex-reactive (a soft dependency):

class UserForm < Forms::Base
live model: User
def fields = field(:email)
end

Blur/debounced input POST every field to a :validate action that runs the REAL model validators and morphs the errors back in, focus intact. Only Forms::Base subclasses can be live — the endpoint rebuilds the form from its class; an inline block cannot be serialized.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/forms/base.rb', line 55

def live(model:, scope: nil, debounce: 300)
  unless reactive_available?
    raise PhlexForms::FeatureUnavailable,
      "#{name || 'this form'} declares `live` but the phlex-reactive gem is not " \
      "installed. Add `gem \"phlex-reactive\"` to your Gemfile, or use " \
      "`validate: true` for the client-side Stimulus mirror instead."
  end

  include Forms::Live

  setup_live(model_class: model, scope: scope || derive_live_scope(model), debounce:)
end

.reactive_available?Boolean

Extracted so specs can exercise the FeatureUnavailable guard.

Returns:

  • (Boolean)


69
70
71
# File 'lib/forms/base.rb', line 69

def reactive_available?
  defined?(Phlex::Reactive::Component) ? true : false
end

Instance Method Details

#fieldsObject

The hook name fields is verified free against Phlex::HTML (phlex 2.4.x) and this gem (Form defines fields_for/field_object, never fields).

Raises:

  • (NotImplementedError)


96
97
98
# File 'lib/forms/base.rb', line 96

def fields
  raise NotImplementedError, "#{self.class} must define #fields"
end

#view_templateObject

Renders Form's chrome (action/method/CSRF/enctype), then the declared fields. yield/block_given? inside the inner block bind to view_template's own block — the optional render-time block.



87
88
89
90
91
92
# File 'lib/forms/base.rb', line 87

def view_template
  super do
    fields
    yield self if block_given?
  end
end