structured_search 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []