thinking-sphinx 1.3.15 → 1.3.16

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.15
1
+ 1.3.16
@@ -63,17 +63,26 @@ module ThinkingSphinx
63
63
  # The collection of indexed models. Keep in mind that Rails lazily loads
64
64
  # its classes, so this may not actually be populated with _all_ the models
65
65
  # that have Sphinx indexes.
66
+ @@sphinx_mutex = Mutex.new
67
+ @@context = nil
68
+
66
69
  def self.context
67
- if Thread.current[:thinking_sphinx_context].nil?
68
- Thread.current[:thinking_sphinx_context] = ThinkingSphinx::Context.new
69
- Thread.current[:thinking_sphinx_context].prepare
70
+ if @@context.nil?
71
+ @@sphinx_mutex.synchronize do
72
+ if @@context.nil?
73
+ @@context = ThinkingSphinx::Context.new
74
+ @@context.prepare
75
+ end
76
+ end
70
77
  end
71
78
 
72
- Thread.current[:thinking_sphinx_context]
79
+ @@context
73
80
  end
74
-
81
+
75
82
  def self.reset_context!
76
- Thread.current[:thinking_sphinx_context] = nil
83
+ @@sphinx_mutex.synchronize do
84
+ @@context = nil
85
+ end
77
86
  end
78
87
 
79
88
  def self.unique_id_expression(offset = nil)
@@ -2,28 +2,50 @@ module ThinkingSphinx
2
2
  module ActiveRecord
3
3
  module HasManyAssociation
4
4
  def search(*args)
5
+ options = args.extract_options!
6
+ options[:with] ||= {}
7
+ options[:with].merge! default_filter
8
+
9
+ args << options
10
+ @reflection.klass.search(*args)
11
+ end
12
+
13
+ def method_missing(method, *args, &block)
14
+ if responds_to_scope(method)
15
+ @reflection.klass.
16
+ search(:with => default_filter).
17
+ send(method, *args, &block)
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def attribute_for_foreign_key
5
26
  foreign_key = @reflection.primary_key_name
6
27
  stack = [@reflection.options[:through]].compact
7
28
 
8
- attribute = nil
9
29
  @reflection.klass.define_indexes
10
30
  (@reflection.klass.sphinx_indexes || []).each do |index|
11
31
  attribute = index.attributes.detect { |attrib|
12
32
  attrib.columns.length == 1 &&
13
33
  attrib.columns.first.__name == foreign_key.to_sym
14
34
  }
15
- break if attribute
35
+ return attribute unless attribute.nil?
16
36
  end
17
37
 
18
- raise "Missing Attribute for Foreign Key #{foreign_key}" unless attribute
19
-
20
- options = args.extract_options!
21
- options[:with] ||= {}
22
- options[:with][attribute.unique_name] = @owner.id
23
-
24
- args << options
25
- @reflection.klass.search(*args)
38
+ raise "Missing Attribute for Foreign Key #{foreign_key}"
39
+ end
40
+
41
+ def default_filter
42
+ {attribute_for_foreign_key.unique_name => @owner.id}
43
+ end
44
+
45
+ def responds_to_scope(scope)
46
+ @reflection.klass.respond_to?(:sphinx_scopes) &&
47
+ @reflection.klass.sphinx_scopes.include?(scope)
26
48
  end
27
49
  end
28
50
  end
29
- end
51
+ end
@@ -33,6 +33,10 @@ module ThinkingSphinx
33
33
  "#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}"
34
34
  end
35
35
 
36
+ def bigint_pattern
37
+ /bigint/i
38
+ end
39
+
36
40
  protected
37
41
 
38
42
  def connection
@@ -93,19 +93,18 @@ module ThinkingSphinx
93
93
 
94
94
  separator = all_ints? || all_datetimes? || @crc ? ',' : ' '
95
95
 
96
- clause = @columns.collect { |column|
97
- part = column_with_prefix(column)
96
+ clause = columns_with_prefixes.collect { |column|
98
97
  case type
99
98
  when :string
100
- adapter.convert_nulls(part)
99
+ adapter.convert_nulls(column)
101
100
  when :datetime
102
- adapter.cast_to_datetime(part)
101
+ adapter.cast_to_datetime(column)
103
102
  when :multi
104
- part = adapter.cast_to_datetime(part) if is_many_datetimes?
105
- part = adapter.convert_nulls(part, '0') if is_many_ints?
106
- part
103
+ column = adapter.cast_to_datetime(column) if is_many_datetimes?
104
+ column = adapter.convert_nulls(column, '0') if is_many_ints?
105
+ column
107
106
  else
108
- part
107
+ column
109
108
  end
110
109
  }.join(', ')
111
110
 
@@ -123,7 +122,8 @@ module ThinkingSphinx
123
122
  :string => :sql_attr_str2ordinal,
124
123
  :float => :sql_attr_float,
125
124
  :boolean => :sql_attr_bool,
126
- :integer => :sql_attr_uint
125
+ :integer => :sql_attr_uint,
126
+ :bigint => :sql_attr_bigint
127
127
  }[type]
128
128
  end
129
129
 
@@ -301,19 +301,11 @@ WHERE #{@source.index.delta_object.clause(model, true)})
301
301
  is_many? && all_strings?
302
302
  end
303
303
 
304
- def type_from_database
305
- klass = @associations.values.flatten.first ?
306
- @associations.values.flatten.first.reflection.klass : @model
307
-
308
- column = klass.columns.detect { |col|
309
- @columns.collect { |c| c.__name.to_s }.include? col.name
310
- }
311
- column.nil? ? nil : column.type
312
- end
313
-
314
304
  def translated_type_from_database
315
305
  case type_from_db = type_from_database
316
- when :datetime, :string, :float, :boolean, :integer
306
+ when :integer
307
+ integer_type_from_db
308
+ when :datetime, :string, :float, :boolean
317
309
  type_from_db
318
310
  when :decimal
319
311
  :float
@@ -331,6 +323,32 @@ block:
331
323
  end
332
324
  end
333
325
 
326
+ def type_from_database
327
+ column = column_from_db
328
+ column.nil? ? nil : column.type
329
+ end
330
+
331
+ def integer_type_from_db
332
+ column = column_from_db
333
+ return nil if column.nil?
334
+
335
+ case column.sql_type
336
+ when adapter.bigint_pattern
337
+ :bigint
338
+ else
339
+ :integer
340
+ end
341
+ end
342
+
343
+ def column_from_db
344
+ klass = @associations.values.flatten.first ?
345
+ @associations.values.flatten.first.reflection.klass : @model
346
+
347
+ klass.columns.detect { |col|
348
+ @columns.collect { |c| c.__name.to_s }.include? col.name
349
+ }
350
+ end
351
+
334
352
  def all_of_type?(*column_types)
335
353
  @columns.all? { |col|
336
354
  klasses = @associations[col].empty? ? [@model] :
@@ -359,4 +377,4 @@ block:
359
377
  end
360
378
  end
361
379
  end
362
- end
380
+ end
@@ -1,7 +1,7 @@
1
1
  module ThinkingSphinx
2
2
  class AutoVersion
3
3
  def self.detect
4
- version = ThinkingSphinx::Configuration.instance.controller.sphinx_version
4
+ version = ThinkingSphinx::Configuration.instance.version
5
5
  case version
6
6
  when '0.9.8', '0.9.9'
7
7
  require "riddle/#{version}"
@@ -68,6 +68,7 @@ module ThinkingSphinx
68
68
  :app_root, :model_directories, :delayed_job_priority
69
69
 
70
70
  attr_accessor :source_options, :index_options
71
+ attr_accessor :version
71
72
 
72
73
  attr_reader :environment, :configuration, :controller
73
74
 
@@ -114,7 +115,9 @@ module ThinkingSphinx
114
115
  :charset_type => "utf-8"
115
116
  }
116
117
 
118
+ self.version = nil
117
119
  parse_config
120
+ self.version ||= @controller.sphinx_version
118
121
 
119
122
  self
120
123
  end
@@ -69,14 +69,12 @@ module ThinkingSphinx
69
69
  # multiple data values (has_many or has_and_belongs_to_many associations).
70
70
  #
71
71
  def to_select_sql
72
- clause = @columns.collect { |column|
73
- column_with_prefix(column)
74
- }.join(', ')
72
+ clause = columns_with_prefixes.join(', ')
75
73
 
76
74
  clause = adapter.concatenate(clause) if concat_ws?
77
75
  clause = adapter.group_concatenate(clause) if is_many?
78
76
 
79
- "#{adapter.cast_to_string clause } AS #{quote_column(unique_name)}"
77
+ "#{clause} AS #{quote_column(unique_name)}"
80
78
  end
81
79
  end
82
80
  end
@@ -136,10 +136,16 @@ module ThinkingSphinx
136
136
  assoc.has_column?(column.__name) ?
137
137
  "#{quote_with_table(assoc.join.aliased_table_name, column.__name)}" :
138
138
  nil
139
- }.compact.join(', ')
139
+ }.compact
140
140
  end
141
141
  end
142
142
 
143
+ def columns_with_prefixes
144
+ @columns.collect { |column|
145
+ column_with_prefix column
146
+ }.flatten
147
+ end
148
+
143
149
  # Gets a stack of associations for a specific path.
144
150
  #
145
151
  def association_stack(path, parent = nil)
@@ -159,4 +165,4 @@ module ThinkingSphinx
159
165
  assocs
160
166
  end
161
167
  end
162
- end
168
+ end
@@ -73,6 +73,8 @@ module ThinkingSphinx
73
73
  @array = []
74
74
  @options = args.extract_options!
75
75
  @args = args
76
+
77
+ populate if @options[:populate]
76
78
  end
77
79
 
78
80
  def to_a
@@ -129,8 +131,8 @@ module ThinkingSphinx
129
131
  # @param [Symbol] method The method name
130
132
  # @return [Boolean] true if either Search or Array responds to the method.
131
133
  #
132
- def respond_to?(method)
133
- super || @array.respond_to?(method)
134
+ def respond_to?(method, include_private = false)
135
+ super || @array.respond_to?(method, include_private)
134
136
  end
135
137
 
136
138
  # The current page number of the result set. Defaults to 1 if no page was
@@ -2,7 +2,8 @@ module ThinkingSphinx
2
2
  class Source
3
3
  module InternalProperties
4
4
  def add_internal_attributes_and_facets
5
- add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key_for_sphinx.to_sym
5
+ add_internal_attribute :sphinx_internal_id, nil,
6
+ @model.primary_key_for_sphinx.to_sym
6
7
  add_internal_attribute :class_crc, :integer, crc_column, true
7
8
  add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
8
9
  add_internal_attribute :sphinx_deleted, :integer, "0"
@@ -52,4 +52,20 @@ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
52
52
  @person.friends.search "test"
53
53
  end
54
54
  end
55
- end
55
+
56
+ describe 'filtering sphinx scopes' do
57
+ before :each do
58
+ Friendship.stub!(:search => Friendship)
59
+
60
+ @person = Person.find(:first)
61
+ end
62
+
63
+ it "should add a filter for the attribute in a sphinx scope call" do
64
+ Friendship.should_receive(:search).with do |options|
65
+ options[:with][:person_id].should == @person.id
66
+ end
67
+
68
+ @person.friendships.reverse
69
+ end
70
+ end
71
+ end
@@ -47,47 +47,6 @@ describe ThinkingSphinx::Attribute do
47
47
  end
48
48
  end
49
49
 
50
- describe '#column_with_prefix' do
51
- before :each do
52
- @attribute = ThinkingSphinx::Attribute.new @source, [
53
- ThinkingSphinx::Index::FauxColumn.new(:col_name)
54
- ]
55
- @attribute.columns.each { |col| @attribute.associations[col] = [] }
56
- @attribute.model = Person
57
-
58
- @first_join = Object.new
59
- @first_join.stub!(:aliased_table_name => "tabular")
60
- @second_join = Object.new
61
- @second_join.stub!(:aliased_table_name => "data")
62
-
63
- @first_assoc = ThinkingSphinx::Association.new nil, nil
64
- @first_assoc.stub!(:join => @first_join, :has_column? => true)
65
- @second_assoc = ThinkingSphinx::Association.new nil, nil
66
- @second_assoc.stub!(:join => @second_join, :has_column? => true)
67
- end
68
-
69
- it "should return the column name if the column is a string" do
70
- @attribute.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
71
- @attribute.send(:column_with_prefix, @attribute.columns.first).should == "string"
72
- end
73
-
74
- it "should return the column with model's table prefix if there's no associations for the column" do
75
- @attribute.send(:column_with_prefix, @attribute.columns.first).should == "`people`.`col_name`"
76
- end
77
-
78
- it "should return the column with its join table prefix if an association exists" do
79
- column = @attribute.columns.first
80
- @attribute.associations[column] = [@first_assoc]
81
- @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`"
82
- end
83
-
84
- it "should return multiple columns concatenated if more than one association exists" do
85
- column = @attribute.columns.first
86
- @attribute.associations[column] = [@first_assoc, @second_assoc]
87
- @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`"
88
- end
89
- end
90
-
91
50
  describe '#to_select_sql' do
92
51
  it "should convert a mixture of dates and datetimes to timestamps" do
93
52
  attribute = ThinkingSphinx::Attribute.new(@source,
@@ -99,6 +58,16 @@ describe ThinkingSphinx::Attribute do
99
58
 
100
59
  attribute.to_select_sql.should == "CONCAT_WS(',', UNIX_TIMESTAMP(`friendships`.`created_at`), UNIX_TIMESTAMP(`friendships`.`created_on`)) AS `times`"
101
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
102
71
  end
103
72
 
104
73
  describe '#is_many?' do
@@ -183,15 +152,15 @@ describe ThinkingSphinx::Attribute do
183
152
 
184
153
  it "should return the column type from the database if not :multi or more than one association" do
185
154
  @column.send(:instance_variable_set, :@name, "birthday")
186
- @attribute.send(:type).should == :datetime
155
+ @attribute.type.should == :datetime
187
156
 
188
157
  @attribute.send(:instance_variable_set, :@type, nil)
189
158
  @column.send(:instance_variable_set, :@name, "first_name")
190
- @attribute.send(:type).should == :string
159
+ @attribute.type.should == :string
191
160
 
192
161
  @attribute.send(:instance_variable_set, :@type, nil)
193
162
  @column.send(:instance_variable_set, :@name, "id")
194
- @attribute.send(:type).should == :integer
163
+ @attribute.type.should == :integer
195
164
  end
196
165
 
197
166
  it "should return :multi if the columns return multiple datetimes" do
@@ -200,6 +169,15 @@ describe ThinkingSphinx::Attribute do
200
169
 
201
170
  @attribute.type.should == :multi
202
171
  end
172
+
173
+ it "should return :bigint for 64bit integers" do
174
+ Person.columns.detect { |col|
175
+ col.name == 'id'
176
+ }.stub!(:sql_type => 'BIGINT(20)')
177
+ @column.send(:instance_variable_set, :@name, 'id')
178
+
179
+ @attribute.type.should == :bigint
180
+ end
203
181
  end
204
182
 
205
183
  describe '#all_ints?' do
@@ -3,14 +3,14 @@ require 'spec/spec_helper'
3
3
  describe ThinkingSphinx::AutoVersion do
4
4
  describe '.detect' do
5
5
  before :each do
6
- @controller = ThinkingSphinx::Configuration.instance.controller
6
+ @config = ThinkingSphinx::Configuration.instance
7
7
  end
8
8
 
9
9
  it "should require 0.9.8 if that is the detected version" do
10
10
  ThinkingSphinx::AutoVersion.should_receive(:require).
11
11
  with('riddle/0.9.8')
12
12
 
13
- @controller.stub!(:sphinx_version => '0.9.8')
13
+ @config.stub!(:version => '0.9.8')
14
14
  ThinkingSphinx::AutoVersion.detect
15
15
  end
16
16
 
@@ -18,21 +18,21 @@ describe ThinkingSphinx::AutoVersion do
18
18
  ThinkingSphinx::AutoVersion.should_receive(:require).
19
19
  with('riddle/0.9.9')
20
20
 
21
- @controller.stub!(:sphinx_version => '0.9.9')
21
+ @config.stub!(:version => '0.9.9')
22
22
  ThinkingSphinx::AutoVersion.detect
23
23
  end
24
24
 
25
25
  it "should output a warning if the detected version is something else" do
26
26
  STDERR.should_receive(:puts)
27
27
 
28
- @controller.stub!(:sphinx_version => '0.9.7')
28
+ @config.stub!(:version => '0.9.7')
29
29
  ThinkingSphinx::AutoVersion.detect
30
30
  end
31
31
 
32
32
  it "should output a warning if the version cannot be determined" do
33
33
  STDERR.should_receive(:puts)
34
34
 
35
- @controller.stub!(:sphinx_version => nil)
35
+ @config.stub!(:version => nil)
36
36
  ThinkingSphinx::AutoVersion.detect
37
37
  end
38
38
  end
@@ -37,6 +37,34 @@ describe ThinkingSphinx::Configuration do
37
37
  ThinkingSphinx::Configuration.environment.should == "development"
38
38
  end
39
39
  end
40
+
41
+ describe '#version' do
42
+ before :each do
43
+ @config = ThinkingSphinx::Configuration.instance
44
+ @config.reset
45
+ end
46
+
47
+ it "should use the given version from sphinx.yml if there is one" do
48
+ open("#{RAILS_ROOT}/config/sphinx.yml", "w") do |f|
49
+ f.write YAML.dump({'development' => {'version' => '0.9.7'}})
50
+ end
51
+ @config.reset
52
+
53
+ @config.version.should == '0.9.7'
54
+
55
+ FileUtils.rm "#{RAILS_ROOT}/config/sphinx.yml"
56
+ end
57
+
58
+ it "should detect the version from Riddle otherwise" do
59
+ controller = @config.controller
60
+ controller.stub!(:sphinx_version => '0.9.6')
61
+
62
+ Riddle::Controller.stub!(:new => controller)
63
+ @config.reset
64
+
65
+ @config.version.should == '0.9.6'
66
+ end
67
+ end
40
68
 
41
69
  describe "parse_config method" do
42
70
  before :each do
@@ -77,47 +77,6 @@ describe ThinkingSphinx::Field do
77
77
  end
78
78
  end
79
79
 
80
- describe "column_with_prefix method" do
81
- before :each do
82
- @field = ThinkingSphinx::Field.new @source, [
83
- ThinkingSphinx::Index::FauxColumn.new(:col_name)
84
- ]
85
- @field.columns.each { |col| @field.associations[col] = [] }
86
- @field.model = Person
87
-
88
- @first_join = Object.new
89
- @first_join.stub!(:aliased_table_name => "tabular")
90
- @second_join = Object.new
91
- @second_join.stub!(:aliased_table_name => "data")
92
-
93
- @first_assoc = ThinkingSphinx::Association.new nil, nil
94
- @first_assoc.stub!(:join => @first_join, :has_column? => true)
95
- @second_assoc = ThinkingSphinx::Association.new nil, nil
96
- @second_assoc.stub!(:join => @second_join, :has_column? => true)
97
- end
98
-
99
- it "should return the column name if the column is a string" do
100
- @field.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
101
- @field.send(:column_with_prefix, @field.columns.first).should == "string"
102
- end
103
-
104
- it "should return the column with model's table prefix if there's no associations for the column" do
105
- @field.send(:column_with_prefix, @field.columns.first).should == "`people`.`col_name`"
106
- end
107
-
108
- it "should return the column with its join table prefix if an association exists" do
109
- column = @field.columns.first
110
- @field.associations[column] = [@first_assoc]
111
- @field.send(:column_with_prefix, column).should == "`tabular`.`col_name`"
112
- end
113
-
114
- it "should return multiple columns concatenated if more than one association exists" do
115
- column = @field.columns.first
116
- @field.associations[column] = [@first_assoc, @second_assoc]
117
- @field.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`"
118
- end
119
- end
120
-
121
80
  describe "is_many? method" do
122
81
  before :each do
123
82
  @assoc_a = stub('assoc', :is_many? => true)
@@ -43,7 +43,12 @@ describe ThinkingSphinx::Search do
43
43
 
44
44
  it "should be true once the client request has been made" do
45
45
  @search.first
46
- @search.populated?.should be_true
46
+ @search.should be_populated
47
+ end
48
+
49
+ it "should be populated if :populate is set to true" do
50
+ search = ThinkingSphinx::Search.new(:populate => true)
51
+ search.should be_populated
47
52
  end
48
53
  end
49
54
 
@@ -28,8 +28,8 @@ Jeweler::Tasks.new do |gem|
28
28
  ]
29
29
 
30
30
  gem.add_dependency 'activerecord', '>= 1.15.6'
31
- gem.add_dependency 'riddle', '>= 1.0.9'
32
- gem.add_dependency 'after_commit', '>= 1.0.5'
31
+ gem.add_dependency 'riddle', '>= 1.0.10'
32
+ gem.add_dependency 'after_commit', '>= 1.0.6'
33
33
 
34
34
  gem.post_install_message = <<-MESSAGE
35
35
  If you're upgrading, you should read this:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.15
4
+ version: 1.3.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-29 00:00:00 +08:00
12
+ date: 2010-02-16 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.9
33
+ version: 1.0.10
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: after_commit
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 1.0.5
43
+ version: 1.0.6
44
44
  version:
45
45
  description: A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.
46
46
  email: pat@freelancing-gods.com