friendlyfashion-thinking-sphinx 2.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. data/HISTORY +244 -0
  2. data/LICENCE +20 -0
  3. data/README.textile +235 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +21 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +88 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/field_sorting.feature +18 -0
  15. data/features/handling_edits.feature +94 -0
  16. data/features/retry_stale_indexes.feature +24 -0
  17. data/features/searching_across_models.feature +20 -0
  18. data/features/searching_by_index.feature +40 -0
  19. data/features/searching_by_model.feature +175 -0
  20. data/features/searching_with_find_arguments.feature +56 -0
  21. data/features/sphinx_detection.feature +25 -0
  22. data/features/sphinx_scopes.feature +68 -0
  23. data/features/step_definitions/alpha_steps.rb +16 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +201 -0
  26. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  27. data/features/step_definitions/facet_steps.rb +96 -0
  28. data/features/step_definitions/find_arguments_steps.rb +36 -0
  29. data/features/step_definitions/gamma_steps.rb +15 -0
  30. data/features/step_definitions/scope_steps.rb +19 -0
  31. data/features/step_definitions/search_steps.rb +94 -0
  32. data/features/step_definitions/sphinx_steps.rb +35 -0
  33. data/features/sti_searching.feature +19 -0
  34. data/features/support/env.rb +27 -0
  35. data/features/support/lib/generic_delta_handler.rb +8 -0
  36. data/features/thinking_sphinx/database.example.yml +3 -0
  37. data/features/thinking_sphinx/db/.gitignore +1 -0
  38. data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
  39. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  40. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  41. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  42. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  43. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  44. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  45. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  46. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  47. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  48. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  49. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  50. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  51. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  52. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
  53. data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
  54. data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
  55. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  56. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  57. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  58. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  59. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  60. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  61. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  62. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  63. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  64. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  65. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  66. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  67. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  68. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  69. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  70. data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
  71. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  72. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  73. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  74. data/features/thinking_sphinx/models/alpha.rb +23 -0
  75. data/features/thinking_sphinx/models/andrew.rb +17 -0
  76. data/features/thinking_sphinx/models/animal.rb +5 -0
  77. data/features/thinking_sphinx/models/author.rb +3 -0
  78. data/features/thinking_sphinx/models/beta.rb +13 -0
  79. data/features/thinking_sphinx/models/box.rb +8 -0
  80. data/features/thinking_sphinx/models/cat.rb +3 -0
  81. data/features/thinking_sphinx/models/category.rb +4 -0
  82. data/features/thinking_sphinx/models/comment.rb +10 -0
  83. data/features/thinking_sphinx/models/developer.rb +21 -0
  84. data/features/thinking_sphinx/models/dog.rb +3 -0
  85. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  86. data/features/thinking_sphinx/models/fox.rb +5 -0
  87. data/features/thinking_sphinx/models/gamma.rb +5 -0
  88. data/features/thinking_sphinx/models/genre.rb +3 -0
  89. data/features/thinking_sphinx/models/medium.rb +5 -0
  90. data/features/thinking_sphinx/models/music.rb +10 -0
  91. data/features/thinking_sphinx/models/person.rb +24 -0
  92. data/features/thinking_sphinx/models/post.rb +22 -0
  93. data/features/thinking_sphinx/models/robot.rb +12 -0
  94. data/features/thinking_sphinx/models/tag.rb +3 -0
  95. data/features/thinking_sphinx/models/tagging.rb +4 -0
  96. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  97. data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
  98. data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
  99. data/lib/thinking-sphinx.rb +1 -0
  100. data/lib/thinking_sphinx/action_controller.rb +31 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  102. data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
  103. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  104. data/lib/thinking_sphinx/active_record/delta.rb +67 -0
  105. data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
  106. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  107. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  108. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  109. data/lib/thinking_sphinx/active_record.rb +386 -0
  110. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  111. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  112. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
  113. data/lib/thinking_sphinx/association.rb +230 -0
  114. data/lib/thinking_sphinx/attribute.rb +405 -0
  115. data/lib/thinking_sphinx/auto_version.rb +40 -0
  116. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  117. data/lib/thinking_sphinx/class_facet.rb +20 -0
  118. data/lib/thinking_sphinx/configuration.rb +375 -0
  119. data/lib/thinking_sphinx/context.rb +76 -0
  120. data/lib/thinking_sphinx/core/string.rb +15 -0
  121. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  122. data/lib/thinking_sphinx/deltas.rb +28 -0
  123. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  124. data/lib/thinking_sphinx/excerpter.rb +23 -0
  125. data/lib/thinking_sphinx/facet.rb +135 -0
  126. data/lib/thinking_sphinx/facet_search.rb +170 -0
  127. data/lib/thinking_sphinx/field.rb +98 -0
  128. data/lib/thinking_sphinx/index/builder.rb +315 -0
  129. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  130. data/lib/thinking_sphinx/index.rb +159 -0
  131. data/lib/thinking_sphinx/join.rb +37 -0
  132. data/lib/thinking_sphinx/property.rb +187 -0
  133. data/lib/thinking_sphinx/railtie.rb +43 -0
  134. data/lib/thinking_sphinx/search.rb +1061 -0
  135. data/lib/thinking_sphinx/search_methods.rb +439 -0
  136. data/lib/thinking_sphinx/sinatra.rb +7 -0
  137. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  138. data/lib/thinking_sphinx/source/sql.rb +174 -0
  139. data/lib/thinking_sphinx/source.rb +194 -0
  140. data/lib/thinking_sphinx/tasks.rb +142 -0
  141. data/lib/thinking_sphinx/test.rb +55 -0
  142. data/lib/thinking_sphinx/version.rb +3 -0
  143. data/lib/thinking_sphinx.rb +297 -0
  144. data/spec/fixtures/data.sql +32 -0
  145. data/spec/fixtures/database.yml.default +3 -0
  146. data/spec/fixtures/models.rb +164 -0
  147. data/spec/fixtures/structure.sql +146 -0
  148. data/spec/spec_helper.rb +61 -0
  149. data/spec/sphinx_helper.rb +60 -0
  150. data/spec/support/rails.rb +25 -0
  151. data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
  152. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
  153. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  154. data/spec/thinking_sphinx/active_record_spec.rb +573 -0
  155. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  156. data/spec/thinking_sphinx/association_spec.rb +250 -0
  157. data/spec/thinking_sphinx/attribute_spec.rb +552 -0
  158. data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
  159. data/spec/thinking_sphinx/configuration_spec.rb +326 -0
  160. data/spec/thinking_sphinx/context_spec.rb +126 -0
  161. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  162. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  163. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  164. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  165. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  166. data/spec/thinking_sphinx/field_spec.rb +127 -0
  167. data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
  168. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  169. data/spec/thinking_sphinx/index_spec.rb +189 -0
  170. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  171. data/spec/thinking_sphinx/search_spec.rb +1455 -0
  172. data/spec/thinking_sphinx/source_spec.rb +267 -0
  173. data/spec/thinking_sphinx/test_spec.rb +20 -0
  174. data/spec/thinking_sphinx_spec.rb +204 -0
  175. metadata +524 -0
@@ -0,0 +1,326 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Configuration do
4
+ describe "environment class method" do
5
+ before :each do
6
+ ThinkingSphinx::Configuration.reset_environment
7
+
8
+ ENV["RAILS_ENV"] = nil
9
+ end
10
+
11
+ it "should use Rails.env if set" do
12
+ was = Rails.env
13
+ Rails.env = 'global_rails'
14
+
15
+ ThinkingSphinx::Configuration.environment.should == 'global_rails'
16
+
17
+ Rails.env = was
18
+ end
19
+
20
+ it "should use the Rails environment value if set" do
21
+ Rails.stub!(:env => "rails_production")
22
+ ThinkingSphinx::Configuration.environment.should == "rails_production"
23
+ end
24
+
25
+ it "should default to development" do
26
+ ThinkingSphinx::Configuration.environment.should == "development"
27
+ end
28
+ end
29
+
30
+ describe '#version' do
31
+ before :each do
32
+ @config = ThinkingSphinx::Configuration.instance
33
+ @config.reset
34
+ end
35
+
36
+ it "should use the given version from sphinx.yml if there is one" do
37
+ open("#{Rails.root}/config/sphinx.yml", "w") do |f|
38
+ f.write YAML.dump({'development' => {'version' => '0.9.7'}})
39
+ end
40
+ @config.reset
41
+
42
+ @config.version.should == '0.9.7'
43
+
44
+ FileUtils.rm "#{Rails.root}/config/sphinx.yml"
45
+ end
46
+
47
+ it "should detect the version from Riddle otherwise" do
48
+ controller = @config.controller
49
+ controller.stub!(:sphinx_version => '0.9.6')
50
+
51
+ Riddle::Controller.stub!(:new => controller)
52
+ @config.reset
53
+
54
+ @config.version.should == '0.9.6'
55
+ end
56
+ end
57
+
58
+ describe "parse_config method" do
59
+ before :each do
60
+ @settings = {
61
+ "development" => {
62
+ "config_file" => "tmp/config/development.sphinx.conf",
63
+ "searchd_log_file" => "searchd_log_file.log",
64
+ "query_log_file" => "query_log_file.log",
65
+ "pid_file" => "pid_file.pid",
66
+ "searchd_file_path" => "searchd/file/path",
67
+ "address" => "127.0.0.1",
68
+ "port" => 3333,
69
+ "min_prefix_len" => 2,
70
+ "min_infix_len" => 3,
71
+ "mem_limit" => "128M",
72
+ "max_matches" => 1001,
73
+ "morphology" => "stem_ru",
74
+ "charset_type" => "latin1",
75
+ "charset_table" => "table",
76
+ "ignore_chars" => "e",
77
+ "searchd_binary_name" => "sphinx-searchd",
78
+ "indexer_binary_name" => "sphinx-indexer",
79
+ "index_exact_words" => true,
80
+ "indexed_models" => ['Alpha', 'Beta']
81
+ }
82
+ }
83
+
84
+ open("#{Rails.root}/config/sphinx.yml", "w") do |f|
85
+ f.write YAML.dump(@settings)
86
+ end
87
+ end
88
+
89
+ it "should use the accessors to set the configuration values" do
90
+ config = ThinkingSphinx::Configuration.instance
91
+ config.send(:parse_config)
92
+
93
+ %w(config_file searchd_log_file query_log_file pid_file searchd_file_path
94
+ address port searchd_binary_name indexer_binary_name).each do |key|
95
+ config.send(key).should == @settings["development"][key]
96
+ end
97
+ end
98
+
99
+ after :each do
100
+ FileUtils.rm "#{Rails.root}/config/sphinx.yml"
101
+ end
102
+ end
103
+
104
+ describe "block configuration" do
105
+ it "should let the user set-up a custom app_root" do
106
+ ThinkingSphinx::Configuration.configure do |config|
107
+ config.app_root = "/here/somewhere"
108
+ end
109
+ ThinkingSphinx::Configuration.instance.app_root.should == "/here/somewhere"
110
+
111
+ ThinkingSphinx::Configuration.instance.reset
112
+ end
113
+ end
114
+
115
+ describe "initialisation" do
116
+ it "should have a default bin_path of nothing" do
117
+ ThinkingSphinx::Configuration.instance.bin_path.should == ""
118
+ end
119
+
120
+ it "should append a / to bin_path if one is supplied" do
121
+ @settings = {
122
+ "development" => {
123
+ "bin_path" => "path/to/somewhere"
124
+ }
125
+ }
126
+
127
+ open("#{Rails.root}/config/sphinx.yml", "w") do |f|
128
+ f.write YAML.dump(@settings)
129
+ end
130
+
131
+ ThinkingSphinx::Configuration.instance.send(:parse_config)
132
+ ThinkingSphinx::Configuration.instance.bin_path.should match(/\/$/)
133
+
134
+ FileUtils.rm "#{Rails.root}/config/sphinx.yml"
135
+ end
136
+ end
137
+
138
+ describe "index options" do
139
+ before :each do
140
+ @settings = {
141
+ "development" => {"disable_range" => true}
142
+ }
143
+
144
+ open("#{Rails.root}/config/sphinx.yml", "w") do |f|
145
+ f.write YAML.dump(@settings)
146
+ end
147
+
148
+ @config = ThinkingSphinx::Configuration.instance
149
+ @config.send(:parse_config)
150
+ end
151
+
152
+ it "should collect disable_range" do
153
+ @config.index_options[:disable_range].should be_true
154
+ end
155
+
156
+ after :each do
157
+ FileUtils.rm "#{Rails.root}/config/sphinx.yml"
158
+ end
159
+ end
160
+
161
+ it "should insert set index options into the configuration file" do
162
+ config = ThinkingSphinx::Configuration.instance
163
+
164
+ ThinkingSphinx::Configuration::IndexOptions.each do |option|
165
+ config.reset
166
+ config.index_options[option.to_sym] = "something"
167
+ config.build
168
+
169
+ file = open(config.config_file) { |f| f.read }
170
+ file.should match(/#{option}\s+= something/)
171
+
172
+ config.index_options[option.to_sym] = nil
173
+ end
174
+ end
175
+
176
+ it "should insert set source options into the configuration file" do
177
+ config = ThinkingSphinx::Configuration.instance
178
+ config.reset
179
+
180
+ config.source_options[:sql_query_pre] = ["something"]
181
+ ThinkingSphinx::Configuration::SourceOptions.each do |option|
182
+ config.source_options[option.to_sym] ||= "something"
183
+ config.build
184
+
185
+ file = open(config.config_file) { |f| f.read }
186
+ file.should match(/#{option}\s+= something/)
187
+
188
+ config.source_options.delete option.to_sym
189
+ end
190
+
191
+ config.source_options[:sql_query_pre] = []
192
+ end
193
+
194
+ it "should not blow away delta or utf options if sql pre is specified in config" do
195
+ config = ThinkingSphinx::Configuration.instance
196
+ config.reset
197
+
198
+ config.source_options[:sql_query_pre] = ["a pre query"]
199
+ config.build
200
+ file = open(config.config_file) { |f| f.read }
201
+
202
+ file.should match(/sql_query_pre = a pre query\n\s*sql_query_pre = UPDATE `\w+` SET `delta` = 0 WHERE `delta` = 1/im)
203
+ file.should match(/sql_query_pre = a pre query\n\s*sql_query_pre = \n/im)
204
+
205
+ config.source_options[:sql_query_pre] = []
206
+ end
207
+
208
+ it "should set any explicit prefixed or infixed fields" do
209
+ ThinkingSphinx::Configuration.instance.build
210
+
211
+ file = open(ThinkingSphinx::Configuration.instance.config_file) { |f|
212
+ f.read
213
+ }
214
+ file.should match(/prefix_fields\s+= city/)
215
+ file.should match(/infix_fields\s+= state/)
216
+ end
217
+
218
+ it "should not have prefix fields in indexes where nothing is set" do
219
+ ThinkingSphinx::Configuration.instance.build
220
+
221
+ file = open(ThinkingSphinx::Configuration.instance.config_file) { |f|
222
+ f.read
223
+ }
224
+ file.should_not match(/index alpha_core\s+\{\s+[^\}]*prefix_fields\s+=[^\}]*\}/m)
225
+ end
226
+
227
+ describe '#generate' do
228
+ let(:config) { ThinkingSphinx::Configuration.instance }
229
+
230
+ it "should set all sphinx_internal_id attributes to bigints if one is" do
231
+ config.reset
232
+ config.generate
233
+
234
+ config.configuration.indices.each do |index|
235
+ next if index.is_a? Riddle::Configuration::DistributedIndex
236
+
237
+ index.sources.each do |source|
238
+ source.sql_attr_bigint.should include(:sphinx_internal_id)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '#client' do
245
+ before :each do
246
+ @config = ThinkingSphinx::Configuration.instance
247
+ @config.address = 'domain.url'
248
+ @config.port = 3333
249
+ @config.configuration.searchd.max_matches = 100
250
+ @config.timeout = 1
251
+ end
252
+
253
+ it "should return an instance of Riddle::Client" do
254
+ @config.client.should be_a(Riddle::Client)
255
+ end
256
+
257
+ it "should use the configuration address" do
258
+ @config.client.server.should == 'domain.url'
259
+ end
260
+
261
+ it "should use the configuration port" do
262
+ @config.client.port.should == 3333
263
+ end
264
+
265
+ it "should use the configuration max matches" do
266
+ @config.client.max_matches.should == 100
267
+ end
268
+
269
+ it "should use the configuration timeout" do
270
+ @config.client.timeout.should == 1
271
+ end
272
+
273
+ describe 'when shuffle is enabled' do
274
+ let(:client) { double('client', :max_matches= => nil, :timeout= => nil) }
275
+
276
+ before :each do
277
+ @config.shuffle = true
278
+ end
279
+
280
+ it "should shuffle client servers" do
281
+ @config.address = ['1.1.1.1', '2.2.2.2']
282
+ @config.address.stub!(:shuffle => ['2.2.2.2', '1.1.1.1'])
283
+
284
+ Riddle::Client.should_receive(:new) do |addresses, port, key|
285
+ addresses.should == ['2.2.2.2', '1.1.1.1']
286
+ client
287
+ end
288
+ @config.client
289
+ end
290
+ end
291
+
292
+ describe 'when shuffle is disabled' do
293
+ let(:client) { double('client', :max_matches= => nil, :timeout= => nil) }
294
+
295
+ before :each do
296
+ @config.shuffle = false
297
+ end
298
+
299
+ it "should not shuffle client servers" do
300
+ @config.address = ['1.1.1.1', '2.2.2.2.', '3.3.3.3', '4.4.4.4', '5.5.5.5']
301
+
302
+ @config.address.should_not_receive(:shuffle)
303
+ Riddle::Client.should_receive(:new) do |addresses, port, key|
304
+ addresses.should == ['1.1.1.1', '2.2.2.2.', '3.3.3.3', '4.4.4.4', '5.5.5.5']
305
+ client
306
+ end
307
+ @config.client
308
+ end
309
+ end
310
+ end
311
+
312
+ describe '#models_by_crc' do
313
+ before :each do
314
+ @config = ThinkingSphinx::Configuration.instance
315
+ end
316
+
317
+ it "should return a hash" do
318
+ @config.models_by_crc.should be_a(Hash)
319
+ end
320
+
321
+ it "should pair class names to their crc codes" do
322
+ @config.models_by_crc[Person.to_crc32].should == 'Person'
323
+ @config.models_by_crc[Alpha.to_crc32].should == 'Alpha'
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Context do
4
+ let(:ts_context) { ThinkingSphinx::Context.new }
5
+
6
+ describe '#prepare' do
7
+ let(:config) { ThinkingSphinx::Configuration.instance }
8
+ let(:file_name) { 'a.rb' }
9
+ let(:model_name_lower) { 'a' }
10
+ let(:class_name) { 'A' }
11
+
12
+ before :each do
13
+ config.model_directories = ['']
14
+
15
+ file_name.stub!(:gsub).and_return(model_name_lower)
16
+ model_name_lower.stub!(:camelize).and_return(class_name)
17
+ Dir.stub(:[]).and_return([file_name])
18
+ end
19
+
20
+ it "should load the files by guessing the file name" do
21
+ class_name.should_receive(:constantize).and_return(true)
22
+
23
+ ts_context.prepare
24
+ end
25
+
26
+ it "should not raise errors if the model name is nil" do
27
+ file_name.stub!(:gsub).and_return(nil)
28
+
29
+ lambda {
30
+ ts_context.prepare
31
+ }.should_not raise_error
32
+ end
33
+
34
+ it "should report name errors but not raise them" do
35
+ class_name.stub(:constantize).and_raise(NameError)
36
+ STDERR.stub!(:puts => '')
37
+ STDERR.should_receive(:puts).with('Warning: Error loading a.rb:')
38
+
39
+ lambda {
40
+ ts_context.prepare
41
+ }.should_not raise_error
42
+ end
43
+
44
+ it "should retry if the first load fails and contains a directory" do
45
+ model_name_lower.should_receive(:gsub!).twice.and_return(true, nil)
46
+ class_name.stub(:constantize).and_raise(LoadError)
47
+
48
+ lambda {
49
+ ts_context.prepare
50
+ }.should_not raise_error
51
+ end
52
+
53
+ it "should catch database errors with a warning" do
54
+ class_name.should_receive(:constantize).and_raise(Mysql2::Error)
55
+ STDERR.stub!(:puts => '')
56
+ STDERR.should_receive(:puts).with('Warning: Error loading a.rb:')
57
+
58
+ lambda {
59
+ ts_context.prepare
60
+ }.should_not raise_error
61
+ end unless RUBY_PLATFORM == 'java'
62
+
63
+ it "should not load models if they're explicitly set in the configuration" do
64
+ config.indexed_models = ['Alpha', 'Beta']
65
+ ts_context.prepare
66
+
67
+ ts_context.indexed_models.should == ['Alpha', 'Beta']
68
+ end
69
+ end
70
+
71
+ describe '#define_indexes' do
72
+ it "should call define_indexes on all known indexed models" do
73
+ ts_context.stub!(:indexed_models => ['Alpha', 'Beta'])
74
+ Alpha.should_receive(:define_indexes)
75
+ Beta.should_receive(:define_indexes)
76
+
77
+ ts_context.define_indexes
78
+ end
79
+ end
80
+
81
+ describe '#add_indexed_model' do
82
+ before :each do
83
+ ts_context.indexed_models.clear
84
+ end
85
+
86
+ it "should add the model to the collection" do
87
+ ts_context.add_indexed_model 'Alpha'
88
+
89
+ ts_context.indexed_models.should == ['Alpha']
90
+ end
91
+
92
+ it "should not duplicate models in the collection" do
93
+ ts_context.add_indexed_model 'Alpha'
94
+ ts_context.add_indexed_model 'Alpha'
95
+
96
+ ts_context.indexed_models.should == ['Alpha']
97
+ end
98
+
99
+ it "should keep the collection in alphabetical order" do
100
+ ts_context.add_indexed_model 'Beta'
101
+ ts_context.add_indexed_model 'Alpha'
102
+
103
+ ts_context.indexed_models.should == ['Alpha', 'Beta']
104
+ end
105
+
106
+ it "should translate classes to their names" do
107
+ ts_context.add_indexed_model Alpha
108
+
109
+ ts_context.indexed_models.should == ['Alpha']
110
+ end
111
+ end
112
+
113
+ describe '#superclass_indexed_models' do
114
+ it "should return indexed model names" do
115
+ ts_context.stub!(:indexed_models => ['Alpha', 'Beta'])
116
+
117
+ ts_context.superclass_indexed_models.should == ['Alpha', 'Beta']
118
+ end
119
+
120
+ it "should not include classes which have indexed superclasses" do
121
+ ts_context.stub!(:indexed_models => ['Parent', 'Person'])
122
+
123
+ ts_context.superclass_indexed_models.should == ['Person']
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Array do
4
+ describe '.===' do
5
+ it "should return true if an instance of ThinkingSphinx::Search" do
6
+ Array.should === ThinkingSphinx::Search.new
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe String do
4
+ describe "to_crc32 instance method" do
5
+ it "should return an integer" do
6
+ 'to_crc32'.to_crc32.should be_a_kind_of(Integer)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Excerpter do
4
+ before :each do
5
+ @alpha = Alpha.find(:first)
6
+ @search = mock 'search', :excerpt_for => 'excerpted value'
7
+ @excerpter = ThinkingSphinx::Excerpter.new(@search, @alpha)
8
+ end
9
+
10
+ it "should not respond to id" do
11
+ @excerpter.should_not respond_to(:id)
12
+ end
13
+
14
+ describe '#method_missing' do
15
+ it "should return the excerpt from Sphinx" do
16
+ @excerpter.name.should == 'excerpted value'
17
+ end
18
+
19
+ it "should send through the instance class to excerpt_for" do
20
+ @search.should_receive(:excerpt_for) do |string, model|
21
+ model.should == Alpha
22
+ end
23
+
24
+ @excerpter.name
25
+ end
26
+
27
+ it "should use attribute methods for excerpts calls" do
28
+ @search.should_receive(:excerpt_for) do |string, model|
29
+ string.should == 'one'
30
+ end
31
+
32
+ @excerpter.name
33
+ end
34
+
35
+ it "should use instance methods for excerpts calls" do
36
+ @search.should_receive(:excerpt_for) do |string, model|
37
+ string.should == 'ONE'
38
+ end
39
+
40
+ @excerpter.big_name
41
+ end
42
+
43
+ it "should still raise an exception if no column or method exists" do
44
+ lambda {
45
+ @excerpter.foo
46
+ }.should raise_error(NoMethodError)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::FacetSearch do
4
+ let(:search) { stub('search', :append_to => nil, :empty? => true) }
5
+ let(:config) { ThinkingSphinx::Configuration.instance }
6
+ let(:client) { stub('client', :run => []) }
7
+
8
+ before :each do
9
+ config.stub!(:client => client)
10
+ end
11
+
12
+ describe 'populate' do
13
+ before :each do
14
+ config.configuration.searchd.max_matches = 10_000
15
+ end
16
+
17
+ it "should request all shared facets in a multi-model request by default" do
18
+ ThinkingSphinx.stub!(:search => search)
19
+ if Riddle.loaded_version.to_i < 2
20
+ ThinkingSphinx::FacetSearch.new.facet_names.should == ['class_crc']
21
+ else
22
+ ThinkingSphinx::FacetSearch.new.facet_names.should == ['sphinx_internal_class']
23
+ end
24
+ end
25
+
26
+ it "should request all facets in a multi-model request if specified" do
27
+ ThinkingSphinx.stub!(:search => search)
28
+ names = ThinkingSphinx::FacetSearch.new(:all_facets => true).facet_names
29
+
30
+ if Riddle.loaded_version.to_i < 2
31
+ names.should == ['class_crc', 'city_facet', 'state_facet', 'birthday']
32
+ else
33
+ names.should == ['sphinx_internal_class', 'city_facet', 'state_facet', 'birthday']
34
+ end
35
+ end
36
+
37
+ it "should use the system-set max_matches for limit on facet calls" do
38
+ ThinkingSphinx.should_receive(:search) do |options|
39
+ options[:max_matches].should == 10_000
40
+ options[:limit].should == 10_000
41
+ search
42
+ end
43
+
44
+ ThinkingSphinx::FacetSearch.new
45
+ end
46
+
47
+ it "should use the default max-matches if there is no explicit setting" do
48
+ config.configuration.searchd.max_matches = nil
49
+ ThinkingSphinx.should_receive(:search) do |options|
50
+ options[:max_matches].should == 1000
51
+ options[:limit].should == 1000
52
+ search
53
+ end
54
+
55
+ ThinkingSphinx::FacetSearch.new
56
+ end
57
+
58
+ it "should ignore user-provided max_matches and limit on facet calls" do
59
+ ThinkingSphinx.should_receive(:search) do |options|
60
+ options[:max_matches].should == 10_000
61
+ options[:limit].should == 10_000
62
+ search
63
+ end
64
+
65
+ ThinkingSphinx::FacetSearch.new(
66
+ :max_matches => 500,
67
+ :limit => 200
68
+ )
69
+ end
70
+
71
+ it "should not use an explicit :page" do
72
+ ThinkingSphinx.should_receive(:search) do |options|
73
+ options[:page].should == 1
74
+ search
75
+ end
76
+
77
+ ThinkingSphinx::FacetSearch.new(:page => 3)
78
+ end
79
+
80
+ describe "conflicting facets" do
81
+ before :each do
82
+ @index = ThinkingSphinx::Index::Builder.generate(Alpha) do
83
+ indexes :name
84
+ has :value, :as => :city, :facet => true
85
+ end
86
+ end
87
+
88
+ after :each do
89
+ Alpha.sphinx_facets.delete_at(-1)
90
+ end
91
+
92
+ it "should raise an error if searching with facets of same name but different type" do
93
+ lambda {
94
+ facets = ThinkingSphinx.facets :all_facets => true
95
+ }.should raise_error
96
+ end
97
+ end
98
+
99
+ describe ':facets option' do
100
+ it "should limit facets to the requested set" do
101
+ ThinkingSphinx.should_receive(:search).once.and_return(search)
102
+
103
+ ThinkingSphinx::FacetSearch.new(
104
+ :classes => [Person], :facets => :state
105
+ )
106
+ end
107
+ end
108
+
109
+ describe "empty result set for attributes" do
110
+ before :each do
111
+ ThinkingSphinx.stub!(:search => search)
112
+ @facets = ThinkingSphinx::FacetSearch.new(
113
+ :classes => [Person], :facets => :state
114
+ )
115
+ end
116
+
117
+ it "should add key as attribute" do
118
+ @facets.should have_key(:state)
119
+ end
120
+
121
+ it "should return an empty hash for the facet results" do
122
+ @facets[:state].should be_empty
123
+ end
124
+ end
125
+
126
+ describe "non-empty result set" do
127
+ before :each do
128
+ @person = Person.find(:first)
129
+ @people = [@person]
130
+ search.stub!(:empty? => false)
131
+ search.stub!(:each_with_match).
132
+ and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
133
+ ThinkingSphinx::Search.stub!(:bundle_searches => [search])
134
+
135
+ @facets = ThinkingSphinx::FacetSearch.new(
136
+ :classes => [Person], :facets => :city
137
+ )
138
+ end
139
+
140
+ it "should return a hash" do
141
+ @facets.should be_a_kind_of(Hash)
142
+ end
143
+
144
+ it "should add key as attribute" do
145
+ @facets.keys.should include(:city)
146
+ end
147
+
148
+ it "should return a hash" do
149
+ @facets[:city].should == {@person.city => 1}
150
+ end
151
+ end
152
+ end
153
+
154
+ describe "#for" do
155
+ before do
156
+ @person = Person.find(:first)
157
+ @people = [@person]
158
+ search.stub!(:each_with_match).
159
+ and_yield(@person, {:attributes => {'@groupby' => @person.city.to_crc32, '@count' => 1}})
160
+ ThinkingSphinx::Search.stub!(:bundle_searches => [search])
161
+
162
+ @facets = ThinkingSphinx::FacetSearch.new(
163
+ :classes => [Person], :facets => :city
164
+ )
165
+ end
166
+
167
+ it "should return the search results for the attribute and key pair" do
168
+ ThinkingSphinx.should_receive(:search) do |options|
169
+ options[:with].should have_key('city_facet')
170
+ options[:with]['city_facet'].should == @person.city.to_crc32
171
+ end
172
+
173
+ @facets.for(:city => @person.city)
174
+ end
175
+ end
176
+ end