outoftime-record_filter 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/Rakefile +35 -1
- data/VERSION.yml +2 -2
- data/lib/record_filter.rb +26 -5
- data/lib/record_filter/active_record.rb +55 -12
- data/lib/record_filter/conjunctions.rb +1 -1
- data/lib/record_filter/dsl.rb +16 -1
- data/lib/record_filter/dsl/class_join.rb +1 -1
- data/lib/record_filter/dsl/conjunction.rb +1 -1
- data/lib/record_filter/dsl/conjunction_dsl.rb +244 -18
- data/lib/record_filter/dsl/dsl.rb +90 -25
- data/lib/record_filter/dsl/dsl_factory.rb +19 -0
- data/lib/record_filter/dsl/group_by.rb +1 -1
- data/lib/record_filter/dsl/join.rb +1 -1
- data/lib/record_filter/dsl/join_condition.rb +1 -1
- data/lib/record_filter/dsl/join_dsl.rb +36 -1
- data/lib/record_filter/dsl/limit.rb +1 -1
- data/lib/record_filter/dsl/named_filter.rb +1 -1
- data/lib/record_filter/dsl/order.rb +1 -1
- data/lib/record_filter/dsl/restriction.rb +218 -22
- data/lib/record_filter/filter.rb +11 -9
- data/lib/record_filter/group_by.rb +1 -1
- data/lib/record_filter/join.rb +1 -1
- data/lib/record_filter/order.rb +1 -1
- data/lib/record_filter/query.rb +4 -4
- data/lib/record_filter/restrictions.rb +1 -1
- data/lib/record_filter/table.rb +23 -12
- data/spec/active_record_spec.rb +9 -28
- data/spec/exception_spec.rb +23 -0
- data/spec/named_filter_spec.rb +5 -2
- data/spec/restrictions_spec.rb +28 -0
- data/spec/test.db +0 -0
- data/test/performance_test.rb +36 -0
- data/{lib/record_filter/join_table.rb → test/test.db} +0 -0
- metadata +6 -3
data/lib/record_filter/filter.rb
CHANGED
@@ -1,41 +1,43 @@
|
|
1
1
|
module RecordFilter
|
2
|
+
# This class is the value that is returned from the execution of a filter.
|
2
3
|
class Filter
|
3
4
|
|
4
5
|
NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?)
|
6
|
+
|
5
7
|
[].methods.each do |m|
|
6
8
|
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
|
7
9
|
delegate m, :to => :loaded_data
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
def initialize(clazz, named_filter, *args, &block)
|
13
|
+
def initialize(clazz, named_filter, *args, &block) # :nodoc:
|
12
14
|
@current_scoped_methods = clazz.send(:current_scoped_methods)
|
13
15
|
@clazz = clazz
|
14
16
|
|
15
17
|
@query = Query.new(@clazz, named_filter, *args, &block)
|
16
18
|
end
|
17
19
|
|
18
|
-
def first(*args)
|
20
|
+
def first(*args) # :nodoc:
|
19
21
|
do_with_scope do
|
20
22
|
@clazz.find(:first, *args)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
|
-
def last(*args)
|
26
|
+
def last(*args) # :nodoc:
|
25
27
|
do_with_scope do
|
26
28
|
@clazz.find(:last, *args)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
def size
|
32
|
+
def size # :nodoc:
|
31
33
|
@loaded_data ? @loaded_data.length : count
|
32
34
|
end
|
33
35
|
|
34
|
-
def empty?
|
36
|
+
def empty? # :nodoc:
|
35
37
|
@loaded_data ? @loaded_data.empty? : count.zero?
|
36
38
|
end
|
37
39
|
|
38
|
-
def any?
|
40
|
+
def any? # :nodoc:
|
39
41
|
if block_given?
|
40
42
|
loaded_data.any? { |*block_args| yield(*block_args) }
|
41
43
|
else
|
@@ -55,7 +57,7 @@ module RecordFilter
|
|
55
57
|
|
56
58
|
protected
|
57
59
|
|
58
|
-
def method_missing(method, *args, &block)
|
60
|
+
def method_missing(method, *args, &block) # :nodoc:
|
59
61
|
if @clazz.named_filters.include?(method)
|
60
62
|
do_with_scope do
|
61
63
|
Filter.new(@clazz, method, *args)
|
@@ -67,7 +69,7 @@ module RecordFilter
|
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
|
-
def do_with_scope(count_query=false, &block)
|
72
|
+
def do_with_scope(count_query=false, &block) # :nodoc:
|
71
73
|
@clazz.send(:with_scope, { :find => proxy_options(count_query), :create => proxy_options(count_query) }, :reverse_merge) do
|
72
74
|
if @current_scoped_methods
|
73
75
|
@clazz.send(:with_scope, @current_scoped_methods) do
|
@@ -79,7 +81,7 @@ module RecordFilter
|
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
def loaded_data
|
84
|
+
def loaded_data # :nodoc:
|
83
85
|
@loaded_data ||= do_with_scope do
|
84
86
|
@clazz.find(:all)
|
85
87
|
end
|
data/lib/record_filter/join.rb
CHANGED
data/lib/record_filter/order.rb
CHANGED
data/lib/record_filter/query.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module RecordFilter
|
2
|
-
class Query
|
2
|
+
class Query # :nodoc: all
|
3
3
|
|
4
4
|
attr_reader :dsl_conjunction
|
5
5
|
attr_reader :conjunction
|
@@ -42,10 +42,10 @@ module RecordFilter
|
|
42
42
|
protected
|
43
43
|
|
44
44
|
def dsl_for_named_filter(clazz, named_filter)
|
45
|
-
return DSL::
|
45
|
+
return DSL::DSLFactory.create(clazz) if named_filter.blank?
|
46
46
|
while (clazz)
|
47
|
-
dsl = DSL::
|
48
|
-
return DSL::
|
47
|
+
dsl = DSL::DSLFactory.subclass(clazz)
|
48
|
+
return DSL::DSLFactory.create(clazz) if dsl && dsl.instance_methods(false).include?(named_filter.to_s)
|
49
49
|
clazz = clazz.superclass
|
50
50
|
end
|
51
51
|
end
|
data/lib/record_filter/table.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module RecordFilter
|
2
|
-
class Table
|
2
|
+
class Table # :nodoc: all
|
3
3
|
attr_reader :table_alias, :orders, :group_bys, :model_class
|
4
4
|
|
5
5
|
def initialize(model_class, table_alias = nil)
|
@@ -16,7 +16,7 @@ module RecordFilter
|
|
16
16
|
@model_class.quoted_table_name
|
17
17
|
end
|
18
18
|
|
19
|
-
def join_association(association_name, join_type=nil,
|
19
|
+
def join_association(association_name, join_type=nil, options={})
|
20
20
|
@joins_cache[association_name] ||=
|
21
21
|
begin
|
22
22
|
association = @model_class.reflect_on_association(association_name)
|
@@ -25,13 +25,19 @@ module RecordFilter
|
|
25
25
|
end
|
26
26
|
if (association.options[:through])
|
27
27
|
through_association = @model_class.reflect_on_association(association.options[:through])
|
28
|
-
|
28
|
+
|
29
|
+
through_join = join_association(
|
30
|
+
association.options[:through],
|
31
|
+
join_type,
|
32
|
+
:type_restriction => association.options[:source_type],
|
33
|
+
:source => association.options[:source])
|
34
|
+
|
29
35
|
through_join.right_table.join_association(
|
30
|
-
association.options[:source] || association_name, join_type, association.options[:source_type])
|
36
|
+
association.options[:source] || association_name, join_type, :join_class => association.options[:source_type])
|
31
37
|
else
|
32
38
|
case association.macro
|
33
39
|
when :belongs_to, :has_many, :has_one
|
34
|
-
simple_join(association, join_type,
|
40
|
+
simple_join(association, join_type, options)
|
35
41
|
when :has_and_belongs_to_many
|
36
42
|
compound_join(association, join_type)
|
37
43
|
end
|
@@ -69,20 +75,25 @@ module RecordFilter
|
|
69
75
|
|
70
76
|
private
|
71
77
|
|
72
|
-
def simple_join(association, join_type,
|
78
|
+
def simple_join(association, join_type, options)
|
73
79
|
join_predicate =
|
74
80
|
case association.macro
|
75
81
|
when :belongs_to
|
76
|
-
[{ association.options[:foreign_key] || association.association_foreign_key.to_sym =>
|
82
|
+
[{ association.options[:foreign_key] || association.association_foreign_key.to_sym => @model_class.primary_key }]
|
77
83
|
when :has_many, :has_one
|
78
|
-
[{ association.options[:primary_key] ||
|
84
|
+
[{ association.options[:primary_key] || @model_class.primary_key => association.primary_key_name.to_sym }]
|
79
85
|
end
|
80
86
|
|
81
87
|
if association.options[:as]
|
82
88
|
join_predicate << DSL::Restriction.new(association.options[:as].to_s + '_type').equal_to(association.active_record.base_class.name)
|
83
89
|
end
|
84
90
|
|
85
|
-
|
91
|
+
if options[:type_restriction] && options[:source]
|
92
|
+
foreign_type = association.klass.reflect_on_association(options[:source]).options[:foreign_type]
|
93
|
+
join_predicate << DSL::Restriction.new(foreign_type).equal_to(options[:type_restriction])
|
94
|
+
end
|
95
|
+
|
96
|
+
clazz = options[:join_class].nil? ? association.klass : options[:join_class].constantize
|
86
97
|
|
87
98
|
join_table = Table.new(clazz, alias_for_association(association))
|
88
99
|
@joins << join = Join.new(self, join_table, join_predicate, join_type)
|
@@ -90,11 +101,11 @@ module RecordFilter
|
|
90
101
|
end
|
91
102
|
|
92
103
|
def compound_join(association, join_type)
|
93
|
-
pivot_join_predicate = [{
|
104
|
+
pivot_join_predicate = [{ @model_class.primary_key => association.primary_key_name.to_sym }]
|
94
105
|
table_name = @model_class.connection.quote_table_name(association.options[:join_table])
|
95
106
|
pivot_table = PivotTable.new(table_name, association, "__#{alias_for_association(association)}")
|
96
107
|
pivot_join = Join.new(self, pivot_table, pivot_join_predicate, join_type)
|
97
|
-
join_predicate = [{ association.association_foreign_key.to_sym =>
|
108
|
+
join_predicate = [{ association.association_foreign_key.to_sym => @model_class.primary_key }]
|
98
109
|
join_table = Table.new(association.klass, alias_for_association(association))
|
99
110
|
pivot_table.joins << join = Join.new(pivot_table, join_table, join_predicate, join_type)
|
100
111
|
@joins << pivot_join
|
@@ -110,7 +121,7 @@ module RecordFilter
|
|
110
121
|
alias_method :alias_for_class, :alias_for_association
|
111
122
|
end
|
112
123
|
|
113
|
-
class PivotTable < Table
|
124
|
+
class PivotTable < Table # :nodoc: all
|
114
125
|
attr_reader :table_name, :joins
|
115
126
|
|
116
127
|
def initialize(table_name, association, table_alias = table_name)
|
data/spec/active_record_spec.rb
CHANGED
@@ -78,34 +78,15 @@ describe 'active record options' do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'should create the correct condition' do
|
81
|
-
|
82
|
-
Blog.last_find[:conditions].should == [%q(blogs__posts__bad_comments.contents = ?), 'blammo']
|
81
|
+
Blog.last_find[:conditions].should == [%q(blogs__features__featurable.permalink = ?), 'slam dunk']
|
83
82
|
end
|
84
83
|
|
85
84
|
it 'should create the correct join' do
|
86
|
-
|
87
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id INNER JOIN "comments" AS blogs__posts__bad_comments ON blogs__posts.id = blogs__posts__bad_comments.post_id)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe 'using include' do
|
92
|
-
before do
|
93
|
-
Blog.filter do
|
94
|
-
having(:posts_with_comments).with(:permalink, 'home run')
|
95
|
-
end.inspect
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'should create the correct condition' do
|
99
|
-
pending 'using include'
|
100
|
-
Blog.last_find[:conditions].should == 'ack'
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'should create the correct join' do
|
104
|
-
pending 'using include'
|
105
|
-
Blog.last_find[:joins].should == 'ack'
|
85
|
+
Blog.last_find[:joins].should == %q(INNER JOIN "features" AS blogs__features ON "blogs".id = blogs__features.blog_id AND (blogs__features.featurable_type = 'Post') INNER JOIN "posts" AS blogs__features__featurable ON blogs__features.featurable_id = blogs__features__featurable.id)
|
106
86
|
end
|
107
87
|
end
|
108
88
|
|
89
|
+
# :include
|
109
90
|
# :finder_sql
|
110
91
|
# :counter_sql
|
111
92
|
# :group
|
@@ -153,11 +134,11 @@ describe 'active record options' do
|
|
153
134
|
end
|
154
135
|
end
|
155
136
|
|
156
|
-
#include
|
157
|
-
#conditions
|
158
|
-
#select
|
159
|
-
#foreign_key
|
160
|
-
#polymorphic
|
161
|
-
#readonly
|
137
|
+
# :include
|
138
|
+
# :conditions
|
139
|
+
# :select
|
140
|
+
# :foreign_key
|
141
|
+
# :polymorphic
|
142
|
+
# :readonly
|
162
143
|
end
|
163
144
|
end
|
data/spec/exception_spec.rb
CHANGED
@@ -173,6 +173,16 @@ describe 'raising exceptions' do
|
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
+
describe 'calling order with an invalid direction' do
|
177
|
+
it 'should raise an InvalidFilterException' do
|
178
|
+
lambda {
|
179
|
+
Post.filter do
|
180
|
+
order(:id, :oops)
|
181
|
+
end
|
182
|
+
}.should raise_error(RecordFilter::InvalidFilterException)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
176
186
|
describe 'calling named filters within filters' do
|
177
187
|
it 'should raise an excpetion if the named filter does not exist' do
|
178
188
|
lambda {
|
@@ -182,4 +192,17 @@ describe 'raising exceptions' do
|
|
182
192
|
}.should raise_error(RecordFilter::NamedFilterNotFoundException)
|
183
193
|
end
|
184
194
|
end
|
195
|
+
|
196
|
+
describe 'creating named filters with the same name as an existing one' do
|
197
|
+
it 'should raise an InvalidFilterNameException' do
|
198
|
+
Post.named_filter(:original) do
|
199
|
+
with(:permalink, 'abc')
|
200
|
+
end
|
201
|
+
lambda {
|
202
|
+
Post.named_filter(:original) do
|
203
|
+
with(:permalink, 'def')
|
204
|
+
end
|
205
|
+
}.should raise_error(RecordFilter::InvalidFilterNameException)
|
206
|
+
end
|
207
|
+
end
|
185
208
|
end
|
data/spec/named_filter_spec.rb
CHANGED
@@ -108,7 +108,7 @@ describe 'named filters' do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
describe 'using compound filters' do
|
111
|
-
before do
|
111
|
+
before(:all) do
|
112
112
|
Comment.named_filter(:offensive_or_not) do |state|
|
113
113
|
with(:offensive, state)
|
114
114
|
end
|
@@ -208,10 +208,13 @@ describe 'named filters' do
|
|
208
208
|
end
|
209
209
|
|
210
210
|
describe 'chaining named filters with AR associations that involve joins' do
|
211
|
-
before do
|
211
|
+
before(:all) do
|
212
212
|
Comment.named_filter(:with_user_named) do |name|
|
213
213
|
having(:user).with(:first_name, name)
|
214
214
|
end
|
215
|
+
end
|
216
|
+
|
217
|
+
before(:each) do
|
215
218
|
@blog = Blog.create
|
216
219
|
@blog.comments.with_user_named('Bob').inspect
|
217
220
|
end
|
data/spec/restrictions_spec.rb
CHANGED
@@ -40,6 +40,13 @@ describe 'RecordFilter restrictions' do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
it 'should create an IS NULL restriction when passing nil as the value to equal_to' do
|
44
|
+
Post.filter do
|
45
|
+
with(:blog_id).equal_to(nil)
|
46
|
+
end.inspect
|
47
|
+
Post.last_find.should == { :conditions => [%q("posts".blog_id IS NULL)] }
|
48
|
+
end
|
49
|
+
|
43
50
|
it 'should filter for in' do
|
44
51
|
Post.filter do
|
45
52
|
with(:blog_id).in [1, 3, 5]
|
@@ -61,6 +68,13 @@ describe 'RecordFilter restrictions' do
|
|
61
68
|
Post.last_find.should == { :conditions => [%q("posts".blog_id IN (?)), []] }
|
62
69
|
end
|
63
70
|
|
71
|
+
it 'should do the right thing for IN filters with single values' do
|
72
|
+
Post.filter do
|
73
|
+
with(:blog_id).in(1)
|
74
|
+
end.inspect
|
75
|
+
Post.last_find.should == { :conditions => [%q("posts".blog_id IN (?)), 1] }
|
76
|
+
end
|
77
|
+
|
64
78
|
it 'should do the right thing for IN filters with nil' do
|
65
79
|
Post.filter do
|
66
80
|
with(:blog_id).in(nil)
|
@@ -68,6 +82,20 @@ describe 'RecordFilter restrictions' do
|
|
68
82
|
Post.last_find.should == { :conditions => [%q("posts".blog_id IN (?)), nil] }
|
69
83
|
end
|
70
84
|
|
85
|
+
it 'should negate is_not_null conditions correctly' do
|
86
|
+
Post.filter do
|
87
|
+
with(:blog_id).is_not_null.not
|
88
|
+
end.inspect
|
89
|
+
Post.last_find.should == { :conditions => [%q("posts".blog_id IS NULL)] }
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should double-negate correctly' do
|
93
|
+
Post.filter do
|
94
|
+
with(:blog_id, 3).not.not
|
95
|
+
end.inspect
|
96
|
+
Post.last_find.should == { :conditions => [%q("posts".blog_id = ?), 3] }
|
97
|
+
end
|
98
|
+
|
71
99
|
it 'should filter for between' do
|
72
100
|
time1 = Time.parse('2008-01-03 23:23:00')
|
73
101
|
time2 = Time.parse('2008-01-03 23:36:00')
|
data/spec/test.db
CHANGED
Binary file
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'sqlite3-ruby'
|
3
|
+
|
4
|
+
require 'ruby-debug'
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'record_filter')
|
7
|
+
|
8
|
+
module TestModel
|
9
|
+
end
|
10
|
+
|
11
|
+
require File.join(File.dirname(__FILE__), '..', 'spec', 'models')
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(
|
14
|
+
:adapter => 'sqlite3',
|
15
|
+
:database => File.join(File.dirname(__FILE__), '..', 'spec', 'test.db')
|
16
|
+
)
|
17
|
+
|
18
|
+
@blog = Class.new(Blog)
|
19
|
+
@blog.named_filter :somethings do
|
20
|
+
having(:ads) do
|
21
|
+
with(:content, nil)
|
22
|
+
end
|
23
|
+
join(Post, :left) do
|
24
|
+
on(:id => :blog_id)
|
25
|
+
join(Comment, :inner) do
|
26
|
+
on(:id => :post_id)
|
27
|
+
on(:offensive, true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
group_by(:id)
|
31
|
+
end
|
32
|
+
|
33
|
+
10000.times do
|
34
|
+
@blog.somethings
|
35
|
+
end
|
36
|
+
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: outoftime-record_filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mat Brown
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-05-
|
13
|
+
date: 2009-05-04 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/record_filter/dsl/conjunction.rb
|
35
35
|
- lib/record_filter/dsl/conjunction_dsl.rb
|
36
36
|
- lib/record_filter/dsl/dsl.rb
|
37
|
+
- lib/record_filter/dsl/dsl_factory.rb
|
37
38
|
- lib/record_filter/dsl/group_by.rb
|
38
39
|
- lib/record_filter/dsl/join.rb
|
39
40
|
- lib/record_filter/dsl/join_condition.rb
|
@@ -45,7 +46,6 @@ files:
|
|
45
46
|
- lib/record_filter/filter.rb
|
46
47
|
- lib/record_filter/group_by.rb
|
47
48
|
- lib/record_filter/join.rb
|
48
|
-
- lib/record_filter/join_table.rb
|
49
49
|
- lib/record_filter/order.rb
|
50
50
|
- lib/record_filter/query.rb
|
51
51
|
- lib/record_filter/restrictions.rb
|
@@ -62,6 +62,8 @@ files:
|
|
62
62
|
- spec/select_spec.rb
|
63
63
|
- spec/spec_helper.rb
|
64
64
|
- spec/test.db
|
65
|
+
- test/performance_test.rb
|
66
|
+
- test/test.db
|
65
67
|
has_rdoc: true
|
66
68
|
homepage: http://github.com/outoftime/record_filter/tree/master
|
67
69
|
post_install_message:
|
@@ -100,3 +102,4 @@ test_files:
|
|
100
102
|
- spec/restrictions_spec.rb
|
101
103
|
- spec/select_spec.rb
|
102
104
|
- spec/spec_helper.rb
|
105
|
+
- test/performance_test.rb
|