sayso-thinking-sphinx 2.0.3.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +251 -0
  3. data/VERSION +1 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +21 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +88 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/field_sorting.feature +18 -0
  15. data/features/handling_edits.feature +94 -0
  16. data/features/retry_stale_indexes.feature +24 -0
  17. data/features/searching_across_models.feature +20 -0
  18. data/features/searching_by_index.feature +40 -0
  19. data/features/searching_by_model.feature +168 -0
  20. data/features/searching_with_find_arguments.feature +56 -0
  21. data/features/sphinx_detection.feature +25 -0
  22. data/features/sphinx_scopes.feature +68 -0
  23. data/features/step_definitions/alpha_steps.rb +16 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +197 -0
  26. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  27. data/features/step_definitions/facet_steps.rb +96 -0
  28. data/features/step_definitions/find_arguments_steps.rb +36 -0
  29. data/features/step_definitions/gamma_steps.rb +15 -0
  30. data/features/step_definitions/scope_steps.rb +19 -0
  31. data/features/step_definitions/search_steps.rb +94 -0
  32. data/features/step_definitions/sphinx_steps.rb +35 -0
  33. data/features/sti_searching.feature +19 -0
  34. data/features/support/env.rb +27 -0
  35. data/features/support/lib/generic_delta_handler.rb +8 -0
  36. data/features/thinking_sphinx/database.example.yml +3 -0
  37. data/features/thinking_sphinx/db/fixtures/alphas.rb +10 -0
  38. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  39. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  40. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  41. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  42. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  43. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  44. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  45. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  46. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  47. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  48. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  49. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  50. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  51. data/features/thinking_sphinx/db/fixtures/posts.rb +6 -0
  52. data/features/thinking_sphinx/db/fixtures/robots.rb +14 -0
  53. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  54. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  55. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  56. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  57. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  58. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  59. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  60. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  61. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  62. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  63. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  64. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  65. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  66. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  67. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  68. data/features/thinking_sphinx/db/migrations/create_posts.rb +5 -0
  69. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  70. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  71. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  72. data/features/thinking_sphinx/models/alpha.rb +23 -0
  73. data/features/thinking_sphinx/models/andrew.rb +17 -0
  74. data/features/thinking_sphinx/models/animal.rb +5 -0
  75. data/features/thinking_sphinx/models/author.rb +3 -0
  76. data/features/thinking_sphinx/models/beta.rb +13 -0
  77. data/features/thinking_sphinx/models/box.rb +8 -0
  78. data/features/thinking_sphinx/models/cat.rb +3 -0
  79. data/features/thinking_sphinx/models/category.rb +4 -0
  80. data/features/thinking_sphinx/models/comment.rb +10 -0
  81. data/features/thinking_sphinx/models/developer.rb +20 -0
  82. data/features/thinking_sphinx/models/dog.rb +3 -0
  83. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  84. data/features/thinking_sphinx/models/fox.rb +5 -0
  85. data/features/thinking_sphinx/models/gamma.rb +5 -0
  86. data/features/thinking_sphinx/models/genre.rb +3 -0
  87. data/features/thinking_sphinx/models/medium.rb +5 -0
  88. data/features/thinking_sphinx/models/music.rb +8 -0
  89. data/features/thinking_sphinx/models/person.rb +24 -0
  90. data/features/thinking_sphinx/models/post.rb +21 -0
  91. data/features/thinking_sphinx/models/robot.rb +12 -0
  92. data/features/thinking_sphinx/models/tag.rb +3 -0
  93. data/features/thinking_sphinx/models/tagging.rb +4 -0
  94. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  95. data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
  96. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  97. data/lib/thinking-sphinx.rb +1 -0
  98. data/lib/thinking_sphinx.rb +301 -0
  99. data/lib/thinking_sphinx/action_controller.rb +31 -0
  100. data/lib/thinking_sphinx/active_record.rb +352 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +52 -0
  102. data/lib/thinking_sphinx/active_record/delta.rb +92 -0
  103. data/lib/thinking_sphinx/active_record/has_many_association.rb +36 -0
  104. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  105. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  106. data/lib/thinking_sphinx/active_record/scopes.rb +93 -0
  107. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  108. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +58 -0
  109. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +153 -0
  110. data/lib/thinking_sphinx/association.rb +169 -0
  111. data/lib/thinking_sphinx/attribute.rb +389 -0
  112. data/lib/thinking_sphinx/auto_version.rb +38 -0
  113. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  114. data/lib/thinking_sphinx/class_facet.rb +16 -0
  115. data/lib/thinking_sphinx/configuration.rb +355 -0
  116. data/lib/thinking_sphinx/context.rb +76 -0
  117. data/lib/thinking_sphinx/core/string.rb +15 -0
  118. data/lib/thinking_sphinx/deltas.rb +28 -0
  119. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  120. data/lib/thinking_sphinx/deploy/capistrano.rb +101 -0
  121. data/lib/thinking_sphinx/excerpter.rb +23 -0
  122. data/lib/thinking_sphinx/facet.rb +127 -0
  123. data/lib/thinking_sphinx/facet_search.rb +166 -0
  124. data/lib/thinking_sphinx/field.rb +82 -0
  125. data/lib/thinking_sphinx/index.rb +157 -0
  126. data/lib/thinking_sphinx/index/builder.rb +312 -0
  127. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  128. data/lib/thinking_sphinx/join.rb +37 -0
  129. data/lib/thinking_sphinx/property.rb +185 -0
  130. data/lib/thinking_sphinx/railtie.rb +46 -0
  131. data/lib/thinking_sphinx/search.rb +950 -0
  132. data/lib/thinking_sphinx/search_methods.rb +439 -0
  133. data/lib/thinking_sphinx/source.rb +163 -0
  134. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  135. data/lib/thinking_sphinx/source/sql.rb +148 -0
  136. data/lib/thinking_sphinx/tasks.rb +139 -0
  137. data/lib/thinking_sphinx/test.rb +55 -0
  138. data/spec/thinking_sphinx/active_record/delta_spec.rb +128 -0
  139. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +72 -0
  140. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  141. data/spec/thinking_sphinx/active_record_spec.rb +576 -0
  142. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  143. data/spec/thinking_sphinx/association_spec.rb +216 -0
  144. data/spec/thinking_sphinx/attribute_spec.rb +560 -0
  145. data/spec/thinking_sphinx/auto_version_spec.rb +63 -0
  146. data/spec/thinking_sphinx/configuration_spec.rb +288 -0
  147. data/spec/thinking_sphinx/context_spec.rb +128 -0
  148. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  149. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  150. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  151. data/spec/thinking_sphinx/facet_search_spec.rb +170 -0
  152. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  153. data/spec/thinking_sphinx/field_spec.rb +127 -0
  154. data/spec/thinking_sphinx/index/builder_spec.rb +508 -0
  155. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  156. data/spec/thinking_sphinx/index_spec.rb +183 -0
  157. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  158. data/spec/thinking_sphinx/search_spec.rb +1387 -0
  159. data/spec/thinking_sphinx/source_spec.rb +253 -0
  160. data/spec/thinking_sphinx/test_spec.rb +20 -0
  161. data/spec/thinking_sphinx_spec.rb +203 -0
  162. data/tasks/distribution.rb +33 -0
  163. data/tasks/testing.rb +80 -0
  164. metadata +509 -0
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Index::FauxColumn do
4
+ describe "coerce class method" do
5
+ before :each do
6
+ @column = stub('column')
7
+ ThinkingSphinx::Index::FauxColumn.stub!(:new => @column)
8
+ end
9
+
10
+ it "should return a single faux column if passed a string" do
11
+ ThinkingSphinx::Index::FauxColumn.coerce("string").should == @column
12
+ end
13
+
14
+ it "should return a single faux column if passed a symbol" do
15
+ ThinkingSphinx::Index::FauxColumn.coerce(:string).should == @column
16
+ end
17
+
18
+ it "should return an array of faux columns if passed an array of strings" do
19
+ ThinkingSphinx::Index::FauxColumn.coerce(["one", "two"]).should == [
20
+ @column, @column
21
+ ]
22
+ end
23
+
24
+ it "should return an array of faux columns if passed an array of symbols" do
25
+ ThinkingSphinx::Index::FauxColumn.coerce([:one, :two]).should == [
26
+ @column, @column
27
+ ]
28
+ end
29
+ end
30
+
31
+ describe '#to_ary' do
32
+ it "should return an array with the instance inside it" do
33
+ subject.to_ary.should == [subject]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Index do
4
+ describe "prefix_fields method" do
5
+ before :each do
6
+ @index = ThinkingSphinx::Index.new(Person)
7
+
8
+ @field_a = stub('field', :prefixes => true)
9
+ @field_b = stub('field', :prefixes => false)
10
+ @field_c = stub('field', :prefixes => true)
11
+
12
+ @index.stub!(:fields => [@field_a, @field_b, @field_c])
13
+ end
14
+
15
+ it "should return fields that are flagged as prefixed" do
16
+ @index.prefix_fields.should include(@field_a)
17
+ @index.prefix_fields.should include(@field_c)
18
+ end
19
+
20
+ it "should not return fields that aren't flagged as prefixed" do
21
+ @index.prefix_fields.should_not include(@field_b)
22
+ end
23
+ end
24
+
25
+ describe "infix_fields method" do
26
+ before :each do
27
+ @index = ThinkingSphinx::Index.new(Person)
28
+
29
+ @field_a = stub('field', :infixes => true)
30
+ @field_b = stub('field', :infixes => false)
31
+ @field_c = stub('field', :infixes => true)
32
+
33
+ @index.stub!(:fields => [@field_a, @field_b, @field_c])
34
+ end
35
+
36
+ it "should return fields that are flagged as infixed" do
37
+ @index.infix_fields.should include(@field_a)
38
+ @index.infix_fields.should include(@field_c)
39
+ end
40
+
41
+ it "should not return fields that aren't flagged as infixed" do
42
+ @index.infix_fields.should_not include(@field_b)
43
+ end
44
+ end
45
+
46
+ describe '.name_for' do
47
+ it "should return the model's name downcased" do
48
+ ThinkingSphinx::Index.name_for(Alpha).should == 'alpha'
49
+ end
50
+
51
+ it "should separate words by underscores" do
52
+ ThinkingSphinx::Index.name_for(ActiveRecord).should == 'active_record'
53
+ end
54
+
55
+ it "should separate namespaces by underscores" do
56
+ ThinkingSphinx::Index.name_for(ActiveRecord::Base).
57
+ should == 'active_record_base'
58
+ end
59
+ end
60
+
61
+ describe '#name' do
62
+ it "should return the downcased name of the index's model" do
63
+ ThinkingSphinx::Index.new(Alpha).name.should == 'alpha'
64
+ end
65
+
66
+ it "should return a custom name if one is set" do
67
+ index = ThinkingSphinx::Index.new(Alpha)
68
+ index.name = 'custom'
69
+ index.name.should == 'custom'
70
+ end
71
+ end
72
+
73
+ describe '#core_name' do
74
+ it "should take the index's name and append _core" do
75
+ ThinkingSphinx::Index.new(Alpha).core_name.should == 'alpha_core'
76
+ end
77
+ end
78
+
79
+ describe '#delta_name' do
80
+ it "should take the index's name and append _delta" do
81
+ ThinkingSphinx::Index.new(Alpha).delta_name.should == 'alpha_delta'
82
+ end
83
+ end
84
+
85
+ describe '#all_names' do
86
+ it "should return the core index name by default" do
87
+ ThinkingSphinx::Index.new(Alpha).all_names.should == ['alpha_core']
88
+ end
89
+
90
+ it "should return both core and delta names if deltas are enabled" do
91
+ index = ThinkingSphinx::Index.new(Alpha)
92
+ index.delta_object = stub('delta')
93
+
94
+ index.all_names.should == ['alpha_core', 'alpha_delta']
95
+ end
96
+
97
+ it "should respect custom names" do
98
+ index = ThinkingSphinx::Index.new(Alpha)
99
+ index.name = 'custom'
100
+
101
+ index.all_names.should == ['custom_core']
102
+ end
103
+
104
+ it "should respect custom names when deltas are enabled" do
105
+ index = ThinkingSphinx::Index.new(Alpha)
106
+ index.name = 'custom'
107
+ index.delta_object = stub('delta')
108
+
109
+ index.all_names.should == ['custom_core', 'custom_delta']
110
+ end
111
+ end
112
+
113
+ describe '#to_riddle' do
114
+ it "should return two Riddle indexes if deltas are disabled" do
115
+ index = ThinkingSphinx::Index.new(Alpha)
116
+
117
+ index.to_riddle(0).length.should == 2
118
+ end
119
+
120
+ it "should return three Riddle indexes if deltas are enabled" do
121
+ index = ThinkingSphinx::Index.new(Beta)
122
+ index.delta_object = stub('delta')
123
+
124
+ index.to_riddle(0).length.should == 3
125
+ end
126
+
127
+ it "should include a distributed index" do
128
+ index = ThinkingSphinx::Index.new(Alpha)
129
+
130
+ index.to_riddle(0).last.
131
+ should be_a(Riddle::Configuration::DistributedIndex)
132
+ end
133
+
134
+ context 'core index' do
135
+ it "should use the core name" do
136
+ @index = ThinkingSphinx::Index.new(Alpha).to_riddle(0).first
137
+ @index.name.should == 'alpha_core'
138
+ end
139
+
140
+ it "should not try to set disable_range on the index" do
141
+ ThinkingSphinx::Configuration.instance.
142
+ index_options[:disable_range] = true
143
+
144
+ lambda {
145
+ @index = ThinkingSphinx::Index.new(Alpha).to_riddle(0).first
146
+ }.should_not raise_error(NoMethodError)
147
+ end
148
+ end
149
+
150
+ context 'delta index' do
151
+ before :each do
152
+ index = ThinkingSphinx::Index.new(Beta)
153
+ index.delta_object = stub('delta')
154
+ @index = index.to_riddle(0)[1]
155
+ end
156
+
157
+ it "should use the delta name" do
158
+ @index.name.should == 'beta_delta'
159
+ end
160
+ end
161
+
162
+ context 'distributed index' do
163
+ it "should use the index's name" do
164
+ index = ThinkingSphinx::Index.new(Alpha)
165
+
166
+ index.to_riddle(0).last.name.should == 'alpha'
167
+ end
168
+
169
+ it "should add the core index" do
170
+ index = ThinkingSphinx::Index.new(Alpha)
171
+
172
+ index.to_riddle(0).last.local_indexes.should include('alpha_core')
173
+ end
174
+
175
+ it "should add the delta index if there is one" do
176
+ index = ThinkingSphinx::Index.new(Beta)
177
+ index.delta_object = stub('delta')
178
+
179
+ index.to_riddle(0).last.local_indexes.should include('beta_delta')
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::SearchMethods do
4
+ it "should be included into models with indexes" do
5
+ Alpha.included_modules.should include(ThinkingSphinx::SearchMethods)
6
+ end
7
+
8
+ it "should not be included into models that don't have indexes" do
9
+ Gamma.included_modules.should_not include(ThinkingSphinx::SearchMethods)
10
+ end
11
+
12
+ describe '.search_context' do
13
+ it "should return nil if not within a model" do
14
+ ThinkingSphinx.search_context.should be_nil
15
+ end
16
+
17
+ it "should return the model if within one" do
18
+ Alpha.search_context.should == Alpha
19
+ end
20
+ end
21
+
22
+ describe '.search' do
23
+ it "should return an instance of ThinkingSphinx::Search" do
24
+ Alpha.search.class.should == ThinkingSphinx::Search
25
+ end
26
+
27
+ it "should set the classes option if not already set" do
28
+ search = Alpha.search
29
+ search.options[:classes].should == [Alpha]
30
+ end
31
+
32
+ it "shouldn't set the classes option if already defined" do
33
+ search = Alpha.search :classes => [Beta]
34
+ search.options[:classes].should == [Beta]
35
+ end
36
+
37
+ it "should default to nil for the classes options" do
38
+ ThinkingSphinx.search.options[:classes].should be_nil
39
+ end
40
+ end
41
+
42
+ describe '.search_for_ids' do
43
+ it "should return an instance of ThinkingSphinx::Search" do
44
+ Alpha.search.class.should == ThinkingSphinx::Search
45
+ end
46
+
47
+ it "should set the classes option if not already set" do
48
+ search = Alpha.search_for_ids
49
+ search.options[:classes].should == [Alpha]
50
+ end
51
+
52
+ it "shouldn't set the classes option if already defined" do
53
+ search = Alpha.search_for_ids :classes => [Beta]
54
+ search.options[:classes].should == [Beta]
55
+ end
56
+
57
+ it "should set ids_only to true" do
58
+ search = Alpha.search_for_ids
59
+ search.options[:ids_only].should be_true
60
+ end
61
+ end
62
+
63
+ describe '.search_for_id' do
64
+ before :each do
65
+ @config = ThinkingSphinx::Configuration.instance
66
+ @client = Riddle::Client.new
67
+
68
+ @config.stub!(:client => @client)
69
+ @client.stub!(:query => {:matches => [], :total_found => 0})
70
+ end
71
+
72
+ it "should set the id range to the given id value" do
73
+ ThinkingSphinx.search_for_id(101, 'alpha_core')
74
+
75
+ @client.id_range.should == (101..101)
76
+ end
77
+
78
+ it "should not make any calls to the database" do
79
+ Alpha.should_not_receive(:find)
80
+
81
+ ThinkingSphinx.search_for_id(101, 'alpha_core', :classes => [Alpha])
82
+ end
83
+
84
+ it "should return true if there is a record" do
85
+ @client.stub!(:query => {:matches => [
86
+ {:attributes => {'sphinx_internal_id' => 100}}
87
+ ], :total_found => 1})
88
+
89
+ ThinkingSphinx.search_for_id(101, 'alpha_core').should be_true
90
+ end
91
+
92
+ it "should return false if there isn't a record" do
93
+ ThinkingSphinx.search_for_id(101, 'alpha_core').should be_false
94
+ end
95
+ end
96
+
97
+ describe '.count' do
98
+ before :each do
99
+ @config = ThinkingSphinx::Configuration.instance
100
+ @client = Riddle::Client.new
101
+
102
+ @config.stub!(:client => @client)
103
+ @client.stub!(:query => {:matches => [], :total_found => 42})
104
+ end
105
+
106
+ it "should fall through to ActiveRecord if called on a class" do
107
+ @client.should_not_receive(:query)
108
+
109
+ Alpha.count
110
+ end
111
+
112
+ it "should return the total number of results if called globally" do
113
+ ThinkingSphinx.count.should == 42
114
+ end
115
+ end
116
+
117
+ describe '.search_count' do
118
+ before :each do
119
+ @config = ThinkingSphinx::Configuration.instance
120
+ @client = Riddle::Client.new
121
+
122
+ @config.stub!(:client => @client)
123
+ @client.stub!(:query => {:matches => [], :total_found => 42})
124
+ end
125
+
126
+ it "should return the total number of results" do
127
+ Alpha.search_count.should == 42
128
+ end
129
+
130
+ it "should not make any calls to the database" do
131
+ Alpha.should_not_receive(:find)
132
+
133
+ Alpha.search_count
134
+ end
135
+ end
136
+
137
+ describe '.facets' do
138
+ before :each do
139
+ ThinkingSphinx::Search.stub!(:bundle_searches => [])
140
+ end
141
+
142
+ it "should return a FacetSearch instance" do
143
+ Alpha.facets.should be_a(ThinkingSphinx::FacetSearch)
144
+ end
145
+
146
+ it "should set the classes option if not already set" do
147
+ facets = Alpha.facets
148
+ facets.options[:classes].should == [Alpha]
149
+ end
150
+
151
+ it "shouldn't set the classes option if already defined" do
152
+ facets = Alpha.facets :classes => [Beta]
153
+ facets.options[:classes].should == [Beta]
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,1387 @@
1
+ require 'spec_helper'
2
+ require 'will_paginate/collection'
3
+
4
+ describe ThinkingSphinx::Search do
5
+ before :each do
6
+ @config = ThinkingSphinx::Configuration.instance
7
+ @client = Riddle::Client.new
8
+
9
+ @config.stub!(:client => @client)
10
+ @client.stub!(:query => {:matches => [], :total_found => 41, :total => 41})
11
+ end
12
+
13
+ it "not request results from the client if not accessing items" do
14
+ @config.should_not_receive(:client)
15
+
16
+ ThinkingSphinx::Search.new.class
17
+ end
18
+
19
+ it "should request results if access is required" do
20
+ @config.should_receive(:client)
21
+
22
+ ThinkingSphinx::Search.new.first
23
+ end
24
+
25
+ describe '#respond_to?' do
26
+ it "should respond to Array methods" do
27
+ ThinkingSphinx::Search.new.respond_to?(:each).should be_true
28
+ end
29
+
30
+ it "should respond to Search methods" do
31
+ ThinkingSphinx::Search.new.respond_to?(:per_page).should be_true
32
+ end
33
+ end
34
+
35
+ describe '#populated?' do
36
+ before :each do
37
+ @search = ThinkingSphinx::Search.new
38
+ end
39
+
40
+ it "should be false if the client request has not been made" do
41
+ @search.populated?.should be_false
42
+ end
43
+
44
+ it "should be true once the client request has been made" do
45
+ @search.first
46
+ @search.should be_populated
47
+ end
48
+
49
+ it "should be populated if :populate is set to true" do
50
+ search = ThinkingSphinx::Search.new(:populate => true)
51
+ search.should be_populated
52
+ end
53
+ end
54
+
55
+ describe '#error?' do
56
+ before :each do
57
+ @search = ThinkingSphinx::Search.new
58
+ end
59
+
60
+ it "should be false if client requests have not resulted in an error" do
61
+ @search.should_receive(:error).and_return(nil)
62
+ @search.error?.should_not be_true
63
+ end
64
+
65
+ it "should be true when client requests result in an error" do
66
+ @search.should_receive(:error).and_return("error message")
67
+ @search.error?.should be_true
68
+ end
69
+ end
70
+
71
+ describe '#warning?' do
72
+ before :each do
73
+ @search = ThinkingSphinx::Search.new
74
+ end
75
+
76
+ it "should be false if client requests have not resulted in a warning" do
77
+ @search.should_receive(:warning).and_return(nil)
78
+ @search.warning?.should_not be_true
79
+ end
80
+
81
+ it "should be true when client requests result in an error" do
82
+ @search.should_receive(:warning).and_return("warning message")
83
+ @search.warning?.should be_true
84
+ end
85
+ end
86
+
87
+ describe '#results' do
88
+ it "should populate search results before returning" do
89
+ @search = ThinkingSphinx::Search.new
90
+ @search.populated?.should be_false
91
+
92
+ @search.results
93
+ @search.populated?.should be_true
94
+ end
95
+ end
96
+
97
+ describe '#method_missing' do
98
+ before :each do
99
+ Alpha.sphinx_scope(:by_name) { |name|
100
+ {:conditions => {:name => name}}
101
+ }
102
+ Alpha.sphinx_scope(:ids_only) { {:ids_only => true} }
103
+ end
104
+
105
+ after :each do
106
+ Alpha.remove_sphinx_scopes
107
+ end
108
+
109
+ it "should handle Array methods" do
110
+ ThinkingSphinx::Search.new.private_methods.should be_an(Array)
111
+ end
112
+
113
+ it "should raise a NoMethodError exception if unknown method" do
114
+ lambda {
115
+ ThinkingSphinx::Search.new.foo
116
+ }.should raise_error(NoMethodError)
117
+ end
118
+
119
+ it "should not request results from client if method does not exist" do
120
+ @client.should_not_receive(:query)
121
+
122
+ lambda {
123
+ ThinkingSphinx::Search.new.foo
124
+ }.should raise_error(NoMethodError)
125
+ end
126
+
127
+ it "should accept sphinx scopes" do
128
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
129
+
130
+ lambda {
131
+ search.by_name('Pat')
132
+ }.should_not raise_error(NoMethodError)
133
+ end
134
+
135
+ it "should return itself when using a sphinx scope" do
136
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
137
+ search.by_name('Pat').object_id.should == search.object_id
138
+ end
139
+
140
+ it "should keep the same search object when chaining multiple scopes" do
141
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
142
+ search.by_name('Pat').ids_only.object_id.should == search.object_id
143
+ end
144
+ end
145
+
146
+ describe '.search' do
147
+ it "return the output of ThinkingSphinx.search" do
148
+ @results = [] # to confirm same object
149
+ ThinkingSphinx.stub!(:search => @results)
150
+
151
+ ActiveSupport::Deprecation.silence do
152
+ ThinkingSphinx::Search.search.object_id.should == @results.object_id
153
+ end
154
+ end
155
+ end
156
+
157
+ describe '.search_for_ids' do
158
+ it "return the output of ThinkingSphinx.search_for_ids" do
159
+ @results = [] # to confirm same object
160
+ ThinkingSphinx.stub!(:search_for_ids => @results)
161
+
162
+ ActiveSupport::Deprecation.silence do
163
+ ThinkingSphinx::Search.search_for_ids.object_id.
164
+ should == @results.object_id
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '.search_for_id' do
170
+ it "return the output of ThinkingSphinx.search_for_ids" do
171
+ @results = [] # to confirm same object
172
+ ThinkingSphinx.stub!(:search_for_id => @results)
173
+
174
+ ActiveSupport::Deprecation.silence do
175
+ ThinkingSphinx::Search.search_for_id.object_id.
176
+ should == @results.object_id
177
+ end
178
+ end
179
+ end
180
+
181
+ describe '.count' do
182
+ it "return the output of ThinkingSphinx.search" do
183
+ @results = [] # to confirm same object
184
+ ThinkingSphinx.stub!(:count => @results)
185
+
186
+ ActiveSupport::Deprecation.silence do
187
+ ThinkingSphinx::Search.count.object_id.should == @results.object_id
188
+ end
189
+ end
190
+ end
191
+
192
+ describe '.facets' do
193
+ it "return the output of ThinkingSphinx.facets" do
194
+ @results = [] # to confirm same object
195
+ ThinkingSphinx.stub!(:facets => @results)
196
+
197
+ ActiveSupport::Deprecation.silence do
198
+ ThinkingSphinx::Search.facets.object_id.should == @results.object_id
199
+ end
200
+ end
201
+ end
202
+
203
+ describe '.matching_fields' do
204
+ it "should return objects with indexes matching 1's in the bitmask" do
205
+ fields = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta']
206
+ ThinkingSphinx::Search.matching_fields(fields, 85).
207
+ should == ['alpha', 'gamma', 'epsilon', 'eta']
208
+
209
+ ThinkingSphinx::Search.matching_fields(fields, 42).
210
+ should == ['beta', 'delta', 'zeta']
211
+ end
212
+ end
213
+
214
+ describe '#populate' do
215
+ before :each do
216
+ @alpha_a, @alpha_b = Alpha.new, Alpha.new
217
+ @beta_a, @beta_b = Beta.new, Beta.new
218
+
219
+ @alpha_a.stub! :id => 1, :read_attribute => 1
220
+ @alpha_b.stub! :id => 2, :read_attribute => 2
221
+ @beta_a.stub! :id => 1, :read_attribute => 1
222
+ @beta_b.stub! :id => 2, :read_attribute => 2
223
+
224
+ @client.stub! :query => {
225
+ :matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a),
226
+ :fields => ["one", "two", "three", "four", "five"]
227
+ }
228
+ Alpha.stub! :find => [@alpha_a, @alpha_b]
229
+ Beta.stub! :find => [@beta_a, @beta_b]
230
+ end
231
+
232
+ it "should issue only one select per model" do
233
+ Alpha.should_receive(:find).once.and_return([@alpha_a, @alpha_b])
234
+ Beta.should_receive(:find).once.and_return([@beta_a, @beta_b])
235
+
236
+ ThinkingSphinx::Search.new.first
237
+ end
238
+
239
+ it "should mix the results from different models" do
240
+ search = ThinkingSphinx::Search.new
241
+ search[0].should be_a(Alpha)
242
+ search[1].should be_a(Beta)
243
+ search[2].should be_a(Alpha)
244
+ search[3].should be_a(Beta)
245
+ end
246
+
247
+ it "should maintain the Xoopit ordering for results" do
248
+ search = ThinkingSphinx::Search.new
249
+ search[0].id.should == 1
250
+ search[1].id.should == 2
251
+ search[2].id.should == 2
252
+ search[3].id.should == 1
253
+ end
254
+
255
+ it "should use the requested classes to generate the index argument" do
256
+ @client.should_receive(:query) do |query, index, comment|
257
+ index.should == 'alpha_core,beta_core,beta_delta'
258
+ end
259
+
260
+ ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
261
+ end
262
+
263
+ it "should restrict includes to the relevant classes" do
264
+ Alpha.should_receive(:find) do |type, options|
265
+ options[:include].should == [:betas]
266
+ [@alpha_a, @alpha_b]
267
+ end
268
+
269
+ Beta.should_receive(:find) do |type, options|
270
+ options[:include].should == [:gammas]
271
+ [@beta_a, @beta_b]
272
+ end
273
+
274
+ ThinkingSphinx::Search.new(:include => [:betas, :gammas]).first
275
+ end
276
+
277
+ it "should restrict single includes to the relevant classes" do
278
+ Alpha.should_receive(:find) do |type, options|
279
+ options[:include].should == :betas
280
+ [@alpha_a, @alpha_b]
281
+ end
282
+
283
+ Beta.should_receive(:find) do |type, options|
284
+ options[:include].should be_nil
285
+ [@beta_a, @beta_b]
286
+ end
287
+
288
+ ThinkingSphinx::Search.new(:include => :betas).first
289
+ end
290
+
291
+ it "should respect complex includes" do
292
+ Alpha.should_receive(:find) do |type, options|
293
+ options[:include].should == [:thetas, {:betas => :gammas}]
294
+ [@alpha_a, @alpha_b]
295
+ end
296
+
297
+ Beta.should_receive(:find) do |type, options|
298
+ options[:include].should be_nil
299
+ [@beta_a, @beta_b]
300
+ end
301
+
302
+ ThinkingSphinx::Search.new(:include => [:thetas, {:betas => :gammas}]).first
303
+ end
304
+
305
+ it "should respect hash includes" do
306
+ Alpha.should_receive(:find) do |type, options|
307
+ options[:include].should == {:betas => :gammas}
308
+ [@alpha_a, @alpha_b]
309
+ end
310
+
311
+ Beta.should_receive(:find) do |type, options|
312
+ options[:include].should be_nil
313
+ [@beta_a, @beta_b]
314
+ end
315
+
316
+ ThinkingSphinx::Search.new(:include => {:betas => :gammas}).first
317
+ end
318
+
319
+ it "should respect includes for single class searches" do
320
+ Alpha.should_receive(:find) do |type, options|
321
+ options[:include].should == {:betas => :gammas}
322
+ [@alpha_a, @alpha_b]
323
+ end
324
+
325
+ ThinkingSphinx::Search.new(
326
+ :include => {:betas => :gammas},
327
+ :classes => [Alpha]
328
+ ).first
329
+ end
330
+
331
+ describe 'query' do
332
+ it "should concatenate arguments with spaces" do
333
+ @client.should_receive(:query) do |query, index, comment|
334
+ query.should == 'two words'
335
+ end
336
+
337
+ ThinkingSphinx::Search.new('two', 'words').first
338
+ end
339
+
340
+ it "should append conditions to the query" do
341
+ @client.should_receive(:query) do |query, index, comment|
342
+ query.should == 'general @focused specific'
343
+ end
344
+
345
+ ThinkingSphinx::Search.new('general', :conditions => {
346
+ :focused => 'specific'
347
+ }).first
348
+ end
349
+
350
+ it "append multiple conditions together" do
351
+ @client.should_receive(:query) do |query, index, comment|
352
+ query.should match(/general.+@foo word/)
353
+ query.should match(/general.+@bar word/)
354
+ end
355
+
356
+ ThinkingSphinx::Search.new('general', :conditions => {
357
+ :foo => 'word', :bar => 'word'
358
+ }).first
359
+ end
360
+
361
+ it "should apply stars if requested, and handle full extended syntax" do
362
+ input = %{a b* c (d | e) 123 5&6 (f_f g) !h "i j" "k l"~10 "m n"/3 @o p -(q|r)}
363
+ expected = %{*a* b* *c* (*d* | *e*) *123* *5*&*6* (*f_f* *g*) !*h* "i j" "k l"~10 "m n"/3 @o *p* -(*q*|*r*)}
364
+
365
+ @client.should_receive(:query) do |query, index, comment|
366
+ query.should == expected
367
+ end
368
+
369
+ ThinkingSphinx::Search.new(input, :star => true).first
370
+ end
371
+
372
+ it "should default to /\w+/ as token for auto-starring" do
373
+ @client.should_receive(:query) do |query, index, comment|
374
+ query.should == '*foo*@*bar*.*com*'
375
+ end
376
+
377
+ ThinkingSphinx::Search.new('foo@bar.com', :star => true).first
378
+ end
379
+
380
+ it "should honour custom star tokens" do
381
+ @client.should_receive(:query) do |query, index, comment|
382
+ query.should == '*foo@bar.com* -*foo-bar*'
383
+ end
384
+
385
+ ThinkingSphinx::Search.new(
386
+ 'foo@bar.com -foo-bar', :star => /[\w@.-]+/u
387
+ ).first
388
+ end
389
+
390
+ it "should ignore multi-field limitations" do
391
+ @client.should_receive(:query) do |query, index, comment|
392
+ query.should == '@(foo,bar) *baz*'
393
+ end
394
+
395
+ ThinkingSphinx::Search.new('@(foo,bar) baz', :star => true).first
396
+ end
397
+
398
+ it "should ignore multi-field limitations with spaces" do
399
+ @client.should_receive(:query) do |query, index, comment|
400
+ query.should == '@(foo bar) *baz*'
401
+ end
402
+
403
+ ThinkingSphinx::Search.new('@(foo bar) baz', :star => true).first
404
+ end
405
+
406
+ it "should ignore multi-field limitations in the middle of queries" do
407
+ @client.should_receive(:query) do |query, index, comment|
408
+ query.should == '*baz* @foo *bar* @(foo,bar) *baz*'
409
+ end
410
+
411
+ ThinkingSphinx::Search.new(
412
+ 'baz @foo bar @(foo,bar) baz', :star => true
413
+ ).first
414
+ end
415
+ end
416
+
417
+ describe 'comment' do
418
+ it "should add comment if explicitly provided" do
419
+ @client.should_receive(:query) do |query, index, comment|
420
+ comment.should == 'custom log'
421
+ end
422
+
423
+ ThinkingSphinx::Search.new(:comment => 'custom log').first
424
+ end
425
+
426
+ it "should default to a blank comment" do
427
+ @client.should_receive(:query) do |query, index, comment|
428
+ comment.should == ''
429
+ end
430
+
431
+ ThinkingSphinx::Search.new.first
432
+ end
433
+ end
434
+
435
+ describe 'match mode' do
436
+ it "should default to :all" do
437
+ ThinkingSphinx::Search.new.first
438
+
439
+ @client.match_mode.should == :all
440
+ end
441
+
442
+ it "should default to :extended if conditions are supplied" do
443
+ ThinkingSphinx::Search.new('general', :conditions => {
444
+ :foo => 'word', :bar => 'word'
445
+ }).first
446
+
447
+ @client.match_mode.should == :extended
448
+ end
449
+
450
+ it "should use explicit match modes" do
451
+ ThinkingSphinx::Search.new('general', :conditions => {
452
+ :foo => 'word', :bar => 'word'
453
+ }, :match_mode => :extended2).first
454
+
455
+ @client.match_mode.should == :extended2
456
+ end
457
+ end
458
+
459
+ describe 'sphinx_select' do
460
+ it "should default to *" do
461
+ ThinkingSphinx::Search.new.first
462
+
463
+ @client.select.should == "*"
464
+ end
465
+
466
+ it "should get set on the client if specified" do
467
+ ThinkingSphinx::Search.new('general',
468
+ :sphinx_select => "*, foo as bar"
469
+ ).first
470
+
471
+ @client.select.should == "*, foo as bar"
472
+ end
473
+
474
+ end
475
+
476
+ describe 'pagination' do
477
+ it "should set the limit using per_page" do
478
+ ThinkingSphinx::Search.new(:per_page => 30).first
479
+ @client.limit.should == 30
480
+ end
481
+
482
+ it "should set the offset if pagination is requested" do
483
+ ThinkingSphinx::Search.new(:page => 3).first
484
+ @client.offset.should == 40
485
+ end
486
+
487
+ it "should set the offset by the per_page value" do
488
+ ThinkingSphinx::Search.new(:page => 3, :per_page => 30).first
489
+ @client.offset.should == 60
490
+ end
491
+ end
492
+
493
+ describe 'filters' do
494
+ it "should filter out deleted values by default" do
495
+ ThinkingSphinx::Search.new.first
496
+
497
+ filter = @client.filters.last
498
+ filter.values.should == [0]
499
+ filter.attribute.should == 'sphinx_deleted'
500
+ filter.exclude?.should be_false
501
+ end
502
+
503
+ it "should add class filters for explicit classes" do
504
+ ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
505
+
506
+ filter = @client.filters.last
507
+ filter.values.should == [Alpha.to_crc32, Beta.to_crc32]
508
+ filter.attribute.should == 'class_crc'
509
+ filter.exclude?.should be_false
510
+ end
511
+
512
+ it "should add class filters for subclasses of requested classes" do
513
+ ThinkingSphinx::Search.new(:classes => [Person]).first
514
+
515
+ filter = @client.filters.last
516
+ filter.values.should == [
517
+ Parent.to_crc32, Admin::Person.to_crc32,
518
+ Child.to_crc32, Person.to_crc32
519
+ ]
520
+ filter.attribute.should == 'class_crc'
521
+ filter.exclude?.should be_false
522
+ end
523
+
524
+ it "should append inclusive filters of integers" do
525
+ ThinkingSphinx::Search.new(:with => {:int => 1}).first
526
+
527
+ filter = @client.filters.last
528
+ filter.values.should == [1]
529
+ filter.attribute.should == 'int'
530
+ filter.exclude?.should be_false
531
+ end
532
+
533
+ it "should append inclusive filters of floats" do
534
+ ThinkingSphinx::Search.new(:with => {:float => 1.5}).first
535
+
536
+ filter = @client.filters.last
537
+ filter.values.should == [1.5]
538
+ filter.attribute.should == 'float'
539
+ filter.exclude?.should be_false
540
+ end
541
+
542
+ it "should append inclusive filters of booleans" do
543
+ ThinkingSphinx::Search.new(:with => {:boolean => true}).first
544
+
545
+ filter = @client.filters.last
546
+ filter.values.should == [true]
547
+ filter.attribute.should == 'boolean'
548
+ filter.exclude?.should be_false
549
+ end
550
+
551
+ it "should append inclusive filters of arrays" do
552
+ ThinkingSphinx::Search.new(:with => {:ints => [1, 2, 3]}).first
553
+
554
+ filter = @client.filters.last
555
+ filter.values.should == [1, 2, 3]
556
+ filter.attribute.should == 'ints'
557
+ filter.exclude?.should be_false
558
+ end
559
+
560
+ it "should treat nils in arrays as 0" do
561
+ ThinkingSphinx::Search.new(:with => {:ints => [nil, 1, 2, 3]}).first
562
+
563
+ filter = @client.filters.last
564
+ filter.values.should == [0, 1, 2, 3]
565
+ end
566
+
567
+ it "should append inclusive filters of time ranges" do
568
+ first, last = 1.week.ago, Time.now
569
+ ThinkingSphinx::Search.new(:with => {
570
+ :time => first..last
571
+ }).first
572
+
573
+ filter = @client.filters.last
574
+ filter.values.should == (first.to_i..last.to_i)
575
+ filter.attribute.should == 'time'
576
+ filter.exclude?.should be_false
577
+ end
578
+
579
+ it "should append exclusive filters of integers" do
580
+ ThinkingSphinx::Search.new(:without => {:int => 1}).first
581
+
582
+ filter = @client.filters.last
583
+ filter.values.should == [1]
584
+ filter.attribute.should == 'int'
585
+ filter.exclude?.should be_true
586
+ end
587
+
588
+ it "should append exclusive filters of floats" do
589
+ ThinkingSphinx::Search.new(:without => {:float => 1.5}).first
590
+
591
+ filter = @client.filters.last
592
+ filter.values.should == [1.5]
593
+ filter.attribute.should == 'float'
594
+ filter.exclude?.should be_true
595
+ end
596
+
597
+ it "should append exclusive filters of booleans" do
598
+ ThinkingSphinx::Search.new(:without => {:boolean => true}).first
599
+
600
+ filter = @client.filters.last
601
+ filter.values.should == [true]
602
+ filter.attribute.should == 'boolean'
603
+ filter.exclude?.should be_true
604
+ end
605
+
606
+ it "should append exclusive filters of arrays" do
607
+ ThinkingSphinx::Search.new(:without => {:ints => [1, 2, 3]}).first
608
+
609
+ filter = @client.filters.last
610
+ filter.values.should == [1, 2, 3]
611
+ filter.attribute.should == 'ints'
612
+ filter.exclude?.should be_true
613
+ end
614
+
615
+ it "should append exclusive filters of time ranges" do
616
+ first, last = 1.week.ago, Time.now
617
+ ThinkingSphinx::Search.new(:without => {
618
+ :time => first..last
619
+ }).first
620
+
621
+ filter = @client.filters.last
622
+ filter.values.should == (first.to_i..last.to_i)
623
+ filter.attribute.should == 'time'
624
+ filter.exclude?.should be_true
625
+ end
626
+
627
+ it "should add separate filters for each item in a with_all value" do
628
+ ThinkingSphinx::Search.new(:with_all => {:ints => [1, 2, 3]}).first
629
+
630
+ filters = @client.filters[-3, 3]
631
+ filters.each do |filter|
632
+ filter.attribute.should == 'ints'
633
+ filter.exclude?.should be_false
634
+ end
635
+
636
+ filters[0].values.should == [1]
637
+ filters[1].values.should == [2]
638
+ filters[2].values.should == [3]
639
+ end
640
+
641
+ it "should filter out specific ids using :without_ids" do
642
+ ThinkingSphinx::Search.new(:without_ids => [4, 5, 6]).first
643
+
644
+ filter = @client.filters.last
645
+ filter.values.should == [4, 5, 6]
646
+ filter.attribute.should == 'sphinx_internal_id'
647
+ filter.exclude?.should be_true
648
+ end
649
+ end
650
+
651
+ describe 'sort mode' do
652
+ it "should use :relevance as a default" do
653
+ ThinkingSphinx::Search.new.first
654
+ @client.sort_mode.should == :relevance
655
+ end
656
+
657
+ it "should use :attr_asc if a symbol is supplied to :order" do
658
+ ThinkingSphinx::Search.new(:order => :created_at).first
659
+ @client.sort_mode.should == :attr_asc
660
+ end
661
+
662
+ it "should use :attr_desc if :desc is the mode" do
663
+ ThinkingSphinx::Search.new(
664
+ :order => :created_at, :sort_mode => :desc
665
+ ).first
666
+ @client.sort_mode.should == :attr_desc
667
+ end
668
+
669
+ it "should use :extended if a string is supplied to :order" do
670
+ ThinkingSphinx::Search.new(:order => "created_at ASC").first
671
+ @client.sort_mode.should == :extended
672
+ end
673
+
674
+ it "should use :expr if explicitly requested" do
675
+ ThinkingSphinx::Search.new(
676
+ :order => "created_at ASC", :sort_mode => :expr
677
+ ).first
678
+ @client.sort_mode.should == :expr
679
+ end
680
+
681
+ it "should use :attr_desc if explicitly requested" do
682
+ ThinkingSphinx::Search.new(
683
+ :order => "created_at", :sort_mode => :desc
684
+ ).first
685
+ @client.sort_mode.should == :attr_desc
686
+ end
687
+ end
688
+
689
+ describe 'sort by' do
690
+ it "should presume order symbols are attributes" do
691
+ ThinkingSphinx::Search.new(:order => :created_at).first
692
+ @client.sort_by.should == 'created_at'
693
+ end
694
+
695
+ it "replace field names with their sortable attributes" do
696
+ ThinkingSphinx::Search.new(:order => :name, :classes => [Alpha]).first
697
+ @client.sort_by.should == 'name_sort'
698
+ end
699
+
700
+ it "should replace field names in strings" do
701
+ ThinkingSphinx::Search.new(
702
+ :order => "created_at ASC, name DESC", :classes => [Alpha]
703
+ ).first
704
+ @client.sort_by.should == 'created_at ASC, name_sort DESC'
705
+ end
706
+ end
707
+
708
+ describe 'max matches' do
709
+ it "should use the global setting by default" do
710
+ ThinkingSphinx::Search.new.first
711
+ @client.max_matches.should == 1000
712
+ end
713
+
714
+ it "should use explicit setting" do
715
+ ThinkingSphinx::Search.new(:max_matches => 2000).first
716
+ @client.max_matches.should == 2000
717
+ end
718
+ end
719
+
720
+ describe 'field weights' do
721
+ it "should set field weights as provided" do
722
+ ThinkingSphinx::Search.new(
723
+ :field_weights => {'foo' => 10, 'bar' => 5}
724
+ ).first
725
+
726
+ @client.field_weights.should == {
727
+ 'foo' => 10, 'bar' => 5
728
+ }
729
+ end
730
+
731
+ it "should use field weights set in the index" do
732
+ ThinkingSphinx::Search.new(:classes => [Alpha]).first
733
+
734
+ @client.field_weights.should == {'name' => 10}
735
+ end
736
+ end
737
+
738
+ describe 'index weights' do
739
+ it "should send index weights through to the client" do
740
+ ThinkingSphinx::Search.new(:index_weights => {'foo' => 100}).first
741
+ @client.index_weights.should == {'foo' => 100}
742
+ end
743
+
744
+ it "should convert classes to their core and delta index names" do
745
+ ThinkingSphinx::Search.new(:index_weights => {Alpha => 100}).first
746
+ @client.index_weights.should == {
747
+ 'alpha_core' => 100,
748
+ 'alpha_delta' => 100
749
+ }
750
+ end
751
+ end
752
+
753
+ describe 'grouping' do
754
+ it "should convert group into group_by and group_function" do
755
+ ThinkingSphinx::Search.new(:group => :edition).first
756
+
757
+ @client.group_function.should == :attr
758
+ @client.group_by.should == "edition"
759
+ end
760
+
761
+ it "should pass on explicit grouping arguments" do
762
+ ThinkingSphinx::Search.new(
763
+ :group_by => 'created_at',
764
+ :group_function => :attr,
765
+ :group_clause => 'clause',
766
+ :group_distinct => 'distinct'
767
+ ).first
768
+
769
+ @client.group_by.should == 'created_at'
770
+ @client.group_function.should == :attr
771
+ @client.group_clause.should == 'clause'
772
+ @client.group_distinct.should == 'distinct'
773
+ end
774
+ end
775
+
776
+ describe 'anchor' do
777
+ it "should detect lat and lng attributes on the given model" do
778
+ ThinkingSphinx::Search.new(
779
+ :geo => [1.0, -1.0],
780
+ :classes => [Alpha]
781
+ ).first
782
+
783
+ @client.anchor[:latitude_attribute].should == 'lat'
784
+ @client.anchor[:longitude_attribute].should == 'lng'
785
+ end
786
+
787
+ it "should detect lat and lon attributes on the given model" do
788
+ ThinkingSphinx::Search.new(
789
+ :geo => [1.0, -1.0],
790
+ :classes => [Beta]
791
+ ).first
792
+
793
+ @client.anchor[:latitude_attribute].should == 'lat'
794
+ @client.anchor[:longitude_attribute].should == 'lon'
795
+ end
796
+
797
+ it "should detect latitude and longitude attributes on the given model" do
798
+ ThinkingSphinx::Search.new(
799
+ :geo => [1.0, -1.0],
800
+ :classes => [Person]
801
+ ).first
802
+
803
+ @client.anchor[:latitude_attribute].should == 'latitude'
804
+ @client.anchor[:longitude_attribute].should == 'longitude'
805
+ end
806
+
807
+ it "should accept manually defined latitude and longitude attributes" do
808
+ ThinkingSphinx::Search.new(
809
+ :geo => [1.0, -1.0],
810
+ :classes => [Alpha],
811
+ :latitude_attr => :updown,
812
+ :longitude_attr => :leftright
813
+ ).first
814
+
815
+ @client.anchor[:latitude_attribute].should == 'updown'
816
+ @client.anchor[:longitude_attribute].should == 'leftright'
817
+ end
818
+
819
+ it "should accept manually defined latitude and longitude attributes in the given model" do
820
+ ThinkingSphinx::Search.new(
821
+ :geo => [1.0, -1.0],
822
+ :classes => [Friendship]
823
+ ).first
824
+
825
+ @client.anchor[:latitude_attribute].should == 'person_id'
826
+ @client.anchor[:longitude_attribute].should == 'person_id'
827
+ end
828
+
829
+ it "should accept geo array for geo-position values" do
830
+ ThinkingSphinx::Search.new(
831
+ :geo => [1.0, -1.0],
832
+ :classes => [Alpha]
833
+ ).first
834
+
835
+ @client.anchor[:latitude].should == 1.0
836
+ @client.anchor[:longitude].should == -1.0
837
+ end
838
+
839
+ it "should accept lat and lng options for geo-position values" do
840
+ ThinkingSphinx::Search.new(
841
+ :lat => 1.0,
842
+ :lng => -1.0,
843
+ :classes => [Alpha]
844
+ ).first
845
+
846
+ @client.anchor[:latitude].should == 1.0
847
+ @client.anchor[:longitude].should == -1.0
848
+ end
849
+ end
850
+
851
+ describe 'sql ordering' do
852
+ before :each do
853
+ @client.stub! :query => {
854
+ :matches => minimal_result_hashes(@alpha_b, @alpha_a)
855
+ }
856
+ Alpha.stub! :find => [@alpha_a, @alpha_b]
857
+ end
858
+
859
+ it "shouldn't re-sort SQL results based on Sphinx information" do
860
+ search = ThinkingSphinx::Search.new(
861
+ :classes => [Alpha],
862
+ :sql_order => 'id'
863
+ )
864
+ search.first.should == @alpha_a
865
+ search.last.should == @alpha_b
866
+ end
867
+
868
+ it "should use the option for the ActiveRecord::Base#find calls" do
869
+ Alpha.should_receive(:find) do |mode, options|
870
+ options[:order].should == 'id'
871
+ end
872
+
873
+ ThinkingSphinx::Search.new(
874
+ :classes => [Alpha],
875
+ :sql_order => 'id'
876
+ ).first
877
+ end
878
+ end
879
+
880
+ describe ':only option' do
881
+ it "returns the requested attribute as an array" do
882
+ ThinkingSphinx::Search.new(:only => :class_crc).first.
883
+ should == Alpha.to_crc32
884
+ end
885
+
886
+ it "returns multiple attributes as hashes with values" do
887
+ ThinkingSphinx::Search.new(
888
+ :only => [:class_crc, :sphinx_internal_id]
889
+ ).first.should == {
890
+ :class_crc => Alpha.to_crc32,
891
+ :sphinx_internal_id => @alpha_a.id
892
+ }
893
+ end
894
+
895
+ it "handles strings for a single attribute name" do
896
+ ThinkingSphinx::Search.new(:only => 'class_crc').first.
897
+ should == Alpha.to_crc32
898
+ end
899
+
900
+ it "handles strings for multiple attribute names" do
901
+ ThinkingSphinx::Search.new(
902
+ :only => ['class_crc', 'sphinx_internal_id']
903
+ ).first.should == {
904
+ :class_crc => Alpha.to_crc32,
905
+ :sphinx_internal_id => @alpha_a.id
906
+ }
907
+ end
908
+ end
909
+
910
+ context 'result objects' do
911
+ describe '#excerpts' do
912
+ before :each do
913
+ @search = ThinkingSphinx::Search.new
914
+ end
915
+
916
+ it "should add excerpts method if objects don't already have one" do
917
+ @search.first.should respond_to(:excerpts)
918
+ end
919
+
920
+ it "should return an instance of ThinkingSphinx::Excerpter" do
921
+ @search.first.excerpts.should be_a(ThinkingSphinx::Excerpter)
922
+ end
923
+
924
+ it "should not add excerpts method if objects already have one" do
925
+ @search.last.excerpts.should_not be_a(ThinkingSphinx::Excerpter)
926
+ end
927
+
928
+ # Fails in Ruby 1.9 (or maybe it's an RSpec update). Not sure why.
929
+ it "should set up the excerpter with the instances and search" do
930
+ [@alpha_a, @beta_b, @alpha_b, @beta_a].each do |object|
931
+ ThinkingSphinx::Excerpter.should_receive(:new).with(@search, object)
932
+ end
933
+
934
+ @search.first
935
+ end
936
+ end
937
+
938
+ describe '#sphinx_attributes' do
939
+ before :each do
940
+ @search = ThinkingSphinx::Search.new
941
+ end
942
+
943
+ it "should add sphinx_attributes method if objects don't already have one" do
944
+ @search.last.should respond_to(:sphinx_attributes)
945
+ end
946
+
947
+ it "should return a hash" do
948
+ @search.last.sphinx_attributes.should be_a(Hash)
949
+ end
950
+
951
+ it "should not add sphinx_attributes if objects have a method of that name already" do
952
+ @search.first.sphinx_attributes.should_not be_a(Hash)
953
+ end
954
+
955
+ it "should pair sphinx_attributes with the correct hash" do
956
+ hash = @search.last.sphinx_attributes
957
+ hash['sphinx_internal_id'].should == @search.last.id
958
+ hash['class_crc'].should == @search.last.class.to_crc32
959
+ end
960
+ end
961
+
962
+ describe '#matching_fields' do
963
+ it "should add matching_fields method if using fieldmask ranking mode" do
964
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
965
+ search.first.should respond_to(:matching_fields)
966
+ end
967
+
968
+ it "should not add matching_fields method if object already have one" do
969
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
970
+ search.last.matching_fields.should_not be_an(Array)
971
+ end
972
+
973
+ it "should return an array" do
974
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
975
+ search.first.matching_fields.should be_an(Array)
976
+ end
977
+
978
+ it "should return the fields that the bitmask match" do
979
+ search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
980
+ search.first.matching_fields.should == ['one', 'three', 'five']
981
+ end
982
+ end
983
+ end
984
+
985
+ context 'Sphinx errors' do
986
+ describe '#error?' do
987
+ before :each do
988
+ @client.stub! :query => {
989
+ :error => @warning = "Not good"
990
+ }
991
+ # @search.should_receive(:error).and_return(nil)
992
+ end
993
+ it "should raise an error" do
994
+ lambda{
995
+ ThinkingSphinx::Search.new.first
996
+ }.should raise_error(ThinkingSphinx::SphinxError)
997
+ end
998
+ it "should not raise an error when ignore_errors is true" do
999
+ lambda{
1000
+ ThinkingSphinx::Search.new(:ignore_errors => true).first
1001
+ }.should_not raise_error(ThinkingSphinx::SphinxError)
1002
+ end
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ describe '#current_page' do
1008
+ it "should return 1 by default" do
1009
+ ThinkingSphinx::Search.new.current_page.should == 1
1010
+ end
1011
+
1012
+ it "should handle string page values" do
1013
+ ThinkingSphinx::Search.new(:page => '2').current_page.should == 2
1014
+ end
1015
+
1016
+ it "should handle empty string page values" do
1017
+ ThinkingSphinx::Search.new(:page => '').current_page.should == 1
1018
+ end
1019
+
1020
+ it "should return the requested page" do
1021
+ ThinkingSphinx::Search.new(:page => 10).current_page.should == 10
1022
+ end
1023
+ end
1024
+
1025
+ describe '#per_page' do
1026
+ it "should return 20 by default" do
1027
+ ThinkingSphinx::Search.new.per_page.should == 20
1028
+ end
1029
+
1030
+ it "should allow for custom values" do
1031
+ ThinkingSphinx::Search.new(:per_page => 30).per_page.should == 30
1032
+ end
1033
+
1034
+ it "should prioritise :limit over :per_page if given" do
1035
+ ThinkingSphinx::Search.new(
1036
+ :per_page => 30, :limit => 40
1037
+ ).per_page.should == 40
1038
+ end
1039
+
1040
+ it "should allow for string arguments" do
1041
+ ThinkingSphinx::Search.new(:per_page => '10').per_page.should == 10
1042
+ end
1043
+ end
1044
+
1045
+ describe '#total_pages' do
1046
+ it "should calculate the total pages depending on per_page and total_entries" do
1047
+ ThinkingSphinx::Search.new.total_pages.should == 3
1048
+ end
1049
+
1050
+ it "should allow for custom per_page values" do
1051
+ ThinkingSphinx::Search.new(:per_page => 30).total_pages.should == 2
1052
+ end
1053
+
1054
+ it "should not overstep the max_matches implied limit" do
1055
+ @client.stub!(:query => {
1056
+ :matches => [], :total_found => 41, :total => 40
1057
+ })
1058
+
1059
+ ThinkingSphinx::Search.new.total_pages.should == 2
1060
+ end
1061
+
1062
+ it "should return 0 if there is no index and therefore no results" do
1063
+ @client.stub!(:query => {
1064
+ :matches => [], :total_found => nil, :total => nil
1065
+ })
1066
+
1067
+ ThinkingSphinx::Search.new.total_pages.should == 0
1068
+ end
1069
+ end
1070
+
1071
+ describe '#next_page' do
1072
+ it "should return one more than the current page" do
1073
+ ThinkingSphinx::Search.new.next_page.should == 2
1074
+ end
1075
+
1076
+ it "should return nil if on the last page" do
1077
+ ThinkingSphinx::Search.new(:page => 3).next_page.should be_nil
1078
+ end
1079
+ end
1080
+
1081
+ describe '#previous_page' do
1082
+ it "should return one less than the current page" do
1083
+ ThinkingSphinx::Search.new(:page => 2).previous_page.should == 1
1084
+ end
1085
+
1086
+ it "should return nil if on the first page" do
1087
+ ThinkingSphinx::Search.new.previous_page.should be_nil
1088
+ end
1089
+ end
1090
+
1091
+ describe '#total_entries' do
1092
+ it "should return the total number of results, not just the amount on the page" do
1093
+ ThinkingSphinx::Search.new.total_entries.should == 41
1094
+ end
1095
+
1096
+ it "should return 0 if there is no index and therefore no results" do
1097
+ @client.stub!(:query => {
1098
+ :matches => [], :total_found => nil
1099
+ })
1100
+
1101
+ ThinkingSphinx::Search.new.total_entries.should == 0
1102
+ end
1103
+ end
1104
+
1105
+ describe '#offset' do
1106
+ it "should default to 0" do
1107
+ ThinkingSphinx::Search.new.offset.should == 0
1108
+ end
1109
+
1110
+ it "should increase by the per_page value for each page in" do
1111
+ ThinkingSphinx::Search.new(:per_page => 25, :page => 2).offset.should == 25
1112
+ end
1113
+
1114
+ it "should prioritise explicit :offset over calculated if given" do
1115
+ ThinkingSphinx::Search.new(:offset => 5).offset.should == 5
1116
+ end
1117
+ end
1118
+
1119
+ describe '#indexes' do
1120
+ it "should default to '*'" do
1121
+ ThinkingSphinx::Search.new.indexes.should == '*'
1122
+ end
1123
+
1124
+ it "should use given class to determine index name" do
1125
+ ThinkingSphinx::Search.new(:classes => [Alpha]).indexes.
1126
+ should == 'alpha_core'
1127
+ end
1128
+
1129
+ it "should add both core and delta indexes for given classes" do
1130
+ ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).indexes.
1131
+ should == 'alpha_core,beta_core,beta_delta'
1132
+ end
1133
+
1134
+ it "should respect the :index option" do
1135
+ ThinkingSphinx::Search.new(:classes => [Alpha], :index => '*').indexes.
1136
+ should == '*'
1137
+ end
1138
+ end
1139
+
1140
+ describe '.each_with_groupby_and_count' do
1141
+ before :each do
1142
+ @alpha = Alpha.new
1143
+ @alpha.stub!(:id => 1, :read_attribute => 1)
1144
+
1145
+ @client.stub! :query => {
1146
+ :matches => [{
1147
+ :attributes => {
1148
+ 'sphinx_internal_id' => @alpha.id,
1149
+ 'class_crc' => Alpha.to_crc32,
1150
+ '@groupby' => 101,
1151
+ '@count' => 5
1152
+ }
1153
+ }]
1154
+ }
1155
+ Alpha.stub!(:find => [@alpha])
1156
+ end
1157
+
1158
+ it "should yield the match, group and count" do
1159
+ search = ThinkingSphinx::Search.new
1160
+ search.each_with_groupby_and_count do |obj, group, count|
1161
+ obj.should == @alpha
1162
+ group.should == 101
1163
+ count.should == 5
1164
+ end
1165
+ end
1166
+
1167
+ it "should be aliased to each_with_group_and_count" do
1168
+ search = ThinkingSphinx::Search.new
1169
+ search.each_with_group_and_count do |obj, group, count|
1170
+ obj.should == @alpha
1171
+ group.should == 101
1172
+ count.should == 5
1173
+ end
1174
+ end
1175
+ end
1176
+
1177
+ describe '.each_with_weighting' do
1178
+ before :each do
1179
+ @alpha = Alpha.new
1180
+ @alpha.stub!(:id => 1, :read_attribute => 1)
1181
+
1182
+ @client.stub! :query => {
1183
+ :matches => [{
1184
+ :attributes => {
1185
+ 'sphinx_internal_id' => @alpha.id,
1186
+ 'class_crc' => Alpha.to_crc32
1187
+ }, :weight => 12
1188
+ }]
1189
+ }
1190
+ Alpha.stub!(:find => [@alpha])
1191
+ end
1192
+
1193
+ it "should yield the match and weight" do
1194
+ search = ThinkingSphinx::Search.new
1195
+ search.each_with_weighting do |obj, weight|
1196
+ obj.should == @alpha
1197
+ weight.should == 12
1198
+ end
1199
+ end
1200
+ end
1201
+
1202
+ describe '.each_with_*' do
1203
+ before :each do
1204
+ @alpha = Alpha.new
1205
+ @alpha.stub!(:id => 1, :read_attribute => 1)
1206
+
1207
+ @client.stub! :query => {
1208
+ :matches => [{
1209
+ :attributes => {
1210
+ 'sphinx_internal_id' => @alpha.id,
1211
+ 'class_crc' => Alpha.to_crc32,
1212
+ '@geodist' => 101,
1213
+ '@groupby' => 102,
1214
+ '@count' => 103
1215
+ }, :weight => 12
1216
+ }]
1217
+ }
1218
+ Alpha.stub!(:find => [@alpha])
1219
+
1220
+ @search = ThinkingSphinx::Search.new
1221
+ end
1222
+
1223
+ it "should yield geodist if requested" do
1224
+ @search.each_with_geodist do |obj, distance|
1225
+ obj.should == @alpha
1226
+ distance.should == 101
1227
+ end
1228
+ end
1229
+
1230
+ it "should yield count if requested" do
1231
+ @search.each_with_count do |obj, count|
1232
+ obj.should == @alpha
1233
+ count.should == 103
1234
+ end
1235
+ end
1236
+
1237
+ it "should yield groupby if requested" do
1238
+ @search.each_with_groupby do |obj, group|
1239
+ obj.should == @alpha
1240
+ group.should == 102
1241
+ end
1242
+ end
1243
+
1244
+ it "should still use the array's each_with_index" do
1245
+ @search.each_with_index do |obj, index|
1246
+ obj.should == @alpha
1247
+ index.should == 0
1248
+ end
1249
+ end
1250
+ end
1251
+
1252
+ describe '#excerpt_for' do
1253
+ before :each do
1254
+ @client.stub!(:excerpts => ['excerpted string'])
1255
+ @client.stub!(:query => {
1256
+ :matches => [],
1257
+ :words => {'one' => {}, 'two' => {}}
1258
+ })
1259
+ @search = ThinkingSphinx::Search.new(:classes => [Alpha])
1260
+ end
1261
+
1262
+ it "should return the Sphinx excerpt value" do
1263
+ @search.excerpt_for('string').should == 'excerpted string'
1264
+ end
1265
+
1266
+ it "should use the given model's core index" do
1267
+ @client.should_receive(:excerpts) do |options|
1268
+ options[:index].should == 'alpha_core'
1269
+ end
1270
+
1271
+ @search.excerpt_for('string')
1272
+ end
1273
+
1274
+ it "should respect the provided index option" do
1275
+ @search = ThinkingSphinx::Search.new(:classes => [Alpha], :index => 'foo')
1276
+ @client.should_receive(:excerpts) do |options|
1277
+ options[:index].should == 'foo'
1278
+ end
1279
+
1280
+ @search.excerpt_for('string')
1281
+ end
1282
+
1283
+ it "should optionally take a second argument to allow for multi-model searches" do
1284
+ @client.should_receive(:excerpts) do |options|
1285
+ options[:index].should == 'beta_core'
1286
+ end
1287
+
1288
+ @search.excerpt_for('string', Beta)
1289
+ end
1290
+
1291
+ it "should join the words together" do
1292
+ @client.should_receive(:excerpts) do |options|
1293
+ options[:words].should == @search.results[:words].keys.join(' ')
1294
+ end
1295
+
1296
+ @search.excerpt_for('string', Beta)
1297
+ end
1298
+
1299
+ it "should use the correct index in STI situations" do
1300
+ @client.should_receive(:excerpts) do |options|
1301
+ options[:index].should == 'person_core'
1302
+ end
1303
+
1304
+ @search.excerpt_for('string', Parent)
1305
+ end
1306
+ end
1307
+
1308
+ describe '#search' do
1309
+ before :each do
1310
+ @search = ThinkingSphinx::Search.new('word',
1311
+ :conditions => {:field => 'field'},
1312
+ :with => {:int => 5}
1313
+ )
1314
+ end
1315
+
1316
+ it "should return itself" do
1317
+ @search.search.object_id.should == @search.object_id
1318
+ end
1319
+
1320
+ it "should merge in arguments" do
1321
+ @client.should_receive(:query) do |query, index, comments|
1322
+ query.should == 'word more @field field'
1323
+ end
1324
+
1325
+ @search.search('more').first
1326
+ end
1327
+
1328
+ it "should merge conditions" do
1329
+ @client.should_receive(:query) do |query, index, comments|
1330
+ query.should match(/@name plato/)
1331
+ query.should match(/@field field/)
1332
+ end
1333
+
1334
+ @search.search(:conditions => {:name => 'plato'}).first
1335
+ end
1336
+
1337
+ it "should merge filters" do
1338
+ @search.search(:with => {:float => 1.5}).first
1339
+
1340
+ @client.filters.detect { |filter|
1341
+ filter.attribute == 'float'
1342
+ }.should_not be_nil
1343
+ @client.filters.detect { |filter|
1344
+ filter.attribute == 'int'
1345
+ }.should_not be_nil
1346
+ end
1347
+ end
1348
+
1349
+ describe '#freeze' do
1350
+ before :each do
1351
+ @search = ThinkingSphinx::Search.new
1352
+ end
1353
+
1354
+ it "should populate the result set" do
1355
+ @search.freeze
1356
+ @search.should be_populated
1357
+ end
1358
+
1359
+ it "should freeze the underlying array" do
1360
+ @search.freeze
1361
+ @search.to_a.should be_frozen
1362
+ end
1363
+
1364
+ it "should return the Search object" do
1365
+ @search.freeze.should be_a(ThinkingSphinx::Search)
1366
+ end
1367
+ end
1368
+
1369
+ describe '#client' do
1370
+ let(:client) { Riddle::Client.new }
1371
+ it "should respect the client in options" do
1372
+ search = ThinkingSphinx::Search.new :client => client
1373
+ search.client.should == client
1374
+ end
1375
+
1376
+ it "should get a new client from the configuration singleton by default" do
1377
+ ThinkingSphinx::Configuration.instance.stub!(:client => client)
1378
+ ThinkingSphinx::Search.new.client.should == client
1379
+ end
1380
+ end
1381
+ end
1382
+
1383
+ describe ThinkingSphinx::Search, "playing nice with Search model" do
1384
+ it "should not conflict with models called Search" do
1385
+ lambda { Search.find(:all) }.should_not raise_error
1386
+ end
1387
+ end