wherewolf 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|