Module: Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon

Included in:
AnchormodelCheckBoxesInput, AnchormodelInput, AnchormodelRadioButtonsInput
Defined in:
lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb

Overview

Shared logic for all anchormodel SimpleForm inputs (AnchormodelInput, AnchormodelRadioButtonsInput, AnchormodelCheckBoxesInput). Resolves the anchormodel class from the bound object, builds the collection of label/key tuples, and computes the currently-selected key(s).

Callers can supply a custom :collection (either an array of anchormodels or an array of [label, key] tuples) to override the default of am_class.all. Callers without a bound object must pass :anchormodel_attribute explicitly.

Instance Method Summary collapse

Instance Method Details

#input(wrapper_options = nil) ⇒ String

Resolves the anchormodel attribute, builds the collection, and delegates to the underlying SimpleForm input class via super.

Parameters:

  • wrapper_options (Hash, nil) (defaults to: nil)

Returns:

  • (String)

    Rendered HTML.

Raises:

  • (RuntimeError)

    if the bound object does not include ModelMixin, or if the attribute does not look like an anchormodel attribute.



19
20
21
22
23
24
25
26
27
28
29
30
31
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
# File 'lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb', line 19

def input(wrapper_options = nil)
  if object.present?
    unless object.respond_to?(:anchormodel_attributes)
      fail("The form field object does not appear to respond to `anchormodel_attributes`, \
  did you `include Anchormodel::ModelMixin` in your `application_record.rb`? Affected object: #{object.inspect}")
    end
    am_attr = object.anchormodel_attributes[@attribute_name]
  elsif options.key?(:anchormodel_attribute)
    am_attr = options.delete(:anchormodel_attribute)
  else
    fail(':anchormodel input requires either an object or the input option :anchormodel_attribute, but neither is set.')
  end

  unless am_attr
    fail("#{@attribute_name.inspect} does not look like an Anchormodel attribute, is `belongs_to_anchormodel` called in your model? \
Affected object: #{object.inspect}")
  end
  am_class = am_attr.anchormodel_class
  options[:multiple] = true if am_attr.multiple? && options[:multiple] != false # allow overriding

  # Attempt to read selected key from html input options "value", as the caller might not know that this is a select.
  selected_key = input_options[:value].presence || options.dig(:input_html, :value)
  if selected_key.blank? && object
    # No selected key override present and a model is present, use the model to find out what to select.
    selected_am = object.send(@attribute_name)
    if am_attr.multiple?
      selected_key = selected_am&.map(&:key)&.map(&:to_s) || []
    else
      selected_key = (selected_am&.key || am_class.all.first).to_s
    end
  end

  options.deep_merge!(
    label_method:  :first,
    value_method:  :second,
    sf_selection_key =>       selected_key
  )

  if options[:collection]
    # Case where collection is given as an option consisting of an array of pairs
    if options[:collection].first.is_a?(Enumerable) && options[:collection].first&.size == 2
      @collection = options[:collection]
    else
      # Case where collection is given as an option consisting of an array of anchormodels
      @collection = collect(options[:collection])
    end
  else
    # Case where collection is not given
    @collection = collect(am_class.all)
  end

  before_render_input

  super
end