outoftime-record_filter 0.1.4 → 0.2.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/VERSION.yml +2 -2
- data/lib/record_filter/active_record.rb +2 -2
- data/lib/record_filter/filter.rb +73 -19
- data/lib/record_filter/join.rb +3 -4
- data/lib/record_filter/query.rb +3 -1
- data/lib/record_filter/table.rb +13 -6
- data/spec/exception_spec.rb +4 -1
- data/spec/explicit_join_spec.rb +5 -5
- data/spec/implicit_join_spec.rb +41 -5
- data/spec/limits_and_ordering_spec.rb +1 -1
- data/spec/models.rb +70 -0
- data/spec/named_filter_spec.rb +80 -14
- data/spec/proxying_spec.rb +49 -0
- data/spec/select_spec.rb +58 -0
- data/spec/spec_helper.rb +8 -3
- data/spec/test.db +0 -0
- metadata +8 -16
- data/spec/models/blog.rb +0 -6
- data/spec/models/comment.rb +0 -5
- data/spec/models/feature.rb +0 -5
- data/spec/models/photo.rb +0 -3
- data/spec/models/post.rb +0 -10
- data/spec/models/review.rb +0 -5
- data/spec/models/tag.rb +0 -3
data/VERSION.yml
CHANGED
@@ -3,7 +3,7 @@ module RecordFilter
|
|
3
3
|
module ClassMethods
|
4
4
|
|
5
5
|
def filter(&block)
|
6
|
-
Filter.new(self, nil,
|
6
|
+
Filter.new(self, nil, &block)
|
7
7
|
end
|
8
8
|
|
9
9
|
def named_filter(name, &block)
|
@@ -15,7 +15,7 @@ module RecordFilter
|
|
15
15
|
|
16
16
|
(class << self; self; end).instance_eval do
|
17
17
|
define_method(name.to_s) do |*args|
|
18
|
-
Filter.new(self, name,
|
18
|
+
Filter.new(self, name, *args)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/lib/record_filter/filter.rb
CHANGED
@@ -1,44 +1,92 @@
|
|
1
1
|
module RecordFilter
|
2
2
|
class Filter
|
3
3
|
|
4
|
-
|
4
|
+
NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?)
|
5
|
+
[].methods.each do |m|
|
6
|
+
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
|
7
|
+
delegate m, :to => :loaded_data
|
8
|
+
end
|
9
|
+
end
|
5
10
|
|
6
|
-
def initialize(clazz, named_filter,
|
11
|
+
def initialize(clazz, named_filter, *args, &block)
|
12
|
+
@current_scoped_methods = clazz.send(:current_scoped_methods)
|
7
13
|
@clazz = clazz
|
8
14
|
|
9
15
|
@dsl = dsl_for_named_filter(@clazz, named_filter)
|
10
16
|
@dsl.instance_eval(&block) if block
|
11
17
|
@dsl.send(named_filter, *args) if named_filter && @dsl.respond_to?(named_filter)
|
12
|
-
@dsl.conjunction
|
18
|
+
@query = Query.new(@clazz, @dsl.conjunction)
|
13
19
|
end
|
14
20
|
|
15
|
-
def
|
16
|
-
|
21
|
+
def first(*args)
|
22
|
+
if args.first.kind_of?(Integer)
|
23
|
+
loaded_data.first(*args)
|
24
|
+
else
|
25
|
+
do_with_scope do
|
26
|
+
@clazz.find(:first, *args)
|
27
|
+
end
|
28
|
+
end
|
17
29
|
end
|
18
30
|
|
19
|
-
def
|
20
|
-
if
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
def last(*args)
|
32
|
+
if args.first.kind_of?(Integer)
|
33
|
+
loaded_data.last(*args)
|
34
|
+
else
|
35
|
+
do_with_scope do
|
36
|
+
@clazz.find(:last, *args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def size
|
42
|
+
@loaded_data ? @loaded_data.length : count
|
43
|
+
end
|
44
|
+
|
45
|
+
def empty?
|
46
|
+
@loaded_data ? @loaded_data.empty? : count.zero?
|
47
|
+
end
|
48
|
+
|
49
|
+
def any?
|
50
|
+
if block_given?
|
51
|
+
loaded_data.any? { |*block_args| yield(*block_args) }
|
24
52
|
else
|
25
|
-
|
53
|
+
!empty?
|
26
54
|
end
|
27
55
|
end
|
28
56
|
|
57
|
+
def filter(&block)
|
58
|
+
do_with_scope do
|
59
|
+
Filter.new(@clazz, nil, &block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def proxy_options(count_query=false)
|
64
|
+
@query.to_find_params(count_query)
|
65
|
+
end
|
66
|
+
|
29
67
|
protected
|
30
68
|
|
31
|
-
def
|
32
|
-
@
|
33
|
-
|
34
|
-
|
69
|
+
def method_missing(method, *args, &block)
|
70
|
+
if @clazz.named_filters.include?(method)
|
71
|
+
do_with_scope do
|
72
|
+
Filter.new(@clazz, method, *args)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
do_with_scope(method == :count) do
|
76
|
+
@clazz.send(method, *args, &block)
|
77
|
+
end
|
35
78
|
end
|
36
79
|
end
|
37
80
|
|
38
|
-
def
|
39
|
-
@
|
40
|
-
|
41
|
-
|
81
|
+
def do_with_scope(count_query=false, &block)
|
82
|
+
@clazz.send(:with_scope, { :find => proxy_options(count_query), :create => proxy_options(count_query) }, :reverse_merge) do
|
83
|
+
if @current_scoped_methods
|
84
|
+
@clazz.send(:with_scope, @current_scoped_methods) do
|
85
|
+
block.call
|
86
|
+
end
|
87
|
+
else
|
88
|
+
block.call
|
89
|
+
end
|
42
90
|
end
|
43
91
|
end
|
44
92
|
|
@@ -51,5 +99,11 @@ module RecordFilter
|
|
51
99
|
end
|
52
100
|
nil
|
53
101
|
end
|
102
|
+
|
103
|
+
def loaded_data
|
104
|
+
@loaded_data ||= do_with_scope do
|
105
|
+
@clazz.find(:all)
|
106
|
+
end
|
107
|
+
end
|
54
108
|
end
|
55
109
|
end
|
data/lib/record_filter/join.rb
CHANGED
@@ -22,7 +22,7 @@ module RecordFilter
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def requires_distinct_select?
|
25
|
-
[:
|
25
|
+
[:right, :left].include?(@join_type)
|
26
26
|
end
|
27
27
|
|
28
28
|
protected
|
@@ -55,9 +55,8 @@ module RecordFilter
|
|
55
55
|
def join_type_string
|
56
56
|
@join_type_string ||= case(@join_type)
|
57
57
|
when :inner then 'INNER'
|
58
|
-
when :left then 'LEFT'
|
59
|
-
when :
|
60
|
-
when :outer then 'OUTER'
|
58
|
+
when :left then 'LEFT OUTER'
|
59
|
+
when :right then 'RIGHT OUTER'
|
61
60
|
else nil
|
62
61
|
end
|
63
62
|
end
|
data/lib/record_filter/query.rb
CHANGED
@@ -7,7 +7,9 @@ module RecordFilter
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def to_find_params(count_query=false)
|
10
|
-
params = {
|
10
|
+
params = {}
|
11
|
+
conditions = @conjunction.to_conditions
|
12
|
+
params = { :conditions => conditions } if conditions
|
11
13
|
joins = @table.all_joins
|
12
14
|
params[:joins] = joins.map { |join| join.to_sql } * ' ' unless joins.empty?
|
13
15
|
if (joins.any? { |j| j.requires_distinct_select? })
|
data/lib/record_filter/table.rb
CHANGED
@@ -23,11 +23,18 @@ module RecordFilter
|
|
23
23
|
if association.nil?
|
24
24
|
raise AssociationNotFoundException.new("The association #{association_name} was not found on #{@model_class.name}.")
|
25
25
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
|
27
|
+
if (association.options[:through])
|
28
|
+
through_association = @model_class.reflect_on_association(association.options[:through])
|
29
|
+
through_join = join_association(association.options[:through], join_type)
|
30
|
+
through_join.right_table.join_association(association_name, join_type)
|
31
|
+
else
|
32
|
+
case association.macro
|
33
|
+
when :belongs_to, :has_many, :has_one
|
34
|
+
simple_join(association, join_type)
|
35
|
+
when :has_and_belongs_to_many
|
36
|
+
compound_join(association, join_type)
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
33
40
|
end
|
@@ -72,7 +79,7 @@ module RecordFilter
|
|
72
79
|
end
|
73
80
|
|
74
81
|
if association.options[:as]
|
75
|
-
join_predicate << DSL::Restriction.new(association.options[:as].to_s + '_type').equal_to(
|
82
|
+
join_predicate << DSL::Restriction.new(association.options[:as].to_s + '_type').equal_to(association.active_record.base_class.name)
|
76
83
|
end
|
77
84
|
join_table = Table.new(association.klass, alias_for_association(association))
|
78
85
|
@joins << join = Join.new(self, join_table, join_predicate, join_type)
|
data/spec/exception_spec.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
2
|
|
3
3
|
describe 'raising exceptions' do
|
4
|
-
|
4
|
+
before do
|
5
|
+
TestModel.extended_models.each { |model| model.last_find = {} }
|
6
|
+
end
|
7
|
+
|
5
8
|
describe 'on missing associations' do
|
6
9
|
it 'should get AssociationNotFoundException' do
|
7
10
|
lambda {
|
data/spec/explicit_join_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe 'explicit joins' do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should add correct join' do
|
19
|
-
Post.last_find[:joins].should == %q(LEFT JOIN "blogs" AS posts_blogs ON "posts".blog_id = posts_blogs.id)
|
19
|
+
Post.last_find[:joins].should == %q(LEFT OUTER JOIN "blogs" AS posts_blogs ON "posts".blog_id = posts_blogs.id)
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should query against condition on join table' do
|
@@ -36,7 +36,7 @@ describe 'explicit joins' do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'should add correct join' do
|
39
|
-
Review.last_find[:joins].should == %q(LEFT JOIN "features" AS reviews_features ON "reviews".reviewable_id = reviews_features.featurable_id AND "reviews".reviewable_type = reviews_features.featurable_type)
|
39
|
+
Review.last_find[:joins].should == %q(LEFT OUTER JOIN "features" AS reviews_features ON "reviews".reviewable_id = reviews_features.featurable_id AND "reviews".reviewable_type = reviews_features.featurable_type)
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'should query against condition on join table' do
|
@@ -57,7 +57,7 @@ describe 'explicit joins' do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
it 'should add correct join' do
|
60
|
-
Review.last_find[:joins].should == %q(LEFT JOIN "features" AS reviews__Feature ON "reviews".reviewable_id = reviews__Feature.featurable_id AND "reviews".reviewable_type = reviews__Feature.featurable_type AND (reviews__Feature.featurable_type = 'SomeType'))
|
60
|
+
Review.last_find[:joins].should == %q(LEFT OUTER JOIN "features" AS reviews__Feature ON "reviews".reviewable_id = reviews__Feature.featurable_id AND "reviews".reviewable_type = reviews__Feature.featurable_type AND (reviews__Feature.featurable_type = 'SomeType'))
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -73,7 +73,7 @@ describe 'explicit joins' do
|
|
73
73
|
end
|
74
74
|
|
75
75
|
it 'should add the correct join' do
|
76
|
-
Review.last_find[:joins].should == %q(LEFT JOIN "features" AS reviews__Feature ON (reviews__Feature.featurable_type IS NULL) AND (reviews__Feature.featurable_id >= 12) AND (reviews__Feature.priority <> 6))
|
76
|
+
Review.last_find[:joins].should == %q(LEFT OUTER JOIN "features" AS reviews__Feature ON (reviews__Feature.featurable_type IS NULL) AND (reviews__Feature.featurable_id >= 12) AND (reviews__Feature.priority <> 6))
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -100,7 +100,7 @@ describe 'explicit joins' do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'should produce the correct join' do
|
103
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "ads" AS blogs__ads ON "blogs".id = blogs__ads.blog_id LEFT JOIN "posts" AS blogs__Post ON "blogs".id = blogs__Post.blog_id INNER JOIN "comments" AS blogs__Post__Comment ON blogs__Post.id = blogs__Post__Comment.post_id AND (blogs__Post__Comment.offensive = 't'))
|
103
|
+
Blog.last_find[:joins].should == %q(INNER JOIN "ads" AS blogs__ads ON "blogs".id = blogs__ads.blog_id LEFT OUTER JOIN "posts" AS blogs__Post ON "blogs".id = blogs__Post.blog_id INNER JOIN "comments" AS blogs__Post__Comment ON blogs__Post.id = blogs__Post__Comment.post_id AND (blogs__Post__Comment.offensive = 't'))
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
data/spec/implicit_join_spec.rb
CHANGED
@@ -224,7 +224,7 @@ describe 'implicit joins' do
|
|
224
224
|
describe 'passing the join type to having' do
|
225
225
|
before do
|
226
226
|
Blog.filter do
|
227
|
-
having(:
|
227
|
+
having(:left, :posts) do
|
228
228
|
with(:permalink, 'ack')
|
229
229
|
end
|
230
230
|
end.inspect
|
@@ -242,7 +242,7 @@ describe 'implicit joins' do
|
|
242
242
|
describe 'passing the join type to having with multiple joins' do
|
243
243
|
before do
|
244
244
|
Blog.filter do
|
245
|
-
having(:
|
245
|
+
having(:left, :posts => :comments) do
|
246
246
|
with(:offensive, true)
|
247
247
|
end
|
248
248
|
end.inspect
|
@@ -259,7 +259,7 @@ describe 'implicit joins' do
|
|
259
259
|
|
260
260
|
describe 'on polymorphic associations' do
|
261
261
|
before do
|
262
|
-
|
262
|
+
PublicPost.filter do
|
263
263
|
having(:reviews) do
|
264
264
|
with(:stars_count, 3)
|
265
265
|
end
|
@@ -267,11 +267,47 @@ describe 'implicit joins' do
|
|
267
267
|
end
|
268
268
|
|
269
269
|
it 'should create the correct condition' do
|
270
|
-
|
270
|
+
PublicPost.last_find[:conditions].should == [%q(posts__reviews.stars_count = ?), 3]
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should create the correct join' do
|
274
|
+
PublicPost.last_find[:joins].should == %q(INNER JOIN "reviews" AS posts__reviews ON "posts".id = posts__reviews.reviewable_id AND (posts__reviews.reviewable_type = 'Post'))
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe 'on has_many_through associations' do
|
279
|
+
before do
|
280
|
+
Blog.filter do
|
281
|
+
having(:comments) do
|
282
|
+
with(:offensive, true)
|
283
|
+
end
|
284
|
+
end.inspect
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'should create the correct condition' do
|
288
|
+
Blog.last_find[:conditions].should == [%q(blogs__posts__comments.offensive = ?), true]
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'should create the correct join' do
|
292
|
+
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__comments ON blogs__posts.id = blogs__posts__comments.post_id)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe 'on has_one_through associations' do
|
297
|
+
before do
|
298
|
+
Post.filter do
|
299
|
+
having(:user) do
|
300
|
+
with(:first_name, 'Joe')
|
301
|
+
end
|
302
|
+
end.inspect
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'should create the correct condition' do
|
306
|
+
Post.last_find[:conditions].should == [%q(posts__author__user.first_name = ?), 'Joe']
|
271
307
|
end
|
272
308
|
|
273
309
|
it 'should create the correct join' do
|
274
|
-
Post.last_find[:joins].should == %q(INNER JOIN "
|
310
|
+
Post.last_find[:joins].should == %q(INNER JOIN "authors" AS posts__author ON "posts".id = posts__author.post_id INNER JOIN "users" AS posts__author__user ON posts__author.user_id = posts__author__user.id)
|
275
311
|
end
|
276
312
|
end
|
277
313
|
end
|
data/spec/models.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
class Ad < ActiveRecord::Base
|
2
|
+
extend TestModel
|
3
|
+
belongs_to :blog
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
class Author < ActiveRecord::Base
|
8
|
+
extend TestModel
|
9
|
+
belongs_to :user
|
10
|
+
belongs_to :post
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
class Blog < ActiveRecord::Base
|
15
|
+
extend TestModel
|
16
|
+
has_many :posts
|
17
|
+
has_many :comments, :through => :posts
|
18
|
+
has_many :ads
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Comment < ActiveRecord::Base
|
23
|
+
extend TestModel
|
24
|
+
belongs_to :post
|
25
|
+
belongs_to :user
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
class Feature < ActiveRecord::Base
|
30
|
+
extend TestModel
|
31
|
+
belongs_to :featurable, :polymorphic => true
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Photo < ActiveRecord::Base
|
36
|
+
belongs_to :post
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Post < ActiveRecord::Base
|
41
|
+
extend TestModel
|
42
|
+
belongs_to :blog
|
43
|
+
has_many :comments
|
44
|
+
has_one :photo
|
45
|
+
has_and_belongs_to_many :tags
|
46
|
+
has_many :features, :as => :featurable
|
47
|
+
has_many :reviews, :as => :reviewable
|
48
|
+
has_one :author
|
49
|
+
has_one :user, :through => :author
|
50
|
+
end
|
51
|
+
|
52
|
+
class PublicPost < Post
|
53
|
+
end
|
54
|
+
|
55
|
+
class Review < ActiveRecord::Base
|
56
|
+
extend TestModel
|
57
|
+
belongs_to :reviewable, :polymorphic => true
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
class Tag < ActiveRecord::Base
|
62
|
+
has_and_belongs_to_many :posts
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class User < ActiveRecord::Base
|
67
|
+
extend TestModel
|
68
|
+
has_one :author
|
69
|
+
has_many :comments
|
70
|
+
end
|
data/spec/named_filter_spec.rb
CHANGED
@@ -50,7 +50,8 @@ describe 'named filters' do
|
|
50
50
|
|
51
51
|
it 'should call the filter passing all of the arguments' do
|
52
52
|
Blog.with_name_and_post_with_permalink('booya', 'ftw').inspect
|
53
|
-
Blog.last_find[:conditions].should ==
|
53
|
+
Blog.last_find[:conditions].should ==
|
54
|
+
[%q(("blogs".name = ?) AND (blogs__posts.permalink = ?)), 'booya', 'ftw']
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -81,7 +82,8 @@ describe 'named filters' do
|
|
81
82
|
|
82
83
|
it 'should execute the parent class filters correctly' do
|
83
84
|
NiceComment.with_contents('test contents').inspect
|
84
|
-
NiceComment.last_find[:conditions].should ==
|
85
|
+
NiceComment.last_find[:conditions].should ==
|
86
|
+
[%q("comments".contents = ?), 'test contents']
|
85
87
|
end
|
86
88
|
|
87
89
|
it 'should not have the subclass filters in the parent class' do
|
@@ -90,7 +92,8 @@ describe 'named filters' do
|
|
90
92
|
|
91
93
|
it 'should have parent class filters in the subclass' do
|
92
94
|
NiceComment.offensive.with_contents('something').inspect
|
93
|
-
NiceComment.last_find[:conditions].should ==
|
95
|
+
NiceComment.last_find[:conditions].should ==
|
96
|
+
%q(("comments".contents = 'something') AND ("comments".offensive = 't'))
|
94
97
|
end
|
95
98
|
|
96
99
|
it 'should provide access to the named filters' do
|
@@ -126,25 +129,25 @@ describe 'named filters' do
|
|
126
129
|
|
127
130
|
it 'should chain the filters into a single query' do
|
128
131
|
Post.for_blog(1).with_offensive_comments.inspect
|
129
|
-
Post.last_find[:conditions].should ==
|
130
|
-
Post.last_find[:joins].should == %q(INNER JOIN "
|
132
|
+
Post.last_find[:conditions].should == %q((posts__comments.offensive = 't') AND (posts__blog.id = 1))
|
133
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id), %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
131
134
|
end
|
132
135
|
|
133
136
|
it 'should remove duplicate joins' do
|
134
137
|
Post.for_blog(1).with_offensive_comments.with_interesting_comments.inspect
|
135
|
-
Post.last_find[:joins].should == %q(INNER JOIN "
|
138
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id), %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
136
139
|
end
|
137
140
|
|
138
141
|
it 'should allow for filtering a named_filter' do
|
139
142
|
Post.for_blog(1).filter { having(:comments).with :offensive, true }.inspect
|
140
|
-
Post.last_find[:conditions].should ==
|
141
|
-
Post.last_find[:joins].should == %q(INNER JOIN "
|
143
|
+
Post.last_find[:conditions].should == %q((posts__comments.offensive = 't') AND (posts__blog.id = 1))
|
144
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id), %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
142
145
|
end
|
143
146
|
|
144
147
|
it 'should allow for applying a named filter to a filter' do
|
145
148
|
Post.filter { having(:comments).with :offensive, false }.for_blog(1).inspect
|
146
|
-
Post.last_find[:conditions].should ==
|
147
|
-
Post.last_find[:joins].should == %q(INNER JOIN "
|
149
|
+
Post.last_find[:conditions].should == %q((posts__blog.id = 1) AND (posts__comments.offensive = 'f'))
|
150
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id), %q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)]
|
148
151
|
end
|
149
152
|
|
150
153
|
it 'should not change the inner filter conditions when chaining filters' do
|
@@ -163,11 +166,74 @@ describe 'named filters' do
|
|
163
166
|
|
164
167
|
it 'should not change an original filter when reusing it' do
|
165
168
|
base = Post.for_blog(1)
|
166
|
-
level1 = base.with_offensive_comments
|
169
|
+
level1 = base.with_offensive_comments.inspect
|
167
170
|
level2 = base.with_interesting_comments
|
168
|
-
|
169
|
-
Post.last_find[:
|
170
|
-
|
171
|
+
Post.last_find[:conditions].should == %q((posts__comments.offensive = 't') AND (posts__blog.id = 1))
|
172
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id), %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'chaining named filters with regular AR associations' do
|
177
|
+
before do
|
178
|
+
Post.named_filter(:published) do
|
179
|
+
with(:published, true)
|
180
|
+
end
|
181
|
+
@blog = Blog.create
|
182
|
+
@blog.posts.published.inspect
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should combine the conditions from the association with the named filter' do
|
186
|
+
Post.last_find[:conditions].should == "(\"posts\".published = 't') AND (\"posts\".blog_id = #{@blog.id})"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe 'chaining named filters with AR associations that involve joins' do
|
191
|
+
before do
|
192
|
+
Comment.named_filter(:with_user_named) do |name|
|
193
|
+
having(:user).with(:first_name, name)
|
194
|
+
end
|
195
|
+
@blog = Blog.create
|
196
|
+
@blog.comments.with_user_named('Bob').inspect
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should combine the joins from the association with the named filter' do
|
200
|
+
Comment.last_find[:joins].should == [%q(INNER JOIN "users" AS comments__user ON "comments".user_id = comments__user.id), %q(INNER JOIN "posts" ON "comments".post_id = "posts".id)]
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should combine the conditions from the association with the named filter' do
|
204
|
+
Comment.last_find[:conditions].should == "(comments__user.first_name = 'Bob') AND ((\"posts\".blog_id = #{@blog.id}))"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe 'chaining multiple named filters with an AR association' do
|
209
|
+
before do
|
210
|
+
Comment.named_filter(:offensive) { with(:offensive, true) }
|
211
|
+
Comment.named_filter(:with_fun_in_contents) { with(:contents).like('%fun%') }
|
212
|
+
@post = Post.create
|
213
|
+
@post.comments.offensive.with_fun_in_contents.inspect
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should combine the conditions correctly' do
|
217
|
+
Comment.last_find[:conditions].should == "(\"comments\".contents LIKE '%fun%') AND ((\"comments\".offensive = 't') AND (\"comments\".post_id = #{@post.id}))"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'chaining multiple named filters with different joins' do
|
222
|
+
before do
|
223
|
+
Blog.named_filter(:with_offensive_comments) { having(:comments).with(:offensive, true) }
|
224
|
+
Blog.named_filter(:with_ads_with_content) { |content| having(:ads).with(:content, content) }
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'compile the joins correctly' do
|
228
|
+
Blog.with_offensive_comments.with_ads_with_content('ack').inspect
|
229
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "ads" AS blogs__ads ON "blogs".id = blogs__ads.blog_id), %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe 'with named filters that only include orders' do
|
234
|
+
it 'should have an empty conditions hash' do
|
235
|
+
Blog.named_filter(:ordered_by_id) { order(:id, :desc) }
|
236
|
+
Blog.ordered_by_id.proxy_options.should == { :order => %q("blogs".id DESC) }
|
171
237
|
end
|
172
238
|
end
|
173
239
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe 'proxying to the found data' do
|
4
|
+
before do
|
5
|
+
TestModel.extended_models.each { |model| model.last_find = {} }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'calling first and last on a filter result' do
|
9
|
+
before do
|
10
|
+
Blog.all.each { |b| b.destroy }
|
11
|
+
Blog.named_filter(:by_name) do
|
12
|
+
order(:name)
|
13
|
+
end
|
14
|
+
@blog1 = Blog.create(:name => 'a')
|
15
|
+
@blog2 = Blog.create(:name => 'b')
|
16
|
+
@blog3 = Blog.create(:name => 'c')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return the first element in the result correctly' do
|
20
|
+
Blog.by_name.first.should == @blog1
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should return the last element in the result correctly' do
|
24
|
+
Blog.by_name.last.should == @blog3
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should proxy arguments through' do
|
28
|
+
Blog.by_name.first(:conditions => ['name = ?', 'b']).should == @blog2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'calling reject! on a filter result' do
|
33
|
+
before do
|
34
|
+
Blog.all.each { |blog| blog.destroy }
|
35
|
+
Blog.named_filter(:by_name) do
|
36
|
+
order(:name)
|
37
|
+
end
|
38
|
+
@blog1 = Blog.create
|
39
|
+
@blog2 = Blog.create
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should remove the rejected items from the list' do
|
43
|
+
items = Blog.by_name
|
44
|
+
items.size.should == 2
|
45
|
+
items.reject! { |i| true } # should reject them all
|
46
|
+
items.size.should == 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/select_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe 'with custom selects for cases where DISTINCT is required' do
|
4
|
+
before do
|
5
|
+
TestModel.extended_models.each { |model| model.last_find = {} }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'on a standard filter' do
|
9
|
+
it 'should put nothing in the select' do
|
10
|
+
Post.filter do
|
11
|
+
having(:comments).with(:offensive, true)
|
12
|
+
end.inspect
|
13
|
+
Post.last_find[:select].should be_nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'with join types that require distinct' do
|
18
|
+
it 'should put the distinct clause in the select' do
|
19
|
+
[:left, :right].each do |join_type|
|
20
|
+
Post.filter do
|
21
|
+
having(join_type, :comments).with(:offensive, true)
|
22
|
+
end.inspect rescue nil # required because sqlite doesn't support right joins
|
23
|
+
Post.last_find[:select].should == %q(DISTINCT "posts".*)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'with join types that do not require distinct' do
|
29
|
+
it 'should not put the distinct clause in the select' do
|
30
|
+
[:inner].each do |join_type|
|
31
|
+
Post.filter do
|
32
|
+
having(join_type, :comments).with(:offensive, true)
|
33
|
+
end.inspect
|
34
|
+
Post.last_find[:select].should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'on a filter with nested joins that require distinct' do
|
40
|
+
it 'should put the distinct clause in the select' do
|
41
|
+
Blog.filter do
|
42
|
+
having(:posts) do
|
43
|
+
having(:left, :comments).with(:offensive, true)
|
44
|
+
end
|
45
|
+
end.inspect
|
46
|
+
Blog.last_find[:select].should == %q(DISTINCT "blogs".*)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'on a filter that requires distinct with a count call' do
|
51
|
+
it 'should put the distinct clause in the select' do
|
52
|
+
Post.filter do
|
53
|
+
having(:left, :comments).with(:offensive, true)
|
54
|
+
end.count
|
55
|
+
Post.last_find[:select].should == %q(DISTINCT "posts".id)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,8 +13,13 @@ module TestModel
|
|
13
13
|
|
14
14
|
attr_accessor :last_find
|
15
15
|
|
16
|
-
def
|
17
|
-
@last_find =
|
16
|
+
def find(*args)
|
17
|
+
@last_find = current_scoped_methods[:find] if current_scoped_methods
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def count(*args)
|
22
|
+
@last_find = current_scoped_methods[:find] if current_scoped_methods
|
18
23
|
super
|
19
24
|
end
|
20
25
|
|
@@ -23,7 +28,7 @@ module TestModel
|
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
26
|
-
|
31
|
+
require File.join(File.dirname(__FILE__), 'models')
|
27
32
|
|
28
33
|
ActiveRecord::Base.establish_connection(
|
29
34
|
:adapter => 'sqlite3',
|
data/spec/test.db
CHANGED
Binary file
|
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.2.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-04-
|
13
|
+
date: 2009-04-28 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -52,15 +52,11 @@ files:
|
|
52
52
|
- spec/explicit_join_spec.rb
|
53
53
|
- spec/implicit_join_spec.rb
|
54
54
|
- spec/limits_and_ordering_spec.rb
|
55
|
-
- spec/models
|
56
|
-
- spec/models/comment.rb
|
57
|
-
- spec/models/feature.rb
|
58
|
-
- spec/models/photo.rb
|
59
|
-
- spec/models/post.rb
|
60
|
-
- spec/models/review.rb
|
61
|
-
- spec/models/tag.rb
|
55
|
+
- spec/models.rb
|
62
56
|
- spec/named_filter_spec.rb
|
57
|
+
- spec/proxying_spec.rb
|
63
58
|
- spec/restrictions_spec.rb
|
59
|
+
- spec/select_spec.rb
|
64
60
|
- spec/spec_helper.rb
|
65
61
|
- spec/test.db
|
66
62
|
has_rdoc: true
|
@@ -94,13 +90,9 @@ test_files:
|
|
94
90
|
- spec/explicit_join_spec.rb
|
95
91
|
- spec/implicit_join_spec.rb
|
96
92
|
- spec/limits_and_ordering_spec.rb
|
97
|
-
- spec/models
|
98
|
-
- spec/models/comment.rb
|
99
|
-
- spec/models/feature.rb
|
100
|
-
- spec/models/photo.rb
|
101
|
-
- spec/models/post.rb
|
102
|
-
- spec/models/review.rb
|
103
|
-
- spec/models/tag.rb
|
93
|
+
- spec/models.rb
|
104
94
|
- spec/named_filter_spec.rb
|
95
|
+
- spec/proxying_spec.rb
|
105
96
|
- spec/restrictions_spec.rb
|
97
|
+
- spec/select_spec.rb
|
106
98
|
- spec/spec_helper.rb
|
data/spec/models/blog.rb
DELETED
data/spec/models/comment.rb
DELETED
data/spec/models/feature.rb
DELETED
data/spec/models/photo.rb
DELETED
data/spec/models/post.rb
DELETED
data/spec/models/review.rb
DELETED
data/spec/models/tag.rb
DELETED