substantial-sunspot_rails 2.0.0.pre.111215

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 (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