aub-record_filter 0.9.3 → 0.9.4
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 +53 -20
- data/Rakefile +31 -3
- data/VERSION.yml +1 -1
- data/lib/record_filter/active_record.rb +4 -3
- data/lib/record_filter/column_parser.rb +14 -0
- data/lib/record_filter/conjunctions.rb +4 -4
- data/lib/record_filter/dsl/dsl.rb +23 -3
- data/lib/record_filter/dsl/restriction.rb +1 -1
- data/lib/record_filter/group_by.rb +3 -5
- data/lib/record_filter/join.rb +5 -3
- data/lib/record_filter/order.rb +9 -10
- data/lib/record_filter/query.rb +13 -9
- data/lib/record_filter/restriction_factory.rb +21 -0
- data/lib/record_filter/restrictions.rb +0 -4
- data/lib/record_filter/table.rb +18 -10
- data/lib/record_filter.rb +2 -2
- data/spec/exception_spec.rb +3 -3
- data/spec/limits_and_ordering_spec.rb +59 -1
- data/spec/restrictions_spec.rb +7 -0
- data/spec/test.db +0 -0
- data/test/performance_test.rb +3 -0
- metadata +5 -3
data/README.rdoc
CHANGED
@@ -19,6 +19,10 @@ intended to be a getting started guide that should cover the most common uses.
|
|
19
19
|
|
20
20
|
gem install aub-record_filter --source=http://gems.github.com
|
21
21
|
|
22
|
+
In Rails, you'll need to add this to your config/environment.rb file:
|
23
|
+
|
24
|
+
config.gem 'aub-record_filter', :lib => 'record_filter', :source => 'http://gems.github.com'
|
25
|
+
|
22
26
|
== Using Filters
|
23
27
|
|
24
28
|
Given a Blog model having a has_many relationship with a Post model, a simple
|
@@ -31,24 +35,27 @@ filter with conditions and joins might look like this.
|
|
31
35
|
|
32
36
|
This could be expressed in ActiveRecord as:
|
33
37
|
|
34
|
-
Blog.
|
35
|
-
:all,
|
38
|
+
Blog.all(
|
36
39
|
:joins => :posts,
|
37
|
-
:conditions =>
|
40
|
+
:conditions => {
|
41
|
+
:posts => {:permalink => nil},
|
42
|
+
:created_at => 1.day.ago..Time.now
|
43
|
+
}
|
44
|
+
)
|
38
45
|
|
39
46
|
and it returns the same result, a list of Blog objects that are the result of the query. This
|
40
47
|
type of filter is designed to be created on the fly, but if you have a filter that you would like
|
41
48
|
to use in more than one place, it can be added to a class as a named filter. The following example
|
42
49
|
creates the same filter as above and executes it:
|
43
50
|
|
44
|
-
class
|
45
|
-
named_filter(:
|
51
|
+
class Blog < ActiveRecord::Base
|
52
|
+
named_filter(:new_with_unlinked_posts) do
|
46
53
|
with(:created_at).greater_than(1.day.ago)
|
47
54
|
having(:posts).with(:permalink, nil)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
51
|
-
|
58
|
+
Blog.new_with_unlinked_posts
|
52
59
|
|
53
60
|
This returns the same result as the example above but with the advantages that it is
|
54
61
|
easily reusable and that it can be combined with other named filters to produce a more
|
@@ -85,13 +92,15 @@ model even if called from a join.
|
|
85
92
|
end
|
86
93
|
|
87
94
|
class Post < ActiveRecord::Base
|
88
|
-
named_filter(:
|
95
|
+
named_filter(:using_other_filter) do
|
89
96
|
having(:comments) do
|
90
97
|
offensive
|
91
98
|
end
|
92
99
|
end
|
93
100
|
end
|
94
101
|
|
102
|
+
Post.using_other_filter
|
103
|
+
|
95
104
|
== Specifying Filters
|
96
105
|
|
97
106
|
record_filter supports all of the SQL query abstractions provided by ActiveRecord, specifically:
|
@@ -125,13 +134,13 @@ the name of the column to restrict. If a second argument is given, it will autom
|
|
125
134
|
be used as the value in an equality condition. The 'with' function will return a Restriction
|
126
135
|
object that has methods to specify a number of different conditions and to negate them:
|
127
136
|
|
128
|
-
with(:permalink, 'aardvarks') #
|
129
|
-
with(:permalink).equal_to('sheep') #
|
130
|
-
with(:permalink).not.equal_to('cats') #
|
137
|
+
with(:permalink, 'aardvarks') # ['permalink = ?', 'aardvarks']
|
138
|
+
with(:permalink).equal_to('sheep') # ['permalink = ?', 'sheep']
|
139
|
+
with(:permalink).not.equal_to('cats') # ['permailnk <> ?', 'cats']
|
131
140
|
|
132
|
-
with(:permalink, nil) #
|
133
|
-
with(:permalink).is_null #
|
134
|
-
with(:permalink, nil).not #
|
141
|
+
with(:permalink, nil) # ['permalink IS NULL']
|
142
|
+
with(:permalink).is_null # ['permalink IS NULL']
|
143
|
+
with(:permalink, nil).not # ['permalink IS NOT NULL']
|
135
144
|
|
136
145
|
The following condition types are supported through the Restriction API:
|
137
146
|
|
@@ -141,8 +150,23 @@ The following condition types are supported through the Restriction API:
|
|
141
150
|
* In
|
142
151
|
* Is null
|
143
152
|
* Like
|
153
|
+
* Negation of all of the above
|
154
|
+
|
155
|
+
And here are some examples. See the RDoc page for
|
156
|
+
{DSL::Restriction}[http://aub.github.com/record_filter/rdoc/classes/RecordFilter/DSL/Restriction.html]
|
157
|
+
for more details on how to use them.
|
158
|
+
|
159
|
+
with(:featured_at).greater_than(Time.now) # ['featured_at > ?', Time.now]
|
160
|
+
|
161
|
+
with(:price).lte(1000) # ['price <= ?', 1000]
|
162
|
+
|
163
|
+
with(:created_at).between(time_a..time_b) # ['created_at BETWEEN ? AND ?', time_a, time_b]
|
144
164
|
|
145
|
-
|
165
|
+
with(:id).in([1, 2, 3]) # ['id in (?)', [1, 2, 3]]
|
166
|
+
|
167
|
+
with(:content).like('%easy%') # ['content LIKE ?', '%easy%']
|
168
|
+
|
169
|
+
with(:content).not.like('%hard%') # ['content NOT LIKE ?', '%hard%']
|
146
170
|
|
147
171
|
=== Boolean Operations
|
148
172
|
|
@@ -156,25 +180,34 @@ joins or other boolean operations. The default operator is all_of.
|
|
156
180
|
with(:permalink, 'ack')
|
157
181
|
end
|
158
182
|
|
159
|
-
|
183
|
+
# ['id = ? AND permalink = ?', 4, 'ack']
|
160
184
|
|
161
185
|
Post.filter do
|
162
|
-
any_of
|
186
|
+
any_of do
|
163
187
|
with(:id, 3)
|
164
188
|
with(:permalink, 'booya')
|
165
189
|
end
|
166
190
|
end
|
167
191
|
|
168
|
-
|
192
|
+
# ['id = ? OR permalink = ?', 3, 'booya']
|
169
193
|
|
170
194
|
Post.filter do
|
171
|
-
none_of
|
195
|
+
none_of do
|
172
196
|
with(:id, 2)
|
173
197
|
with(:permalink, 'ouch')
|
174
198
|
end
|
175
199
|
end
|
176
200
|
|
177
|
-
|
201
|
+
# ['NOT (id = ? OR permalink = ?)', 2, 'ouch']
|
202
|
+
|
203
|
+
Post.filter do
|
204
|
+
not_all_of do
|
205
|
+
with(:id, 1)
|
206
|
+
with(:permalink, 'bonobo')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# ['NOT (id = ? AND permalink = ?)', 1, 'bonobo']
|
178
211
|
|
179
212
|
=== Joins
|
180
213
|
|
@@ -257,7 +290,7 @@ order in which they were given.
|
|
257
290
|
|
258
291
|
(The MIT License)
|
259
292
|
|
260
|
-
Copyright (c) 2008 Aubrey Holland, Mat Brown
|
293
|
+
Copyright (c) 2008-2009 Aubrey Holland, Mat Brown
|
261
294
|
|
262
295
|
Permission is hereby granted, free of charge, to any person obtaining
|
263
296
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
# ENV['RUBYOPT'] = '-W1'
|
2
|
-
|
3
1
|
require 'rubygems'
|
4
2
|
require 'rake'
|
5
3
|
require 'rake/testtask'
|
6
4
|
|
7
5
|
FileList['tasks/**/*.rake'].each { |file| load file }
|
8
6
|
|
9
|
-
task :default => :spec
|
7
|
+
task :default => ["db:spec:prepare", :spec]
|
10
8
|
|
11
9
|
begin
|
12
10
|
require 'jeweler'
|
@@ -41,6 +39,7 @@ Rake::RDocTask.new do |rdoc|
|
|
41
39
|
rdoc.title = "record_filter #{version}"
|
42
40
|
rdoc.rdoc_files.include('README*')
|
43
41
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
42
|
+
rdoc.options << '--webcvs=http://github.com/aub/record_filter/tree/master/'
|
44
43
|
end
|
45
44
|
|
46
45
|
begin
|
@@ -56,3 +55,32 @@ rescue LoadError
|
|
56
55
|
puts 'Ruby-prof not available. Profiling tests are disabled.'
|
57
56
|
end
|
58
57
|
|
58
|
+
begin
|
59
|
+
require 'metric_fu'
|
60
|
+
MetricFu::Configuration.run do |config|
|
61
|
+
#define which metrics you want to use
|
62
|
+
config.metrics = [:churn, :flog, :flay, :reek, :roodi, :rcov] # :saikuro, :stats
|
63
|
+
config.flay = { :dirs_to_flay => ['lib'] }
|
64
|
+
config.flog = { :dirs_to_flog => ['lib'] }
|
65
|
+
config.reek = { :dirs_to_reek => ['lib'] }
|
66
|
+
config.roodi = { :dirs_to_roodi => ['lib'] }
|
67
|
+
config.saikuro = { :output_directory => 'scratch_directory/saikuro',
|
68
|
+
:input_directory => ['lib'],
|
69
|
+
:cyclo => "",
|
70
|
+
:filter_cyclo => "0",
|
71
|
+
:warn_cyclo => "5",
|
72
|
+
:error_cyclo => "7",
|
73
|
+
:formater => "text"} #this needs to be set to "text"
|
74
|
+
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
75
|
+
config.rcov = { :test_files => ['spec/**/*_spec.rb'],
|
76
|
+
:rcov_opts => ["--sort coverage",
|
77
|
+
"--no-html",
|
78
|
+
"--text-coverage",
|
79
|
+
"--no-color",
|
80
|
+
"--profile",
|
81
|
+
"--exclude spec"]}
|
82
|
+
end
|
83
|
+
rescue LoadError
|
84
|
+
puts 'Install metric_fu for code quality metric tests.'
|
85
|
+
end
|
86
|
+
|
data/VERSION.yml
CHANGED
@@ -59,16 +59,17 @@ module RecordFilter
|
|
59
59
|
#
|
60
60
|
# @public
|
61
61
|
def named_filter(name, &block)
|
62
|
-
|
62
|
+
name = name.to_sym
|
63
|
+
if named_filters.include?(name)
|
63
64
|
raise InvalidFilterNameException.new("A named filter with the name #{name} already exists on the class #{self.name}.")
|
64
65
|
end
|
65
|
-
local_named_filters << name
|
66
|
+
local_named_filters << name
|
66
67
|
DSL::DSLFactory::get_subclass(self).module_eval do
|
67
68
|
define_method(name, &block)
|
68
69
|
end
|
69
70
|
|
70
71
|
(class << self; self; end).instance_eval do
|
71
|
-
define_method(name
|
72
|
+
define_method(name) do |*args|
|
72
73
|
Filter.new(self, name, *args)
|
73
74
|
end
|
74
75
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RecordFilter
|
2
|
+
module ColumnParser # :nodoc: all
|
3
|
+
|
4
|
+
protected
|
5
|
+
|
6
|
+
def parse_column_in_table(column, table)
|
7
|
+
while column.is_a?(Hash)
|
8
|
+
table = table.join_association(column.keys[0]).right_table
|
9
|
+
column = column.values[0]
|
10
|
+
end
|
11
|
+
[column, table]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -9,6 +9,7 @@ module RecordFilter
|
|
9
9
|
when :all_of then AllOf.new(table)
|
10
10
|
when :none_of then NoneOf.new(table)
|
11
11
|
when :not_all_of then NotAllOf.new(table)
|
12
|
+
else raise InvalidFilterException.new("An invalid conjunction type of #{dsl_conjunction.type} was used.")
|
12
13
|
end
|
13
14
|
|
14
15
|
dsl_conjunction.steps.each do |step|
|
@@ -32,6 +33,7 @@ module RecordFilter
|
|
32
33
|
result.add_group_by(step.column)
|
33
34
|
when DSL::NamedFilter
|
34
35
|
result.add_named_filter(step.name, step.args)
|
36
|
+
else raise InvalidFilterException.new('And invalid filter step was provided.')
|
35
37
|
end
|
36
38
|
end
|
37
39
|
result
|
@@ -46,8 +48,7 @@ module RecordFilter
|
|
46
48
|
|
47
49
|
def add_restriction(column_name, operator, value, options={})
|
48
50
|
check_column_exists!(column_name)
|
49
|
-
|
50
|
-
restriction = restriction_class.new("#{@table_name}.#{column_name}", value, options)
|
51
|
+
restriction = RestrictionFactory.build(operator, "#{@table_name}.#{column_name}", value, options)
|
51
52
|
self << restriction
|
52
53
|
restriction
|
53
54
|
end
|
@@ -60,8 +61,7 @@ module RecordFilter
|
|
60
61
|
def add_join_on_association(association_name, join_type)
|
61
62
|
table = @table
|
62
63
|
while association_name.is_a?(Hash)
|
63
|
-
|
64
|
-
table = result.right_table
|
64
|
+
table = table.join_association(association_name.keys[0], join_type).right_table
|
65
65
|
association_name = association_name.values[0]
|
66
66
|
end
|
67
67
|
table.join_association(association_name, join_type)
|
@@ -30,6 +30,24 @@ module RecordFilter
|
|
30
30
|
nil
|
31
31
|
end
|
32
32
|
|
33
|
+
# Define an offset for the results returned from the current
|
34
|
+
# filter. This method can only be called from the outermost scope of a filter
|
35
|
+
# (i.e. not inside of a having block, etc.). If it is called multiple times, the
|
36
|
+
# last one will override any others.
|
37
|
+
#
|
38
|
+
# ==== Parameters
|
39
|
+
# offset<Integer>::
|
40
|
+
# The offset of the query.
|
41
|
+
#
|
42
|
+
# ==== Returns
|
43
|
+
# nil
|
44
|
+
#
|
45
|
+
# @public
|
46
|
+
def offset(offset)
|
47
|
+
@conjunction.add_limit(nil, offset)
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
33
51
|
# Define an order clause for the current query, with options for specifying
|
34
52
|
# both the column to use as well as the direction. This method can only be called
|
35
53
|
# in the outermost scope of a filter (i.e. not inside of a having block, etc.).
|
@@ -51,9 +69,11 @@ module RecordFilter
|
|
51
69
|
# column<Symbol, Hash>::
|
52
70
|
# Specify the column for the ordering. If a symbol is given, it is assumed to represent
|
53
71
|
# a column in the class that is being filtered. With a hash argument, it is possible
|
54
|
-
# to specify a path to a column in one of the joined tables, as seen above.
|
72
|
+
# to specify a path to a column in one of the joined tables, as seen above. If a string
|
73
|
+
# is given and it doesn't match up with a column name, it is used as a literal string
|
74
|
+
# for ordering.
|
55
75
|
# direction<Symbol>::
|
56
|
-
# Specifies the direction of the
|
76
|
+
# Specifies the direction of the order. Should be either :asc or :desc and defaults to :asc.
|
57
77
|
#
|
58
78
|
# ==== Returns
|
59
79
|
# nil
|
@@ -63,7 +83,7 @@ module RecordFilter
|
|
63
83
|
# If the direction is neither :asc nor :desc.
|
64
84
|
#
|
65
85
|
# ==== Alternatives
|
66
|
-
# As described above, it is possible to pass
|
86
|
+
# As described above, it is possible to pass a symbol, a hash or a string as the first
|
67
87
|
# argument.
|
68
88
|
#
|
69
89
|
# @public
|
@@ -190,7 +190,7 @@ module RecordFilter
|
|
190
190
|
#
|
191
191
|
# ==== Parameters
|
192
192
|
# value::
|
193
|
-
# Either a single item
|
193
|
+
# Either a single item, an array of values, or a range to form the values to be tested for inclusion.
|
194
194
|
#
|
195
195
|
# ==== Returns
|
196
196
|
# Restriction:: self
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module RecordFilter
|
2
2
|
class GroupBy # :nodoc: all
|
3
|
+
include ColumnParser
|
4
|
+
|
3
5
|
attr_reader :column, :table
|
4
6
|
|
5
7
|
def initialize(column, table)
|
@@ -7,11 +9,7 @@ module RecordFilter
|
|
7
9
|
end
|
8
10
|
|
9
11
|
def to_sql
|
10
|
-
|
11
|
-
while column.is_a?(Hash)
|
12
|
-
table = table.join_association(column.keys[0]).right_table
|
13
|
-
column = column.values[0]
|
14
|
-
end
|
12
|
+
column, table = parse_column_in_table(@column, @table)
|
15
13
|
|
16
14
|
if (table.has_column(column))
|
17
15
|
"#{table.table_alias}.#{column}"
|
data/lib/record_filter/join.rb
CHANGED
@@ -46,9 +46,11 @@ module RecordFilter
|
|
46
46
|
unless @right_table.has_column(dsl_restriction.column)
|
47
47
|
raise ColumnNotFoundException.new("The column #{dsl_restriction.column} was not found in the table #{@right_table.table_name}")
|
48
48
|
end
|
49
|
-
|
50
|
-
|
51
|
-
"#{@right_table.table_alias}.#{dsl_restriction.column}",
|
49
|
+
restriction = RestrictionFactory.build(
|
50
|
+
dsl_restriction.operator,
|
51
|
+
"#{@right_table.table_alias}.#{dsl_restriction.column}",
|
52
|
+
dsl_restriction.value,
|
53
|
+
:negated => dsl_restriction.negated)
|
52
54
|
@right_table.model_class.merge_conditions(restriction.to_conditions)
|
53
55
|
end
|
54
56
|
|
data/lib/record_filter/order.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module RecordFilter
|
2
2
|
class Order # :nodoc: all
|
3
|
+
include ColumnParser
|
4
|
+
|
3
5
|
attr_reader :column, :direction, :table
|
4
6
|
|
5
7
|
def initialize(column, direction, table)
|
@@ -10,19 +12,16 @@ module RecordFilter
|
|
10
12
|
dir = case @direction
|
11
13
|
when :asc then 'ASC'
|
12
14
|
when :desc then 'DESC'
|
13
|
-
|
14
|
-
|
15
|
-
table, column = @table, @column
|
16
|
-
while column.is_a?(Hash)
|
17
|
-
table = table.join_association(column.keys[0]).right_table
|
18
|
-
column = column.values[0]
|
15
|
+
else raise InvalidFilterException.new("An invalid order of #{@direction} was specified.")
|
19
16
|
end
|
20
17
|
|
21
|
-
|
22
|
-
raise ColumnNotFoundException.new("The column #{column} was not found in #{table.table_name}.")
|
23
|
-
end
|
18
|
+
column, table = parse_column_in_table(@column, @table)
|
24
19
|
|
25
|
-
|
20
|
+
if (table.has_column(column))
|
21
|
+
"#{table.table_alias}.#{column} #{dir}"
|
22
|
+
else
|
23
|
+
"#{column} #{dir}"
|
24
|
+
end
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
data/lib/record_filter/query.rb
CHANGED
@@ -20,15 +20,7 @@ module RecordFilter
|
|
20
20
|
params = {}
|
21
21
|
conditions = @conjunction.to_conditions
|
22
22
|
params = { :conditions => conditions } if conditions
|
23
|
-
|
24
|
-
params[:joins] = joins.map { |join| join.to_sql } unless joins.empty?
|
25
|
-
if (joins.any? { |j| j.requires_distinct_select? })
|
26
|
-
if count_query
|
27
|
-
params[:select] = "DISTINCT #{@table.model_class.quoted_table_name}.#{@table.model_class.primary_key}"
|
28
|
-
else
|
29
|
-
params[:select] = "DISTINCT #{@table.model_class.quoted_table_name}.*"
|
30
|
-
end
|
31
|
-
end
|
23
|
+
add_joins(params, count_query)
|
32
24
|
orders = @table.orders
|
33
25
|
params[:order] = orders.map { |order| order.to_sql } * ', ' unless orders.empty?
|
34
26
|
group_bys = @table.group_bys
|
@@ -41,6 +33,18 @@ module RecordFilter
|
|
41
33
|
|
42
34
|
protected
|
43
35
|
|
36
|
+
def add_joins(params, count_query)
|
37
|
+
joins = @table.all_joins
|
38
|
+
params[:joins] = joins.map { |join| join.to_sql } unless joins.empty?
|
39
|
+
if (joins.any? { |j| j.requires_distinct_select? })
|
40
|
+
if count_query
|
41
|
+
params[:select] = "DISTINCT #{@table.table_name}.#{@table.model_class.primary_key}"
|
42
|
+
else
|
43
|
+
params[:select] = "DISTINCT #{@table.table_name}.*"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
44
48
|
def dsl_for_named_filter(clazz, named_filter)
|
45
49
|
return DSL::DSLFactory.create(clazz) if named_filter.blank?
|
46
50
|
while (clazz)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RecordFilter
|
2
|
+
class RestrictionFactory # :nodoc: all
|
3
|
+
|
4
|
+
OPERATOR_HASH = {
|
5
|
+
:equal_to => Restrictions::EqualTo,
|
6
|
+
:is_null => Restrictions::IsNull,
|
7
|
+
:less_than => Restrictions::LessThan,
|
8
|
+
:less_than_or_equal_to => Restrictions::LessThanOrEqualTo,
|
9
|
+
:greater_than => Restrictions::GreaterThan,
|
10
|
+
:greater_than_or_equal_to => Restrictions::GreaterThanOrEqualTo,
|
11
|
+
:in => Restrictions::In,
|
12
|
+
:between => Restrictions::Between,
|
13
|
+
:like => Restrictions::Like
|
14
|
+
}
|
15
|
+
|
16
|
+
def self.build(operator, column_name, value, options)
|
17
|
+
OPERATOR_HASH[operator].new(column_name, value, options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/record_filter/table.rb
CHANGED
@@ -5,7 +5,7 @@ module RecordFilter
|
|
5
5
|
def initialize(model_class, table_alias = nil)
|
6
6
|
@model_class = model_class
|
7
7
|
@aliased = !table_alias.nil?
|
8
|
-
@table_alias = table_alias ||
|
8
|
+
@table_alias = table_alias || table_name
|
9
9
|
@joins_cache = {}
|
10
10
|
@joins = []
|
11
11
|
@orders = []
|
@@ -13,7 +13,7 @@ module RecordFilter
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def table_name
|
16
|
-
@model_class.quoted_table_name
|
16
|
+
@table_name ||= @model_class.quoted_table_name
|
17
17
|
end
|
18
18
|
|
19
19
|
def join_association(association_name, join_type=nil, options={})
|
@@ -41,6 +41,7 @@ module RecordFilter
|
|
41
41
|
simple_join(association, join_type, options)
|
42
42
|
when :has_and_belongs_to_many
|
43
43
|
compound_join(association, join_type)
|
44
|
+
else raise InvalidJoinException.new("I don't know how to join on an association of type #{association.macro}.")
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -77,13 +78,7 @@ module RecordFilter
|
|
77
78
|
private
|
78
79
|
|
79
80
|
def simple_join(association, join_type, options)
|
80
|
-
join_predicate =
|
81
|
-
case association.macro
|
82
|
-
when :belongs_to
|
83
|
-
[{ association.options[:foreign_key] || association.primary_key_name.to_sym => @model_class.primary_key }]
|
84
|
-
when :has_many, :has_one
|
85
|
-
[{ association.options[:primary_key] || @model_class.primary_key => association.primary_key_name.to_sym }]
|
86
|
-
end
|
81
|
+
join_predicate = simple_join_predicate(association)
|
87
82
|
|
88
83
|
if association.options[:as]
|
89
84
|
join_predicate << DSL::Restriction.new(association.options[:as].to_s + '_type').equal_to(association.active_record.base_class.name)
|
@@ -101,6 +96,17 @@ module RecordFilter
|
|
101
96
|
join
|
102
97
|
end
|
103
98
|
|
99
|
+
def simple_join_predicate(association)
|
100
|
+
join_predicate =
|
101
|
+
case association.macro
|
102
|
+
when :belongs_to
|
103
|
+
[{ association.options[:foreign_key] || association.primary_key_name.to_sym => @model_class.primary_key }]
|
104
|
+
when :has_many, :has_one
|
105
|
+
[{ association.options[:primary_key] || @model_class.primary_key => association.primary_key_name.to_sym }]
|
106
|
+
else raise InvalidJoinException.new("I don't know how to do a simple join on an association of type #{association.macro}.")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
104
110
|
def compound_join(association, join_type)
|
105
111
|
pivot_join_predicate = [{ @model_class.primary_key => association.primary_key_name.to_sym }]
|
106
112
|
table_name = @model_class.connection.quote_table_name(association.options[:join_table])
|
@@ -116,7 +122,9 @@ module RecordFilter
|
|
116
122
|
protected
|
117
123
|
|
118
124
|
def alias_for_association(association)
|
119
|
-
|
125
|
+
@alias_cache ||= {}
|
126
|
+
@alias_cache[association.name] ||=
|
127
|
+
"#{@aliased ? @table_alias.to_s : @model_class.table_name}__#{association.name.to_s.downcase}"
|
120
128
|
end
|
121
129
|
|
122
130
|
alias_method :alias_for_class, :alias_for_association
|
data/lib/record_filter.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
gem 'activerecord', '~> 2.
|
2
|
+
gem 'activerecord', '~> 2.3'
|
3
3
|
require 'active_record'
|
4
4
|
|
5
|
-
%w(active_record query table conjunctions restrictions filter join order group_by dsl).each do |file|
|
5
|
+
%w(active_record column_parser query table conjunctions restrictions restriction_factory filter join order group_by dsl).each do |file|
|
6
6
|
require File.join(File.dirname(__FILE__), 'record_filter', file)
|
7
7
|
end
|
8
8
|
|
data/spec/exception_spec.rb
CHANGED
@@ -34,12 +34,12 @@ describe 'raising exceptions' do
|
|
34
34
|
}.should raise_error(RecordFilter::ColumnNotFoundException)
|
35
35
|
end
|
36
36
|
|
37
|
-
it 'should get ColumnNotFoundException for order' do
|
37
|
+
it 'should not get ColumnNotFoundException for order' do
|
38
38
|
lambda {
|
39
39
|
Post.filter do
|
40
|
-
order(
|
40
|
+
order('this_is_not_there', :asc)
|
41
41
|
end.inspect
|
42
|
-
}.
|
42
|
+
}.should_not raise_error(RecordFilter::ColumnNotFoundException)
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'should not get ColumnNotFoundException for group_by' do
|
@@ -63,6 +63,50 @@ describe 'filter qualifiers' do
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
describe 'offsets' do
|
67
|
+
describe 'simple offset setting' do
|
68
|
+
before do
|
69
|
+
Post.filter do
|
70
|
+
with :published, true
|
71
|
+
offset 10
|
72
|
+
end.inspect
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should add the offset to the parameters' do
|
76
|
+
Post.last_find[:offset].should == 10
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'with multiple calls to offset' do
|
81
|
+
before do
|
82
|
+
Post.filter do
|
83
|
+
offset 5
|
84
|
+
with :published, true
|
85
|
+
offset 6
|
86
|
+
end.inspect
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should add the offset to the parameters' do
|
90
|
+
Post.last_find[:offset].should == 6
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'offsetting named scopes' do
|
95
|
+
before do
|
96
|
+
@post = Class.new(Post)
|
97
|
+
@post.named_filter(:published_ones) do
|
98
|
+
offset(2)
|
99
|
+
with(:published, false)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should offset the query' do
|
104
|
+
@post.published_ones.inspect
|
105
|
+
@post.last_find[:offset].should == 2
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
66
110
|
describe 'ordering' do
|
67
111
|
describe 'with a simple order supplied' do
|
68
112
|
before do
|
@@ -115,10 +159,24 @@ describe 'filter qualifiers' do
|
|
115
159
|
end.inspect
|
116
160
|
end
|
117
161
|
|
118
|
-
it 'should add the
|
162
|
+
it 'should add the order to the parameters' do
|
119
163
|
Post.last_find[:order].should == %q(posts__photo.path DESC, "posts".permalink ASC)
|
120
164
|
end
|
121
165
|
end
|
166
|
+
|
167
|
+
describe 'with the order supplied as a string' do
|
168
|
+
before do
|
169
|
+
Post.filter do
|
170
|
+
with :published, true
|
171
|
+
group_by(:published)
|
172
|
+
order('SUM(id)', :desc)
|
173
|
+
end.inspect
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should add the order to the query' do
|
177
|
+
Post.last_find[:order].should == %q(SUM(id) DESC)
|
178
|
+
end
|
179
|
+
end
|
122
180
|
end
|
123
181
|
|
124
182
|
describe 'group_by' do
|
data/spec/restrictions_spec.rb
CHANGED
@@ -89,6 +89,13 @@ describe 'RecordFilter restrictions' do
|
|
89
89
|
Post.last_find.should == { :conditions => [%q("posts".blog_id IN (?)), nil] }
|
90
90
|
end
|
91
91
|
|
92
|
+
it 'should do the right thing for IN filters with a range' do
|
93
|
+
Post.filter do
|
94
|
+
with(:blog_id).in(1..5)
|
95
|
+
end.inspect
|
96
|
+
Post.last_find.should == { :conditions => [%q("posts".blog_id IN (?)), 1..5] }
|
97
|
+
end
|
98
|
+
|
92
99
|
it 'should negate is_not_null conditions correctly' do
|
93
100
|
Post.filter do
|
94
101
|
with(:blog_id).is_not_null.not
|
data/spec/test.db
CHANGED
Binary file
|
data/test/performance_test.rb
CHANGED
@@ -19,6 +19,7 @@ ActiveRecord::Base.establish_connection(
|
|
19
19
|
@blog.named_filter :somethings do
|
20
20
|
having(:ads) do
|
21
21
|
with(:content, nil)
|
22
|
+
with(:id).greater_than(25)
|
22
23
|
end
|
23
24
|
join(Post, :left) do
|
24
25
|
on(:id => :blog_id)
|
@@ -28,6 +29,8 @@ ActiveRecord::Base.establish_connection(
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
group_by(:id)
|
32
|
+
limit(10, 100)
|
33
|
+
order(:ads => :id)
|
31
34
|
end
|
32
35
|
|
33
36
|
10000.times do
|
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.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aubrey Holland
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-05-
|
13
|
+
date: 2009-05-09 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- VERSION.yml
|
29
29
|
- lib/record_filter.rb
|
30
30
|
- lib/record_filter/active_record.rb
|
31
|
+
- lib/record_filter/column_parser.rb
|
31
32
|
- lib/record_filter/conjunctions.rb
|
32
33
|
- lib/record_filter/dsl.rb
|
33
34
|
- lib/record_filter/dsl/class_join.rb
|
@@ -48,6 +49,7 @@ files:
|
|
48
49
|
- lib/record_filter/join.rb
|
49
50
|
- lib/record_filter/order.rb
|
50
51
|
- lib/record_filter/query.rb
|
52
|
+
- lib/record_filter/restriction_factory.rb
|
51
53
|
- lib/record_filter/restrictions.rb
|
52
54
|
- lib/record_filter/table.rb
|
53
55
|
- spec/active_record_spec.rb
|
@@ -64,7 +66,7 @@ files:
|
|
64
66
|
- spec/test.db
|
65
67
|
- test/performance_test.rb
|
66
68
|
- test/test.db
|
67
|
-
has_rdoc:
|
69
|
+
has_rdoc: false
|
68
70
|
homepage: http://github.com/aub/record_filter/tree/master
|
69
71
|
post_install_message:
|
70
72
|
rdoc_options:
|