Class: SearchLingo::AbstractSearch

Inherits:
Object
  • Object
show all
Defined in:
lib/search_lingo/abstract_search.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query, scope) ⇒ AbstractSearch

Instantiates a new search object. query is the string that is to be parsed and compiled into an actual query. If query is falsey, an empty string will be used. scope is the object to which the compiled query should be sent, e.g., an ActiveRecord model.



12
13
14
15
# File 'lib/search_lingo/abstract_search.rb', line 12

def initialize(query, scope)
  @query = query || ''
  @scope = scope
end

Instance Attribute Details

#queryObject (readonly)

Returns the value of attribute query.



5
6
7
# File 'lib/search_lingo/abstract_search.rb', line 5

def query
  @query
end

Class Method Details

.parser(parser = nil, &block) ⇒ Object

Adds a new parser to the list of parsers used by this class.

The parser may be given as an anonymous block or as any argument which responds to #call. The parser will be send #call with a single argument which will be a token from the query string.

If both a callable object and a block are given, or if neither a callable object nor a block are given, an ArgumentError will be raised.

class MyParser
  def call(token)
    # return something
  end
end

class MySearch < SearchLingo::AbstractSearch
  parser MyParser.new
  parser do |token|
    # return something
  end
end


45
46
47
48
49
50
# File 'lib/search_lingo/abstract_search.rb', line 45

def self.parser(parser = nil, &block)
  unless block_given? ^ parser.respond_to?(:call)
    raise ArgumentError, 'parse must be called with callable OR block'
  end
  parsers << (parser || block)
end

.parsersObject

Returns an list of parsers that have been added to this class.



19
20
21
# File 'lib/search_lingo/abstract_search.rb', line 19

def self.parsers
  @parsers ||= []
end

Instance Method Details

#conditionsObject

Returns an Array of compiled query parameters.

list of defined parsers. If a parser is successful, :match is thrown, the compiled condition is saved, and processing moves on to the next token. If none of the parsers succeeds and the token is compound, that is, it has both a modifier and a term, the token is simplified, and reprocessed through the list of parsers. As during the first pass, if a parser succeeds, :match is thrown, the compiled condition for the now simplified token is saved, and processing moves on to the next token (the remains of the original compound token). If none of the parsers succeeds during the second pass, the now simplified token is finally sent to #default_parse, and whatever it returns will be saved as the compiled condition.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/search_lingo/abstract_search.rb', line 87

def conditions
  tokenizer.inject([]) do |conditions, token|
    conditions << catch(:match) do
      # 1. Try each parser with the token until :match is thrown.
      parse token

      # 2. If :match not thrown and token is compound, simplify and try again.
      if token.compound?
        token = tokenizer.simplify
        parse token
      end

      # 3. If :match still not thrown, fallback on default parser.
      default_parse token
    end
  end
end

#default_parse(token) ⇒ Object

Raises NotImplementedError. Classes which inherit from SearchLingo::AbstractSearch must provide their own implementation, and it should always succeed.

Raises:

  • (NotImplementedError)


130
131
132
133
# File 'lib/search_lingo/abstract_search.rb', line 130

def default_parse(token)
  raise NotImplementedError,
    "#default_parse must be implemented by #{self.class}"
end

#load_resultsObject

Constructs and performs the query.



66
67
68
69
70
# File 'lib/search_lingo/abstract_search.rb', line 66

def load_results
  conditions.inject(scope) do |query, condition|
    query.public_send(*condition)
  end
end

#parse(token) ⇒ Object

Passes token to each parser in turn. If a parser succeeds, throws :match with the compiled result.

A parser succeeds if call returns a truthy value. The return value of a successful parser will be splatted and sent to @scope using public_send.



118
119
120
121
122
123
124
# File 'lib/search_lingo/abstract_search.rb', line 118

def parse(token)
  parsers.each do |parser|
    result = parser.call token
    throw :match, result if result
  end
  nil
end

#parsersObject

Delegates to SearchLingo::AbstractSearch.parsers.



54
55
56
# File 'lib/search_lingo/abstract_search.rb', line 54

def parsers
  self.class.parsers
end

#resultsObject

Returns the results of executing the search.



60
61
62
# File 'lib/search_lingo/abstract_search.rb', line 60

def results
  @results ||= load_results
end

#scopeObject

Returns @scope.

You may override this method in your search class if you want to ensure additional messages are sent to search scope before executing the query. For example, if @scope is an ActiveRecord model, you might want to join additional tables.



142
143
144
# File 'lib/search_lingo/abstract_search.rb', line 142

def scope
  @scope
end

#tokenizerObject

Returns a SearchLingo::Tokenizer for @query.



107
108
109
# File 'lib/search_lingo/abstract_search.rb', line 107

def tokenizer
  @tokenizer ||= Tokenizer.new query
end