dpickett-thinking-sphinx 1.1.4 → 1.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.textile +126 -0
  2. data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
  3. data/lib/thinking_sphinx/active_record/delta.rb +14 -1
  4. data/lib/thinking_sphinx/active_record.rb +23 -5
  5. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +9 -1
  6. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +3 -2
  7. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +4 -3
  8. data/lib/thinking_sphinx/association.rb +17 -0
  9. data/lib/thinking_sphinx/attribute.rb +106 -95
  10. data/lib/thinking_sphinx/class_facet.rb +0 -5
  11. data/lib/thinking_sphinx/collection.rb +7 -1
  12. data/lib/thinking_sphinx/configuration.rb +9 -4
  13. data/lib/thinking_sphinx/core/string.rb +3 -10
  14. data/lib/thinking_sphinx/deltas/default_delta.rb +8 -5
  15. data/lib/thinking_sphinx/deltas/delayed_delta.rb +4 -2
  16. data/lib/thinking_sphinx/deltas.rb +7 -2
  17. data/lib/thinking_sphinx/deploy/capistrano.rb +80 -0
  18. data/lib/thinking_sphinx/facet.rb +22 -9
  19. data/lib/thinking_sphinx/facet_collection.rb +27 -12
  20. data/lib/thinking_sphinx/field.rb +4 -96
  21. data/lib/thinking_sphinx/index/builder.rb +46 -15
  22. data/lib/thinking_sphinx/index.rb +58 -66
  23. data/lib/thinking_sphinx/property.rb +133 -0
  24. data/lib/thinking_sphinx/rails_additions.rb +7 -4
  25. data/lib/thinking_sphinx/search.rb +181 -44
  26. data/lib/thinking_sphinx/tasks.rb +4 -4
  27. data/lib/thinking_sphinx.rb +47 -11
  28. data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +2 -2
  29. data/spec/unit/thinking_sphinx/active_record_spec.rb +64 -4
  30. data/spec/unit/thinking_sphinx/attribute_spec.rb +16 -1
  31. data/spec/unit/thinking_sphinx/facet_collection_spec.rb +64 -0
  32. data/spec/unit/thinking_sphinx/facet_spec.rb +46 -0
  33. data/spec/unit/thinking_sphinx/index_spec.rb +90 -0
  34. data/spec/unit/thinking_sphinx/rails_additions_spec.rb +183 -0
  35. data/spec/unit/thinking_sphinx/search_spec.rb +44 -0
  36. data/spec/unit/thinking_sphinx_spec.rb +10 -6
  37. data/tasks/distribution.rb +1 -1
  38. data/tasks/testing.rb +7 -15
  39. data/vendor/after_commit/init.rb +3 -0
  40. data/vendor/after_commit/lib/after_commit/active_record.rb +27 -4
  41. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +1 -1
  42. data/vendor/after_commit/lib/after_commit.rb +4 -1
  43. metadata +12 -3
  44. data/README +0 -107
@@ -7,6 +7,7 @@ require 'riddle'
7
7
  require 'after_commit'
8
8
 
9
9
  require 'thinking_sphinx/core/string'
10
+ require 'thinking_sphinx/property'
10
11
  require 'thinking_sphinx/active_record'
11
12
  require 'thinking_sphinx/association'
12
13
  require 'thinking_sphinx/attribute'
@@ -35,7 +36,7 @@ module ThinkingSphinx
35
36
  module Version #:nodoc:
36
37
  Major = 1
37
38
  Minor = 1
38
- Tiny = 4
39
+ Tiny = 12
39
40
 
40
41
  String = [Major, Minor, Tiny].join('.')
41
42
  end
@@ -61,6 +62,10 @@ module ThinkingSphinx
61
62
  @@indexed_models ||= []
62
63
  end
63
64
 
65
+ def self.unique_id_expression(offset = nil)
66
+ "* #{ThinkingSphinx.indexed_models.size} + #{offset || 0}"
67
+ end
68
+
64
69
  # Check if index definition is disabled.
65
70
  #
66
71
  def self.define_indexes?
@@ -125,21 +130,52 @@ module ThinkingSphinx
125
130
  # or if not using MySQL, this will return false.
126
131
  #
127
132
  def self.use_group_by_shortcut?
128
- ::ActiveRecord::ConnectionAdapters.constants.include?("MysqlAdapter") &&
129
- ::ActiveRecord::Base.connection.is_a?(
130
- ::ActiveRecord::ConnectionAdapters::MysqlAdapter
131
- ) &&
132
- ::ActiveRecord::Base.connection.select_all(
133
- "SELECT @@global.sql_mode, @@session.sql_mode;"
134
- ).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
133
+ !!(
134
+ mysql? && ::ActiveRecord::Base.connection.select_all(
135
+ "SELECT @@global.sql_mode, @@session.sql_mode;"
136
+ ).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
137
+ )
135
138
  end
136
139
 
137
140
  def self.sphinx_running?
138
- !!sphinx_pid
141
+ !!sphinx_pid && pid_active?(sphinx_pid)
139
142
  end
140
143
 
141
144
  def self.sphinx_pid
142
- pid_file = ThinkingSphinx::Configuration.instance.pid_file
143
- `cat #{pid_file}`[/\d+/] if File.exists?(pid_file)
145
+ pid_file = ThinkingSphinx::Configuration.instance.pid_file
146
+ cat_command = 'cat'
147
+ return nil unless File.exists?(pid_file)
148
+
149
+ if microsoft?
150
+ pid_file.gsub!('/', '\\')
151
+ cat_command = 'type'
152
+ end
153
+
154
+ `#{cat_command} #{pid_file}`[/\d+/]
155
+ end
156
+
157
+ def self.pid_active?(pid)
158
+ return true if microsoft?
159
+
160
+ begin
161
+ # In JRuby this returns -1 if the process doesn't exist
162
+ Process.getpgid(pid.to_i) != -1
163
+ rescue Exception => e
164
+ false
165
+ end
166
+ end
167
+
168
+ def self.microsoft?
169
+ RUBY_PLATFORM =~ /mswin/
170
+ end
171
+
172
+ def self.jruby?
173
+ defined?(JRUBY_VERSION)
174
+ end
175
+
176
+ def self.mysql?
177
+ ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlAdapter" || (
178
+ jruby? && ::ActiveRecord::Base.connection.config[:adapter] == "jdbcmysql"
179
+ )
144
180
  end
145
181
  end
@@ -76,7 +76,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
76
76
 
77
77
  @person = Person.new
78
78
  @person.stub_method(
79
- :in_core_index? => false,
79
+ :in_both_indexes? => false,
80
80
  :sphinx_document_id => 1
81
81
  )
82
82
 
@@ -126,7 +126,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
126
126
  end
127
127
 
128
128
  it "should update the deleted attribute if in the core index" do
129
- @person.stub_method(:in_core_index? => true)
129
+ @person.stub_method(:in_both_indexes? => true)
130
130
 
131
131
  @person.send(:index_delta)
132
132
 
@@ -64,15 +64,15 @@ describe "ThinkingSphinx::ActiveRecord" do
64
64
 
65
65
  TestModule::TestModel.define_index do; end
66
66
 
67
- TestModule::TestModel.should have_received(:before_save)
68
- TestModule::TestModel.should have_received(:after_commit)
67
+ TestModule::TestModel.should have_received(:before_save).with(:toggle_delta)
68
+ TestModule::TestModel.should have_received(:after_commit).with(:index_delta)
69
69
  end
70
70
 
71
71
  it "should not add before_save and after_commit hooks to the model if delta indexing is disabled" do
72
72
  TestModule::TestModel.define_index do; end
73
73
 
74
- TestModule::TestModel.should_not have_received(:before_save)
75
- TestModule::TestModel.should_not have_received(:after_commit)
74
+ TestModule::TestModel.should_not have_received(:before_save).with(:toggle_delta)
75
+ TestModule::TestModel.should_not have_received(:after_commit).with(:index_delta)
76
76
  end
77
77
 
78
78
  it "should add an after_destroy hook with delta indexing enabled" do
@@ -93,6 +93,60 @@ describe "ThinkingSphinx::ActiveRecord" do
93
93
  TestModule::TestModel.define_index.should == @index
94
94
  end
95
95
  end
96
+
97
+ describe "index methods" do
98
+ before(:all) do
99
+ @person = Person.find(:first)
100
+ end
101
+
102
+ describe "in_both_indexes?" do
103
+ it "should return true if in core and delta indexes" do
104
+ @person.should_receive(:in_core_index?).and_return(true)
105
+ @person.should_receive(:in_delta_index?).and_return(true)
106
+ @person.in_both_indexes?.should be_true
107
+ end
108
+
109
+ it "should return false if in one index and not the other" do
110
+ @person.should_receive(:in_core_index?).and_return(true)
111
+ @person.should_receive(:in_delta_index?).and_return(false)
112
+ @person.in_both_indexes?.should be_false
113
+ end
114
+ end
115
+
116
+ describe "in_core_index?" do
117
+ it "should call in_index? with core" do
118
+ @person.should_receive(:in_index?).with('core')
119
+ @person.in_core_index?
120
+ end
121
+ end
122
+
123
+ describe "in_delta_index?" do
124
+ it "should call in_index? with delta" do
125
+ @person.should_receive(:in_index?).with('delta')
126
+ @person.in_delta_index?
127
+ end
128
+ end
129
+
130
+ describe "in_index?" do
131
+ it "should return true if in the specified index" do
132
+ @person.should_receive(:sphinx_document_id).and_return(1)
133
+ @person.should_receive(:sphinx_index_name).and_return('person_core')
134
+ Person.should_receive(:search_for_id).with(1, 'person_core').and_return(true)
135
+
136
+ @person.in_index?('core').should be_true
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "source_of_sphinx_index method" do
142
+ it "should return self if model defines an index" do
143
+ Person.source_of_sphinx_index.should == Person
144
+ end
145
+
146
+ it "should return the parent if model inherits an index" do
147
+ Parent.source_of_sphinx_index.should == Person
148
+ end
149
+ end
96
150
 
97
151
  describe "to_crc32 method" do
98
152
  it "should return an integer" do
@@ -100,6 +154,12 @@ describe "ThinkingSphinx::ActiveRecord" do
100
154
  end
101
155
  end
102
156
 
157
+ describe "to_crc32s method" do
158
+ it "should return an array" do
159
+ Person.to_crc32s.should be_a_kind_of(Array)
160
+ end
161
+ end
162
+
103
163
  describe "toggle_deleted method" do
104
164
  before :each do
105
165
  ThinkingSphinx.stub_method(:sphinx_running? => true)
@@ -157,7 +157,7 @@ describe ThinkingSphinx::Attribute do
157
157
  end
158
158
 
159
159
  it "should return :string if there's more than one association" do
160
- @attribute.associations = {:a => :assoc, :b => :assoc}
160
+ @attribute.associations = {:a => [:assoc], :b => [:assoc]}
161
161
  @attribute.send(:type).should == :string
162
162
  end
163
163
 
@@ -209,4 +209,19 @@ describe ThinkingSphinx::Attribute do
209
209
  attribute.send(:all_ints?).should be_false
210
210
  end
211
211
  end
212
+
213
+ describe "with custom queries" do
214
+ before :each do
215
+ index = CricketTeam.sphinx_indexes.first
216
+ @statement = index.to_riddle_for_core(0, 0).sql_attr_multi.first
217
+ end
218
+
219
+ it "should track the query type accordingly" do
220
+ @statement.should match(/uint tags from query/)
221
+ end
222
+
223
+ it "should include the SQL statement" do
224
+ @statement.should match(/SELECT cricket_team_id, id FROM tags/)
225
+ end
226
+ end
212
227
  end
@@ -0,0 +1,64 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::FacetCollection do
4
+ before do
5
+ @facet_collection = ThinkingSphinx::FacetCollection.new([])
6
+ end
7
+
8
+ # TODO fix nasty hack when we have internet!
9
+ def mock_results
10
+ return @results if defined? @results
11
+ @result = Person.find(:first)
12
+ @results = [@result]
13
+ @results.stub!(:each_with_groupby_and_count).and_yield(@result, @result.city.to_crc32, 1)
14
+ @results
15
+ end
16
+
17
+ describe "#add_from_results" do
18
+ describe "with empty result set" do
19
+ before do
20
+ @facet_collection.add_from_results('attribute_facet', [])
21
+ end
22
+
23
+ it "should add key as attribute" do
24
+ @facet_collection.should have_key(:attribute)
25
+ end
26
+
27
+ it "should return an empty hash for the facet results" do
28
+ @facet_collection[:attribute].should be_empty
29
+ end
30
+ end
31
+
32
+ describe "with non-empty result set" do
33
+ before do
34
+ @facet_collection.add_from_results('city_facet', mock_results)
35
+ end
36
+
37
+ it "should return a hash" do
38
+ @facet_collection.should be_a_kind_of(Hash)
39
+ end
40
+
41
+ it "should add key as attribute" do
42
+ @facet_collection.keys.should include(:city)
43
+ end
44
+
45
+ it "should return a hash" do
46
+ @facet_collection[:city].should == {@result.city => 1}
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#for" do
52
+ before do
53
+ @facet_collection.add_from_results('city_facet', mock_results)
54
+ end
55
+
56
+ it "should return the search results for the attribute and key pair" do
57
+ ThinkingSphinx::Search.should_receive(:search) do |options|
58
+ options[:with].should have_key('city_facet')
59
+ options[:with]['city_facet'].should == @result.city.to_crc32
60
+ end
61
+ @facet_collection.for(:city => @result.city)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Facet do
4
+ describe ".name_for" do
5
+ it "should remove '_facet' from provided string and return a symbol" do
6
+ ThinkingSphinx::Facet.name_for('attribute_facet').should == :attribute
7
+ end
8
+
9
+ it "should remove '_facet' from provided symbol" do
10
+ ThinkingSphinx::Facet.name_for(:attribute_facet).should == :attribute
11
+ end
12
+
13
+ it "should return the name of the facet if a Facet is passed" do
14
+ facet = ThinkingSphinx::Facet.new(
15
+ ThinkingSphinx::Attribute.stub_instance(:unique_name => :attribute, :columns => ['attribute'])
16
+ )
17
+ ThinkingSphinx::Facet.name_for(facet).should == :attribute
18
+ end
19
+
20
+ it "should return 'class' for special case name 'class_crc'" do
21
+ ThinkingSphinx::Facet.name_for(:class_crc).should == :class
22
+ end
23
+
24
+ it "should cycle" do
25
+ ThinkingSphinx::Facet.name_for(ThinkingSphinx::Facet.attribute_name_for(:attribute)).should == :attribute
26
+ end
27
+ end
28
+
29
+ describe ".attribute_name_for" do
30
+ it "should append '_facet' to provided string" do
31
+ ThinkingSphinx::Facet.attribute_name_for('attribute').should == 'attribute_facet'
32
+ end
33
+
34
+ it "should append '_facet' to provided symbol and return a string" do
35
+ ThinkingSphinx::Facet.attribute_name_for(:attribute).should == 'attribute_facet'
36
+ end
37
+
38
+ it "should return 'class_crc' for special case attribute 'class'" do
39
+ ThinkingSphinx::Facet.attribute_name_for(:class).should == 'class_crc'
40
+ end
41
+
42
+ it "should cycle" do
43
+ ThinkingSphinx::Facet.attribute_name_for(ThinkingSphinx::Facet.name_for('attribute_facet')).should == 'attribute_facet'
44
+ end
45
+ end
46
+ end
@@ -51,4 +51,94 @@ describe ThinkingSphinx::Index do
51
51
  @index.infix_fields.should_not include(@field_b)
52
52
  end
53
53
  end
54
+
55
+ describe "multi-value attribute as ranged-query with has-many association" do
56
+ before :each do
57
+ @index = ThinkingSphinx::Index.new(Person) do
58
+ has tags(:id), :as => :tag_ids, :source => :ranged_query
59
+ end
60
+ @index.link!
61
+
62
+ @sql = @index.to_riddle_for_core(0, 0).sql_query
63
+ end
64
+
65
+ it "should not include attribute in select-clause sql_query" do
66
+ @sql.should_not match(/tag_ids/)
67
+ @sql.should_not match(/GROUP_CONCAT\(`tags`.`id`/)
68
+ end
69
+
70
+ it "should not join with association table" do
71
+ @sql.should_not match(/LEFT OUTER JOIN `tags`/)
72
+ end
73
+
74
+ it "should include sql_attr_multi as ranged-query" do
75
+ attribute = @index.send(:attributes).first
76
+ attribute.send(:type_to_config).to_s.should == "sql_attr_multi"
77
+
78
+ declaration, query, range_query = attribute.send(:config_value).split('; ')
79
+ declaration.should == "uint tag_ids from ranged-query"
80
+ 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"
81
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
82
+ end
83
+ end
84
+
85
+ describe "multi-value attribute as ranged-query with has-many-through association" do
86
+ before :each do
87
+ @index = ThinkingSphinx::Index.new(Person) do
88
+ has football_teams(:id), :as => :football_teams_ids, :source => :ranged_query
89
+ end
90
+ @index.link!
91
+
92
+ @sql = @index.to_riddle_for_core(0, 0).sql_query
93
+ end
94
+
95
+ it "should not include attribute in select-clause sql_query" do
96
+ @sql.should_not match(/football_teams_ids/)
97
+ @sql.should_not match(/GROUP_CONCAT\(`tags`.`football_team_id`/)
98
+ end
99
+
100
+ it "should not join with association table" do
101
+ @sql.should_not match(/LEFT OUTER JOIN `tags`/)
102
+ end
103
+
104
+ it "should include sql_attr_multi as ranged-query" do
105
+ attribute = @index.send(:attributes).first
106
+ attribute.send(:type_to_config).to_s.should == "sql_attr_multi"
107
+
108
+ declaration, query, range_query = attribute.send(:config_value).split('; ')
109
+ declaration.should == "uint football_teams_ids from ranged-query"
110
+ query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`football_team_id` AS `football_teams_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end"
111
+ range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`"
112
+ end
113
+ end
114
+
115
+ describe "multi-value attribute as ranged-query with has-many-through association and foreign_key" do
116
+ before :each do
117
+ @index = ThinkingSphinx::Index.new(Person) do
118
+ has friends(:id), :as => :friend_ids, :source => :ranged_query
119
+ end
120
+ @index.link!
121
+
122
+ @sql = @index.to_riddle_for_core(0, 0).sql_query
123
+ end
124
+
125
+ it "should not include attribute in select-clause sql_query" do
126
+ @sql.should_not match(/friend_ids/)
127
+ @sql.should_not match(/GROUP_CONCAT\(`friendships`.`friend_id`/)
128
+ end
129
+
130
+ it "should not join with association table" do
131
+ @sql.should_not match(/LEFT OUTER JOIN `friendships`/)
132
+ end
133
+
134
+ it "should include sql_attr_multi as ranged-query" do
135
+ attribute = @index.send(:attributes).first
136
+ attribute.send(:type_to_config).to_s.should == "sql_attr_multi"
137
+
138
+ declaration, query, range_query = attribute.send(:config_value).split('; ')
139
+ declaration.should == "uint friend_ids from ranged-query"
140
+ 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"
141
+ range_query.should == "SELECT MIN(`friendships`.`person_id`), MAX(`friendships`.`person_id`) FROM `friendships`"
142
+ end
143
+ end
54
144
  end
@@ -0,0 +1,183 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::HashExcept do
4
+ before(:each) do
5
+ @hash = { :number => 20, :letter => 'b', :shape => 'rectangle' }
6
+ end
7
+
8
+ describe "except method" do
9
+ it "returns a hash without the specified keys" do
10
+ new_hash = @hash.except(:number)
11
+ new_hash.should_not have_key(:number)
12
+ end
13
+ end
14
+
15
+ describe "except! method" do
16
+ it "modifies hash removing specified keys" do
17
+ @hash.except!(:number)
18
+ @hash.should_not have_key(:number)
19
+ end
20
+ end
21
+
22
+ describe "extends Hash" do
23
+ it 'with except' do
24
+ Hash.instance_methods.include?('except').should be_true
25
+ end
26
+
27
+ it 'with except!' do
28
+ Hash.instance_methods.include?('except!').should be_true
29
+ end
30
+ end
31
+ end
32
+
33
+ describe ThinkingSphinx::ArrayExtractOptions do
34
+ describe 'extract_options! method' do
35
+ it 'returns a hash' do
36
+ array = []
37
+ array.extract_options!.should be_kind_of(Hash)
38
+ end
39
+
40
+ it 'returns the last option if it is a hash' do
41
+ array = ['a', 'b', {:c => 'd'}]
42
+ array.extract_options!.should == {:c => 'd'}
43
+ end
44
+ end
45
+
46
+ describe "extends Array" do
47
+ it 'with extract_options!' do
48
+ Array.instance_methods.include?('extract_options!').should be_true
49
+ end
50
+ end
51
+ end
52
+
53
+ describe ThinkingSphinx::AbstractQuotedTableName do
54
+ describe 'quote_table_name method' do
55
+ it 'calls quote_column_name' do
56
+ adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql')
57
+ adapter.should_receive(:quote_column_name).with('messages')
58
+ adapter.quote_table_name('messages')
59
+ end
60
+ end
61
+
62
+ describe "extends ActiveRecord::ConnectionAdapters::AbstractAdapter" do
63
+ it 'with quote_table_name' do
64
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_methods.include?('quote_table_name').should be_true
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ThinkingSphinx::MysqlQuotedTableName do
70
+ describe "quote_table_name method" do
71
+ it 'correctly quotes' do
72
+ adapter = ActiveRecord::Base.connection
73
+ adapter.quote_table_name('thinking_sphinx.messages').should == "`thinking_sphinx`.`messages`"
74
+ end
75
+ end
76
+
77
+ describe "extends ActiveRecord::ConnectionAdapters::MysqlAdapter" do
78
+ it 'with quote_table_name' do
79
+ adapter = defined?(JRUBY_VERSION) ? :JdbcAdapter : :MysqlAdapter
80
+ ActiveRecord::ConnectionAdapters.const_get(adapter).instance_methods.include?("quote_table_name").should be_true
81
+ end
82
+ end
83
+ end
84
+
85
+ describe ThinkingSphinx::ActiveRecordQuotedName do
86
+ describe "quoted_table_name method" do
87
+ it 'returns table name wrappd in quotes' do
88
+ Person.quoted_table_name.should == '`people`'
89
+ end
90
+ end
91
+
92
+ describe "extends ActiveRecord::Base" do
93
+ it 'with quoted_table_name' do
94
+ ActiveRecord::Base.respond_to?("quoted_table_name").should be_true
95
+ end
96
+ end
97
+ end
98
+
99
+ describe ThinkingSphinx::ActiveRecordStoreFullSTIClass do
100
+ describe "store_full_sti_class method" do
101
+ it 'returns false' do
102
+ Person.store_full_sti_class.should be_false
103
+ end
104
+ end
105
+
106
+ describe "extends ActiveRecord::Base" do
107
+ it 'with store_full_sti_class' do
108
+ ActiveRecord::Base.respond_to?(:store_full_sti_class).should be_true
109
+ end
110
+ end
111
+ end
112
+
113
+ class TestModel
114
+ @@squares = 89
115
+ @@circles = 43
116
+
117
+ def number_of_polygons
118
+ @@polygons
119
+ end
120
+ end
121
+
122
+ describe ThinkingSphinx::ClassAttributeMethods do
123
+ describe "cattr_reader method" do
124
+ it 'creates getters' do
125
+ TestModel.cattr_reader :herbivores
126
+ test_model = TestModel.new
127
+ test_model.respond_to?(:herbivores).should be_true
128
+ end
129
+
130
+ it 'sets the initial value to nil' do
131
+ TestModel.cattr_reader :carnivores
132
+ test_model = TestModel.new
133
+ test_model.carnivores.should be_nil
134
+ end
135
+
136
+ it 'does not override an existing definition' do
137
+ TestModel.cattr_reader :squares
138
+ test_model = TestModel.new
139
+ test_model.squares.should == 89
140
+ end
141
+ end
142
+
143
+ describe "cattr_writer method" do
144
+ it 'creates setters' do
145
+ TestModel.cattr_writer :herbivores
146
+ test_model = TestModel.new
147
+ test_model.respond_to?(:herbivores=).should be_true
148
+ end
149
+
150
+ it 'does not override an existing definition' do
151
+ TestModel.cattr_writer :polygons
152
+ test_model = TestModel.new
153
+ test_model.polygons = 100
154
+ test_model.number_of_polygons.should == 100
155
+ end
156
+ end
157
+
158
+ describe "cattr_accessor method" do
159
+ it 'calls cattr_reader' do
160
+ Class.should_receive(:cattr_reader).with('polygons')
161
+ Class.cattr_accessor('polygons')
162
+ end
163
+
164
+ it 'calls cattr_writer' do
165
+ Class.should_receive(:cattr_writer).with('polygons')
166
+ Class.cattr_accessor('polygons')
167
+ end
168
+ end
169
+
170
+ describe "extends Class" do
171
+ it 'with cattr_reader' do
172
+ Class.respond_to?('cattr_reader').should be_true
173
+ end
174
+
175
+ it 'with cattr_writer' do
176
+ Class.respond_to?('cattr_writer').should be_true
177
+ end
178
+
179
+ it 'with cattr_accessor' do
180
+ Class.respond_to?('cattr_accessor').should be_true
181
+ end
182
+ end
183
+ end
@@ -50,6 +50,50 @@ describe ThinkingSphinx::Search do
50
50
 
51
51
  end
52
52
  end
53
+
54
+ describe "facets method" do
55
+ before :each do
56
+ @results = [Person.find(:first)]
57
+ @results.stub!(:each_with_groupby_and_count).
58
+ and_yield(@results.first, @results.first.city.to_crc32, 1)
59
+ ThinkingSphinx::Search.stub!(:search => @results)
60
+
61
+ @config = ThinkingSphinx::Configuration.instance
62
+ @config.configuration.searchd.max_matches = 10_000
63
+ end
64
+
65
+ it "should use the system-set max_matches for limit on facet calls" do
66
+ ThinkingSphinx::Search.should_receive(:search) do |options|
67
+ options[:max_matches].should == 10_000
68
+ options[:limit].should == 10_000
69
+ end
70
+
71
+ ThinkingSphinx::Search.facets :all_attributes => true
72
+ end
73
+
74
+ it "should use the default max-matches if there is no explicit setting" do
75
+ @config.configuration.searchd.max_matches = nil
76
+ ThinkingSphinx::Search.should_receive(:search) do |options|
77
+ options[:max_matches].should == 1000
78
+ options[:limit].should == 1000
79
+ end
80
+
81
+ ThinkingSphinx::Search.facets :all_attributes => true
82
+ end
83
+
84
+ it "should ignore user-provided max_matches and limit on facet calls" do
85
+ ThinkingSphinx::Search.should_receive(:search) do |options|
86
+ options[:max_matches].should == 10_000
87
+ options[:limit].should == 10_000
88
+ end
89
+
90
+ ThinkingSphinx::Search.facets(
91
+ :all_attributes => true,
92
+ :max_matches => 500,
93
+ :limit => 200
94
+ )
95
+ end
96
+ end
53
97
  end
54
98
 
55
99
  describe ThinkingSphinx::Search, "playing nice with Search model" do