Class: Brut::FrontEnd::Form

Inherits:
Object
  • Object
show all
Extended by:
Brut::FrontEnd::Forms::InputDeclarations
Defined in:
lib/brut/front_end/form.rb

Overview

Base class for forms you create to process an HTML form. Generally, your form subclasses will only declare their inputs using methods from Brut::FrontEnd::Forms::InputDeclarations. That said, you will likely create instances of forms as part of the logic for processing them or for testing.

Direct Known Subclasses

Handlers::MissingHandler::Form

Instance Method Summary collapse

Methods included from Brut::FrontEnd::Forms::InputDeclarations

inputs_from, radio_button_group, select

Constructor Details

#initialize(params: {}) ⇒ Form

Create an instance of this form, optionally initialized with the given values for its params. Because any values can be posted to a form endpoint, the initializer does not use kewyord arguments. Instead, it's initialize with whatever parameters were received. This intializer will then set only those values defined.



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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/brut/front_end/form.rb', line 32

def initialize(params: {})
  params = convert_to_string_or_nil(params.to_h)
  unknown_params = params.keys.map(&:to_s).reject { |key|
    self.class.input_definitions.key?(key)
  }
  if unknown_params.any?
    Brut.container.instrumentation.add_attributes(prefix: :brut, ignored_unknown_params: unknown_params.join(","))
  end
  @params = params.except(*unknown_params).map { |name,value|
      input_definition = begin
                           self.class.input_definitions[name] || self.class.input_definitions.fetch(name.to_s)
                         rescue KeyError
                           raise "cannot find input definition for '#{name}'. Have these: #{self.class.input_definitions.keys.inspect}"
                         end
    if value.kind_of?(Array)
      input_definition = begin
                           self.class.input_definitions[name] || self.class.input_definitions.fetch(name.to_s)
                         rescue KeyError
                           raise "cannot find input definition for '#{name}'. Have these: #{self.class.input_definitions.keys.inspect}"
                         end
      if input_definition.respond_to?(:type) && input_definition.type == "checkbox"
        if value.all? { it.to_s =~ /^\d+$/ }
          # the values represent the indexes of which checkboxes were checked
          new_values = []
          value.each do |index_as_string|
            index = Integer(index_as_string)
            new_values[index] = true
          end
          value = new_values.map { !!it }
        end
      end
    end
    [ name, value ]
  }.to_h

  @new = params_empty?(@params)
  @inputs = self.class.input_definitions.map { |name,input_definition|
    value = @params[name] || @params[name.to_sym]
    inputs = if value.kind_of?(Array)
               value.map.with_index { |one_value, index|
                 input_definition.make_input(value: one_value, index:)
               }
             else
               [
                 input_definition.make_input(value:, index: nil)
               ]
             end

    [ name, inputs ]
  }.to_h
end

Instance Method Details

#constraint_violations(server_side_only: false) ⇒ Hash<String|Array[2]>

Returns a map of any input with a constraint violation and the list of violations. The keys in the hash are input names and the values are arrays of Brut::FrontEnd::Forms::ValidityState instances.

array is the validity state for the input, which is a Brut::FrontEnd::Forms::ValidityState instance. The second element is the index of the input in the array. This index is used when you have more than one field with the same name.

Examples:

iterating

form.constraint_violations.each do |input_name, (constraint_violations,index)|
  # input_name is the input's name, e.g. "email"
  # constraint_violations is an array of {Brut::FrontEnd::Forms::ValidityState} instances, one for each
  #                       problem with the field's value
  # index is the index of the input in the array, e.g. 0  for the first email field, 1 for the second, etc.
end

Parameters:

  • server_side_only (true|false) (defaults to: false)

    if true, only server side constraints are returned.

Returns:

  • (Hash<String|Array[2]>)

    a map of input names to arrays of constraint violations. The first element in the



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/brut/front_end/form.rb', line 148

def constraint_violations(server_side_only: false)
  @inputs.map { |input_name, inputs|
    inputs.map.with_index { |input,index|
      if input.valid?
        nil
      else
        [
          input_name,
          [
            input.validity_state.select { |constraint|
              if server_side_only
                !constraint.client_side?
              else
                true
              end
            },
            index
          ]
        ]
      end
    }.compact
  }.select { !it.empty? }.flatten(1).to_h
end

#constraint_violations?Boolean

Returns true if this form has constraint violations.

Returns:

  • (Boolean)


117
# File 'lib/brut/front_end/form.rb', line 117

def constraint_violations? = !@inputs.values.flatten.all?(&:valid?)

#input(input_name, index: nil) ⇒ Brut::FrontEnd::Forms::Input

Access an input with the given name

Parameters:

Returns:



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

def input(input_name, index: nil)
  index ||= 0
  inputs = self.inputs(input_name)
  input = inputs[index]
  if input.nil?
    input_definition = self.class.input_definitions.fetch(input_name.to_s)
    input = input_definition.make_input(value:"", index:)
    inputs[index] = input
  end
  input
end

#inputs(input_name) ⇒ Brut::FrontEnd::Forms::Input

Return all inputs for the given name.

Parameters:

Returns:



110
111
112
113
114
# File 'lib/brut/front_end/form.rb', line 110

def inputs(input_name)
  @inputs.fetch(input_name.to_s)
rescue KeyError => ex
  raise Brut::Framework::Errors::Bug, "Form does not define the input '#{input_name}'. You must add this to your form. Found these inputs: #{@inputs.keys.join(', ')}"
end

#new?Boolean

Returns true if this form represents a new, empty, untouched form. This is useful for determining if this form has never been submitted and thus any required values don't represent an intentional omission by the user. Generally, don't override this. Instead, override #params_empty?.

Returns:

  • (Boolean)


88
# File 'lib/brut/front_end/form.rb', line 88

def new? = @new

#params_empty?(params) ⇒ Boolean

Template method that is used to determine if the params given to the form's initializer represent an empty value. By default, this returns true if those params were nil or empty. You'd override this if there are values you want to initialize a form with that aren't considered values provided by the user. This can allow a form with values in it to be considered un-submitted.

Returns:

  • (Boolean)


176
# File 'lib/brut/front_end/form.rb', line 176

def params_empty?(params) = params.nil? || params.empty?

#server_side_constraint_violation(input_name:, key:, index: nil, context: {}) ⇒ Object

Set a server-side constraint violation on a given input's name.

to form the entire key.

Parameters:

  • input_name (String|Symbol)

    the name of the input, as passed to Brut::FrontEnd::Forms::InputDeclarations#input et. al.

  • key (String)

    the i18n key fragment representing the constraint. Assume this will be appended to cv.be. in order

  • context (Hash) (defaults to: {})

    additional information about the violation, typically interpolated values for the I18n message.



125
126
127
128
# File 'lib/brut/front_end/form.rb', line 125

def server_side_constraint_violation(input_name:, key:, index: nil, context:{})
  index ||= 0
  self.input(input_name, index:).server_side_constraint_violation(key,context)
end

#to_hHash<String,nil|String|Array<String>>

Return this form as a hash, which is a map of its parameters' current values. This is not simply a filtered version of what was passed into the initializer. This will only have the keys for the inputs this form defines. Those keys will be strings, not symbols. The values will be either nil, a String, or an array of strings. Not every input defined by this form will be represented as a key in the resulting hash—only those keys that were passed to the initializer.

Returns:

  • (Hash<String,nil|String|Array<String>>)

    the form's params as a hash.



184
185
186
187
188
# File 'lib/brut/front_end/form.rb', line 184

def to_h
  @params.map { |key,value|
    [ key.to_s, value ]
  }.to_h
end