skalee-thinking-sphinx 1.3.14.1

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 (199) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +201 -0
  3. data/Rakefile +3 -0
  4. data/VERSION +1 -0
  5. data/contribute.rb +385 -0
  6. data/cucumber.yml +1 -0
  7. data/features/abstract_inheritance.feature +10 -0
  8. data/features/alternate_primary_key.feature +27 -0
  9. data/features/attribute_transformation.feature +22 -0
  10. data/features/attribute_updates.feature +51 -0
  11. data/features/deleting_instances.feature +67 -0
  12. data/features/direct_attributes.feature +11 -0
  13. data/features/excerpts.feature +13 -0
  14. data/features/extensible_delta_indexing.feature +9 -0
  15. data/features/facets.feature +82 -0
  16. data/features/facets_across_model.feature +29 -0
  17. data/features/handling_edits.feature +92 -0
  18. data/features/retry_stale_indexes.feature +24 -0
  19. data/features/searching_across_models.feature +20 -0
  20. data/features/searching_by_index.feature +40 -0
  21. data/features/searching_by_model.feature +175 -0
  22. data/features/searching_with_find_arguments.feature +56 -0
  23. data/features/sphinx_detection.feature +25 -0
  24. data/features/sphinx_scopes.feature +42 -0
  25. data/features/step_definitions/alpha_steps.rb +16 -0
  26. data/features/step_definitions/beta_steps.rb +7 -0
  27. data/features/step_definitions/common_steps.rb +188 -0
  28. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  29. data/features/step_definitions/facet_steps.rb +96 -0
  30. data/features/step_definitions/find_arguments_steps.rb +36 -0
  31. data/features/step_definitions/gamma_steps.rb +15 -0
  32. data/features/step_definitions/scope_steps.rb +15 -0
  33. data/features/step_definitions/search_steps.rb +89 -0
  34. data/features/step_definitions/sphinx_steps.rb +35 -0
  35. data/features/sti_searching.feature +19 -0
  36. data/features/support/database.example.yml +3 -0
  37. data/features/support/db/.gitignore +1 -0
  38. data/features/support/db/fixtures/alphas.rb +10 -0
  39. data/features/support/db/fixtures/authors.rb +1 -0
  40. data/features/support/db/fixtures/betas.rb +10 -0
  41. data/features/support/db/fixtures/boxes.rb +9 -0
  42. data/features/support/db/fixtures/categories.rb +1 -0
  43. data/features/support/db/fixtures/cats.rb +3 -0
  44. data/features/support/db/fixtures/comments.rb +24 -0
  45. data/features/support/db/fixtures/developers.rb +29 -0
  46. data/features/support/db/fixtures/dogs.rb +3 -0
  47. data/features/support/db/fixtures/extensible_betas.rb +10 -0
  48. data/features/support/db/fixtures/foxes.rb +3 -0
  49. data/features/support/db/fixtures/gammas.rb +10 -0
  50. data/features/support/db/fixtures/music.rb +4 -0
  51. data/features/support/db/fixtures/people.rb +1001 -0
  52. data/features/support/db/fixtures/posts.rb +6 -0
  53. data/features/support/db/fixtures/robots.rb +14 -0
  54. data/features/support/db/fixtures/tags.rb +27 -0
  55. data/features/support/db/migrations/create_alphas.rb +8 -0
  56. data/features/support/db/migrations/create_animals.rb +5 -0
  57. data/features/support/db/migrations/create_authors.rb +3 -0
  58. data/features/support/db/migrations/create_authors_posts.rb +6 -0
  59. data/features/support/db/migrations/create_betas.rb +5 -0
  60. data/features/support/db/migrations/create_boxes.rb +5 -0
  61. data/features/support/db/migrations/create_categories.rb +3 -0
  62. data/features/support/db/migrations/create_comments.rb +10 -0
  63. data/features/support/db/migrations/create_developers.rb +9 -0
  64. data/features/support/db/migrations/create_extensible_betas.rb +5 -0
  65. data/features/support/db/migrations/create_gammas.rb +3 -0
  66. data/features/support/db/migrations/create_genres.rb +3 -0
  67. data/features/support/db/migrations/create_music.rb +6 -0
  68. data/features/support/db/migrations/create_people.rb +13 -0
  69. data/features/support/db/migrations/create_posts.rb +5 -0
  70. data/features/support/db/migrations/create_robots.rb +4 -0
  71. data/features/support/db/migrations/create_taggings.rb +5 -0
  72. data/features/support/db/migrations/create_tags.rb +4 -0
  73. data/features/support/env.rb +21 -0
  74. data/features/support/lib/generic_delta_handler.rb +8 -0
  75. data/features/support/models/alpha.rb +22 -0
  76. data/features/support/models/animal.rb +5 -0
  77. data/features/support/models/author.rb +3 -0
  78. data/features/support/models/beta.rb +8 -0
  79. data/features/support/models/box.rb +8 -0
  80. data/features/support/models/cat.rb +3 -0
  81. data/features/support/models/category.rb +4 -0
  82. data/features/support/models/comment.rb +10 -0
  83. data/features/support/models/developer.rb +16 -0
  84. data/features/support/models/dog.rb +3 -0
  85. data/features/support/models/extensible_beta.rb +9 -0
  86. data/features/support/models/fox.rb +5 -0
  87. data/features/support/models/gamma.rb +5 -0
  88. data/features/support/models/genre.rb +3 -0
  89. data/features/support/models/medium.rb +5 -0
  90. data/features/support/models/music.rb +8 -0
  91. data/features/support/models/person.rb +23 -0
  92. data/features/support/models/post.rb +21 -0
  93. data/features/support/models/robot.rb +12 -0
  94. data/features/support/models/tag.rb +3 -0
  95. data/features/support/models/tagging.rb +4 -0
  96. data/ginger_scenarios.rb +28 -0
  97. data/init.rb +5 -0
  98. data/install.rb +5 -0
  99. data/lib/cucumber/thinking_sphinx/external_world.rb +8 -0
  100. data/lib/cucumber/thinking_sphinx/internal_world.rb +126 -0
  101. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  102. data/lib/thinking_sphinx/active_record/attribute_updates.rb +19 -0
  103. data/lib/thinking_sphinx/active_record/delta.rb +47 -0
  104. data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
  105. data/lib/thinking_sphinx/active_record/scopes.rb +75 -0
  106. data/lib/thinking_sphinx/active_record.rb +348 -0
  107. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
  108. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
  109. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +143 -0
  110. data/lib/thinking_sphinx/association.rb +164 -0
  111. data/lib/thinking_sphinx/attribute.rb +362 -0
  112. data/lib/thinking_sphinx/auto_version.rb +22 -0
  113. data/lib/thinking_sphinx/class_facet.rb +15 -0
  114. data/lib/thinking_sphinx/configuration.rb +300 -0
  115. data/lib/thinking_sphinx/context.rb +68 -0
  116. data/lib/thinking_sphinx/core/array.rb +7 -0
  117. data/lib/thinking_sphinx/core/string.rb +15 -0
  118. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  119. data/lib/thinking_sphinx/deltas.rb +28 -0
  120. data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
  121. data/lib/thinking_sphinx/excerpter.rb +22 -0
  122. data/lib/thinking_sphinx/facet.rb +125 -0
  123. data/lib/thinking_sphinx/facet_search.rb +136 -0
  124. data/lib/thinking_sphinx/field.rb +82 -0
  125. data/lib/thinking_sphinx/index/builder.rb +296 -0
  126. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  127. data/lib/thinking_sphinx/index.rb +157 -0
  128. data/lib/thinking_sphinx/property.rb +162 -0
  129. data/lib/thinking_sphinx/rails_additions.rb +150 -0
  130. data/lib/thinking_sphinx/search.rb +769 -0
  131. data/lib/thinking_sphinx/search_methods.rb +439 -0
  132. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  133. data/lib/thinking_sphinx/source/sql.rb +130 -0
  134. data/lib/thinking_sphinx/source.rb +153 -0
  135. data/lib/thinking_sphinx/tasks.rb +131 -0
  136. data/lib/thinking_sphinx/test.rb +52 -0
  137. data/lib/thinking_sphinx.rb +225 -0
  138. data/rails/init.rb +16 -0
  139. data/recipes/thinking_sphinx.rb +3 -0
  140. data/spec/fixtures/data.sql +32 -0
  141. data/spec/fixtures/database.yml.default +3 -0
  142. data/spec/fixtures/models.rb +145 -0
  143. data/spec/fixtures/structure.sql +125 -0
  144. data/spec/spec_helper.rb +60 -0
  145. data/spec/sphinx_helper.rb +81 -0
  146. data/spec/thinking_sphinx/active_record/delta_spec.rb +128 -0
  147. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +55 -0
  148. data/spec/thinking_sphinx/active_record/scopes_spec.rb +177 -0
  149. data/spec/thinking_sphinx/active_record_spec.rb +622 -0
  150. data/spec/thinking_sphinx/association_spec.rb +239 -0
  151. data/spec/thinking_sphinx/attribute_spec.rb +570 -0
  152. data/spec/thinking_sphinx/auto_version_spec.rb +39 -0
  153. data/spec/thinking_sphinx/configuration_spec.rb +234 -0
  154. data/spec/thinking_sphinx/context_spec.rb +119 -0
  155. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  156. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  157. data/spec/thinking_sphinx/excerpter_spec.rb +57 -0
  158. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  159. data/spec/thinking_sphinx/facet_spec.rb +333 -0
  160. data/spec/thinking_sphinx/field_spec.rb +154 -0
  161. data/spec/thinking_sphinx/index/builder_spec.rb +479 -0
  162. data/spec/thinking_sphinx/index/faux_column_spec.rb +30 -0
  163. data/spec/thinking_sphinx/index_spec.rb +183 -0
  164. data/spec/thinking_sphinx/rails_additions_spec.rb +203 -0
  165. data/spec/thinking_sphinx/search_methods_spec.rb +152 -0
  166. data/spec/thinking_sphinx/search_spec.rb +1181 -0
  167. data/spec/thinking_sphinx/source_spec.rb +235 -0
  168. data/spec/thinking_sphinx_spec.rb +204 -0
  169. data/tasks/distribution.rb +41 -0
  170. data/tasks/rails.rake +1 -0
  171. data/tasks/testing.rb +72 -0
  172. data/vendor/after_commit/.gitignore +1 -0
  173. data/vendor/after_commit/lib/after_commit/active_record.rb +122 -0
  174. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +168 -0
  175. data/vendor/after_commit/lib/after_commit/test_bypass.rb +30 -0
  176. data/vendor/after_commit/lib/after_commit.rb +70 -0
  177. data/vendor/riddle/lib/riddle/0.9.8.rb +1 -0
  178. data/vendor/riddle/lib/riddle/0.9.9/client/filter.rb +22 -0
  179. data/vendor/riddle/lib/riddle/0.9.9/client.rb +49 -0
  180. data/vendor/riddle/lib/riddle/0.9.9/configuration/searchd.rb +28 -0
  181. data/vendor/riddle/lib/riddle/0.9.9.rb +7 -0
  182. data/vendor/riddle/lib/riddle/auto_version.rb +11 -0
  183. data/vendor/riddle/lib/riddle/client/filter.rb +62 -0
  184. data/vendor/riddle/lib/riddle/client/message.rb +70 -0
  185. data/vendor/riddle/lib/riddle/client/response.rb +94 -0
  186. data/vendor/riddle/lib/riddle/client.rb +745 -0
  187. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +49 -0
  188. data/vendor/riddle/lib/riddle/configuration/index.rb +149 -0
  189. data/vendor/riddle/lib/riddle/configuration/indexer.rb +20 -0
  190. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
  191. data/vendor/riddle/lib/riddle/configuration/searchd.rb +28 -0
  192. data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
  193. data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
  194. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +53 -0
  195. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +29 -0
  196. data/vendor/riddle/lib/riddle/configuration.rb +33 -0
  197. data/vendor/riddle/lib/riddle/controller.rb +78 -0
  198. data/vendor/riddle/lib/riddle.rb +51 -0
  199. metadata +312 -0
@@ -0,0 +1,570 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Attribute do
4
+ before :each do
5
+ @index = ThinkingSphinx::Index.new(Person)
6
+ @source = ThinkingSphinx::Source.new(@index)
7
+
8
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
9
+ end
10
+
11
+ describe '#initialize' do
12
+ it 'raises if no columns are provided so that configuration errors are easier to track down' do
13
+ lambda {
14
+ ThinkingSphinx::Attribute.new(@source, [])
15
+ }.should raise_error(RuntimeError)
16
+ end
17
+
18
+ it 'raises if an element of the columns param is an integer - as happens when you use id instead of :id - so that configuration errors are easier to track down' do
19
+ lambda {
20
+ ThinkingSphinx::Attribute.new(@source, [1234])
21
+ }.should raise_error(RuntimeError)
22
+ end
23
+ end
24
+
25
+ describe '#unique_name' do
26
+ before :each do
27
+ @attribute = ThinkingSphinx::Attribute.new @source, [
28
+ stub('column', :__stack => [], :__name => "col_name")
29
+ ]
30
+ end
31
+
32
+ it "should use the alias if there is one" do
33
+ @attribute.alias = "alias"
34
+ @attribute.unique_name.should == "alias"
35
+ end
36
+
37
+ it "should use the alias if there's multiple columns" do
38
+ @attribute.columns << stub('column', :__stack => [], :__name => "col_name")
39
+ @attribute.unique_name.should be_nil
40
+
41
+ @attribute.alias = "alias"
42
+ @attribute.unique_name.should == "alias"
43
+ end
44
+
45
+ it "should use the column name if there's no alias and just one column" do
46
+ @attribute.unique_name.should == "col_name"
47
+ end
48
+ end
49
+
50
+ describe '#column_with_prefix' do
51
+ before :each do
52
+ @attribute = ThinkingSphinx::Attribute.new @source, [
53
+ ThinkingSphinx::Index::FauxColumn.new(:col_name)
54
+ ]
55
+ @attribute.columns.each { |col| @attribute.associations[col] = [] }
56
+ @attribute.model = Person
57
+
58
+ @first_join = Object.new
59
+ @first_join.stub!(:aliased_table_name => "tabular")
60
+ @second_join = Object.new
61
+ @second_join.stub!(:aliased_table_name => "data")
62
+
63
+ @first_assoc = ThinkingSphinx::Association.new nil, nil
64
+ @first_assoc.stub!(:join => @first_join, :has_column? => true)
65
+ @second_assoc = ThinkingSphinx::Association.new nil, nil
66
+ @second_assoc.stub!(:join => @second_join, :has_column? => true)
67
+ end
68
+
69
+ it "should return the column name if the column is a string" do
70
+ @attribute.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
71
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "string"
72
+ end
73
+
74
+ it "should return the column with model's table prefix if there's no associations for the column" do
75
+ @attribute.send(:column_with_prefix, @attribute.columns.first).should == "`people`.`col_name`"
76
+ end
77
+
78
+ it "should return the column with its join table prefix if an association exists" do
79
+ column = @attribute.columns.first
80
+ @attribute.associations[column] = [@first_assoc]
81
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`"
82
+ end
83
+
84
+ it "should return multiple columns concatenated if more than one association exists" do
85
+ column = @attribute.columns.first
86
+ @attribute.associations[column] = [@first_assoc, @second_assoc]
87
+ @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`"
88
+ end
89
+ end
90
+
91
+ describe '#to_select_sql' do
92
+ it "should convert a mixture of dates and datetimes to timestamps" do
93
+ attribute = ThinkingSphinx::Attribute.new(@source,
94
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
95
+ ThinkingSphinx::Index::FauxColumn.new(:created_on) ],
96
+ :as => :times
97
+ )
98
+ attribute.model = Friendship
99
+
100
+ attribute.to_select_sql.should == "CONCAT_WS(',', UNIX_TIMESTAMP(`friendships`.`created_at`), UNIX_TIMESTAMP(`friendships`.`created_on`)) AS `times`"
101
+ end
102
+ end
103
+
104
+ describe '#is_many?' do
105
+ before :each do
106
+ @assoc_a = stub('assoc', :is_many? => true)
107
+ @assoc_b = stub('assoc', :is_many? => true)
108
+ @assoc_c = stub('assoc', :is_many? => true)
109
+
110
+ @attribute = ThinkingSphinx::Attribute.new(
111
+ @source, [ThinkingSphinx::Index::FauxColumn.new(:col_name)]
112
+ )
113
+ @attribute.associations = {
114
+ :a => @assoc_a, :b => @assoc_b, :c => @assoc_c
115
+ }
116
+ end
117
+
118
+ it "should return true if all associations return true to is_many?" do
119
+ @attribute.send(:is_many?).should be_true
120
+ end
121
+
122
+ it "should return true if one association returns true to is_many?" do
123
+ @assoc_b.stub!(:is_many? => false)
124
+ @assoc_c.stub!(:is_many? => false)
125
+
126
+ @attribute.send(:is_many?).should be_true
127
+ end
128
+
129
+ it "should return false if all associations return false to is_many?" do
130
+ @assoc_a.stub!(:is_many? => false)
131
+ @assoc_b.stub!(:is_many? => false)
132
+ @assoc_c.stub!(:is_many? => false)
133
+
134
+ @attribute.send(:is_many?).should be_false
135
+ end
136
+ end
137
+
138
+ describe '#is_string?' do
139
+ before :each do
140
+ @col_a = ThinkingSphinx::Index::FauxColumn.new("a")
141
+ @col_b = ThinkingSphinx::Index::FauxColumn.new("b")
142
+ @col_c = ThinkingSphinx::Index::FauxColumn.new("c")
143
+
144
+ @attribute = ThinkingSphinx::Attribute.new(
145
+ @source, [@col_a, @col_b, @col_c]
146
+ )
147
+ end
148
+
149
+ it "should return true if all columns return true to is_string?" do
150
+ @attribute.send(:is_string?).should be_true
151
+ end
152
+
153
+ it "should return false if one column returns true to is_string?" do
154
+ @col_a.send(:instance_variable_set, :@name, :a)
155
+ @attribute.send(:is_string?).should be_false
156
+ end
157
+
158
+ it "should return false if all columns return false to is_string?" do
159
+ @col_a.send(:instance_variable_set, :@name, :a)
160
+ @col_b.send(:instance_variable_set, :@name, :b)
161
+ @col_c.send(:instance_variable_set, :@name, :c)
162
+ @attribute.send(:is_string?).should be_false
163
+ end
164
+ end
165
+
166
+ describe '#type' do
167
+ before :each do
168
+ @column = ThinkingSphinx::Index::FauxColumn.new(:col_name)
169
+ @attribute = ThinkingSphinx::Attribute.new(@source, [@column])
170
+ @attribute.model = Person
171
+ @attribute.stub!(:is_many? => false)
172
+ end
173
+
174
+ it "should return :multi if is_many? is true" do
175
+ @attribute.stub!(:is_many? => true)
176
+ @attribute.send(:type).should == :multi
177
+ end
178
+
179
+ it "should return :string if there's more than one association" do
180
+ @attribute.associations = {:a => [:assoc], :b => [:assoc]}
181
+ @attribute.send(:type).should == :string
182
+ end
183
+
184
+ it "should return the column type from the database if not :multi or more than one association" do
185
+ @column.send(:instance_variable_set, :@name, "birthday")
186
+ @attribute.send(:type).should == :datetime
187
+
188
+ @attribute.send(:instance_variable_set, :@type, nil)
189
+ @column.send(:instance_variable_set, :@name, "first_name")
190
+ @attribute.send(:type).should == :string
191
+
192
+ @attribute.send(:instance_variable_set, :@type, nil)
193
+ @column.send(:instance_variable_set, :@name, "id")
194
+ @attribute.send(:type).should == :integer
195
+ end
196
+
197
+ it "should return :multi if the columns return multiple datetimes" do
198
+ @attribute.stub!(:is_many? => true)
199
+ @attribute.stub!(:all_datetimes? => true)
200
+
201
+ @attribute.type.should == :multi
202
+ end
203
+ end
204
+
205
+ describe '#all_ints?' do
206
+ it "should return true if all columns are integers" do
207
+ attribute = ThinkingSphinx::Attribute.new(@source,
208
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
209
+ ThinkingSphinx::Index::FauxColumn.new(:team_id) ]
210
+ )
211
+ attribute.model = Person
212
+ attribute.columns.each { |col| attribute.associations[col] = [] }
213
+
214
+ attribute.should be_all_ints
215
+ end
216
+
217
+ it "should return false if only some columns are integers" do
218
+ attribute = ThinkingSphinx::Attribute.new(@source,
219
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
220
+ ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
221
+ )
222
+ attribute.model = Person
223
+ attribute.columns.each { |col| attribute.associations[col] = [] }
224
+
225
+ attribute.should_not be_all_ints
226
+ end
227
+
228
+ it "should return false if no columns are integers" do
229
+ attribute = ThinkingSphinx::Attribute.new(@source,
230
+ [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
231
+ ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
232
+ )
233
+ attribute.model = Person
234
+ attribute.columns.each { |col| attribute.associations[col] = [] }
235
+
236
+ attribute.should_not be_all_ints
237
+ end
238
+ end
239
+
240
+ describe '#all_datetimes?' do
241
+ it "should return true if all columns are datetimes" do
242
+ attribute = ThinkingSphinx::Attribute.new(@source,
243
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
244
+ ThinkingSphinx::Index::FauxColumn.new(:updated_at) ]
245
+ )
246
+ attribute.model = Friendship
247
+ attribute.columns.each { |col| attribute.associations[col] = [] }
248
+
249
+ attribute.should be_all_datetimes
250
+ end
251
+
252
+ it "should return false if only some columns are datetimes" do
253
+ attribute = ThinkingSphinx::Attribute.new(@source,
254
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
255
+ ThinkingSphinx::Index::FauxColumn.new(:created_at) ]
256
+ )
257
+ attribute.model = Friendship
258
+ attribute.columns.each { |col| attribute.associations[col] = [] }
259
+
260
+ attribute.should_not be_all_datetimes
261
+ end
262
+
263
+ it "should return true if all columns can be " do
264
+ attribute = ThinkingSphinx::Attribute.new(@source,
265
+ [ ThinkingSphinx::Index::FauxColumn.new(:created_at),
266
+ ThinkingSphinx::Index::FauxColumn.new(:created_on) ]
267
+ )
268
+ attribute.model = Friendship
269
+ attribute.columns.each { |col| attribute.associations[col] = [] }
270
+
271
+ attribute.should be_all_datetimes
272
+ end
273
+ end
274
+
275
+ describe '#all_strings?' do
276
+ it "should return true if all columns are strings or text" do
277
+ attribute = ThinkingSphinx::Attribute.new(@source,
278
+ [ ThinkingSphinx::Index::FauxColumn.new(:first_name),
279
+ ThinkingSphinx::Index::FauxColumn.new(:last_name) ]
280
+ )
281
+ attribute.model = Person
282
+ attribute.columns.each { |col| attribute.associations[col] = [] }
283
+
284
+ attribute.should be_all_strings
285
+ end
286
+
287
+ it "should return false if only some columns are strings" do
288
+ attribute = ThinkingSphinx::Attribute.new(@source,
289
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
290
+ ThinkingSphinx::Index::FauxColumn.new(:first_name) ]
291
+ )
292
+ attribute.model = Person
293
+ attribute.columns.each { |col| attribute.associations[col] = [] }
294
+
295
+ attribute.should_not be_all_strings
296
+ end
297
+
298
+ it "should return true if all columns are not strings" do
299
+ attribute = ThinkingSphinx::Attribute.new(@source,
300
+ [ ThinkingSphinx::Index::FauxColumn.new(:id),
301
+ ThinkingSphinx::Index::FauxColumn.new(:parent_id) ]
302
+ )
303
+ attribute.model = Person
304
+ attribute.columns.each { |col| attribute.associations[col] = [] }
305
+
306
+ attribute.should_not be_all_strings
307
+ end
308
+ end
309
+
310
+ describe "MVA with source query" do
311
+ before :each do
312
+ @attribute = ThinkingSphinx::Attribute.new(@source,
313
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
314
+ :as => :tag_ids, :source => :query
315
+ )
316
+ end
317
+
318
+ it "should use a query" do
319
+ @attribute.type_to_config.should == :sql_attr_multi
320
+
321
+ declaration, query = @attribute.config_value.split('; ')
322
+ declaration.should == "uint tag_ids from query"
323
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags`"
324
+ end
325
+ end
326
+
327
+ describe "MVA with source query for a delta source" do
328
+ before :each do
329
+ @attribute = ThinkingSphinx::Attribute.new(@source,
330
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
331
+ :as => :tag_ids, :source => :query
332
+ )
333
+ end
334
+
335
+ it "should use a query" do
336
+ @attribute.type_to_config.should == :sql_attr_multi
337
+
338
+ declaration, query = @attribute.config_value(nil, true).split('; ')
339
+ declaration.should == "uint tag_ids from query"
340
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` IN (SELECT `id` FROM `people` WHERE `people`.`delta` = 1)"
341
+ end
342
+ end
343
+
344
+ describe "MVA via a HABTM association with a source query" do
345
+ before :each do
346
+ @attribute = ThinkingSphinx::Attribute.new(@source,
347
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
348
+ :as => :link_ids, :source => :query
349
+ )
350
+ end
351
+
352
+ it "should use a ranged query" do
353
+ @attribute.type_to_config.should == :sql_attr_multi
354
+
355
+ declaration, query = @attribute.config_value.split('; ')
356
+ declaration.should == "uint link_ids from query"
357
+ query.should == "SELECT `links_people`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `links_people`.`link_id` AS `link_ids` FROM `links_people`"
358
+ end
359
+ end
360
+
361
+ describe "MVA with ranged source query" do
362
+ before :each do
363
+ @attribute = ThinkingSphinx::Attribute.new(@source,
364
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
365
+ :as => :tag_ids, :source => :ranged_query
366
+ )
367
+ end
368
+
369
+ it "should use a ranged query" do
370
+ @attribute.type_to_config.should == :sql_attr_multi
371
+
372
+ declaration, query, range_query = @attribute.config_value.split('; ')
373
+ declaration.should == "uint tag_ids from ranged-query"
374
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end"
375
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
376
+ end
377
+ end
378
+
379
+ describe "MVA with ranged source query for a delta source" do
380
+ before :each do
381
+ @attribute = ThinkingSphinx::Attribute.new(@source,
382
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
383
+ :as => :tag_ids, :source => :ranged_query
384
+ )
385
+ end
386
+
387
+ it "should use a ranged query" do
388
+ @attribute.type_to_config.should == :sql_attr_multi
389
+
390
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
391
+ declaration.should == "uint tag_ids from ranged-query"
392
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end AND `tags`.`person_id` IN (SELECT `id` FROM `people` WHERE `people`.`delta` = 1)"
393
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
394
+ end
395
+ end
396
+
397
+ describe "MVA via a has-many :through with a ranged source query" do
398
+ before :each do
399
+ @attribute = ThinkingSphinx::Attribute.new(@source,
400
+ [ThinkingSphinx::Index::FauxColumn.new(:football_teams, :id)],
401
+ :as => :football_team_ids, :source => :ranged_query
402
+ )
403
+ end
404
+
405
+ it "should use a ranged query" do
406
+ @attribute.type_to_config.should == :sql_attr_multi
407
+
408
+ declaration, query, range_query = @attribute.config_value.split('; ')
409
+ declaration.should == "uint football_team_ids from ranged-query"
410
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`football_team_id` AS `football_team_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end"
411
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
412
+ end
413
+ end
414
+
415
+ describe "MVA via a has-many :through using a foreign key with a ranged source query" do
416
+ before :each do
417
+ @attribute = ThinkingSphinx::Attribute.new(@source,
418
+ [ThinkingSphinx::Index::FauxColumn.new(:friends, :id)],
419
+ :as => :friend_ids, :source => :ranged_query
420
+ )
421
+ end
422
+
423
+ it "should use a ranged query" do
424
+ @attribute.type_to_config.should == :sql_attr_multi
425
+
426
+ declaration, query, range_query = @attribute.config_value.split('; ')
427
+ declaration.should == "uint friend_ids from ranged-query"
428
+ query.should == "SELECT `friendships`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `friendships`.`friend_id` AS `friend_ids` FROM `friendships` WHERE `friendships`.`person_id` >= $start AND `friendships`.`person_id` <= $end"
429
+ range_query.should == "SELECT MIN(`friendships`.`person_id`), MAX(`friendships`.`person_id`) FROM `friendships`"
430
+ end
431
+ end
432
+
433
+ describe "MVA via a HABTM with a ranged source query" do
434
+ before :each do
435
+ @attribute = ThinkingSphinx::Attribute.new(@source,
436
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
437
+ :as => :link_ids, :source => :ranged_query
438
+ )
439
+ end
440
+
441
+ it "should use a ranged query" do
442
+ @attribute.type_to_config.should == :sql_attr_multi
443
+
444
+ declaration, query, range_query = @attribute.config_value.split('; ')
445
+ declaration.should == "uint link_ids from ranged-query"
446
+ query.should == "SELECT `links_people`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `links_people`.`link_id` AS `link_ids` FROM `links_people` WHERE `links_people`.`person_id` >= $start AND `links_people`.`person_id` <= $end"
447
+ range_query.should == "SELECT MIN(`links_people`.`person_id`), MAX(`links_people`.`person_id`) FROM `links_people`"
448
+ end
449
+ end
450
+
451
+ describe "MVA via two has-many associations with a ranged source query" do
452
+ before :each do
453
+ @index = ThinkingSphinx::Index.new(Alpha)
454
+ @source = ThinkingSphinx::Source.new(@index)
455
+ @attribute = ThinkingSphinx::Attribute.new(@source,
456
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
457
+ :as => :gamma_values, :source => :ranged_query
458
+ )
459
+ end
460
+
461
+ it "should use a ranged query" do
462
+ @attribute.type_to_config.should == :sql_attr_multi
463
+
464
+ declaration, query, range_query = @attribute.config_value.split('; ')
465
+ declaration.should == "uint gamma_values from ranged-query"
466
+ query.should == "SELECT `betas`.`alpha_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `gammas`.`value` AS `gamma_values` FROM `betas` LEFT OUTER JOIN `gammas` ON gammas.beta_id = betas.id WHERE `betas`.`alpha_id` >= $start AND `betas`.`alpha_id` <= $end"
467
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
468
+ end
469
+ end
470
+
471
+ describe "MVA via two has-many associations with a ranged source query for a delta source" do
472
+ before :each do
473
+ @index = ThinkingSphinx::Index.new(Alpha)
474
+ @source = ThinkingSphinx::Source.new(@index)
475
+ @attribute = ThinkingSphinx::Attribute.new(@source,
476
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
477
+ :as => :gamma_values, :source => :ranged_query
478
+ )
479
+
480
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
481
+ end
482
+
483
+ it "should use a ranged query" do
484
+ @attribute.type_to_config.should == :sql_attr_multi
485
+
486
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
487
+ declaration.should == "uint gamma_values from ranged-query"
488
+ query.should == "SELECT `betas`.`alpha_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `gammas`.`value` AS `gamma_values` FROM `betas` LEFT OUTER JOIN `gammas` ON gammas.beta_id = betas.id WHERE `betas`.`alpha_id` >= $start AND `betas`.`alpha_id` <= $end AND `betas`.`alpha_id` IN (SELECT `id` FROM `alphas` WHERE `alphas`.`delta` = 1)"
489
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
490
+ end
491
+ end
492
+
493
+ describe "with custom queries" do
494
+ before :each do
495
+ index = CricketTeam.sphinx_indexes.first
496
+ @statement = index.sources.first.to_riddle_for_core(0, 0).sql_attr_multi.last
497
+ end
498
+
499
+ it "should track the query type accordingly" do
500
+ @statement.should match(/uint tags from query/)
501
+ end
502
+
503
+ it "should include the SQL statement" do
504
+ @statement.should match(/SELECT cricket_team_id, id FROM tags/)
505
+ end
506
+ end
507
+
508
+ describe '#live_value' do
509
+ before :each do
510
+ @attribute = ThinkingSphinx::Attribute.new @source, [
511
+ stub('column', :__stack => [], :__name => "col_name")
512
+ ]
513
+ @instance = stub('model')
514
+ end
515
+
516
+ it "should translate boolean values to integers" do
517
+ @instance.stub!(:col_name => true)
518
+ @attribute.live_value(@instance).should == 1
519
+
520
+ @instance.stub!(:col_name => false)
521
+ @attribute.live_value(@instance).should == 0
522
+ end
523
+
524
+ it "should translate timestamps to integers" do
525
+ now = Time.now
526
+ @instance.stub!(:col_name => now)
527
+ @attribute.live_value(@instance).should == now.to_i
528
+ end
529
+
530
+ it "should translate dates to timestamp integers" do
531
+ today = Date.today
532
+ @instance.stub!(:col_name => today)
533
+ @attribute.live_value(@instance).should == today.to_time.to_i
534
+ end
535
+
536
+ it "should translate nils to 0" do
537
+ @instance.stub!(:col_name => nil)
538
+ @attribute.live_value(@instance).should == 0
539
+ end
540
+
541
+ it "should return integers as integers" do
542
+ @instance.stub!(:col_name => 42)
543
+ @attribute.live_value(@instance).should == 42
544
+ end
545
+
546
+ it "should handle nils in the association chain" do
547
+ @attribute = ThinkingSphinx::Attribute.new @source, [
548
+ stub('column', :__stack => [:assoc_name], :__name => :id)
549
+ ]
550
+ @instance.stub!(:assoc_name => nil)
551
+ @attribute.live_value(@instance).should == 0
552
+ end
553
+
554
+ it "should handle association chains" do
555
+ @attribute = ThinkingSphinx::Attribute.new @source, [
556
+ stub('column', :__stack => [:assoc_name], :__name => :id)
557
+ ]
558
+ @instance.stub!(:assoc_name => stub('object', :id => 42))
559
+ @attribute.live_value(@instance).should == 42
560
+ end
561
+
562
+ it "should translate crc strings to their integer values" do
563
+ @attribute = ThinkingSphinx::Attribute.new @source, [
564
+ stub('column', :__stack => [], :__name => "col_name")
565
+ ], :crc => true, :type => :string
566
+ @instance.stub!(:col_name => 'foo')
567
+ @attribute.live_value(@instance).should == 'foo'.to_crc32
568
+ end
569
+ end
570
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::AutoVersion do
4
+ describe '.detect' do
5
+ before :each do
6
+ @controller = ThinkingSphinx::Configuration.instance.controller
7
+ end
8
+
9
+ it "should require 0.9.8 if that is the detected version" do
10
+ ThinkingSphinx::AutoVersion.should_receive(:require).
11
+ with('riddle/0.9.8')
12
+
13
+ @controller.stub!(:sphinx_version => '0.9.8')
14
+ ThinkingSphinx::AutoVersion.detect
15
+ end
16
+
17
+ it "should require 0.9.9 if that is the detected version" do
18
+ ThinkingSphinx::AutoVersion.should_receive(:require).
19
+ with('riddle/0.9.9')
20
+
21
+ @controller.stub!(:sphinx_version => '0.9.9')
22
+ ThinkingSphinx::AutoVersion.detect
23
+ end
24
+
25
+ it "should output a warning if the detected version is something else" do
26
+ STDERR.should_receive(:puts)
27
+
28
+ @controller.stub!(:sphinx_version => '0.9.7')
29
+ ThinkingSphinx::AutoVersion.detect
30
+ end
31
+
32
+ it "should output a warning if the version cannot be determined" do
33
+ STDERR.should_receive(:puts)
34
+
35
+ @controller.stub!(:sphinx_version => nil)
36
+ ThinkingSphinx::AutoVersion.detect
37
+ end
38
+ end
39
+ end