friendlyfashion-thinking-sphinx 2.0.13

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