wherewolf 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -8
- data/README.rdoc +26 -10
- data/Rakefile +2 -2
- data/lib/wherewolf.rb +10 -3
- data/lib/wherewolf/order/parser.rb +20 -0
- data/lib/wherewolf/order/processor.rb +32 -0
- data/lib/wherewolf/processor.rb +1 -78
- data/lib/wherewolf/where/parser.rb +57 -0
- data/lib/wherewolf/where/processor.rb +83 -0
- data/test/helper.rb +23 -0
- data/test/order/parser_test.rb +31 -0
- data/test/order/processor_test.rb +62 -0
- data/test/processor_test.rb +10 -142
- data/test/railtie_test.rb +4 -4
- data/test/{parser_test.rb → where/parser_test.rb} +3 -3
- data/test/where/processor_test.rb +140 -0
- data/wherewolf.gemspec +13 -7
- metadata +96 -30
- data/lib/wherewolf/parser.rb +0 -55
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,3 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ./
|
3
|
-
specs:
|
4
|
-
wherewolf (0.3.0)
|
5
|
-
arel
|
6
|
-
parslet
|
7
|
-
|
8
1
|
GEM
|
9
2
|
remote: http://rubygems.org/
|
10
3
|
specs:
|
@@ -64,7 +57,10 @@ GEM
|
|
64
57
|
i18n (>= 0.4.0)
|
65
58
|
mime-types (~> 1.16)
|
66
59
|
treetop (~> 1.4.8)
|
60
|
+
metaclass (0.0.1)
|
67
61
|
mime-types (1.19)
|
62
|
+
mocha (0.12.4)
|
63
|
+
metaclass (~> 0.0.1)
|
68
64
|
multi_json (1.3.6)
|
69
65
|
parslet (1.4.0)
|
70
66
|
blankslate (~> 2.0)
|
@@ -131,10 +127,10 @@ DEPENDENCIES
|
|
131
127
|
guard
|
132
128
|
guard-test
|
133
129
|
jeweler
|
130
|
+
mocha
|
134
131
|
parslet
|
135
132
|
rails
|
136
133
|
rdoc
|
137
134
|
shoulda
|
138
135
|
simplecov
|
139
136
|
sqlite3
|
140
|
-
wherewolf!
|
data/README.rdoc
CHANGED
@@ -40,28 +40,44 @@ Then for every model that you want to by queryable, do this:
|
|
40
40
|
has_query_parsing
|
41
41
|
end
|
42
42
|
|
43
|
-
This will add the "
|
43
|
+
This will add the "where_query" method, which you pass your query string in to.
|
44
44
|
|
45
45
|
== Example
|
46
46
|
|
47
47
|
For a real-life, running example, check out: http://wherewolf.herokuapp.com/
|
48
48
|
|
49
|
-
player = Player.
|
49
|
+
player = Player.where_query("(position = wing || position = lock) && first_cap < 1905-01-01").order('first_cap')
|
50
50
|
# Returns all players that play 'wing' or 'lock', and played before 1905-01-01
|
51
51
|
|
52
|
-
player = Player.
|
52
|
+
player = Player.where_query('name = "John Eales"')
|
53
53
|
# Returns all players names 'John Eales'
|
54
54
|
|
55
|
-
player = Player.
|
55
|
+
player = Player.where_query("first_cap >= 1905-01-01 && active = false")
|
56
56
|
# Returns all inactitve players that played after 1905-01-01.
|
57
57
|
|
58
|
-
player = Player.
|
58
|
+
player = Player.where_query("first_cap != null")
|
59
59
|
# Returns all players who have received their first cap (ie first_cap is NOT nil)
|
60
60
|
|
61
|
-
player = Player.
|
61
|
+
player = Player.where_query('name ~= "Peter%"')
|
62
62
|
# Returns all players who's name starts with Peter
|
63
63
|
|
64
|
-
As you can see,
|
64
|
+
As you can see, where_query returns an ARel object, so you chain other statements to it. Please note, though: at the moment where_query needs to be the first in the chain.
|
65
|
+
|
66
|
+
== Order
|
67
|
+
|
68
|
+
You can also supply an order_query to handle ordering
|
69
|
+
|
70
|
+
player = Player.order_query("name asc")
|
71
|
+
# Order by name asc
|
72
|
+
|
73
|
+
player = Player.order_query("name desc")
|
74
|
+
# Order by name desc
|
75
|
+
|
76
|
+
player = Player.order_query("name")
|
77
|
+
# By default ordering is ascending
|
78
|
+
|
79
|
+
player = Player.order_query("name desc, position desc")
|
80
|
+
# You can also have multiple order columns
|
65
81
|
|
66
82
|
== Errors
|
67
83
|
|
@@ -72,7 +88,7 @@ At the moment, error handling is very primitive. Just capture
|
|
72
88
|
You can print out a simple error message like so
|
73
89
|
|
74
90
|
begin
|
75
|
-
Player.
|
91
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
76
92
|
rescue Wherewolf::ParseError => e
|
77
93
|
puts e.error_message
|
78
94
|
end
|
@@ -84,7 +100,7 @@ Will print out
|
|
84
100
|
You can get the character number by:
|
85
101
|
|
86
102
|
begin
|
87
|
-
Player.
|
103
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
88
104
|
rescue Wherewolf::ParseError => e
|
89
105
|
e.position # This value will be 28
|
90
106
|
end
|
@@ -94,7 +110,7 @@ You can get the character number by:
|
|
94
110
|
* Better error messages (Give a clue as to why parsing failed)
|
95
111
|
* Aliases such for operators, such as 'and', 'or' etc
|
96
112
|
* Allow single quotes around strings
|
97
|
-
* Allow
|
113
|
+
* Allow where_query to nested (ie Player.where('first_cap < 2000-01-01').where_query('active = true')
|
98
114
|
* More edge case testing
|
99
115
|
* Abillity to filter columns that are searchable
|
100
116
|
* Ability to call named scopes
|
data/Rakefile
CHANGED
@@ -22,7 +22,7 @@ Jeweler::Tasks.new do |gem|
|
|
22
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
23
|
gem.email = "myles@madpilot.com.au"
|
24
24
|
gem.authors = ["Myles Eftos"]
|
25
|
-
gem.version = "0.
|
25
|
+
gem.version = "0.4.0"
|
26
26
|
# dependencies defined in Gemfile
|
27
27
|
end
|
28
28
|
Jeweler::RubygemsDotOrgTasks.new
|
@@ -30,7 +30,7 @@ Jeweler::RubygemsDotOrgTasks.new
|
|
30
30
|
require 'rake/testtask'
|
31
31
|
Rake::TestTask.new(:test) do |test|
|
32
32
|
test.libs << 'lib' << 'test'
|
33
|
-
test.pattern = 'test
|
33
|
+
test.pattern = 'test/**/*_test.rb'
|
34
34
|
test.verbose = true
|
35
35
|
end
|
36
36
|
|
data/lib/wherewolf.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'wherewolf', 'parser.rb')
|
2
1
|
require File.join(File.dirname(__FILE__), 'wherewolf', 'processor.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'where', 'parser.rb')
|
3
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'where', 'processor.rb')
|
4
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'order', 'parser.rb')
|
5
|
+
require File.join(File.dirname(__FILE__), 'wherewolf', 'order', 'processor.rb')
|
3
6
|
require File.join(File.dirname(__FILE__), 'wherewolf', 'parse_error.rb')
|
4
7
|
|
5
8
|
module Wherewolf
|
@@ -14,8 +17,12 @@ module Wherewolf
|
|
14
17
|
end
|
15
18
|
|
16
19
|
module QueryMethods
|
17
|
-
def
|
18
|
-
Wherewolf::Processor.parse(self, query)
|
20
|
+
def where_query(query)
|
21
|
+
Wherewolf::Where::Processor.parse(self, query)
|
22
|
+
end
|
23
|
+
|
24
|
+
def order_query(query)
|
25
|
+
Wherewolf::Order::Processor.parse(self, query)
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
module Order
|
5
|
+
class Parser < Parslet::Parser
|
6
|
+
rule(:space) { match('\s').repeat(1) }
|
7
|
+
rule(:space?) { space.maybe }
|
8
|
+
rule(:comma) { str(',') >> space? }
|
9
|
+
|
10
|
+
rule(:literal) { match('[a-zA-Z0-9\-_]').repeat(1).as(:column) >> space? }
|
11
|
+
rule(:direction) { str('asc') | str('desc') }
|
12
|
+
|
13
|
+
rule(:order) { literal >> direction.maybe.as(:direction) }
|
14
|
+
rule(:multiple) { order >> (comma >> order).repeat }
|
15
|
+
rule(:expression) { multiple | order }
|
16
|
+
|
17
|
+
root :expression
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'arel'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
module Order
|
5
|
+
class Processor < Wherewolf::Processor
|
6
|
+
def self.parse(model, query)
|
7
|
+
instance = self.new
|
8
|
+
instance.parse(model, query)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(model, query)
|
12
|
+
begin
|
13
|
+
ast = Wherewolf::Order::Parser.new.parse(query)
|
14
|
+
process(ast, model)
|
15
|
+
rescue Parslet::ParseFailed => error
|
16
|
+
raise Wherewolf::ParseError, error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(ast, model)
|
21
|
+
ast = [ast] unless ast.is_a?(Array)
|
22
|
+
table = model.arel_table
|
23
|
+
ast.each do |order|
|
24
|
+
check_column!(order[:column], table)
|
25
|
+
direction = (order[:direction] || 'asc').to_sym
|
26
|
+
model = model.order(table[order[:column].to_sym].send(direction))
|
27
|
+
end
|
28
|
+
model
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/wherewolf/processor.rb
CHANGED
@@ -1,71 +1,6 @@
|
|
1
|
-
require 'arel'
|
2
|
-
|
3
1
|
module Wherewolf
|
4
2
|
class Processor
|
5
|
-
|
6
|
-
instance = self.new
|
7
|
-
instance.parse(model, query)
|
8
|
-
end
|
9
|
-
|
10
|
-
def parse(model, query)
|
11
|
-
begin
|
12
|
-
ast = Wherewolf::Parser.new.parse(query)
|
13
|
-
table = model.arel_table
|
14
|
-
model.where(process(ast, table))
|
15
|
-
rescue Parslet::ParseFailed => error
|
16
|
-
raise Wherewolf::ParseError, error
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def process(ast, table)
|
21
|
-
operation = ast.keys.first
|
22
|
-
self.send("process_#{operation}".to_sym, ast[operation], table) if self.respond_to?("process_#{operation}".to_sym)
|
23
|
-
end
|
24
|
-
|
25
|
-
protected
|
26
|
-
def process_and(ast, table)
|
27
|
-
process(ast[:left], table).and(process(ast[:right], table))
|
28
|
-
end
|
29
|
-
|
30
|
-
def process_or(ast, table)
|
31
|
-
process(ast[:left], table).or(process(ast[:right], table))
|
32
|
-
end
|
33
|
-
|
34
|
-
def process_eq(ast, table)
|
35
|
-
check_column!(ast[:left], table)
|
36
|
-
table[ast[:left].to_sym].eq(parse_value(ast[:right]))
|
37
|
-
end
|
38
|
-
|
39
|
-
def process_not_eq(ast, table)
|
40
|
-
check_column!(ast[:left], table)
|
41
|
-
table[ast[:left].to_sym].not_eq(parse_value(ast[:right]))
|
42
|
-
end
|
43
|
-
|
44
|
-
def process_matches(ast, table)
|
45
|
-
check_column!(ast[:left], table)
|
46
|
-
table[ast[:left].to_sym].matches(parse_value(ast[:right]))
|
47
|
-
end
|
48
|
-
|
49
|
-
def process_lt(ast, table)
|
50
|
-
check_column!(ast[:left], table)
|
51
|
-
table[ast[:left].to_sym].lt(parse_value(ast[:right]))
|
52
|
-
end
|
53
|
-
|
54
|
-
def process_lteq(ast, table)
|
55
|
-
check_column!(ast[:left], table)
|
56
|
-
table[ast[:left].to_sym].lteq(parse_value(ast[:right]))
|
57
|
-
end
|
58
|
-
|
59
|
-
def process_gt(ast, table)
|
60
|
-
check_column!(ast[:left], table)
|
61
|
-
table[ast[:left].to_sym].gt(parse_value(ast[:right]))
|
62
|
-
end
|
63
|
-
|
64
|
-
def process_gteq(ast, table)
|
65
|
-
check_column!(ast[:left], table)
|
66
|
-
table[ast[:left].to_sym].gteq(parse_value(ast[:right]))
|
67
|
-
end
|
68
|
-
|
3
|
+
protected
|
69
4
|
def check_column!(value, table)
|
70
5
|
unless table.columns.map(&:name).include?(value.to_sym)
|
71
6
|
source = Parslet::Source.new(value.to_s)
|
@@ -73,17 +8,5 @@ protected
|
|
73
8
|
raise Parslet::ParseFailed.new('Column not found', cause)
|
74
9
|
end
|
75
10
|
end
|
76
|
-
|
77
|
-
def parse_value(value)
|
78
|
-
type = value.keys.first
|
79
|
-
case type
|
80
|
-
when :nil
|
81
|
-
return nil
|
82
|
-
when :boolean
|
83
|
-
return value[:boolean] == "true"
|
84
|
-
else
|
85
|
-
return value[type].to_s
|
86
|
-
end
|
87
|
-
end
|
88
11
|
end
|
89
12
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
module Where
|
5
|
+
class Parser < Parslet::Parser
|
6
|
+
rule(:space) { match('\s').repeat(1) }
|
7
|
+
rule(:space?) { space.maybe }
|
8
|
+
rule(:left_parenthesis) { str('(') }
|
9
|
+
rule(:right_parenthesis) { str(')') }
|
10
|
+
|
11
|
+
# Comparisons
|
12
|
+
rule(:eq) { str('=') }
|
13
|
+
rule(:not_eq) { str('!=') }
|
14
|
+
rule(:matches) { str('~=') }
|
15
|
+
rule(:lt) { str('<') }
|
16
|
+
rule(:lteq) { str('<=') }
|
17
|
+
rule(:gt) { str('>') }
|
18
|
+
rule(:gteq) { str('>=') }
|
19
|
+
|
20
|
+
# Operators
|
21
|
+
rule(:and_operator) { str('&&') }
|
22
|
+
rule(:or_operator) { str('||') }
|
23
|
+
|
24
|
+
# Operand
|
25
|
+
rule(:null) { str("null").as(:nil) }
|
26
|
+
rule(:boolean) { str("true").as(:boolean) | str("false").as(:boolean) }
|
27
|
+
rule(:number) { match('[-+]?([0-9]*\.)?[0-9]').repeat(1).as(:number) }
|
28
|
+
rule(:double_quote_string) do
|
29
|
+
str('"') >>
|
30
|
+
(
|
31
|
+
(str('\\') >> any) |
|
32
|
+
(str('"').absent? >> any)
|
33
|
+
).repeat.as(:string) >>
|
34
|
+
str('"')
|
35
|
+
end
|
36
|
+
rule(:literal) { match('[a-zA-Z0-9\-_]').repeat(1) }
|
37
|
+
rule(:identifier) { null | boolean | number | double_quote_string | literal.as(:string) }
|
38
|
+
|
39
|
+
# Grammar
|
40
|
+
rule(:compare_eq) { (literal.as(:left) >> space? >> eq >> space? >> identifier.as(:right)).as(:eq) }
|
41
|
+
rule(:compare_not_eq) { (literal.as(:left) >> space? >> not_eq >> space? >> identifier.as(:right)).as(:not_eq) }
|
42
|
+
rule(:compare_matches) { (literal.as(:left) >> space? >> matches >> space? >> identifier.as(:right)).as(:matches) }
|
43
|
+
rule(:compare_lt) { (literal.as(:left) >> space? >> lt >> space? >> identifier.as(:right)).as(:lt) }
|
44
|
+
rule(:compare_lteq) { (literal.as(:left) >> space? >> lteq >> space? >> identifier.as(:right)).as(:lteq) }
|
45
|
+
rule(:compare_gt) { (literal.as(:left) >> space? >> gt >> space? >> identifier.as(:right)).as(:gt) }
|
46
|
+
rule(:compare_gteq) { (literal.as(:left) >> space? >> gteq >> space? >> identifier.as(:right)).as(:gteq) }
|
47
|
+
|
48
|
+
rule(:compare) { compare_eq | compare_not_eq | compare_matches | compare_lteq | compare_lt | compare_gteq | compare_gt }
|
49
|
+
|
50
|
+
rule(:primary) { left_parenthesis >> space? >> or_operation >> space? >> right_parenthesis | compare }
|
51
|
+
rule(:and_operation) { (primary.as(:left) >> space? >> and_operator >> space? >> and_operation.as(:right)).as(:and) | primary }
|
52
|
+
rule(:or_operation) { (and_operation.as(:left) >> space? >> or_operator >> space? >> or_operation.as(:right)).as(:or) | and_operation }
|
53
|
+
|
54
|
+
root :or_operation
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'arel'
|
2
|
+
|
3
|
+
module Wherewolf
|
4
|
+
module Where
|
5
|
+
class Processor < Wherewolf::Processor
|
6
|
+
def self.parse(model, query)
|
7
|
+
instance = self.new
|
8
|
+
instance.parse(model, query)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(model, query)
|
12
|
+
begin
|
13
|
+
ast = Wherewolf::Where::Parser.new.parse(query)
|
14
|
+
table = model.arel_table
|
15
|
+
model.where(process(ast, table))
|
16
|
+
rescue Parslet::ParseFailed => error
|
17
|
+
raise Wherewolf::ParseError, error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(ast, table)
|
22
|
+
operation = ast.keys.first
|
23
|
+
self.send("process_#{operation}".to_sym, ast[operation], table) if self.respond_to?("process_#{operation}".to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def process_and(ast, table)
|
28
|
+
process(ast[:left], table).and(process(ast[:right], table))
|
29
|
+
end
|
30
|
+
|
31
|
+
def process_or(ast, table)
|
32
|
+
process(ast[:left], table).or(process(ast[:right], table))
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_eq(ast, table)
|
36
|
+
check_column!(ast[:left], table)
|
37
|
+
table[ast[:left].to_sym].eq(parse_value(ast[:right]))
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_not_eq(ast, table)
|
41
|
+
check_column!(ast[:left], table)
|
42
|
+
table[ast[:left].to_sym].not_eq(parse_value(ast[:right]))
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_matches(ast, table)
|
46
|
+
check_column!(ast[:left], table)
|
47
|
+
table[ast[:left].to_sym].matches(parse_value(ast[:right]))
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_lt(ast, table)
|
51
|
+
check_column!(ast[:left], table)
|
52
|
+
table[ast[:left].to_sym].lt(parse_value(ast[:right]))
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_lteq(ast, table)
|
56
|
+
check_column!(ast[:left], table)
|
57
|
+
table[ast[:left].to_sym].lteq(parse_value(ast[:right]))
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_gt(ast, table)
|
61
|
+
check_column!(ast[:left], table)
|
62
|
+
table[ast[:left].to_sym].gt(parse_value(ast[:right]))
|
63
|
+
end
|
64
|
+
|
65
|
+
def process_gteq(ast, table)
|
66
|
+
check_column!(ast[:left], table)
|
67
|
+
table[ast[:left].to_sym].gteq(parse_value(ast[:right]))
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_value(value)
|
71
|
+
type = value.keys.first
|
72
|
+
case type
|
73
|
+
when :nil
|
74
|
+
return nil
|
75
|
+
when :boolean
|
76
|
+
return value[:boolean] == "true"
|
77
|
+
else
|
78
|
+
return value[type].to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/test/helper.rb
CHANGED
@@ -15,6 +15,7 @@ end
|
|
15
15
|
|
16
16
|
require 'test/unit'
|
17
17
|
require 'shoulda'
|
18
|
+
require 'mocha'
|
18
19
|
|
19
20
|
require 'thread'
|
20
21
|
require 'rails/all'
|
@@ -59,4 +60,26 @@ class Team < ActiveRecord::Base
|
|
59
60
|
end
|
60
61
|
|
61
62
|
class Test::Unit::TestCase
|
63
|
+
def setup_fixtures
|
64
|
+
Player.create!(:name => "Patrick 'Paddy' Carew", :position => 'lock', :first_cap => '1899-06-24', :active => false)
|
65
|
+
Player.create!(:name => "Charlie Ellis", :position => 'flanker', :first_cap => '1899-06-24', :active => false)
|
66
|
+
Player.create!(:name => "Arthur Corfe", :position => 'flanker', :first_cap => '1899-07-22', :active => false)
|
67
|
+
Player.create!(:name => "Syd Miller", :position => 'wing', :first_cap => '1899-08-05', :active => false)
|
68
|
+
Player.create!(:name => "Charlie Redwood", :position => 'wing', :first_cap => '1903-08-15', :active => false)
|
69
|
+
Player.create!(:name => "Patrick 'Pat' Walsh", :position => 'no. 8', :first_cap => '1904-07-02', :active => false)
|
70
|
+
Player.create!(:name => "John Manning", :position => 'fly-half', :first_cap => '1904-07-23', :active => false)
|
71
|
+
Player.create!(:name => "Dally Messenger", :position => 'wing', :first_cap => '1907-08-03', :active => false)
|
72
|
+
Player.create!(:name => "Salesi Ma'afu", :position => 'prop', :first_cap => '2010-06-05', :active => true)
|
73
|
+
Player.create!(:name => "James Slipper", :position => 'prop', :first_cap => nil, :active => true)
|
74
|
+
end
|
75
|
+
|
76
|
+
def setup_database
|
77
|
+
ActiveRecord::Base.establish_connection({
|
78
|
+
:adapter => 'sqlite3',
|
79
|
+
:database => ':memory:',
|
80
|
+
:verbosity => 'quiet'
|
81
|
+
})
|
82
|
+
Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
|
83
|
+
AddUsers.migrate(:up)
|
84
|
+
end
|
62
85
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class OrderParserTest < Test::Unit::TestCase
|
4
|
+
context 'OrderParser' do
|
5
|
+
setup do
|
6
|
+
@parser = Wherewolf::Order::Parser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "operands" do
|
10
|
+
should 'parse column name' do
|
11
|
+
result = @parser.parse('name')
|
12
|
+
assert_equal({ :column => "name", :direction => nil }, result)
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'parse column with desc' do
|
16
|
+
result = @parser.parse('name desc')
|
17
|
+
assert_equal({ :column => "name", :direction => "desc" }, result)
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'parse column with asc' do
|
21
|
+
result = @parser.parse('name asc')
|
22
|
+
assert_equal({ :column => "name", :direction => "asc" }, result)
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'parse multiple comma seperated order expressions' do
|
26
|
+
result = @parser.parse('name asc, age desc')
|
27
|
+
assert_equal([ { :column => "name", :direction => "asc" }, { :column => "age", :direction => "desc" } ], result)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class OrderProcessorTest < 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 'OrderProcessor' do
|
18
|
+
setup do
|
19
|
+
setup_database
|
20
|
+
setup_fixtures
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'be a Wherewolf::Processor' do
|
24
|
+
Wherewolf::Order::Processor.is_a?(Wherewolf::Processor)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'Parsing' do
|
28
|
+
context 'Error' do
|
29
|
+
should 'be raised is there is a parser error' do
|
30
|
+
assert_raise Wherewolf::ParseError do
|
31
|
+
Player.order_query('name up')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'check that the column is in the list' do
|
36
|
+
Wherewolf::Order::Processor.any_instance.expects(:check_column!).once
|
37
|
+
Player.order_query('name asc')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'construct an asc sort' do
|
42
|
+
players = Player.order_query('name asc')
|
43
|
+
assert_equal Player.order('name asc').all.map(&:name), players.all.map(&:name)
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'construct an desc sort' do
|
47
|
+
players = Player.order_query('name desc')
|
48
|
+
assert_equal Player.order('name desc').all.map(&:name), players.all.map(&:name)
|
49
|
+
end
|
50
|
+
|
51
|
+
should 'default to asc' do
|
52
|
+
players = Player.order_query('name')
|
53
|
+
assert_equal Player.order('name asc').all.map(&:name), players.all.map(&:name)
|
54
|
+
end
|
55
|
+
|
56
|
+
should 'handle multiple sorts' do
|
57
|
+
players = Player.order_query('name asc, position asc')
|
58
|
+
assert_equal Player.order('name asc, position asc').all.map(&:name), players.all.map(&:name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/test/processor_test.rb
CHANGED
@@ -1,153 +1,21 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class ProcessorTest < Test::Unit::TestCase
|
4
|
-
|
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
|
4
|
+
context 'WhereProcessor' do
|
18
5
|
setup do
|
19
|
-
|
20
|
-
:adapter => 'sqlite3',
|
21
|
-
:database => ':memory:',
|
22
|
-
:verbosity => 'quiet'
|
23
|
-
})
|
24
|
-
Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
|
25
|
-
AddUsers.migrate(:up)
|
6
|
+
setup_database
|
26
7
|
setup_fixtures
|
27
8
|
end
|
28
9
|
|
29
|
-
context '
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
should 'allow retrieval of the error position' do
|
38
|
-
begin
|
39
|
-
Player.from_query('name ~= "Patrick%" || (position = "fail)')
|
40
|
-
rescue Wherewolf::ParseError => e
|
41
|
-
assert_equal 28, e.position
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
should 'show a nice debug error' do
|
46
|
-
begin
|
47
|
-
Player.from_query('name ~= "Patrick%" || (position = "fail)')
|
48
|
-
rescue Wherewolf::ParseError => e
|
49
|
-
assert_equal "Parsing error occured at character 28", e.error_message
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
should 'to_s should print out nice error' do
|
54
|
-
begin
|
55
|
-
Player.from_query('name ~= "Patrick%" || (position = "fail)')
|
56
|
-
rescue Wherewolf::ParseError => e
|
57
|
-
assert_equal "Parsing error occured at character 28", e.to_s
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
should 'be raised if the requested column is not in the list' do
|
62
|
-
assert_raise Wherewolf::ParseError do
|
63
|
-
Player.from_query('shoe_size > 10')
|
64
|
-
end
|
10
|
+
context 'check_column!' do
|
11
|
+
should 'raise a ParseError if column is not in the mode' do
|
12
|
+
value = mock
|
13
|
+
value.stubs(:to_sym).returns(:show_size)
|
14
|
+
value.stubs(:to_s).returns('show_size')
|
15
|
+
value.stubs(:offset).returns(0)
|
16
|
+
assert_raise Parslet::ParseFailed do
|
17
|
+
Wherewolf::Processor.new.send(:check_column!, value, Player.arel_table)
|
65
18
|
end
|
66
|
-
|
67
|
-
should 'show a nice debug error if the requested column is not in the list' do
|
68
|
-
begin
|
69
|
-
Player.from_query('shoe_size > 10')
|
70
|
-
rescue Wherewolf::ParseError => e
|
71
|
-
assert_equal "Parsing error occured at character 0", e.to_s
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
should 'construct simple boolean statements' do
|
77
|
-
player = Player.from_query('name = "Charlie Ellis"')
|
78
|
-
assert_equal 1, player.count
|
79
|
-
assert_equal 'Charlie Ellis', player.first.name
|
80
|
-
end
|
81
|
-
|
82
|
-
should 'construct two boolean statments' do
|
83
|
-
player = Player.from_query('position=lock && first_cap < 2010-01-01')
|
84
|
-
assert_equal 1, player.count
|
85
|
-
assert_equal "Patrick 'Paddy' Carew", player.first.name
|
86
|
-
end
|
87
|
-
|
88
|
-
should 'contruct nested brackets' do
|
89
|
-
player = Player.from_query("(position = wing || position = lock) && first_cap <= 1905-01-01").order('first_cap')
|
90
|
-
assert_equal 3, player.count
|
91
|
-
assert_equal "Patrick 'Paddy' Carew", player[0].name
|
92
|
-
assert_equal "Syd Miller", player[1].name
|
93
|
-
assert_equal "Charlie Redwood", player[2].name
|
94
|
-
end
|
95
|
-
|
96
|
-
should 'handle matches' do
|
97
|
-
player = Player.from_query('name ~= "James%"')
|
98
|
-
assert_equal 1, player.count
|
99
|
-
assert_equal "James Slipper", player.first.name
|
100
|
-
end
|
101
|
-
|
102
|
-
should 'handle nulls' do
|
103
|
-
player = Player.from_query("first_cap = null")
|
104
|
-
assert_equal 1, player.count
|
105
|
-
assert_equal "James Slipper", player.first.name
|
106
|
-
end
|
107
|
-
|
108
|
-
should 'handle booleans' do
|
109
|
-
player = Player.from_query("active = true")
|
110
|
-
assert_equal 2, player.count
|
111
|
-
player = Player.from_query("active = false")
|
112
|
-
assert_equal 8, player.count
|
113
|
-
end
|
114
|
-
|
115
|
-
should 'handle dates' do
|
116
|
-
player = Player.from_query("first_cap > 2000-01-01")
|
117
|
-
assert_equal 1, player.count
|
118
|
-
assert_equal "Salesi Ma'afu", player.first.name
|
119
|
-
end
|
120
|
-
|
121
|
-
should 'process =' do
|
122
|
-
player = Player.from_query('name = "Charlie Ellis"')
|
123
|
-
assert_equal 1, player.count
|
124
|
-
assert_equal 'Charlie Ellis', player.first.name
|
125
|
-
end
|
126
|
-
|
127
|
-
should 'process !=' do
|
128
|
-
player = Player.from_query('name != "Charlie Ellis"')
|
129
|
-
assert_equal 9, player.count
|
130
|
-
assert_equal false, player.map(&:name).include?('Charlie Ellis')
|
131
|
-
end
|
132
|
-
|
133
|
-
should 'process >' do
|
134
|
-
player = Player.from_query('first_cap > 1907-08-03')
|
135
|
-
assert_equal 1, player.count
|
136
|
-
end
|
137
|
-
|
138
|
-
should 'process >=' do
|
139
|
-
player = Player.from_query('first_cap >= 1907-08-03')
|
140
|
-
assert_equal 2, player.count
|
141
|
-
end
|
142
|
-
|
143
|
-
should 'process <' do
|
144
|
-
player = Player.from_query('first_cap < 1907-08-03')
|
145
|
-
assert_equal 7, player.count
|
146
|
-
end
|
147
|
-
|
148
|
-
should 'process <=' do
|
149
|
-
player = Player.from_query('first_cap <= 1907-08-03')
|
150
|
-
assert_equal 8, player.count
|
151
19
|
end
|
152
20
|
end
|
153
21
|
end
|
data/test/railtie_test.rb
CHANGED
@@ -2,12 +2,12 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class RailtieTest < Test::Unit::TestCase
|
4
4
|
context 'Railtie' do
|
5
|
-
should 'not enable
|
6
|
-
assert_equal false, Team.respond_to?(:
|
5
|
+
should 'not enable where_query if has_query_parsing is not called' do
|
6
|
+
assert_equal false, Team.respond_to?(:where_query)
|
7
7
|
end
|
8
8
|
|
9
|
-
should 'enable
|
10
|
-
assert_equal true, Player.respond_to?(:
|
9
|
+
should 'enable where_query if has_query_parsing is called' do
|
10
|
+
assert_equal true, Player.respond_to?(:where_query)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
context '
|
3
|
+
class WhereParserTest < Test::Unit::TestCase
|
4
|
+
context 'WhereParser' do
|
5
5
|
setup do
|
6
|
-
@parser = Wherewolf::Parser.new
|
6
|
+
@parser = Wherewolf::Where::Parser.new
|
7
7
|
end
|
8
8
|
|
9
9
|
context "operands" do
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class WhereProcessorTest < Test::Unit::TestCase
|
4
|
+
context 'WhereProcessor' do
|
5
|
+
setup do
|
6
|
+
setup_database
|
7
|
+
setup_fixtures
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'be a Wherewolf::Processor' do
|
11
|
+
Wherewolf::Where::Processor.is_a?(Wherewolf::Processor)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'Parsing' do
|
15
|
+
context 'Error' do
|
16
|
+
should 'be raised is there is a parser error' do
|
17
|
+
assert_raise Wherewolf::ParseError do
|
18
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'allow retrieval of the error position' do
|
23
|
+
begin
|
24
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
25
|
+
rescue Wherewolf::ParseError => e
|
26
|
+
assert_equal 28, e.position
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'show a nice debug error' do
|
31
|
+
begin
|
32
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
33
|
+
rescue Wherewolf::ParseError => e
|
34
|
+
assert_equal "Parsing error occured at character 28", e.error_message
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'to_s should print out nice error' do
|
39
|
+
begin
|
40
|
+
Player.where_query('name ~= "Patrick%" || (position = "fail)')
|
41
|
+
rescue Wherewolf::ParseError => e
|
42
|
+
assert_equal "Parsing error occured at character 28", e.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'be raised if the requested column is not in the list' do
|
47
|
+
assert_raise Wherewolf::ParseError do
|
48
|
+
Player.where_query('shoe_size > 10')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'check to see if the column is allowed' do
|
53
|
+
Wherewolf::Where::Processor.any_instance.expects(:check_column!).once
|
54
|
+
begin
|
55
|
+
Player.where_query('shoe_size > 10')
|
56
|
+
rescue Wherewolf::ParseError => e
|
57
|
+
assert_equal "Parsing error occured at character 0", e.to_s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'construct simple boolean statements' do
|
63
|
+
player = Player.where_query('name = "Charlie Ellis"')
|
64
|
+
assert_equal 1, player.count
|
65
|
+
assert_equal 'Charlie Ellis', player.first.name
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'construct two boolean statments' do
|
69
|
+
player = Player.where_query('position=lock && first_cap < 2010-01-01')
|
70
|
+
assert_equal 1, player.count
|
71
|
+
assert_equal "Patrick 'Paddy' Carew", player.first.name
|
72
|
+
end
|
73
|
+
|
74
|
+
should 'contruct nested brackets' do
|
75
|
+
player = Player.where_query("(position = wing || position = lock) && first_cap <= 1905-01-01").order('first_cap')
|
76
|
+
assert_equal 3, player.count
|
77
|
+
assert_equal "Patrick 'Paddy' Carew", player[0].name
|
78
|
+
assert_equal "Syd Miller", player[1].name
|
79
|
+
assert_equal "Charlie Redwood", player[2].name
|
80
|
+
end
|
81
|
+
|
82
|
+
should 'handle matches' do
|
83
|
+
player = Player.where_query('name ~= "James%"')
|
84
|
+
assert_equal 1, player.count
|
85
|
+
assert_equal "James Slipper", player.first.name
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'handle nulls' do
|
89
|
+
player = Player.where_query("first_cap = null")
|
90
|
+
assert_equal 1, player.count
|
91
|
+
assert_equal "James Slipper", player.first.name
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'handle booleans' do
|
95
|
+
player = Player.where_query("active = true")
|
96
|
+
assert_equal 2, player.count
|
97
|
+
player = Player.where_query("active = false")
|
98
|
+
assert_equal 8, player.count
|
99
|
+
end
|
100
|
+
|
101
|
+
should 'handle dates' do
|
102
|
+
player = Player.where_query("first_cap > 2000-01-01")
|
103
|
+
assert_equal 1, player.count
|
104
|
+
assert_equal "Salesi Ma'afu", player.first.name
|
105
|
+
end
|
106
|
+
|
107
|
+
should 'process =' do
|
108
|
+
player = Player.where_query('name = "Charlie Ellis"')
|
109
|
+
assert_equal 1, player.count
|
110
|
+
assert_equal 'Charlie Ellis', player.first.name
|
111
|
+
end
|
112
|
+
|
113
|
+
should 'process !=' do
|
114
|
+
player = Player.where_query('name != "Charlie Ellis"')
|
115
|
+
assert_equal 9, player.count
|
116
|
+
assert_equal false, player.map(&:name).include?('Charlie Ellis')
|
117
|
+
end
|
118
|
+
|
119
|
+
should 'process >' do
|
120
|
+
player = Player.where_query('first_cap > 1907-08-03')
|
121
|
+
assert_equal 1, player.count
|
122
|
+
end
|
123
|
+
|
124
|
+
should 'process >=' do
|
125
|
+
player = Player.where_query('first_cap >= 1907-08-03')
|
126
|
+
assert_equal 2, player.count
|
127
|
+
end
|
128
|
+
|
129
|
+
should 'process <' do
|
130
|
+
player = Player.where_query('first_cap < 1907-08-03')
|
131
|
+
assert_equal 7, player.count
|
132
|
+
end
|
133
|
+
|
134
|
+
should 'process <=' do
|
135
|
+
player = Player.where_query('first_cap <= 1907-08-03')
|
136
|
+
assert_equal 8, player.count
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/wherewolf.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "wherewolf"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Myles Eftos"]
|
@@ -25,21 +25,27 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.rdoc",
|
26
26
|
"Rakefile",
|
27
27
|
"lib/wherewolf.rb",
|
28
|
+
"lib/wherewolf/order/parser.rb",
|
29
|
+
"lib/wherewolf/order/processor.rb",
|
28
30
|
"lib/wherewolf/parse_error.rb",
|
29
|
-
"lib/wherewolf/parser.rb",
|
30
31
|
"lib/wherewolf/processor.rb",
|
31
32
|
"lib/wherewolf/railtie.rb",
|
33
|
+
"lib/wherewolf/where/parser.rb",
|
34
|
+
"lib/wherewolf/where/processor.rb",
|
32
35
|
"test/helper.rb",
|
36
|
+
"test/order/parser_test.rb",
|
37
|
+
"test/order/processor_test.rb",
|
33
38
|
"test/parse_error_test.rb",
|
34
|
-
"test/parser_test.rb",
|
35
39
|
"test/processor_test.rb",
|
36
40
|
"test/railtie_test.rb",
|
41
|
+
"test/where/parser_test.rb",
|
42
|
+
"test/where/processor_test.rb",
|
37
43
|
"wherewolf.gemspec"
|
38
44
|
]
|
39
45
|
s.homepage = "http://github.com/madpilot/wherewolf"
|
40
46
|
s.licenses = ["MIT"]
|
41
47
|
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = "1.8.
|
48
|
+
s.rubygems_version = "1.8.24"
|
43
49
|
s.summary = "Makes filtering and searching to your REST API crazy easy."
|
44
50
|
|
45
51
|
if s.respond_to? :specification_version then
|
@@ -57,7 +63,7 @@ Gem::Specification.new do |s|
|
|
57
63
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
58
64
|
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
59
65
|
s.add_development_dependency(%q<rails>, [">= 0"])
|
60
|
-
s.add_development_dependency(%q<
|
66
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
61
67
|
else
|
62
68
|
s.add_dependency(%q<arel>, [">= 0"])
|
63
69
|
s.add_dependency(%q<parslet>, [">= 0"])
|
@@ -70,7 +76,7 @@ Gem::Specification.new do |s|
|
|
70
76
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
71
77
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
72
78
|
s.add_dependency(%q<rails>, [">= 0"])
|
73
|
-
s.add_dependency(%q<
|
79
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
74
80
|
end
|
75
81
|
else
|
76
82
|
s.add_dependency(%q<arel>, [">= 0"])
|
@@ -84,7 +90,7 @@ Gem::Specification.new do |s|
|
|
84
90
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
85
91
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
86
92
|
s.add_dependency(%q<rails>, [">= 0"])
|
87
|
-
s.add_dependency(%q<
|
93
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wherewolf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-10-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: arel
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: parslet
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: shoulda
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: rdoc
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: bundler
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,10 +85,15 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: jeweler
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ! '>='
|
@@ -76,10 +101,15 @@ dependencies:
|
|
76
101
|
version: '0'
|
77
102
|
type: :development
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
80
110
|
- !ruby/object:Gem::Dependency
|
81
111
|
name: guard
|
82
|
-
requirement:
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
83
113
|
none: false
|
84
114
|
requirements:
|
85
115
|
- - ! '>='
|
@@ -87,10 +117,15 @@ dependencies:
|
|
87
117
|
version: '0'
|
88
118
|
type: :development
|
89
119
|
prerelease: false
|
90
|
-
version_requirements:
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
91
126
|
- !ruby/object:Gem::Dependency
|
92
127
|
name: guard-test
|
93
|
-
requirement:
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
94
129
|
none: false
|
95
130
|
requirements:
|
96
131
|
- - ! '>='
|
@@ -98,10 +133,15 @@ dependencies:
|
|
98
133
|
version: '0'
|
99
134
|
type: :development
|
100
135
|
prerelease: false
|
101
|
-
version_requirements:
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
102
142
|
- !ruby/object:Gem::Dependency
|
103
143
|
name: simplecov
|
104
|
-
requirement:
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
105
145
|
none: false
|
106
146
|
requirements:
|
107
147
|
- - ! '>='
|
@@ -109,10 +149,15 @@ dependencies:
|
|
109
149
|
version: '0'
|
110
150
|
type: :development
|
111
151
|
prerelease: false
|
112
|
-
version_requirements:
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
113
158
|
- !ruby/object:Gem::Dependency
|
114
159
|
name: sqlite3
|
115
|
-
requirement:
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
116
161
|
none: false
|
117
162
|
requirements:
|
118
163
|
- - ! '>='
|
@@ -120,10 +165,15 @@ dependencies:
|
|
120
165
|
version: '0'
|
121
166
|
type: :development
|
122
167
|
prerelease: false
|
123
|
-
version_requirements:
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
124
174
|
- !ruby/object:Gem::Dependency
|
125
175
|
name: rails
|
126
|
-
requirement:
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
127
177
|
none: false
|
128
178
|
requirements:
|
129
179
|
- - ! '>='
|
@@ -131,10 +181,15 @@ dependencies:
|
|
131
181
|
version: '0'
|
132
182
|
type: :development
|
133
183
|
prerelease: false
|
134
|
-
version_requirements:
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
135
190
|
- !ruby/object:Gem::Dependency
|
136
|
-
name:
|
137
|
-
requirement:
|
191
|
+
name: mocha
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
138
193
|
none: false
|
139
194
|
requirements:
|
140
195
|
- - ! '>='
|
@@ -142,7 +197,12 @@ dependencies:
|
|
142
197
|
version: '0'
|
143
198
|
type: :development
|
144
199
|
prerelease: false
|
145
|
-
version_requirements:
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
146
206
|
description: Wherewolf allows you to consume search terms as strings without worrying
|
147
207
|
about database injections. It parses the query and converts it into ARel. It's great
|
148
208
|
for creating filterable REST APIs.
|
@@ -161,15 +221,21 @@ files:
|
|
161
221
|
- README.rdoc
|
162
222
|
- Rakefile
|
163
223
|
- lib/wherewolf.rb
|
224
|
+
- lib/wherewolf/order/parser.rb
|
225
|
+
- lib/wherewolf/order/processor.rb
|
164
226
|
- lib/wherewolf/parse_error.rb
|
165
|
-
- lib/wherewolf/parser.rb
|
166
227
|
- lib/wherewolf/processor.rb
|
167
228
|
- lib/wherewolf/railtie.rb
|
229
|
+
- lib/wherewolf/where/parser.rb
|
230
|
+
- lib/wherewolf/where/processor.rb
|
168
231
|
- test/helper.rb
|
232
|
+
- test/order/parser_test.rb
|
233
|
+
- test/order/processor_test.rb
|
169
234
|
- test/parse_error_test.rb
|
170
|
-
- test/parser_test.rb
|
171
235
|
- test/processor_test.rb
|
172
236
|
- test/railtie_test.rb
|
237
|
+
- test/where/parser_test.rb
|
238
|
+
- test/where/processor_test.rb
|
173
239
|
- wherewolf.gemspec
|
174
240
|
homepage: http://github.com/madpilot/wherewolf
|
175
241
|
licenses:
|
@@ -186,7 +252,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
252
|
version: '0'
|
187
253
|
segments:
|
188
254
|
- 0
|
189
|
-
hash:
|
255
|
+
hash: 3659032346363356481
|
190
256
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
257
|
none: false
|
192
258
|
requirements:
|
@@ -195,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
261
|
version: '0'
|
196
262
|
requirements: []
|
197
263
|
rubyforge_project:
|
198
|
-
rubygems_version: 1.8.
|
264
|
+
rubygems_version: 1.8.24
|
199
265
|
signing_key:
|
200
266
|
specification_version: 3
|
201
267
|
summary: Makes filtering and searching to your REST API crazy easy.
|
data/lib/wherewolf/parser.rb
DELETED
@@ -1,55 +0,0 @@
|
|
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(:matches) { str('~=') }
|
14
|
-
rule(:lt) { str('<') }
|
15
|
-
rule(:lteq) { str('<=') }
|
16
|
-
rule(:gt) { str('>') }
|
17
|
-
rule(:gteq) { str('>=') }
|
18
|
-
|
19
|
-
# Operators
|
20
|
-
rule(:and_operator) { str('&&') }
|
21
|
-
rule(:or_operator) { str('||') }
|
22
|
-
|
23
|
-
# Operand
|
24
|
-
rule(:null) { str("null").as(:nil) }
|
25
|
-
rule(:boolean) { str("true").as(:boolean) | str("false").as(:boolean) }
|
26
|
-
rule(:number) { match('[-+]?([0-9]*\.)?[0-9]').repeat(1).as(:number) }
|
27
|
-
rule(:double_quote_string) do
|
28
|
-
str('"') >>
|
29
|
-
(
|
30
|
-
(str('\\') >> any) |
|
31
|
-
(str('"').absent? >> any)
|
32
|
-
).repeat.as(:string) >>
|
33
|
-
str('"')
|
34
|
-
end
|
35
|
-
rule(:literal) { match('[a-zA-Z0-9\-_]').repeat(1) }
|
36
|
-
rule(:identifier) { null | boolean | number | double_quote_string | literal.as(:string) }
|
37
|
-
|
38
|
-
# Grammar
|
39
|
-
rule(:compare_eq) { (literal.as(:left) >> space? >> eq >> space? >> identifier.as(:right)).as(:eq) }
|
40
|
-
rule(:compare_not_eq) { (literal.as(:left) >> space? >> not_eq >> space? >> identifier.as(:right)).as(:not_eq) }
|
41
|
-
rule(:compare_matches) { (literal.as(:left) >> space? >> matches >> space? >> identifier.as(:right)).as(:matches) }
|
42
|
-
rule(:compare_lt) { (literal.as(:left) >> space? >> lt >> space? >> identifier.as(:right)).as(:lt) }
|
43
|
-
rule(:compare_lteq) { (literal.as(:left) >> space? >> lteq >> space? >> identifier.as(:right)).as(:lteq) }
|
44
|
-
rule(:compare_gt) { (literal.as(:left) >> space? >> gt >> space? >> identifier.as(:right)).as(:gt) }
|
45
|
-
rule(:compare_gteq) { (literal.as(:left) >> space? >> gteq >> space? >> identifier.as(:right)).as(:gteq) }
|
46
|
-
|
47
|
-
rule(:compare) { compare_eq | compare_not_eq | compare_matches | compare_lteq | compare_lt | compare_gteq | compare_gt }
|
48
|
-
|
49
|
-
rule(:primary) { left_parenthesis >> space? >> or_operation >> space? >> right_parenthesis | compare }
|
50
|
-
rule(:and_operation) { (primary.as(:left) >> space? >> and_operator >> space? >> and_operation.as(:right)).as(:and) | primary }
|
51
|
-
rule(:or_operation) { (and_operation.as(:left) >> space? >> or_operator >> space? >> or_operation.as(:right)).as(:or) | and_operation }
|
52
|
-
|
53
|
-
root :or_operation
|
54
|
-
end
|
55
|
-
end
|