Class: Metadata::FormBuilder

Inherits:
BuilderBase
  • Object
show all
Defined in:
app/models/metadata/form_builder.rb

Instance Attribute Summary

Attributes inherited from BuilderBase

#locals

Instance Method Summary collapse

Methods inherited from BuilderBase

#view_for

Constructor Details

#initialize(*args) ⇒ FormBuilder

Returns a new instance of FormBuilder.



3
4
5
6
7
8
9
10
11
# File 'app/models/metadata/form_builder.rb', line 3

def initialize(*args, &)
  super
  view_for(:field, 'shared/metadata/edit_field')
  view_for(:radio_field, 'shared/metadata/radio_field')
  view_for(:header, 'shared/metadata/header')
  view_for(:document, 'shared/metadata/edit_document_field')

  @related_fields, @changing = [], []
end

Instance Method Details

#append_class!(options, klass) ⇒ Hash

Mutates the input html_options hash to add klass to the css classes while maintaining any existing classes

Parameters:

  • options (Hash)

    Hash of HTML options for the rails form renderer

  • klass (String)

    A css class to add to the :class key

Returns:

  • (Hash)

    The HTML options hash. @note The original hash is mutated, the return value is provided for method chaining



93
94
95
96
97
# File 'app/models/metadata/form_builder.rb', line 93

def append_class!(options, klass)
  options[:class] = Array(options[:class])
  options[:class] << klass
  options
end

#change_select_options_for(field, options) ⇒ Object

Allows the options of the specified ‘field’ to be changed based on the value of another field.

Inputs: field [Symbol] The affected field e.g. :data_release_timing options [Hash] Contains the controlling field, & a map of controlling values to resulting values (see e.g. below)

All this method does is reformat the values hash to separate out keys that are arrays e.g. changing the options hash as follows:

{ :when=>:data_release_strategy, :values=>{ “not applicable”=>“never”, [“open”, “managed”]=>[“standard”, “immediate”, “delayed”] } } becomes { :when=>:data_release_strategy, :values=>{ “not applicable”=>, “open”=>[“standard”, “immediate”, “delayed”], “managed”=>[“standard”, “immediate”, “delayed”] } }

It then passes the modified arguments on to the @changing array.



148
149
150
151
152
153
# File 'app/models/metadata/form_builder.rb', line 148

def change_select_options_for(field, options)
  options[:values] = options[:values].inject({}) do |values, (key, value)|
    values.tap { Array(key).each { |k| values[k.to_s] = Array(value) } }
  end
  @changing.push([field, options])
end

#document_field(field, options) ⇒ Object

Creates a file upload field that will be properly handled by Document instances. It’s a bit of a hack because the field we’re actually rendering is not what is requested in ‘field’, but

field_attributes’, so we have to also use a special view for it to alter that.

NOTE: This is immediately overridden by the block below so don’t move it! ++



19
20
21
22
23
24
25
# File 'app/models/metadata/form_builder.rb', line 19

def document_field(field, options)
  property_field(:document, field, options) do
    fields_for(:"#{field}_attributes", builder: ActionView::Helpers::FormBuilder) do |fields|
      fields.file_field(:uploaded_data)
    end
  end
end

Renders the Javascript for dealing with showing and hiding the related fields.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'app/models/metadata/form_builder.rb', line 156

def finalize_related_fields
  related = @related_fields.compact.uniq.map(&:to_s)
  unless related.empty?
    concat(
      render(
        partial: 'shared/metadata/related_fields',
        locals: {
          root: sanitized_object_name,
          related: related,
          changing_fields: @changing
        }
      )
    )
  end
end

#header(field, options = {}) ⇒ Object



99
100
101
# File 'app/models/metadata/form_builder.rb', line 99

def header(field, options = {})
  render_view(:header, field, options)
end

#radio_select(method, choices, options = {}, html_options = {}) ⇒ Object

rubocop:todo Metrics/MethodLength



55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/metadata/form_builder.rb', line 55

def radio_select(method, choices, options = {}, html_options = {}) # rubocop:todo Metrics/MethodLength
  group = html_options.delete(:grouping) || options.delete(:grouping)
  property_field(:radio_field, method, grouping: group) do
    choices.each_with_object(+''.html_safe) do |(label_text, option_value), output|
      output << tag.div(class: %w[custom-control custom-radio custom-control-inline]) do
        value = option_value || label_text
        concat radio_button(method, value, class: 'custom-control-input', required: true)
        concat label(method, label_text, class: 'custom-control-label', value: value)
      end
    end
  end
end

Handles wrapping certain fields so that they are only shown when another field is a certain value. You can use :to to give the name of the field, :when for the single value when the fields should be shown, and :in for a group of values. You must call finalize_related_fields at the end of your view to get the appropriate behaviour



107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/models/metadata/form_builder.rb', line 107

def related_fields(options, &) # rubocop:todo Metrics/AbcSize
  options.symbolize_keys!

  values =
    (options.fetch(:in, Array(options[:when])) - Array(options[:not])).map do |v|
      v.to_s.downcase.gsub(/[^a-z0-9]+/, '_')
    end
  content = capture(&)
  concat(tag.div(content, class: [:related_to, options[:to], values].flatten.join(' ')))

  @related_fields.push(options[:to])
  content
end

#select(method, choices, options = {}, html_options = {}, &block) ⇒ Object



49
50
51
52
53
# File 'app/models/metadata/form_builder.rb', line 49

def select(method, choices, options = {}, html_options = {}, &block)
  group = html_options.delete(:grouping) || options.delete(:grouping)
  append_class!(html_options, 'custom-select select2')
  property_field(:field, method, grouping: group) { super(method, choices, options, html_options, &block) }
end

#select_by_association(association, options = {}, html_options = {}) ⇒ Object



27
28
29
30
31
32
33
34
# File 'app/models/metadata/form_builder.rb', line 27

def select_by_association(association, options = {}, html_options = {})
  append_class!(options, 'select2')
  association_target = association.to_s.classify.constantize
  if @object.send(association).nil? && association_target.default.present?
    options[:selected] = association_target.default.for_select_dropdown.last
  end
  select(:"#{association}_id", association_target.for_select_association, options, html_options)
end