Class: ScopedSearch::Definition
- Inherits:
-
Object
- Object
- ScopedSearch::Definition
- Defined in:
- lib/scoped_search/definition.rb
Overview
The ScopedSearch definition class defines on what fields should be search in the model in what cases
A definition can be created by calling the scoped_search method on an ActiveRecord-based class, so you should not create an instance of this class yourself.
Defined Under Namespace
Classes: Field
Constant Summary collapse
- NUMERICAL_REGXP =
/^\-?\d+(\.\d+)?$/- INTEGER_REGXP =
/^\-?\d+$/- UUID_REGXP =
/^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i
Instance Attribute Summary collapse
-
#default_order ⇒ Object
Returns the value of attribute default_order.
-
#klass ⇒ Object
readonly
Returns the value of attribute klass.
-
#profile ⇒ Object
Returns the value of attribute profile.
Instance Method Summary collapse
-
#date_with_timezone(datetime) ⇒ Object
This should get called only when the relative specifiers (1 day ago and so on), this code path is only viable when Rails are available.
-
#default_fields ⇒ Object
Returns a list of fields that should be searched on by default.
-
#default_fields_for(value, operator = nil) ⇒ Object
Returns a list of appropriate fields to search in given a search keyword and operator.
-
#define(*args, **kwargs) ⇒ Object
Defines a new search field for this search definition.
- #define_field(name, field) ⇒ Object
-
#field_by_name(name) ⇒ Object
this method return definitions::field object from string.
- #fields ⇒ Object
-
#initialize(klass) ⇒ Definition
constructor
Initializes a ScopedSearch definition instance.
-
#operator_by_field_name(name) ⇒ Object
this method is used by the syntax auto completer to suggest operators.
-
#parse_temporal(value) ⇒ Object
Try to parse a string as a datetime.
-
#reflection_by_name(klass, name) ⇒ Object
Returns a reflection for a given klass and name.
- #super_definition ⇒ Object
- #unique_fields ⇒ Object
Constructor Details
#initialize(klass) ⇒ Definition
Initializes a ScopedSearch definition instance. This method will also setup a database adapter and create the :search_for named scope if it does not yet exist.
197 198 199 200 201 202 203 204 205 206 |
# File 'lib/scoped_search/definition.rb', line 197 def initialize(klass) @klass = klass @fields = {} @unique_fields = [] @profile_fields = {:default => {}} @profile_unique_fields = {:default => []} register_named_scope! unless klass.respond_to?(:search_for) register_complete_for! unless klass.respond_to?(:complete_for) end |
Instance Attribute Details
#default_order ⇒ Object
Returns the value of attribute default_order.
208 209 210 |
# File 'lib/scoped_search/definition.rb', line 208 def default_order @default_order end |
#klass ⇒ Object (readonly)
Returns the value of attribute klass.
192 193 194 |
# File 'lib/scoped_search/definition.rb', line 192 def klass @klass end |
#profile ⇒ Object
Returns the value of attribute profile.
208 209 210 |
# File 'lib/scoped_search/definition.rb', line 208 def profile @profile end |
Instance Method Details
#date_with_timezone(datetime) ⇒ Object
This should get called only when the relative specifiers (1 day ago and so on), this code path is only viable when Rails are available
340 341 342 343 344 |
# File 'lib/scoped_search/definition.rb', line 340 def date_with_timezone(datetime) date = datetime.to_date return datetime unless date.respond_to?(:in_time_zone) date.in_time_zone.to_datetime end |
#default_fields ⇒ Object
Returns a list of fields that should be searched on by default.
Every field will show up in this method's result, except for fields for which the only_explicit parameter is set to true.
303 304 305 |
# File 'lib/scoped_search/definition.rb', line 303 def default_fields unique_fields.reject { |field| field.only_explicit } end |
#default_fields_for(value, operator = nil) ⇒ Object
Returns a list of appropriate fields to search in given a search keyword and operator.
266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/scoped_search/definition.rb', line 266 def default_fields_for(value, operator = nil) column_types = [:virtual] column_types += [:string, :text, :citext] if [nil, :like, :unlike, :ne, :eq].include?(operator) column_types += [:double, :float, :decimal] if value =~ NUMERICAL_REGXP column_types += [:integer] if value =~ INTEGER_REGXP column_types += [:uuid] if value =~ UUID_REGXP column_types += [:datetime, :date, :timestamp] if (parse_temporal(value)) default_fields.select { |field| !field.set? && column_types.include?(field.type) } end |
#define(*args, **kwargs) ⇒ Object
Defines a new search field for this search definition.
308 309 310 |
# File 'lib/scoped_search/definition.rb', line 308 def define(*args, **kwargs) Field.new(self, *args, **kwargs) end |
#define_field(name, field) ⇒ Object
214 215 216 217 218 219 220 221 |
# File 'lib/scoped_search/definition.rb', line 214 def define_field(name, field) @profile ||= :default @profile_fields[@profile] ||= {} @profile_fields[@profile][name.to_sym] ||= field @profile_unique_fields[@profile] ||= [] @profile_unique_fields[@profile] = (@profile_unique_fields[@profile] + [field]).uniq field end |
#field_by_name(name) ⇒ Object
this method return definitions::field object from string
236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/scoped_search/definition.rb', line 236 def field_by_name(name) field = fields[name.to_sym] unless name.blank? if field.nil? dotted = name.to_s.split('.')[0] field = fields[dotted.to_sym] unless dotted.blank? if field && field.key_relation.nil? return nil end end field end |
#fields ⇒ Object
223 224 225 226 227 |
# File 'lib/scoped_search/definition.rb', line 223 def fields @profile ||= :default @profile_fields[@profile] ||= {} super_definition ? super_definition.fields.merge(@profile_fields[@profile]) : @profile_fields[@profile] end |
#operator_by_field_name(name) ⇒ Object
this method is used by the syntax auto completer to suggest operators.
249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/scoped_search/definition.rb', line 249 def operator_by_field_name(name) field = field_by_name(name) return [] if field.nil? return field.operators if field.operators return ['=', '!=', '>', '<', '<=', '>=', '~', '!~', '^', '!^'] if field.virtual? return ['=', '!='] if field.set? || field.uuid? return ['=', '>', '<', '<=', '>=', '!=', '^', '!^'] if field.numerical? return ['=', '!=', '~', '!~', '^', '!^'] if field.textual? return ['=', '>', '<'] if field.temporal? raise ScopedSearch::QueryNotSupported, "Unsupported type '#{field.type.inspect}')' for field '#{name}'. This can be a result of a search definition problem." end |
#parse_temporal(value) ⇒ Object
Try to parse a string as a datetime. Supported formats are Today, Yesterday, Sunday, '1 day ago', '2 hours ago', '3 months ago', '4 weeks from now', 'Jan 23, 2004' And many more formats that are documented in Ruby DateTime API Doc. In case Time responds to #zone, we know this is Rails environment and we can use Time.zone.parse. The benefit is that the current timezone is respected and does not have to be specified explicitly. That way even relative dates work as expected.
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/scoped_search/definition.rb', line 283 def parse_temporal(value) return date_with_timezone(Date.current) if value =~ /\btoday\b/i return date_with_timezone(1.day.ago) if value =~ /\byesterday\b/i return date_with_timezone(1.day.from_now) if value =~ /\btomorrow\b/i return (eval($1.strip.gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bago)\b\s*\z/i return date_with_timezone(eval($1.strip.gsub(/\s+/,'.').downcase)) if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bago)\b\s*\z/i return (eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bfrom\s+now)\b\s*\z/i return date_with_timezone(eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)) if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bfrom\s+now)\b\s*\z/i if Time.respond_to?(:zone) && !Time.zone.nil? parsed = Time.zone.parse(value) rescue nil parsed && parsed.to_datetime else DateTime.parse(value, true) rescue nil end end |
#reflection_by_name(klass, name) ⇒ Object
Returns a reflection for a given klass and name
313 314 315 316 |
# File 'lib/scoped_search/definition.rb', line 313 def reflection_by_name(klass, name) return if name.nil? klass.reflections[name.to_sym] || klass.reflections[name.to_s] end |
#super_definition ⇒ Object
210 211 212 |
# File 'lib/scoped_search/definition.rb', line 210 def super_definition klass.superclass.try(:scoped_search_definition) end |
#unique_fields ⇒ Object
229 230 231 232 233 |
# File 'lib/scoped_search/definition.rb', line 229 def unique_fields @profile ||= :default @profile_unique_fields[@profile] ||= [] super_definition ? (super_definition.unique_fields + @profile_unique_fields[@profile]).uniq : @profile_unique_fields[@profile] end |