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,145 @@
1
+ require 'spec_helper'
2
+
3
+ class CustomAdapter < ThinkingSphinx::AbstractAdapter
4
+ #
5
+ end
6
+
7
+ describe ThinkingSphinx::AbstractAdapter do
8
+ describe '.detect' do
9
+ let(:model) { stub('model') }
10
+
11
+ it "returns a MysqlAdapter object for :mysql" do
12
+ ThinkingSphinx::AbstractAdapter.stub(:adapter_for_model => :mysql)
13
+
14
+ adapter = ThinkingSphinx::AbstractAdapter.detect(model)
15
+ adapter.should be_a(ThinkingSphinx::MysqlAdapter)
16
+ end
17
+
18
+ it "returns a PostgreSQLAdapter object for :postgresql" do
19
+ ThinkingSphinx::AbstractAdapter.stub(:adapter_for_model => :postgresql)
20
+
21
+ adapter = ThinkingSphinx::AbstractAdapter.detect(model)
22
+ adapter.should be_a(ThinkingSphinx::PostgreSQLAdapter)
23
+ end
24
+
25
+ it "instantiates the provided class if one is provided" do
26
+ ThinkingSphinx::AbstractAdapter.stub(:adapter_for_model => CustomAdapter)
27
+ CustomAdapter.should_receive(:new).and_return(stub('adapter'))
28
+
29
+ ThinkingSphinx::AbstractAdapter.detect(model)
30
+ end
31
+
32
+ it "raises an exception for other responses" do
33
+ ThinkingSphinx::AbstractAdapter.stub(:adapter_for_model => :sqlite)
34
+
35
+ lambda {
36
+ ThinkingSphinx::AbstractAdapter.detect(model)
37
+ }.should raise_error
38
+ end
39
+ end
40
+
41
+ describe '.adapter_for_model' do
42
+ let(:model) { stub('model') }
43
+
44
+ after :each do
45
+ ThinkingSphinx.database_adapter = nil
46
+ end
47
+
48
+ it "translates strings to symbols" do
49
+ ThinkingSphinx.database_adapter = 'foo'
50
+
51
+ ThinkingSphinx::AbstractAdapter.adapter_for_model(model).should == :foo
52
+ end
53
+
54
+ it "passes through symbols unchanged" do
55
+ ThinkingSphinx.database_adapter = :bar
56
+
57
+ ThinkingSphinx::AbstractAdapter.adapter_for_model(model).should == :bar
58
+ end
59
+
60
+ it "returns standard_adapter_for_model if database_adapter is not set" do
61
+ ThinkingSphinx.database_adapter = nil
62
+ ThinkingSphinx::AbstractAdapter.stub!(:standard_adapter_for_model => :baz)
63
+
64
+ ThinkingSphinx::AbstractAdapter.adapter_for_model(model).should == :baz
65
+ end
66
+
67
+ it "calls the lambda and returns it if one is provided" do
68
+ ThinkingSphinx.database_adapter = lambda { |model| :foo }
69
+
70
+ ThinkingSphinx::AbstractAdapter.adapter_for_model(model).should == :foo
71
+ end
72
+ end
73
+
74
+ describe '.standard_adapter_for_model' do
75
+ let(:klass) { stub('connection class') }
76
+ let(:connection) { stub('connection', :class => klass) }
77
+ let(:model) { stub('model', :connection => connection) }
78
+
79
+ it "translates a normal MySQL adapter" do
80
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::MysqlAdapter')
81
+
82
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
83
+ should == :mysql
84
+ end
85
+
86
+ it "translates a MySQL plus adapter" do
87
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::MysqlplusAdapter')
88
+
89
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
90
+ should == :mysql
91
+ end
92
+
93
+ it "translates a MySQL2 adapter" do
94
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::Mysql2Adapter')
95
+
96
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
97
+ should == :mysql
98
+ end
99
+
100
+ it "translates a NullDB adapter to MySQL" do
101
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::NullDBAdapter')
102
+
103
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
104
+ should == :mysql
105
+ end
106
+
107
+ it "translates a normal PostgreSQL adapter" do
108
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter')
109
+
110
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
111
+ should == :postgresql
112
+ end
113
+
114
+ it "translates a JDBC MySQL adapter to MySQL" do
115
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::JdbcAdapter')
116
+ connection.stub(:config => {:adapter => 'jdbcmysql'})
117
+
118
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
119
+ should == :mysql
120
+ end
121
+
122
+ it "translates a JDBC PostgreSQL adapter to PostgreSQL" do
123
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::JdbcAdapter')
124
+ connection.stub(:config => {:adapter => 'jdbcpostgresql'})
125
+
126
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
127
+ should == :postgresql
128
+ end
129
+
130
+ it "returns other JDBC adapters without translation" do
131
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::JdbcAdapter')
132
+ connection.stub(:config => {:adapter => 'jdbcmssql'})
133
+
134
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
135
+ should == 'jdbcmssql'
136
+ end
137
+
138
+ it "returns other unknown adapters without translation" do
139
+ klass.stub(:name => 'ActiveRecord::ConnectionAdapters::FooAdapter')
140
+
141
+ ThinkingSphinx::AbstractAdapter.standard_adapter_for_model(model).
142
+ should == 'ActiveRecord::ConnectionAdapters::FooAdapter'
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::Association do
4
+ describe '.children' do
5
+ before :each do
6
+ @normal_reflection = stub('reflection', :options => {
7
+ :polymorphic => false
8
+ })
9
+ @normal_association = ThinkingSphinx::Association.new(nil, nil)
10
+ @poly_reflection = stub('reflection',
11
+ :options => {:polymorphic => true},
12
+ :macro => :has_many,
13
+ :name => 'polly',
14
+ :active_record => 'AR'
15
+ )
16
+ @non_poly_reflection = stub('reflection', :name => 'non_polly')
17
+
18
+ Person.stub!(:reflect_on_association => @normal_reflection)
19
+ ThinkingSphinx::Association.stub!(
20
+ :new => @normal_association,
21
+ :polymorphic_classes => [Person, Person],
22
+ :casted_options => {:casted => :options}
23
+ )
24
+ ::ActiveRecord::Reflection::AssociationReflection.stub!(
25
+ :new => @non_poly_reflection
26
+ )
27
+ end
28
+
29
+ it "should return an empty array if no association exists" do
30
+ Person.stub!(:reflect_on_association => nil)
31
+
32
+ ThinkingSphinx::Association.children(Person, :assoc).should == []
33
+ end
34
+
35
+ it "should return a single association instance in an array if assocation isn't polymorphic" do
36
+ ThinkingSphinx::Association.children(Person, :assoc).should == [@normal_association]
37
+ end
38
+
39
+ it "should return multiple association instances for polymorphic associations" do
40
+ Person.stub!(:reflect_on_association => @poly_reflection)
41
+
42
+ ThinkingSphinx::Association.children(Person, :assoc).should ==
43
+ [@normal_association, @normal_association]
44
+ end
45
+
46
+ it "should generate non-polymorphic 'casted' associations for each polymorphic possibility" do
47
+ Person.stub!(:reflect_on_association => @poly_reflection)
48
+ ThinkingSphinx::Association.should_receive(:casted_options).with(
49
+ Person, @poly_reflection
50
+ ).twice
51
+ ::ActiveRecord::Reflection::AssociationReflection.should_receive(:new).
52
+ with(:has_many, :polly_Person, {:casted => :options}, "AR").twice
53
+ ThinkingSphinx::Association.should_receive(:new).with(
54
+ nil, @non_poly_reflection
55
+ ).twice
56
+
57
+ ThinkingSphinx::Association.children(Person, :assoc)
58
+ end
59
+ end
60
+
61
+ describe '#children' do
62
+ before :each do
63
+ @reflection = stub('reflection', :klass => :klass)
64
+ @association = ThinkingSphinx::Association.new(nil, @reflection)
65
+ ThinkingSphinx::Association.stub!(:children => :result)
66
+ end
67
+
68
+ it "should return the children associations for the given association" do
69
+ @association.children(:assoc).should == :result
70
+ end
71
+
72
+ it "should request children for the reflection klass" do
73
+ ThinkingSphinx::Association.should_receive(:children).
74
+ with(:klass, :assoc, @association)
75
+
76
+ @association.children(:assoc)
77
+ end
78
+ end
79
+
80
+ describe '#join_to' do
81
+ before :each do
82
+ @parent_join = stub('join assoc').as_null_object
83
+ @join = stub('join assoc').as_null_object
84
+ @parent = ThinkingSphinx::Association.new(nil, nil)
85
+ @parent.stub!(:join_to => true, :join => nil)
86
+ @base_join = stub('base join', :joins => [:a, :b, :c])
87
+ ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub!(:new => @join)
88
+ end
89
+
90
+ it "should call the parent's join_to if parent has no join" do
91
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
92
+ @parent.should_receive(:join_to).with(@base_join)
93
+
94
+ @assoc.join_to(@base_join)
95
+ end
96
+
97
+ it "should not call the parent's join_to if it already has a join" do
98
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
99
+ @parent.stub!(:join => @parent_join)
100
+ @parent.should_not_receive(:join_to)
101
+
102
+ @assoc.join_to(@base_join)
103
+ end
104
+
105
+ it "should define the join association with a JoinAssociation instance" do
106
+ @assoc = ThinkingSphinx::Association.new(@parent, :ref)
107
+
108
+ @assoc.join_to(@base_join).should == @join
109
+ @assoc.join.should == @join
110
+ end
111
+ end
112
+
113
+ describe '#is_many?' do
114
+ before :each do
115
+ @parent = stub('assoc', :is_many? => :parent_is_many)
116
+ @reflection = stub('reflection', :macro => :has_many)
117
+ end
118
+
119
+ it "should return true if association is either a has_many or a habtm" do
120
+ association = ThinkingSphinx::Association.new(@parent, @reflection)
121
+ association.is_many?.should be_true
122
+
123
+ @reflection.stub!(:macro => :has_and_belongs_to_many)
124
+ association.is_many?.should be_true
125
+ end
126
+
127
+ it "should return the parent value if not a has many or habtm and there is a parent" do
128
+ association = ThinkingSphinx::Association.new(@parent, @reflection)
129
+ @reflection.stub!(:macro => :belongs_to)
130
+ association.is_many?.should == :parent_is_many
131
+ end
132
+
133
+ it "should return false if no parent and not a has many or habtm" do
134
+ association = ThinkingSphinx::Association.new(nil, @reflection)
135
+ @reflection.stub!(:macro => :belongs_to)
136
+ association.is_many?.should be_false
137
+ end
138
+ end
139
+
140
+ describe '#ancestors' do
141
+ it "should return an array of associations - including all parents" do
142
+ parent = stub('assoc', :ancestors => [:all, :ancestors])
143
+ association = ThinkingSphinx::Association.new(parent, @reflection)
144
+ association.ancestors.should == [:all, :ancestors, association]
145
+ end
146
+ end
147
+
148
+ describe '.polymorphic_classes' do
149
+ it "should return all the polymorphic result types as classes" do
150
+ Person.connection.stub!(:select_all => [
151
+ {"person_type" => "Person"},
152
+ {"person_type" => "Friendship"}
153
+ ])
154
+ ref = stub('ref',
155
+ :active_record => Person,
156
+ :options => {:foreign_type => "person_type"}
157
+ )
158
+
159
+ ThinkingSphinx::Association.send(:polymorphic_classes, ref).should == [Person, Friendship]
160
+ end
161
+ end
162
+
163
+ describe '.casted_options' do
164
+ before :each do
165
+ @options = {
166
+ :foreign_key => "thing_id",
167
+ :foreign_type => "thing_type",
168
+ :polymorphic => true
169
+ }
170
+ @reflection = stub('assoc reflection', :options => @options)
171
+ end
172
+
173
+ it "should return a new options set for a specific class" do
174
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
175
+ :polymorphic => nil,
176
+ :class_name => "Person",
177
+ :foreign_key => "thing_id",
178
+ :foreign_type => "thing_type",
179
+ :conditions => "::ts_join_alias::.`thing_type` = 'Person'"
180
+ }
181
+ end
182
+
183
+ it "should append to existing Array of conditions" do
184
+ @options[:conditions] = ["first condition"]
185
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
186
+ :polymorphic => nil,
187
+ :class_name => "Person",
188
+ :foreign_key => "thing_id",
189
+ :foreign_type => "thing_type",
190
+ :conditions => ["first condition", "::ts_join_alias::.`thing_type` = 'Person'"]
191
+ }
192
+ end
193
+
194
+ it "should merge to an existing Hash of conditions" do
195
+ @options[:conditions] = {"field" => "value"}
196
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
197
+ :polymorphic => nil,
198
+ :class_name => "Person",
199
+ :foreign_key => "thing_id",
200
+ :foreign_type => "thing_type",
201
+ :conditions => {"field" => "value", "thing_type" => "Person"}
202
+ }
203
+ end
204
+
205
+ it "should append to an existing String of conditions" do
206
+ @options[:conditions] = "first condition"
207
+ ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == {
208
+ :polymorphic => nil,
209
+ :class_name => "Person",
210
+ :foreign_key => "thing_id",
211
+ :foreign_type => "thing_type",
212
+ :conditions => "first condition AND ::ts_join_alias::.`thing_type` = 'Person'"
213
+ }
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,560 @@
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
+ before :each do
302
+ @attribute = ThinkingSphinx::Attribute.new(@source,
303
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
304
+ :as => :tag_ids, :source => :query
305
+ )
306
+ end
307
+
308
+ it "should use a query" do
309
+ @attribute.type_to_config.should == :sql_attr_multi
310
+
311
+ declaration, query = @attribute.config_value.split('; ')
312
+ declaration.should == "uint tag_ids from query"
313
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags`"
314
+ end
315
+ end
316
+
317
+ describe "MVA with source query for a delta source" do
318
+ before :each do
319
+ @attribute = ThinkingSphinx::Attribute.new(@source,
320
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
321
+ :as => :tag_ids, :source => :query
322
+ )
323
+ end
324
+
325
+ it "should use a query" do
326
+ @attribute.type_to_config.should == :sql_attr_multi
327
+
328
+ declaration, query = @attribute.config_value(nil, true).split('; ')
329
+ declaration.should == "uint tag_ids from query"
330
+ 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)"
331
+ end
332
+ end
333
+
334
+ describe "MVA via a HABTM association with a source query" do
335
+ before :each do
336
+ @attribute = ThinkingSphinx::Attribute.new(@source,
337
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
338
+ :as => :link_ids, :source => :query
339
+ )
340
+ end
341
+
342
+ it "should use a ranged query" do
343
+ @attribute.type_to_config.should == :sql_attr_multi
344
+
345
+ declaration, query = @attribute.config_value.split('; ')
346
+ declaration.should == "uint link_ids from query"
347
+ query.should == "SELECT `links_people`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `links_people`.`link_id` AS `link_ids` FROM `links_people`"
348
+ end
349
+ end
350
+
351
+ describe "MVA with ranged source query" do
352
+ before :each do
353
+ @attribute = ThinkingSphinx::Attribute.new(@source,
354
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
355
+ :as => :tag_ids, :source => :ranged_query
356
+ )
357
+ end
358
+
359
+ it "should use a ranged query" do
360
+ @attribute.type_to_config.should == :sql_attr_multi
361
+
362
+ declaration, query, range_query = @attribute.config_value.split('; ')
363
+ declaration.should == "uint tag_ids from ranged-query"
364
+ 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"
365
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
366
+ end
367
+ end
368
+
369
+ describe "MVA with ranged source query for a delta source" do
370
+ before :each do
371
+ @attribute = ThinkingSphinx::Attribute.new(@source,
372
+ [ThinkingSphinx::Index::FauxColumn.new(:tags, :id)],
373
+ :as => :tag_ids, :source => :ranged_query
374
+ )
375
+ end
376
+
377
+ it "should use a ranged query" do
378
+ @attribute.type_to_config.should == :sql_attr_multi
379
+
380
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
381
+ declaration.should == "uint tag_ids from ranged-query"
382
+ 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)"
383
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
384
+ end
385
+ end
386
+
387
+ describe "MVA via a has-many :through with a ranged source query" do
388
+ before :each do
389
+ @attribute = ThinkingSphinx::Attribute.new(@source,
390
+ [ThinkingSphinx::Index::FauxColumn.new(:football_teams, :id)],
391
+ :as => :football_team_ids, :source => :ranged_query
392
+ )
393
+ end
394
+
395
+ it "should use a ranged query" do
396
+ @attribute.type_to_config.should == :sql_attr_multi
397
+
398
+ declaration, query, range_query = @attribute.config_value.split('; ')
399
+ declaration.should == "uint football_team_ids from ranged-query"
400
+ 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"
401
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
402
+ end
403
+ end
404
+
405
+ describe "MVA via a has-many :through using a foreign key with a ranged source query" do
406
+ before :each do
407
+ @attribute = ThinkingSphinx::Attribute.new(@source,
408
+ [ThinkingSphinx::Index::FauxColumn.new(:friends, :id)],
409
+ :as => :friend_ids, :source => :ranged_query
410
+ )
411
+ end
412
+
413
+ it "should use a ranged query" do
414
+ @attribute.type_to_config.should == :sql_attr_multi
415
+
416
+ declaration, query, range_query = @attribute.config_value.split('; ')
417
+ declaration.should == "uint friend_ids from ranged-query"
418
+ 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"
419
+ range_query.should == "SELECT MIN(`friendships`.`person_id`), MAX(`friendships`.`person_id`) FROM `friendships`"
420
+ end
421
+ end
422
+
423
+ describe "MVA via a HABTM with a ranged source query" do
424
+ before :each do
425
+ @attribute = ThinkingSphinx::Attribute.new(@source,
426
+ [ThinkingSphinx::Index::FauxColumn.new(:links, :id)],
427
+ :as => :link_ids, :source => :ranged_query
428
+ )
429
+ end
430
+
431
+ it "should use a ranged query" do
432
+ @attribute.type_to_config.should == :sql_attr_multi
433
+
434
+ declaration, query, range_query = @attribute.config_value.split('; ')
435
+ declaration.should == "uint link_ids from ranged-query"
436
+ 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"
437
+ range_query.should == "SELECT MIN(`links_people`.`person_id`), MAX(`links_people`.`person_id`) FROM `links_people`"
438
+ end
439
+ end
440
+
441
+ describe "MVA via two has-many associations with a ranged source query" do
442
+ before :each do
443
+ @index = ThinkingSphinx::Index.new(Alpha)
444
+ @source = ThinkingSphinx::Source.new(@index)
445
+ @attribute = ThinkingSphinx::Attribute.new(@source,
446
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
447
+ :as => :gamma_values, :source => :ranged_query
448
+ )
449
+ end
450
+
451
+ it "should use a ranged query" do
452
+ @attribute.type_to_config.should == :sql_attr_multi
453
+
454
+ declaration, query, range_query = @attribute.config_value.split('; ')
455
+ declaration.should == "uint gamma_values from ranged-query"
456
+ 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"
457
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
458
+ end
459
+ end
460
+
461
+ describe "MVA via two has-many associations with a ranged source query for a delta source" do
462
+ before :each do
463
+ @index = ThinkingSphinx::Index.new(Alpha)
464
+ @source = ThinkingSphinx::Source.new(@index)
465
+ @attribute = ThinkingSphinx::Attribute.new(@source,
466
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
467
+ :as => :gamma_values, :source => :ranged_query
468
+ )
469
+
470
+ @index.delta_object = ThinkingSphinx::Deltas::DefaultDelta.new @index, @index.local_options
471
+ end
472
+
473
+ it "should use a ranged query" do
474
+ @attribute.type_to_config.should == :sql_attr_multi
475
+
476
+ declaration, query, range_query = @attribute.config_value(nil, true).split('; ')
477
+ declaration.should == "uint gamma_values from ranged-query"
478
+ 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)"
479
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
480
+ end
481
+ end
482
+
483
+ describe "with custom queries" do
484
+ before :each do
485
+ index = CricketTeam.sphinx_indexes.first
486
+ @statement = index.sources.first.to_riddle_for_core(0, 0).sql_attr_multi.last
487
+ end
488
+
489
+ it "should track the query type accordingly" do
490
+ @statement.should match(/uint tags from query/)
491
+ end
492
+
493
+ it "should include the SQL statement" do
494
+ @statement.should match(/SELECT cricket_team_id, id FROM tags/)
495
+ end
496
+ end
497
+
498
+ describe '#live_value' do
499
+ before :each do
500
+ @attribute = ThinkingSphinx::Attribute.new @source, [
501
+ stub('column', :__stack => [], :__name => "col_name")
502
+ ]
503
+ @instance = stub('model')
504
+ end
505
+
506
+ it "should translate boolean values to integers" do
507
+ @instance.stub!(:col_name => true)
508
+ @attribute.live_value(@instance).should == 1
509
+
510
+ @instance.stub!(:col_name => false)
511
+ @attribute.live_value(@instance).should == 0
512
+ end
513
+
514
+ it "should translate timestamps to integers" do
515
+ now = Time.now
516
+ @instance.stub!(:col_name => now)
517
+ @attribute.live_value(@instance).should == now.to_i
518
+ end
519
+
520
+ it "should translate dates to timestamp integers" do
521
+ today = Date.today
522
+ @instance.stub!(:col_name => today)
523
+ @attribute.live_value(@instance).should == today.to_time.to_i
524
+ end
525
+
526
+ it "should translate nils to 0" do
527
+ @instance.stub!(:col_name => nil)
528
+ @attribute.live_value(@instance).should == 0
529
+ end
530
+
531
+ it "should return integers as integers" do
532
+ @instance.stub!(:col_name => 42)
533
+ @attribute.live_value(@instance).should == 42
534
+ end
535
+
536
+ it "should handle nils in the association chain" do
537
+ @attribute = ThinkingSphinx::Attribute.new @source, [
538
+ stub('column', :__stack => [:assoc_name], :__name => :id)
539
+ ]
540
+ @instance.stub!(:assoc_name => nil)
541
+ @attribute.live_value(@instance).should == 0
542
+ end
543
+
544
+ it "should handle association chains" do
545
+ @attribute = ThinkingSphinx::Attribute.new @source, [
546
+ stub('column', :__stack => [:assoc_name], :__name => :id)
547
+ ]
548
+ @instance.stub!(:assoc_name => stub('object', :id => 42))
549
+ @attribute.live_value(@instance).should == 42
550
+ end
551
+
552
+ it "should translate crc strings to their integer values" do
553
+ @attribute = ThinkingSphinx::Attribute.new @source, [
554
+ stub('column', :__stack => [], :__name => "col_name")
555
+ ], :crc => true, :type => :string
556
+ @instance.stub!(:col_name => 'foo')
557
+ @attribute.live_value(@instance).should == 'foo'.to_crc32
558
+ end
559
+ end
560
+ end