thinking-sphinx 3.0.6 → 3.1.0

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -6
  3. data/Appraisals +5 -5
  4. data/Gemfile +3 -3
  5. data/HISTORY +32 -0
  6. data/README.textile +16 -5
  7. data/gemfiles/rails_3_2.gemfile +2 -1
  8. data/gemfiles/rails_4_0.gemfile +3 -2
  9. data/gemfiles/{rails_3_1.gemfile → rails_4_1.gemfile} +3 -2
  10. data/lib/thinking_sphinx.rb +5 -0
  11. data/lib/thinking_sphinx/active_record.rb +2 -1
  12. data/lib/thinking_sphinx/active_record/association_proxy/attribute_finder.rb +1 -1
  13. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +6 -7
  14. data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +2 -2
  15. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +1 -1
  16. data/lib/thinking_sphinx/active_record/column_sql_presenter.rb +34 -0
  17. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
  18. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
  19. data/lib/thinking_sphinx/active_record/index.rb +2 -1
  20. data/lib/thinking_sphinx/active_record/interpreter.rb +7 -0
  21. data/lib/thinking_sphinx/active_record/property.rb +1 -1
  22. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +10 -16
  23. data/lib/thinking_sphinx/active_record/sql_builder.rb +7 -1
  24. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +0 -7
  25. data/lib/thinking_sphinx/active_record/sql_source.rb +20 -20
  26. data/lib/thinking_sphinx/active_record/sql_source/template.rb +1 -1
  27. data/lib/thinking_sphinx/capistrano.rb +6 -65
  28. data/lib/thinking_sphinx/capistrano/v2.rb +58 -0
  29. data/lib/thinking_sphinx/capistrano/v3.rb +101 -0
  30. data/lib/thinking_sphinx/configuration.rb +8 -3
  31. data/lib/thinking_sphinx/configuration/distributed_indices.rb +29 -0
  32. data/lib/thinking_sphinx/connection.rb +90 -34
  33. data/lib/thinking_sphinx/controller.rb +20 -0
  34. data/lib/thinking_sphinx/core/index.rb +4 -0
  35. data/lib/thinking_sphinx/deletion.rb +15 -11
  36. data/lib/thinking_sphinx/deltas.rb +9 -0
  37. data/lib/thinking_sphinx/deltas/default_delta.rb +2 -0
  38. data/lib/thinking_sphinx/distributed.rb +5 -0
  39. data/lib/thinking_sphinx/distributed/index.rb +24 -0
  40. data/lib/thinking_sphinx/excerpter.rb +7 -3
  41. data/lib/thinking_sphinx/facet_search.rb +1 -1
  42. data/lib/thinking_sphinx/index.rb +2 -6
  43. data/lib/thinking_sphinx/index_set.rb +10 -8
  44. data/lib/thinking_sphinx/middlewares.rb +0 -2
  45. data/lib/thinking_sphinx/middlewares/active_record_translator.rb +1 -0
  46. data/lib/thinking_sphinx/middlewares/geographer.rb +1 -1
  47. data/lib/thinking_sphinx/middlewares/sphinxql.rb +8 -6
  48. data/lib/thinking_sphinx/middlewares/utf8.rb +6 -1
  49. data/lib/thinking_sphinx/query.rb +9 -0
  50. data/lib/thinking_sphinx/railtie.rb +0 -13
  51. data/lib/thinking_sphinx/search/query.rb +3 -21
  52. data/lib/thinking_sphinx/sphinxql.rb +1 -1
  53. data/lib/thinking_sphinx/wildcard.rb +34 -0
  54. data/spec/acceptance/geosearching_spec.rb +13 -0
  55. data/spec/acceptance/indexing_spec.rb +27 -0
  56. data/spec/acceptance/remove_deleted_records_spec.rb +8 -0
  57. data/spec/acceptance/searching_with_sti_spec.rb +7 -0
  58. data/spec/acceptance/searching_within_a_model_spec.rb +8 -0
  59. data/spec/acceptance/sorting_search_results_spec.rb +1 -1
  60. data/spec/acceptance/spec_helper.rb +13 -0
  61. data/spec/acceptance/specifying_sql_spec.rb +2 -2
  62. data/spec/acceptance/support/sphinx_controller.rb +5 -5
  63. data/spec/acceptance/support/sphinx_helpers.rb +3 -0
  64. data/spec/acceptance/suspended_deltas_spec.rb +34 -0
  65. data/spec/internal/config/database.yml +1 -0
  66. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +13 -6
  67. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +2 -2
  68. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +7 -0
  69. data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +6 -0
  70. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +27 -0
  71. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +18 -7
  72. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +17 -7
  73. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +84 -82
  74. data/spec/thinking_sphinx/configuration_spec.rb +5 -4
  75. data/spec/thinking_sphinx/connection_spec.rb +1 -1
  76. data/spec/thinking_sphinx/deletion_spec.rb +10 -28
  77. data/spec/thinking_sphinx/excerpter_spec.rb +3 -3
  78. data/spec/thinking_sphinx/facet_search_spec.rb +13 -4
  79. data/spec/thinking_sphinx/index_set_spec.rb +9 -4
  80. data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +8 -0
  81. data/spec/thinking_sphinx/middlewares/geographer_spec.rb +7 -7
  82. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +17 -1
  83. data/spec/thinking_sphinx/search/query_spec.rb +10 -53
  84. data/spec/thinking_sphinx/wildcard_spec.rb +41 -0
  85. data/thinking-sphinx.gemspec +6 -5
  86. metadata +38 -14
  87. data/lib/thinking_sphinx/active_record/associations.rb +0 -98
  88. data/spec/thinking_sphinx/active_record/associations_spec.rb +0 -230
@@ -0,0 +1,27 @@
1
+ require 'acceptance/spec_helper'
2
+
3
+ describe 'Indexing', :live => true do
4
+ it "does not index files where the temp file exists" do
5
+ path = Rails.root.join('db/sphinx/test/ts-article_core.tmp')
6
+ FileUtils.mkdir_p Rails.root.join('db/sphinx/test')
7
+ FileUtils.touch path
8
+
9
+ article = Article.create! :title => 'Pancakes'
10
+ index 'article_core'
11
+ Article.search.should be_empty
12
+
13
+ FileUtils.rm path
14
+ end
15
+
16
+ it "indexes files when other indices are already being processed" do
17
+ path = Rails.root.join('db/sphinx/test/ts-book_core.tmp')
18
+ FileUtils.mkdir_p Rails.root.join('db/sphinx/test')
19
+ FileUtils.touch path
20
+
21
+ article = Article.create! :title => 'Pancakes'
22
+ index 'article_core'
23
+ Article.search.should_not be_empty
24
+
25
+ FileUtils.rm path
26
+ end
27
+ end
@@ -30,4 +30,12 @@ describe 'Hiding deleted records from search results', :live => true do
30
30
 
31
31
  Product.search_for_ids('Shiny').should be_empty
32
32
  end
33
+
34
+ it "deletes STI child classes from parent indices" do
35
+ duck = Bird.create :name => 'Duck'
36
+ index
37
+ duck.destroy
38
+
39
+ expect(Bird.search_for_ids('duck')).to be_empty
40
+ end
33
41
  end
@@ -52,4 +52,11 @@ describe 'Searching across STI models', :live => true do
52
52
 
53
53
  Bird.search(nil, :skip_sti => true, :classes=>[Bird, FlightlessBird]).to_a.should == [duck, emu]
54
54
  end
55
+
56
+ it 'finds root objects when type is blank' do
57
+ animal = Animal.create :name => 'Animal', type: ''
58
+ index
59
+
60
+ Animal.search.to_a.should == [animal]
61
+ end
55
62
  end
@@ -41,6 +41,14 @@ describe 'Searching within a model', :live => true do
41
41
  articles.to_a.should == [article]
42
42
  end
43
43
 
44
+ it "allows for searching on distributed indices" do
45
+ article = Article.create :title => 'Pancakes'
46
+ index
47
+
48
+ articles = Article.search('pancake', :indices => ['article'])
49
+ articles.to_a.should == [article]
50
+ end
51
+
44
52
  it "can search on namespaced models" do
45
53
  person = Admin::Person.create :name => 'James Bond'
46
54
  index
@@ -42,7 +42,7 @@ describe 'Sorting search results', :live => true do
42
42
  index
43
43
 
44
44
  Book.search(
45
- :select => 'year MOD 2004 as mod_year', :order => 'mod_year ASC'
45
+ :select => '*, year MOD 2004 as mod_year', :order => 'mod_year ASC'
46
46
  ).to_a.should == [boys, grave, gods]
47
47
  end
48
48
  end
@@ -2,3 +2,16 @@ require 'spec_helper'
2
2
 
3
3
  root = File.expand_path File.dirname(__FILE__)
4
4
  Dir["#{root}/support/**/*.rb"].each { |file| require file }
5
+
6
+ if ENV['SPHINX_VERSION'].try :[], /2.0.\d/
7
+ ThinkingSphinx::SphinxQL.variables!
8
+
9
+ ThinkingSphinx::Middlewares::DEFAULT.insert_after(
10
+ ThinkingSphinx::Middlewares::Inquirer,
11
+ ThinkingSphinx::Middlewares::UTF8
12
+ )
13
+ ThinkingSphinx::Middlewares::RAW_ONLY.insert_after(
14
+ ThinkingSphinx::Middlewares::Inquirer,
15
+ ThinkingSphinx::Middlewares::UTF8
16
+ )
17
+ end
@@ -128,7 +128,7 @@ describe 'specifying SQL for index definitions' do
128
128
  query.should match(/articles\..title., books\..title./)
129
129
  end
130
130
 
131
- it "concatenates references where that have column" do
131
+ it "concatenates references that have column" do
132
132
  index = ThinkingSphinx::ActiveRecord::Index.new(:event)
133
133
  index.definition_block = Proc.new {
134
134
  indexes eventable.title, :as => :title
@@ -138,8 +138,8 @@ describe 'specifying SQL for index definitions' do
138
138
 
139
139
  query = index.sources.first.sql_query
140
140
  query.should match(/LEFT OUTER JOIN .articles. ON .articles.\..id. = .events.\..eventable_id. AND .events.\..eventable_type. = 'Article'/)
141
- query.should match(/LEFT OUTER JOIN .users. ON .users.\..id. = .events.\..eventable_id. AND .events.\..eventable_type. = 'User'/)
142
141
  query.should_not match(/articles\..title., users\..title./)
142
+ query.should match(/articles\..title./)
143
143
  end
144
144
 
145
145
  it "respects deeper associations through polymorphic joins" do
@@ -5,21 +5,21 @@ class SphinxController
5
5
 
6
6
  def setup
7
7
  FileUtils.mkdir_p config.indices_location
8
+ config.controller.bin_path = ENV['SPHINX_BIN'] || ''
8
9
  config.render_to_file && index
9
10
 
10
11
  ThinkingSphinx::Configuration.reset
11
12
 
12
- if ENV['SPHINX_VERSION'].try :[], /2.1.\d/
13
- ThinkingSphinx::SphinxQL.functions!
14
- ThinkingSphinx::Configuration.instance.settings['utf8'] = true
15
- end
16
-
17
13
  ActiveSupport::Dependencies.loaded.each do |path|
18
14
  $LOADED_FEATURES.delete "#{path}.rb"
19
15
  end
20
16
 
21
17
  ActiveSupport::Dependencies.clear
22
18
 
19
+ if ENV['SPHINX_VERSION'].try :[], /2.0.\d/
20
+ ThinkingSphinx::Configuration.instance.settings['utf8'] = false
21
+ end
22
+
23
23
  config.searchd.mysql41 = 9307
24
24
  config.settings['quiet_deltas'] = true
25
25
  config.settings['attribute_updates'] = true
@@ -4,10 +4,13 @@ module SphinxHelpers
4
4
  end
5
5
 
6
6
  def index(*indices)
7
+ sleep 0.5 if ENV['TRAVIS']
8
+
7
9
  yield if block_given?
8
10
 
9
11
  sphinx.index *indices
10
12
  sleep 0.25
13
+ sleep 0.5 if ENV['TRAVIS']
11
14
  end
12
15
  end
13
16
 
@@ -17,4 +17,38 @@ describe 'Suspend deltas for a given action', :live => true do
17
17
  sleep 0.25
18
18
  Book.search('Terry').to_a.should == [book]
19
19
  end
20
+
21
+ it "returns core records even though they are no longer valid" do
22
+ book = Book.create :title => 'Night Watch', :author => 'Harry Pritchett'
23
+ index
24
+
25
+ Book.search('Harry').to_a.should == [book]
26
+
27
+ ThinkingSphinx::Deltas.suspend :book do
28
+ book.reload.update_attributes(:author => 'Terry Pratchett')
29
+ sleep 0.25
30
+
31
+ Book.search('Terry').to_a.should == []
32
+ end
33
+
34
+ sleep 0.25
35
+ Book.search('Harry').to_a.should == [book]
36
+ end
37
+
38
+ it "marks core records as deleted" do
39
+ book = Book.create :title => 'Night Watch', :author => 'Harry Pritchett'
40
+ index
41
+
42
+ Book.search('Harry').to_a.should == [book]
43
+
44
+ ThinkingSphinx::Deltas.suspend_and_update :book do
45
+ book.reload.update_attributes(:author => 'Terry Pratchett')
46
+ sleep 0.25
47
+
48
+ Book.search('Terry').to_a.should == []
49
+ end
50
+
51
+ sleep 0.25
52
+ Book.search('Harry').to_a.should be_empty
53
+ end
20
54
  end
@@ -3,3 +3,4 @@ test:
3
3
  database: thinking_sphinx
4
4
  username: <%= ENV['DATABASE'] == 'postgresql' ? ENV['USER'] : 'root' %>
5
5
  min_messages: warning
6
+ encoding: utf8
@@ -31,18 +31,25 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks do
31
31
  end
32
32
 
33
33
  describe '#after_destroy' do
34
- let(:config) { double('config', :indices_for_references => [index],
35
- :preload_indices => true) }
34
+ let(:index_set) { double 'index set', :to_a => [index] }
36
35
  let(:index) { double('index', :name => 'foo_core',
37
- :document_id_for_key => 14, :type => 'plain') }
38
- let(:instance) { double('instance', :id => 7) }
36
+ :document_id_for_key => 14, :type => 'plain', :distributed? => false) }
37
+ let(:instance) { double('instance', :id => 7, :new_record? => false) }
39
38
 
40
39
  before :each do
41
- ThinkingSphinx::Configuration.stub :instance => config
40
+ ThinkingSphinx::IndexSet.stub :new => index_set
42
41
  end
43
42
 
44
43
  it "performs the deletion for the index and instance" do
45
- ThinkingSphinx::Deletion.should_receive(:perform).with(index, instance)
44
+ ThinkingSphinx::Deletion.should_receive(:perform).with(index, 7)
45
+
46
+ callbacks.after_destroy
47
+ end
48
+
49
+ it "doesn't do anything if the instance is a new record" do
50
+ instance.stub :new_record? => true
51
+
52
+ ThinkingSphinx::Deletion.should_not_receive(:perform)
46
53
 
47
54
  callbacks.after_destroy
48
55
  end
@@ -19,8 +19,8 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
19
19
  :settings => {'attribute_updates' => true},
20
20
  :indices_for_references => [index]) }
21
21
  let(:connection) { double('connection', :execute => '') }
22
- let(:index) { double('index', :name => 'article_core',
23
- :sources => [source], :document_id_for_key => 3) }
22
+ let(:index) { double 'index', :name => 'article_core',
23
+ :sources => [source], :document_id_for_key => 3, :distributed? => false }
24
24
  let(:source) { double('source', :attributes => []) }
25
25
 
26
26
  before :each do
@@ -40,6 +40,13 @@ describe ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter do
40
40
  end
41
41
  end
42
42
 
43
+ describe '#convert_blank' do
44
+ it "translates arguments to a COALESCE NULLIF SQL call" do
45
+ adapter.convert_blank('id', 5).should == "COALESCE(NULLIF(id, ''), 5)"
46
+ end
47
+ end
48
+
49
+
43
50
  describe '#group_concatenate' do
44
51
  it "group concatenates the clause with the given separator" do
45
52
  adapter.group_concatenate('foo', ',').
@@ -49,6 +49,12 @@ describe ThinkingSphinx::ActiveRecord::DatabaseAdapters::PostgreSQLAdapter do
49
49
  end
50
50
  end
51
51
 
52
+ describe '#convert_blank' do
53
+ it "translates arguments to a COALESCE NULLIF SQL call" do
54
+ adapter.convert_blank('id', 5).should == "COALESCE(NULLIF(id, ''), 5)"
55
+ end
56
+ end
57
+
52
58
  describe '#group_concatenate' do
53
59
  it "group concatenates the clause with the given separator" do
54
60
  adapter.group_concatenate('foo', ',').
@@ -218,6 +218,33 @@ describe ThinkingSphinx::ActiveRecord::Interpreter do
218
218
  end
219
219
  end
220
220
 
221
+ describe '#set_database' do
222
+ before :each do
223
+ source.stub :set_database_settings => true
224
+
225
+ stub_const 'ActiveRecord::Base',
226
+ double(:configurations => {'other' => {:baz => :qux}})
227
+ end
228
+
229
+ it "sends through a hash if provided" do
230
+ source.should_receive(:set_database_settings).with(:foo => :bar)
231
+
232
+ instance.set_database :foo => :bar
233
+ end
234
+
235
+ it "finds the environment settings if given a string key" do
236
+ source.should_receive(:set_database_settings).with(:baz => :qux)
237
+
238
+ instance.set_database 'other'
239
+ end
240
+
241
+ it "finds the environment settings if given a symbol key" do
242
+ source.should_receive(:set_database_settings).with(:baz => :qux)
243
+
244
+ instance.set_database :other
245
+ end
246
+ end
247
+
221
248
  describe '#set_property' do
222
249
  before :each do
223
250
  index.class.stub :settings => [:morphology]
@@ -2,12 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
4
4
  let(:adapter) { double 'adapter' }
5
- let(:associations) { double 'associations', :alias_for => 'articles',
6
- :aggregate_for? => false, :model_for => model }
5
+ let(:associations) { double 'associations', :alias_for => 'articles' }
7
6
  let(:model) { double :column_names => ['title', 'created_at'] }
7
+ let(:path) { double :aggregate? => false, :model => model }
8
8
 
9
9
  before :each do
10
10
  adapter.stub(:quote) { |column| column }
11
+
12
+ stub_const 'Joiner::Path', double(:new => path)
11
13
  end
12
14
 
13
15
  context 'with a field' do
@@ -17,7 +19,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
17
19
  )
18
20
  }
19
21
  let(:field) { double('field', :name => 'title', :columns => [column],
20
- :type => nil, :multi? => false, :source_type => nil) }
22
+ :type => nil, :multi? => false, :source_type => nil, :model => double) }
21
23
  let(:column) { double('column', :string? => false, :__stack => [],
22
24
  :__name => 'title') }
23
25
 
@@ -36,7 +38,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
36
38
  end
37
39
 
38
40
  it "returns nil if the property is an aggregate" do
39
- associations.stub! :aggregate_for? => true
41
+ path.stub! :aggregate? => true
40
42
 
41
43
  presenter.to_group.should be_nil
42
44
  end
@@ -73,7 +75,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
73
75
  "GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
74
76
  end
75
77
 
76
- associations.stub! :aggregate_for? => true
78
+ path.stub! :aggregate? => true
77
79
 
78
80
  presenter.to_select.
79
81
  should == "GROUP_CONCAT(articles.title SEPARATOR ' ') AS title"
@@ -124,7 +126,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
124
126
  }
125
127
  let(:attribute) { double('attribute', :name => 'created_at',
126
128
  :columns => [column], :type => :integer, :multi? => false,
127
- :source_type => nil) }
129
+ :source_type => nil, :model => double) }
128
130
  let(:column) { double('column', :string? => false, :__stack => [],
129
131
  :__name => 'created_at') }
130
132
 
@@ -155,7 +157,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
155
157
  end
156
158
 
157
159
  it "returns nil if the property is an aggregate" do
158
- associations.stub! :aggregate_for? => true
160
+ path.stub! :aggregate? => true
159
161
 
160
162
  presenter.to_group.should be_nil
161
163
  end
@@ -235,6 +237,15 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
235
237
  presenter.to_select.should == "CONCAT_WS(',', CAST(UNIX_TIMESTAMP(articles.created_at) AS varchar), CAST(UNIX_TIMESTAMP(articles.created_at) AS varchar)) AS created_at"
236
238
  end
237
239
 
240
+ it "does not split attribute clause for timestamp casting if it looks like a function call" do
241
+ column.stub :__name => "COALESCE(articles.updated_at, articles.created_at)", :string? => true
242
+
243
+ attribute.stub :name => 'mod_date', :columns => [column],
244
+ :type => :timestamp
245
+
246
+ presenter.to_select.should == "UNIX_TIMESTAMP(COALESCE(articles.updated_at, articles.created_at)) AS mod_date"
247
+ end
248
+
238
249
  it "returns nil for query sourced attributes" do
239
250
  attribute.stub :source_type => :query
240
251
 
@@ -24,7 +24,7 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
24
24
  before :each do
25
25
  ThinkingSphinx::Configuration.stub! :instance => config
26
26
  ThinkingSphinx::ActiveRecord::PropertySQLPresenter.stub! :new => presenter
27
- ThinkingSphinx::ActiveRecord::Associations.stub! :new => associations
27
+ Joiner::Joins.stub! :new => associations
28
28
  relation.stub! :select => relation, :where => relation, :group => relation,
29
29
  :order => relation, :joins => relation, :to_sql => ''
30
30
  connection.stub!(:quote_column_name) { |column| "`#{column}`"}
@@ -545,6 +545,22 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
545
545
  end
546
546
  end
547
547
 
548
+ describe 'sql_query_post_index' do
549
+ let(:processor) { double('processor', :reset_query => 'RESET DELTAS') }
550
+
551
+ it "adds a reset delta query if there is a delta processor and this is the core source" do
552
+ source.stub :delta_processor => processor, :delta? => false
553
+
554
+ builder.sql_query_post_index.should include('RESET DELTAS')
555
+ end
556
+
557
+ it "adds no reset delta query if there is a delta processor and this is the delta source" do
558
+ source.stub :delta_processor => processor, :delta? => true
559
+
560
+ builder.sql_query_post_index.should_not include('RESET DELTAS')
561
+ end
562
+ end
563
+
548
564
  describe 'sql_query_pre' do
549
565
  let(:processor) { double('processor', :reset_query => 'RESET DELTAS') }
550
566
 
@@ -553,12 +569,6 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
553
569
  adapter.stub :utf8_query_pre => ['SET UTF8']
554
570
  end
555
571
 
556
- it "adds a reset delta query if there is a delta processor and this is the core source" do
557
- source.stub :delta_processor => processor
558
-
559
- builder.sql_query_pre.should include('RESET DELTAS')
560
- end
561
-
562
572
  it "does not add a reset query if there is no delta processor" do
563
573
  builder.sql_query_pre.should_not include('RESET DELTAS')
564
574
  end
@@ -1,8 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ThinkingSphinx::ActiveRecord::SQLSource do
4
- let(:model) { double('model', :name => 'User', :column_names => [],
5
- :inheritance_column => 'type', :primary_key => :id) }
4
+ let(:model) { double('model', :connection => connection,
5
+ :name => 'User', :column_names => [], :inheritance_column => 'type',
6
+ :primary_key => :id) }
6
7
  let(:connection) {
7
8
  double('connection', :instance_variable_get => db_config) }
8
9
  let(:db_config) { {:host => 'localhost', :user => 'root',
@@ -12,8 +13,6 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
12
13
  let(:adapter) { double('adapter') }
13
14
 
14
15
  before :each do
15
- stub_const 'ActiveRecord::Base', double(:connection => connection)
16
-
17
16
  ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter.
18
17
  stub!(:=== => true)
19
18
  ThinkingSphinx::ActiveRecord::DatabaseAdapters.
@@ -56,9 +55,14 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
56
55
  ThinkingSphinx::ActiveRecord::SQLSource.new model,
57
56
  :delta_processor => processor_class
58
57
  }
58
+ let(:source_with_options) {
59
+ ThinkingSphinx::ActiveRecord::SQLSource.new model,
60
+ :delta_processor => processor_class,
61
+ :delta_options => { :opt_key => :opt_value }
62
+ }
59
63
 
60
64
  it "loads the processor with the adapter" do
61
- processor_class.should_receive(:try).with(:new, adapter).
65
+ processor_class.should_receive(:try).with(:new, adapter, {}).
62
66
  and_return processor
63
67
 
64
68
  source.delta_processor
@@ -67,6 +71,11 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
67
71
  it "returns the given processor" do
68
72
  source.delta_processor.should == processor
69
73
  end
74
+
75
+ it "passes given options to the processor" do
76
+ processor_class.should_receive(:try).with(:new, adapter, {:opt_key => :opt_value})
77
+ source_with_options.delta_processor
78
+ end
70
79
  end
71
80
 
72
81
  describe '#delta?' do
@@ -101,15 +110,15 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
101
110
 
102
111
  it "uses the inheritance column if it exists for the sphinx class field" do
103
112
  adapter.stub :quoted_table_name => '"users"', :quote => '"type"'
104
- adapter.stub(:convert_nulls) { |clause, default|
105
- "ifnull(#{clause}, #{default})"
113
+ adapter.stub(:convert_blank) { |clause, default|
114
+ "coalesce(nullif(#{clause}, ''), #{default})"
106
115
  }
107
116
  model.stub :column_names => ['type'], :sti_name => 'User'
108
117
 
109
118
  source.fields.detect { |field|
110
119
  field.name == 'sphinx_internal_class_name'
111
120
  }.columns.first.__name.
112
- should == "ifnull(\"users\".\"type\", 'User')"
121
+ should == "coalesce(nullif(\"users\".\"type\", ''), 'User')"
113
122
  end
114
123
  end
115
124
 
@@ -147,7 +156,9 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
147
156
  end
148
157
 
149
158
  describe '#render' do
150
- let(:builder) { double('builder', :sql_query_pre => []).as_null_object }
159
+ let(:builder) { double('builder', :sql_query_pre => [],
160
+ :sql_query_post_index => [], :sql_query => 'query',
161
+ :sql_query_range => 'range', :sql_query_info => 'info') }
151
162
  let(:config) { double('config', :settings => {}) }
152
163
  let(:presenter) { double('presenter', :collection_type => :uint) }
153
164
  let(:template) { double('template', :apply => true) }
@@ -159,79 +170,6 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
159
170
  ThinkingSphinx::Configuration.stub :instance => config
160
171
  end
161
172
 
162
- it "sets the sql_host setting from the model's database settings" do
163
- db_config[:host] = '12.34.56.78'
164
-
165
- source.render
166
-
167
- source.sql_host.should == '12.34.56.78'
168
- end
169
-
170
- it "defaults sql_host to localhost if the model has no host" do
171
- db_config[:host] = nil
172
-
173
- source.render
174
-
175
- source.sql_host.should == 'localhost'
176
- end
177
-
178
- it "sets the sql_user setting from the model's database settings" do
179
- db_config[:username] = 'pat'
180
-
181
- source.render
182
-
183
- source.sql_user.should == 'pat'
184
- end
185
-
186
- it "uses the user setting if username is not set in the model" do
187
- db_config[:username] = nil
188
- db_config[:user] = 'pat'
189
-
190
- source.render
191
-
192
- source.sql_user.should == 'pat'
193
- end
194
-
195
- it "sets the sql_pass setting from the model's database settings" do
196
- db_config[:password] = 'swordfish'
197
-
198
- source.render
199
-
200
- source.sql_pass.should == 'swordfish'
201
- end
202
-
203
- it "escapes hashes in the password for sql_pass" do
204
- db_config[:password] = 'sword#fish'
205
-
206
- source.render
207
-
208
- source.sql_pass.should == 'sword\#fish'
209
- end
210
-
211
- it "sets the sql_db setting from the model's database settings" do
212
- db_config[:database] = 'rails_app'
213
-
214
- source.render
215
-
216
- source.sql_db.should == 'rails_app'
217
- end
218
-
219
- it "sets the sql_port setting from the model's database settings" do
220
- db_config[:port] = 5432
221
-
222
- source.render
223
-
224
- source.sql_port.should == 5432
225
- end
226
-
227
- it "sets the sql_sock setting from the model's database settings" do
228
- db_config[:socket] = '/unix/socket'
229
-
230
- source.render
231
-
232
- source.sql_sock.should == '/unix/socket'
233
- end
234
-
235
173
  it "uses the builder's sql_query value" do
236
174
  builder.stub! :sql_query => 'select * from table'
237
175
 
@@ -264,6 +202,14 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
264
202
  source.sql_query_pre.should == ['Change Setting']
265
203
  end
266
204
 
205
+ it "appends the builder's sql_query_post_index value" do
206
+ builder.stub! :sql_query_post_index => ['RESET DELTAS']
207
+
208
+ source.render
209
+
210
+ source.sql_query_post_index.should include('RESET DELTAS')
211
+ end
212
+
267
213
  it "adds fields with attributes to sql_field_string" do
268
214
  source.fields << double('field', :name => 'title', :source_type => nil,
269
215
  :with_attribute? => true, :file? => false, :wordcount? => false)
@@ -397,6 +343,62 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
397
343
  end
398
344
  end
399
345
 
346
+ describe '#set_database_settings' do
347
+ it "sets the sql_host setting from the model's database settings" do
348
+ source.set_database_settings :host => '12.34.56.78'
349
+
350
+ source.sql_host.should == '12.34.56.78'
351
+ end
352
+
353
+ it "defaults sql_host to localhost if the model has no host" do
354
+ source.set_database_settings :host => nil
355
+
356
+ source.sql_host.should == 'localhost'
357
+ end
358
+
359
+ it "sets the sql_user setting from the model's database settings" do
360
+ source.set_database_settings :username => 'pat'
361
+
362
+ source.sql_user.should == 'pat'
363
+ end
364
+
365
+ it "uses the user setting if username is not set in the model" do
366
+ source.set_database_settings :username => nil, :user => 'pat'
367
+
368
+ source.sql_user.should == 'pat'
369
+ end
370
+
371
+ it "sets the sql_pass setting from the model's database settings" do
372
+ source.set_database_settings :password => 'swordfish'
373
+
374
+ source.sql_pass.should == 'swordfish'
375
+ end
376
+
377
+ it "escapes hashes in the password for sql_pass" do
378
+ source.set_database_settings :password => 'sword#fish'
379
+
380
+ source.sql_pass.should == 'sword\#fish'
381
+ end
382
+
383
+ it "sets the sql_db setting from the model's database settings" do
384
+ source.set_database_settings :database => 'rails_app'
385
+
386
+ source.sql_db.should == 'rails_app'
387
+ end
388
+
389
+ it "sets the sql_port setting from the model's database settings" do
390
+ source.set_database_settings :port => 5432
391
+
392
+ source.sql_port.should == 5432
393
+ end
394
+
395
+ it "sets the sql_sock setting from the model's database settings" do
396
+ source.set_database_settings :socket => '/unix/socket'
397
+
398
+ source.sql_sock.should == '/unix/socket'
399
+ end
400
+ end
401
+
400
402
  describe '#type' do
401
403
  it "is mysql when using the MySQL Adapter" do
402
404
  ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter.