record_filter 0.9.12

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.
Files changed (53) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +232 -0
  3. data/README.rdoc +354 -0
  4. data/Rakefile +92 -0
  5. data/TODO +3 -0
  6. data/VERSION.yml +4 -0
  7. data/config/roodi.yml +14 -0
  8. data/lib/record_filter/active_record.rb +108 -0
  9. data/lib/record_filter/column_parser.rb +14 -0
  10. data/lib/record_filter/conjunctions.rb +169 -0
  11. data/lib/record_filter/dsl/class_join.rb +16 -0
  12. data/lib/record_filter/dsl/conjunction.rb +57 -0
  13. data/lib/record_filter/dsl/conjunction_dsl.rb +317 -0
  14. data/lib/record_filter/dsl/dsl.rb +143 -0
  15. data/lib/record_filter/dsl/dsl_factory.rb +19 -0
  16. data/lib/record_filter/dsl/group_by.rb +11 -0
  17. data/lib/record_filter/dsl/join.rb +12 -0
  18. data/lib/record_filter/dsl/join_condition.rb +21 -0
  19. data/lib/record_filter/dsl/join_dsl.rb +49 -0
  20. data/lib/record_filter/dsl/limit.rb +12 -0
  21. data/lib/record_filter/dsl/named_filter.rb +12 -0
  22. data/lib/record_filter/dsl/order.rb +12 -0
  23. data/lib/record_filter/dsl/restriction.rb +314 -0
  24. data/lib/record_filter/dsl.rb +21 -0
  25. data/lib/record_filter/filter.rb +105 -0
  26. data/lib/record_filter/group_by.rb +21 -0
  27. data/lib/record_filter/join.rb +66 -0
  28. data/lib/record_filter/order.rb +27 -0
  29. data/lib/record_filter/query.rb +60 -0
  30. data/lib/record_filter/restriction_factory.rb +21 -0
  31. data/lib/record_filter/restrictions.rb +97 -0
  32. data/lib/record_filter/table.rb +172 -0
  33. data/lib/record_filter.rb +35 -0
  34. data/record_filter.gemspec +108 -0
  35. data/script/console +8 -0
  36. data/spec/active_record_spec.rb +211 -0
  37. data/spec/exception_spec.rb +208 -0
  38. data/spec/explicit_join_spec.rb +132 -0
  39. data/spec/implicit_join_spec.rb +403 -0
  40. data/spec/limits_and_ordering_spec.rb +230 -0
  41. data/spec/models.rb +109 -0
  42. data/spec/named_filter_spec.rb +264 -0
  43. data/spec/proxying_spec.rb +63 -0
  44. data/spec/restrictions_spec.rb +251 -0
  45. data/spec/select_spec.rb +79 -0
  46. data/spec/spec_helper.rb +39 -0
  47. data/spec/test.db +0 -0
  48. data/tasks/db.rake +106 -0
  49. data/tasks/rcov.rake +9 -0
  50. data/tasks/spec.rake +10 -0
  51. data/test/performance_test.rb +39 -0
  52. data/test/test.db +0 -0
  53. metadata +137 -0
@@ -0,0 +1,208 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'raising exceptions' do
4
+ before do
5
+ TestModel.extended_models.each { |model| model.last_find = {} }
6
+ end
7
+
8
+ describe 'on missing associations' do
9
+ it 'should get AssociationNotFoundException' do
10
+ lambda {
11
+ Post.filter do
12
+ having(:something_that_does_not_exist) do
13
+ with(:something_bad)
14
+ end
15
+ end.inspect
16
+ }.should raise_error(RecordFilter::AssociationNotFoundException)
17
+ end
18
+ end
19
+
20
+ describe 'on missing columns' do
21
+ it 'should get ColumnNotFoundException for with' do
22
+ lambda {
23
+ Post.filter do
24
+ with(:this_is_not_there, 2)
25
+ end.inspect
26
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
27
+ end
28
+
29
+ it 'should get ColumnNotFoundException for with.not' do
30
+ lambda {
31
+ Post.filter do
32
+ with(:this_is_not_there).not.equal_to(2)
33
+ end.inspect
34
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
35
+ end
36
+
37
+ it 'should not get ColumnNotFoundException for order' do
38
+ lambda {
39
+ Post.filter do
40
+ order('this_is_not_there', :asc)
41
+ end.inspect
42
+ }.should_not raise_error(RecordFilter::ColumnNotFoundException)
43
+ end
44
+
45
+ it 'should not get ColumnNotFoundException for group_by' do
46
+ lambda {
47
+ Post.filter do
48
+ group_by(:this_is_not_there)
49
+ end.inspect
50
+ }.should_not raise_error(RecordFilter::ColumnNotFoundException)
51
+ end
52
+
53
+ it 'should get AssociationNotFoundException for orders on bad associations' do
54
+ lambda {
55
+ Post.filter do
56
+ order({ :this_is_not_there => :eh }, :asc)
57
+ end.inspect
58
+ }.should raise_error(RecordFilter::InvalidJoinException)
59
+ end
60
+
61
+ it 'should raise ColumnNotFoundException for explicit joins on bad column names for the right table' do
62
+ lambda {
63
+ Review.filter do
64
+ join(Feature, :join_type => :left) do
65
+ on(:reviewable_id => :ftrable_id)
66
+ on(:reviewable_type => :ftrable_type)
67
+ with(:priority, 5)
68
+ end
69
+ end.inspect
70
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
71
+ end
72
+
73
+ it 'should raise ColumnNotFoundException for explicit joins on bad column names for the left table' do
74
+ lambda {
75
+ Review.filter do
76
+ join(Feature, :join_type => :inner) do
77
+ on(:rvwable_id => :featurable_id)
78
+ on(:rvwable_type => :featurable_type)
79
+ with(:priority, 5)
80
+ end
81
+ end.inspect
82
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
83
+ end
84
+
85
+ it 'should raise ColumnNotFoundException for explicit joins on bad column names in conditions' do
86
+ lambda {
87
+ Review.filter do
88
+ join(Feature, :join_type => :inner) do
89
+ on(:reviewable_id).gt(12)
90
+ end
91
+ end.inspect
92
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
93
+ end
94
+
95
+ it 'should raise an ArgumentError if an invalid join type is specified' do
96
+ lambda {
97
+ Review.filter do
98
+ join(Feature, :join_type => :crazy) do
99
+ on(:reviewable_type => :featurable_type)
100
+ end
101
+ end.inspect
102
+ }.should raise_error(ArgumentError)
103
+ end
104
+
105
+ it 'should raise an InvalidJoinException if no columns are specified for the join' do
106
+ lambda {
107
+ Review.filter do
108
+ join(Feature, :join_type => :inner)
109
+ end.inspect
110
+ }.should raise_error(RecordFilter::InvalidJoinException)
111
+ end
112
+ end
113
+
114
+ describe 'limiting methods within joins and conjunctions' do
115
+ it 'should not allow calls to limit within joins' do
116
+ lambda {
117
+ Post.filter do
118
+ having(:photo) do
119
+ limit 2
120
+ end
121
+ end
122
+ }.should raise_error(RecordFilter::InvalidFilterException)
123
+ end
124
+
125
+ it 'should not allow calls to group_by within joins' do
126
+ lambda {
127
+ Post.filter do
128
+ having(:photo) do
129
+ group_by(:id)
130
+ end
131
+ end
132
+ }.should raise_error(RecordFilter::InvalidFilterException)
133
+ end
134
+
135
+ it 'should not allow calls to order within joins' do
136
+ lambda {
137
+ Post.filter do
138
+ having(:photo) do
139
+ order :id
140
+ end
141
+ end
142
+ }.should raise_error(RecordFilter::InvalidFilterException)
143
+ end
144
+
145
+ it 'should not allow calls to limit within conjunctions' do
146
+ lambda {
147
+ Post.filter do
148
+ all_of do
149
+ limit 2
150
+ end
151
+ end
152
+ }.should raise_error(RecordFilter::InvalidFilterException)
153
+ end
154
+
155
+ it 'should not allow calls to order within joins' do
156
+ lambda {
157
+ Post.filter do
158
+ all_of do
159
+ order :id
160
+ end
161
+ end
162
+ }.should raise_error(RecordFilter::InvalidFilterException)
163
+ end
164
+ end
165
+
166
+ describe 'limiting calls to on' do
167
+ it 'should not allow calls to on in the outer scope' do
168
+ lambda {
169
+ Post.filter do
170
+ on(:a => :b)
171
+ end
172
+ }.should raise_error(RecordFilter::InvalidFilterException)
173
+ end
174
+ end
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
+
186
+ describe 'calling named filters within filters' do
187
+ it 'should raise an excpetion if the named filter does not exist' do
188
+ lambda {
189
+ Post.filter do
190
+ having(:comments).does_not_exist
191
+ end
192
+ }.should raise_error(RecordFilter::NamedFilterNotFoundException)
193
+ end
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
208
+ end
@@ -0,0 +1,132 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'explicit joins' do
4
+ before do
5
+ TestModel.extended_models.each { |model| model.last_find = {} }
6
+ end
7
+
8
+ describe 'specifying a simple join' do
9
+ before do
10
+ Post.filter do
11
+ join(Blog, :join_type => :left, :alias => :posts_blogs) do
12
+ on(:blog_id => :id)
13
+ with(:name, 'Test Name')
14
+ end
15
+ end.inspect
16
+ end
17
+
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)]
20
+ end
21
+
22
+ it 'should query against condition on join table' do
23
+ Post.last_find[:conditions].should == ['posts_blogs.name = ?', 'Test Name']
24
+ end
25
+ end
26
+
27
+ describe 'specifying a complex join through polymorphic associations' do
28
+ before do
29
+ Review.filter do
30
+ join(Feature, :join_type => :left, :alias => :reviews_features) do
31
+ on(:reviewable_id => :featurable_id)
32
+ on(:reviewable_type => :featurable_type)
33
+ with(:priority, 5)
34
+ end
35
+ end.inspect
36
+ end
37
+
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)]
40
+ end
41
+
42
+ it 'should query against condition on join table' do
43
+ Review.last_find[:conditions].should == ['reviews_features.priority = ?', 5]
44
+ end
45
+ end
46
+
47
+ describe 'should use values as join parameters instead of columns if given' do
48
+ before do
49
+ Review.filter do
50
+ join(Feature, :join_type => :left) do
51
+ on(:reviewable_id => :featurable_id)
52
+ on(:reviewable_type => :featurable_type)
53
+ on(:featurable_type, 'SomeType')
54
+ with(:priority, 5)
55
+ end
56
+ end.inspect
57
+ end
58
+
59
+ it 'should add correct join' do
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
+ end
62
+ end
63
+
64
+ describe 'using restrictions on join conditions' do
65
+ before do
66
+ Review.filter do
67
+ join(Feature, :join_type => :left) do
68
+ on(:featurable_type, nil)
69
+ on(:featurable_id).gte(12)
70
+ on(:priority).not(6)
71
+ end
72
+ end.inspect
73
+ end
74
+
75
+ it 'should add the correct join' do
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
+ end
78
+ end
79
+
80
+ describe 'using implicit and explicit joins together with conditions' do
81
+ before do
82
+ @blog = Class.new(Blog)
83
+ @blog.named_filter :somethings do
84
+ having(:ads) do
85
+ with(:content, nil)
86
+ end
87
+ join(Post, :join_type => :left) do
88
+ on(:id => :blog_id)
89
+ join(Comment, :join_type => :inner) do
90
+ on(:id => :post_id)
91
+ on(:offensive, true)
92
+ end
93
+ end
94
+ group_by(:id)
95
+ end
96
+ @blog.somethings.inspect
97
+ end
98
+
99
+ it 'should produce the correct conditions' do
100
+ @blog.last_find[:conditions].should == [%q((blogs__ads.content IS NULL))]
101
+ end
102
+
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), %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
+ end
106
+ end
107
+
108
+ describe 'using the same join multiple times' do
109
+ before do
110
+ @blog = Class.new(Blog)
111
+ @blog.named_filter(:things) do
112
+ join(Post, :join_type => :inner, :alias => 'blogs_posts_1') do
113
+ on(:id => :blog_id)
114
+ with(:title, 'ack')
115
+ end
116
+ join(Post, :join_type => :inner, :alias => 'blogs_posts_2') do
117
+ on(:id => :blog_id)
118
+ with(:title, 'hmm')
119
+ end
120
+ end
121
+ @blog.things.inspect
122
+ end
123
+
124
+ it 'should create the correct join' do
125
+ @blog.last_find[:joins].should == [%q(INNER JOIN "posts" AS blogs_posts_1 ON "blogs".id = blogs_posts_1.blog_id), %q(INNER JOIN "posts" AS blogs_posts_2 ON "blogs".id = blogs_posts_2.blog_id)]
126
+ end
127
+
128
+ it 'should create the correct conditions' do
129
+ @blog.last_find[:conditions].should == [%q((blogs_posts_1.title = ?) AND (blogs_posts_2.title = ?)), 'ack', 'hmm']
130
+ end
131
+ end
132
+ end