wherewolf 0.4.1 → 0.5.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/README.rdoc +24 -4
- data/Rakefile +1 -1
- data/lib/wherewolf.rb +6 -0
- data/lib/wherewolf/order/processor.rb +7 -6
- data/lib/wherewolf/processor.rb +25 -1
- data/lib/wherewolf/where/processor.rb +4 -4
- data/test/helper.rb +9 -0
- data/test/processor_test.rb +43 -1
- data/test/railtie_test.rb +12 -0
- data/wherewolf.gemspec +1 -1
- metadata +26 -26
data/README.rdoc
CHANGED
@@ -40,7 +40,29 @@ 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 "where_query"
|
43
|
+
This will add the "where_query" and "order_query" methods, which you pass your query string in to.
|
44
|
+
|
45
|
+
has_query_parsing can take two options: whitelist and blacklist which allow you to set which columns consumers can search against
|
46
|
+
|
47
|
+
Setting whitelist will mean ONLY those columns will be searchable
|
48
|
+
|
49
|
+
class Player < ActiveRecord::Base
|
50
|
+
has_query_parsing :whitelist => [ :name ] # Only name will be searchable
|
51
|
+
end
|
52
|
+
|
53
|
+
Setting blacklist will remove those columns from the list
|
54
|
+
|
55
|
+
class Player < ActiveRecord::Base
|
56
|
+
has_query_parsing :blacklist => [ :name ] # Name will not be searchable
|
57
|
+
end
|
58
|
+
|
59
|
+
Both whitelist and blacklist can take a proc if you want to lazy evaluate
|
60
|
+
|
61
|
+
class Player < ActiveRecord::Base
|
62
|
+
has_query_parsing :whitelist => proc { |model| model.accessible_attributes }
|
63
|
+
end
|
64
|
+
|
65
|
+
would restrict the searchable columns to those exposed by accessible_attributes
|
44
66
|
|
45
67
|
== Example
|
46
68
|
|
@@ -79,7 +101,7 @@ You can also supply an order_query to handle ordering
|
|
79
101
|
player = Player.order_query("name desc, position desc")
|
80
102
|
# You can also have multiple order columns
|
81
103
|
|
82
|
-
Of course you can nest them...
|
104
|
+
Of course, you can nest them...
|
83
105
|
|
84
106
|
player = Player.where_query("first_cap != null").order_query('name desc')
|
85
107
|
|
@@ -114,9 +136,7 @@ You can get the character number by:
|
|
114
136
|
* Better error messages (Give a clue as to why parsing failed)
|
115
137
|
* Aliases such for operators, such as 'and', 'or' etc
|
116
138
|
* Allow single quotes around strings
|
117
|
-
* Allow where_query to nested (ie Player.where('first_cap < 2000-01-01').where_query('active = true')
|
118
139
|
* More edge case testing
|
119
|
-
* Abillity to filter columns that are searchable
|
120
140
|
* Ability to call named scopes
|
121
141
|
|
122
142
|
== Contributing to wherewolf
|
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.5.0"
|
26
26
|
# dependencies defined in Gemfile
|
27
27
|
end
|
28
28
|
Jeweler::RubygemsDotOrgTasks.new
|
data/lib/wherewolf.rb
CHANGED
@@ -12,6 +12,12 @@ module Wherewolf
|
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
def has_query_parsing(options = {})
|
15
|
+
self.class_eval <<-EOV
|
16
|
+
def self.wherewolf_options
|
17
|
+
#{options}
|
18
|
+
end
|
19
|
+
EOV
|
20
|
+
|
15
21
|
self.class_eval do
|
16
22
|
scope :where_query, lambda { |query| Wherewolf::Where::Processor.parse(self, query) }
|
17
23
|
scope :order_query, lambda { |query| Wherewolf::Order::Processor.parse(self, query) }
|
@@ -4,22 +4,23 @@ module Wherewolf
|
|
4
4
|
module Order
|
5
5
|
class Processor < Wherewolf::Processor
|
6
6
|
def self.parse(model, query)
|
7
|
-
instance = self.new
|
8
|
-
instance.parse(
|
7
|
+
instance = self.new(model, model.wherewolf_options)
|
8
|
+
instance.parse(query)
|
9
9
|
end
|
10
10
|
|
11
|
-
def parse(
|
11
|
+
def parse(query)
|
12
12
|
begin
|
13
13
|
ast = Wherewolf::Order::Parser.new.parse(query)
|
14
|
-
process(ast
|
14
|
+
process(ast)
|
15
15
|
rescue Parslet::ParseFailed => error
|
16
16
|
raise Wherewolf::ParseError, error
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def process(ast
|
20
|
+
def process(ast)
|
21
21
|
ast = [ast] unless ast.is_a?(Array)
|
22
|
-
table = model.arel_table
|
22
|
+
table = self.model.arel_table
|
23
|
+
model = self.model
|
23
24
|
ast.each do |order|
|
24
25
|
check_column!(order[:column], table)
|
25
26
|
direction = (order[:direction] || 'asc').to_sym
|
data/lib/wherewolf/processor.rb
CHANGED
@@ -1,12 +1,36 @@
|
|
1
1
|
module Wherewolf
|
2
2
|
class Processor
|
3
|
+
attr_accessor :model
|
4
|
+
def initialize(model, options = {})
|
5
|
+
@model = model
|
6
|
+
@options = options
|
7
|
+
end
|
3
8
|
protected
|
4
9
|
def check_column!(value, table)
|
5
|
-
unless
|
10
|
+
unless columns(table).include?(value.to_sym)
|
6
11
|
source = Parslet::Source.new(value.to_s)
|
7
12
|
cause = Parslet::Cause.new('Column not found', source, value.offset, [])
|
8
13
|
raise Parslet::ParseFailed.new('Column not found', cause)
|
9
14
|
end
|
10
15
|
end
|
16
|
+
|
17
|
+
def columns(table)
|
18
|
+
columns = table.columns.map(&:name)
|
19
|
+
columns = columns & whitelist if whitelist
|
20
|
+
columns -= blacklist if blacklist
|
21
|
+
columns
|
22
|
+
end
|
23
|
+
|
24
|
+
def whitelist
|
25
|
+
return @options[:whitelist].call if @options[:whitelist].is_a?(Proc)
|
26
|
+
return @options[:whitelist] if @options[:whitelist]
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def blacklist
|
31
|
+
return @options[:blacklist].call if @options[:blacklist].is_a?(Proc)
|
32
|
+
return @options[:blacklist] if @options[:blacklist]
|
33
|
+
return nil
|
34
|
+
end
|
11
35
|
end
|
12
36
|
end
|
@@ -4,15 +4,15 @@ module Wherewolf
|
|
4
4
|
module Where
|
5
5
|
class Processor < Wherewolf::Processor
|
6
6
|
def self.parse(model, query)
|
7
|
-
instance = self.new
|
8
|
-
instance.parse(
|
7
|
+
instance = self.new(model, model.wherewolf_options)
|
8
|
+
instance.parse(query)
|
9
9
|
end
|
10
10
|
|
11
|
-
def parse(
|
11
|
+
def parse(query)
|
12
12
|
begin
|
13
13
|
ast = Wherewolf::Where::Parser.new.parse(query)
|
14
14
|
table = model.arel_table
|
15
|
-
model.where(process(ast, table))
|
15
|
+
self.model.where(process(ast, table))
|
16
16
|
rescue Parslet::ParseFailed => error
|
17
17
|
raise Wherewolf::ParseError, error
|
18
18
|
end
|
data/test/helper.rb
CHANGED
@@ -44,11 +44,16 @@ class AddUsers < ActiveRecord::Migration
|
|
44
44
|
create_table :teams do |t|
|
45
45
|
t.string :team
|
46
46
|
end
|
47
|
+
|
48
|
+
create_table :countries do |t|
|
49
|
+
t.string :team
|
50
|
+
end
|
47
51
|
end
|
48
52
|
|
49
53
|
def down
|
50
54
|
drop_table :users
|
51
55
|
drop_table :teams
|
56
|
+
drop_table :countries
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
@@ -59,6 +64,10 @@ end
|
|
59
64
|
class Team < ActiveRecord::Base
|
60
65
|
end
|
61
66
|
|
67
|
+
class Country < ActiveRecord::Base
|
68
|
+
has_query_parsing :whitelist => [ :name ]
|
69
|
+
end
|
70
|
+
|
62
71
|
class Test::Unit::TestCase
|
63
72
|
def setup_fixtures
|
64
73
|
Player.create!(:name => "Patrick 'Paddy' Carew", :position => 'lock', :first_cap => '1899-06-24', :active => false)
|
data/test/processor_test.rb
CHANGED
@@ -14,9 +14,51 @@ class ProcessorTest < Test::Unit::TestCase
|
|
14
14
|
value.stubs(:to_s).returns('show_size')
|
15
15
|
value.stubs(:offset).returns(0)
|
16
16
|
assert_raise Parslet::ParseFailed do
|
17
|
-
Wherewolf::Processor.new.send(:check_column!, value, Player.arel_table)
|
17
|
+
Wherewolf::Processor.new(Player).send(:check_column!, value, Player.arel_table)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
context 'columns' do
|
23
|
+
should 'return all the ARel table columns if options[:whitelist] and options[:blacklist] are not set' do
|
24
|
+
assert_equal Player.arel_table.columns.map(&:name), Wherewolf::Processor.new(Player).send(:columns, Player.arel_table)
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'return just the columns in the intersection of columns and whitelist if whitelist is set' do
|
28
|
+
assert_equal [ :id, :name ], Wherewolf::Processor.new(Player, :whitelist => [ :not_in_column_set, :id, :name ]).send(:columns, Player.arel_table)
|
29
|
+
end
|
30
|
+
|
31
|
+
should 'return columns minus items in options[:blacklist] if options[:blacklist] is set' do
|
32
|
+
assert_equal [ :name, :position, :active ], Wherewolf::Processor.new(Player, :blacklist => [ :id, :first_cap ]).send(:columns, Player.arel_table)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'whitelist' do
|
37
|
+
should 'return nil of options[:whitelist] is not set' do
|
38
|
+
assert_equal nil, Wherewolf::Processor.new(Player).send(:whitelist)
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'return options[:whitelist] if set' do
|
42
|
+
assert_equal [ :name, :type ], Wherewolf::Processor.new(Player, :whitelist => [ :name, :type ]).send(:whitelist)
|
43
|
+
end
|
44
|
+
|
45
|
+
should 'evaluate options[:whitelist] if is a proc' do
|
46
|
+
assert_equal [ :name, :type ], Wherewolf::Processor.new(Player, :whitelist => proc { return [ :name, :type ] }).send(:whitelist)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'blacklist' do
|
51
|
+
should 'return nil of options[:blacklist] is not set' do
|
52
|
+
assert_equal nil, Wherewolf::Processor.new(Player).send(:blacklist)
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'return options[:blacklist] if set' do
|
56
|
+
assert_equal [ :name, :type ], Wherewolf::Processor.new(Player, :blacklist => [ :name, :type ]).send(:blacklist)
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'evaluate options[:blacklist] if is a proc' do
|
60
|
+
assert_equal [ :name, :type ], Wherewolf::Processor.new(Player, :blacklist => proc { return [ :name, :type ] }).send(:blacklist)
|
61
|
+
end
|
62
|
+
end
|
21
63
|
end
|
22
64
|
end
|
data/test/railtie_test.rb
CHANGED
@@ -6,7 +6,19 @@ class RailtieTest < Test::Unit::TestCase
|
|
6
6
|
setup_database
|
7
7
|
setup_fixtures
|
8
8
|
end
|
9
|
+
|
10
|
+
should 'not enable wherewolf_options if has_query_parsing is not called' do
|
11
|
+
assert_equal false, Team.respond_to?(:wherewolf_options)
|
12
|
+
end
|
9
13
|
|
14
|
+
should 'enable wherewolf_options if has_query_parsing is called' do
|
15
|
+
assert_equal true, Player.respond_to?(:wherewolf_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'set wherewolf_options' do
|
19
|
+
assert_equal({ :whitelist => [ :name ] }, Country.wherewolf_options)
|
20
|
+
end
|
21
|
+
|
10
22
|
should 'not enable where_query if has_query_parsing is not called' do
|
11
23
|
assert_equal false, Team.respond_to?(:where_query)
|
12
24
|
end
|
data/wherewolf.gemspec
CHANGED
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.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-10-12 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: arel
|
16
|
-
requirement: &
|
16
|
+
requirement: &70293403498180 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70293403498180
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: parslet
|
27
|
-
requirement: &
|
27
|
+
requirement: &70293403497160 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70293403497160
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: shoulda
|
38
|
-
requirement: &
|
38
|
+
requirement: &70293403496160 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70293403496160
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rdoc
|
49
|
-
requirement: &
|
49
|
+
requirement: &70293403495640 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70293403495640
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bundler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70293403494940 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70293403494940
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: jeweler
|
71
|
-
requirement: &
|
71
|
+
requirement: &70293403451080 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70293403451080
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: guard
|
82
|
-
requirement: &
|
82
|
+
requirement: &70293403450480 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70293403450480
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: guard-test
|
93
|
-
requirement: &
|
93
|
+
requirement: &70293403449820 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70293403449820
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: simplecov
|
104
|
-
requirement: &
|
104
|
+
requirement: &70293403448900 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70293403448900
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: sqlite3
|
115
|
-
requirement: &
|
115
|
+
requirement: &70293403448020 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70293403448020
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: rails
|
126
|
-
requirement: &
|
126
|
+
requirement: &70293403446960 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70293403446960
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: mocha
|
137
|
-
requirement: &
|
137
|
+
requirement: &70293403446380 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,7 +142,7 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *70293403446380
|
146
146
|
description: Wherewolf allows you to consume search terms as strings without worrying
|
147
147
|
about database injections. It parses the query and converts it into ARel. It's great
|
148
148
|
for creating filterable REST APIs.
|
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
192
|
version: '0'
|
193
193
|
segments:
|
194
194
|
- 0
|
195
|
-
hash: -
|
195
|
+
hash: -2148443602705417876
|
196
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
197
|
none: false
|
198
198
|
requirements:
|