Module: Plutonium::Resource::Controllers::Typeahead
- Extended by:
- ActiveSupport::Concern
- Included in:
- Plutonium::Resource::Controller
- Defined in:
- lib/plutonium/resource/controllers/typeahead.rb
Overview
Backend dispatch for typeahead/autocomplete queries against resource form inputs and index filter inputs. Auto-mounted on every Plutonium resource via the ‘interactive_resource_actions` routing concern (see Plutonium::Routing::MapperExtensions).
The controller resolves what to query directly from the input definition + the model’s association reflection — no widget indirection. Two source kinds are supported:
1. Static `choices: [...]` — case-insensitive substring filter.
2. Association — either `association_class:` set on the input,
or inferred from `resource_class.reflect_on_association(name)`.
Association queries route through the associated resource’s ‘policy.relation_scope` so users only see records they can read.
Constant Summary collapse
- TYPEAHEAD_LIMIT =
50- FALLBACK_SEARCH_COLUMNS =
Priority list tried when the input doesn’t tell us which column carries its label. Aligns with what ‘to_label` usually wraps. Used only as a last resort.
%w[name title label slug display_name email].freeze
- LIKE_ESCAPE_CHAR =
Escapes the SQL LIKE wildcards ‘%` and `_` (plus the escape char itself) so a user searching for “100%” doesn’t match everything. The literal ‘!` is used as the ESCAPE character —unambiguous across sqlite/postgres/mysql, no backslash-quoting surprises.
"!"
Class Method Summary collapse
- .escape_like(value) ⇒ Object
-
.searchable_column_for(klass, label_method: nil) ⇒ Object
Returns the column to LIKE against when no ‘search` block is defined.
Instance Method Summary collapse
-
#typeahead_filter ⇒ Object
GET /<resource>/typeahead/filter/:name?q=…
-
#typeahead_input ⇒ Object
GET /<resource>/typeahead/input/:name?q=…
Class Method Details
.escape_like(value) ⇒ Object
60 61 62 |
# File 'lib/plutonium/resource/controllers/typeahead.rb', line 60 def self.escape_like(value) value.to_s.gsub(/[!%_]/) { |c| "#{LIKE_ESCAPE_CHAR}#{c}" } end |
.searchable_column_for(klass, label_method: nil) ⇒ Object
Returns the column to LIKE against when no ‘search` block is defined. Used by both the server (to build the WHERE clause) and the input component (to decide whether to attach the typeahead URL).
Resolution order:
-
The input’s ‘label_method` if it names a real column (so `input :user, label_method: :email` just works).
-
The first match from FALLBACK_SEARCH_COLUMNS.
-
nil — no usable column, server returns unfiltered.
The fallback is fine for moderate tables but uses a leading- wildcard LIKE which can’t be served by a b-tree index. For large tables, declare a ‘search` block that uses a trigram or full-text index instead.
46 47 48 49 50 51 52 |
# File 'lib/plutonium/resource/controllers/typeahead.rb', line 46 def self.searchable_column_for(klass, label_method: nil) cols = klass.column_names if label_method && cols.include?(label_method.to_s) return label_method.to_s end FALLBACK_SEARCH_COLUMNS.find { |c| cols.include?(c) } end |
Instance Method Details
#typeahead_filter ⇒ Object
GET /<resource>/typeahead/filter/:name?q=…
87 88 89 90 91 92 93 94 95 |
# File 'lib/plutonium/resource/controllers/typeahead.rb', line 87 def typeahead_filter filter = current_query_object.filter_definitions[params[:name].to_sym] return head(:not_found) unless filter defn = filter.defined_inputs[:value] return head(:not_found) unless defn render_typeahead_response(defn, params[:name].to_sym) end |
#typeahead_input ⇒ Object
GET /<resource>/typeahead/input/:name?q=…
73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/plutonium/resource/controllers/typeahead.rb', line 73 def typeahead_input field_name = params[:name].to_sym defn = current_definition.defined_inputs[field_name] # Inputs are often inferred from the model (no explicit # `input :foo` in the definition). Accept the request when the # field name maps to a real association even without an entry. unless defn || resource_class.reflect_on_association(field_name) return head(:not_found) end render_typeahead_response(defn || {}, field_name) end |