structured_search 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d44d9c424f6c8016aa9f87f520eb13fa78597259
4
+ data.tar.gz: efcd6f229905161abdccde0eb5740c94baefdbcf
5
+ SHA512:
6
+ metadata.gz: ca7d8fde1e85b7cd55d862b98f90142cb27d2914b464ccd1d5692f3fa2f43df31e6372c7f69048581c964a3f1f851df90a88762c2261465e4b089d593c5ec852
7
+ data.tar.gz: 7640c8320709de35fe5e4c92b017e68280214f6b02cc22f522863c4dbdec49178c2755dd36b6d287a477d0c1171731b00ee070277a8eaa8768bd08b7f525678a
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ = StructuredSearch
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'StructuredSearch'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ # route :default to :spec for travis ci
18
+ require 'rspec/core/rake_task'
19
+ task :default => :spec
20
+ RSpec::Core::RakeTask.new
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,25 @@
1
+ require 'structured_search/lexer'
2
+ require 'structured_search/parser'
3
+ require 'structured_search/evaluator'
4
+ require 'structured_search/errors'
5
+ require 'structured_search/patterns'
6
+ require 'structured_search/token'
7
+ require 'structured_search/base_node'
8
+
9
+ module StructuredSearch
10
+ class << self
11
+
12
+ # Creates an evaluator instance, with a given input and provider hash
13
+ # and returns the evaluator result.
14
+ # Params:
15
+ # +input+:: Input string to parse and evaluate.
16
+ # +providers+:: The search provider keys and classes.
17
+ def evaluate(input, providers)
18
+ parser = StructuredSearch::Parser.new(input, providers)
19
+ parser.parse_to_end
20
+ @evaluator = StructuredSearch::Evaluator.new(parser.statements)
21
+ @evaluator.eval if @evaluator
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ # The base of any node in the AST, it stores the line,
5
+ # column and type of token.
6
+ class BaseNode
7
+
8
+ #+line+:: The line this AST node was found
9
+ #+column+:: The column this AST node was found
10
+ #+type+:: Holds the type of node for fast lookup
11
+ attr_accessor :line, :column, :type
12
+
13
+ # sends each token value to it's respective attribute
14
+ def initialize(topts = {})
15
+ topts.each { |key, val| send "#{key}=", val }
16
+ end
17
+ end
18
+
19
+ # require all parse tree files
20
+ Dir[File.expand_path("../tree/*", __FILE__)].each { |f| require f }
21
+
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module StructuredSearch
2
+
3
+ # Raised when an error occurs during lexical analysis,
4
+ # such as an incorrect character in the input
5
+ class LexicalError < StandardError
6
+ end
7
+
8
+ # Raised when an error occurs during parsing, such as
9
+ # an invalid token in the stream (FROM before a SELECT etc.)
10
+ class SyntaxError < StandardError
11
+ end
12
+
13
+ # Raised when a provider is referenced, but no provider
14
+ # class is given to the evaluator
15
+ class UnregisteredProviderError < StandardError
16
+ end
17
+
18
+ # Raised when a provider does not have a method required
19
+ # by StructuredSearch (e.g. doesn't respond to search() etc.)
20
+ class ProviderNotAvailableError < StandardError
21
+ end
22
+ end
@@ -0,0 +1,89 @@
1
+ require 'structured_search/errors'
2
+
3
+ module StructuredSearch
4
+
5
+ # Evaluates one or more statements that make up a query.
6
+ # Each statement is made up of a series of AST nodes produced
7
+ # by the parser, from input generated by the lexer.
8
+ class Evaluator
9
+
10
+ # statements to be evaluated
11
+ attr_accessor :statements
12
+
13
+ #:nodoc:
14
+ def initialize(statements = [])
15
+ @statements = statements
16
+ end
17
+
18
+ # Evaluate every statement given to the initializer.
19
+ # Returns:
20
+ # +result+:: An array containing the result of each statement
21
+ def eval
22
+ results = []
23
+
24
+ @statements.each do |s|
25
+ st_res = eval_statement(s)
26
+ results << st_res if st_res
27
+ end
28
+ results
29
+ end
30
+
31
+ # Evaluates a single statement.
32
+ # Returns:
33
+ # +result+:: An array containing the result of the statement
34
+ def eval_statement(statement)
35
+ search_terms = results = []
36
+
37
+ statement.each do |node|
38
+
39
+ case node.type
40
+ when :SELECT
41
+ search_terms = node.search_terms
42
+ when :FROM
43
+ results = provider_lookup(node, search_terms)
44
+ when :WHERE
45
+ node.constraints.each do
46
+ # results = send :method, results (or something like that)
47
+ end
48
+ end
49
+ end
50
+
51
+ results
52
+ end
53
+
54
+ private
55
+
56
+ # Performs a search operation, looking up each search term
57
+ # on each provider (by way of Provider#search), and storing it in a Hash.
58
+ # Parameters:
59
+ # +node+:: The FROM node that contains search provider sources
60
+ # +search_terms+:: The search terms from a SELECT node, as an array
61
+ # Returns:
62
+ # +provider_results+::A Hash that contains the provider as a key, and the
63
+ # search term as a value, which can be used to lookup the result via
64
+ # 'result[:provider][:search_term]'.
65
+ def provider_lookup(node, search_terms)
66
+ provider_results = {}
67
+ search_results = {}
68
+
69
+ node.sources.each do |provider, prov_class|
70
+ if prov_class.respond_to? :search
71
+ search_terms.each do |search_term|
72
+ search_result = prov_class.send :search, search_term
73
+ search_results[search_term] = search_result if search_result
74
+ end
75
+
76
+ provider_results[provider] = search_results
77
+
78
+ else
79
+ raise ProviderNotAvailableError, "Method 'search' not available for provider #{provider}"
80
+ end
81
+ end
82
+
83
+ provider_results
84
+ end
85
+
86
+
87
+
88
+ end
89
+ end
@@ -0,0 +1,89 @@
1
+ require 'structured_search/token'
2
+ require 'structured_search/errors'
3
+
4
+ module StructuredSearch
5
+
6
+ # Converts the input into a token stream, that can be worked
7
+ # by the syntax parser.
8
+ class Lexer
9
+
10
+ # +input+:: Input string to parse
11
+ # +column+:: Current column position
12
+ # +line+:: Current position in the line
13
+ # +lexer_offset+:: Current character position in the input string
14
+ attr_accessor :input, :column, :line, :lexer_offset
15
+
16
+ # Returns the current state of the lexer, by way of input,
17
+ # current line and column.
18
+ def state
19
+ { input: input, column: column, line: line }
20
+ end
21
+
22
+ # Sets the state of the lexer
23
+ def state=(state)
24
+ @input = state[:input]
25
+ @column = state[:column]
26
+ @line = state[:line]
27
+ end
28
+
29
+ # Creates a new instance of the Lexer.
30
+ # Params:
31
+ # +input+:: The SQL input that will be parsed.
32
+ def initialize(input)
33
+ @input = input
34
+ @column = 1
35
+ @line = 1
36
+ @lexer_offset = 0
37
+ end
38
+
39
+ # Scans the input, matching each token that appears and
40
+ # returns the token. Supports both read and peek operations
41
+ # determined by the state of the peek flag.
42
+ # Params:
43
+ # +is_peek+:: Whether the lexer will consume the token, or
44
+ # remain in it's current position (false by default)
45
+ # Returns:
46
+ # +token+:: A StructuredSeach::Token is returned to the caller.
47
+ def scan(is_peek = false)
48
+ PATTERNS.each do |pattern|
49
+ match = pattern[1].match(@input, @lexer_offset)
50
+ if match
51
+ token_data = { token: pattern[0],
52
+ lexeme: pattern[2] ? pattern[2].call(match) : '',
53
+ line: @line, column: @column }
54
+ token = Token.new(token_data)
55
+
56
+ # increment line and col position if a read op:
57
+ if !is_peek
58
+ tok_length = match[0].size
59
+ newline_count = match[0].count("\n")
60
+ @lexer_offset += tok_length
61
+ @line += newline_count
62
+ @column = 1 if newline_count
63
+ @column += tok_length - (match[0].rindex("\n") || 0)
64
+ end
65
+
66
+ # clear any whitespace
67
+ if pattern[0] == :WHITESPACE
68
+ @lexer_offset += match[0].size
69
+ return scan(is_peek)
70
+ else
71
+ return token
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ # have we underrun the input due to lex error?:
78
+ if @lexer_offset < @input.size
79
+ raise LexicalError, "Unexpected character \"#{@input[@lexer_offset+1]}\" at (Line #{@line}, Column #{@column})"
80
+ end
81
+
82
+ nil
83
+ end
84
+
85
+ end
86
+ end
87
+
88
+
89
+
@@ -0,0 +1,134 @@
1
+ module StructuredSearch
2
+
3
+ # Parses a token stream, returning an array of StructuredSearch::Statement
4
+ class Parser
5
+
6
+ # stores all parse tree nodes
7
+ attr_reader :statements, :providers
8
+
9
+ # Creates a new instance of the Parser, taking a Lexer and a hash of
10
+ # providers (An identifier and class that contains the search method)
11
+ # Params:
12
+ # +lexer+:: A StructuredSearch::Lexer object
13
+ # +providers+:: A Hash of provider names and their classes
14
+ def initialize(input, providers)
15
+ @lexer = StructuredSearch::Lexer.new(input)
16
+ @providers = Hash.new
17
+ providers.each { |k,v| @providers[k.downcase] = v }
18
+
19
+ @nodes, @statements = [], []
20
+ end
21
+
22
+ # Reads the next token in the stream
23
+ def read_token
24
+ @lexer.scan
25
+ end
26
+
27
+ # Peeks at the next token in stream
28
+ def peek_token
29
+ @lexer.scan(true)
30
+ end
31
+
32
+ # Parses the token stream into statements until there
33
+ # are no more tokens left
34
+ def parse_to_end
35
+ while peek_token
36
+ new_node = parse
37
+ @nodes << new_node if new_node
38
+ end
39
+
40
+ # flush token stream to a statement if no semicolon
41
+ new_statement if @nodes.length > 0
42
+ end
43
+
44
+ # Parses the next token in the token stream into an AST node
45
+ # Returns an AST node
46
+ def parse
47
+ @current_token = read_token
48
+
49
+ case @current_token.token
50
+ when :SEMICOLON
51
+ new_statement
52
+ else
53
+ send "new_#{@current_token.token.downcase}"
54
+ end
55
+ end
56
+
57
+ # Basic options given to BaseNode when creating a new instance
58
+ # of a node, including the line, column and type
59
+ def basic_options
60
+ { line: @current_token.line, column: @current_token.column, type: @current_token.token }
61
+ end
62
+
63
+ private
64
+
65
+ # Creates a new Tree::Select
66
+ def new_select
67
+ quant_list = [ :ALL, :DISTINCT ]
68
+ select_list = [ :ASTERISK, :STRING ]
69
+
70
+ select_tok = Tree::Select.new(basic_options)
71
+
72
+ # handle an optional set quantifier (ALL or DISTINCT)
73
+ if quant_list.include? peek_token.token
74
+ select_tok.set_quantifier = read_token.token
75
+ end
76
+
77
+ # handle a select list (ASTERISK or search terms)
78
+ if select_list.include? peek_token.token
79
+
80
+ # read in all search terms from the query:
81
+ while peek_token.token == :STRING
82
+ select_tok.add_search_term(read_token.lexeme)
83
+ read_token if peek_token.token == :COMMA
84
+ end
85
+
86
+ else
87
+ raise SyntaxError "No valid select list given (#{error_location})"
88
+ end
89
+
90
+ select_tok
91
+ end
92
+
93
+ # Creates a new Tree::From
94
+ def new_from
95
+ source_tokens = [ :STRING ]
96
+ from_tok = Tree::From.new(basic_options)
97
+
98
+ # read in all the derived columns - search sources:
99
+ while peek_token and source_tokens.include? peek_token.token
100
+ src_token = read_token
101
+ # check if the provider is registered in the given list:
102
+ if provider_exists? src_token.lexeme.downcase
103
+ from_tok.sources[src_token.lexeme] = @providers[src_token.lexeme.downcase.to_sym]
104
+ else raise UnregisteredProviderError, "#{src_token.lexeme} is not a registered provider"
105
+ end
106
+ end
107
+
108
+ if from_tok.sources.count == 0
109
+ raise SyntaxError, "No search sources given (#{error_location})"
110
+ end
111
+
112
+ from_tok
113
+ end
114
+
115
+ # Creates a new Tree::Statement
116
+ def new_statement
117
+ @statements.push Tree::Statement.new(@nodes)
118
+ @nodes = [] # reset node array
119
+ nil
120
+ end
121
+
122
+ # Checks whether a search provider class exists
123
+ def provider_exists?(source)
124
+ @providers.has_key? source.to_sym
125
+ end
126
+
127
+ # Returns a string that will inform the user where an error
128
+ # has occurred.
129
+ def error_location
130
+ "Line #{basic_options[:line]}, Column #{basic_options[:column]}"
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,45 @@
1
+ module StructuredSearch
2
+
3
+ class Lexer
4
+
5
+ # SQL reserved words list
6
+ RESERVED = %w{
7
+ SELECT ALL DISTINCT FROM WHERE ASC DESC
8
+ }
9
+
10
+ # Pattern hash of token keys and regex values
11
+ PATTERNS = [
12
+
13
+ [:WHITESPACE, /[\r\v\f\t ]+/],
14
+ [:TERMINATOR, /[\r\n]/],
15
+
16
+ # intern reserved words and their patterns
17
+ *RESERVED.map { |rw| [rw.intern, /#{rw}(?=[^A-z0-9_])/] },
18
+
19
+ # match single / double quoted strings
20
+ [:STRING, /(['"])(\\n|\\.|((?!\1)(?!\\)|.)*?((?!\1)(?!\\).)?)\1/, -> match { match[2] } ],
21
+ [:L_PAREN, /\(/],
22
+ [:R_PAREN, /\)/],
23
+ [:L_BRACKET, /\[/],
24
+ [:R_BRACKET, /\]/],
25
+ [:L_BRACE, /\{/],
26
+ [:R_BRACE, /\}/],
27
+ [:PERCENT, /%/ ],
28
+ [:AMPERSAND, /&/ ],
29
+ [:ASTERISK, /\*/],
30
+ [:PLUS, /\+/],
31
+ [:MINUS, /-/],
32
+ [:COMMA, /,/ ],
33
+ [:PERIOD, /\./],
34
+ [:COLON, /:/ ],
35
+ [:SEMICOLON, /;/ ],
36
+ [:LEQ, /<=/],
37
+ [:EQUALS, /=/ ],
38
+ [:GEQ, />=/],
39
+ [:QUESTION, /\?/],
40
+ [:CIRCUMFLEX, /\^/],
41
+ [:UNDERSCORE, /_/ ],
42
+ [:PIPE, /\|/]
43
+ ].map { |pattern| [pattern[0], /\G#{pattern[1]}/m, pattern[2]] }
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ module StructuredSearch
2
+
3
+ # A token generated by the lexer
4
+ class Token
5
+
6
+ #+token+:: The token's type
7
+ #+lexeme+:: The matched characters that make up the token
8
+ #+column+:: Column this token was located
9
+ #+line+:: Line this token was located
10
+ attr_accessor :token, :lexeme, :column, :line
11
+
12
+ #:nodoc:
13
+ def initialize(tok = {})
14
+ #send val to key setter
15
+ tok.each do |k, v|
16
+ send "#{k}=", v
17
+ end
18
+ end
19
+
20
+ # Returns a string representation of the token
21
+ def to_s
22
+ "#{@token}: '#{@lexeme}' (Line #{@line}, Column #{@column})"
23
+ end
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,16 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ # Asterisk (*) node
5
+ class Asterisk < BaseNode
6
+
7
+ #:nodoc:
8
+ def initialize(*argv)
9
+ super *argv
10
+ end
11
+
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ # FROM reserved word node
5
+ class From < BaseNode
6
+
7
+ # in sql spec, this is where we store all the
8
+ # derived columns, such as Google, Omniref etc
9
+ #
10
+ # This is a hash containing the symbol reference
11
+ # and a class reference to what generates the query
12
+ attr_accessor :sources
13
+
14
+ #:nodoc:
15
+ def initialize(*argv)
16
+ @sources = {}
17
+ super *argv
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ ##
5
+ # SELECT reserved word node
6
+ class Select < BaseNode
7
+
8
+ # +set_quantifier+:: Whether this search uses ALL or DISTINCT
9
+ # +search_terms+:: The search terms we're looking for
10
+ attr_accessor :set_quantifier, :search_terms
11
+
12
+ # Sets the set quantifier (either ALL or DISTINCT) for this statement
13
+ def set_quantifier=(quantifier)
14
+ if [:ALL, :DISTINCT].include? quantifier
15
+ @set_quantifier = quantifier
16
+ end
17
+ end
18
+
19
+ # Adds a search term to the list of terms
20
+ def add_search_term(term)
21
+ @search_terms.push term
22
+ end
23
+
24
+ #:nodoc:
25
+ def initialize(*argv)
26
+ @search_terms = []
27
+ @set_quantifier = :ALL
28
+ super *argv
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ module StructuredSearch::Tree
2
+
3
+ # A statement is a collection of nodes that make up a
4
+ # single query, such as "SELECT * FROM 'Here', 'There';"
5
+ class Statement < Array
6
+
7
+ #:nodoc:
8
+ def initialize(nodes)
9
+ self.concat(nodes)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ # Represents a string node, 'Google', 'SearchThisString' etc.
5
+ class String < BaseNode
6
+
7
+ #:nodoc:
8
+ def initialize(*argv)
9
+ super *argv
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module StructuredSearch
2
+ module Tree
3
+
4
+ # WHERE reserved word node
5
+ class Where < BaseNode
6
+
7
+ #:nodoc:
8
+ def initialize(*argv)
9
+ super *argv
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ module StructuredSearch
2
+ #:nodoc:
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :structured_search do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: structured_search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Ryder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coveralls
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-spork
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: google_custom_search_api
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Use SQL to query search engines and APIs.
126
+ email:
127
+ - matt@mattryder.co.uk
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.rdoc
134
+ - Rakefile
135
+ - lib/structured_search.rb
136
+ - lib/structured_search/base_node.rb
137
+ - lib/structured_search/errors.rb
138
+ - lib/structured_search/evaluator.rb
139
+ - lib/structured_search/lexer.rb
140
+ - lib/structured_search/parser.rb
141
+ - lib/structured_search/patterns.rb
142
+ - lib/structured_search/token.rb
143
+ - lib/structured_search/tree/asterisk.rb
144
+ - lib/structured_search/tree/from.rb
145
+ - lib/structured_search/tree/select.rb
146
+ - lib/structured_search/tree/statement.rb
147
+ - lib/structured_search/tree/string.rb
148
+ - lib/structured_search/tree/where.rb
149
+ - lib/structured_search/version.rb
150
+ - lib/tasks/structured_search_tasks.rake
151
+ homepage: http://www.mattryder.co.uk
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.4.7
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Use SQL to query search engines and APIs.
175
+ test_files: []