Class: Lifeform::Form

Inherits:
Object
  • Object
show all
Extended by:
Sequel::Inflections
Includes:
Streamlined::Renderable
Defined in:
lib/lifeform/form.rb

Overview

A form object which stores field definitions and can be rendered as a component

Constant Summary collapse

MODEL_PATH_HELPER =
:polymorphic_path

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model = nil, url: nil, library: self.class.library, emit_form_tag: true, parent_name: nil, **parameters) ⇒ Form

rubocop:disable Metrics/ParameterLists



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/lifeform/form.rb', line 132

def initialize( # rubocop:disable Metrics/ParameterLists
  model = nil, url: nil, library: self.class.library, emit_form_tag: true, parent_name: nil, **parameters
)
  @model, @url, @library_name, @parameters, @emit_form_tag, @parent_name =
    model, url, library, parameters, emit_form_tag, parent_name
  @library = Libraries.const_get(self.class.send(:camelize, @library_name))
  @subform_instances = {}

  self.class.initialize_field_definitions!

  @method = parameters[:method] ||= model.respond_to?(:persisted?) && model.persisted? ? :patch : :post
  parameters[:accept_charset] ||= "UTF-8"
  verify_method
end

Class Attribute Details

.rodauthObject



29
# File 'lib/lifeform/form.rb', line 29

def rodauth = Form.instance_variable_get(:@rodauth)

Instance Attribute Details

#emit_form_tagBoolean (readonly)

Returns:

  • (Boolean)


127
128
129
# File 'lib/lifeform/form.rb', line 127

def emit_form_tag
  @emit_form_tag
end

#libraryClass<Libraries::Default> (readonly)

Returns:



121
122
123
# File 'lib/lifeform/form.rb', line 121

def library
  @library
end

#modelObject (readonly)

Returns:

  • (Object)


115
116
117
# File 'lib/lifeform/form.rb', line 115

def model
  @model
end

#parametersHash (readonly)

Returns:

  • (Hash)


124
125
126
# File 'lib/lifeform/form.rb', line 124

def parameters
  @parameters
end

#parent_nameBoolean (readonly)

Returns:

  • (Boolean)


130
131
132
# File 'lib/lifeform/form.rb', line 130

def parent_name
  @parent_name
end

#urlString (readonly)

Returns:

  • (String)


118
119
120
# File 'lib/lifeform/form.rb', line 118

def url
  @url
end

Class Method Details

.configurationObject



31
# File 'lib/lifeform/form.rb', line 31

def configuration = @configuration ||= HashWithDotAccess::Hash.new

.field(name, type: :text, library: self.library, **parameters) ⇒ Object



50
51
52
53
# File 'lib/lifeform/form.rb', line 50

def field(name, type: :text, library: self.library, **parameters)
  parameters[:name] = name.to_sym
  fields[name] = FieldDefinition.new(type, Libraries.const_get(camelize(library)), parameters)
end

.fields(&block) ⇒ Hash<Symbol, FieldDefinition>

Parameters:

  • block (Proc, nil)

Returns:



35
36
37
38
39
40
# File 'lib/lifeform/form.rb', line 35

def fields(&block)
  @fields ||= {}
  @fields_setup_block = block if block

  @fields
end

.inherited(subclass) ⇒ Object



18
19
20
21
# File 'lib/lifeform/form.rb', line 18

def inherited(subclass)
  super
  subclass.library library
end

.initialize_field_definitions!Object



42
43
44
45
46
# File 'lib/lifeform/form.rb', line 42

def initialize_field_definitions!
  return unless @fields_setup_block

  @fields_setup_block.(configuration)
end

.library(library_name = nil) ⇒ Object



59
60
61
62
# File 'lib/lifeform/form.rb', line 59

def library(library_name = nil)
  @library = library_name.to_sym if library_name
  @library ||= :default
end

.name_of_model(model) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/lifeform/form.rb', line 96

def name_of_model(model)
  return "" if model.nil?

  if model.respond_to?(:to_model)
    model.to_model.model_name.param_key
  else
    # Or just use basic underscore
    underscore(model.class.name).tr("/", "_")
  end
end

.param_keysArray<Symbol>

Returns:

  • (Array<Symbol>)


108
# File 'lib/lifeform/form.rb', line 108

def param_keys = fields.keys

.param_string_keysArray<String>

Returns:

  • (Array<String>)


111
# File 'lib/lifeform/form.rb', line 111

def param_string_keys = fields.keys.map(&:to_s)

.parameters_to_attributes(kwargs) ⇒ Object

Parameters:

  • kwargs (Hash)


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/lifeform/form.rb', line 80

def parameters_to_attributes(kwargs)
  attributes = {}
  kwargs.each do |key, value|
    case value
    when Hash
      value.each do |inner_key, inner_value|
        attributes[:"#{key}_#{inner_key}"] = process_value(inner_key, inner_value)
      end
    else
      attributes[key] = process_value(key, value) unless value.nil?
    end
  end

  attributes
end

.process_value(key, value) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/lifeform/form.rb', line 64

def process_value(key, value)
  return value if key == :if

  case value
  when TrueClass
    key.to_s
  when FalseClass
    nil
  when Symbol, Integer
    value.to_s
  else
    value
  end
end

.subform(name, klass, parent_name: nil) ⇒ Object



55
56
57
# File 'lib/lifeform/form.rb', line 55

def subform(name, klass, parent_name: nil)
  subforms[name.to_sym] = { class: klass, parent_name: parent_name }
end

.subformsObject



48
# File 'lib/lifeform/form.rb', line 48

def subforms = @subforms ||= {}

.tObject

Helper to point to ‘I18n.t` method



24
# File 'lib/lifeform/form.rb', line 24

def t(...) = I18n.t(...)

Instance Method Details

#add_authenticity_tokenObject



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/lifeform/form.rb', line 161

def add_authenticity_token
  if helpers.respond_to?(:token_tag, true) # Rails
    helpers.send(:token_tag, nil, form_options: {
      action: parameters[:action].to_s,
      method: parameters[:method].to_s.downcase,
    })
  elsif helpers.respond_to?(:csrf_tag, true) # Roda
    helpers.send(:csrf_tag, parameters[:action].to_s, @method.to_s)
  else
    raise Lifeform::Error, "Missing token tag helper. Override `add_authenticity_token' in your Form object"
  end
end

#attributesObject



174
175
176
# File 'lib/lifeform/form.rb', line 174

def attributes
  @attributes ||= self.class.parameters_to_attributes(parameters)
end

#auto_render_fieldsObject



210
# File 'lib/lifeform/form.rb', line 210

def auto_render_fields = html_map(self.class.fields) { |k, _v| render(field(k)) }

#field(name, **field_parameters) ⇒ Object



178
179
180
181
182
183
184
185
186
# File 'lib/lifeform/form.rb', line 178

def field(name, **field_parameters)
  # @type [FieldDefinition]
  field_definition = self.class.fields[name.to_sym]
  # @type [Class<Libraries::Default>]
  field_library = field_definition.library
  field_library.object_for_field_definition(
    self, field_definition, self.class.parameters_to_attributes(field_parameters)
  )
end

#render(field_object) ⇒ Object



212
213
214
# File 'lib/lifeform/form.rb', line 212

def render(field_object)
  field_object.render_in(helpers || self)
end

#subform(name, model = nil) ⇒ Object



188
189
190
191
192
193
194
# File 'lib/lifeform/form.rb', line 188

def subform(name, model = nil)
  @subform_instances[name.to_sym] ||= self.class.subforms[name.to_sym][:class].new(
    model,
    emit_form_tag: false,
    parent_name: self.class.subforms[name.to_sym][:parent_name] || self.class.name_of_model(self.model)
  )
end

#template(&block) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity



196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/lifeform/form.rb', line 196

def template(&block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
  form_tag = library::FORM_TAG
  parameters[:action] ||= url || (model ? helpers.send(self.class.const_get(:MODEL_PATH_HELPER), model) : nil)

  html -> { <<~HTML # rubocop:disable Bridgetown/InsecureHeredoc
    <#{form_tag} #{html_attributes attributes}>
      #{add_authenticity_token unless parameters[:method].to_s.casecmp("get").zero?}
      #{@method_tag&.() || ""}
      #{block ? capture(self, &block) : auto_render_fields}
    </#{form_tag}>
  HTML
  }
end

#verify_methodObject



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/lifeform/form.rb', line 147

def verify_method
  return if %w[get post].include?(parameters[:method].to_s.downcase)

  method_value = @parameters[:method].to_s.downcase

  @method_tag = -> {
    <<~HTML
      <input type="hidden" name="_method" value="#{text -> { method_value }}" autocomplete="off">
    HTML
  }

  parameters[:method] = :post
end