aub-record_filter 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +163 -97
- data/VERSION.yml +1 -1
- data/lib/record_filter/dsl/conjunction_dsl.rb +6 -5
- data/lib/record_filter/dsl/dsl.rb +1 -1
- data/lib/record_filter/dsl/restriction.rb +15 -0
- data/lib/record_filter/filter.rb +1 -1
- data/lib/record_filter/query.rb +1 -1
- data/lib/record_filter/table.rb +2 -1
- data/spec/active_record_spec.rb +81 -28
- data/spec/explicit_join_spec.rb +5 -5
- data/spec/implicit_join_spec.rb +36 -17
- data/spec/models.rb +7 -0
- data/spec/named_filter_spec.rb +4 -4
- data/spec/restrictions_spec.rb +7 -0
- data/spec/test.db +0 -0
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -14,176 +14,242 @@ record_filter has the following top-level features:
|
|
14
14
|
|
15
15
|
gem install outoftime-record_filter --source=http://gems.github.com
|
16
16
|
|
17
|
-
==
|
17
|
+
== Using Filters
|
18
18
|
|
19
|
-
|
19
|
+
Given a Blog model having a has_many relationship with a Post model, a simple
|
20
|
+
filter with conditions and joins might look like this.
|
20
21
|
|
21
|
-
|
22
|
-
with(:
|
23
|
-
having(:
|
22
|
+
Blog.filter do
|
23
|
+
with(:created_at).greater_than(1.day.ago)
|
24
|
+
having(:posts).with(:permalink, nil)
|
24
25
|
end
|
25
26
|
|
26
27
|
This could be expressed in ActiveRecord as:
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
Blog.find(
|
30
|
+
:all,
|
31
|
+
:joins => :posts,
|
32
|
+
:conditions => ['posts.permalink IS NULL AND blogs.created_at > ?', 'blog-post', 1.day.ago)
|
31
33
|
|
32
|
-
|
34
|
+
and it returns the same result, a list of Blog objects that are returned from the query. This
|
35
|
+
type of filter is designed to be created on the fly, but if you have a filter that you would like
|
36
|
+
to use in more than one place, it can be added to a class as a named filter. The following example
|
37
|
+
creates the same filter as above and executes it:
|
33
38
|
|
34
39
|
class Post < ActiveRecord::Base
|
35
|
-
named_filter(:
|
36
|
-
with(:
|
40
|
+
named_filter(:new_with_nil_permalink) do
|
41
|
+
with(:created_at).greater_than(1.day.ago)
|
42
|
+
having(:posts).with(:permalink, nil)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
Post.
|
46
|
+
Post.new_with_nil_permalink
|
41
47
|
|
42
|
-
This
|
48
|
+
This returns the same result as the example above but with the advantages that it is
|
49
|
+
easily reusable and that it can be combined with other named filters to produce a more
|
50
|
+
complex query:
|
43
51
|
|
44
52
|
class Post < ActiveRecord::Base
|
45
|
-
|
53
|
+
named_filter(:title_is_monkeys) { with(:title, 'monkeys') }
|
54
|
+
named_filter(:permalink_is_donkeys) { with(:title, 'donkeys') }
|
46
55
|
end
|
47
|
-
|
48
|
-
Post.with_title('scoped')
|
49
|
-
|
50
|
-
=== Restrictions
|
51
|
-
|
52
|
-
Restrictions are specified through the API using the 'with' function. The first argument to 'with' should be the
|
53
|
-
name of the field that the restriction applies to. All restriction types can be negated by chaining the 'with'
|
54
|
-
method with a call to 'not', as seen in some examples below.
|
55
56
|
|
56
|
-
|
57
|
+
Post.title_is_monkeys.permalink_is_donkeys
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
This example will return all of the posts that meet both animal-related conditions.
|
60
|
+
There is no limit to the number of filters that can be combined, and because record_filter works
|
61
|
+
seamlessly with named scopes, they can also be combined in this way as well.
|
60
62
|
|
61
|
-
|
63
|
+
Named filters can also be customized by taking any number of arguments. The example above can
|
64
|
+
be replicated with the following filter:
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
class Post < ActiveRecord::Base
|
67
|
+
named_filter(:with_title_and_permalink) do |title, permalink|
|
68
|
+
with(:title, title)
|
69
|
+
with(:permalink, permalink)
|
70
|
+
end
|
71
|
+
end
|
66
72
|
|
67
|
-
|
73
|
+
Post.with_title_and_permalink('monkeys', 'donkeys')
|
68
74
|
|
69
|
-
|
75
|
+
Named filters can also be called from other named filters and will be invoked on the correct
|
76
|
+
model even if called from a join.
|
70
77
|
|
71
|
-
|
72
|
-
|
78
|
+
class Comment < ActiveRecord::Base
|
79
|
+
named_filter(:offensive) { with(:offensive, true) }
|
80
|
+
end
|
73
81
|
|
74
|
-
|
82
|
+
class Post < ActiveRecord::Base
|
83
|
+
named_filter(:recursive_test) do
|
84
|
+
having(:comments) do
|
85
|
+
offensive
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
75
89
|
|
76
|
-
|
90
|
+
== Specifying Filters
|
77
91
|
|
78
|
-
|
92
|
+
record_filter supports all of SQL query abstractions provided by ActiveRecord, specifically:
|
79
93
|
|
80
|
-
|
81
|
-
|
94
|
+
* Conditions
|
95
|
+
* Boolean operations
|
96
|
+
* Joins
|
97
|
+
* Limits
|
98
|
+
* Offsets
|
99
|
+
* Ordering
|
100
|
+
* Grouping
|
82
101
|
|
83
|
-
|
102
|
+
The following example shows the use of each of these techniques:
|
84
103
|
|
85
|
-
|
104
|
+
Post.filter do
|
105
|
+
any_of do
|
106
|
+
with(:permalink).is_null
|
107
|
+
having(:comments) do
|
108
|
+
with(:offensive, true)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
limit(10, 100)
|
112
|
+
order(:created_at, :desc)
|
113
|
+
group_by(:comments => :offensive)
|
114
|
+
end
|
86
115
|
|
87
|
-
|
116
|
+
=== Conditions
|
88
117
|
|
89
|
-
|
118
|
+
Conditions are specified using the 'with' function, which takes as its first argument
|
119
|
+
the name of the column to restrict. If a second argument is given, it will automatically
|
120
|
+
be used as the value in an equality condition. The 'with' function will return a Restriction
|
121
|
+
object that has methods to specify a number of different conditions and to negate them:
|
90
122
|
|
91
|
-
with(:
|
123
|
+
with(:permalink, 'aardvarks') # :conditions => ['permalink = ?', 'aardvarks']
|
124
|
+
with(:permalink).equal_to('sheep') # :conditions => ['permalink = ?', 'sheep']
|
125
|
+
with(:permalink).not.equal_to('cats') # :conditions => ['permailnk <> ?', 'cats']
|
92
126
|
|
93
|
-
|
127
|
+
with(:permalink, nil) # :conditions => ['permalink IS NULL']
|
128
|
+
with(:permalink).is_null # :conditions => ['permalink IS NULL']
|
129
|
+
with(:permalink, nil).not # :conditions => ['permalink IS NOT NULL']
|
94
130
|
|
95
|
-
|
96
|
-
with(:price).is_null.not # "
|
131
|
+
The following condition types are supported through the Restriction API:
|
97
132
|
|
98
|
-
|
133
|
+
* Equality
|
134
|
+
* Comparisons (> >= < <=)
|
135
|
+
* Between
|
136
|
+
* In
|
137
|
+
* Is null
|
138
|
+
* Like
|
99
139
|
|
100
|
-
|
140
|
+
=== Boolean Operations
|
101
141
|
|
102
|
-
|
142
|
+
Conditions can be combined with boolean operators using the methods all_of, any_of, none_of
|
143
|
+
and not_all_of. These methods take a block where any conditions they contain will be combined
|
144
|
+
using AND, OR and NOT to create the correct condition. The block can also contain any number of
|
145
|
+
joins or other boolean operations. The default operator is all_of.
|
103
146
|
|
104
|
-
|
147
|
+
Post.filter do
|
148
|
+
with(:id, 4)
|
149
|
+
with(:permalink, 'ack')
|
150
|
+
end
|
105
151
|
|
106
|
-
|
152
|
+
:conditions => ['id = ? AND permalink = ?', 4, 'ack']
|
107
153
|
|
108
|
-
|
154
|
+
Post.filter do
|
155
|
+
any_of
|
156
|
+
with(:id, 3)
|
157
|
+
with(:permalink, 'booya')
|
158
|
+
end
|
159
|
+
end
|
109
160
|
|
110
|
-
|
161
|
+
:conditions => ['id = ? OR permalink = ?', 3, 'booya']
|
111
162
|
|
112
|
-
|
163
|
+
Post.filter do
|
164
|
+
none_of
|
165
|
+
with(:id, 2)
|
166
|
+
with(:permalink, 'ouch')
|
167
|
+
end
|
168
|
+
end
|
113
169
|
|
114
|
-
|
170
|
+
:conditions => ['NOT (id = ? OR permalink = ?', 2, 'ouch']
|
115
171
|
|
172
|
+
=== Joins
|
116
173
|
|
117
|
-
|
174
|
+
Joins in record_filter come in two varieties. Using the information in ActiveRecord associations,
|
175
|
+
it is possible to perform most joins easily using the 'having' method, which requires no specification
|
176
|
+
of the columns to use for the join. In cases where an association does not apply, it is also possible
|
177
|
+
to create an explicit join that can include both the columns to combine as well as restrictions on
|
178
|
+
the columns in the join table.
|
118
179
|
|
119
|
-
|
180
|
+
In a filter for a Post model that has_many comments, the following two examples are equivalent:
|
120
181
|
|
121
|
-
having(:comments)
|
182
|
+
having(:comments)
|
122
183
|
|
123
|
-
|
184
|
+
join(Comment, :inner) do
|
185
|
+
on(:id => :post_id)
|
186
|
+
end
|
124
187
|
|
125
|
-
|
188
|
+
With an explicit join, any number of columns can be matched in this way, and both join types
|
189
|
+
accept a block in which any number of conditions, boolean operations, or other joins can be
|
190
|
+
added. Explicit joins also allow conditions to be set on columns of the table being joined:
|
126
191
|
|
127
|
-
|
192
|
+
having(:comments).with(:offensive, true)
|
128
193
|
|
129
194
|
having(:comments) do
|
130
|
-
with(:created_at).
|
131
|
-
having(:author).with(:name, 'Bubba')
|
195
|
+
with(:created_at).greater_than(2.days.ago)
|
132
196
|
end
|
133
197
|
|
134
|
-
|
135
|
-
|
198
|
+
join(Comment, :inner) do
|
199
|
+
on(:id => :commentable_id)
|
200
|
+
on(:commentable_type).equal_to('Post')
|
201
|
+
end
|
136
202
|
|
137
|
-
|
203
|
+
With implicit joins, it is also possible to use a hash as the association name, in which case
|
204
|
+
multiple joins can be created with one statement. If the comment model has_one Author, this
|
205
|
+
example will join both tables and add a condition on the author.
|
138
206
|
|
139
|
-
|
140
|
-
supported, using the 'join' function. Its arguments are the class to be joined against, the join type (:inner, left or :right) and
|
141
|
-
an optional alias for the join table. A block should also be supplied in order to specify the columns to use for the join using the
|
142
|
-
'on' method.
|
207
|
+
having(:comments => :author).with(:name, 'Bob')
|
143
208
|
|
144
|
-
|
145
|
-
join(Comment, :inner, :posts__comments_alias) do
|
146
|
-
on(:id => :commentable_id)
|
147
|
-
on(:commentable_type, 'Post')
|
148
|
-
end
|
149
|
-
end
|
209
|
+
=== Limits and Offsets
|
150
210
|
|
151
|
-
|
211
|
+
These are specified using the 'limit' method, which takes two arguments, the offset and the
|
212
|
+
limit. If only one argument is given, it is assumed to be the limit.
|
152
213
|
|
153
|
-
|
214
|
+
limit(10, 100) # :offset => 10, :limit => 100
|
215
|
+
limit(100) # :offset => 0, :limit => 100
|
154
216
|
|
155
|
-
|
156
|
-
* all_of
|
157
|
-
* none_of
|
158
|
-
* not_all_of
|
217
|
+
=== Ordering
|
159
218
|
|
160
|
-
|
219
|
+
Ordering is done through the 'order' method, which accepts arguments for the column and direction.
|
220
|
+
The column can either be passed as the name of a column in the class that is being filtered or as
|
221
|
+
a hash that represents a path through the joined associations to the correct column. The direction argument
|
222
|
+
should be either :asc or :desc and defaults to :asc if not given. Multiple calls to 'order' are
|
223
|
+
allowed and will be applied in the order in which they were given.
|
161
224
|
|
162
|
-
|
163
|
-
with(:
|
164
|
-
|
225
|
+
Post.filter do
|
226
|
+
having(:comments).with(:offensive, true)
|
227
|
+
order(:created_at, :desc)
|
228
|
+
order(:comments => :id)
|
165
229
|
end
|
166
230
|
|
167
|
-
# :
|
168
|
-
|
169
|
-
=== Limits and ordering
|
231
|
+
# :order => "'posts'.created_at DESC posts__comments.id ASC"
|
170
232
|
|
171
|
-
|
233
|
+
=== Grouping
|
172
234
|
|
173
|
-
|
235
|
+
Grouping is specified with the 'group_by' method, which accepts either the name of a column in the
|
236
|
+
class that is being filtered or a hash that represents a path through the joined associations. If
|
237
|
+
there are multiple calls to 'group_by' they will be combined in the final result, maintaining the
|
238
|
+
order in which they were given.
|
174
239
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
240
|
+
Post.filter do
|
241
|
+
having(:comments).with(:created_at).greater_than(1.hour.ago)
|
242
|
+
group_by(:permalink)
|
243
|
+
group_by(:comments => :offensive)
|
244
|
+
end
|
180
245
|
|
246
|
+
# :group => "'posts'.permalink, posts__comments.offensive'
|
181
247
|
|
182
248
|
== LICENSE:
|
183
249
|
|
184
250
|
(The MIT License)
|
185
251
|
|
186
|
-
Copyright (c) 2008 Mat Brown
|
252
|
+
Copyright (c) 2008 Mat Brown, Aubrey Holland
|
187
253
|
|
188
254
|
Permission is hereby granted, free of charge, to any person obtaining
|
189
255
|
a copy of this software and associated documentation files (the
|
data/VERSION.yml
CHANGED
@@ -159,7 +159,7 @@ module RecordFilter
|
|
159
159
|
|
160
160
|
# Create an implicit join using an association as the target. This method allows you to
|
161
161
|
# easily specify a join without specifying the columns to use by taking any needed data
|
162
|
-
# from the
|
162
|
+
# from the given ActiveRecord association. If provided, the block will be evaluated in
|
163
163
|
# the context of the table that has been joined, so any restrictions or other joins will
|
164
164
|
# be performed using its columns and associations. For example, if a Post has_many comments
|
165
165
|
# then the following code will join to the comments table and restrict the comments based
|
@@ -169,12 +169,13 @@ module RecordFilter
|
|
169
169
|
# with(:created_at).greater_than(3.days.ago)
|
170
170
|
# end
|
171
171
|
# end
|
172
|
-
# If one argument is given, it is assumed to
|
173
|
-
# that will be used for the join and a join type of :inner will be used. If two arguments
|
174
|
-
# provided, the first one is assumed to be the join type, which can be one of :inner, :left or
|
172
|
+
# If one argument is given, it is assumed to represent the name of the association
|
173
|
+
# that will be used for the join and a join type of :inner will be used by default. If two arguments
|
174
|
+
# are provided, the first one is assumed to be the join type, which can be one of :inner, :left or
|
175
175
|
# :right and the second one is the association name. An alias will automatically be created
|
176
176
|
# for the joined table named "#{left_table}__#{association_name}", so in the above example, the
|
177
|
-
# alias would be posts__comments.
|
177
|
+
# alias would be posts__comments. It is also possible to provide a hash as the association
|
178
|
+
# name, in which case a trail of associations can be joined in one statment.
|
178
179
|
#
|
179
180
|
# ==== Parameters
|
180
181
|
# join_type<Symbol>::
|
@@ -53,7 +53,7 @@ module RecordFilter
|
|
53
53
|
# a column in the class that is being filtered. With a hash argument, it is possible
|
54
54
|
# to specify a path to a column in one of the joined tables, as seen above.
|
55
55
|
# direction<Symbol>::
|
56
|
-
# Specifies the direction of the join. Should be either :asc or :desc.
|
56
|
+
# Specifies the direction of the join. Should be either :asc or :desc and defaults to :asc.
|
57
57
|
#
|
58
58
|
# ==== Returns
|
59
59
|
# nil
|
@@ -201,6 +201,21 @@ module RecordFilter
|
|
201
201
|
self
|
202
202
|
end
|
203
203
|
|
204
|
+
# Create a negated IN restriction of the form ['column NOT IN (?)', value]
|
205
|
+
#
|
206
|
+
# ==== Parameters
|
207
|
+
# value::
|
208
|
+
# Either a single item or an array of values to form the inclusion test.
|
209
|
+
#
|
210
|
+
# ==== Returns
|
211
|
+
# Restriction:: self
|
212
|
+
#
|
213
|
+
# @public
|
214
|
+
def not_in(value)
|
215
|
+
@value, @operator, @negated = value, :in, true
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
204
219
|
# Create a LIKE restriction of the form ['column LIKE ?', value]
|
205
220
|
#
|
206
221
|
# ==== Parameters
|
data/lib/record_filter/filter.rb
CHANGED
@@ -2,7 +2,7 @@ module RecordFilter
|
|
2
2
|
# This class is the value that is returned from the execution of a filter.
|
3
3
|
class Filter
|
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?)
|
5
|
+
NON_DELEGATE_METHODS = %w(debugger nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?)
|
6
6
|
|
7
7
|
[].methods.each do |m|
|
8
8
|
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
|
data/lib/record_filter/query.rb
CHANGED
@@ -21,7 +21,7 @@ module RecordFilter
|
|
21
21
|
conditions = @conjunction.to_conditions
|
22
22
|
params = { :conditions => conditions } if conditions
|
23
23
|
joins = @table.all_joins
|
24
|
-
params[:joins] = joins.map { |join| join.to_sql }
|
24
|
+
params[:joins] = joins.map { |join| join.to_sql } unless joins.empty?
|
25
25
|
if (joins.any? { |j| j.requires_distinct_select? })
|
26
26
|
if count_query
|
27
27
|
params[:select] = "DISTINCT #{@table.model_class.quoted_table_name}.#{@table.model_class.primary_key}"
|
data/lib/record_filter/table.rb
CHANGED
@@ -17,6 +17,7 @@ module RecordFilter
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def join_association(association_name, join_type=nil, options={})
|
20
|
+
association_name = association_name.to_sym
|
20
21
|
@joins_cache[association_name] ||=
|
21
22
|
begin
|
22
23
|
association = @model_class.reflect_on_association(association_name)
|
@@ -115,7 +116,7 @@ module RecordFilter
|
|
115
116
|
protected
|
116
117
|
|
117
118
|
def alias_for_association(association)
|
118
|
-
"#{@aliased ? @table_alias.to_s : @model_class.table_name}__#{association.name}"
|
119
|
+
"#{@aliased ? @table_alias.to_s : @model_class.table_name}__#{association.name.to_s.downcase}"
|
119
120
|
end
|
120
121
|
|
121
122
|
alias_method :alias_for_class, :alias_for_association
|
data/spec/active_record_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe 'active record options' do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'should create the correct join' do
|
21
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "news_stories" AS blogs__stories ON "blogs".id = blogs__stories.blog_id)
|
21
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "news_stories" AS blogs__stories ON "blogs".id = blogs__stories.blog_id)]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -34,7 +34,7 @@ describe 'active record options' do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'should create the correct join' do
|
37
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__special_posts ON "blogs".id = blogs__special_posts.special_blog_id)
|
37
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__special_posts ON "blogs".id = blogs__special_posts.special_blog_id)]
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -50,7 +50,7 @@ describe 'active record options' do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'should create the correct join' do
|
53
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__special_public_posts ON "blogs".special_id = blogs__special_public_posts.blog_id)
|
53
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__special_public_posts ON "blogs".special_id = blogs__special_public_posts.blog_id)]
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -66,7 +66,7 @@ describe 'active record options' do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'should create the correct join' do
|
69
|
-
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)
|
69
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id), %q(INNER JOIN "comments" AS blogs__posts__bad_comments ON blogs__posts.id = blogs__posts__bad_comments.post_id)]
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -82,22 +82,9 @@ describe 'active record options' do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'should create the correct join' do
|
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)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# :include
|
90
|
-
# :finder_sql
|
91
|
-
# :counter_sql
|
92
|
-
# :group
|
93
|
-
# :having
|
94
|
-
# :limit
|
95
|
-
# :offset
|
96
|
-
# :select
|
97
|
-
# :uniq
|
98
|
-
# :readonly
|
99
|
-
# :order
|
100
|
-
# :conditions
|
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')), %q(INNER JOIN "posts" AS blogs__features__featurable ON blogs__features.featurable_id = blogs__features__featurable.id)]
|
86
|
+
end
|
87
|
+
end
|
101
88
|
end
|
102
89
|
|
103
90
|
describe 'for belongs_to' do
|
@@ -114,7 +101,7 @@ describe 'active record options' do
|
|
114
101
|
end
|
115
102
|
|
116
103
|
it 'should create the correct join' do
|
117
|
-
Post.last_find[:joins].should == %q(INNER JOIN "blogs" AS posts__publication ON "posts".blog_id = posts__publication.id)
|
104
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__publication ON "posts".blog_id = posts__publication.id)]
|
118
105
|
end
|
119
106
|
end
|
120
107
|
|
@@ -130,15 +117,81 @@ describe 'active record options' do
|
|
130
117
|
end
|
131
118
|
|
132
119
|
it 'should create the correct join' do
|
133
|
-
Post.last_find[:joins].should == %q(INNER JOIN "blogs" AS posts__special_blog ON "posts".special_blog_id = posts__special_blog.id)
|
120
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__special_blog ON "posts".special_blog_id = posts__special_blog.id)]
|
134
121
|
end
|
135
122
|
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'working with named scopes' do
|
126
|
+
before do
|
127
|
+
@blog = Class.new(Blog)
|
128
|
+
@blog.named_scope :with_high_id, { :conditions => ['id > 100'] }
|
129
|
+
@blog.named_filter(:published) { with(:published, true) }
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should concatenate the filter with the scope correctly' do
|
133
|
+
@blog.with_high_id.published.inspect
|
134
|
+
@blog.last_find[:conditions].should == %q(("blogs".published = 't') AND (id > 100))
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should concatenate correctly when called in the other order' do
|
138
|
+
@blog.published.with_high_id.inspect
|
139
|
+
@blog.last_find[:conditions].should == %q((id > 100) AND ("blogs".published = 't'))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'working with named scopes when there are a number of joins' do
|
144
|
+
before do
|
145
|
+
@blog = Class.new(Blog)
|
146
|
+
@blog.named_scope :ads_with_sale, { :joins => :ads, :conditions => ["'ads'.content LIKE ?", '%sale%'] }
|
147
|
+
@blog.named_filter(:with_permalinked_posts) { having(:posts).with(:permalink).is_not_null }
|
148
|
+
@blog.named_filter(:with_offensive_comments) { having(:comments).with(:offensive, true) }
|
149
|
+
@blog.with_permalinked_posts.ads_with_sale.with_offensive_comments.inspect
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should concatenate the conditions correctly' do
|
153
|
+
@blog.last_find[:conditions].should == %q((blogs__posts__comments.offensive = 't') AND (('ads'.content LIKE '%sale%') AND (blogs__posts.permalink IS NOT NULL)))
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should concatenate the joins correctly and not throw away my joins like AR usually does' do
|
157
|
+
@blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id), %q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id), %q(INNER JOIN "ads" ON ads.blog_id = blogs.id)]
|
158
|
+
end
|
159
|
+
end
|
136
160
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
161
|
+
describe 'working with named scopes that join to the same table' do
|
162
|
+
before do
|
163
|
+
@blog = Class.new(Blog)
|
164
|
+
@blog.named_scope :with_crazy_post_permalinks, { :joins => :posts, :conditions => ["'posts'.permalink = ?", 'crazy'] }
|
165
|
+
@blog.named_filter(:with_empty_permalinks) { having(:posts).with(:permalink, nil) }
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should concatenate the conditions correctly' do
|
169
|
+
@blog.with_crazy_post_permalinks.with_empty_permalinks.inspect
|
170
|
+
@blog.last_find[:conditions].should == %q((blogs__posts.permalink IS NULL) AND ('posts'.permalink = 'crazy'))
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should concatenate the joins correctly' do
|
174
|
+
@blog.with_crazy_post_permalinks.with_empty_permalinks.inspect
|
175
|
+
@blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id), %q(INNER JOIN "posts" ON posts.blog_id = blogs.id)]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'working with default scopes' do
|
180
|
+
describe 'with a simple filter' do
|
181
|
+
before do
|
182
|
+
Article.filter do
|
183
|
+
with(:contents, 'something')
|
184
|
+
end.inspect
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should use the correct order' do
|
188
|
+
Article.last_find[:order].should == %q(created_at DESC)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should use the correct conditions' do
|
192
|
+
pending 'currently the IS NULL condition is added twice.'
|
193
|
+
Article.last_find[:conditions].should == %q((("articles"."created_at" IS NULL) AND ("articles".contents = 'something')))
|
194
|
+
end
|
195
|
+
end
|
143
196
|
end
|
144
197
|
end
|
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 OUTER 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 OUTER 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 OUTER JOIN "features" AS
|
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 OUTER JOIN "features" AS
|
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
|
|
@@ -101,7 +101,7 @@ describe 'explicit joins' do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
it 'should produce the correct join' do
|
104
|
-
@blog.last_find[:joins].should == %q(INNER JOIN "ads" AS blogs__ads ON "blogs".id = blogs__ads.blog_id LEFT OUTER JOIN "posts" AS
|
104
|
+
@blog.last_find[:joins].should == [%q(INNER JOIN "ads" AS blogs__ads ON "blogs".id = blogs__ads.blog_id), %q(LEFT OUTER JOIN "posts" AS blogs__post ON "blogs".id = blogs__post.blog_id), %q(INNER JOIN "comments" AS blogs__post__comment ON blogs__post.id = blogs__post__comment.post_id AND (blogs__post__comment.offensive = 't'))]
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
data/spec/implicit_join_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe 'implicit joins' do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should add correct join' do
|
17
|
-
Post.last_find[:joins].should == %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)
|
17
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'should query against condition on join table' do
|
@@ -24,7 +24,7 @@ describe 'implicit joins' do
|
|
24
24
|
|
25
25
|
shared_examples_for 'multiple conditions on single join' do
|
26
26
|
it 'should add join once' do
|
27
|
-
Post.last_find[:joins].should == %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)
|
27
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'should query against conditions on join table' do
|
@@ -65,7 +65,7 @@ describe 'implicit joins' do
|
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should add correct join' do
|
68
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id)
|
68
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id)]
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'should query against condition on join table' do
|
@@ -84,8 +84,8 @@ describe 'implicit joins' do
|
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'should add both joins' do
|
87
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id
|
88
|
-
|
87
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id),
|
88
|
+
%q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'should query against both conditions' do
|
@@ -103,8 +103,8 @@ describe 'implicit joins' do
|
|
103
103
|
end
|
104
104
|
|
105
105
|
it 'should add both joins' do
|
106
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id
|
107
|
-
|
106
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id),
|
107
|
+
%q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
108
108
|
end
|
109
109
|
|
110
110
|
it 'should query against both conditions' do
|
@@ -120,7 +120,7 @@ describe 'implicit joins' do
|
|
120
120
|
end
|
121
121
|
|
122
122
|
it 'should add correct join' do
|
123
|
-
Post.last_find[:joins].should == %q(INNER JOIN "photos" AS posts__photo ON "posts".id = posts__photo.post_id)
|
123
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "photos" AS posts__photo ON "posts".id = posts__photo.post_id)]
|
124
124
|
end
|
125
125
|
|
126
126
|
it 'should query against condition on join table' do
|
@@ -136,8 +136,8 @@ describe 'implicit joins' do
|
|
136
136
|
end
|
137
137
|
|
138
138
|
it 'should add correct join' do
|
139
|
-
Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id
|
140
|
-
|
139
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id),
|
140
|
+
%q(INNER JOIN "photos" AS blogs__posts__photo ON blogs__posts.id = blogs__posts__photo.post_id)]
|
141
141
|
end
|
142
142
|
|
143
143
|
it 'should query against condition on join table' do
|
@@ -153,8 +153,8 @@ describe 'implicit joins' do
|
|
153
153
|
end
|
154
154
|
|
155
155
|
it 'should add correct join' do
|
156
|
-
Post.last_find[:joins].should == %q(INNER JOIN "posts_tags" AS __posts__tags ON "posts".id = __posts__tags.post_id
|
157
|
-
|
156
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "posts_tags" AS __posts__tags ON "posts".id = __posts__tags.post_id),
|
157
|
+
%q(INNER JOIN "tags" AS posts__tags ON __posts__tags.tag_id = posts__tags.id)]
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
@@ -235,7 +235,7 @@ describe 'implicit joins' do
|
|
235
235
|
end
|
236
236
|
|
237
237
|
it 'should create the correct join' do
|
238
|
-
Blog.last_find[:joins].should == %q(LEFT OUTER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id)
|
238
|
+
Blog.last_find[:joins].should == [%q(LEFT OUTER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id)]
|
239
239
|
end
|
240
240
|
end
|
241
241
|
|
@@ -253,7 +253,7 @@ describe 'implicit joins' do
|
|
253
253
|
end
|
254
254
|
|
255
255
|
it 'should create the correct join' do
|
256
|
-
Blog.last_find[:joins].should == %q(LEFT OUTER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id LEFT OUTER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)
|
256
|
+
Blog.last_find[:joins].should == [%q(LEFT OUTER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id), %q(LEFT OUTER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
@@ -271,7 +271,7 @@ describe 'implicit joins' do
|
|
271
271
|
end
|
272
272
|
|
273
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'))
|
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
275
|
end
|
276
276
|
end
|
277
277
|
|
@@ -289,7 +289,7 @@ describe 'implicit joins' do
|
|
289
289
|
end
|
290
290
|
|
291
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)
|
292
|
+
Blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id), %q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
293
293
|
end
|
294
294
|
end
|
295
295
|
|
@@ -307,7 +307,26 @@ describe 'implicit joins' do
|
|
307
307
|
end
|
308
308
|
|
309
309
|
it 'should create the correct join' do
|
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)
|
310
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "authors" AS posts__author ON "posts".id = posts__author.post_id), %q(INNER JOIN "users" AS posts__author__user ON posts__author.user_id = posts__author__user.id)]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe 'passing strings instead of symbols' do
|
315
|
+
before do
|
316
|
+
Post.filter do
|
317
|
+
having('comments') do
|
318
|
+
with('offensive', true)
|
319
|
+
end
|
320
|
+
with('id').gte(12)
|
321
|
+
end.inspect
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should create the correct condition' do
|
325
|
+
Post.last_find[:conditions].should == [%q((posts__comments.offensive = ?) AND ("posts".id >= ?)), true, 12]
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'should create the correct join' do
|
329
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)]
|
311
330
|
end
|
312
331
|
end
|
313
332
|
end
|
data/spec/models.rb
CHANGED
@@ -4,6 +4,12 @@ class Ad < ActiveRecord::Base
|
|
4
4
|
end
|
5
5
|
|
6
6
|
|
7
|
+
class Article < ActiveRecord::Base
|
8
|
+
extend TestModel
|
9
|
+
default_scope :order => 'created_at DESC', :conditions => { :created_at => nil }
|
10
|
+
end
|
11
|
+
|
12
|
+
|
7
13
|
class Author < ActiveRecord::Base
|
8
14
|
extend TestModel
|
9
15
|
belongs_to :user
|
@@ -24,6 +30,7 @@ class Blog < ActiveRecord::Base
|
|
24
30
|
has_many :features
|
25
31
|
has_many :featured_posts, :through => :features, :source => :featurable, :source_type => 'Post'
|
26
32
|
has_many :posts_with_comments, :class_name => 'Post', :include => :comments
|
33
|
+
has_many :articles
|
27
34
|
end
|
28
35
|
|
29
36
|
|
data/spec/named_filter_spec.rb
CHANGED
@@ -119,7 +119,7 @@ describe 'named filters' do
|
|
119
119
|
having(:comments).offensive_or_not(true)
|
120
120
|
end.inspect
|
121
121
|
Post.last_find[:conditions].should == [%q(posts__comments.offensive = ?), true]
|
122
|
-
Post.last_find[:joins].should == %q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)
|
122
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)]
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'should work correctly with the named filter called within the having block' do
|
@@ -129,7 +129,7 @@ describe 'named filters' do
|
|
129
129
|
end
|
130
130
|
end.inspect
|
131
131
|
Post.last_find[:conditions].should == [%q(posts__comments.offensive = ?), false]
|
132
|
-
Post.last_find[:joins].should == %q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)
|
132
|
+
Post.last_find[:joins].should == [%q(INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id)]
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
@@ -181,7 +181,7 @@ describe 'named filters' do
|
|
181
181
|
base = @post.for_blog(1)
|
182
182
|
base.with_offensive_comments
|
183
183
|
base.inspect
|
184
|
-
@post.last_find[:joins].should == %q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)
|
184
|
+
@post.last_find[:joins].should == [%q(INNER JOIN "blogs" AS posts__blog ON "posts".blog_id = posts__blog.id)]
|
185
185
|
end
|
186
186
|
|
187
187
|
it 'should not change an original filter when reusing it' do
|
@@ -250,7 +250,7 @@ describe 'named filters' do
|
|
250
250
|
|
251
251
|
it 'compile the joins correctly' do
|
252
252
|
@blog.with_offensive_comments.with_ads_with_content('ack').inspect
|
253
|
-
@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)]
|
253
|
+
@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), %q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)]
|
254
254
|
end
|
255
255
|
end
|
256
256
|
|
data/spec/restrictions_spec.rb
CHANGED
@@ -61,6 +61,13 @@ describe 'RecordFilter restrictions' do
|
|
61
61
|
Post.last_find.should == { :conditions => [%q{"posts".blog_id NOT IN (?)}, [1, 3, 5]] }
|
62
62
|
end
|
63
63
|
|
64
|
+
it 'should work correctly for NOT IN' do
|
65
|
+
Post.filter do
|
66
|
+
with(:blog_id).not_in [1, 3, 5]
|
67
|
+
end.inspect
|
68
|
+
Post.last_find.should == { :conditions => [%q{"posts".blog_id NOT IN (?)}, [1, 3, 5]] }
|
69
|
+
end
|
70
|
+
|
64
71
|
it 'should do the right thing for IN filters with empty arrays' do
|
65
72
|
Post.filter do
|
66
73
|
with(:blog_id).in([])
|
data/spec/test.db
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aub-record_filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.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-06 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|