ryanb-thinking_sphinx 0.9.8

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 (38) hide show
  1. data/LICENCE +20 -0
  2. data/README +60 -0
  3. data/lib/riddle.rb +26 -0
  4. data/lib/riddle/client.rb +639 -0
  5. data/lib/riddle/client/filter.rb +44 -0
  6. data/lib/riddle/client/message.rb +65 -0
  7. data/lib/riddle/client/response.rb +84 -0
  8. data/lib/test.rb +46 -0
  9. data/lib/thinking_sphinx.rb +102 -0
  10. data/lib/thinking_sphinx/active_record.rb +141 -0
  11. data/lib/thinking_sphinx/active_record/delta.rb +97 -0
  12. data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
  13. data/lib/thinking_sphinx/active_record/search.rb +50 -0
  14. data/lib/thinking_sphinx/association.rb +144 -0
  15. data/lib/thinking_sphinx/attribute.rb +284 -0
  16. data/lib/thinking_sphinx/configuration.rb +283 -0
  17. data/lib/thinking_sphinx/field.rb +200 -0
  18. data/lib/thinking_sphinx/index.rb +340 -0
  19. data/lib/thinking_sphinx/index/builder.rb +195 -0
  20. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  21. data/lib/thinking_sphinx/rails_additions.rb +56 -0
  22. data/lib/thinking_sphinx/search.rb +482 -0
  23. data/lib/thinking_sphinx/tasks.rb +86 -0
  24. data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +207 -0
  25. data/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb +53 -0
  26. data/spec/unit/thinking_sphinx/active_record/search_spec.rb +107 -0
  27. data/spec/unit/thinking_sphinx/active_record_spec.rb +236 -0
  28. data/spec/unit/thinking_sphinx/association_spec.rb +247 -0
  29. data/spec/unit/thinking_sphinx/attribute_spec.rb +360 -0
  30. data/spec/unit/thinking_sphinx/configuration_spec.rb +493 -0
  31. data/spec/unit/thinking_sphinx/field_spec.rb +219 -0
  32. data/spec/unit/thinking_sphinx/index/builder_spec.rb +33 -0
  33. data/spec/unit/thinking_sphinx/index/faux_column_spec.rb +68 -0
  34. data/spec/unit/thinking_sphinx/index_spec.rb +277 -0
  35. data/spec/unit/thinking_sphinx/search_spec.rb +190 -0
  36. data/spec/unit/thinking_sphinx_spec.rb +129 -0
  37. data/tasks/thinking_sphinx_tasks.rake +1 -0
  38. metadata +103 -0
@@ -0,0 +1,360 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Attribute do
4
+ describe '#initialize' do
5
+ it 'raises if no columns are provided so that configuration errors are easier to track down' do
6
+ lambda {
7
+ ThinkingSphinx::Attribute.new([])
8
+ }.should raise_error(RuntimeError)
9
+ end
10
+
11
+ it 'raises if an element of the columns param is an integer - as happens when you use id instead of :id - so that configuration errors are easier to track down' do
12
+ lambda {
13
+ ThinkingSphinx::Attribute.new([1234])
14
+ }.should raise_error(RuntimeError)
15
+ end
16
+ end
17
+
18
+ describe "to_select_sql method with MySQL" do
19
+ before :each do
20
+ @index = Person.indexes.first
21
+ @index.link!
22
+ end
23
+
24
+ it "should concat with spaces if there's more than one non-integer column" do
25
+ @index.attributes[0].to_select_sql.should match(/CONCAT_WS\(' ', /)
26
+ end
27
+
28
+ it "should concat with spaces if there's more than one association for a non-integer column" do
29
+ @index.attributes[1].to_select_sql.should match(/CONCAT_WS\(' ', /)
30
+ end
31
+
32
+ it "should concat with commas if there's multiple integer columns" do
33
+ @index.attributes[2].to_select_sql.should match(/CONCAT_WS\(',', /)
34
+ end
35
+
36
+ it "should concat with commas if there's more than one association for an integer column" do
37
+ @index.attributes[3].to_select_sql.should match(/CONCAT_WS\(',', /)
38
+ end
39
+
40
+ it "should group with spaces if there's string columns from a has_many or has_and_belongs_to_many association" do
41
+ @index.attributes[4].to_select_sql.should match(/GROUP_CONCAT\(.+ SEPARATOR ' '\)/)
42
+ end
43
+
44
+ it "should group with commas if there's integer columns from a has_many or has_and_belongs_to_many association" do
45
+ @index.attributes[5].to_select_sql.should match(/GROUP_CONCAT\(.+ SEPARATOR ','\)/)
46
+ end
47
+
48
+ it "should convert datetime values to timestamps" do
49
+ @index.attributes[6].to_select_sql.should match(/UNIX_TIMESTAMP/)
50
+ end
51
+ end
52
+
53
+ describe "to_select_sql method with PostgreSQL" do
54
+ before :each do
55
+ @index = Person.indexes.first
56
+ Person.connection.class.stub_method(
57
+ :name => "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
58
+ )
59
+ @index.link!
60
+ end
61
+
62
+ it "should concat with spaces if there's more than one non-integer column" do
63
+ @index.attributes[0].to_select_sql.should match(/|| ' ' ||/)
64
+ end
65
+
66
+ it "should concat with spaces if there's more than one association for a non-integer column" do
67
+ @index.attributes[1].to_select_sql.should match(/|| ' ' ||/)
68
+ end
69
+
70
+ it "should concat with commas if there's multiple integer columns" do
71
+ @index.attributes[2].to_select_sql.should match(/|| ',' ||/)
72
+ end
73
+
74
+ it "should concat with commas if there's more than one association for an integer column" do
75
+ @index.attributes[3].to_select_sql.should match(/|| ',' ||/)
76
+ end
77
+
78
+ it "should group with spaces if there's string columns from a has_many or has_and_belongs_to_many association" do
79
+ @index.attributes[4].to_select_sql.should match(/array_to_string\(array_accum\(.+, ' '\)/)
80
+ end
81
+
82
+ it "should group with commas if there's integer columns from a has_many or has_and_belongs_to_many association" do
83
+ @index.attributes[5].to_select_sql.should match(/array_to_string\(array_accum\(.+, ','\)/)
84
+ end
85
+ end
86
+
87
+ describe "to_group_sql method" do
88
+ before :each do
89
+ @attribute = ThinkingSphinx::Attribute.new([Object.stub_instance(:__stack => [])])
90
+ @attribute.stub_method(:is_many? => false, :is_string? => false)
91
+
92
+ ThinkingSphinx.stub_method(:use_group_by_shortcut? => false)
93
+ end
94
+
95
+ it "should return nil if is_many?" do
96
+ @attribute.stub_method(:is_many? => true)
97
+
98
+ @attribute.to_group_sql.should be_nil
99
+ end
100
+
101
+ it "should return nil if is_string?" do
102
+ @attribute.stub_method(:is_string? => true)
103
+
104
+ @attribute.to_group_sql.should be_nil
105
+ end
106
+
107
+ it "should return nil if group_by shortcut is allowed" do
108
+ ThinkingSphinx.stub_method(:use_group_by_shortcut? => true)
109
+
110
+ @attribute.to_group_sql.should be_nil
111
+ end
112
+
113
+ it "should return an array if neither is_many? or shortcut allowed" do
114
+ @attribute.stub_method(:column_with_prefix => 'hello')
115
+ @attribute.to_group_sql.should be_a_kind_of(Array)
116
+ end
117
+
118
+ after :each do
119
+ ThinkingSphinx.unstub_method(:use_group_by_shortcut?)
120
+ end
121
+ end
122
+
123
+ describe "to_sphinx_clause method" do
124
+ before :each do
125
+ @attribute = ThinkingSphinx::Attribute.new [Object.stub_instance(:__stack => [])]
126
+ @attribute.stub_method(:unique_name => "unique name")
127
+ end
128
+
129
+ it "should use sql_attr_multi syntax for MVA attributes" do
130
+ @attribute.stub_method(:type => :multi)
131
+ @attribute.to_sphinx_clause.should match(/^sql_attr_multi\s+= uint unique name from field$/)
132
+ end
133
+
134
+ it "should use sql_attr_timestamp syntax for datetime values" do
135
+ @attribute.stub_method(:type => :datetime)
136
+ @attribute.to_sphinx_clause.should match(/^sql_attr_timestamp\s+= unique name$/)
137
+ end
138
+
139
+ it "should use sql_attr_str2ordinal for string values" do
140
+ @attribute.stub_method(:type => :string)
141
+ @attribute.to_sphinx_clause.should match(/^sql_attr_str2ordinal\s+= unique name$/)
142
+ end
143
+
144
+ it "should use sql_attr_float for float values" do
145
+ @attribute.stub_method(:type => :float)
146
+ @attribute.to_sphinx_clause.should match(/^sql_attr_float\s+= unique name$/)
147
+ end
148
+
149
+ it "should use sql_attr_bool for boolean values" do
150
+ @attribute.stub_method(:type => :boolean)
151
+ @attribute.to_sphinx_clause.should match(/^sql_attr_bool\s+= unique name$/)
152
+ end
153
+
154
+ it "should use sql_attr_uint for integer values" do
155
+ @attribute.stub_method(:type => :integer)
156
+ @attribute.to_sphinx_clause.should match(/^sql_attr_uint\s+= unique name$/)
157
+ end
158
+
159
+ it "should assume integer for any other types" do
160
+ @attribute.stub_method(:type => :unknown)
161
+ @attribute.to_sphinx_clause.should match(/^sql_attr_uint\s+= unique name$/)
162
+ end
163
+
164
+ end
165
+
166
+ describe "unique_name method" do
167
+ before :each do
168
+ @attribute = ThinkingSphinx::Attribute.new [
169
+ Object.stub_instance(:__stack => [], :__name => "col_name")
170
+ ]
171
+ end
172
+
173
+ it "should use the alias if there is one" do
174
+ @attribute.alias = "alias"
175
+ @attribute.unique_name.should == "alias"
176
+ end
177
+
178
+ it "should use the alias if there's multiple columns" do
179
+ @attribute.columns << Object.stub_instance(:__stack => [], :__name => "col_name")
180
+ @attribute.unique_name.should be_nil
181
+
182
+ @attribute.alias = "alias"
183
+ @attribute.unique_name.should == "alias"
184
+ end
185
+
186
+ it "should use the column name if there's no alias and just one column" do
187
+ @attribute.unique_name.should == "col_name"
188
+ end
189
+ end
190
+
191
+ describe "column_with_prefix method" do
192
+ before :each do
193
+ @attribute = ThinkingSphinx::Attribute.new [
194
+ ThinkingSphinx::Index::FauxColumn.new(:col_name)
195
+ ]
196
+ @attribute.columns.each { |col| @attribute.associations[col] = [] }
197
+ @attribute.model = Person
198
+
199
+ @first_join = Object.stub_instance(:aliased_table_name => "tabular")
200
+ @second_join = Object.stub_instance(:aliased_table_name => "data")
201
+
202
+ @first_assoc = ThinkingSphinx::Association.stub_instance(
203
+ :join => @first_join, :has_column? => true
204
+ )
205
+ @second_assoc = ThinkingSphinx::Association.stub_instance(
206
+ :join => @second_join, :has_column? => true
207
+ )
208
+ end
209
+
210
+ it "should return the column name if the column is a string" do
211
+ @attribute.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
212
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "string"
213
+ end
214
+
215
+ it "should return the column with model's table prefix if there's no associations for the column" do
216
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "`people`.`col_name`"
217
+ end
218
+
219
+ it "should return the column with its join table prefix if an association exists" do
220
+ column = @attribute.columns.first
221
+ @attribute.associations[column] = [@first_assoc]
222
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`"
223
+ end
224
+
225
+ it "should return multiple columns concatenated if more than one association exists" do
226
+ column = @attribute.columns.first
227
+ @attribute.associations[column] = [@first_assoc, @second_assoc]
228
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`"
229
+ end
230
+ end
231
+
232
+ describe "is_many? method" do
233
+ before :each do
234
+ @assoc_a = Object.stub_instance(:is_many? => true)
235
+ @assoc_b = Object.stub_instance(:is_many? => true)
236
+ @assoc_c = Object.stub_instance(:is_many? => true)
237
+
238
+ @attribute = ThinkingSphinx::Attribute.new(
239
+ [ThinkingSphinx::Index::FauxColumn.new(:col_name)]
240
+ )
241
+ @attribute.associations = {
242
+ :a => @assoc_a, :b => @assoc_b, :c => @assoc_c
243
+ }
244
+ end
245
+
246
+ it "should return true if all associations return true to is_many?" do
247
+ @attribute.send(:is_many?).should be_true
248
+ end
249
+
250
+ it "should return true if one association returns true to is_many?" do
251
+ @assoc_b.stub_method(:is_many? => false)
252
+ @assoc_c.stub_method(:is_many? => false)
253
+
254
+ @attribute.send(:is_many?).should be_true
255
+ end
256
+
257
+ it "should return false if all associations return false to is_many?" do
258
+ @assoc_a.stub_method(:is_many? => false)
259
+ @assoc_b.stub_method(:is_many? => false)
260
+ @assoc_c.stub_method(:is_many? => false)
261
+
262
+ @attribute.send(:is_many?).should be_false
263
+ end
264
+ end
265
+
266
+ describe "is_string? method" do
267
+ before :each do
268
+ @col_a = ThinkingSphinx::Index::FauxColumn.new("a")
269
+ @col_b = ThinkingSphinx::Index::FauxColumn.new("b")
270
+ @col_c = ThinkingSphinx::Index::FauxColumn.new("c")
271
+
272
+ @attribute = ThinkingSphinx::Attribute.new(
273
+ [@col_a, @col_b, @col_c]
274
+ )
275
+ end
276
+
277
+ it "should return true if all columns return true to is_string?" do
278
+ @attribute.send(:is_string?).should be_true
279
+ end
280
+
281
+ it "should return false if one column returns true to is_string?" do
282
+ @col_a.send(:instance_variable_set, :@name, :a)
283
+ @attribute.send(:is_string?).should be_false
284
+ end
285
+
286
+ it "should return false if all columns return false to is_string?" do
287
+ @col_a.send(:instance_variable_set, :@name, :a)
288
+ @col_b.send(:instance_variable_set, :@name, :b)
289
+ @col_c.send(:instance_variable_set, :@name, :c)
290
+ @attribute.send(:is_string?).should be_false
291
+ end
292
+ end
293
+
294
+ describe "type method" do
295
+ before :each do
296
+ @column = ThinkingSphinx::Index::FauxColumn.new(:col_name)
297
+ @attribute = ThinkingSphinx::Attribute.new([@column])
298
+ @attribute.model = Person
299
+ @attribute.stub_method(:is_many? => false)
300
+ end
301
+
302
+ it "should return :multi if is_many? is true" do
303
+ @attribute.stub_method(:is_many? => true)
304
+ @attribute.send(:type).should == :multi
305
+ end
306
+
307
+ it "should return :string if there's more than one association" do
308
+ @attribute.associations = {:a => :assoc, :b => :assoc}
309
+ @attribute.send(:type).should == :string
310
+ end
311
+
312
+ it "should return the column type from the database if not :multi or more than one association" do
313
+ @column.send(:instance_variable_set, :@name, "birthday")
314
+ @attribute.send(:type).should == :datetime
315
+
316
+ @attribute.send(:instance_variable_set, :@type, nil)
317
+ @column.send(:instance_variable_set, :@name, "first_name")
318
+ @attribute.send(:type).should == :string
319
+
320
+ @attribute.send(:instance_variable_set, :@type, nil)
321
+ @column.send(:instance_variable_set, :@name, "id")
322
+ @attribute.send(:type).should == :integer
323
+ end
324
+ end
325
+
326
+ describe "all_ints? method" do
327
+ it "should return true if all columns are integers" do
328
+ attribute = ThinkingSphinx::Attribute.new(
329
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
330
+ ThinkingSphinx::Index::FauxColumn.new(:team_id) ]
331
+ )
332
+ attribute.model = Person
333
+ attribute.columns.each { |col| attribute.associations[col] = [] }
334
+
335
+ attribute.send(:all_ints?).should be_true
336
+ end
337
+
338
+ it "should return false if only some columns are integers" do
339
+ attribute = ThinkingSphinx::Attribute.new(
340
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
341
+ ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
342
+ )
343
+ attribute.model = Person
344
+ attribute.columns.each { |col| attribute.associations[col] = [] }
345
+
346
+ attribute.send(:all_ints?).should be_false
347
+ end
348
+
349
+ it "should return false if no columns are integers" do
350
+ attribute = ThinkingSphinx::Attribute.new(
351
+ [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
352
+ ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
353
+ )
354
+ attribute.model = Person
355
+ attribute.columns.each { |col| attribute.associations[col] = [] }
356
+
357
+ attribute.send(:all_ints?).should be_false
358
+ end
359
+ end
360
+ end
@@ -0,0 +1,493 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Configuration do
4
+ describe "environment class method" do
5
+ before :each do
6
+ ThinkingSphinx::Configuration.send(:class_variable_set, :@@environment, nil)
7
+
8
+ ENV["RAILS_ENV"] = nil
9
+ ENV["MERB_ENV"] = nil
10
+ end
11
+
12
+ it "should use the Merb environment value if set" do
13
+ unless defined?(Merb)
14
+ module Merb; end
15
+ end
16
+
17
+ ThinkingSphinx::Configuration.stub_method(:defined? => true)
18
+ ENV["MERB_ENV"] = "merb_production"
19
+ ThinkingSphinx::Configuration.environment.should == "merb_production"
20
+
21
+ Object.send(:remove_const, :Merb)
22
+ end
23
+
24
+ it "should use the Rails environment value if set" do
25
+ ENV["RAILS_ENV"] = "rails_production"
26
+ ThinkingSphinx::Configuration.environment.should == "rails_production"
27
+ end
28
+
29
+ it "should default to development" do
30
+ ThinkingSphinx::Configuration.environment.should == "development"
31
+ end
32
+ end
33
+
34
+ describe "environment instance method" do
35
+ it "should return the class method" do
36
+ ThinkingSphinx::Configuration.stub_method(:environment => "spec")
37
+ ThinkingSphinx::Configuration.new.environment.should == "spec"
38
+ ThinkingSphinx::Configuration.should have_received(:environment)
39
+ end
40
+ end
41
+
42
+ describe "build method" do
43
+ before :each do
44
+ @config = ThinkingSphinx::Configuration.new
45
+
46
+ @config.stub_methods(
47
+ :load_models => "",
48
+ :core_index_for_model => "",
49
+ :delta_index_for_model => "",
50
+ :distributed_index_for_model => "",
51
+ :create_array_accum => true
52
+ )
53
+
54
+ ThinkingSphinx.stub_method :indexed_models => ["Person", "Friendship"]
55
+ YAML.stub_method(:load => {
56
+ :development => {
57
+ "option" => "value"
58
+ }
59
+ })
60
+
61
+ @person_index_a = ThinkingSphinx::Index.stub_instance(
62
+ :to_config => "", :adapter => :mysql, :delta? => false, :name => "person"
63
+ )
64
+ @person_index_b = ThinkingSphinx::Index.stub_instance(
65
+ :to_config => "", :adapter => :mysql, :delta? => false, :name => "person"
66
+ )
67
+ @friendship_index_a = ThinkingSphinx::Index.stub_instance(
68
+ :to_config => "", :adapter => :mysql, :delta? => false, :name => "friendship"
69
+ )
70
+
71
+ Person.stub_method(:indexes => [@person_index_a, @person_index_b])
72
+ Friendship.stub_method(:indexes => [@friendship_index_a])
73
+
74
+ FileUtils.mkdir_p "#{@config.app_root}/config"
75
+ FileUtils.touch "#{@config.app_root}/config/database.yml"
76
+ end
77
+
78
+ after :each do
79
+ ThinkingSphinx.unstub_method :indexed_models
80
+ YAML.unstub_method :load
81
+
82
+ Person.unstub_method :indexes
83
+ Friendship.unstub_method :indexes
84
+
85
+ FileUtils.rm_rf "#{@config.app_root}/config"
86
+ end
87
+
88
+ it "should load the models" do
89
+ @config.build
90
+
91
+ @config.should have_received(:load_models)
92
+ end
93
+
94
+ it "should load in the database YAML configuration" do
95
+ @config.build
96
+
97
+ YAML.should have_received(:load)
98
+ end
99
+
100
+ it "should set the mem limit based on the configuration" do
101
+ @config.build
102
+
103
+ file = open(@config.config_file) { |f| f.read }
104
+ file.should match(/mem_limit\s+= #{@config.mem_limit}/)
105
+ end
106
+
107
+ it "should use the configuration port" do
108
+ @config.build
109
+
110
+ file = open(@config.config_file) { |f| f.read }
111
+ file.should match(/port\s+= #{@config.port}/)
112
+ end
113
+
114
+ it "should use the configuration's log file locations" do
115
+ @config.build
116
+
117
+ file = open(@config.config_file) { |f| f.read }
118
+ file.should match(/log\s+= #{@config.searchd_log_file}/)
119
+ file.should match(/query_log\s+= #{@config.query_log_file}/)
120
+ end
121
+
122
+ it "should use the configuration's pid file location" do
123
+ @config.build
124
+
125
+ file = open(@config.config_file) { |f| f.read }
126
+ file.should match(/pid_file\s+= #{@config.pid_file}/)
127
+ end
128
+
129
+ it "should set max matches from configuration" do
130
+ @config.build
131
+
132
+ file = open(@config.config_file) { |f| f.read }
133
+ file.should match(/max_matches\s+= #{@config.max_matches}/)
134
+ end
135
+
136
+ it "should request configuration for each index for each model" do
137
+ @config.build
138
+
139
+ @person_index_a.should have_received(:to_config).with(
140
+ 0, {:option => "value"}, @config.charset_type
141
+ )
142
+ @person_index_b.should have_received(:to_config).with(
143
+ 1, {:option => "value"}, @config.charset_type
144
+ )
145
+ @friendship_index_a.should have_received(:to_config).with(
146
+ 0, {:option => "value"}, @config.charset_type
147
+ )
148
+ end
149
+
150
+ it "should call create_array_accum if any index uses postgres" do
151
+ @person_index_a.stub_method(:adapter => :postgres)
152
+
153
+ @config.build
154
+
155
+ @config.should have_received(:create_array_accum)
156
+ end
157
+
158
+ it "should not call create_array_accum if no index uses postgres" do
159
+ @config.build
160
+
161
+ @config.should_not have_received(:create_array_accum)
162
+ end
163
+
164
+ it "should call core_index_for_model for each model" do
165
+ @config.build
166
+
167
+ @config.should have_received(:core_index_for_model).with(
168
+ Person, "source = person_0_core\nsource = person_1_core"
169
+ )
170
+ @config.should have_received(:core_index_for_model).with(
171
+ Friendship, "source = friendship_0_core"
172
+ )
173
+ end
174
+
175
+ it "should call delta_index_for_model for each model if any index has a delta" do
176
+ @person_index_b.stub_method(:delta? => true)
177
+
178
+ @config.build
179
+
180
+ @config.should have_received(:delta_index_for_model).with(
181
+ Person, "source = person_1_delta"
182
+ )
183
+ end
184
+
185
+ it "should not call delta_index_for_model for each model if no indexes have deltas" do
186
+ @config.build
187
+
188
+ @config.should_not have_received(:delta_index_for_model)
189
+ end
190
+
191
+ it "should call distributed_index_for_model for each model" do
192
+ @config.build
193
+
194
+ @config.should have_received(:distributed_index_for_model).with(Person)
195
+ @config.should have_received(:distributed_index_for_model).with(Friendship)
196
+ end
197
+ end
198
+
199
+ describe "load_models method" do
200
+ it "should have some specs"
201
+ end
202
+
203
+ describe "parse_config method" do
204
+ before :each do
205
+ @settings = {
206
+ "development" => {
207
+ "config_file" => "my_conf_file.conf",
208
+ "searchd_log_file" => "searchd_log_file.log",
209
+ "query_log_file" => "query_log_file.log",
210
+ "pid_file" => "pid_file.pid",
211
+ "searchd_file_path" => "searchd/file/path",
212
+ "address" => "127.0.0.1",
213
+ "port" => 3333,
214
+ "allow_star" => true,
215
+ "min_prefix_len" => 2,
216
+ "min_infix_len" => 3,
217
+ "mem_limit" => "128M",
218
+ "max_matches" => 1001,
219
+ "morphology" => "stem_ru",
220
+ "charset_type" => "latin1",
221
+ "charset_table" => "table",
222
+ "ignore_chars" => "e"
223
+ }
224
+ }
225
+ # puts YAML.dump(settings)
226
+ open("#{RAILS_ROOT}/config/sphinx.yml", "w") do |f|
227
+ f.write YAML.dump(@settings)
228
+ end
229
+ end
230
+
231
+ it "should use the accessors to set the configuration values" do
232
+ config = ThinkingSphinx::Configuration.new
233
+ @settings["development"].each do |key, value|
234
+ config.send(key).should == value
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "core_index_for_model method" do
240
+ before :each do
241
+ @config = ThinkingSphinx::Configuration.new
242
+ @model = Class.stub_instance(
243
+ :indexes => [ThinkingSphinx::Index.new(Person)]
244
+ )
245
+ end
246
+
247
+ it "should take its name from the model, with _core appended" do
248
+ @config.send(:core_index_for_model, @model, "my sources").should match(
249
+ /index person_core/
250
+ )
251
+ end
252
+
253
+ it "should set the path to follow the name" do
254
+ @config.searchd_file_path = "/my/file/path"
255
+ @config.send(:core_index_for_model, @model, "my sources").should match(
256
+ /path = \/my\/file\/path\/person_core/
257
+ )
258
+ end
259
+
260
+ it "should include the charset type setting" do
261
+ @config.charset_type = "specchars"
262
+ @config.send(:core_index_for_model, @model, "my sources").should match(
263
+ /charset_type = specchars/
264
+ )
265
+ end
266
+
267
+ it "should include the morphology setting if it isn't blank" do
268
+ @config.morphology = "morph"
269
+ @config.send(:core_index_for_model, @model, "my sources").should match(
270
+ /morphology\s+= morph/
271
+ )
272
+ end
273
+
274
+ it "should not include the morphology setting if it is blank" do
275
+ @config.morphology = nil
276
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
277
+ /morphology\s+=/
278
+ )
279
+
280
+ @config.morphology = ""
281
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
282
+ /morphology\s+=/
283
+ )
284
+ end
285
+
286
+ it "should include the charset_table value if it isn't nil" do
287
+ @config.charset_table = "table_chars"
288
+ @config.send(:core_index_for_model, @model, "my sources").should match(
289
+ /charset_table\s+= table_chars/
290
+ )
291
+ end
292
+
293
+ it "should not set the charset_table value if it is nil" do
294
+ @config.charset_table = nil
295
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
296
+ /charset_table\s+=/
297
+ )
298
+ end
299
+
300
+ it "should set the ignore_chars value if it isn't nil" do
301
+ @config.ignore_chars = "ignorable"
302
+ @config.send(:core_index_for_model, @model, "my sources").should match(
303
+ /ignore_chars\s+= ignorable/
304
+ )
305
+ end
306
+
307
+ it "should not set the ignore_chars value if it is nil" do
308
+ @config.ignore_chars = nil
309
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
310
+ /ignore_chars\s+=/
311
+ )
312
+ end
313
+
314
+ it "should include the star-related settings when allow_star is true" do
315
+ @config.allow_star = true
316
+ @config.min_prefix_len = 1
317
+ text = @config.send(:core_index_for_model, @model, "my sources")
318
+
319
+ text.should match(/enable_star\s+= 1/)
320
+ text.should match(/min_prefix_len\s+= 1/)
321
+ # text.should match(/min_infix_len\s+= 1/)
322
+ end
323
+
324
+ it "should use the configuration's infix and prefix length values if set" do
325
+ @config.allow_star = true
326
+ @config.min_prefix_len = 3
327
+ @config.min_infix_len = 2
328
+ text = @config.send(:core_index_for_model, @model, "my sources")
329
+
330
+ text.should match(/min_prefix_len\s+= 3/)
331
+ # text.should match(/min_infix_len\s+= 2/)
332
+ end
333
+
334
+ it "should not include the star-related settings when allow_star is false" do
335
+ @config.allow_star = false
336
+ text = @config.send(:core_index_for_model, @model, "my sources")
337
+
338
+ text.should_not match(/enable_star\s+=/)
339
+ text.should_not match(/min_prefix_len\s+=/)
340
+ text.should_not match(/min_infix_len\s+=/)
341
+ end
342
+
343
+ it "should set prefix_fields if any fields are flagged explicitly" do
344
+ @model.indexes.first.stub_methods(
345
+ :prefix_fields => [
346
+ ThinkingSphinx::Field.stub_instance(:unique_name => "a"),
347
+ ThinkingSphinx::Field.stub_instance(:unique_name => "b"),
348
+ ThinkingSphinx::Field.stub_instance(:unique_name => "c")
349
+ ],
350
+ :infix_fields => [
351
+ ThinkingSphinx::Field.stub_instance(:unique_name => "d"),
352
+ ThinkingSphinx::Field.stub_instance(:unique_name => "e"),
353
+ ThinkingSphinx::Field.stub_instance(:unique_name => "f")
354
+ ]
355
+ )
356
+
357
+ @config.send(:core_index_for_model, @model, "my sources").should match(
358
+ /prefix_fields\s+= a, b, c/
359
+ )
360
+ end
361
+
362
+ it "shouldn't set prefix_fields if none are flagged explicitly" do
363
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
364
+ /prefix_fields\s+=/
365
+ )
366
+ end
367
+
368
+ it "should set infix_fields if any fields are flagged explicitly" do
369
+ @model.indexes.first.stub_methods(
370
+ :prefix_fields => [
371
+ ThinkingSphinx::Field.stub_instance(:unique_name => "a"),
372
+ ThinkingSphinx::Field.stub_instance(:unique_name => "b"),
373
+ ThinkingSphinx::Field.stub_instance(:unique_name => "c")
374
+ ],
375
+ :infix_fields => [
376
+ ThinkingSphinx::Field.stub_instance(:unique_name => "d"),
377
+ ThinkingSphinx::Field.stub_instance(:unique_name => "e"),
378
+ ThinkingSphinx::Field.stub_instance(:unique_name => "f")
379
+ ]
380
+ )
381
+
382
+ @config.send(:core_index_for_model, @model, "my sources").should match(
383
+ /infix_fields\s+= d, e, f/
384
+ )
385
+ end
386
+
387
+ it "shouldn't set infix_fields if none are flagged explicitly" do
388
+ @config.send(:core_index_for_model, @model, "my sources").should_not match(
389
+ /infix_fields\s+=/
390
+ )
391
+ end
392
+
393
+ it "should include html_strip if value is set" do
394
+ @config.html_strip = 1
395
+ text = @config.send(:core_index_for_model, @model, "my sources")
396
+ text.should match(/html_strip\s+= 1/)
397
+ end
398
+
399
+ it "shouldn't include html_strip if value is not set" do
400
+ text = @config.send(:core_index_for_model, @model, "my sources")
401
+ text.should_not match(/html_strip/)
402
+ end
403
+
404
+ it "should include html_remove_elements if values are set" do
405
+ @config.html_remove_elements = 'script'
406
+ text = @config.send(:core_index_for_model, @model, "my sources")
407
+ text.should match(/html_remove_elements\s+= script/)
408
+ end
409
+
410
+ it "shouldn't include html_remove_elements if no values are set" do
411
+ text = @config.send(:core_index_for_model, @model, "my sources")
412
+ text.should_not match(/html_remove_elements/)
413
+ end
414
+ end
415
+
416
+ describe "delta_index_for_model method" do
417
+ before :each do
418
+ @config = ThinkingSphinx::Configuration.new
419
+ @model = Class.stub_instance(
420
+ :indexes => [ThinkingSphinx::Index.new(Person)]
421
+ )
422
+ end
423
+
424
+ it "should take its name from the model, with _delta appended" do
425
+ @config.send(:delta_index_for_model, @model, "delta_sources").should match(
426
+ /index person_delta/
427
+ )
428
+ end
429
+
430
+ it "should inherit from the equivalent core index" do
431
+ @config.send(:delta_index_for_model, @model, "delta_sources").should match(
432
+ /index person_delta : person_core/
433
+ )
434
+ end
435
+
436
+ it "should set the path to follow the name" do
437
+ @config.searchd_file_path = "/my/file/path"
438
+ @config.send(:delta_index_for_model, @model, "delta_sources").should match(
439
+ /path = \/my\/file\/path\/person_delta/
440
+ )
441
+ end
442
+ end
443
+
444
+ describe "distributed_index_for_model method" do
445
+ before :each do
446
+ @config = ThinkingSphinx::Configuration.new
447
+ @model = Class.stub_instance(
448
+ :indexes => [ThinkingSphinx::Index.new(Person)]
449
+ )
450
+ end
451
+
452
+ it "should take its name from the model" do
453
+ @config.send(:distributed_index_for_model, @model).should match(
454
+ /index person/
455
+ )
456
+ end
457
+
458
+ it "should have a type of distributed" do
459
+ @config.send(:distributed_index_for_model, @model).should match(
460
+ /type = distributed/
461
+ )
462
+ end
463
+
464
+ it "should include the core as a local source" do
465
+ @config.send(:distributed_index_for_model, @model).should match(
466
+ /local = person_core/
467
+ )
468
+ end
469
+
470
+ it "should only include the delta as a local source if an index is flagged to be delta" do
471
+ @config.send(:distributed_index_for_model, @model).should_not match(
472
+ /local = person_delta/
473
+ )
474
+
475
+ @model.indexes.first.stub_method(:delta? => true)
476
+ @config.send(:distributed_index_for_model, @model).should match(
477
+ /local = person_delta/
478
+ )
479
+ end
480
+
481
+ it "should handle namespaced models correctly" do
482
+ Person.stub_method(:name => "Namespaced::Model")
483
+
484
+ @config.send(:distributed_index_for_model, @model).should match(
485
+ /index namespaced_model/
486
+ )
487
+ end
488
+ end
489
+
490
+ describe "create_array_accum method" do
491
+ it "should create the array_accum method on PostgreSQL"
492
+ end
493
+ end