gravis-searchlogic 2.4.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/.gitignore +6 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +308 -0
  4. data/Rakefile +35 -0
  5. data/VERSION.yml +5 -0
  6. data/init.rb +1 -0
  7. data/lib/searchlogic.rb +47 -0
  8. data/lib/searchlogic/active_record/association_proxy.rb +19 -0
  9. data/lib/searchlogic/active_record/consistency.rb +49 -0
  10. data/lib/searchlogic/active_record/named_scope_tools.rb +101 -0
  11. data/lib/searchlogic/core_ext/object.rb +43 -0
  12. data/lib/searchlogic/core_ext/proc.rb +17 -0
  13. data/lib/searchlogic/named_scopes/alias_scope.rb +67 -0
  14. data/lib/searchlogic/named_scopes/association_conditions.rb +131 -0
  15. data/lib/searchlogic/named_scopes/association_ordering.rb +44 -0
  16. data/lib/searchlogic/named_scopes/conditions.rb +226 -0
  17. data/lib/searchlogic/named_scopes/or_conditions.rb +141 -0
  18. data/lib/searchlogic/named_scopes/ordering.rb +48 -0
  19. data/lib/searchlogic/rails_helpers.rb +79 -0
  20. data/lib/searchlogic/search.rb +251 -0
  21. data/rails/init.rb +1 -0
  22. data/searchlogic.gemspec +89 -0
  23. data/spec/searchlogic/active_record/association_proxy_spec.rb +23 -0
  24. data/spec/searchlogic/active_record/consistency_spec.rb +28 -0
  25. data/spec/searchlogic/core_ext/object_spec.rb +9 -0
  26. data/spec/searchlogic/core_ext/proc_spec.rb +8 -0
  27. data/spec/searchlogic/named_scopes/alias_scope_spec.rb +23 -0
  28. data/spec/searchlogic/named_scopes/association_conditions_spec.rb +198 -0
  29. data/spec/searchlogic/named_scopes/association_ordering_spec.rb +27 -0
  30. data/spec/searchlogic/named_scopes/conditions_spec.rb +319 -0
  31. data/spec/searchlogic/named_scopes/or_conditions_spec.rb +66 -0
  32. data/spec/searchlogic/named_scopes/ordering_spec.rb +34 -0
  33. data/spec/searchlogic/search_spec.rb +459 -0
  34. data/spec/spec_helper.rb +132 -0
  35. metadata +127 -0
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::NamedScopes::Ordering do
4
+ it "should allow ascending" do
5
+ Company.ascend_by_users_username.proxy_options.should == User.ascend_by_username.proxy_options.merge(:joins => :users)
6
+ end
7
+
8
+ it "should allow descending" do
9
+ Company.descend_by_users_username.proxy_options.should == User.descend_by_username.proxy_options.merge(:joins => :users)
10
+ end
11
+
12
+ it "should allow deep ascending" do
13
+ Company.ascend_by_users_orders_total.proxy_options.should == Order.ascend_by_total.proxy_options.merge(:joins => {:users => :orders})
14
+ end
15
+
16
+ it "should allow deep descending" do
17
+ Company.descend_by_users_orders_total.proxy_options.should == Order.descend_by_total.proxy_options.merge(:joins => {:users => :orders})
18
+ end
19
+
20
+ it "should ascend with a belongs to" do
21
+ User.ascend_by_company_name.proxy_options.should == Company.ascend_by_name.proxy_options.merge(:joins => :company)
22
+ end
23
+
24
+ it "should work through #order" do
25
+ Company.order('ascend_by_users_username').proxy_options.should == Company.ascend_by_users_username.proxy_options
26
+ end
27
+ end
@@ -0,0 +1,319 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::NamedScopes::Conditions do
4
+ it "should be dynamically created and then cached" do
5
+ User.should_not respond_to(:age_less_than)
6
+ User.age_less_than(5)
7
+ User.should respond_to(:age_less_than)
8
+ end
9
+
10
+ it "should not allow conditions on non columns" do
11
+ lambda { User.whatever_equals(2) }.should raise_error(NoMethodError)
12
+ end
13
+
14
+ context "comparison conditions" do
15
+ it "should have equals" do
16
+ (5..7).each { |age| User.create(:age => age) }
17
+ User.age_equals(6).all.should == User.find_all_by_age(6)
18
+ User.age_equals(5..6).all.should == User.find_all_by_age(5..6)
19
+ User.age_equals([5, 7]).all.should == User.find_all_by_age([5, 7])
20
+ end
21
+
22
+ it "should have does not equal" do
23
+ (5..7).each { |age| User.create(:age => age) }
24
+ User.age_does_not_equal(6).all.should == User.find_all_by_age([5,7])
25
+ end
26
+
27
+ it "should have less than" do
28
+ (5..7).each { |age| User.create(:age => age) }
29
+ User.age_less_than(6).all.should == User.find_all_by_age(5)
30
+ end
31
+
32
+ it "should have less than or equal to" do
33
+ (5..7).each { |age| User.create(:age => age) }
34
+ User.age_less_than_or_equal_to(6).all.should == User.find_all_by_age([5, 6])
35
+ end
36
+
37
+ it "should have greater than" do
38
+ (5..7).each { |age| User.create(:age => age) }
39
+ User.age_greater_than(6).all.should == User.find_all_by_age(7)
40
+ end
41
+
42
+ it "should have greater than or equal to" do
43
+ (5..7).each { |age| User.create(:age => age) }
44
+ User.age_greater_than_or_equal_to(6).all.should == User.find_all_by_age([6, 7])
45
+ end
46
+ end
47
+
48
+ context "wildcard conditions" do
49
+ it "should have like" do
50
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
51
+ User.username_like("john").all.should == User.find_all_by_username("bjohnson")
52
+ end
53
+
54
+ it "should have not like" do
55
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
56
+ User.username_not_like("john").all.should == User.find_all_by_username("thunt")
57
+ end
58
+
59
+ it "should have begins with" do
60
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
61
+ User.username_begins_with("bj").all.should == User.find_all_by_username("bjohnson")
62
+ end
63
+
64
+ it "should have not begin with" do
65
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
66
+ User.username_not_begin_with("bj").all.should == User.find_all_by_username("thunt")
67
+ end
68
+
69
+ it "should have ends with" do
70
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
71
+ User.username_ends_with("son").all.should == User.find_all_by_username("bjohnson")
72
+ end
73
+
74
+ it "should have not end with" do
75
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
76
+ User.username_not_end_with("son").all.should == User.find_all_by_username("thunt")
77
+ end
78
+ end
79
+
80
+ context "boolean conditions" do
81
+ it "should have scopes for boolean columns" do
82
+ female = User.create(:male => false)
83
+ male = User.create(:male => true)
84
+ User.male.all.should == [male]
85
+ User.not_male.all.should == [female]
86
+ end
87
+
88
+ it "should have null" do
89
+ ["bjohnson", nil].each { |username| User.create(:username => username) }
90
+ User.username_null.all.should == User.find_all_by_username(nil)
91
+ end
92
+
93
+ it "should have not null" do
94
+ ["bjohnson", nil].each { |username| User.create(:username => username) }
95
+ User.username_not_null.all.should == User.find_all_by_username("bjohnson")
96
+ end
97
+
98
+ it "should have empty" do
99
+ ["bjohnson", ""].each { |username| User.create(:username => username) }
100
+ User.username_empty.all.should == User.find_all_by_username("")
101
+ end
102
+
103
+ it "should have blank" do
104
+ ["bjohnson", "", nil].each { |username| User.create(:username => username) }
105
+ User.username_blank.all.should == [User.find_by_username(""), User.find_by_username(nil)]
106
+ end
107
+
108
+ it "should have not blank" do
109
+ ["bjohnson", "", nil].each { |username| User.create(:username => username) }
110
+ User.username_not_blank.all.should == User.find_all_by_username("bjohnson")
111
+ end
112
+ end
113
+
114
+ context "any and all conditions" do
115
+ it "should do nothing if no arguments are passed" do
116
+ User.username_equals_any.proxy_options.should == {}
117
+ end
118
+
119
+ it "should treat an array and multiple arguments the same" do
120
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
121
+ User.username_like_any("bjohnson", "thunt").should == User.username_like_any(["bjohnson", "thunt"])
122
+ end
123
+
124
+ it "should have equals any" do
125
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
126
+ User.username_equals_any("bjohnson", "thunt").all.should == User.find_all_by_username(["bjohnson", "thunt"])
127
+ end
128
+
129
+ it "should have equals all" do
130
+ %w(bjohnson thunt dainor).each { |username| User.create(:username => username) }
131
+ User.username_equals_all("bjohnson", "thunt").all.should == []
132
+ end
133
+
134
+ it "should have does not equal any" do
135
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
136
+ User.username_does_not_equal_any("bjohnson", "thunt").all.should == User.find_all_by_username(["bjohnson", "thunt", "dgainor"])
137
+ end
138
+
139
+ it "should have does not equal all" do
140
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
141
+ User.username_does_not_equal_all("bjohnson", "thunt").all.should == User.find_all_by_username("dgainor")
142
+ end
143
+
144
+ it "should have less than any" do
145
+ (5..7).each { |age| User.create(:age => age) }
146
+ User.age_less_than_any(7,6).all.should == User.find_all_by_age([5, 6])
147
+ end
148
+
149
+ it "should have less than all" do
150
+ (5..7).each { |age| User.create(:age => age) }
151
+ User.age_less_than_all(7,6).all.should == User.find_all_by_age(5)
152
+ end
153
+
154
+ it "should have less than or equal to any" do
155
+ (5..7).each { |age| User.create(:age => age) }
156
+ User.age_less_than_or_equal_to_any(7,6).all.should == User.find_all_by_age([5, 6, 7])
157
+ end
158
+
159
+ it "should have less than or equal to all" do
160
+ (5..7).each { |age| User.create(:age => age) }
161
+ User.age_less_than_or_equal_to_all(7,6).all.should == User.find_all_by_age([5, 6])
162
+ end
163
+
164
+ it "should have less than any" do
165
+ (5..7).each { |age| User.create(:age => age) }
166
+ User.age_greater_than_any(5,6).all.should == User.find_all_by_age([6, 7])
167
+ end
168
+
169
+ it "should have greater than all" do
170
+ (5..7).each { |age| User.create(:age => age) }
171
+ User.age_greater_than_all(5,6).all.should == User.find_all_by_age(7)
172
+ end
173
+
174
+ it "should have greater than or equal to any" do
175
+ (5..7).each { |age| User.create(:age => age) }
176
+ User.age_greater_than_or_equal_to_any(5,6).all.should == User.find_all_by_age([5, 6, 7])
177
+ end
178
+
179
+ it "should have greater than or equal to all" do
180
+ (5..7).each { |age| User.create(:age => age) }
181
+ User.age_greater_than_or_equal_to_all(5,6).all.should == User.find_all_by_age([6, 7])
182
+ end
183
+
184
+ it "should have like all" do
185
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
186
+ User.username_like_all("bjohnson", "thunt").all.should == []
187
+ User.username_like_all("n", "o").all.should == User.find_all_by_username(["bjohnson", "dgainor"])
188
+ end
189
+
190
+ it "should have like any" do
191
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
192
+ User.username_like_any("bjohnson", "thunt").all.should == User.find_all_by_username(["bjohnson", "thunt"])
193
+ end
194
+
195
+ it "should have begins with all" do
196
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
197
+ User.username_begins_with_all("bjohnson", "thunt").all.should == []
198
+ end
199
+
200
+ it "should have begins with any" do
201
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
202
+ User.username_begins_with_any("bj", "th").all.should == User.find_all_by_username(["bjohnson", "thunt"])
203
+ end
204
+
205
+ it "should have ends with all" do
206
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
207
+ User.username_ends_with_all("n", "r").all.should == []
208
+ end
209
+
210
+ it "should have ends with any" do
211
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
212
+ User.username_ends_with_any("n", "r").all.should == User.find_all_by_username(["bjohnson", "dgainor"])
213
+ end
214
+ end
215
+
216
+ context "alias conditions" do
217
+ it "should have is" do
218
+ User.age_is(5).proxy_options.should == User.age_equals(5).proxy_options
219
+ end
220
+
221
+ it "should have eq" do
222
+ User.age_eq(5).proxy_options.should == User.age_equals(5).proxy_options
223
+ end
224
+
225
+ it "should have not_equal_to" do
226
+ User.age_not_equal_to(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
227
+ end
228
+
229
+ it "should have is_not" do
230
+ User.age_is_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
231
+ end
232
+
233
+ it "should have not" do
234
+ User.age_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
235
+ end
236
+
237
+ it "should have ne" do
238
+ User.age_ne(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
239
+ end
240
+
241
+ it "should have lt" do
242
+ User.age_lt(5).proxy_options.should == User.age_less_than(5).proxy_options
243
+ end
244
+
245
+ it "should have lte" do
246
+ User.age_lte(5).proxy_options.should == User.age_less_than_or_equal_to(5).proxy_options
247
+ end
248
+
249
+ it "should have gt" do
250
+ User.age_gt(5).proxy_options.should == User.age_greater_than(5).proxy_options
251
+ end
252
+
253
+ it "should have gte" do
254
+ User.age_gte(5).proxy_options.should == User.age_greater_than_or_equal_to(5).proxy_options
255
+ end
256
+
257
+ it "should have contains" do
258
+ User.username_contains(5).proxy_options.should == User.username_like(5).proxy_options
259
+ end
260
+
261
+ it "should have contains" do
262
+ User.username_includes(5).proxy_options.should == User.username_like(5).proxy_options
263
+ end
264
+
265
+ it "should have bw" do
266
+ User.username_bw(5).proxy_options.should == User.username_begins_with(5).proxy_options
267
+ end
268
+
269
+ it "should have ew" do
270
+ User.username_ew(5).proxy_options.should == User.username_ends_with(5).proxy_options
271
+ end
272
+
273
+ it "should have nil" do
274
+ User.username_nil.proxy_options.should == User.username_nil.proxy_options
275
+ end
276
+ end
277
+
278
+ context "group conditions" do
279
+ it "should have in" do
280
+ (5..7).each { |age| User.create(:age => age) }
281
+ User.age_in([5,6]).all.should == User.find(:all, :conditions => ["users.age IN (?)", [5, 6]])
282
+ end
283
+
284
+ it "should have not_in" do
285
+ (5..7).each { |age| User.create(:age => age) }
286
+ User.age_not_in([5,6]).all.should == User.find(:all, :conditions => ["users.age NOT IN (?)", [5, 6]])
287
+ end
288
+ end
289
+
290
+ context "searchlogic lambda" do
291
+ it "should be a string" do
292
+ User.username_like("test")
293
+ User.named_scope_options(:username_like).searchlogic_options[:type].should == :string
294
+ end
295
+
296
+ it "should be an integer" do
297
+ User.id_gt(10)
298
+ User.named_scope_options(:id_gt).searchlogic_options[:type].should == :integer
299
+ end
300
+
301
+ it "should be a float" do
302
+ Order.total_gt(10)
303
+ Order.named_scope_options(:total_gt).searchlogic_options[:type].should == :float
304
+ end
305
+ end
306
+
307
+ it "should have priorty to columns over conflicting association conditions" do
308
+ Company.users_count_gt(10)
309
+ User.create
310
+ User.company_id_null.count.should == 1
311
+ User.company_id_not_null.count.should == 0
312
+ end
313
+
314
+ it "should fix bug for issue 26" do
315
+ count1 = User.id_ne(10).username_not_like("root").count
316
+ count2 = User.id_ne(10).username_not_like("root").count
317
+ count1.should == count2
318
+ end
319
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::NamedScopes::OrConditions do
4
+ it "should define a scope by the exact same name as requested by the code" do
5
+ User.name_or_username_like('Test')
6
+ User.respond_to?(:name_or_username_like).should be_true
7
+ end
8
+
9
+ it "should match username or name" do
10
+ User.username_or_name_like("ben").proxy_options.should == {:conditions => "(users.username LIKE '%ben%') OR (users.name LIKE '%ben%')"}
11
+ end
12
+
13
+ it "should use the specified condition" do
14
+ User.username_begins_with_or_name_like("ben").proxy_options.should == {:conditions => "(users.username LIKE 'ben%') OR (users.name LIKE '%ben%')"}
15
+ end
16
+
17
+ it "should use the last specified condition" do
18
+ User.username_or_name_like_or_id_or_age_lt(10).proxy_options.should == {:conditions => "(users.username LIKE '%10%') OR (users.name LIKE '%10%') OR (users.id < 10) OR (users.age < 10)"}
19
+ end
20
+
21
+ it "should raise an error on unknown conditions" do
22
+ lambda { User.usernme_begins_with_or_name_like("ben") }.should raise_error(Searchlogic::NamedScopes::OrConditions::UnknownConditionError)
23
+ end
24
+
25
+ it "should work well with _or_equal_to" do
26
+ User.id_less_than_or_equal_to_or_age_gt(10).proxy_options.should == {:conditions => "(users.id <= 10) OR (users.age > 10)"}
27
+ end
28
+
29
+ it "should work well with _or_equal_to_any" do
30
+ User.id_less_than_or_equal_to_all_or_age_gt(10).proxy_options.should == {:conditions => "(users.id <= 10) OR (users.age > 10)"}
31
+ end
32
+
33
+ it "should work well with _or_equal_to_all" do
34
+ User.id_less_than_or_equal_to_any_or_age_gt(10).proxy_options.should == {:conditions => "(users.id <= 10) OR (users.age > 10)"}
35
+ end
36
+
37
+ it "should play nice with other scopes" do
38
+ User.username_begins_with("ben").id_gt(10).age_not_nil.username_or_name_ends_with("ben").scope(:find).should ==
39
+ {:conditions => "((users.username LIKE '%ben') OR (users.name LIKE '%ben')) AND ((users.age IS NOT NULL) AND ((users.id > 10) AND (users.username LIKE 'ben%')))"}
40
+ end
41
+
42
+ it "should play nice with scopes on associations" do
43
+ lambda { User.name_or_company_name_like("ben") }.should_not raise_error(Searchlogic::NamedScopes::OrConditions::NoConditionSpecifiedError)
44
+ User.name_or_company_name_like("ben").proxy_options.should == {:joins => :company, :conditions => "(users.name LIKE '%ben%') OR (companies.name LIKE '%ben%')"}
45
+ User.company_name_or_name_like("ben").proxy_options.should == {:joins => :company, :conditions => "(companies.name LIKE '%ben%') OR (users.name LIKE '%ben%')"}
46
+ User.company_name_or_company_description_like("ben").proxy_options.should == {:joins =>[:company], :conditions => "(companies.name LIKE '%ben%') OR (companies.description LIKE '%ben%')"}
47
+ end
48
+
49
+ it "should not get confused by the 'or' in find_or_create_by_* methods" do
50
+ User.create(:name => "Fred")
51
+ User.find_or_create_by_name("Fred").should be_a_kind_of User
52
+ end
53
+
54
+ it "should not get confused by the 'or' in compound find_or_create_by_* methods" do
55
+ User.create(:name => "Fred", :username => "fredb")
56
+ User.find_or_create_by_name_and_username("Fred", "fredb").should be_a_kind_of User
57
+ end
58
+
59
+ it "should work with User.search(conditions) method" do
60
+ User.search(:username_or_name_like => 'ben').proxy_options.should == {:conditions => "(users.username LIKE '%ben%') OR (users.name LIKE '%ben%')"}
61
+ end
62
+
63
+ it "should convert types properly when used with User.search(conditions) method" do
64
+ User.search(:id_or_age_lte => '10').proxy_options.should == {:conditions => "(users.id <= 10) OR (users.age <= 10)"}
65
+ end
66
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+
3
+ describe Searchlogic::NamedScopes::Ordering do
4
+ it "should be dynamically created and then cached" do
5
+ User.should_not respond_to(:ascend_by_username)
6
+ User.ascend_by_username
7
+ User.should respond_to(:ascend_by_username)
8
+ end
9
+
10
+ it "should have ascending" do
11
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
12
+ User.ascend_by_username.all.should == User.all(:order => "username ASC")
13
+ end
14
+
15
+ it "should have descending" do
16
+ %w(bjohnson thunt).each { |username| User.create(:username => username) }
17
+ User.descend_by_username.all.should == User.all(:order => "username DESC")
18
+ end
19
+
20
+ it "should have order" do
21
+ User.order("ascend_by_username").proxy_options.should == User.ascend_by_username.proxy_options
22
+ end
23
+
24
+ it "should have order by custom scope" do
25
+ User.column_names.should_not include("custom")
26
+ %w(bjohnson thunt fisons).each { |username| User.create(:username => username) }
27
+ User.named_scope(:ascend_by_custom, :order => "username ASC, name DESC")
28
+ User.order("ascend_by_custom").proxy_options.should == User.ascend_by_custom.proxy_options
29
+ end
30
+
31
+ it "should have priorty to columns over conflicting association columns" do
32
+ Company.ascend_by_users_count
33
+ end
34
+ end
@@ -0,0 +1,459 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+
3
+ describe Searchlogic::Search do
4
+ describe "Implementation" do
5
+ context "#searchlogic" do
6
+ it "should create a search proxy" do
7
+ User.search(:username => "joe").should be_kind_of(Searchlogic::Search)
8
+ end
9
+
10
+ it "should create a search proxy using the same class" do
11
+ User.search.klass.should == User
12
+ end
13
+
14
+ it "should pass on the current scope to the proxy" do
15
+ company = Company.create
16
+ user = company.users.create
17
+ search = company.users.search
18
+ search.current_scope.should == company.users.scope(:find)
19
+ end
20
+ end
21
+ end
22
+
23
+ context "#initialize" do
24
+ it "should require a class" do
25
+ lambda { Searchlogic::Search.new }.should raise_error(ArgumentError)
26
+ end
27
+
28
+ it "should set the conditions" do
29
+ search = User.search(:username => "bjohnson")
30
+ search.conditions.should == {:username => "bjohnson"}
31
+ end
32
+ end
33
+
34
+ context "#clone" do
35
+ it "should clone properly" do
36
+ company = Company.create
37
+ user1 = company.users.create(:age => 5)
38
+ user2 = company.users.create(:age => 25)
39
+ search1 = company.users.search(:age_gt => 10)
40
+ search2 = search1.clone
41
+ search2.age_gt = 1
42
+ search2.all.should == User.all
43
+ search1.all.should == [user2]
44
+ end
45
+
46
+ it "should clone properly without scope" do
47
+ user1 = User.create(:age => 5)
48
+ user2 = User.create(:age => 25)
49
+ search1 = User.search(:age_gt => 10)
50
+ search2 = search1.clone
51
+ search2.age_gt = 1
52
+ search2.all.should == User.all
53
+ search1.all.should == [user2]
54
+ end
55
+ end
56
+
57
+ context "#conditions" do
58
+ it "should set the conditions and be accessible individually" do
59
+ search = User.search
60
+ search.conditions = {:username => "bjohnson"}
61
+ search.username.should == "bjohnson"
62
+ end
63
+
64
+ it "should set the conditions and allow string keys" do
65
+ search = User.search
66
+ search.conditions = {"username" => "bjohnson"}
67
+ search.username.should == "bjohnson"
68
+ end
69
+
70
+ it "should use custom scopes before normalizing" do
71
+ User.create(:username => "bjohnson")
72
+ User.named_scope :username, lambda { |value| {:conditions => {:username => value.reverse}} }
73
+ search1 = User.search(:username => "bjohnson")
74
+ search2 = User.search(:username => "nosnhojb")
75
+ search1.count.should == 0
76
+ search2.count.should == 1
77
+ end
78
+
79
+ # We ignore them upon execution. But we still want to accept the condition so that returning the conditions
80
+ # preserves the values.
81
+ it "should ignore blank values but still return on conditions" do
82
+ search = User.search
83
+ search.conditions = {"username" => ""}
84
+ search.username.should be_nil
85
+ search.conditions.should == {:username => ""}
86
+ end
87
+
88
+ it "should not ignore blank values and should not cast them" do
89
+ search = User.search
90
+ search.conditions = {"id_equals" => ""}
91
+ search.id_equals.should be_nil
92
+ search.conditions.should == {:id_equals => ""}
93
+ end
94
+
95
+ it "should ignore blank values in arrays" do
96
+ search = User.search
97
+ search.conditions = {"username_equals_any" => [""]}
98
+ search.username_equals_any.should be_nil
99
+
100
+ search.conditions = {"id_equals_any" => ["", "1"]}
101
+ search.id_equals_any.should == [1]
102
+ end
103
+ end
104
+
105
+ context "condition accessors" do
106
+ it "should allow setting exact columns individually" do
107
+ search = User.search
108
+ search.username = "bjohnson"
109
+ search.username.should == "bjohnson"
110
+ end
111
+
112
+ it "should allow setting local column conditions individually" do
113
+ search = User.search
114
+ search.username_gt = "bjohnson"
115
+ search.username_gt.should == "bjohnson"
116
+ end
117
+
118
+ it "should allow chaining conditions" do
119
+ user = User.create(:username => "bjohnson", :age => 20)
120
+ User.create(:username => "bjohnson", :age => 5)
121
+ search = User.search
122
+ search.username_equals("bjohnson").age_gt(10)
123
+ search.all.should == [user]
124
+ end
125
+
126
+ it "should allow setting association conditions" do
127
+ search = User.search
128
+ search.orders_total_gt = 10
129
+ search.orders_total_gt.should == 10
130
+ end
131
+
132
+ it "should allow setting pre-existing association conditions" do
133
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
134
+ search = Company.search
135
+ search.users_uname = "bjohnson"
136
+ search.users_uname.should == "bjohnson"
137
+ end
138
+
139
+ it "should allow setting pre-existing association alias conditions" do
140
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
141
+ search = Company.search
142
+ search.users_username_has = "bjohnson"
143
+ search.users_username_has.should == "bjohnson"
144
+ end
145
+
146
+ it "should allow using custom conditions" do
147
+ User.named_scope(:four_year_olds, { :conditions => { :age => 4 } })
148
+ search = User.search
149
+ search.four_year_olds = true
150
+ search.four_year_olds.should == true
151
+ search.proxy_options.should == User.four_year_olds.proxy_options
152
+ end
153
+
154
+ it "should not merge conflicting conditions into one value" do
155
+ # This class should JUST be a proxy. It should not do anything more than that.
156
+ # A user would be allowed to call both named scopes if they wanted.
157
+ search = User.search
158
+ search.username_greater_than = "bjohnson1"
159
+ search.username_gt = "bjohnson2"
160
+ search.username_greater_than.should == "bjohnson1"
161
+ search.username_gt.should == "bjohnson2"
162
+ end
163
+
164
+ it "should allow setting custom conditions individually with an arity of 0" do
165
+ User.named_scope(:four_year_olds, :conditions => {:age => 4})
166
+ search = User.search
167
+ search.four_year_olds = true
168
+ search.four_year_olds.should == true
169
+ end
170
+
171
+ it "should allow setting custom conditions individually with an arity of 1" do
172
+ User.named_scope(:username_should_be, lambda { |u| {:conditions => {:username => u}} })
173
+ search = User.search
174
+ search.username_should_be = "bjohnson"
175
+ search.username_should_be.should == "bjohnson"
176
+ end
177
+
178
+ it "should not allow setting conditions that are not scopes" do
179
+ search = User.search
180
+ lambda { search.unknown = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
181
+ end
182
+
183
+ it "should not allow setting conditions on sensitive methods" do
184
+ search = User.search
185
+ lambda { search.destroy = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
186
+ end
187
+
188
+ it "should not use the ruby implementation of the id method" do
189
+ search = User.search
190
+ search.id.should be_nil
191
+ end
192
+
193
+ context "type casting" do
194
+ it "should be a Boolean given true" do
195
+ search = User.search
196
+ search.id_nil = true
197
+ search.id_nil.should == true
198
+ end
199
+
200
+ it "should be a Boolean given 'true'" do
201
+ search = User.search
202
+ search.id_nil = "true"
203
+ search.id_nil.should == true
204
+ end
205
+
206
+ it "should be a Boolean given '1'" do
207
+ search = User.search
208
+ search.id_nil = "1"
209
+ search.id_nil.should == true
210
+ end
211
+
212
+ it "should be a Boolean given false" do
213
+ search = User.search
214
+ search.id_nil = false
215
+ search.id_nil.should == false
216
+ end
217
+
218
+ it "should be a Boolean given 'false'" do
219
+ search = User.search
220
+ search.id_nil = "false"
221
+ search.id_nil.should == false
222
+ end
223
+
224
+ it "should be a Boolean given '0'" do
225
+ search = User.search
226
+ search.id_nil = "0"
227
+ search.id_nil.should == false
228
+ end
229
+
230
+ it "should be an Integer given ''" do
231
+ search = User.search
232
+ search.id_gt = ''
233
+ search.id_gt.should == 0
234
+ end
235
+
236
+ it "should be an Integer given 1" do
237
+ search = User.search
238
+ search.id_gt = 1
239
+ search.id_gt.should == 1
240
+ end
241
+
242
+ it "should be an Integer given '1'" do
243
+ search = User.search
244
+ search.id_gt = "1"
245
+ search.id_gt.should == 1
246
+ end
247
+
248
+ it "should be a Float given 1.0" do
249
+ search = Order.search
250
+ search.total_gt = 1.0
251
+ search.total_gt.should == 1.0
252
+ end
253
+
254
+ it "should be a Float given '1'" do
255
+ search = Order.search
256
+ search.total_gt = "1"
257
+ search.total_gt.should == 1.0
258
+ end
259
+
260
+ it "should be a Float given '1.5'" do
261
+ search = Order.search
262
+ search.total_gt = "1.5"
263
+ search.total_gt.should == 1.5
264
+ end
265
+
266
+ it "should be a Range given 1..3" do
267
+ search = Order.search
268
+ search.total_eq = (1..3)
269
+ search.total_eq.should == (1..3)
270
+ end
271
+
272
+ it "should be a Date given 'Jan 1, 2009'" do
273
+ search = Order.search
274
+ search.shipped_on_after = "Jan 1, 2009"
275
+ search.shipped_on_after.should == Date.parse("Jan 1, 2009")
276
+ end
277
+
278
+ it "should be a Time given 'Jan 1, 2009'" do
279
+ search = Order.search
280
+ search.created_at_after = "Jan 1, 2009"
281
+ search.created_at_after.should == Time.zone.parse("Jan 1, 2009")
282
+ end
283
+
284
+ it "should be a Time given 'Jan 1, 2009 9:33AM'" do
285
+ search = Order.search
286
+ search.created_at_after = "Jan 1, 2009 9:33AM"
287
+ search.created_at_after.should == Time.zone.parse("Jan 1, 2009 9:33AM")
288
+ end
289
+
290
+ it "should skip time zone conversion for attributes skipped" do
291
+ search = User.search
292
+ search.whatever_at_after = "Jan 1, 2009 9:33AM"
293
+ search.whatever_at_after.should == Time.parse("Jan 1, 2009 9:33AM").utc
294
+ end
295
+
296
+ it "should convert the time to the current zone" do
297
+ search = Order.search
298
+ now = Time.now
299
+ search.created_at_after = now
300
+ search.created_at_after.should == now.in_time_zone
301
+ end
302
+
303
+ it "should be an Array and cast it's values given ['1', '2', '3']" do
304
+ search = Order.search
305
+ search.id_equals_any = ["1", "2", "3"]
306
+ search.id_equals_any.should == [1, 2, 3]
307
+ end
308
+
309
+ it "should type cast association conditions" do
310
+ search = User.search
311
+ search.orders_total_gt = "10"
312
+ search.orders_total_gt.should == 10
313
+ end
314
+
315
+ it "should type cast deep association conditions" do
316
+ search = Company.search
317
+ search.users_orders_total_gt = "10"
318
+ search.users_orders_total_gt.should == 10
319
+ end
320
+
321
+ it "should support Rails' date_select and datetime_select out of the box" do
322
+ search = Company.search('created_at_after(1i)' => 2000, 'created_at_after(2i)' => 1, 'created_at_after(3i)' => 1)
323
+ search.created_at_after.should_not be_nil
324
+ search.created_at_after.should == Time.zone.local(2000, 1, 1)
325
+ end
326
+ end
327
+ end
328
+
329
+ context "#delete" do
330
+ it "should delete the condition" do
331
+ search = User.search(:username_like => "bjohnson")
332
+ search.delete("username_like")
333
+ search.username_like.should be_nil
334
+ search.conditions["username_like"].should be_nil
335
+ end
336
+ end
337
+
338
+ context "#ordering_by" do
339
+ it "should return nil if we aren't ordering" do
340
+ search = User.search
341
+ search.ordering_by.should be_nil
342
+ end
343
+
344
+ it "should return the column name for ascending" do
345
+ search = User.search(:order => "ascend_by_first_name")
346
+ search.ordering_by.should == "first_name"
347
+ end
348
+
349
+ it "should return the column name for descending" do
350
+ search = User.search(:order => "descend_by_first_name")
351
+ search.ordering_by.should == "first_name"
352
+ end
353
+
354
+ it "should handle symbols" do
355
+ search = User.search(:order => :descend_by_first_name)
356
+ search.ordering_by.should == "first_name"
357
+ end
358
+ end
359
+
360
+ context "#method_missing" do
361
+ context "setting" do
362
+ it "should call named scopes for conditions" do
363
+ User.search(:age_less_than => 5).proxy_options.should == User.age_less_than(5).proxy_options
364
+ end
365
+
366
+ it "should alias exact column names to use equals" do
367
+ User.search(:username => "joe").proxy_options.should == User.username_equals("joe").proxy_options
368
+ end
369
+
370
+ it "should recognize conditions with a value of true where the named scope has an arity of 0" do
371
+ User.search(:username_nil => true).proxy_options.should == User.username_nil.proxy_options
372
+ end
373
+
374
+ it "should ignore conditions with a value of false where the named scope has an arity of 0" do
375
+ User.search(:username_nil => false).proxy_options.should == {}
376
+ end
377
+
378
+ it "should not ignore conditions with a value of false where the named scope does not have an arity of 0" do
379
+ User.search(:username_is => false).proxy_options.should == User.username_is(false).proxy_options
380
+ end
381
+
382
+ it "should recognize the order condition" do
383
+ User.search(:order => "ascend_by_username").proxy_options.should == User.ascend_by_username.proxy_options
384
+ end
385
+
386
+ it "should pass array values as multiple arguments with arity -1" do
387
+ User.named_scope(:multiple_args, lambda { |*args|
388
+ raise "This should not be an array, it should be 1" if args.first.is_a?(Array)
389
+ {:conditions => ["id IN (?)", args]}
390
+ })
391
+ User.search(:multiple_args => [1,2]).proxy_options.should == User.multiple_args(1,2).proxy_options
392
+ end
393
+
394
+ it "should pass array as a single value with arity >= 0" do
395
+ User.named_scope(:multiple_args, lambda { |args|
396
+ raise "This should be an array" if !args.is_a?(Array)
397
+ {:conditions => ["id IN (?)", args]}
398
+ })
399
+ User.search(:multiple_args => [1,2]).proxy_options.should == User.multiple_args([1,2]).proxy_options
400
+ end
401
+
402
+ it "should not split out dates or times (big fix)" do
403
+ s = User.search
404
+ s.created_at_after = Time.now
405
+ lambda { s.count }.should_not raise_error
406
+ end
407
+
408
+ it "should not include blank values" do
409
+ s = User.search
410
+ s.conditions = {"id_equals" => ""}
411
+ s.proxy_options.should == {}
412
+ end
413
+ end
414
+ end
415
+
416
+ context "delegation" do
417
+ it "should return all when not given any conditions" do
418
+ 3.times { User.create }
419
+ User.search.all.length.should == 3
420
+ end
421
+
422
+ it "should implement the current scope based on an association" do
423
+ User.create
424
+ company = Company.create
425
+ user = company.users.create
426
+ company.users.search.all.should == [user]
427
+ end
428
+
429
+ it "should implement the current scope based on a named scope" do
430
+ User.named_scope(:four_year_olds, :conditions => {:age => 4})
431
+ (3..5).each { |age| User.create(:age => age) }
432
+ User.four_year_olds.search.all.should == User.find_all_by_age(4)
433
+ end
434
+
435
+ it "should respond to count" do
436
+ User.create(:username => "bjohnson")
437
+ search1 = User.search(:username => "bjohnson")
438
+ search2 = User.search(:username => "nosnhojb")
439
+ search1.count.should == 1
440
+ search2.count.should == 0
441
+ end
442
+
443
+ it "should respond to empty?" do
444
+ User.create(:username => "bjohnson")
445
+ search1 = User.search(:username => "bjohnson")
446
+ search2 = User.search(:username => "nosnhojb")
447
+ search1.empty?.should == false
448
+ search2.empty?.should == true
449
+ end
450
+
451
+ it "should delegate to named scopes with arity > 1" do
452
+ User.named_scope :paged, lambda {|start, limit| { :limit => limit, :offset => start }}
453
+ User.create(:username => "bjohnson")
454
+ search = User.search(:username => "bjohnson")
455
+ search.paged(0, 1).count.should == 1
456
+ search.paged(0, 0).count.should == 0
457
+ end
458
+ end
459
+ end