Class: CafeCar::Filter::FormBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/cafe_car/filter/form_builder.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.dotted_name(method) ⇒ Object

Rewrites a control's name to its bare dot-query key — price stays price, operators compose as price.min — instead of the edit form's model[price] nesting, so a submitted control IS a filter param (Controller::Filtering treats every non-control index param as a filter).



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

def self.dotted_name(method)
  define_method method do |key, *args, **opts, &block|
    super(key, *args, name: field_name(key), **opts, &block)
  end
end

Instance Method Details

#clean(method) ⇒ Object



32
# File 'lib/cafe_car/filter/form_builder.rb', line 32

def clean(method)        = method.to_s.sub(/^\W+|\W+$/, "")

#collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) ⇒ Object



21
22
23
24
25
26
27
28
# File 'lib/cafe_car/filter/form_builder.rb', line 21

def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
  # A multi-select posts a set (`author_id[]`). We name every filter control
  # explicitly, which defeats Rails' automatic `[]` suffix, so restore it here.
  name  = field_name(clean(method))
  name += "[]" if html_options[:multiple]
  super(method, collection, value_method, text_method, options,
        { name: }.merge(html_options))
end

#field_name(*methods) ⇒ Object



33
# File 'lib/cafe_car/filter/form_builder.rb', line 33

def field_name(*methods) = methods.join(".")

#filter(method, **opts) ⇒ Object

The typed control for one permitted filter: the _<type>_filter partial (host-overridable per type), _range_filter for the min/max family, then the generic _filter fallback.



59
60
61
62
63
64
# File 'lib/cafe_car/filter/form_builder.rb', line 59

def filter(method, **opts)
  info    = info(method)
  partial = [ "#{info.type}_filter", ("range_filter" if info.range?), "filter" ]
              .compact.find { @template.partial?(_1) }
  @template.render(partial, f: self, info:, **opts)
end

#info(method) ⇒ Object

Filter fields reflect through Filter::FieldInfo (the typed filter control mapping), keyed on the cleaned name so name~ and name share one info.



37
38
39
40
# File 'lib/cafe_car/filter/form_builder.rb', line 37

def info(method)
  @infos ||= {}
  @infos[clean(method)] ||= const(:FieldInfo).new(model:, method: clean(method))
end

#label(method, text = nil, **opts) ⇒ Object

A filter is always optional — no required-* marker on its label.



51
52
53
54
# File 'lib/cafe_car/filter/form_builder.rb', line 51

def label(method, text = nil, **opts, &)
  opts[:class] = [ ui.class(%i[field label]), *opts[:class] ]
  super(method, *text, required: false, **opts, &)
end

#modelObject



30
# File 'lib/cafe_car/filter/form_builder.rb', line 30

def model = object.model

#remaining_attributesObject

The policy's permitted_filters — the panel's enumeration source — minus what the view already rendered and the types no control can express.



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

def remaining_attributes
  policy.attributes.filterable.reject { info(_1).unfilterable? } - @fields.keys
end

#scope_toggle(scope) ⇒ Object

A checkbox that turns a permitted model scope on (?published=true).



67
68
69
# File 'lib/cafe_car/filter/form_builder.rb', line 67

def scope_toggle(scope)
  @template.render("scope_filter", f: self, scope:)
end

#scopesObject



48
# File 'lib/cafe_car/filter/form_builder.rb', line 48

def scopes = policy.permitted_scopes

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

The select family takes (options, html_options) hash pairs positionally, so the name rewrite rides in html_options (an explicit :name still wins).



17
18
19
# File 'lib/cafe_car/filter/form_builder.rb', line 17

def select(method, choices = nil, options = {}, html_options = {}, &block)
  super(method, choices, options, { name: field_name(clean(method)) }.merge(html_options), &block)
end