wherewolf 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +140 -0
- data/Guardfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +72 -0
- data/Rakefile +47 -0
- data/lib/wherewolf/parser.rb +53 -0
- data/lib/wherewolf/processor.rb +66 -0
- data/lib/wherewolf/railtie.rb +9 -0
- data/lib/wherewolf.rb +22 -0
- data/test/helper.rb +62 -0
- data/test/parser_test.rb +214 -0
- data/test/processor_test.rb +102 -0
- data/test/railtie_test.rb +13 -0
- data/wherewolf.gemspec +88 -0
- metadata +200 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "arel"
|
4
|
+
gem "parslet"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "shoulda"
|
8
|
+
gem "rdoc"
|
9
|
+
gem "bundler"
|
10
|
+
gem "jeweler"
|
11
|
+
gem "guard"
|
12
|
+
gem "guard-test"
|
13
|
+
gem "simplecov", :require => false
|
14
|
+
gem "sqlite3"
|
15
|
+
gem "rails"
|
16
|
+
gem "wherewolf", :path => "./"
|
17
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ./
|
3
|
+
specs:
|
4
|
+
wherewolf (0.1.0)
|
5
|
+
arel
|
6
|
+
parslet
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionmailer (3.2.8)
|
12
|
+
actionpack (= 3.2.8)
|
13
|
+
mail (~> 2.4.4)
|
14
|
+
actionpack (3.2.8)
|
15
|
+
activemodel (= 3.2.8)
|
16
|
+
activesupport (= 3.2.8)
|
17
|
+
builder (~> 3.0.0)
|
18
|
+
erubis (~> 2.7.0)
|
19
|
+
journey (~> 1.0.4)
|
20
|
+
rack (~> 1.4.0)
|
21
|
+
rack-cache (~> 1.2)
|
22
|
+
rack-test (~> 0.6.1)
|
23
|
+
sprockets (~> 2.1.3)
|
24
|
+
activemodel (3.2.8)
|
25
|
+
activesupport (= 3.2.8)
|
26
|
+
builder (~> 3.0.0)
|
27
|
+
activerecord (3.2.8)
|
28
|
+
activemodel (= 3.2.8)
|
29
|
+
activesupport (= 3.2.8)
|
30
|
+
arel (~> 3.0.2)
|
31
|
+
tzinfo (~> 0.3.29)
|
32
|
+
activeresource (3.2.8)
|
33
|
+
activemodel (= 3.2.8)
|
34
|
+
activesupport (= 3.2.8)
|
35
|
+
activesupport (3.2.8)
|
36
|
+
i18n (~> 0.6)
|
37
|
+
multi_json (~> 1.0)
|
38
|
+
arel (3.0.2)
|
39
|
+
blankslate (2.1.2.4)
|
40
|
+
builder (3.0.3)
|
41
|
+
erubis (2.7.0)
|
42
|
+
ffi (1.1.5)
|
43
|
+
git (1.2.5)
|
44
|
+
guard (1.3.2)
|
45
|
+
listen (>= 0.4.2)
|
46
|
+
thor (>= 0.14.6)
|
47
|
+
guard-test (0.5.0)
|
48
|
+
guard (>= 1.1.0)
|
49
|
+
test-unit (~> 2.2)
|
50
|
+
hike (1.2.1)
|
51
|
+
i18n (0.6.1)
|
52
|
+
jeweler (1.8.4)
|
53
|
+
bundler (~> 1.0)
|
54
|
+
git (>= 1.2.5)
|
55
|
+
rake
|
56
|
+
rdoc
|
57
|
+
journey (1.0.4)
|
58
|
+
json (1.7.5)
|
59
|
+
listen (0.4.7)
|
60
|
+
rb-fchange (~> 0.0.5)
|
61
|
+
rb-fsevent (~> 0.9.1)
|
62
|
+
rb-inotify (~> 0.8.8)
|
63
|
+
mail (2.4.4)
|
64
|
+
i18n (>= 0.4.0)
|
65
|
+
mime-types (~> 1.16)
|
66
|
+
treetop (~> 1.4.8)
|
67
|
+
mime-types (1.19)
|
68
|
+
multi_json (1.3.6)
|
69
|
+
parslet (1.4.0)
|
70
|
+
blankslate (~> 2.0)
|
71
|
+
polyglot (0.3.3)
|
72
|
+
rack (1.4.1)
|
73
|
+
rack-cache (1.2)
|
74
|
+
rack (>= 0.4)
|
75
|
+
rack-ssl (1.3.2)
|
76
|
+
rack
|
77
|
+
rack-test (0.6.1)
|
78
|
+
rack (>= 1.0)
|
79
|
+
rails (3.2.8)
|
80
|
+
actionmailer (= 3.2.8)
|
81
|
+
actionpack (= 3.2.8)
|
82
|
+
activerecord (= 3.2.8)
|
83
|
+
activeresource (= 3.2.8)
|
84
|
+
activesupport (= 3.2.8)
|
85
|
+
bundler (~> 1.0)
|
86
|
+
railties (= 3.2.8)
|
87
|
+
railties (3.2.8)
|
88
|
+
actionpack (= 3.2.8)
|
89
|
+
activesupport (= 3.2.8)
|
90
|
+
rack-ssl (~> 1.3.2)
|
91
|
+
rake (>= 0.8.7)
|
92
|
+
rdoc (~> 3.4)
|
93
|
+
thor (>= 0.14.6, < 2.0)
|
94
|
+
rake (0.9.2.2)
|
95
|
+
rb-fchange (0.0.5)
|
96
|
+
ffi
|
97
|
+
rb-fsevent (0.9.1)
|
98
|
+
rb-inotify (0.8.8)
|
99
|
+
ffi (>= 0.5.0)
|
100
|
+
rdoc (3.12)
|
101
|
+
json (~> 1.4)
|
102
|
+
shoulda (3.3.0)
|
103
|
+
shoulda-context (~> 1.0)
|
104
|
+
shoulda-matchers (~> 1.4)
|
105
|
+
shoulda-context (1.0.0)
|
106
|
+
shoulda-matchers (1.4.0)
|
107
|
+
activesupport (>= 3.0.0)
|
108
|
+
simplecov (0.6.4)
|
109
|
+
multi_json (~> 1.0)
|
110
|
+
simplecov-html (~> 0.5.3)
|
111
|
+
simplecov-html (0.5.3)
|
112
|
+
sprockets (2.1.3)
|
113
|
+
hike (~> 1.2)
|
114
|
+
rack (~> 1.0)
|
115
|
+
tilt (~> 1.1, != 1.3.0)
|
116
|
+
sqlite3 (1.3.6)
|
117
|
+
test-unit (2.5.2)
|
118
|
+
thor (0.16.0)
|
119
|
+
tilt (1.3.3)
|
120
|
+
treetop (1.4.10)
|
121
|
+
polyglot
|
122
|
+
polyglot (>= 0.3.1)
|
123
|
+
tzinfo (0.3.33)
|
124
|
+
|
125
|
+
PLATFORMS
|
126
|
+
ruby
|
127
|
+
|
128
|
+
DEPENDENCIES
|
129
|
+
arel
|
130
|
+
bundler
|
131
|
+
guard
|
132
|
+
guard-test
|
133
|
+
jeweler
|
134
|
+
parslet
|
135
|
+
rails
|
136
|
+
rdoc
|
137
|
+
shoulda
|
138
|
+
simplecov
|
139
|
+
sqlite3
|
140
|
+
wherewolf!
|
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Myles Eftos
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= wherewolf
|
2
|
+
|
3
|
+
== Problem
|
4
|
+
|
5
|
+
Most RESTful APIs expose a "/index" endpoint that return all of objects at a given endpoint. That is fine until you need the ability to filter them.
|
6
|
+
|
7
|
+
Consider the following scenario:
|
8
|
+
|
9
|
+
/companies.json Return all the companies.
|
10
|
+
|
11
|
+
But what if I want only companies that are active? Most developers would simply add a query parameter like so:
|
12
|
+
|
13
|
+
/companies.json?active=true
|
14
|
+
|
15
|
+
Ok, now what if you only want companies created after the first of January 2012? Maybe:
|
16
|
+
|
17
|
+
/companies.json?created_after=2012-01-01
|
18
|
+
|
19
|
+
Yeah, great - but it doesn't really scale. Wouldn't if be better if we could do something like this?
|
20
|
+
|
21
|
+
/companies.json?where=active%20%3D%20true%20%26%26%20created_at%20%3E%3D%202012-01-01
|
22
|
+
|
23
|
+
Ok, it doesn't read amazingly, but this is an API, so encoding that stuff is trivial for the client. For those of you that doesn't speak URI-coded string that is the same as:
|
24
|
+
|
25
|
+
active = true && created_at >= 2012-01-01
|
26
|
+
|
27
|
+
Wherewolf will take that string and converts it in to AREL, so your clients can run arbitary queries against your API.
|
28
|
+
|
29
|
+
== Get started
|
30
|
+
|
31
|
+
The easiest way is to use Bundler:
|
32
|
+
|
33
|
+
gem 'wherewolf'
|
34
|
+
|
35
|
+
== Where we are at
|
36
|
+
|
37
|
+
What Works:
|
38
|
+
|
39
|
+
* &&
|
40
|
+
* ||
|
41
|
+
* =
|
42
|
+
* !=
|
43
|
+
* <
|
44
|
+
* <=
|
45
|
+
* >
|
46
|
+
* >=
|
47
|
+
* Parenthesis
|
48
|
+
|
49
|
+
Need to implement:
|
50
|
+
|
51
|
+
* Aliases such for operators, such as 'and', 'or' etc
|
52
|
+
* Allow from_query to nested (ie Player.where('first_cap < 2000-01-01').from_query('active = true')
|
53
|
+
* More edge case testing
|
54
|
+
|
55
|
+
== Example
|
56
|
+
|
57
|
+
player = Player.from_query("(position = wing || position = lock) && first_cap < 1905-01-01").order('first_cap')
|
58
|
+
|
59
|
+
== Contributing to wherewolf
|
60
|
+
|
61
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
62
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
63
|
+
* Fork the project.
|
64
|
+
* Start a feature/bugfix branch.
|
65
|
+
* Commit and push until you are happy with your contribution.
|
66
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
67
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
68
|
+
|
69
|
+
== Copyright
|
70
|
+
|
71
|
+
Copyright (c) 2012 Myles Eftos. See LICENSE.txt for
|
72
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
18
|
+
gem.name = "wherewolf"
|
19
|
+
gem.homepage = "http://github.com/madpilot/wherewolf"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{Query parser that converts search terms to AREL for use in APIs}
|
22
|
+
gem.description = %Q{Wherewolf allows you to consume search terms as strings without worrying about database injections. It parses the query and converts it into AREL. It's great for creating filterable REST APIs}
|
23
|
+
gem.email = "myles@madpilot.com.au"
|
24
|
+
gem.authors = ["Myles Eftos"]
|
25
|
+
gem.version = "0.1.0"
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/*_test.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :default => :test
|
38
|
+
|
39
|
+
require 'rdoc/task'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "wherewolf #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
class Parser < Parslet::Parser
|
5
|
+
rule(:space) { match('\s').repeat(1) }
|
6
|
+
rule(:space?) { space.maybe }
|
7
|
+
rule(:left_parenthesis) { str('(') }
|
8
|
+
rule(:right_parenthesis) { str(')') }
|
9
|
+
|
10
|
+
# Comparisons
|
11
|
+
rule(:eq) { str('=') }
|
12
|
+
rule(:not_eq) { str('!=') }
|
13
|
+
rule(:lt) { str('<') }
|
14
|
+
rule(:lteq) { str('<=') }
|
15
|
+
rule(:gt) { str('>') }
|
16
|
+
rule(:gteq) { str('>=') }
|
17
|
+
|
18
|
+
# Operators
|
19
|
+
rule(:and_operator) { str('&&') }
|
20
|
+
rule(:or_operator) { str('||') }
|
21
|
+
|
22
|
+
# Operand
|
23
|
+
rule(:null) { str("null").as(:nil) }
|
24
|
+
rule(:boolean) { str("true").as(:boolean) | str("false").as(:boolean) }
|
25
|
+
rule(:number) { match('[-+]?([0-9]*\.)?[0-9]').repeat(1).as(:number) }
|
26
|
+
rule(:double_quote_string) do
|
27
|
+
str('"') >>
|
28
|
+
(
|
29
|
+
(str('\\') >> any) |
|
30
|
+
(str('"').absent? >> any)
|
31
|
+
).repeat.as(:string) >>
|
32
|
+
str('"')
|
33
|
+
end
|
34
|
+
rule(:literal) { match('[a-zA-Z0-9\-_]').repeat(1) }
|
35
|
+
rule(:identifier) { null | boolean | number | double_quote_string | literal.as(:string) }
|
36
|
+
|
37
|
+
# Grammar
|
38
|
+
rule(:compare_eq) { (literal.as(:left) >> space? >> eq >> space? >> identifier.as(:right)).as(:eq) }
|
39
|
+
rule(:compare_not_eq) { (literal.as(:left) >> space? >> not_eq >> space? >> identifier.as(:right)).as(:not_eq) }
|
40
|
+
rule(:compare_lt) { (literal.as(:left) >> space? >> lt >> space? >> identifier.as(:right)).as(:lt) }
|
41
|
+
rule(:compare_lteq) { (literal.as(:left) >> space? >> lteq >> space? >> identifier.as(:right)).as(:lteq) }
|
42
|
+
rule(:compare_gt) { (literal.as(:left) >> space? >> gt >> space? >> identifier.as(:right)).as(:gt) }
|
43
|
+
rule(:compare_gteq) { (literal.as(:left) >> space? >> gteq >> space? >> identifier.as(:right)).as(:gteq) }
|
44
|
+
|
45
|
+
rule(:compare) { compare_eq | compare_not_eq | compare_lteq | compare_lt | compare_gteq | compare_gt }
|
46
|
+
|
47
|
+
rule(:primary) { left_parenthesis >> space? >> or_operation >> space? >> right_parenthesis | compare }
|
48
|
+
rule(:and_operation) { (primary.as(:left) >> space? >> and_operator >> space? >> and_operation.as(:right)).as(:and) | primary }
|
49
|
+
rule(:or_operation) { (and_operation.as(:left) >> space? >> or_operator >> space? >> or_operation.as(:right)).as(:or) | and_operation }
|
50
|
+
|
51
|
+
root :or_operation
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'arel'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
class Processor
|
5
|
+
def self.parse(model, query)
|
6
|
+
instance = self.new
|
7
|
+
instance.parse(model, query)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(model, query)
|
11
|
+
ast = Wherewolf::Parser.new.parse(query)
|
12
|
+
table = model.arel_table
|
13
|
+
model.where(process(ast, table))
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(ast, table)
|
17
|
+
operation = ast.keys.first
|
18
|
+
self.send("process_#{operation}".to_sym, ast[operation], table) if self.respond_to?("process_#{operation}".to_sym)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def process_and(ast, table)
|
23
|
+
process(ast[:left], table).and(process(ast[:right], table))
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_or(ast, table)
|
27
|
+
process(ast[:left], table).or(process(ast[:right], table))
|
28
|
+
end
|
29
|
+
|
30
|
+
def process_eq(ast, table)
|
31
|
+
table[ast[:left].to_sym].eq(parse_value(ast[:right]))
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_not_eq(ast, table)
|
35
|
+
table[ast[:left].to_sym].not_eq(parse_value(ast[:right]))
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_lt(ast, table)
|
39
|
+
table[ast[:left].to_sym].lt(parse_value(ast[:right]))
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_lteq(ast, table)
|
43
|
+
table[ast[:left].to_sym].lteq(parse_value(ast[:right]))
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_gt(ast, table)
|
47
|
+
table[ast[:left].to_sym].gt(parse_value(ast[:right]))
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_gteq(ast, table)
|
51
|
+
table[ast[:left].to_sym].gteq(parse_value(ast[:right]))
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_value(value)
|
55
|
+
type = value.keys.first
|
56
|
+
case type
|
57
|
+
when :nil
|
58
|
+
return nil
|
59
|
+
when :boolean
|
60
|
+
return value[:boolean] == "true"
|
61
|
+
else
|
62
|
+
return value[type].to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/wherewolf.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'parser.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'processor.rb')
|
3
|
+
|
4
|
+
module Wherewolf
|
5
|
+
def self.included(base)
|
6
|
+
base.send :extend, ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def has_query_parsing(options = {})
|
11
|
+
self.extend QueryMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module QueryMethods
|
16
|
+
def from_query(query)
|
17
|
+
Wherewolf::Processor.parse(self, query)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'railtie.rb') if defined?(Rails::Railtie)
|
data/test/helper.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'simplecov'
|
11
|
+
SimpleCov.command_name 'Unit Tests'
|
12
|
+
SimpleCov.start do
|
13
|
+
add_filter "/test/"
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'test/unit'
|
17
|
+
require 'shoulda'
|
18
|
+
|
19
|
+
require 'thread'
|
20
|
+
require 'rails/all'
|
21
|
+
|
22
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
23
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
24
|
+
require 'wherewolf'
|
25
|
+
|
26
|
+
class TestApp < Rails::Application
|
27
|
+
config.root = File.dirname(__FILE__)
|
28
|
+
end
|
29
|
+
|
30
|
+
Rails.application = TestApp
|
31
|
+
Wherewolf::Railtie.initializers.first.run(Rails.application)
|
32
|
+
ActiveRecord::Migration.verbose = false
|
33
|
+
|
34
|
+
class AddUsers < ActiveRecord::Migration
|
35
|
+
def up
|
36
|
+
create_table :players do |t|
|
37
|
+
t.string :name
|
38
|
+
t.string :position
|
39
|
+
t.boolean :active
|
40
|
+
t.date :first_cap
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :teams do |t|
|
44
|
+
t.string :team
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def down
|
49
|
+
drop_table :users
|
50
|
+
drop_table :teams
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Player < ActiveRecord::Base
|
55
|
+
has_query_parsing
|
56
|
+
end
|
57
|
+
|
58
|
+
class Team < ActiveRecord::Base
|
59
|
+
end
|
60
|
+
|
61
|
+
class Test::Unit::TestCase
|
62
|
+
end
|
data/test/parser_test.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ParserTest < Test::Unit::TestCase
|
4
|
+
context 'Parser' do
|
5
|
+
setup do
|
6
|
+
@parser = Wherewolf::Parser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "operands" do
|
10
|
+
should 'parse [op1]=[literal]' do
|
11
|
+
result = @parser.parse('name=Myles')
|
12
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles" } } }, result)
|
13
|
+
end
|
14
|
+
should 'parse [op1]="[string]"' do
|
15
|
+
result = @parser.parse('name="Myles Eftos"')
|
16
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles Eftos" } } }, result)
|
17
|
+
end
|
18
|
+
should 'parse [op1]=null' do
|
19
|
+
result = @parser.parse('name=null')
|
20
|
+
assert_equal( { :eq => { :left => "name", :right => { :nil => "null" } } }, result)
|
21
|
+
end
|
22
|
+
should 'parse [op1]=true' do
|
23
|
+
result = @parser.parse('active=true')
|
24
|
+
assert_equal( { :eq => { :left => "active", :right => { :boolean => "true" } } }, result)
|
25
|
+
end
|
26
|
+
should 'parse [op1]=false' do
|
27
|
+
result = @parser.parse('active=false')
|
28
|
+
assert_equal( { :eq => { :left => "active", :right => { :boolean => "false" } } }, result)
|
29
|
+
end
|
30
|
+
should 'parse [op1]=[integer]' do
|
31
|
+
result = @parser.parse('count=10')
|
32
|
+
assert_equal( { :eq => { :left => "count", :right => { :number => "10" } } }, result)
|
33
|
+
end
|
34
|
+
should 'parse [op1]=[float]' do
|
35
|
+
result = @parser.parse('count=-10.3')
|
36
|
+
assert_equal( { :eq => { :left => "count", :right => { :number => "-10.3" } } }, result)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "comparators" do
|
41
|
+
context "equals" do
|
42
|
+
should 'parse [op1]=[op2]' do
|
43
|
+
result = @parser.parse('name="Myles"')
|
44
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles" } } }, result)
|
45
|
+
end
|
46
|
+
should 'parse [op1] =[op2]' do
|
47
|
+
result = @parser.parse('name ="Myles"')
|
48
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles" } } }, result)
|
49
|
+
end
|
50
|
+
should 'parse [op1]= [op2]' do
|
51
|
+
result = @parser.parse('name= "Myles"')
|
52
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles" } } }, result)
|
53
|
+
end
|
54
|
+
should 'parse [op1] = [op2]' do
|
55
|
+
result = @parser.parse('name = "Myles"')
|
56
|
+
assert_equal( { :eq => { :left => "name", :right => { :string => "Myles" } } }, result)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "not equals" do
|
61
|
+
should 'parse [op1]!=[op2]' do
|
62
|
+
result = @parser.parse('name!="Myles Eftos"')
|
63
|
+
assert_equal( { :not_eq => { :left => "name", :right => { :string => 'Myles Eftos' } } }, result)
|
64
|
+
end
|
65
|
+
should 'parse [op1] !=[op2]' do
|
66
|
+
result = @parser.parse('name !="Myles Eftos"')
|
67
|
+
assert_equal( { :not_eq => { :left => "name", :right => { :string => 'Myles Eftos' } } }, result)
|
68
|
+
end
|
69
|
+
should 'parse [op1]!= [op2]' do
|
70
|
+
result = @parser.parse('name!= "Myles Eftos"')
|
71
|
+
assert_equal( { :not_eq => { :left => "name", :right => { :string => 'Myles Eftos' } } }, result)
|
72
|
+
end
|
73
|
+
should 'parse [op1] != [op2]' do
|
74
|
+
result = @parser.parse('name != "Myles Eftos"')
|
75
|
+
assert_equal( { :not_eq => { :left => "name", :right => { :string => 'Myles Eftos' } } }, result)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "less than" do
|
80
|
+
should 'parse [op1]<[op2]' do
|
81
|
+
result = @parser.parse("size<12")
|
82
|
+
assert_equal( { :lt => { :left => "size", :right => { :number => "12" } } }, result)
|
83
|
+
end
|
84
|
+
should 'parse [op1] <[op2]' do
|
85
|
+
result = @parser.parse("size <12")
|
86
|
+
assert_equal( { :lt => { :left => "size", :right => { :number => "12" } } }, result)
|
87
|
+
end
|
88
|
+
should 'parse [op1]< [op2]' do
|
89
|
+
result = @parser.parse("size< 12")
|
90
|
+
assert_equal( { :lt => { :left => "size", :right => { :number => "12" } } }, result)
|
91
|
+
end
|
92
|
+
should 'parse [op1] < [op2]' do
|
93
|
+
result = @parser.parse("size < 12")
|
94
|
+
assert_equal( { :lt => { :left => "size", :right => { :number => "12" } } }, result)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "less than or equal to" do
|
99
|
+
should 'parse [op1]<=[op2]' do
|
100
|
+
result = @parser.parse("size<=12")
|
101
|
+
assert_equal( { :lteq => { :left => "size", :right => { :number => '12' } } }, result)
|
102
|
+
end
|
103
|
+
should 'parse [op1] <=[op2]' do
|
104
|
+
result = @parser.parse("size <=12")
|
105
|
+
assert_equal( { :lteq => { :left => "size", :right => { :number => '12' } } }, result)
|
106
|
+
end
|
107
|
+
should 'parse [op1]<= [op2]' do
|
108
|
+
result = @parser.parse("size<= 12")
|
109
|
+
assert_equal( { :lteq => { :left => "size", :right => { :number => '12' } } }, result)
|
110
|
+
end
|
111
|
+
should 'parse [op1] <= [op2]' do
|
112
|
+
result = @parser.parse("size <= 12")
|
113
|
+
assert_equal( { :lteq => { :left => "size", :right => { :number => '12' } } }, result)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "greater than" do
|
118
|
+
should 'parse [op1]>[op2]' do
|
119
|
+
result = @parser.parse("size>12")
|
120
|
+
assert_equal( { :gt => { :left => "size", :right => { :number => '12' } } }, result)
|
121
|
+
end
|
122
|
+
should 'parse [op1] >[op2]' do
|
123
|
+
result = @parser.parse("size >12")
|
124
|
+
assert_equal( { :gt => { :left => "size", :right => { :number => '12' } } }, result)
|
125
|
+
end
|
126
|
+
should 'parse [op1]> [op2]' do
|
127
|
+
result = @parser.parse("size> 12")
|
128
|
+
assert_equal( { :gt => { :left => "size", :right => { :number => '12' } } }, result)
|
129
|
+
end
|
130
|
+
should 'parse [op1] > [op2]' do
|
131
|
+
result = @parser.parse("size > 12")
|
132
|
+
assert_equal( { :gt => { :left => "size", :right => { :number => '12' } } }, result)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "greater than or equal to" do
|
137
|
+
should 'parse [op1]>=[op2]' do
|
138
|
+
result = @parser.parse("size>=12")
|
139
|
+
assert_equal( { :gteq => { :left => "size", :right => { :number => '12' } } }, result)
|
140
|
+
end
|
141
|
+
should 'parse [op1] >=[op2]' do
|
142
|
+
result = @parser.parse("size >=12")
|
143
|
+
assert_equal( { :gteq => { :left => "size", :right => { :number => '12' } } }, result)
|
144
|
+
end
|
145
|
+
should 'parse [op1]>= [op2]' do
|
146
|
+
result = @parser.parse("size>= 12")
|
147
|
+
assert_equal( { :gteq => { :left => "size", :right => { :number => '12' } } }, result)
|
148
|
+
end
|
149
|
+
should 'parse [op1] >= [op2]' do
|
150
|
+
result = @parser.parse("size >= 12")
|
151
|
+
assert_equal( { :gteq => { :left => "size", :right => { :number => '12' } } }, result)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'operators' do
|
157
|
+
context 'and' do
|
158
|
+
should 'parse [comp]&&[comp]' do
|
159
|
+
result = @parser.parse("name=myles&&size>=12")
|
160
|
+
assert_equal( { :and => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
161
|
+
end
|
162
|
+
|
163
|
+
should 'parse [comp] &&[comp]' do
|
164
|
+
result = @parser.parse("name=myles &&size>=12")
|
165
|
+
assert_equal( { :and => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
166
|
+
end
|
167
|
+
|
168
|
+
should 'parse [comp]&& [comp]' do
|
169
|
+
result = @parser.parse("name=myles&& size>=12")
|
170
|
+
assert_equal( { :and => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
171
|
+
end
|
172
|
+
|
173
|
+
should 'parse [comp] && [comp]' do
|
174
|
+
result = @parser.parse("name=myles && size>=12")
|
175
|
+
assert_equal( { :and => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'or' do
|
180
|
+
should 'parse [comp]||[comp]' do
|
181
|
+
result = @parser.parse("name=myles||size>=12")
|
182
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
183
|
+
end
|
184
|
+
|
185
|
+
should 'parse [comp] ||[comp]' do
|
186
|
+
result = @parser.parse("name=myles ||size>=12")
|
187
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
188
|
+
end
|
189
|
+
|
190
|
+
should 'parse [comp]|| [comp]' do
|
191
|
+
result = @parser.parse("name=myles|| size>=12")
|
192
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
193
|
+
end
|
194
|
+
|
195
|
+
should 'parse [comp] || [comp]' do
|
196
|
+
result = @parser.parse("name=myles || size>=12")
|
197
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'group' do
|
203
|
+
should 'parse ([op] || [op])' do
|
204
|
+
result = @parser.parse("(name=myles || size>=12)")
|
205
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } }, result)
|
206
|
+
end
|
207
|
+
|
208
|
+
should 'parse [op] || ([op] && [op])' do
|
209
|
+
result = @parser.parse("name=myles || (job=programmer && size>=12)")
|
210
|
+
assert_equal( { :or => { :left => { :eq => { :left => 'name', :right => { :string => 'myles' } } }, :right => { :and => { :left => { :eq => { :left => 'job', :right => { :string => 'programmer' } } }, :right => { :gteq => { :left => 'size', :right => { :number => '12' } } } } } } }, result)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ProcessorTest < Test::Unit::TestCase
|
4
|
+
def setup_fixtures
|
5
|
+
Player.create!(:name => "Patrick 'Paddy' Carew", :position => 'lock', :first_cap => '1899-06-24', :active => false)
|
6
|
+
Player.create!(:name => "Charlie Ellis", :position => 'flanker', :first_cap => '1899-06-24', :active => false)
|
7
|
+
Player.create!(:name => "Arthur Corfe", :position => 'flanker', :first_cap => '1899-07-22', :active => false)
|
8
|
+
Player.create!(:name => "Syd Miller", :position => 'wing', :first_cap => '1899-08-05', :active => false)
|
9
|
+
Player.create!(:name => "Charlie Redwood", :position => 'wing', :first_cap => '1903-08-15', :active => false)
|
10
|
+
Player.create!(:name => "Patrick 'Pat' Walsh", :position => 'no. 8', :first_cap => '1904-07-02', :active => false)
|
11
|
+
Player.create!(:name => "John Manning", :position => 'fly-half', :first_cap => '1904-07-23', :active => false)
|
12
|
+
Player.create!(:name => "Dally Messenger", :position => 'wing', :first_cap => '1907-08-03', :active => false)
|
13
|
+
Player.create!(:name => "Salesi Ma'afu", :position => 'prop', :first_cap => '2010-06-05', :active => true)
|
14
|
+
Player.create!(:name => "James Slipper", :position => 'prop', :first_cap => nil, :active => true)
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'Processor' do
|
18
|
+
setup do
|
19
|
+
ActiveRecord::Base.establish_connection({
|
20
|
+
:adapter => 'sqlite3',
|
21
|
+
:database => ':memory:',
|
22
|
+
:verbosity => 'quiet'
|
23
|
+
})
|
24
|
+
Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
|
25
|
+
AddUsers.migrate(:up)
|
26
|
+
setup_fixtures
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'Parsing' do
|
30
|
+
should 'construct simple boolean statements' do
|
31
|
+
player = Player.from_query('name = "Charlie Ellis"')
|
32
|
+
assert_equal 1, player.count
|
33
|
+
assert_equal 'Charlie Ellis', player.first.name
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'construct two boolean statments' do
|
37
|
+
player = Player.from_query('position=lock && first_cap < 2010-01-01')
|
38
|
+
assert_equal 1, player.count
|
39
|
+
assert_equal "Patrick 'Paddy' Carew", player.first.name
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'contruct nested brackets' do
|
43
|
+
player = Player.from_query("(position = wing || position = lock) && first_cap <= 1905-01-01").order('first_cap')
|
44
|
+
assert_equal 3, player.count
|
45
|
+
assert_equal "Patrick 'Paddy' Carew", player[0].name
|
46
|
+
assert_equal "Syd Miller", player[1].name
|
47
|
+
assert_equal "Charlie Redwood", player[2].name
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'handle nulls' do
|
51
|
+
player = Player.from_query("first_cap = null")
|
52
|
+
assert_equal 1, player.count
|
53
|
+
assert_equal "James Slipper", player.first.name
|
54
|
+
end
|
55
|
+
|
56
|
+
should 'handle booleans' do
|
57
|
+
player = Player.from_query("active = true")
|
58
|
+
assert_equal 2, player.count
|
59
|
+
player = Player.from_query("active = false")
|
60
|
+
assert_equal 8, player.count
|
61
|
+
end
|
62
|
+
|
63
|
+
should 'handle dates' do
|
64
|
+
player = Player.from_query("first_cap > 2000-01-01")
|
65
|
+
assert_equal 1, player.count
|
66
|
+
assert_equal "Salesi Ma'afu", player.first.name
|
67
|
+
end
|
68
|
+
|
69
|
+
should 'process =' do
|
70
|
+
player = Player.from_query('name = "Charlie Ellis"')
|
71
|
+
assert_equal 1, player.count
|
72
|
+
assert_equal 'Charlie Ellis', player.first.name
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'process !=' do
|
76
|
+
player = Player.from_query('name != "Charlie Ellis"')
|
77
|
+
assert_equal 9, player.count
|
78
|
+
assert_equal false, player.map(&:name).include?('Charlie Ellis')
|
79
|
+
end
|
80
|
+
|
81
|
+
should 'process >' do
|
82
|
+
player = Player.from_query('first_cap > 1907-08-03')
|
83
|
+
assert_equal 1, player.count
|
84
|
+
end
|
85
|
+
|
86
|
+
should 'process >=' do
|
87
|
+
player = Player.from_query('first_cap >= 1907-08-03')
|
88
|
+
assert_equal 2, player.count
|
89
|
+
end
|
90
|
+
|
91
|
+
should 'process <' do
|
92
|
+
player = Player.from_query('first_cap < 1907-08-03')
|
93
|
+
assert_equal 7, player.count
|
94
|
+
end
|
95
|
+
|
96
|
+
should 'process <=' do
|
97
|
+
player = Player.from_query('first_cap <= 1907-08-03')
|
98
|
+
assert_equal 8, player.count
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class RailtieTest < Test::Unit::TestCase
|
4
|
+
context 'Railtie' do
|
5
|
+
should 'not enable from_query if has_query_parsing is not called' do
|
6
|
+
assert_equal false, Team.respond_to?(:from_query)
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'enable from_query if has_query_parsing is called' do
|
10
|
+
assert_equal true, Player.respond_to?(:from_query)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/wherewolf.gemspec
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "wherewolf"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Myles Eftos"]
|
12
|
+
s.date = "2012-10-10"
|
13
|
+
s.description = "Wherewolf allows you to consume search terms as strings without worrying about database injections. It parses the query and converts it into AREL. It's great for creating filterable REST APIs"
|
14
|
+
s.email = "myles@madpilot.com.au"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"Guardfile",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"lib/wherewolf.rb",
|
28
|
+
"lib/wherewolf/parser.rb",
|
29
|
+
"lib/wherewolf/processor.rb",
|
30
|
+
"lib/wherewolf/railtie.rb",
|
31
|
+
"test/helper.rb",
|
32
|
+
"test/parser_test.rb",
|
33
|
+
"test/processor_test.rb",
|
34
|
+
"test/railtie_test.rb",
|
35
|
+
"wherewolf.gemspec"
|
36
|
+
]
|
37
|
+
s.homepage = "http://github.com/madpilot/wherewolf"
|
38
|
+
s.licenses = ["MIT"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = "1.8.15"
|
41
|
+
s.summary = "Query parser that converts search terms to AREL for use in APIs"
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<arel>, [">= 0"])
|
48
|
+
s.add_runtime_dependency(%q<parslet>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<guard>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<guard-test>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<rails>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<wherewolf>, [">= 0"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<arel>, [">= 0"])
|
61
|
+
s.add_dependency(%q<parslet>, [">= 0"])
|
62
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
63
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
64
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
65
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
66
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
67
|
+
s.add_dependency(%q<guard-test>, [">= 0"])
|
68
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
69
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rails>, [">= 0"])
|
71
|
+
s.add_dependency(%q<wherewolf>, [">= 0"])
|
72
|
+
end
|
73
|
+
else
|
74
|
+
s.add_dependency(%q<arel>, [">= 0"])
|
75
|
+
s.add_dependency(%q<parslet>, [">= 0"])
|
76
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
78
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
79
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
80
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
81
|
+
s.add_dependency(%q<guard-test>, [">= 0"])
|
82
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
83
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rails>, [">= 0"])
|
85
|
+
s.add_dependency(%q<wherewolf>, [">= 0"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wherewolf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Myles Eftos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: arel
|
16
|
+
requirement: &70230035647040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70230035647040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: parslet
|
27
|
+
requirement: &70230035645880 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70230035645880
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: shoulda
|
38
|
+
requirement: &70230035645020 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70230035645020
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdoc
|
49
|
+
requirement: &70230035658520 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70230035658520
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: bundler
|
60
|
+
requirement: &70230035655100 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70230035655100
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jeweler
|
71
|
+
requirement: &70230035654260 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70230035654260
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: guard
|
82
|
+
requirement: &70230035653240 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70230035653240
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: guard-test
|
93
|
+
requirement: &70230035681240 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70230035681240
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: simplecov
|
104
|
+
requirement: &70230035677480 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *70230035677480
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: sqlite3
|
115
|
+
requirement: &70230035689500 !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: *70230035689500
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: rails
|
126
|
+
requirement: &70230035685280 !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: *70230035685280
|
135
|
+
- !ruby/object:Gem::Dependency
|
136
|
+
name: wherewolf
|
137
|
+
requirement: &70230035683060 !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
type: :development
|
144
|
+
prerelease: false
|
145
|
+
version_requirements: *70230035683060
|
146
|
+
description: Wherewolf allows you to consume search terms as strings without worrying
|
147
|
+
about database injections. It parses the query and converts it into AREL. It's great
|
148
|
+
for creating filterable REST APIs
|
149
|
+
email: myles@madpilot.com.au
|
150
|
+
executables: []
|
151
|
+
extensions: []
|
152
|
+
extra_rdoc_files:
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.rdoc
|
155
|
+
files:
|
156
|
+
- .document
|
157
|
+
- Gemfile
|
158
|
+
- Gemfile.lock
|
159
|
+
- Guardfile
|
160
|
+
- LICENSE.txt
|
161
|
+
- README.rdoc
|
162
|
+
- Rakefile
|
163
|
+
- lib/wherewolf.rb
|
164
|
+
- lib/wherewolf/parser.rb
|
165
|
+
- lib/wherewolf/processor.rb
|
166
|
+
- lib/wherewolf/railtie.rb
|
167
|
+
- test/helper.rb
|
168
|
+
- test/parser_test.rb
|
169
|
+
- test/processor_test.rb
|
170
|
+
- test/railtie_test.rb
|
171
|
+
- wherewolf.gemspec
|
172
|
+
homepage: http://github.com/madpilot/wherewolf
|
173
|
+
licenses:
|
174
|
+
- MIT
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
none: false
|
181
|
+
requirements:
|
182
|
+
- - ! '>='
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
segments:
|
186
|
+
- 0
|
187
|
+
hash: 3955971048555964075
|
188
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
|
+
none: false
|
190
|
+
requirements:
|
191
|
+
- - ! '>='
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
requirements: []
|
195
|
+
rubyforge_project:
|
196
|
+
rubygems_version: 1.8.15
|
197
|
+
signing_key:
|
198
|
+
specification_version: 3
|
199
|
+
summary: Query parser that converts search terms to AREL for use in APIs
|
200
|
+
test_files: []
|