sunspot 2.2.7 → 2.2.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Appraisals +7 -0
  5. data/Gemfile +0 -8
  6. data/gemfiles/.gitkeep +0 -0
  7. data/lib/sunspot/dsl/scope.rb +6 -1
  8. data/lib/sunspot/field.rb +11 -0
  9. data/lib/sunspot/field_factory.rb +6 -2
  10. data/lib/sunspot/query/bbox.rb +5 -1
  11. data/lib/sunspot/query/restriction.rb +11 -0
  12. data/lib/sunspot/search/hit_enumerable.rb +4 -1
  13. data/lib/sunspot/search/standard_search.rb +2 -3
  14. data/lib/sunspot/version.rb +1 -1
  15. data/spec/api/adapters_spec.rb +19 -19
  16. data/spec/api/batcher_spec.rb +15 -15
  17. data/spec/api/binding_spec.rb +3 -3
  18. data/spec/api/class_set_spec.rb +3 -3
  19. data/spec/api/hit_enumerable_spec.rb +32 -9
  20. data/spec/api/indexer/attributes_spec.rb +31 -31
  21. data/spec/api/indexer/batch_spec.rb +8 -7
  22. data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
  23. data/spec/api/indexer/fixed_fields_spec.rb +12 -12
  24. data/spec/api/indexer/fulltext_spec.rb +8 -8
  25. data/spec/api/indexer/removal_spec.rb +14 -14
  26. data/spec/api/indexer_spec.rb +2 -2
  27. data/spec/api/query/advanced_manipulation_examples.rb +3 -3
  28. data/spec/api/query/connectives_examples.rb +26 -14
  29. data/spec/api/query/dsl_spec.rb +17 -9
  30. data/spec/api/query/dynamic_fields_examples.rb +18 -18
  31. data/spec/api/query/faceting_examples.rb +62 -62
  32. data/spec/api/query/fulltext_examples.rb +56 -55
  33. data/spec/api/query/function_spec.rb +26 -26
  34. data/spec/api/query/geo_examples.rb +6 -6
  35. data/spec/api/query/group_spec.rb +6 -6
  36. data/spec/api/query/highlighting_examples.rb +26 -26
  37. data/spec/api/query/join_spec.rb +2 -2
  38. data/spec/api/query/more_like_this_spec.rb +29 -29
  39. data/spec/api/query/ordering_pagination_examples.rb +25 -25
  40. data/spec/api/query/scope_examples.rb +39 -39
  41. data/spec/api/query/spatial_examples.rb +3 -3
  42. data/spec/api/query/spellcheck_examples.rb +3 -3
  43. data/spec/api/query/standard_spec.rb +1 -1
  44. data/spec/api/query/stats_examples.rb +8 -8
  45. data/spec/api/query/text_field_scoping_examples.rb +5 -5
  46. data/spec/api/query/types_spec.rb +4 -4
  47. data/spec/api/search/cursor_paginated_collection_spec.rb +12 -12
  48. data/spec/api/search/dynamic_fields_spec.rb +4 -4
  49. data/spec/api/search/faceting_spec.rb +55 -52
  50. data/spec/api/search/highlighting_spec.rb +7 -7
  51. data/spec/api/search/hits_spec.rb +29 -29
  52. data/spec/api/search/paginated_collection_spec.rb +18 -18
  53. data/spec/api/search/results_spec.rb +13 -13
  54. data/spec/api/search/search_spec.rb +3 -3
  55. data/spec/api/search/stats_spec.rb +10 -10
  56. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +19 -18
  57. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +9 -9
  58. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
  59. data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +10 -10
  60. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +14 -13
  61. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +2 -2
  62. data/spec/api/session_proxy/spec_helper.rb +1 -1
  63. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +9 -5
  64. data/spec/api/session_spec.rb +42 -42
  65. data/spec/api/sunspot_spec.rb +4 -4
  66. data/spec/integration/atomic_updates_spec.rb +25 -11
  67. data/spec/integration/dynamic_fields_spec.rb +10 -10
  68. data/spec/integration/faceting_spec.rb +39 -39
  69. data/spec/integration/field_grouping_spec.rb +16 -16
  70. data/spec/integration/field_lists_spec.rb +41 -0
  71. data/spec/integration/geospatial_spec.rb +19 -8
  72. data/spec/integration/highlighting_spec.rb +5 -5
  73. data/spec/integration/indexing_spec.rb +5 -5
  74. data/spec/integration/keyword_search_spec.rb +47 -45
  75. data/spec/integration/local_search_spec.rb +4 -4
  76. data/spec/integration/more_like_this_spec.rb +7 -7
  77. data/spec/integration/scoped_search_spec.rb +107 -107
  78. data/spec/integration/spellcheck_spec.rb +52 -7
  79. data/spec/integration/stats_spec.rb +10 -10
  80. data/spec/integration/stored_fields_spec.rb +1 -1
  81. data/spec/integration/test_pagination.rb +4 -4
  82. data/spec/integration/unicode_spec.rb +1 -1
  83. data/spec/mocks/post.rb +5 -1
  84. data/spec/spec_helper.rb +11 -6
  85. data/sunspot.gemspec +3 -1
  86. metadata +40 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4bdef6ed9f0c2c63ec693c6db3fe71149987c50d
4
- data.tar.gz: c7451cff132715d22bff11d90bed4ba62bf516e2
3
+ metadata.gz: 97232f93d9d1c5ce704e6249cd1afcec04f7f231
4
+ data.tar.gz: 9eb35de3e65284cdec3eb299d6085cc32e662590
5
5
  SHA512:
6
- metadata.gz: 71b60a64a69d3397a7d81c01282f8ab80e8c7b662bdb4196e91c84b9c06787d8787a2f37ba80e163f653ac0635ecf6edf682b2fec27a0dbef8ecfb79e9fd62c0
7
- data.tar.gz: bdb4c1d76331b386db7a487b3161afbf4f10c7f5c3a74f0d45e1d1876d348f84ba7fa7dfc750d45a2342f0d73d8cf51b72567dae3972ffd5ac08549cd8bf5ec6
6
+ metadata.gz: 5bd0d81960ded04f07fdfa72811ddf959c5c97b0339e58246761469d3f9b4d9a82bf71ec69e8ee222b05ff9c07b5ff406f2702ef1ab8caeacbb1d219415b0044
7
+ data.tar.gz: b8adf8fc509ea65e460139687084832f8353e81e6ad1fd50f976db0a721545934761894ca42281962b19ce64b179a69b607dd3bb05b735b5fc53603aff0685a5
data/.gitignore CHANGED
@@ -11,3 +11,4 @@ pkg
11
11
  README.rdoc
12
12
  .bundle
13
13
  Gemfile.lock
14
+ gemfiles/*.gemfile*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,7 @@
1
+ appraise 'rsolr-1.1.x' do
2
+ gem 'rsolr', '~> 1.1.0'
3
+ end
4
+
5
+ appraise 'rsolr-2.1.x' do
6
+ gem 'rsolr', '~> 2.1.0'
7
+ end
data/Gemfile CHANGED
@@ -1,11 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'sunspot_solr', :path => File.expand_path('../../sunspot_solr', __FILE__)
4
-
5
3
  gemspec
6
-
7
- group :development do
8
- gem 'rake'
9
- end
10
-
11
- gem 'rsolr', ENV['RSOLR'] if ENV['RSOLR']
File without changes
@@ -14,7 +14,12 @@ module Sunspot
14
14
 
15
15
  # Build a restriction to return only fields of the type in the results.
16
16
  def field_list(*args)
17
- @query.add_field_list(Sunspot::Query::FieldList.new(args.flatten))
17
+ list = args.flatten.map { |field| @setup.field(field.to_sym).indexed_name.to_sym }
18
+ @query.add_field_list(Sunspot::Query::FieldList.new([:id] + list)) unless list.empty?
19
+ end
20
+
21
+ def without_stored_fields
22
+ @query.add_field_list(Sunspot::Query::FieldList.new([:id]))
18
23
  end
19
24
 
20
25
  #
@@ -93,6 +93,17 @@ module Sunspot
93
93
  !!@joined
94
94
  end
95
95
 
96
+ #
97
+ # Whether the field is stored or not.
98
+ #
99
+ # ==== Returns
100
+ #
101
+ # Boolean:: True if this field is a stored field
102
+ #
103
+ def stored?
104
+ !!@stored
105
+ end
106
+
96
107
  def hash
97
108
  indexed_name.hash
98
109
  end
@@ -72,11 +72,15 @@ module Sunspot
72
72
  # into the Solr document for indexing.
73
73
  #
74
74
  def populate_document(document, model, options = {}) #:nodoc:
75
+ atomic_operation = options[:update] == :set
75
76
  value = extract_value(model, options)
76
- unless value.nil?
77
- Util.Array(@field.to_indexed(value)).each do |scalar_value|
77
+ if value != nil || atomic_operation
78
+ indexed_values = Util.Array(@field.to_indexed(value))
79
+ indexed_values = [nil] if indexed_values.empty? && atomic_operation
80
+ indexed_values.each do |scalar_value|
78
81
  field_options = {}
79
82
  field_options[:boost] = @field.boost if @field.boost
83
+ field_options[:null] = true if scalar_value.nil? && atomic_operation
80
84
  document.add_field(
81
85
  @field.indexed_name.to_sym,
82
86
  scalar_value,
@@ -5,8 +5,12 @@ module Sunspot
5
5
  @field, @first_corner, @second_corner = field, first_corner, second_corner
6
6
  end
7
7
 
8
+ def to_solr_conditional
9
+ "[#{@first_corner.join(",")} TO #{@second_corner.join(",")}]"
10
+ end
11
+
8
12
  def to_params
9
- filter = "#{@field.indexed_name}:[#{@first_corner.join(",")} TO #{@second_corner.join(",")}]"
13
+ filter = "#{@field.indexed_name}:#{to_solr_conditional}"
10
14
 
11
15
  {:fq => filter}
12
16
  end
@@ -169,6 +169,17 @@ module Sunspot
169
169
  end
170
170
  end
171
171
 
172
+ class InBoundingBox < Base
173
+ def initialize(negated, field, first_corner, second_corner)
174
+ @bbox = Sunspot::Query::Bbox.new(field, first_corner, second_corner)
175
+ super negated, field, [first_corner, second_corner]
176
+ end
177
+
178
+ def to_solr_conditional
179
+ @bbox.to_solr_conditional
180
+ end
181
+ end
182
+
172
183
  #
173
184
  # Results must have field with value equal to given value. If the value
174
185
  # is nil, results must have no value for the given field.
@@ -11,6 +11,9 @@ module Sunspot
11
11
  end
12
12
  end
13
13
 
14
+ #
15
+ # Returns all of the hits that have a result
16
+ #
14
17
  def verified_hits
15
18
  hits.select { |h| h.result }
16
19
  end
@@ -31,7 +34,7 @@ module Sunspot
31
34
  hits_for_class = id_hit_hash[class_name]
32
35
  data_accessor.load_all(ids).each do |result|
33
36
  hit = hits_for_class.delete(Adapters::InstanceAdapter.adapt(result).id.to_s)
34
- hit.result = result
37
+ hit.result = result if hit
35
38
  end
36
39
  hits_for_class.values.each { |hit| hit.result = nil }
37
40
  end
@@ -49,12 +49,11 @@ module Sunspot
49
49
  # the index. Otherwise return Solr's suggested collation.
50
50
  #
51
51
  # Solr's suggested collation is more liberal, replacing even terms that
52
- # are present in the index. This may not be useful if only one term is
53
- # misspelled and preventing useful results.
52
+ # are present in the index.
54
53
  #
55
54
  # Mix and match in your views for a blend of strict and liberal collations.
56
55
  def spellcheck_collation(*terms)
57
- if solr_spellcheck['suggestions'] && solr_spellcheck['suggestions'].length > 2
56
+ if solr_spellcheck['suggestions'] && solr_spellcheck['suggestions'].length > 0
58
57
  collation = terms.join(" ").dup if terms
59
58
 
60
59
  # If we are given a query string, tokenize it and strictly replace
@@ -1,3 +1,3 @@
1
1
  module Sunspot
2
- VERSION = '2.2.7'
2
+ VERSION = '2.2.8'
3
3
  end
@@ -2,45 +2,45 @@ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe Sunspot::Adapters::InstanceAdapter do
4
4
  it "finds adapter by superclass" do
5
- Sunspot::Adapters::InstanceAdapter::for(Model).should be(AbstractModelInstanceAdapter)
5
+ expect(Sunspot::Adapters::InstanceAdapter::for(Model)).to be(AbstractModelInstanceAdapter)
6
6
  end
7
7
 
8
8
  it "finds adapter by mixin" do
9
- Sunspot::Adapters::InstanceAdapter::for(MixModel).should be(MixInModelInstanceAdapter)
9
+ expect(Sunspot::Adapters::InstanceAdapter::for(MixModel)).to be(MixInModelInstanceAdapter)
10
10
  end
11
11
 
12
12
  it 'throws NoAdapterError if anonymous module passed in' do
13
- lambda do
13
+ expect do
14
14
  Sunspot::Adapters::InstanceAdapter::for(Module.new)
15
- end.should raise_error(Sunspot::NoAdapterError)
15
+ end.to raise_error(Sunspot::NoAdapterError)
16
16
  end
17
17
 
18
18
  it "registers adapters found by ancestor lookup with the descendant class" do
19
- Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel).should be(nil)
19
+ expect(Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel)).to be(nil)
20
20
  Sunspot::Adapters::InstanceAdapter::for(UnseenModel)
21
- Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel).should be(AbstractModelInstanceAdapter)
21
+ expect(Sunspot::Adapters::InstanceAdapter::registered_adapter_for(UnseenModel)).to be(AbstractModelInstanceAdapter)
22
22
  end
23
23
  end
24
24
 
25
25
  describe Sunspot::Adapters::DataAccessor do
26
26
  it "finds adapter by superclass" do
27
- Sunspot::Adapters::DataAccessor::for(Model).should be(AbstractModelDataAccessor)
27
+ expect(Sunspot::Adapters::DataAccessor::for(Model)).to be(AbstractModelDataAccessor)
28
28
  end
29
29
 
30
30
  it "finds adapter by mixin" do
31
- Sunspot::Adapters::DataAccessor::for(MixModel).should be(MixInModelDataAccessor)
31
+ expect(Sunspot::Adapters::DataAccessor::for(MixModel)).to be(MixInModelDataAccessor)
32
32
  end
33
33
 
34
34
  it 'throws NoAdapterError if anonymous module passed in' do
35
- lambda do
35
+ expect do
36
36
  Sunspot::Adapters::DataAccessor::for(Module.new)
37
- end.should raise_error(Sunspot::NoAdapterError)
37
+ end.to raise_error(Sunspot::NoAdapterError)
38
38
  end
39
39
 
40
40
  it "registers adapters found by ancestor lookup with the descendant class" do
41
- Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel).should be(nil)
41
+ expect(Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel)).to be(nil)
42
42
  Sunspot::Adapters::DataAccessor::for(UnseenModel)
43
- Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel).should be(AbstractModelDataAccessor)
43
+ expect(Sunspot::Adapters::DataAccessor::registered_accessor_for(UnseenModel)).to be(AbstractModelDataAccessor)
44
44
  end
45
45
  end
46
46
 
@@ -51,30 +51,30 @@ describe Sunspot::Adapters::Registry do
51
51
  let(:mixin_accessor){ Sunspot::Adapters::DataAccessor::for(MixModel) }
52
52
 
53
53
  it "registers and retrieves a data accessor for abstractclass" do
54
- registry.retrieve(AbstractModel).should be_a(abstractclass_accessor)
54
+ expect(registry.retrieve(AbstractModel)).to be_a(abstractclass_accessor)
55
55
  end
56
56
 
57
57
  it "registers and retrieves a data accessor for superclass" do
58
- registry.retrieve(Model).should be_a(superclass_accessor)
58
+ expect(registry.retrieve(Model)).to be_a(superclass_accessor)
59
59
  end
60
60
 
61
61
  it "registers and retrieves a data accessor for mixin" do
62
- registry.retrieve(MixModel).should be_a(mixin_accessor)
62
+ expect(registry.retrieve(MixModel)).to be_a(mixin_accessor)
63
63
  end
64
64
 
65
65
  it "injects inherited attributes" do
66
- AbstractModelDataAccessor.any_instance.stub(:inherited_attributes).and_return([:to_be_injected])
66
+ allow_any_instance_of(AbstractModelDataAccessor).to receive(:inherited_attributes).and_return([:to_be_injected])
67
67
  in_registry_data_accessor = registry.retrieve(AbstractModel)
68
68
  in_registry_data_accessor.to_be_injected = "value"
69
- registry.retrieve(Model).to_be_injected.should == "value"
69
+ expect(registry.retrieve(Model).to_be_injected).to eq("value")
70
70
  end
71
71
 
72
72
  it "not overrides inherited attributes" do
73
- AbstractModelDataAccessor.any_instance.stub(:inherited_attributes).and_return([:to_be_injected])
73
+ allow_any_instance_of(AbstractModelDataAccessor).to receive(:inherited_attributes).and_return([:to_be_injected])
74
74
  parent_data_accessor = registry.retrieve(AbstractModel)
75
75
  current_data_accessor = registry.retrieve(Model)
76
76
  parent_data_accessor.to_be_injected = "value"
77
77
  current_data_accessor.to_be_injected = "safe-value"
78
- registry.retrieve(Model).to_be_injected.should == "safe-value"
78
+ expect(registry.retrieve(Model).to_be_injected).to eq("safe-value")
79
79
  end
80
80
  end
@@ -2,12 +2,12 @@ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe Sunspot::Batcher do
4
4
  it "includes Enumerable" do
5
- described_class.should include Enumerable
5
+ expect(described_class).to include Enumerable
6
6
  end
7
7
 
8
8
  describe "#each" do
9
9
  let(:current) { [:foo, :bar] }
10
- before { subject.stub(:current).and_return current }
10
+ before { allow(subject).to receive(:current).and_return current }
11
11
 
12
12
  it "iterates over current" do
13
13
  yielded_values = []
@@ -16,25 +16,25 @@ describe Sunspot::Batcher do
16
16
  yielded_values << value
17
17
  end
18
18
 
19
- yielded_values.should eq current
19
+ expect(yielded_values).to eq current
20
20
  end
21
21
  end
22
22
 
23
23
  describe "adding to current batch" do
24
24
  it "#push pushes to current" do
25
25
  subject.push :foo
26
- subject.current.should include :foo
26
+ expect(subject.current).to include :foo
27
27
  end
28
28
 
29
29
  it "#<< pushes to current" do
30
30
  subject.push :foo
31
- subject.current.should include :foo
31
+ expect(subject.current).to include :foo
32
32
  end
33
33
 
34
34
  it "#concat concatinates on current batch" do
35
35
  subject << :foo
36
36
  subject.concat [:bar, :mix]
37
- should include :foo, :bar, :mix
37
+ is_expected.to include :foo, :bar, :mix
38
38
  end
39
39
  end
40
40
 
@@ -46,7 +46,7 @@ describe Sunspot::Batcher do
46
46
  end
47
47
 
48
48
  it "is empty by default" do
49
- subject.current.should be_empty
49
+ expect(subject.current).to be_empty
50
50
  end
51
51
  end
52
52
 
@@ -58,7 +58,7 @@ describe Sunspot::Batcher do
58
58
  end
59
59
 
60
60
  it "returns the same as last time" do
61
- subject.current.should eq subject.current
61
+ expect(subject.current).to eq subject.current
62
62
  end
63
63
  end
64
64
  end
@@ -71,7 +71,7 @@ describe Sunspot::Batcher do
71
71
  it "changes current" do
72
72
  subject << :foo
73
73
  subject.start_new
74
- should_not include :foo
74
+ is_expected.not_to include :foo
75
75
  end
76
76
  end
77
77
 
@@ -88,25 +88,25 @@ describe Sunspot::Batcher do
88
88
  it "changes current" do
89
89
  subject << :foo
90
90
  subject.end_current
91
- should_not include :foo
91
+ is_expected.not_to include :foo
92
92
  end
93
93
 
94
94
  it "returns current" do
95
95
  subject << :foo
96
- subject.end_current.should include :foo
96
+ expect(subject.end_current).to include :foo
97
97
  end
98
98
  end
99
99
  end
100
100
 
101
101
  describe "#batching?" do
102
102
  it "is false when depth is 0" do
103
- subject.should_receive(:depth).and_return 0
104
- should_not be_batching
103
+ expect(subject).to receive(:depth).and_return 0
104
+ is_expected.not_to be_batching
105
105
  end
106
106
 
107
107
  it "is true when depth is more than 0" do
108
- subject.should_receive(:depth).and_return 1
109
- should be_batching
108
+ expect(subject).to receive(:depth).and_return 1
109
+ is_expected.to be_batching
110
110
  end
111
111
  end
112
112
  end
@@ -6,7 +6,7 @@ describe "DSL bindings" do
6
6
  session.search(Post) do
7
7
  value = test_method
8
8
  end
9
- value.should == 'value'
9
+ expect(value).to eq('value')
10
10
  end
11
11
 
12
12
  it 'should give access to calling context\'s id method in search DSL' do
@@ -14,7 +14,7 @@ describe "DSL bindings" do
14
14
  session.search(Post) do
15
15
  value = id
16
16
  end
17
- value.should == 16
17
+ expect(value).to eq(16)
18
18
  end
19
19
 
20
20
  it 'should give access to calling context\'s methods in nested DSL block' do
@@ -24,7 +24,7 @@ describe "DSL bindings" do
24
24
  value = test_method
25
25
  end
26
26
  end
27
- value.should == 'value'
27
+ expect(value).to eq('value')
28
28
  end
29
29
 
30
30
  it 'should give access to calling context\'s methods in double-nested DSL block' do
@@ -7,7 +7,7 @@ describe Sunspot::ClassSet do
7
7
  set = described_class.new
8
8
  set << class1 << class2
9
9
 
10
- set.to_a.should =~ [class1, class2]
10
+ expect(set.to_a).to match_array([class1, class2])
11
11
  end
12
12
 
13
13
  it "replaces classes with the same name" do
@@ -15,10 +15,10 @@ describe Sunspot::ClassSet do
15
15
 
16
16
  class1 = double(:name => "Class1")
17
17
  set << class1
18
- set.to_a.should == [class1]
18
+ expect(set.to_a).to eq([class1])
19
19
 
20
20
  class1_dup = double(:name => "Class1")
21
21
  set << class1_dup
22
- set.to_a.should == [class1_dup]
22
+ expect(set.to_a).to eq([class1_dup])
23
23
  end
24
24
  end
@@ -9,37 +9,60 @@ describe Sunspot::Search::HitEnumerable do
9
9
 
10
10
  describe "#hits" do
11
11
  before do
12
- subject.stub(:solr_docs).and_return([{"id" => "Post 1", "score" => 3.14}])
13
- subject.stub(:highlights_for)
12
+ allow(subject).to receive(:solr_docs).and_return([{"id" => "Post 1", "score" => 3.14}])
13
+ allow(subject).to receive(:highlights_for)
14
14
  end
15
15
 
16
16
  it "retrieves the raw Solr response from #solr_docs and constructs Hit objects" do
17
- Sunspot::Search::Hit.should_receive(:new).
17
+ expect(Sunspot::Search::Hit).to receive(:new).
18
18
  with({"id" => "Post 1", "score" => 3.14}, anything, anything)
19
19
 
20
20
  subject.hits
21
21
  end
22
22
 
23
23
  it "constructs Hit objects with highlights" do
24
- subject.should_receive(:highlights_for).with({"id" => "Post 1", "score" => 3.14})
24
+ expect(subject).to receive(:highlights_for).with({"id" => "Post 1", "score" => 3.14})
25
25
 
26
26
  subject.hits
27
27
  end
28
28
 
29
29
  it "returns only verified hits if :verify => true is passed" do
30
- Sunspot::Search::Hit.any_instance.stub(:result).and_return(nil)
30
+ allow_any_instance_of(Sunspot::Search::Hit).to receive(:result).and_return(nil)
31
31
 
32
- subject.hits(:verify => true).should be_empty
32
+ expect(subject.hits(:verify => true)).to be_empty
33
33
  end
34
34
 
35
35
  it "returns an empty array if no results are available from Solr" do
36
- subject.stub(:solr_docs).and_return(nil)
36
+ allow(subject).to receive(:solr_docs).and_return(nil)
37
37
 
38
- subject.hits.should == []
38
+ expect(subject.hits).to eq([])
39
39
  end
40
40
 
41
41
  it "provides #populate_hits so that querying for one hit result will eager load the rest" do
42
- Sunspot::Search::Hit.any_instance.should_receive(:result=)
42
+ expect_any_instance_of(Sunspot::Search::Hit).to receive(:result=)
43
+
44
+ subject.populate_hits
45
+ end
46
+ end
47
+
48
+ describe "#populate_hits" do
49
+ let(:post_1) { Post.new(id: "1", title: "Title 1") }
50
+
51
+ before do
52
+ allow(subject).to receive(:solr_docs).and_return([{ "id" => "Post 1", "score" => 3.14 }])
53
+ allow(subject).to receive(:highlights_for)
54
+ end
55
+
56
+ it "populates the hit object with the result from the data accessor" do
57
+ allow_any_instance_of(MockAdapter::DataAccessor).to receive(:load_all).and_return([post_1])
58
+ expect_any_instance_of(Sunspot::Search::Hit).to receive(:result=).with(post_1)
59
+
60
+ subject.populate_hits
61
+ end
62
+
63
+ it "populates the hit object only once if there are duplicate entries in the database (ex: db missing unique constraints)" do
64
+ allow_any_instance_of(MockAdapter::DataAccessor).to receive(:load_all).and_return([post_1, post_1])
65
+ expect_any_instance_of(Sunspot::Search::Hit).to receive(:result=).with(post_1)
43
66
 
44
67
  subject.populate_hits
45
68
  end