thinking-sphinx 1.3.4 → 1.3.6

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 (55) hide show
  1. data/README.textile +15 -4
  2. data/VERSION +1 -0
  3. data/features/alternate_primary_key.feature +1 -1
  4. data/features/attribute_updates.feature +11 -5
  5. data/features/deleting_instances.feature +3 -0
  6. data/features/searching_by_index.feature +40 -0
  7. data/features/step_definitions/alpha_steps.rb +5 -1
  8. data/features/step_definitions/beta_steps.rb +1 -1
  9. data/features/step_definitions/common_steps.rb +12 -1
  10. data/features/step_definitions/sphinx_steps.rb +8 -4
  11. data/features/support/db/fixtures/tags.rb +1 -1
  12. data/features/support/env.rb +3 -0
  13. data/features/support/models/alpha.rb +11 -0
  14. data/lib/cucumber/thinking_sphinx/internal_world.rb +7 -6
  15. data/lib/thinking_sphinx.rb +40 -31
  16. data/lib/thinking_sphinx/active_record.rb +164 -195
  17. data/lib/thinking_sphinx/active_record/attribute_updates.rb +9 -6
  18. data/lib/thinking_sphinx/configuration.rb +1 -1
  19. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  20. data/lib/thinking_sphinx/index.rb +76 -19
  21. data/lib/thinking_sphinx/index/builder.rb +2 -2
  22. data/lib/thinking_sphinx/search.rb +7 -0
  23. data/lib/thinking_sphinx/search_methods.rb +22 -4
  24. data/lib/thinking_sphinx/source.rb +6 -6
  25. data/lib/thinking_sphinx/source/sql.rb +4 -2
  26. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +4 -6
  27. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +0 -0
  28. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/scopes_spec.rb +0 -0
  29. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record_spec.rb +254 -94
  30. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +0 -0
  31. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +0 -0
  32. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +2 -2
  33. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +0 -0
  34. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +0 -0
  35. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +0 -0
  36. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +0 -0
  37. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +0 -0
  38. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +0 -0
  39. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +10 -0
  40. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +0 -0
  41. data/spec/thinking_sphinx/index_spec.rb +177 -0
  42. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +0 -0
  43. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +0 -0
  44. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +33 -0
  45. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +0 -0
  46. data/spec/{lib/thinking_sphinx_spec.rb → thinking_sphinx_spec.rb} +62 -50
  47. data/tasks/distribution.rb +3 -3
  48. metadata +27 -31
  49. data/VERSION.yml +0 -5
  50. data/features/support/db/active_record.rb +0 -40
  51. data/features/support/db/database.yml +0 -5
  52. data/features/support/db/mysql.rb +0 -3
  53. data/features/support/db/postgresql.rb +0 -3
  54. data/features/support/post_database.rb +0 -43
  55. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
@@ -10,22 +10,25 @@ module ThinkingSphinx
10
10
  private
11
11
 
12
12
  def update_attribute_values
13
- return unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.sphinx_running?
13
+ return true unless ThinkingSphinx.updates_enabled? &&
14
+ ThinkingSphinx.sphinx_running?
14
15
 
15
16
  config = ThinkingSphinx::Configuration.instance
16
- client = Riddle::Client.new config.address, config.port
17
+ client = config.client
17
18
 
18
- self.sphinx_indexes.each do |index|
19
+ self.class.sphinx_indexes.each do |index|
19
20
  attribute_pairs = attribute_values_for_index(index)
20
21
  attribute_names = attribute_pairs.keys
21
22
  attribute_values = attribute_names.collect { |key|
22
23
  attribute_pairs[key]
23
24
  }
24
25
 
25
- client.update "#{index.name}_core", attribute_names, {
26
+ client.update "#{index.core_name}", attribute_names, {
26
27
  sphinx_document_id => attribute_values
27
- } if in_core_index?
28
+ } if self.class.search_for_id(sphinx_document_id, index.core_name)
28
29
  end
30
+
31
+ true
29
32
  end
30
33
 
31
34
  def updatable_attributes(index)
@@ -45,4 +48,4 @@ module ThinkingSphinx
45
48
  end
46
49
  end
47
50
  end
48
- end
51
+ end
@@ -124,7 +124,7 @@ module ThinkingSphinx
124
124
  end
125
125
 
126
126
  def self.environment
127
- @@environment ||= (
127
+ Thread.current[:thinking_sphinx_environment] ||= (
128
128
  defined?(Merb) ? Merb.environment : ENV['RAILS_ENV']
129
129
  ) || "development"
130
130
  end
@@ -13,18 +13,8 @@ module ThinkingSphinx
13
13
  ThinkingSphinx.deltas_enabled?
14
14
  return true if instance && !toggled(instance)
15
15
 
16
- config = ThinkingSphinx::Configuration.instance
17
- client = Riddle::Client.new config.address, config.port
18
- rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
19
-
20
- output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
21
- puts(output) unless ThinkingSphinx.suppress_delta_output?
22
-
23
- client.update(
24
- core_index_name(model),
25
- ['sphinx_deleted'],
26
- {instance.sphinx_document_id => [1]}
27
- ) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
16
+ update_delta_indexes model
17
+ delete_from_core model, instance if instance
28
18
 
29
19
  true
30
20
  end
@@ -48,17 +38,21 @@ module ThinkingSphinx
48
38
  " = #{adapter.boolean(toggled)}"
49
39
  end
50
40
 
51
- protected
41
+ private
52
42
 
53
- def core_index_name(model)
54
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
43
+ def update_delta_indexes(model)
44
+ config = ThinkingSphinx::Configuration.instance
45
+ rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
46
+
47
+ output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{model.delta_index_names.join(' ')}`
48
+ puts(output) unless ThinkingSphinx.suppress_delta_output?
55
49
  end
56
50
 
57
- def delta_index_name(model)
58
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
59
- end
60
-
61
- private
51
+ def delete_from_core(model, instance)
52
+ model.core_index_names.each do |index_name|
53
+ model.delete_in_index index_name, instance.sphinx_document_id
54
+ end
55
+ end
62
56
 
63
57
  def adapter
64
58
  @adapter = @index.model.sphinx_database_adapter
@@ -2,14 +2,8 @@ require 'thinking_sphinx/index/builder'
2
2
  require 'thinking_sphinx/index/faux_column'
3
3
 
4
4
  module ThinkingSphinx
5
- # The Index class is a ruby representation of a Sphinx source (not a Sphinx
6
- # index - yes, I know it's a little confusing. You'll manage). This is
7
- # another 'internal' Thinking Sphinx class - if you're using it directly,
8
- # you either know what you're doing, or messing with things beyond your ken.
9
- # Enjoy.
10
- #
11
5
  class Index
12
- attr_accessor :model, :sources, :delta_object
6
+ attr_accessor :name, :model, :sources, :delta_object
13
7
 
14
8
  # Create a new index instance by passing in the model it is tied to, and
15
9
  # a block to build it with (optional but recommended). For documentation
@@ -26,6 +20,7 @@ module ThinkingSphinx
26
20
  # end
27
21
  #
28
22
  def initialize(model, &block)
23
+ @name = self.class.name_for model
29
24
  @model = model
30
25
  @sources = []
31
26
  @options = {}
@@ -40,8 +35,19 @@ module ThinkingSphinx
40
35
  @sources.collect { |source| source.attributes }.flatten
41
36
  end
42
37
 
43
- def name
44
- self.class.name_for @model
38
+ def core_name
39
+ "#{name}_core"
40
+ end
41
+
42
+ def delta_name
43
+ "#{name}_delta"
44
+ end
45
+
46
+ def all_names
47
+ names = [core_name]
48
+ names << delta_name if delta?
49
+
50
+ names
45
51
  end
46
52
 
47
53
  def self.name_for(model)
@@ -61,7 +67,7 @@ module ThinkingSphinx
61
67
  end
62
68
 
63
69
  def options
64
- all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
70
+ all_index_options = config.index_options.clone
65
71
  @options.keys.select { |key|
66
72
  ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) ||
67
73
  ThinkingSphinx::Configuration::CustomOptions.include?(key.to_s)
@@ -73,6 +79,12 @@ module ThinkingSphinx
73
79
  !@delta_object.nil?
74
80
  end
75
81
 
82
+ def to_riddle(offset)
83
+ indexes = [to_riddle_for_core(offset)]
84
+ indexes << to_riddle_for_delta(offset) if delta?
85
+ indexes << to_riddle_for_distributed
86
+ end
87
+
76
88
  private
77
89
 
78
90
  def adapter
@@ -83,17 +95,62 @@ module ThinkingSphinx
83
95
  options[:charset_type] == "utf-8"
84
96
  end
85
97
 
86
- # Does all the magic with the block provided to the base #initialize.
87
- # Creates a new class subclassed from Builder, and evaluates the block
88
- # on it, then pulls all relevant settings - fields, attributes, conditions,
89
- # properties - into the new index.
90
- #
91
- def initialize_from_builder(&block)
92
- #
93
- end
94
-
95
98
  def sql_query_pre_for_delta
96
99
  [""]
97
100
  end
101
+
102
+ def config
103
+ @config ||= ThinkingSphinx::Configuration.instance
104
+ end
105
+
106
+ def to_riddle_for_core(offset)
107
+ index = Riddle::Configuration::Index.new core_name
108
+ index.path = File.join config.searchd_file_path, index.name
109
+
110
+ set_configuration_options_for_indexes index
111
+ set_field_settings_for_indexes index
112
+
113
+ sources.each_with_index do |source, i|
114
+ index.sources << source.to_riddle_for_core(offset, i)
115
+ end
116
+
117
+ index
118
+ end
119
+
120
+ def to_riddle_for_delta(offset)
121
+ index = Riddle::Configuration::Index.new delta_name
122
+ index.parent = core_name
123
+ index.path = File.join config.searchd_file_path, index.name
124
+
125
+ sources.each_with_index do |source, i|
126
+ index.sources << source.to_riddle_for_delta(offset, i)
127
+ end
128
+
129
+ index
130
+ end
131
+
132
+ def to_riddle_for_distributed
133
+ index = Riddle::Configuration::DistributedIndex.new name
134
+ index.local_indexes << core_name
135
+ index.local_indexes.unshift delta_name if delta?
136
+ index
137
+ end
138
+
139
+ def set_configuration_options_for_indexes(index)
140
+ config.index_options.each do |key, value|
141
+ index.send("#{key}=".to_sym, value)
142
+ end
143
+
144
+ options.each do |key, value|
145
+ index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
146
+ end
147
+ end
148
+
149
+ def set_field_settings_for_indexes(index)
150
+ field_names = lambda { |field| field.unique_name.to_s }
151
+
152
+ index.prefix_field_names += prefix_fields.collect(&field_names)
153
+ index.infix_field_names += infix_fields.collect(&field_names)
154
+ end
98
155
  end
99
156
  end
@@ -20,9 +20,9 @@ module ThinkingSphinx
20
20
  }
21
21
  }
22
22
 
23
- def self.generate(model, &block)
23
+ def self.generate(model, name = nil, &block)
24
24
  index = ThinkingSphinx::Index.new(model)
25
- model.sphinx_facets ||= []
25
+ index.name = name unless name.nil?
26
26
 
27
27
  Builder.new(index, &block) if block_given?
28
28
 
@@ -155,6 +155,8 @@ module ThinkingSphinx
155
155
  #
156
156
  def total_pages
157
157
  populate
158
+ return 0 if @results[:total].nil?
159
+
158
160
  @total_pages ||= (@results[:total] / per_page.to_f).ceil
159
161
  end
160
162
  # Compatibility with older versions of will_paginate
@@ -166,6 +168,8 @@ module ThinkingSphinx
166
168
  #
167
169
  def total_entries
168
170
  populate
171
+ return 0 if @results[:total_found].nil?
172
+
169
173
  @total_entries ||= @results[:total_found]
170
174
  end
171
175
 
@@ -313,6 +317,9 @@ module ThinkingSphinx
313
317
  # puts "value: #{value.inspect}"
314
318
  client.send("#{key}=", value) if value
315
319
  end
320
+
321
+ # treated non-standard as :select is already used for AR queries
322
+ client.select = options[:sphinx_select] || '*'
316
323
 
317
324
  client.limit = per_page
318
325
  client.offset = offset
@@ -155,7 +155,7 @@ module ThinkingSphinx
155
155
  # asterisks. You need to make the config/sphinx.yml changes yourself.
156
156
  #
157
157
  # By default, the tokens are assumed to match the regular expression
158
- # /\w+/u. If you've modified the charset_table, pass another regular
158
+ # /\w\+/u\+. If you've modified the charset_table, pass another regular
159
159
  # expression, e.g.
160
160
  #
161
161
  # User.search("oo@bar.c", :star => /[\w@.]+/u)
@@ -313,13 +313,31 @@ module ThinkingSphinx
313
313
  # Once you've got your results set, you can access the distances as
314
314
  # follows:
315
315
  #
316
- # @results.each_with_geodist do |result, distance|
317
- # # ...
318
- # end
316
+ # @results.each_with_geodist do |result, distance|
317
+ # # ...
318
+ # end
319
319
  #
320
320
  # The distance value is returned as a float, representing the distance in
321
321
  # metres.
322
322
  #
323
+ # == Filtering by custom attributes
324
+ #
325
+ # Do note that this applies only to sphinx 0.9.9
326
+ #
327
+ # Should you find yourself in desperate need of a filter that involves
328
+ # selecting either one of multiple conditions, one solution could be
329
+ # provided by the :sphinx_select option within the search.
330
+ # This handles which fields are selected by sphinx from its store.
331
+ #
332
+ # The default value is "*", and you can add custom fields using syntax
333
+ # similar to sql:
334
+ #
335
+ # Flower.search "foo",
336
+ # :sphinx_select => "*, petals < 1 or color = 2 as grass"
337
+ #
338
+ # This will add the 'grass' attribute, which will now be usable in your
339
+ # filters.
340
+ #
323
341
  # == Handling a Stale Index
324
342
  #
325
343
  # Especially if you don't use delta indexing, you risk having records in
@@ -33,12 +33,12 @@ module ThinkingSphinx
33
33
  end
34
34
 
35
35
  def name
36
- @model.sphinx_name
36
+ index.name
37
37
  end
38
38
 
39
- def to_riddle_for_core(offset, index)
39
+ def to_riddle_for_core(offset, position)
40
40
  source = Riddle::Configuration::SQLSource.new(
41
- "#{name}_core_#{index}", adapter.sphinx_identifier
41
+ "#{index.core_name}_#{position}", adapter.sphinx_identifier
42
42
  )
43
43
 
44
44
  set_source_database_settings source
@@ -49,11 +49,11 @@ module ThinkingSphinx
49
49
  source
50
50
  end
51
51
 
52
- def to_riddle_for_delta(offset, index)
52
+ def to_riddle_for_delta(offset, position)
53
53
  source = Riddle::Configuration::SQLSource.new(
54
- "#{name}_delta_#{index}", adapter.sphinx_identifier
54
+ "#{index.delta_name}_#{position}", adapter.sphinx_identifier
55
55
  )
56
- source.parent = "#{name}_core_#{index}"
56
+ source.parent = "#{index.core_name}_#{position}"
57
57
 
58
58
  set_source_database_settings source
59
59
  set_source_attributes source, offset, true
@@ -13,8 +13,10 @@ module ThinkingSphinx
13
13
  # source.to_sql(:delta => true)
14
14
  #
15
15
  def to_sql(options={})
16
- sql = <<-SQL
17
- SELECT #{ sql_select_clause options[:offset] }
16
+ sql = "SELECT "
17
+ sql += "SQL_NO_CACHE " if adapter.sphinx_identifier == "mysql"
18
+ sql += <<-SQL
19
+ #{ sql_select_clause options[:offset] }
18
20
  FROM #{ @model.quoted_table_name }
19
21
  #{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
20
22
  #{ sql_where_clause(options) }
@@ -73,14 +73,12 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
73
73
  Person.delta_object.stub!(:` => "", :toggled => true)
74
74
 
75
75
  @person = Person.new
76
- @person.stub!(
77
- :in_both_indexes? => false,
78
- :sphinx_document_id => 1
79
- )
76
+ Person.stub!(:search_for_id => false)
77
+ @person.stub!(:sphinx_document_id => 1)
80
78
 
81
79
  @client = Riddle::Client.new
82
80
  @client.stub!(:update => true)
83
- Riddle::Client.stub!(:new => @client)
81
+ ThinkingSphinx::Configuration.instance.stub!(:client => @client)
84
82
  end
85
83
 
86
84
  it "shouldn't index if delta indexing is disabled" do
@@ -121,7 +119,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
121
119
  end
122
120
 
123
121
  it "should update the deleted attribute if in the core index" do
124
- @person.stub!(:in_both_indexes? => true)
122
+ Person.stub!(:search_for_id => true)
125
123
  @client.should_receive(:update)
126
124
 
127
125
  @person.send(:index_delta)
@@ -1,102 +1,59 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
3
  describe ThinkingSphinx::ActiveRecord do
4
- describe '.define_index' do
5
- before :each do
6
- module ::TestModule
7
- class TestModel < ActiveRecord::Base; end
8
- end
9
-
10
- TestModule::TestModel.stub!(
11
- :before_save => true,
12
- :after_commit => true,
13
- :after_destroy => true
14
- )
15
-
16
- @index = ThinkingSphinx::Index.new(TestModule::TestModel)
17
- @index.stub!(:delta? => false)
18
- ThinkingSphinx::Index::Builder.stub!(:generate => @index)
19
- end
20
-
21
- after :each do
22
- # Remove the class so we can redefine it
23
- TestModule.send(:remove_const, :TestModel)
24
-
25
- ThinkingSphinx.indexed_models.delete "TestModule::TestModel"
26
- end
4
+ before :each do
5
+ @existing_alpha_indexes = Alpha.sphinx_indexes.clone
6
+ @existing_beta_indexes = Beta.sphinx_indexes.clone
27
7
 
8
+ Alpha.sphinx_indexes.clear
9
+ Beta.sphinx_indexes.clear
10
+ end
11
+
12
+ after :each do
13
+ Alpha.sphinx_indexes.replace @existing_alpha_indexes
14
+ Beta.sphinx_indexes.replace @existing_beta_indexes
15
+ end
16
+
17
+ describe '.define_index' do
28
18
  it "should do nothing if indexes are disabled" do
29
19
  ThinkingSphinx.define_indexes = false
30
20
  ThinkingSphinx::Index.should_not_receive(:new)
31
21
 
32
- TestModule::TestModel.define_index {}
22
+ Alpha.define_index { }
33
23
 
34
24
  ThinkingSphinx.define_indexes = true
35
25
  end
36
26
 
37
27
  it "should add a new index to the model" do
38
- TestModule::TestModel.define_index {}
28
+ index = Alpha.define_index { indexes :name }
39
29
 
40
- TestModule::TestModel.sphinx_indexes.length.should == 1
30
+ Alpha.sphinx_indexes.should include(index)
41
31
  end
42
32
 
43
33
  it "should add to ThinkingSphinx.indexed_models if the model doesn't already exist in the array" do
44
- TestModule::TestModel.define_index do; end
34
+ Alpha.define_index { indexes :name }
45
35
 
46
- ThinkingSphinx.indexed_models.should include("TestModule::TestModel")
36
+ ThinkingSphinx.indexed_models.should include("Alpha")
47
37
  end
48
38
 
49
39
  it "shouldn't add to ThinkingSphinx.indexed_models if the model already exists in the array" do
50
- TestModule::TestModel.define_index do; end
40
+ Alpha.define_index { indexes :name }
41
+ Alpha.define_index { indexes :name }
51
42
 
52
43
  ThinkingSphinx.indexed_models.select { |model|
53
- model == "TestModule::TestModel"
44
+ model == "Alpha"
54
45
  }.length.should == 1
55
-
56
- TestModule::TestModel.define_index do; end
57
-
58
- ThinkingSphinx.indexed_models.select { |model|
59
- model == "TestModule::TestModel"
60
- }.length.should == 1
61
- end
62
-
63
- it "should add before_save and after_commit hooks to the model if delta indexing is enabled" do
64
- @index.stub!(:delta? => true)
65
- TestModule::TestModel.should_receive(:before_save).with(:toggle_delta)
66
- TestModule::TestModel.should_receive(:after_commit).with(:index_delta)
67
-
68
- TestModule::TestModel.define_index do; end
69
- end
70
-
71
- it "should not add before_save and after_commit hooks to the model if delta indexing is disabled" do
72
- TestModule::TestModel.should_not_receive(:before_save).with(:toggle_delta)
73
- TestModule::TestModel.should_not_receive(:after_commit).with(:index_delta)
74
-
75
- TestModule::TestModel.define_index do; end
76
- end
77
-
78
- it "should add an after_destroy hook with delta indexing enabled" do
79
- @index.stub!(:delta? => true)
80
- TestModule::TestModel.should_receive(:after_destroy).with(:toggle_deleted)
81
-
82
- TestModule::TestModel.define_index do; end
83
- end
84
-
85
- it "should add an after_destroy hook with delta indexing disabled" do
86
- TestModule::TestModel.should_receive(:after_destroy).with(:toggle_deleted)
87
-
88
- TestModule::TestModel.define_index do; end
89
46
  end
90
47
 
91
48
  it "should return the new index" do
92
- TestModule::TestModel.define_index.should == @index
49
+ Alpha.define_index.should be_a(ThinkingSphinx::Index)
93
50
  end
94
51
 
95
52
  it "should die quietly if there is a database error" do
96
53
  ThinkingSphinx::Index::Builder.stub(:generate) { raise Mysql::Error }
97
54
 
98
55
  lambda {
99
- TestModule::TestModel.define_index
56
+ Alpha.define_index { indexes :name }
100
57
  }.should_not raise_error
101
58
  end
102
59
 
@@ -104,9 +61,122 @@ describe ThinkingSphinx::ActiveRecord do
104
61
  ThinkingSphinx::Index::Builder.stub(:generate) { raise StandardError }
105
62
 
106
63
  lambda {
107
- TestModule::TestModel.define_index
64
+ Alpha.define_index { indexes :name }
108
65
  }.should raise_error
109
66
  end
67
+
68
+ it "should set the index's name using the parameter if provided" do
69
+ index = Alpha.define_index('custom') { indexes :name }
70
+
71
+ index.name.should == 'custom'
72
+ end
73
+
74
+ context 'callbacks' do
75
+ it "should add a toggle_deleted callback" do
76
+ Alpha.should_receive(:after_destroy).with(:toggle_deleted)
77
+
78
+ Alpha.define_index { indexes :name }
79
+ end
80
+
81
+ it "should not add toggle_deleted callback more than once" do
82
+ Alpha.should_receive(:after_destroy).with(:toggle_deleted).once
83
+
84
+ Alpha.define_index { indexes :name }
85
+ Alpha.define_index { indexes :name }
86
+ end
87
+
88
+ it "should add a update_attribute_values callback" do
89
+ Alpha.should_receive(:after_commit).with(:update_attribute_values)
90
+
91
+ Alpha.define_index { indexes :name }
92
+ end
93
+
94
+ it "should not add update_attribute_values callback more than once" do
95
+ Alpha.should_receive(:after_commit).with(:update_attribute_values).once
96
+
97
+ Alpha.define_index { indexes :name }
98
+ Alpha.define_index { indexes :name }
99
+ end
100
+
101
+ it "should add a toggle_delta callback if deltas are enabled" do
102
+ Beta.should_receive(:before_save).with(:toggle_delta)
103
+
104
+ Beta.define_index {
105
+ indexes :name
106
+ set_property :delta => true
107
+ }
108
+ end
109
+
110
+ it "should not add a toggle_delta callback if deltas are disabled" do
111
+ Alpha.should_not_receive(:before_save).with(:toggle_delta)
112
+
113
+ Alpha.define_index { indexes :name }
114
+ end
115
+
116
+ it "should add the toggle_delta callback if deltas are disabled in other indexes" do
117
+ Beta.should_receive(:before_save).with(:toggle_delta).once
118
+
119
+ Beta.define_index { indexes :name }
120
+ Beta.define_index {
121
+ indexes :name
122
+ set_property :delta => true
123
+ }
124
+ end
125
+
126
+ it "should only add the toggle_delta callback once" do
127
+ Beta.should_receive(:before_save).with(:toggle_delta).once
128
+
129
+ Beta.define_index {
130
+ indexes :name
131
+ set_property :delta => true
132
+ }
133
+ Beta.define_index {
134
+ indexes :name
135
+ set_property :delta => true
136
+ }
137
+ end
138
+
139
+ it "should add an index_delta callback if deltas are enabled" do
140
+ Beta.stub!(:after_commit => true)
141
+ Beta.should_receive(:after_commit).with(:index_delta)
142
+
143
+ Beta.define_index {
144
+ indexes :name
145
+ set_property :delta => true
146
+ }
147
+ end
148
+
149
+ it "should not add an index_delta callback if deltas are disabled" do
150
+ Alpha.should_not_receive(:after_commit).with(:index_delta)
151
+
152
+ Alpha.define_index { indexes :name }
153
+ end
154
+
155
+ it "should add the index_delta callback if deltas are disabled in other indexes" do
156
+ Beta.stub!(:after_commit => true)
157
+ Beta.should_receive(:after_commit).with(:index_delta).once
158
+
159
+ Beta.define_index { indexes :name }
160
+ Beta.define_index {
161
+ indexes :name
162
+ set_property :delta => true
163
+ }
164
+ end
165
+
166
+ it "should only add the index_delta callback once" do
167
+ Beta.stub!(:after_commit => true)
168
+ Beta.should_receive(:after_commit).with(:index_delta).once
169
+
170
+ Beta.define_index {
171
+ indexes :name
172
+ set_property :delta => true
173
+ }
174
+ Beta.define_index {
175
+ indexes :name
176
+ set_property :delta => true
177
+ }
178
+ end
179
+ end
110
180
  end
111
181
 
112
182
  describe "index methods" do
@@ -188,17 +258,9 @@ describe ThinkingSphinx::ActiveRecord do
188
258
  @client.stub!(:update => true)
189
259
  @person = Person.find(:first)
190
260
 
191
- Riddle::Client.stub!(:new => @client)
261
+ @configuration.stub!(:client => @client)
192
262
  Person.sphinx_indexes.each { |index| index.stub!(:delta? => false) }
193
- @person.stub!(:in_core_index? => true)
194
- end
195
-
196
- it "should create a client using the Configuration's address and port" do
197
- Riddle::Client.should_receive(:new).with(
198
- @configuration.address, @configuration.port
199
- )
200
-
201
- @person.toggle_deleted
263
+ Person.stub!(:search_for_id => true)
202
264
  end
203
265
 
204
266
  it "should update the core index's deleted flag if in core index" do
@@ -210,7 +272,7 @@ describe ThinkingSphinx::ActiveRecord do
210
272
  end
211
273
 
212
274
  it "shouldn't update the core index's deleted flag if the record isn't in it" do
213
- @person.stub!(:in_core_index? => false)
275
+ Person.stub!(:search_for_id => false)
214
276
  @client.should_not_receive(:update).with(
215
277
  "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => [1]}
216
278
  )
@@ -221,7 +283,7 @@ describe ThinkingSphinx::ActiveRecord do
221
283
  it "shouldn't attempt to update the deleted flag if sphinx isn't running" do
222
284
  ThinkingSphinx.stub!(:sphinx_running? => false)
223
285
  @client.should_not_receive(:update)
224
- @person.should_not_receive(:in_core_index?)
286
+ Person.should_not_receive(:search_for_id)
225
287
 
226
288
  @person.toggle_deleted
227
289
  end
@@ -294,22 +356,29 @@ describe ThinkingSphinx::ActiveRecord do
294
356
  end
295
357
  end
296
358
 
297
- it "should return the sphinx document id as expected" do
298
- person = Person.find(:first)
299
- model_count = ThinkingSphinx.indexed_models.length
300
- offset = ThinkingSphinx.indexed_models.index("Person")
301
-
302
- (person.id * model_count + offset).should == person.sphinx_document_id
303
-
304
- alpha = Alpha.find(:first)
305
- offset = ThinkingSphinx.indexed_models.index("Alpha")
306
-
307
- (alpha.id * model_count + offset).should == alpha.sphinx_document_id
308
-
309
- beta = Beta.find(:first)
310
- offset = ThinkingSphinx.indexed_models.index("Beta")
359
+ describe '#sphinx_document_id' do
360
+ before :each do
361
+ Alpha.define_index { indexes :name }
362
+ Beta.define_index { indexes :name }
363
+ end
311
364
 
312
- (beta.id * model_count + offset).should == beta.sphinx_document_id
365
+ it "should return values with the expected offset" do
366
+ person = Person.find(:first)
367
+ model_count = ThinkingSphinx.indexed_models.length
368
+ offset = ThinkingSphinx.indexed_models.index("Person")
369
+
370
+ (person.id * model_count + offset).should == person.sphinx_document_id
371
+
372
+ alpha = Alpha.find(:first)
373
+ offset = ThinkingSphinx.indexed_models.index("Alpha")
374
+
375
+ (alpha.id * model_count + offset).should == alpha.sphinx_document_id
376
+
377
+ beta = Beta.find(:first)
378
+ offset = ThinkingSphinx.indexed_models.index("Beta")
379
+
380
+ (beta.id * model_count + offset).should == beta.sphinx_document_id
381
+ end
313
382
  end
314
383
 
315
384
  describe '#primary_key_for_sphinx' do
@@ -339,10 +408,16 @@ describe ThinkingSphinx::ActiveRecord do
339
408
 
340
409
  describe '.sphinx_index_names' do
341
410
  it "should return the core index" do
411
+ Alpha.define_index { indexes :name }
342
412
  Alpha.sphinx_index_names.should == ['alpha_core']
343
413
  end
344
414
 
345
415
  it "should return the delta index if enabled" do
416
+ Beta.define_index {
417
+ indexes :name
418
+ set_property :delta => true
419
+ }
420
+
346
421
  Beta.sphinx_index_names.should == ['beta_core', 'beta_delta']
347
422
  end
348
423
 
@@ -350,4 +425,89 @@ describe ThinkingSphinx::ActiveRecord do
350
425
  Parent.sphinx_index_names.should == ['person_core', 'person_delta']
351
426
  end
352
427
  end
428
+
429
+ describe '.indexed_by_sphinx?' do
430
+ it "should return true if there is at least one index on the model" do
431
+ Alpha.define_index { indexes :name }
432
+
433
+ Alpha.should be_indexed_by_sphinx
434
+ end
435
+
436
+ it "should return false if there are no indexes on the model" do
437
+ Gamma.should_not be_indexed_by_sphinx
438
+ end
439
+ end
440
+
441
+ describe '.delta_indexed_by_sphinx?' do
442
+ it "should return true if there is at least one delta index on the model" do
443
+ Beta.define_index {
444
+ indexes :name
445
+ set_property :delta => true
446
+ }
447
+
448
+ Beta.should be_delta_indexed_by_sphinx
449
+ end
450
+
451
+ it "should return false if there are no delta indexes on the model" do
452
+ Alpha.define_index { indexes :name }
453
+
454
+ Alpha.should_not be_delta_indexed_by_sphinx
455
+ end
456
+ end
457
+
458
+ describe '.delete_in_index' do
459
+ before :each do
460
+ @client = stub('client')
461
+ ThinkingSphinx.stub!(:sphinx_running? => true)
462
+ ThinkingSphinx::Configuration.instance.stub!(:client => @client)
463
+ Alpha.stub!(:search_for_id => true)
464
+ end
465
+
466
+ it "should not update if the document isn't in the given index" do
467
+ Alpha.stub!(:search_for_id => false)
468
+ @client.should_not_receive(:update)
469
+
470
+ Alpha.delete_in_index('alpha_core', 42)
471
+ end
472
+
473
+ it "should direct the update to the supplied index" do
474
+ @client.should_receive(:update) do |index, attributes, values|
475
+ index.should == 'custom_index_core'
476
+ end
477
+
478
+ Alpha.delete_in_index('custom_index_core', 42)
479
+ end
480
+
481
+ it "should set the sphinx_deleted flag to true" do
482
+ @client.should_receive(:update) do |index, attributes, values|
483
+ attributes.should == ['sphinx_deleted']
484
+ values.should == {42 => [1]}
485
+ end
486
+
487
+ Alpha.delete_in_index('alpha_core', 42)
488
+ end
489
+ end
490
+
491
+ describe '.core_index_names' do
492
+ it "should return each index's core name" do
493
+ foo = Alpha.define_index { indexes :name }
494
+ foo.name = 'foo'
495
+ bar = Alpha.define_index { indexes :name }
496
+ bar.name = 'bar'
497
+
498
+ Alpha.core_index_names.should == ['foo_core', 'bar_core']
499
+ end
500
+ end
501
+
502
+ describe '.delta_index_names' do
503
+ it "should return index delta names, for indexes with deltas enabled" do
504
+ foo = Alpha.define_index { indexes :name }
505
+ foo.name = 'foo'
506
+ foo.delta_object = stub('delta')
507
+ bar = Alpha.define_index { indexes :name }
508
+ bar.name = 'bar'
509
+
510
+ Alpha.delta_index_names.should == ['foo_delta']
511
+ end
512
+ end
353
513
  end