thinking-sphinx 3.0.0.rc → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Gemfile +0 -4
  2. data/HISTORY +9 -0
  3. data/lib/thinking_sphinx.rb +1 -0
  4. data/lib/thinking_sphinx/active_record/index.rb +5 -0
  5. data/lib/thinking_sphinx/active_record/interpreter.rb +5 -0
  6. data/lib/thinking_sphinx/active_record/property.rb +2 -12
  7. data/lib/thinking_sphinx/active_record/sql_source.rb +3 -10
  8. data/lib/thinking_sphinx/active_record/sql_source/template.rb +4 -3
  9. data/lib/thinking_sphinx/configuration.rb +1 -1
  10. data/lib/thinking_sphinx/configuration/consistent_ids.rb +3 -1
  11. data/lib/thinking_sphinx/connection.rb +14 -0
  12. data/lib/thinking_sphinx/core.rb +1 -0
  13. data/lib/thinking_sphinx/core/index.rb +5 -9
  14. data/lib/thinking_sphinx/core/property.rb +13 -0
  15. data/lib/thinking_sphinx/middlewares/sphinxql.rb +1 -1
  16. data/lib/thinking_sphinx/rake_interface.rb +28 -5
  17. data/lib/thinking_sphinx/real_time.rb +1 -0
  18. data/lib/thinking_sphinx/real_time/attribute.rb +10 -0
  19. data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +3 -14
  20. data/lib/thinking_sphinx/real_time/field.rb +3 -1
  21. data/lib/thinking_sphinx/real_time/index.rb +29 -2
  22. data/lib/thinking_sphinx/real_time/index/template.rb +6 -5
  23. data/lib/thinking_sphinx/real_time/interpreter.rb +4 -0
  24. data/lib/thinking_sphinx/real_time/property.rb +2 -0
  25. data/lib/thinking_sphinx/real_time/transcriber.rb +37 -0
  26. data/lib/thinking_sphinx/tasks.rb +6 -5
  27. data/spec/acceptance/facets_spec.rb +2 -1
  28. data/spec/acceptance/index_options_spec.rb +20 -0
  29. data/spec/acceptance/searching_within_a_model_spec.rb +0 -1
  30. data/spec/acceptance/support/sphinx_helpers.rb +3 -0
  31. data/spec/internal/app/indices/product_index.rb +3 -3
  32. data/spec/thinking_sphinx/active_record/sql_source_spec.rb +14 -9
  33. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +1 -1
  34. data/spec/thinking_sphinx/real_time/attribute_spec.rb +2 -2
  35. data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +3 -2
  36. data/spec/thinking_sphinx/real_time/field_spec.rb +4 -4
  37. data/spec/thinking_sphinx/real_time/index_spec.rb +9 -4
  38. data/thinking-sphinx.gemspec +4 -3
  39. metadata +42 -7
data/Gemfile CHANGED
@@ -2,10 +2,6 @@ source :rubygems
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'combustion',
6
- :git => 'git://github.com/pat/combustion.git',
7
- :ref => '45f50e64c3'
8
-
9
5
  gem 'mysql2', '~> 0.3.12b4', :platform => :ruby
10
6
  gem 'pg', '~> 0.11.0', :platform => :ruby
11
7
 
data/HISTORY CHANGED
@@ -1,3 +1,12 @@
1
+ 2013-01-02: 3.0.0
2
+ * [CHANGE] Updating Riddle dependency to 1.5.4.
3
+ * [FIX] Respect source options as well as underlying settings via the set_property method in index definitions.
4
+ * [FIX] Load real-time index definitions when listing fields, attributes, and/or conditions.
5
+ * [CHANGE] UTF-8 is now the default charset again (as it was in earlier Thinking Sphinx versions).
6
+ * [FEATURE] Initial realtime index support, including the ts:generate task for building index datasets. Sphinx 2.0.6 is required.
7
+ * [CHANGE] Removing ts:version rake task.
8
+ * [FEATURE] SphinxQL connection pooling via the Innertube gem.
9
+
1
10
  2012-12-22: 3.0.0.rc
2
11
  * [FEATURE] Source type support (query and ranged query) for both attributes and fields. Custom SQL strings can be supplied as well.
3
12
  * [FEATURE] Wordcount attributes and fields now supported.
@@ -8,6 +8,7 @@ end
8
8
  require 'riddle'
9
9
  require 'middleware'
10
10
  require 'active_record'
11
+ require 'innertube'
11
12
 
12
13
  module ThinkingSphinx
13
14
  def self.count(query = '', options = {})
@@ -24,6 +24,11 @@ class ThinkingSphinx::ActiveRecord::Index < Riddle::Configuration::Index
24
24
  @facets ||= sources.collect(&:facets).flatten
25
25
  end
26
26
 
27
+ def sources
28
+ interpret_definition!
29
+ super
30
+ end
31
+
27
32
  def unique_attribute_names
28
33
  attributes.collect(&:name)
29
34
  end
@@ -36,6 +36,7 @@ class ThinkingSphinx::ActiveRecord::Interpreter <
36
36
  properties.each do |key, value|
37
37
  @index.send("#{key}=", value) if @index.class.settings.include?(key)
38
38
  __source.send("#{key}=", value) if __source.class.settings.include?(key)
39
+ __source.options[key] = value if source_option?(key)
39
40
  end
40
41
  end
41
42
 
@@ -53,4 +54,8 @@ class ThinkingSphinx::ActiveRecord::Interpreter <
53
54
  options = columns.extract_options!
54
55
  columns.collect { |column| klass.new(__source.model, column, options) }
55
56
  end
57
+
58
+ def source_option?(key)
59
+ ::ThinkingSphinx::ActiveRecord::SQLSource::OPTIONS.include?(key)
60
+ end
56
61
  end
@@ -1,4 +1,6 @@
1
1
  class ThinkingSphinx::ActiveRecord::Property
2
+ include ThinkingSphinx::Core::Property
3
+
2
4
  attr_reader :columns, :options
3
5
 
4
6
  def initialize(model, columns, options = {})
@@ -10,14 +12,6 @@ class ThinkingSphinx::ActiveRecord::Property
10
12
  }
11
13
  end
12
14
 
13
- def facet?
14
- options[:facet]
15
- end
16
-
17
- def multi?
18
- false
19
- end
20
-
21
15
  def name
22
16
  (options[:as] || columns.first.__name).to_s
23
17
  end
@@ -25,8 +19,4 @@ class ThinkingSphinx::ActiveRecord::Property
25
19
  def source_type
26
20
  options[:source]
27
21
  end
28
-
29
- def type
30
- nil
31
- end
32
22
  end
@@ -2,16 +2,9 @@ class ThinkingSphinx::ActiveRecord::SQLSource < Riddle::Configuration::SQLSource
2
2
  attr_reader :model, :database_settings, :options
3
3
  attr_accessor :fields, :attributes, :associations, :conditions, :groupings
4
4
 
5
- # Options:
6
- # - :name
7
- # - :offset
8
- # - :delta_processor
9
- # - :delta?
10
- # - :disable_range?
11
- # - :group_concat_max_len
12
- # - :utf8?
13
- # - :position
14
- #
5
+ OPTIONS = [:name, :offset, :delta_processor, :delta?, :disable_range?,
6
+ :group_concat_max_len, :utf8?, :position]
7
+
15
8
  def initialize(model, options = {})
16
9
  @model = model
17
10
  @database_settings = model.connection.instance_variable_get(:@config).clone
@@ -6,10 +6,11 @@ class ThinkingSphinx::ActiveRecord::SQLSource::Template
6
6
  end
7
7
 
8
8
  def apply
9
- add_field class_column, :sphinx_internal_class, :facet => true
9
+ add_field class_column, :sphinx_internal_class_name
10
10
 
11
- add_attribute :id, :sphinx_internal_id, nil
12
- add_attribute '0', :sphinx_deleted, :integer
11
+ add_attribute :id, :sphinx_internal_id, nil
12
+ add_attribute class_column, :sphinx_internal_class, :string, :facet => true
13
+ add_attribute '0', :sphinx_deleted, :integer
13
14
  end
14
15
 
15
16
  private
@@ -25,7 +25,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
25
25
  searchd.address = Defaults::ADDRESS unless searchd.address.present?
26
26
  searchd.mysql41 = settings['mysql41'] || settings['port'] ||
27
27
  Defaults::PORT
28
- # searchd.workers = 'threads'
28
+ searchd.workers = 'threads'
29
29
 
30
30
  @offsets = {}
31
31
  end
@@ -26,6 +26,8 @@ class ThinkingSphinx::Configuration::ConsistentIds
26
26
  end
27
27
 
28
28
  def sources
29
- @sources ||= @indices.collect(&:sources).flatten
29
+ @sources ||= @indices.select { |index|
30
+ index.respond_to?(:sources)
31
+ }.collect(&:sources).flatten
30
32
  end
31
33
  end
@@ -21,6 +21,13 @@ module ThinkingSphinx::Connection
21
21
  ThinkingSphinx::Connection::MRI
22
22
  end
23
23
 
24
+ def self.pool
25
+ @pool ||= Innertube::Pool.new(
26
+ Proc.new { ThinkingSphinx::Connection.new },
27
+ Proc.new { |connection| connection.close }
28
+ )
29
+ end
30
+
24
31
  class MRI
25
32
  attr_reader :client
26
33
 
@@ -32,8 +39,15 @@ module ThinkingSphinx::Connection
32
39
  }.merge(options))
33
40
  end
34
41
 
42
+ def close
43
+ client.close
44
+ end
45
+
35
46
  def execute(statement)
36
47
  client.query statement
48
+ rescue
49
+ puts "Error with statement: #{statement}"
50
+ raise
37
51
  end
38
52
 
39
53
  def query(statement)
@@ -5,3 +5,4 @@ end
5
5
  require 'thinking_sphinx/core/field'
6
6
  require 'thinking_sphinx/core/index'
7
7
  require 'thinking_sphinx/core/interpreter'
8
+ require 'thinking_sphinx/core/property'
@@ -7,10 +7,11 @@ module ThinkingSphinx::Core::Index
7
7
  end
8
8
 
9
9
  def initialize(reference, options = {})
10
- @reference = reference.to_sym
11
- @docinfo = :extern
12
- @options = options
13
- @offset = config.next_offset(reference)
10
+ @reference = reference.to_sym
11
+ @docinfo = :extern
12
+ @charset_type = 'utf-8'
13
+ @options = options
14
+ @offset = config.next_offset(reference)
14
15
 
15
16
  super "#{options[:name] || reference.to_s.gsub('/', '_')}_#{name_suffix}"
16
17
  end
@@ -47,11 +48,6 @@ module ThinkingSphinx::Core::Index
47
48
  super
48
49
  end
49
50
 
50
- def sources
51
- interpret_definition!
52
- super
53
- end
54
-
55
51
  private
56
52
 
57
53
  def config
@@ -0,0 +1,13 @@
1
+ module ThinkingSphinx::Core::Property
2
+ def facet?
3
+ options[:facet]
4
+ end
5
+
6
+ def multi?
7
+ false
8
+ end
9
+
10
+ def type
11
+ nil
12
+ end
13
+ end
@@ -69,7 +69,7 @@ class ThinkingSphinx::Middlewares::SphinxQL <
69
69
 
70
70
  def extended_query
71
71
  conditions = options[:conditions] || {}
72
- conditions[:sphinx_internal_class] = class_condition if classes.any?
72
+ conditions[:sphinx_internal_class_name] = class_condition if classes.any?
73
73
  @extended_query ||= begin
74
74
  ThinkingSphinx::Search::Query.new(context.search.query, conditions,
75
75
  options[:star]).to_s
@@ -1,12 +1,35 @@
1
1
  class ThinkingSphinx::RakeInterface
2
2
  def configure
3
- puts "Generating configuration to #{config.configuration_file}"
4
- config.render_to_file
3
+ puts "Generating configuration to #{configuration.configuration_file}"
4
+ configuration.render_to_file
5
+ end
6
+
7
+ def generate
8
+ configuration.preload_indices
9
+ configuration.render
10
+
11
+ FileUtils.mkdir_p configuration.indices_location
12
+
13
+ configuration.indices.each do |index|
14
+ next unless index.is_a?(ThinkingSphinx::RealTime::Index)
15
+
16
+ puts "Generating index files for #{index.name}"
17
+ transcriber = ThinkingSphinx::RealTime::Transcriber.new index
18
+ Dir["#{index.path}*"].each { |file| FileUtils.rm file }
19
+
20
+ index.model.find_each do |instance|
21
+ transcriber.copy instance
22
+ print "."
23
+ end
24
+ print "\n"
25
+
26
+ controller.rotate
27
+ end
5
28
  end
6
29
 
7
30
  def index(reconfigure = true)
8
31
  configure if reconfigure
9
- FileUtils.mkdir_p config.indices_location
32
+ FileUtils.mkdir_p configuration.indices_location
10
33
  controller.index :verbose => true
11
34
  end
12
35
 
@@ -37,11 +60,11 @@ class ThinkingSphinx::RakeInterface
37
60
 
38
61
  private
39
62
 
40
- def config
63
+ def configuration
41
64
  ThinkingSphinx::Configuration.instance
42
65
  end
43
66
 
44
67
  def controller
45
- config.controller
68
+ configuration.controller
46
69
  end
47
70
  end
@@ -7,5 +7,6 @@ require 'thinking_sphinx/real_time/attribute'
7
7
  require 'thinking_sphinx/real_time/field'
8
8
  require 'thinking_sphinx/real_time/index'
9
9
  require 'thinking_sphinx/real_time/interpreter'
10
+ require 'thinking_sphinx/real_time/transcriber'
10
11
 
11
12
  require 'thinking_sphinx/real_time/callbacks/real_time_callbacks'
@@ -2,4 +2,14 @@ class ThinkingSphinx::RealTime::Attribute < ThinkingSphinx::RealTime::Property
2
2
  def type
3
3
  @options[:type]
4
4
  end
5
+
6
+ def translate(object)
7
+ super || default_value
8
+ end
9
+
10
+ private
11
+
12
+ def default_value
13
+ type == :string ? '' : 0
14
+ end
5
15
  end
@@ -7,29 +7,18 @@ class ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks <
7
7
  return unless real_time_indices?
8
8
 
9
9
  real_time_indices.each do |index|
10
- columns, values = ['id'], [index.document_id_for_key(instance.id)]
11
- (index.fields + index.attributes).each do |property|
12
- columns << property.name
13
- values << property.translate(instance)
14
- end
15
-
16
- sphinxql = Riddle::Query::Insert.new(index.name, columns, values).replace!
17
- connection.execute sphinxql.to_sql
10
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy instance
18
11
  end
19
12
  end
20
13
 
21
14
  private
22
15
 
23
- def config
16
+ def configuration
24
17
  ThinkingSphinx::Configuration.instance
25
18
  end
26
19
 
27
- def connection
28
- connection = ThinkingSphinx::Connection.new
29
- end
30
-
31
20
  def indices
32
- @indices ||= config.indices_for_references reference
21
+ @indices ||= configuration.indices_for_references reference
33
22
  end
34
23
 
35
24
  def real_time_indices?
@@ -1,5 +1,7 @@
1
1
  class ThinkingSphinx::RealTime::Field < ThinkingSphinx::RealTime::Property
2
2
  include ThinkingSphinx::Core::Field
3
3
 
4
- #
4
+ def translate(object)
5
+ Array(super || '').join(' ')
6
+ end
5
7
  end
@@ -1,17 +1,40 @@
1
1
  class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
2
2
  include ThinkingSphinx::Core::Index
3
3
 
4
- attr_accessor :fields, :attributes
4
+ attr_writer :fields, :attributes, :conditions
5
5
 
6
6
  def initialize(reference, options = {})
7
7
  @fields = []
8
8
  @attributes = []
9
+ @conditions = []
9
10
 
10
11
  Template.new(self).apply
11
12
 
12
13
  super reference, options
13
14
  end
14
15
 
16
+ def attributes
17
+ interpret_definition!
18
+
19
+ @attributes
20
+ end
21
+
22
+ def conditions
23
+ interpret_definition!
24
+
25
+ @conditions
26
+ end
27
+
28
+ def facets
29
+ properties.select(&:facet?)
30
+ end
31
+
32
+ def fields
33
+ interpret_definition!
34
+
35
+ @fields
36
+ end
37
+
15
38
  def unique_attribute_names
16
39
  attributes.collect(&:name)
17
40
  end
@@ -38,10 +61,14 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
38
61
  when :float
39
62
  @rt_attr_float << attribute.name unless @rt_attr_float.include?(attribute.name)
40
63
  else
41
- raise "Unknown attribute type '#{attribute.type(model)}'"
64
+ raise "Unknown attribute type '#{attribute.type}'"
42
65
  end
43
66
  end
44
67
  end
68
+
69
+ def properties
70
+ fields + attributes
71
+ end
45
72
  end
46
73
 
47
74
  require 'thinking_sphinx/real_time/index/template'
@@ -6,18 +6,19 @@ class ThinkingSphinx::RealTime::Index::Template
6
6
  end
7
7
 
8
8
  def apply
9
- add_field class_column, :sphinx_internal_class
9
+ add_field class_column, :sphinx_internal_class_name
10
10
 
11
- add_attribute :id, :sphinx_internal_id, :integer
12
- add_attribute 0, :sphinx_deleted, :integer
11
+ add_attribute :id, :sphinx_internal_id, :integer
12
+ add_attribute class_column, :sphinx_internal_class, :string, :facet => true
13
+ add_attribute 0, :sphinx_deleted, :integer
13
14
  end
14
15
 
15
16
  private
16
17
 
17
- def add_attribute(column, name, type)
18
+ def add_attribute(column, name, type, options = {})
18
19
  index.attributes << ThinkingSphinx::RealTime::Attribute.new(
19
20
  ThinkingSphinx::ActiveRecord::Column.new(*column),
20
- :as => name, :type => type
21
+ options.merge(:as => name, :type => type)
21
22
  )
22
23
  end
23
24
 
@@ -20,4 +20,8 @@ class ThinkingSphinx::RealTime::Interpreter <
20
20
  @index.send("#{key}=", value) if @index.class.settings.include?(key)
21
21
  end
22
22
  end
23
+
24
+ def where(condition)
25
+ @index.conditions << condition
26
+ end
23
27
  end
@@ -1,4 +1,6 @@
1
1
  class ThinkingSphinx::RealTime::Property
2
+ include ThinkingSphinx::Core::Property
3
+
2
4
  attr_reader :options
3
5
 
4
6
  def initialize(column, options = {})
@@ -0,0 +1,37 @@
1
+ class ThinkingSphinx::RealTime::Transcriber
2
+ def initialize(index)
3
+ @index = index
4
+ end
5
+
6
+ def copy(instance)
7
+ return unless copy? instance
8
+
9
+ columns, values = ['id'], [index.document_id_for_key(instance.id)]
10
+ (index.fields + index.attributes).each do |property|
11
+ columns << property.name
12
+ values << property.translate(instance)
13
+ end
14
+
15
+ sphinxql = Riddle::Query::Insert.new index.name, columns, values
16
+ ThinkingSphinx::Connection.pool.take do |connection|
17
+ connection.execute sphinxql.replace!.to_sql
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :index
24
+
25
+ def copy?(instance)
26
+ index.conditions.empty? || index.conditions.all? { |condition|
27
+ case condition
28
+ when Symbol
29
+ instance.send(condition)
30
+ when Proc
31
+ condition.call instance
32
+ else
33
+ "Unexpected condition: #{condition}. Expecting Symbol or Proc."
34
+ end
35
+ }
36
+ end
37
+ end
@@ -1,9 +1,4 @@
1
1
  namespace :ts do
2
- desc 'The current version of Thinking Sphinx'
3
- task :version do
4
- puts "Thinking Sphinx v#{ThinkingSphinx::VERSION}"
5
- end
6
-
7
2
  desc 'Generate the Sphinx configuration file'
8
3
  task :configure => :environment do
9
4
  interface.configure
@@ -14,8 +9,14 @@ namespace :ts do
14
9
  interface.index(ENV['INDEX_ONLY'] != 'true')
15
10
  end
16
11
 
12
+ desc 'Generate fresh index files for real-time indices'
13
+ task :generate => :environment do
14
+ interface.generate
15
+ end
16
+
17
17
  desc 'Stop Sphinx, index and then restart Sphinx'
18
18
  task :rebuild => [:stop, :index, :start]
19
+
19
20
  desc 'Restart the Sphinx daemon'
20
21
  task :restart => [:stop, :start]
21
22
 
@@ -23,10 +23,11 @@ describe 'Faceted searching', :live => true do
23
23
  Tee.create!
24
24
  Tee.create!
25
25
  City.create!
26
+ Product.create!
26
27
  index
27
28
 
28
29
  ThinkingSphinx.facets.to_hash[:class].should == {
29
- 'Tee' => 2, 'City' => 1
30
+ 'Tee' => 2, 'City' => 1, 'Product' => 1
30
31
  }
31
32
  end
32
33
 
@@ -84,4 +84,24 @@ describe 'Index options' do
84
84
  index.sources.first.sql_attr_str2wordcount.should == ['content']
85
85
  end
86
86
  end
87
+
88
+ context 'respecting source options' do
89
+ before :each do
90
+ index.definition_block = Proc.new {
91
+ indexes title
92
+
93
+ set_property :sql_range_step => 5
94
+ set_property :disable_range? => true
95
+ }
96
+ index.render
97
+ end
98
+
99
+ it "allows for core source settings" do
100
+ index.sources.first.sql_range_step.should == 5
101
+ end
102
+
103
+ it "allows for source options" do
104
+ index.sources.first.disable_range?.should be_true
105
+ end
106
+ end
87
107
  end
@@ -51,7 +51,6 @@ end
51
51
 
52
52
  describe 'Searching within a model with a realtime index', :live => true do
53
53
  it "returns results" do
54
- pending "Sphinx bug on OS X means I can't currently test this."
55
54
  product = Product.create! :name => 'Widget'
56
55
 
57
56
  Product.search.first.should == product
@@ -15,6 +15,9 @@ RSpec.configure do |config|
15
15
  config.include SphinxHelpers
16
16
 
17
17
  config.before :all do |group|
18
+ FileUtils.rm_rf ThinkingSphinx::Configuration.instance.indices_location
19
+ FileUtils.rm_rf ThinkingSphinx::Configuration.instance.searchd.binlog_path
20
+
18
21
  sphinx.setup && sphinx.start if group.class.metadata[:live]
19
22
  end
20
23
 
@@ -1,3 +1,3 @@
1
- # ThinkingSphinx::Index.define :product, :with => :real_time do
2
- # indexes name
3
- # end
1
+ ThinkingSphinx::Index.define :product, :with => :real_time do
2
+ indexes name
3
+ end
@@ -32,9 +32,19 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
32
32
  source.attributes.collect(&:name).should include('sphinx_internal_id')
33
33
  end
34
34
 
35
+ it "has the class name attribute by default" do
36
+ source.attributes.collect(&:name).should include('sphinx_internal_class')
37
+ end
38
+
35
39
  it "has the internal deleted attribute by default" do
36
40
  source.attributes.collect(&:name).should include('sphinx_deleted')
37
41
  end
42
+
43
+ it "marks the internal class attribute as a facet" do
44
+ source.attributes.detect { |attribute|
45
+ attribute.name == 'sphinx_internal_class'
46
+ }.options[:facet].should be_true
47
+ end
38
48
  end
39
49
 
40
50
  describe '#delta_processor' do
@@ -77,12 +87,13 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
77
87
 
78
88
  describe '#fields' do
79
89
  it "has the internal class field by default" do
80
- source.fields.collect(&:name).should include('sphinx_internal_class')
90
+ source.fields.collect(&:name).
91
+ should include('sphinx_internal_class_name')
81
92
  end
82
93
 
83
94
  it "sets the sphinx class field to use a string of the class name" do
84
95
  source.fields.detect { |field|
85
- field.name == 'sphinx_internal_class'
96
+ field.name == 'sphinx_internal_class_name'
86
97
  }.columns.first.__name.should == "'User'"
87
98
  end
88
99
 
@@ -94,16 +105,10 @@ describe ThinkingSphinx::ActiveRecord::SQLSource do
94
105
  model.stub :column_names => ['type'], :sti_name => 'User'
95
106
 
96
107
  source.fields.detect { |field|
97
- field.name == 'sphinx_internal_class'
108
+ field.name == 'sphinx_internal_class_name'
98
109
  }.columns.first.__name.
99
110
  should == "ifnull(\"users\".\"type\", 'User')"
100
111
  end
101
-
102
- it "marks the internal class field as a facet" do
103
- source.fields.detect { |field|
104
- field.name == 'sphinx_internal_class'
105
- }.options[:facet].should be_true
106
- end
107
112
  end
108
113
 
109
114
  describe '#name' do
@@ -102,7 +102,7 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
102
102
  search.options[:classes] = [submodel]
103
103
 
104
104
  ThinkingSphinx::Search::Query.should_receive(:new).with(anything,
105
- hash_including(:sphinx_internal_class => '(Lion)'), anything).
105
+ hash_including(:sphinx_internal_class_name => '(Lion)'), anything).
106
106
  and_return(query)
107
107
 
108
108
  middleware.call [context]
@@ -44,11 +44,11 @@ describe ThinkingSphinx::RealTime::Attribute do
44
44
  attribute.translate(object).should == 'the parent name'
45
45
  end
46
46
 
47
- it "returns nil if any element in the object tree is nil" do
47
+ it "returns zero if any element in the object tree is nil" do
48
48
  column.stub :__name => :name, :__stack => [:parent]
49
49
  object.parent = nil
50
50
 
51
- attribute.translate(object).should be_nil
51
+ attribute.translate(object).should be_zero
52
52
  end
53
53
  end
54
54
 
@@ -7,12 +7,13 @@ describe ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks do
7
7
  let(:instance) { double('instance', :id => 12) }
8
8
  let(:config) { double('config', :indices_for_references => [index]) }
9
9
  let(:index) { double('index', :name => 'my_index', :is_a? => true,
10
- :document_id_for_key => 123, :fields => [], :attributes => []) }
10
+ :document_id_for_key => 123, :fields => [], :attributes => [],
11
+ :conditions => []) }
11
12
  let(:connection) { double('connection', :execute => true) }
12
13
 
13
14
  before :each do
14
15
  ThinkingSphinx::Configuration.stub :instance => config
15
- ThinkingSphinx::Connection.stub :new => connection
16
+ ThinkingSphinx::Connection.stub_chain(:pool, :take).and_yield connection
16
17
  end
17
18
 
18
19
  describe '.after_save' do
@@ -26,10 +26,10 @@ describe ThinkingSphinx::RealTime::Field do
26
26
  field.translate(object).should == 'value'
27
27
  end
28
28
 
29
- it "returns the column's name if it's an integer" do
29
+ it "returns the column's name as a string if it's an integer" do
30
30
  column.stub :__name => 404
31
31
 
32
- field.translate(object).should == 404
32
+ field.translate(object).should == '404'
33
33
  end
34
34
 
35
35
  it "returns the object's method matching the column's name" do
@@ -44,11 +44,11 @@ describe ThinkingSphinx::RealTime::Field do
44
44
  field.translate(object).should == 'the parent name'
45
45
  end
46
46
 
47
- it "returns nil if any element in the object tree is nil" do
47
+ it "returns a blank string if any element in the object tree is nil" do
48
48
  column.stub :__name => :name, :__stack => [:parent]
49
49
  object.parent = nil
50
50
 
51
- field.translate(object).should be_nil
51
+ field.translate(object).should == ''
52
52
  end
53
53
  end
54
54
  end
@@ -15,6 +15,10 @@ describe ThinkingSphinx::RealTime::Index do
15
15
  index.attributes.collect(&:name).should include('sphinx_internal_id')
16
16
  end
17
17
 
18
+ it "has the class name attribute by default" do
19
+ index.attributes.collect(&:name).should include('sphinx_internal_class')
20
+ end
21
+
18
22
  it "has the internal deleted attribute by default" do
19
23
  index.attributes.collect(&:name).should include('sphinx_deleted')
20
24
  end
@@ -37,7 +41,7 @@ describe ThinkingSphinx::RealTime::Index do
37
41
 
38
42
  describe '#fields' do
39
43
  it "has the internal class field by default" do
40
- index.fields.collect(&:name).should include('sphinx_internal_class')
44
+ index.fields.collect(&:name).should include('sphinx_internal_class_name')
41
45
  end
42
46
  end
43
47
 
@@ -135,7 +139,7 @@ describe ThinkingSphinx::RealTime::Index do
135
139
 
136
140
  describe '#render' do
137
141
  it "interprets the provided definition" do
138
- index.should_receive(:interpret_definition!)
142
+ index.should_receive(:interpret_definition!).at_least(:once)
139
143
 
140
144
  begin
141
145
  index.render
@@ -147,8 +151,9 @@ describe ThinkingSphinx::RealTime::Index do
147
151
 
148
152
  describe '#unique_attribute_names' do
149
153
  it "returns all attribute names" do
150
- index.unique_attribute_names.
151
- should == ['sphinx_internal_id', 'sphinx_deleted']
154
+ index.unique_attribute_names.should == [
155
+ 'sphinx_internal_id', 'sphinx_internal_class', 'sphinx_deleted'
156
+ ]
152
157
  end
153
158
  end
154
159
  end
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'thinking-sphinx'
6
- s.version = '3.0.0.rc'
6
+ s.version = '3.0.0'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Pat Allan"]
9
9
  s.email = ["pat@freelancing-gods.com"]
@@ -23,10 +23,11 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency 'activerecord', '>= 3.1.0'
24
24
  s.add_runtime_dependency 'builder', '>= 2.1.2'
25
25
  s.add_runtime_dependency 'middleware', '>= 0.1.0'
26
- s.add_runtime_dependency 'riddle', '>= 1.5.3'
26
+ s.add_runtime_dependency 'innertube', '>= 1.0.2'
27
+ s.add_runtime_dependency 'riddle', '>= 1.5.4'
27
28
 
28
29
  s.add_development_dependency 'appraisal', '~> 0.4.0'
29
- # s.add_development_dependency 'combustion', '~> 0.3.1'
30
+ s.add_development_dependency 'combustion', '~> 0.3.3'
30
31
  s.add_development_dependency 'database_cleaner', '~> 0.7.1'
31
32
  s.add_development_dependency 'rspec', '~> 2.11.0'
32
33
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.rc
5
- prerelease: 6
4
+ version: 3.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Pat Allan
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-22 00:00:00.000000000 Z
12
+ date: 2013-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.1.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: innertube
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.0.2
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.2
62
78
  - !ruby/object:Gem::Dependency
63
79
  name: riddle
64
80
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +82,7 @@ dependencies:
66
82
  requirements:
67
83
  - - ! '>='
68
84
  - !ruby/object:Gem::Version
69
- version: 1.5.3
85
+ version: 1.5.4
70
86
  type: :runtime
71
87
  prerelease: false
72
88
  version_requirements: !ruby/object:Gem::Requirement
@@ -74,7 +90,7 @@ dependencies:
74
90
  requirements:
75
91
  - - ! '>='
76
92
  - !ruby/object:Gem::Version
77
- version: 1.5.3
93
+ version: 1.5.4
78
94
  - !ruby/object:Gem::Dependency
79
95
  name: appraisal
80
96
  requirement: !ruby/object:Gem::Requirement
@@ -91,6 +107,22 @@ dependencies:
91
107
  - - ~>
92
108
  - !ruby/object:Gem::Version
93
109
  version: 0.4.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: combustion
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 0.3.3
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 0.3.3
94
126
  - !ruby/object:Gem::Dependency
95
127
  name: database_cleaner
96
128
  requirement: !ruby/object:Gem::Requirement
@@ -181,6 +213,7 @@ files:
181
213
  - lib/thinking_sphinx/core/field.rb
182
214
  - lib/thinking_sphinx/core/index.rb
183
215
  - lib/thinking_sphinx/core/interpreter.rb
216
+ - lib/thinking_sphinx/core/property.rb
184
217
  - lib/thinking_sphinx/deltas.rb
185
218
  - lib/thinking_sphinx/deltas/default_delta.rb
186
219
  - lib/thinking_sphinx/excerpter.rb
@@ -221,6 +254,7 @@ files:
221
254
  - lib/thinking_sphinx/real_time/index/template.rb
222
255
  - lib/thinking_sphinx/real_time/interpreter.rb
223
256
  - lib/thinking_sphinx/real_time/property.rb
257
+ - lib/thinking_sphinx/real_time/transcriber.rb
224
258
  - lib/thinking_sphinx/scopes.rb
225
259
  - lib/thinking_sphinx/search.rb
226
260
  - lib/thinking_sphinx/search/batch_inquirer.rb
@@ -358,9 +392,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
358
392
  required_rubygems_version: !ruby/object:Gem::Requirement
359
393
  none: false
360
394
  requirements:
361
- - - ! '>'
395
+ - - ! '>='
362
396
  - !ruby/object:Gem::Version
363
- version: 1.3.1
397
+ version: '0'
364
398
  requirements: []
365
399
  rubyforge_project: thinking-sphinx
366
400
  rubygems_version: 1.8.23
@@ -476,3 +510,4 @@ test_files:
476
510
  - spec/thinking_sphinx/search/query_spec.rb
477
511
  - spec/thinking_sphinx/search_spec.rb
478
512
  - spec/thinking_sphinx_spec.rb
513
+ has_rdoc: