substantial-sunspot_rails 2.0.0.pre.111215

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +1 -0
  3. data/History.txt +66 -0
  4. data/LICENSE +18 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +265 -0
  7. data/Rakefile +12 -0
  8. data/TODO +8 -0
  9. data/dev_tasks/rdoc.rake +24 -0
  10. data/dev_tasks/release.rake +4 -0
  11. data/dev_tasks/spec.rake +107 -0
  12. data/dev_tasks/todo.rake +4 -0
  13. data/gemfiles/rails-2.3.14 +15 -0
  14. data/gemfiles/rails-3.0.11 +15 -0
  15. data/gemfiles/rails-3.1.3 +15 -0
  16. data/generators/sunspot/sunspot_generator.rb +9 -0
  17. data/generators/sunspot/templates/sunspot.yml +18 -0
  18. data/install.rb +1 -0
  19. data/lib/generators/sunspot_rails.rb +9 -0
  20. data/lib/generators/sunspot_rails/install/install_generator.rb +13 -0
  21. data/lib/generators/sunspot_rails/install/templates/config/sunspot.yml +17 -0
  22. data/lib/substantial-sunspot_rails.rb +1 -0
  23. data/lib/sunspot/rails.rb +65 -0
  24. data/lib/sunspot/rails/adapters.rb +83 -0
  25. data/lib/sunspot/rails/configuration.rb +340 -0
  26. data/lib/sunspot/rails/init.rb +5 -0
  27. data/lib/sunspot/rails/log_subscriber.rb +33 -0
  28. data/lib/sunspot/rails/railtie.rb +36 -0
  29. data/lib/sunspot/rails/railties/controller_runtime.rb +36 -0
  30. data/lib/sunspot/rails/request_lifecycle.rb +36 -0
  31. data/lib/sunspot/rails/searchable.rb +480 -0
  32. data/lib/sunspot/rails/server.rb +106 -0
  33. data/lib/sunspot/rails/solr_instrumentation.rb +18 -0
  34. data/lib/sunspot/rails/solr_logging.rb +62 -0
  35. data/lib/sunspot/rails/spec_helper.rb +26 -0
  36. data/lib/sunspot/rails/stub_session_proxy.rb +142 -0
  37. data/lib/sunspot/rails/tasks.rb +84 -0
  38. data/lib/sunspot_rails.rb +12 -0
  39. data/spec/configuration_spec.rb +195 -0
  40. data/spec/model_lifecycle_spec.rb +63 -0
  41. data/spec/model_spec.rb +595 -0
  42. data/spec/rails_template/app/controllers/application_controller.rb +10 -0
  43. data/spec/rails_template/app/controllers/posts_controller.rb +6 -0
  44. data/spec/rails_template/app/models/author.rb +8 -0
  45. data/spec/rails_template/app/models/blog.rb +12 -0
  46. data/spec/rails_template/app/models/location.rb +2 -0
  47. data/spec/rails_template/app/models/photo_post.rb +2 -0
  48. data/spec/rails_template/app/models/post.rb +11 -0
  49. data/spec/rails_template/app/models/post_with_auto.rb +10 -0
  50. data/spec/rails_template/app/models/post_with_default_scope.rb +11 -0
  51. data/spec/rails_template/config/boot.rb +127 -0
  52. data/spec/rails_template/config/preinitializer.rb +22 -0
  53. data/spec/rails_template/config/routes.rb +9 -0
  54. data/spec/rails_template/config/sunspot.yml +22 -0
  55. data/spec/rails_template/db/schema.rb +27 -0
  56. data/spec/request_lifecycle_spec.rb +61 -0
  57. data/spec/schema.rb +27 -0
  58. data/spec/searchable_spec.rb +12 -0
  59. data/spec/server_spec.rb +33 -0
  60. data/spec/session_spec.rb +57 -0
  61. data/spec/shared_examples/indexed_after_save.rb +8 -0
  62. data/spec/shared_examples/not_indexed_after_save.rb +8 -0
  63. data/spec/spec_helper.rb +48 -0
  64. data/spec/stub_session_proxy_spec.rb +122 -0
  65. data/substantial-sunspot_rails.gemspec +43 -0
  66. metadata +228 -0
@@ -0,0 +1,195 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Sunspot::Rails::Configuration, "default values without a sunspot.yml" do
4
+ before(:each) do
5
+ File.stub!(:exist?).and_return(false) # simulate sunspot.yml not existing
6
+ @config = Sunspot::Rails::Configuration.new
7
+ end
8
+
9
+ it "should handle the 'hostname' property when not set" do
10
+ @config.hostname.should == 'localhost'
11
+ end
12
+
13
+ it "should handle the 'path' property when not set" do
14
+ @config.path.should == '/solr'
15
+ end
16
+
17
+ describe "port" do
18
+ it "should default to port 8981 in test" do
19
+ ::Rails.stub!(:env => 'test')
20
+ @config = Sunspot::Rails::Configuration.new
21
+ @config.port.should == 8981
22
+ end
23
+ it "should default to port 8982 in development" do
24
+ ::Rails.stub!(:env => 'development')
25
+ @config = Sunspot::Rails::Configuration.new
26
+ @config.port.should == 8982
27
+ end
28
+ it "should default to 8983 in production" do
29
+ ::Rails.stub!(:env => 'production')
30
+ @config = Sunspot::Rails::Configuration.new
31
+ @config.port.should == 8983
32
+ end
33
+ it "should generally default to 8983" do
34
+ ::Rails.stub!(:env => 'staging')
35
+ @config = Sunspot::Rails::Configuration.new
36
+ @config.port.should == 8983
37
+ end
38
+ end
39
+
40
+ it "should handle the 'log_level' property when not set" do
41
+ @config.log_level.should == 'INFO'
42
+ end
43
+
44
+ it "should handle the 'log_file' property" do
45
+ @config.log_file.should =~ /log\/solr_test.log/
46
+ end
47
+
48
+ it "should handle the 'solr_home' property when not set" do
49
+ Rails.should_receive(:root).at_least(1).and_return('/some/path')
50
+ @config.solr_home.should == '/some/path/solr'
51
+ end
52
+
53
+ it "should handle the 'data_path' property when not set" do
54
+ Rails.should_receive(:root).at_least(1).and_return('/some/path')
55
+ @config.data_path.should == '/some/path/solr/data/test'
56
+ end
57
+
58
+ it "should handle the 'pid_dir' property when not set" do
59
+ Rails.should_receive(:root).at_least(1).and_return('/some/path')
60
+ @config.pid_dir.should == '/some/path/solr/pids/test'
61
+ end
62
+
63
+ it "should handle the 'auto_commit_after_request' propery when not set" do
64
+ @config.auto_commit_after_request?.should == true
65
+ end
66
+
67
+ it "should handle the 'auto_commit_after_delete_request' propery when not set" do
68
+ @config.auto_commit_after_delete_request?.should == false
69
+ end
70
+
71
+ it "should handle the 'bind_address' property when not set" do
72
+ @config.bind_address.should be_nil
73
+ end
74
+
75
+ it "should handle the 'disabled' property when not set" do
76
+ @config.disabled?.should be_false
77
+ end
78
+ end
79
+
80
+ describe Sunspot::Rails::Configuration, "user provided sunspot.yml" do
81
+ before(:each) do
82
+ ::Rails.stub!(:env => 'config_test')
83
+ @config = Sunspot::Rails::Configuration.new
84
+ end
85
+
86
+ it "should handle the 'hostname' property when set" do
87
+ @config.hostname.should == 'some.host'
88
+ end
89
+
90
+ it "should handle the 'port' property when set" do
91
+ @config.port.should == 1234
92
+ end
93
+
94
+ it "should handle the 'path' property when set" do
95
+ @config.path.should == '/solr/idx'
96
+ end
97
+
98
+ it "should handle the 'log_level' propery when set" do
99
+ @config.log_level.should == 'WARNING'
100
+ end
101
+
102
+ it "should handle the 'solr_home' propery when set" do
103
+ @config.solr_home.should == '/my_superior_path'
104
+ end
105
+
106
+ it "should handle the 'data_path' property when set" do
107
+ @config.data_path.should == '/my_superior_path/data'
108
+ end
109
+
110
+ it "should handle the 'pid_dir' property when set" do
111
+ @config.pid_dir.should == '/my_superior_path/pids'
112
+ end
113
+
114
+ it "should handle the 'solr_home' property when set" do
115
+ @config.solr_home.should == '/my_superior_path'
116
+ end
117
+
118
+ it "should handle the 'auto_commit_after_request' propery when set" do
119
+ @config.auto_commit_after_request?.should == false
120
+ end
121
+
122
+ it "should handle the 'auto_commit_after_delete_request' propery when set" do
123
+ @config.auto_commit_after_delete_request?.should == true
124
+ end
125
+
126
+ it "should handle the 'bind_address' property when set" do
127
+ @config.bind_address.should == "127.0.0.1"
128
+ end
129
+ end
130
+
131
+ describe Sunspot::Rails::Configuration, "with disabled: true in sunspot.yml" do
132
+ before(:each) do
133
+ ::Rails.stub!(:env => 'config_disabled_test')
134
+ @config = Sunspot::Rails::Configuration.new
135
+ end
136
+
137
+ it "should handle the 'disabled' property when set" do
138
+ @config.disabled?.should be_true
139
+ end
140
+ end
141
+
142
+ describe Sunspot::Rails::Configuration, "with ENV['SOLR_URL'] overriding sunspot.yml" do
143
+ before(:all) do
144
+ ENV['SOLR_URL'] = 'http://environment.host:5432/solr/env'
145
+ end
146
+
147
+ before(:each) do
148
+ ::Rails.stub!(:env => 'config_test')
149
+ @config = Sunspot::Rails::Configuration.new
150
+ end
151
+
152
+ after(:all) do
153
+ ENV.delete('SOLR_URL')
154
+ end
155
+
156
+ it "should handle the 'hostname' property when set" do
157
+ @config.hostname.should == 'environment.host'
158
+ end
159
+
160
+ it "should handle the 'port' property when set" do
161
+ @config.port.should == 5432
162
+ end
163
+
164
+ it "should handle the 'path' property when set" do
165
+ @config.path.should == '/solr/env'
166
+ end
167
+ end
168
+
169
+ describe Sunspot::Rails::Configuration, "with ENV['WEBSOLR_URL'] overriding sunspot.yml" do
170
+ before(:all) do
171
+ ENV['WEBSOLR_URL'] = 'http://index.websolr.test/solr/a1b2c3d4e5f'
172
+ end
173
+
174
+ before(:each) do
175
+ ::Rails.stub!(:env => 'config_test')
176
+ @config = Sunspot::Rails::Configuration.new
177
+ end
178
+
179
+ after(:all) do
180
+ ENV.delete('WEBSOLR_URL')
181
+ end
182
+
183
+ it "should handle the 'hostname' property when set" do
184
+ @config.hostname.should == 'index.websolr.test'
185
+ end
186
+
187
+ it "should handle the 'port' property when set" do
188
+ @config.port.should == 80
189
+ end
190
+
191
+ it "should handle the 'path' property when set" do
192
+ @config.path.should == '/solr/a1b2c3d4e5f'
193
+ end
194
+ end
195
+
@@ -0,0 +1,63 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'searchable with lifecycle' do
4
+ describe 'on create' do
5
+ before :each do
6
+ @post = PostWithAuto.create
7
+ Sunspot.commit
8
+ end
9
+
10
+ it 'should automatically index' do
11
+ PostWithAuto.search.results.should == [@post]
12
+ end
13
+ end
14
+
15
+ describe 'on update' do
16
+ before :each do
17
+ @post = PostWithAuto.create
18
+ @post.update_attributes(:title => 'Test 1')
19
+ Sunspot.commit
20
+ end
21
+
22
+ it 'should automatically update index' do
23
+ PostWithAuto.search { with :title, 'Test 1' }.results.should == [@post]
24
+ end
25
+
26
+ it "should index model if relevant attribute changed" do
27
+ @post = PostWithAuto.create!
28
+ @post.title = 'new title'
29
+ @post.should_receive :solr_index
30
+ @post.save!
31
+ end
32
+
33
+ it "should not index model if relevant attribute not changed" do
34
+ @post = PostWithAuto.create!
35
+ @post.updated_at = Date.tomorrow
36
+ @post.should_not_receive :solr_index
37
+ @post.save!
38
+ end
39
+ end
40
+
41
+ describe 'on destroy' do
42
+ before :each do
43
+ @post = PostWithAuto.create
44
+ @post.destroy
45
+ Sunspot.commit
46
+ end
47
+
48
+ it 'should automatically remove it from the index' do
49
+ PostWithAuto.search_ids.should be_empty
50
+ end
51
+ end
52
+ end
53
+
54
+ describe 'searchable with lifecycle - ignoring specific attributes' do
55
+ before(:each) do
56
+ @post = PostWithAuto.create
57
+ end
58
+
59
+ it "should not reindex the object on an update_at change, because it is marked as to-ignore" do
60
+ Sunspot.should_not_receive(:index).with(@post)
61
+ @post.update_attribute :updated_at, 123.seconds.from_now
62
+ end
63
+ end
@@ -0,0 +1,595 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'ActiveRecord mixin' do
4
+ describe 'index()' do
5
+ before :each do
6
+ @post = Post.create!
7
+ @post.index
8
+ end
9
+
10
+ it 'should not commit the model' do
11
+ Post.search.results.should be_empty
12
+ end
13
+
14
+ it 'should index the model' do
15
+ Sunspot.commit
16
+ Post.search.results.should == [@post]
17
+ end
18
+
19
+ it "should not blow up if there's a default scope specifying order" do
20
+ posts = Array.new(2) { |j| PostWithDefaultScope.create! :title => (10-j).to_s }
21
+ lambda { PostWithDefaultScope.index(:batch_size => 1) }.should_not raise_error
22
+ end
23
+ end
24
+
25
+ describe 'single table inheritence' do
26
+ before :each do
27
+ @post = PhotoPost.create!
28
+ end
29
+
30
+ it 'should not break auto-indexing' do
31
+ @post.title = 'Title'
32
+ lambda { @post.save! }.should_not raise_error
33
+ end
34
+ end
35
+
36
+ describe 'index!()' do
37
+ before :each do
38
+ @post = Post.create!
39
+ @post.index!
40
+ end
41
+
42
+ it 'should immediately index and commit' do
43
+ Post.search.results.should == [@post]
44
+ end
45
+ end
46
+
47
+ describe 'remove_from_index()' do
48
+ before :each do
49
+ @post = Post.create!
50
+ @post.index!
51
+ @post.remove_from_index
52
+ end
53
+
54
+ it 'should not commit immediately' do
55
+ Post.search.results.should == [@post]
56
+ end
57
+
58
+ it 'should remove the model from the index' do
59
+ Sunspot.commit
60
+ Post.search.results.should be_empty
61
+ end
62
+ end
63
+
64
+ describe 'remove_from_index!()' do
65
+ before :each do
66
+ @post = Post.create!
67
+ @post.index!
68
+ @post.remove_from_index!
69
+ end
70
+
71
+ it 'should immediately remove the model and commit' do
72
+ Post.search.results.should be_empty
73
+ end
74
+ end
75
+
76
+ describe 'remove_all_from_index' do
77
+ before :each do
78
+ @posts = Array.new(2) { Post.create! }.each { |post| Sunspot.index(post) }
79
+ Sunspot.commit
80
+ Post.remove_all_from_index
81
+ end
82
+
83
+ it 'should not commit immediately' do
84
+ Post.search.results.to_set.should == @posts.to_set
85
+ end
86
+
87
+ it 'should remove all instances from the index' do
88
+ Sunspot.commit
89
+ Post.search.results.should be_empty
90
+ end
91
+ end
92
+
93
+ describe 'remove_all_from_index!' do
94
+ before :each do
95
+ Array.new(2) { Post.create! }.each { |post| Sunspot.index(post) }
96
+ Sunspot.commit
97
+ Post.remove_all_from_index!
98
+ end
99
+
100
+ it 'should remove all instances from the index and commit immediately' do
101
+ Post.search.results.should be_empty
102
+ end
103
+ end
104
+
105
+ describe 'search()' do
106
+ before :each do
107
+ @post = Post.create!(:title => 'Test Post')
108
+ @post.index!
109
+ end
110
+
111
+ it 'should return results specified by search' do
112
+ Post.search do
113
+ with :title, 'Test Post'
114
+ end.results.should == [@post]
115
+ end
116
+
117
+ it 'should not return results excluded by search' do
118
+ Post.search do
119
+ with :title, 'Bogus Post'
120
+ end.results.should be_empty
121
+ end
122
+
123
+ it 'should use the include option on the data accessor when specified' do
124
+ Post.should_receive(:all).with(hash_including(:include => [:blog])).and_return([@post])
125
+ Post.search do
126
+ with :title, 'Test Post'
127
+ data_accessor_for(Post).include = [:blog]
128
+ end.results.should == [@post]
129
+ end
130
+
131
+ it 'should pass :include option from search call to data accessor' do
132
+ Post.should_receive(:all).with(hash_including(:include => [:blog])).and_return([@post])
133
+ Post.search(:include => [:blog]) do
134
+ with :title, 'Test Post'
135
+ end.results.should == [@post]
136
+ end
137
+
138
+ it 'should use the select option from search call to data accessor' do
139
+ Post.should_receive(:all).with(hash_including(:select => 'title, published_at')).and_return([@post])
140
+ Post.search(:select => 'title, published_at') do
141
+ with :title, 'Test Post'
142
+ end.results.should == [@post]
143
+ end
144
+
145
+ it 'should not allow bogus options to search' do
146
+ lambda { Post.search(:bogus => :option) }.should raise_error(ArgumentError)
147
+ end
148
+
149
+ it 'should use the select option on the data accessor when specified' do
150
+ Post.should_receive(:all).with(hash_including(:select => 'title, published_at')).and_return([@post])
151
+ Post.search do
152
+ with :title, 'Test Post'
153
+ data_accessor_for(Post).select = [:title, :published_at]
154
+ end.results.should == [@post]
155
+ end
156
+
157
+ it 'should not use the select option on the data accessor when not specified' do
158
+ Post.should_receive(:all).with(hash_not_including(:select)).and_return([@post])
159
+ Post.search do
160
+ with :title, 'Test Post'
161
+ end.results.should == [@post]
162
+ end
163
+
164
+ it 'should gracefully handle nonexistent records' do
165
+ post2 = Post.create!(:title => 'Test Post')
166
+ post2.index!
167
+ post2.destroy
168
+ Post.search do
169
+ with :title, 'Test Post'
170
+ end.results.should == [@post]
171
+ end
172
+
173
+ it 'should use an ActiveRecord object for coordinates' do
174
+ post = Post.new(:title => 'Test Post')
175
+ post.location = Location.create!(:lat => 40.0, :lng => -70.0)
176
+ post.save
177
+ post.index!
178
+ Post.search { with(:location).near(40.0, -70.0) }.results.should == [post]
179
+ end
180
+
181
+ end
182
+
183
+ describe 'search_ids()' do
184
+ before :each do
185
+ @posts = Array.new(2) { Post.create! }.each { |post| post.index }
186
+ Sunspot.commit
187
+ end
188
+
189
+ it 'should return IDs' do
190
+ Post.search_ids.to_set.should == @posts.map { |post| post.id }.to_set
191
+ end
192
+ end
193
+
194
+ describe 'searchable?()' do
195
+ it 'should not be true for models that have not been configured for search' do
196
+ Location.should_not be_searchable
197
+ end
198
+
199
+ it 'should be true for models that have been configured for search' do
200
+ Post.should be_searchable
201
+ end
202
+ end
203
+
204
+ describe 'index_orphans()' do
205
+ before :each do
206
+ @posts = Array.new(2) { Post.create }.each { |post| post.index }
207
+ Sunspot.commit
208
+ @posts.first.destroy
209
+ end
210
+
211
+ it 'should return IDs of objects that are in the index but not the database' do
212
+ Post.index_orphans.should == [@posts.first.id]
213
+ end
214
+ end
215
+
216
+ describe 'clean_index_orphans()' do
217
+ before :each do
218
+ @posts = Array.new(2) { Post.create }.each { |post| post.index }
219
+ Sunspot.commit
220
+ @posts.first.destroy
221
+ end
222
+
223
+ it 'should remove orphans from the index' do
224
+ Post.clean_index_orphans
225
+ Sunspot.commit
226
+ Post.search.results.should == [@posts.last]
227
+ end
228
+ end
229
+
230
+ describe 'reindex()' do
231
+ before :each do
232
+ @posts = Array.new(2) { Post.create }
233
+ end
234
+
235
+ it 'should index all instances' do
236
+ Post.reindex(:batch_size => nil)
237
+ Sunspot.commit
238
+ Post.search.results.to_set.should == @posts.to_set
239
+ end
240
+
241
+ it 'should remove all currently indexed instances' do
242
+ old_post = Post.create!
243
+ old_post.index!
244
+ old_post.destroy
245
+ Post.reindex
246
+ Sunspot.commit
247
+ Post.search.results.to_set.should == @posts.to_set
248
+ end
249
+
250
+ end
251
+
252
+ describe 'reindex() with real data' do
253
+ before :each do
254
+ @posts = Array.new(2) { Post.create }
255
+ end
256
+
257
+ it 'should index all instances' do
258
+ Post.reindex(:batch_size => nil)
259
+ Sunspot.commit
260
+ Post.search.results.to_set.should == @posts.to_set
261
+ end
262
+
263
+ it 'should remove all currently indexed instances' do
264
+ old_post = Post.create!
265
+ old_post.index!
266
+ old_post.destroy
267
+ Post.reindex
268
+ Sunspot.commit
269
+ Post.search.results.to_set.should == @posts.to_set
270
+ end
271
+
272
+ describe "using batch sizes" do
273
+ it 'should index with a specified batch size' do
274
+ Post.reindex(:batch_size => 1)
275
+ Sunspot.commit
276
+ Post.search.results.to_set.should == @posts.to_set
277
+ end
278
+ end
279
+ end
280
+
281
+
282
+
283
+ describe "reindex()" do
284
+
285
+ before(:each) do
286
+ @posts = Array.new(2) { Post.create }
287
+ end
288
+
289
+ describe "when not using batches" do
290
+
291
+ it "should select all if the batch_size is nil" do
292
+ Post.should_receive(:all).with(:include => []).and_return([])
293
+ Post.reindex(:batch_size => nil)
294
+ end
295
+
296
+ it "should search for models with includes" do
297
+ Post.should_receive(:all).with(:include => :author).and_return([])
298
+ Post.reindex(:batch_size => nil, :include => :author)
299
+ end
300
+
301
+ describe ':if constraints' do
302
+ before do
303
+ Post.sunspot_options[:if] = proc { |model| model.id != @posts.first.id }
304
+ end
305
+
306
+ after do
307
+ Post.sunspot_options[:if] = nil
308
+ end
309
+
310
+ it 'should only index those models where :if constraints pass' do
311
+ Post.reindex(:batch_size => nil)
312
+
313
+ Post.search.results.should_not include(@posts.first)
314
+ end
315
+ end
316
+
317
+ end
318
+
319
+ describe "when using batches" do
320
+ it "should commit after indexing each batch" do
321
+ Sunspot.should_receive(:commit).twice
322
+ Post.reindex(:batch_size => 1)
323
+ end
324
+
325
+ it "should commit after indexing everything" do
326
+ Sunspot.should_receive(:commit).once
327
+ Post.reindex(:batch_commit => false)
328
+ end
329
+
330
+ describe ':if constraints' do
331
+ before do
332
+ Post.sunspot_options[:if] = proc { |model| model.id != @posts.first.id }
333
+ end
334
+
335
+ after do
336
+ Post.sunspot_options[:if] = nil
337
+ end
338
+
339
+ it 'should only index those models where :if constraints pass' do
340
+ Post.reindex(:batch_size => 50)
341
+
342
+ Post.search.results.should_not include(@posts.first)
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ describe "more_like_this()" do
349
+ before(:each) do
350
+ @posts = [
351
+ Post.create!(:title => 'Post123', :body => "one two three"),
352
+ Post.create!(:title => 'Post345', :body => "three four five"),
353
+ Post.create!(:title => 'Post456', :body => "four five six"),
354
+ Post.create!(:title => 'Post234', :body => "two three four"),
355
+ ]
356
+ @posts_with_auto = [
357
+ PostWithAuto.create!(:body => "one two three"),
358
+ PostWithAuto.create!(:body => "four five six")
359
+ ]
360
+ @posts.each { |p| p.index! }
361
+ end
362
+
363
+ it "should return results" do
364
+ @posts.first.more_like_this.results.should == [@posts[3], @posts[1]]
365
+ end
366
+
367
+ it "should return results for specified classes" do
368
+ @posts.first.more_like_this(Post, PostWithAuto).results.to_set.should ==
369
+ Set[@posts_with_auto[0], @posts[1], @posts[3]]
370
+ end
371
+ end
372
+
373
+ describe 'more_like_this_ids()' do
374
+ before :each do
375
+ @posts = [
376
+ Post.create!(:title => 'Post123', :body => "one two three"),
377
+ Post.create!(:title => 'Post345', :body => "three four five"),
378
+ Post.create!(:title => 'Post456', :body => "four five six"),
379
+ Post.create!(:title => 'Post234', :body => "two three four"),
380
+ ]
381
+ @posts.each { |p| p.index! }
382
+ end
383
+
384
+ it 'should return IDs' do
385
+ @posts.first.more_like_this_ids.to_set.should == [@posts[3], @posts[1]].map { |post| post.id }.to_set
386
+ end
387
+ end
388
+
389
+ describe ':if constraint' do
390
+ subject do
391
+ PostWithAuto.new(:title => 'Post123')
392
+ end
393
+
394
+ after do
395
+ subject.class.sunspot_options[:if] = nil
396
+ end
397
+
398
+ context 'Symbol' do
399
+ context 'constraint returns true' do
400
+ # searchable :if => :returns_true
401
+ before do
402
+ subject.should_receive(:returns_true).and_return(true)
403
+ subject.class.sunspot_options[:if] = :returns_true
404
+ end
405
+
406
+ it_should_behave_like 'indexed after save'
407
+ end
408
+
409
+ context 'constraint returns false' do
410
+ # searchable :if => :returns_false
411
+ before do
412
+ subject.should_receive(:returns_false).and_return(false)
413
+ subject.class.sunspot_options[:if] = :returns_false
414
+ end
415
+
416
+ it_should_behave_like 'not indexed after save'
417
+ end
418
+ end
419
+
420
+ context 'String' do
421
+ context 'constraint returns true' do
422
+ # searchable :if => 'returns_true'
423
+ before do
424
+ subject.should_receive(:returns_true).and_return(true)
425
+ subject.class.sunspot_options[:if] = 'returns_true'
426
+ end
427
+
428
+ it_should_behave_like 'indexed after save'
429
+ end
430
+
431
+ context 'constraint returns false' do
432
+ # searchable :if => 'returns_false'
433
+ before do
434
+ subject.should_receive(:returns_false).and_return(false)
435
+ subject.class.sunspot_options[:if] = 'returns_false'
436
+ end
437
+
438
+ it_should_behave_like 'not indexed after save'
439
+ end
440
+ end
441
+
442
+ context 'Proc' do
443
+ context 'constraint returns true' do
444
+ # searchable :if => proc { true }
445
+ before do
446
+ subject.class.sunspot_options[:if] = proc { true }
447
+ end
448
+
449
+ it_should_behave_like 'indexed after save'
450
+ end
451
+
452
+ context 'constraint returns false' do
453
+ # searchable :if => proc { false }
454
+ before do
455
+ subject.class.sunspot_options[:if] = proc { false }
456
+ end
457
+
458
+ it_should_behave_like 'not indexed after save'
459
+ end
460
+ end
461
+
462
+ context 'Array' do
463
+ context 'all constraints returns true' do
464
+ # searchable :if => [:returns_true_1, :returns_true_2]
465
+ before do
466
+ subject.should_receive(:returns_true_1).and_return(true)
467
+ subject.should_receive(:returns_true_2).and_return(true)
468
+ subject.class.sunspot_options[:if] = [:returns_true_1, 'returns_true_2']
469
+ end
470
+
471
+ it_should_behave_like 'indexed after save'
472
+ end
473
+
474
+ context 'one constraint returns false' do
475
+ # searchable :if => [:returns_true, :returns_false]
476
+ before do
477
+ subject.should_receive(:returns_true).and_return(true)
478
+ subject.should_receive(:returns_false).and_return(false)
479
+ subject.class.sunspot_options[:if] = [:returns_true, 'returns_false']
480
+ end
481
+
482
+ it_should_behave_like 'not indexed after save'
483
+ end
484
+ end
485
+
486
+ it 'removes the model from the index if the constraint does not match' do
487
+ subject.save!
488
+ Sunspot.commit
489
+ subject.class.search.results.should include(subject)
490
+
491
+ subject.class.sunspot_options[:if] = proc { false }
492
+ subject.save!
493
+ Sunspot.commit
494
+ subject.class.search.results.should_not include(subject)
495
+ end
496
+ end
497
+
498
+ describe ':unless constraint' do
499
+ subject do
500
+ PostWithAuto.new(:title => 'Post123')
501
+ end
502
+
503
+ after do
504
+ subject.class.sunspot_options[:unless] = nil
505
+ end
506
+
507
+ context 'Symbol' do
508
+ context 'constraint returns true' do
509
+ # searchable :unless => :returns_true
510
+ before do
511
+ subject.should_receive(:returns_true).and_return(true)
512
+ subject.class.sunspot_options[:unless] = :returns_true
513
+ end
514
+
515
+ it_should_behave_like 'not indexed after save'
516
+ end
517
+
518
+ context 'constraint returns false' do
519
+ # searchable :unless => :returns_false
520
+ before do
521
+ subject.should_receive(:returns_false).and_return(false)
522
+ subject.class.sunspot_options[:unless] = :returns_false
523
+ end
524
+
525
+ it_should_behave_like 'indexed after save'
526
+ end
527
+ end
528
+
529
+ context 'String' do
530
+ context 'constraint returns true' do
531
+ # searchable :unless => 'returns_true'
532
+ before do
533
+ subject.should_receive(:returns_true).and_return(true)
534
+ subject.class.sunspot_options[:unless] = 'returns_true'
535
+ end
536
+
537
+ it_should_behave_like 'not indexed after save'
538
+ end
539
+
540
+ context 'constraint returns false' do
541
+ # searchable :unless => 'returns_false'
542
+ before do
543
+ subject.should_receive(:returns_false).and_return(false)
544
+ subject.class.sunspot_options[:unless] = 'returns_false'
545
+ end
546
+
547
+ it_should_behave_like 'indexed after save'
548
+ end
549
+ end
550
+
551
+ context 'Proc' do
552
+ context 'constraint returns true' do
553
+ # searchable :unless => proc { true }
554
+ before do
555
+ subject.class.sunspot_options[:unless] = proc { |model| model == subject } # true
556
+ end
557
+
558
+ it_should_behave_like 'not indexed after save'
559
+ end
560
+
561
+ context 'constraint returns false' do
562
+ # searchable :unless => proc { false }
563
+ before do
564
+ subject.class.sunspot_options[:unless] = proc { false }
565
+ end
566
+
567
+ it_should_behave_like 'indexed after save'
568
+ end
569
+ end
570
+
571
+ context 'Array' do
572
+ context 'all constraints returns true' do
573
+ # searchable :unless => [:returns_true_1, :returns_true_2]
574
+ before do
575
+ subject.should_receive(:returns_true_1).and_return(true)
576
+ subject.should_receive(:returns_true_2).and_return(true)
577
+ subject.class.sunspot_options[:unless] = [:returns_true_1, 'returns_true_2']
578
+ end
579
+
580
+ it_should_behave_like 'not indexed after save'
581
+ end
582
+
583
+ context 'one constraint returns false' do
584
+ # searchable :unless => [:returns_true, :returns_false]
585
+ before do
586
+ subject.should_receive(:returns_true).and_return(true)
587
+ subject.should_receive(:returns_false).and_return(false)
588
+ subject.class.sunspot_options[:unless] = [:returns_true, 'returns_false']
589
+ end
590
+
591
+ it_should_behave_like 'indexed after save'
592
+ end
593
+ end
594
+ end
595
+ end