thinking-sphinx 3.0.6 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.