dpickett-thinking-sphinx 1.1.4 → 1.1.12

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 (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