Class: CafeCar::FormBuilder

Inherits:
ActionView::Helpers::FormBuilder
  • Object
show all
Includes:
Resolver
Defined in:
lib/cafe_car/form_builder.rb

Instance Method Summary collapse

Methods included from Resolver

#const, #const!

Constructor Details

#initializeFormBuilder

Returns a new instance of FormBuilder.



7
8
9
10
# File 'lib/cafe_car/form_builder.rb', line 7

def initialize(...)
  super
  @fields = {}
end

Instance Method Details

#association(method, collection: nil, multiple: false, **options) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/cafe_car/form_builder.rb', line 16

def association(method, collection: nil, multiple: false, **options)
  info = info(method)

  return show(info.input_key) if info.polymorphic? and object.persisted?
  return hidden(*info.polymorphic_methods) if info.polymorphic?
  return preserved_association(info) unless association_accessible?(info)

  collection ||= with_selected(info, association_collection(info))
  # A multi-select filters by a set (`author_id[]`), so no blank "any" option —
  # an empty selection already means "any". A single select keeps its prompt.
  options[:include_blank] ||= info.prompt unless multiple

  html = searchable_select(info)
  html[:multiple] = true if multiple

  collection_select(info.input_key, collection, :id,
                    -> { @template.present(_1).title }, options, html)
end

#association_accessible?(info) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/cafe_car/form_builder.rb', line 45

def association_accessible?(info)
  @template.policy(info.reflection.klass).index?
end

#association_collection(info) ⇒ Object

Association choices obey the associated model's Pundit scope. The remote typeahead endpoint already applies this boundary; using the same boundary for the initial HTML options keeps the progressive-enhancement path from leaking rows the user cannot reach through that endpoint.



39
40
41
42
43
# File 'lib/cafe_car/form_builder.rb', line 39

def association_collection(info)
  klass = info.reflection.klass
  scope = association_accessible?(info) ? @template.policy_scope(klass) : klass.none
  info.collection(scope)
end

#enum(method, choices = nil, **options) ⇒ Object

An ActiveRecord enum renders as a plain for Tom Select enhancement (see cafe_car.js). When the associated model exposes an options typeahead feed, its URL rides along so keystroke search can reach records past max_collection_options; without it the field degrades to a plain select.



53
54
55
56
57
58
# File 'lib/cafe_car/form_builder.rb', line 53

def searchable_select(info)
  data = { "searchable-select": "" }
  url  = options_url(info.reflection&.klass)
  data["searchable-select-url"] = url if url
  { data: }
end

#showObject



14
# File 'lib/cafe_car/form_builder.rb', line 14

def show(...) = ui.Input { @template.present(@object).show(...) }

#submit(value = nil, **options) ⇒ Object



121
122
123
124
# File 'lib/cafe_car/form_builder.rb', line 121

def submit(value = nil, **options)
  options[:class] ||= ui.class(:button, :primary)
  super(value, options)
end

#with_selected(info, collection) ⇒ Object

The capped option collection, guaranteeing the currently-associated record is among the options even when it sorts past the cap — otherwise editing a record whose association is beyond max_collection_options would silently drop the value.



71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/cafe_car/form_builder.rb', line 71

def with_selected(info, collection)
  return collection unless info.reflection&.belongs_to?
  return collection unless association_accessible?(info)

  selected = object.try(info.reflection.name)
  return collection unless selected

  records = collection.to_a
  return records if records.include?(selected)

  scope = @template.policy_scope(info.reflection.klass)
  scope.where(id: selected.id).exists? ? [ selected, *records ] : records
end