Class: Autoselect::Base
- Inherits:
-
Object
- Object
- Autoselect::Base
- Includes:
- Operators
- Defined in:
- lib/autoselect/base.rb
Overview
lib/autoselect/base.rb
Base class for all model-specific autoselect implementations.
Mirrors the pattern of lib/queries/ where each model subclasses a shared base.
Generated via rails generate taxon_works:autoselect.
Usage:
class Autoselect::TaxonName::Autoselect < Autoselect::Base
...
end
Claude wrote > 50% of this class.
Direct Known Subclasses
AssertedEnvironment::Autoselect, Otu::Autoselect, TaxonName::Autoselect
Constant Summary
Constants included from Operators
Instance Attribute Summary collapse
-
#effective_term ⇒ String?
readonly
Raw search term with operators removed (in theory).
-
#level_params ⇒ Hash
readonly
Extra level-specific params passed through from the controller.
-
#operator ⇒ String?
readonly
The current operator raw search term with operators removed (in theory).
- #project_id ⇒ Integer? readonly
-
#raw_term ⇒ String?
readonly
The raw search term including any operator prefix.
-
#requested_level ⇒ String?
readonly
The level key being queried (e.g. 'fast', 'smart').
- #user_id ⇒ Integer? readonly
Instance Method Summary collapse
- #build_config ⇒ Object private
- #config_response ⇒ Object private
- #execute_level(level_instance, effective_term, operator) ⇒ Object private
- #find_level(key) ⇒ Object private
-
#format_results(records, level_instance) ⇒ Array<Hash>
private
!! This is often overrode !! Build response item hashes from an array of records.
-
#initialize(term: nil, level: nil, project_id: nil, user_id: nil, show_info: true, **kwargs) ⇒ Base
constructor
A new instance of Base.
- #level_map_keys ⇒ Object private
-
#levels ⇒ Array<Autoselect::Level>
Subclasses must override with their own level stack.
- #next_level_key(current_key) ⇒ Object private
-
#nothing_to_search? ⇒ Boolean
private
True when the term is only an operator that needs a term after it (e.g. "!2", "!n" sent before the client strips it), so no level is searched; record-list operators (!r, !b, ...) need no term.
- #record_list_operator?(operator) ⇒ Boolean private
-
#resource_path ⇒ String
Override in subclasses, e.g.
-
#response ⇒ Hash
The full response (config or term response), ready for render json:.
-
#response_values(record) ⇒ Hash
Subclasses override to specify which attribute keys to inject into the parent form.
- #term_response ⇒ Object private
Methods included from Operators
Constructor Details
#initialize(term: nil, level: nil, project_id: nil, user_id: nil, show_info: true, **kwargs) ⇒ Base
Returns a new instance of Base.
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/autoselect/base.rb', line 43 def initialize( term: nil, level: nil, project_id: nil, user_id: nil, show_info: true, **kwargs ) @raw_term = term.presence @requested_level = level.presence @project_id = project_id @user_id = user_id @show_info = show_info.to_s != 'false' @level_params = kwargs end |
Instance Attribute Details
#effective_term ⇒ String? (readonly)
Returns raw search term with operators removed (in theory).
20 21 22 |
# File 'lib/autoselect/base.rb', line 20 def effective_term @effective_term end |
#level_params ⇒ Hash (readonly)
Returns extra level-specific params passed through from the controller.
35 36 37 |
# File 'lib/autoselect/base.rb', line 35 def level_params @level_params end |
#operator ⇒ String? (readonly)
Returns the current operator raw search term with operators removed (in theory).
23 24 25 |
# File 'lib/autoselect/base.rb', line 23 def operator @operator end |
#project_id ⇒ Integer? (readonly)
29 30 31 |
# File 'lib/autoselect/base.rb', line 29 def project_id @project_id end |
#raw_term ⇒ String? (readonly)
Returns the raw search term including any operator prefix.
17 18 19 |
# File 'lib/autoselect/base.rb', line 17 def raw_term @raw_term end |
#requested_level ⇒ String? (readonly)
Returns the level key being queried (e.g. 'fast', 'smart').
26 27 28 |
# File 'lib/autoselect/base.rb', line 26 def requested_level @requested_level end |
#user_id ⇒ Integer? (readonly)
32 33 34 |
# File 'lib/autoselect/base.rb', line 32 def user_id @user_id end |
Instance Method Details
#build_config ⇒ Object (private)
119 120 121 122 123 124 125 126 127 |
# File 'lib/autoselect/base.rb', line 119 def build_config { resource: resource_path, levels: levels.map(&:metadata), operators: self.class.operator_definitions, map: level_map_keys, user_preferences: {} } end |
#config_response ⇒ Object (private)
85 86 87 88 89 90 91 92 93 94 |
# File 'lib/autoselect/base.rb', line 85 def config_response Autoselect::Response.new( config: build_config, request: nil, level: nil, results: nil, next_level: nil, level_map: level_map_keys ).as_json end |
#execute_level(level_instance, effective_term, operator) ⇒ Object (private)
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/autoselect/base.rb', line 144 def execute_level(level_instance, effective_term, operator) return [] if nothing_to_search? # Record-list operators are disabled on external levels (e.g. CoL) return [] if level_instance.external? && record_list_operator?(operator) if record_list_operator?(operator) # Always route to the Smart level regardless of the client's active level, # so !b/!! work even when the user is on the Fast level (the default). smart = levels.find { |l| l.is_a?(::Autoselect::Levels::Smart) } target = smart || level_instance return target.call(term: effective_term, operator:, project_id:, user_id:, **level_params) end level_instance.call( term: effective_term, operator:, project_id:, user_id:, **level_params ) end |
#find_level(key) ⇒ Object (private)
133 134 135 |
# File 'lib/autoselect/base.rb', line 133 def find_level(key) levels.find { |l| l.key.to_s == key.to_s } || levels.first end |
#format_results(records, level_instance) ⇒ Array<Hash> (private)
!! This is often overrode !! Build response item hashes from an array of records. Rendering is delegated to level_instance so external levels can supply their own. Subclasses may override to handle extension data (e.g. CoL pseudo-records).
186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/autoselect/base.rb', line 186 def format_results(records, level_instance) records.map do |record| { id: record.id, global_id: record.respond_to?(:to_global_id) ? record.to_global_id.to_s : nil, label: level_instance.record_label(record), label_html: level_instance.record_label_html(record), info_html: @show_info ? level_instance.record_info_html(record) : '', response_values: response_values(record), extension: {} } end end |
#level_map_keys ⇒ Object (private)
129 130 131 |
# File 'lib/autoselect/base.rb', line 129 def level_map_keys levels.map { |l| l.key.to_s } end |
#levels ⇒ Array<Autoselect::Level>
Subclasses must override with their own level stack.
66 67 68 |
# File 'lib/autoselect/base.rb', line 66 def levels raise NotImplementedError, "#{self.class} must implement #levels" end |
#next_level_key(current_key) ⇒ Object (private)
137 138 139 140 141 142 |
# File 'lib/autoselect/base.rb', line 137 def next_level_key(current_key) map = level_map_keys idx = map.index(current_key.to_s) return nil if idx.nil? || idx >= map.length - 1 map[idx + 1] end |
#nothing_to_search? ⇒ Boolean (private)
Returns true when the term is only an operator that needs a term after it (e.g. "!2", "!n" sent before the client strips it), so no level is searched; record-list operators (!r, !b, ...) need no term.
171 172 173 |
# File 'lib/autoselect/base.rb', line 171 def nothing_to_search? effective_term.blank? && !record_list_operator?(operator) end |
#record_list_operator?(operator) ⇒ Boolean (private)
175 176 177 |
# File 'lib/autoselect/base.rb', line 175 def record_list_operator?(operator) %i[recent recent_mine pinboard pinboard_top].include?(operator) end |
#resource_path ⇒ String
Override in subclasses, e.g. '/taxon_names/autoselect'
79 80 81 |
# File 'lib/autoselect/base.rb', line 79 def resource_path raise NotImplementedError, "#{self.class} must implement #resource_path" end |
#response ⇒ Hash
Returns the full response (config or term response), ready for render json:.
56 57 58 59 60 61 62 |
# File 'lib/autoselect/base.rb', line 56 def response if raw_term.blank? config_response else term_response end end |
#response_values(record) ⇒ Hash
Subclasses override to specify which attribute keys to inject into the parent form.
73 74 75 |
# File 'lib/autoselect/base.rb', line 73 def response_values(record) raise NotImplementedError, "#{self.class} must implement #response_values(record)" end |
#term_response ⇒ Object (private)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/autoselect/base.rb', line 96 def term_response parsed = parse_operators(raw_term) @operator = parsed[:operator] @effective_term = parsed[:effective_term] level_instance = find_level(requested_level) results = execute_level(level_instance, effective_term, operator) formatted = format_results(results, level_instance) suppress_escalation = %i[pinboard pinboard_top].include?(operator) || nothing_to_search? Autoselect::Response.new( config: nil, request: { term: raw_term, level: requested_level, project_id:, operator: operator&.to_s }, level: requested_level, results: formatted, next_level: (!suppress_escalation && formatted.empty?) ? next_level_key(requested_level) : nil, level_map: nil ).as_json end |