outoftime-record_filter 0.6.0 → 0.8.0

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