sunspot_index_queue 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ 1.1.1
2
+
3
+ - Fix bug where entries were lost if they failed to be submitted individually after they failed to be submitted in a batch.
4
+
5
+ - Fix compatibility with latest mongo gem.
6
+
1
7
  1.1.0
2
8
 
3
9
  - Change column type for ActiveRecord and DataMapper implementation queue table for record_id from String to Integer. This makes it far more efficient in the normal case at the expense of being less flexible. If you do end up needing String record id's, just create the table definition yourself. To upgrade, you should clear the queue and then rerun the migration.
data/Rakefile CHANGED
@@ -6,18 +6,17 @@ desc 'Default: run unit tests.'
6
6
  task :default => :test
7
7
 
8
8
  begin
9
- require 'spec/rake/spectask'
10
- desc 'Test the gem.'
11
- Spec::Rake::SpecTask.new(:test) do |t|
12
- t.spec_files = FileList.new('spec/**/*_spec.rb')
13
- end
9
+ require 'rspec'
10
+ require 'rspec/core/rake_task'
11
+ desc 'Run the unit tests'
12
+ RSpec::Core::RakeTask.new(:test)
14
13
  rescue LoadError
15
14
  task :test do
16
- STDERR.puts "You must have rspec >= 1.3.0 to run the tests"
15
+ STDERR.puts "You must have rspec 2.0 installed to run the tests"
17
16
  end
18
17
  end
19
18
 
20
- desc 'Generate documentation for sunspot_index_queue.'
19
+ desc 'Generate documentation.'
21
20
  Rake::RDocTask.new(:rdoc) do |rdoc|
22
21
  rdoc.rdoc_dir = 'rdoc'
23
22
  rdoc.options << '--title' << 'Sunspot Index Queue' << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
@@ -34,7 +33,7 @@ begin
34
33
  gem.email = "brian@embellishedvisions.com"
35
34
  gem.homepage = "http://github.com/bdurand/sunspot_index_queue"
36
35
  gem.authors = ["Brian Durand"]
37
- gem.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc"]
36
+ gem.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc", "MIT_LICENSE"]
38
37
 
39
38
  gem.add_dependency('sunspot', '>= 1.1.0')
40
39
  gem.add_development_dependency('sqlite3')
@@ -43,7 +42,7 @@ begin
43
42
  gem.add_development_dependency('dm-aggregates', '>=1.0.0')
44
43
  gem.add_development_dependency('dm-migrations', '>=1.0.0')
45
44
  gem.add_development_dependency('mongo')
46
- gem.add_development_dependency('rspec', '>= 1.3.0')
45
+ gem.add_development_dependency('rspec', '>= 2.0.0')
47
46
  gem.add_development_dependency('jeweler')
48
47
  end
49
48
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.1
@@ -19,7 +19,7 @@ module Sunspot
19
19
 
20
20
  class << self
21
21
  # Set the default priority for indexing items within a block. Higher priority items will be processed first.
22
- def set_priority (priority, &block)
22
+ def set_priority(priority, &block)
23
23
  save_val = Thread.current[:sunspot_index_queue_priority]
24
24
  begin
25
25
  Thread.current[:sunspot_index_queue_priority] = priority.to_i
@@ -48,7 +48,7 @@ module Sunspot
48
48
  # queues process different classes of records when they need to different configurations.
49
49
  #
50
50
  # +:session+ - The Sunspot::Session object to use for communicating with Solr (defaults to a session with the default config).
51
- def initialize (options = {})
51
+ def initialize(options = {})
52
52
  @retry_interval = options[:retry_interval] || 60
53
53
  @batch_size = options[:batch_size] || 100
54
54
  @batch_handler = nil
@@ -71,14 +71,14 @@ module Sunspot
71
71
  # batch.submit!
72
72
  # end
73
73
  # end
74
- def batch_handler (&block)
74
+ def batch_handler(&block)
75
75
  @batch_handler = block
76
76
  end
77
77
 
78
78
  # Add a record to be indexed to the queue. The record can be specified as either an indexable object or as
79
79
  # as hash with :class and :id keys. The priority to be indexed can be passed in the options as +:priority+
80
80
  # (defaults to 0).
81
- def index (record_or_hash, options = {})
81
+ def index(record_or_hash, options = {})
82
82
  klass, id = class_and_id(record_or_hash)
83
83
  Entry.enqueue(self, klass, id, false, options[:priority] || self.class.default_priority)
84
84
  end
@@ -86,20 +86,20 @@ module Sunspot
86
86
  # Add a record to be removed to the queue. The record can be specified as either an indexable object or as
87
87
  # as hash with :class and :id keys. The priority to be indexed can be passed in the options as +:priority+
88
88
  # (defaults to 0).
89
- def remove (record_or_hash, options = {})
89
+ def remove(record_or_hash, options = {})
90
90
  klass, id = class_and_id(record_or_hash)
91
91
  Entry.enqueue(self, klass, id, true, options[:priority] || self.class.default_priority)
92
92
  end
93
93
 
94
94
  # Add a list of records to be indexed to the queue. The priority to be indexed can be passed in the
95
95
  # options as +:priority+ (defaults to 0).
96
- def index_all (klass, ids, options = {})
96
+ def index_all(klass, ids, options = {})
97
97
  Entry.enqueue(self, klass, ids, false, options[:priority] || self.class.default_priority)
98
98
  end
99
99
 
100
100
  # Add a list of records to be removed to the queue. The priority to be indexed can be passed in the
101
101
  # options as +:priority+ (defaults to 0).
102
- def remove_all (klass, ids, options = {})
102
+ def remove_all(klass, ids, options = {})
103
103
  Entry.enqueue(self, klass, ids, true, options[:priority] || self.class.default_priority)
104
104
  end
105
105
 
@@ -119,7 +119,7 @@ module Sunspot
119
119
  end
120
120
 
121
121
  # Get the entries in the queue that have errors. Supported options are +:limit+ (default 50) and +:offset+ (default 0).
122
- def errors (options = {})
122
+ def errors(options = {})
123
123
  limit = options[:limit] ? options[:limit].to_i : 50
124
124
  Entry.errors(self, limit, options[:offset].to_i)
125
125
  end
@@ -157,7 +157,7 @@ module Sunspot
157
157
  private
158
158
 
159
159
  # Get the class and id for either a record or a hash containing +:class+ and +:id+ options
160
- def class_and_id (record_or_hash)
160
+ def class_and_id(record_or_hash)
161
161
  if record_or_hash.is_a?(Hash)
162
162
  [record_or_hash[:class], record_or_hash[:id]]
163
163
  else
@@ -9,7 +9,7 @@ module Sunspot
9
9
  # be processed.
10
10
  PASS_THROUGH_EXCEPTIONS = [SystemExit, NoMemoryError, Interrupt, SignalException, Errno::ECONNREFUSED]
11
11
 
12
- def initialize (queue, entries = nil)
12
+ def initialize(queue, entries = nil)
13
13
  @queue = queue
14
14
  @entries = []
15
15
  @entries.concat(entries) if entries
@@ -32,6 +32,8 @@ module Sunspot
32
32
  end
33
33
  commit!
34
34
  rescue Exception => e
35
+ @delete_entries.clear
36
+ entries.each{|entry| entry.processed = false}
35
37
  if PASS_THROUGH_EXCEPTIONS.include?(e.class)
36
38
  raise e
37
39
  else
@@ -56,7 +58,7 @@ module Sunspot
56
58
  end
57
59
 
58
60
  # Clear the processed flag on all entries.
59
- def clear_processed (entries)
61
+ def clear_processed(entries)
60
62
  entries.each{|entry| entry.processed = false}
61
63
  end
62
64
 
@@ -66,8 +68,9 @@ module Sunspot
66
68
  Entry.delete_entries(@delete_entries) unless @delete_entries.empty?
67
69
  rescue Exception => e
68
70
  clear_processed(entries)
69
- @delete_entries.clear
70
71
  raise e
72
+ ensure
73
+ @delete_entries.clear
71
74
  end
72
75
 
73
76
  # Submit all entries to Solr individually and then commit.
@@ -90,7 +93,7 @@ module Sunspot
90
93
  end
91
94
 
92
95
  # Send an entry to Solr doing an update or delete as necessary.
93
- def submit_entry (entry)
96
+ def submit_entry(entry)
94
97
  log_entry_error(entry) do
95
98
  if entry.is_delete?
96
99
  session.remove_by_id(entry.record_class_name, entry.record_id)
@@ -102,7 +105,7 @@ module Sunspot
102
105
  end
103
106
 
104
107
  # Update an entry with an error message if a block fails.
105
- def log_entry_error (entry)
108
+ def log_entry_error(entry)
106
109
  begin
107
110
  yield
108
111
  entry.processed = true
@@ -24,7 +24,7 @@ module Sunspot
24
24
  # Implementations should support pulling entries in batches by a priority where higher priority
25
25
  # entries are processed first. Errors should be automatically retried after an interval specified
26
26
  # by the IndexQueue. The batch size set by the IndexQueue should also be honored.
27
- def implementation= (klass)
27
+ def implementation=(klass)
28
28
  unless klass.is_a?(Class) || klass.nil?
29
29
  class_name = klass.to_s
30
30
  class_name = Sunspot::Util.camel_case(class_name).gsub('/', '::') unless class_name.include?('::')
@@ -43,42 +43,42 @@ module Sunspot
43
43
  end
44
44
 
45
45
  # Get a count of the queue entries for an IndexQueue. Implementations must implement this method.
46
- def total_count (queue)
46
+ def total_count(queue)
47
47
  implementation.total_count(queue)
48
48
  end
49
49
 
50
50
  # Get a count of the entries ready to be processed for an IndexQueue. Implementations must implement this method.
51
- def ready_count (queue)
51
+ def ready_count(queue)
52
52
  implementation.ready_count(queue)
53
53
  end
54
54
 
55
55
  # Get a count of the error entries for an IndexQueue. Implementations must implement this method.
56
- def error_count (queue)
56
+ def error_count(queue)
57
57
  implementation.error_count(queue)
58
58
  end
59
59
 
60
60
  # Get the specified number of error entries for an IndexQueue. Implementations must implement this method.
61
- def errors (queue, limit, offset)
61
+ def errors(queue, limit, offset)
62
62
  implementation.errors(queue, limit, offset)
63
63
  end
64
64
 
65
65
  # Get the next batch of entries to process for IndexQueue. Implementations must implement this method.
66
- def next_batch! (queue)
66
+ def next_batch!(queue)
67
67
  implementation.next_batch!(queue)
68
68
  end
69
69
 
70
70
  # Reset the entries in the queue to be excuted again immediately and clear any errors.
71
- def reset! (queue)
71
+ def reset!(queue)
72
72
  implementation.reset!(queue)
73
73
  end
74
74
 
75
75
  # Add an entry the queue. +is_delete+ will be true if the entry is a delete. Implementations must implement this method.
76
- def add (klass, id, delete, options = {})
76
+ def add(klass, id, delete, options = {})
77
77
  raise NotImplementedError.new("add")
78
78
  end
79
79
 
80
80
  # Add multiple entries to the queue. +delete+ will be true if the entry is a delete.
81
- def enqueue (queue, klass, ids, delete, priority)
81
+ def enqueue(queue, klass, ids, delete, priority)
82
82
  klass = Sunspot::Util.full_const_get(klass.to_s) unless klass.is_a?(Class)
83
83
  unless queue.class_names.empty? || queue.class_names.include?(klass.name)
84
84
  raise ArgumentError.new("Class #{klass.name} is not in the class names allowed for the queue")
@@ -94,13 +94,13 @@ module Sunspot
94
94
  end
95
95
 
96
96
  # Delete entries from the queue. Implementations must implement this method.
97
- def delete_entries (entries)
97
+ def delete_entries(entries)
98
98
  implementation.delete_entries(entries)
99
99
  end
100
100
 
101
101
  # Load all records in an array of entries. This can be faster than calling load on each DataAccessor
102
102
  # depending on them implementation
103
- def load_all_records (entries)
103
+ def load_all_records(entries)
104
104
  classes = entries.collect{|entry| entry.record_class_name}.uniq.collect{|name| Sunspot::Util.full_const_get(name) rescue nil}.compact
105
105
  map = entries.inject({}){|hash, entry| hash[entry.record_id.to_s] = entry; hash}
106
106
  classes.each do |klass|
@@ -124,7 +124,7 @@ module Sunspot
124
124
  end
125
125
 
126
126
  # Set the error message on an entry. Implementations must implement this method.
127
- def set_error! (error, retry_interval = nil)
127
+ def set_error!(error, retry_interval = nil)
128
128
  raise NotImplementedError.new("set_error!")
129
129
  end
130
130
 
@@ -25,13 +25,13 @@ module Sunspot
25
25
 
26
26
  class << self
27
27
  # Implementation of the total_count method.
28
- def total_count (queue)
28
+ def total_count(queue)
29
29
  conditions = queue.class_names.empty? ? {} : {:record_class_name => queue.class_names}
30
30
  count(:conditions => conditions)
31
31
  end
32
32
 
33
33
  # Implementation of the ready_count method.
34
- def ready_count (queue)
34
+ def ready_count(queue)
35
35
  conditions = ["#{connection.quote_column_name('run_at')} <= ?", Time.now.utc]
36
36
  unless queue.class_names.empty?
37
37
  conditions.first << " AND #{connection.quote_column_name('record_class_name')} IN (?)"
@@ -41,7 +41,7 @@ module Sunspot
41
41
  end
42
42
 
43
43
  # Implementation of the error_count method.
44
- def error_count (queue)
44
+ def error_count(queue)
45
45
  conditions = ["#{connection.quote_column_name('error')} IS NOT NULL"]
46
46
  unless queue.class_names.empty?
47
47
  conditions.first << " AND #{connection.quote_column_name('record_class_name')} IN (?)"
@@ -51,7 +51,7 @@ module Sunspot
51
51
  end
52
52
 
53
53
  # Implementation of the errors method.
54
- def errors (queue, limit, offset)
54
+ def errors(queue, limit, offset)
55
55
  conditions = ["#{connection.quote_column_name('error')} IS NOT NULL"]
56
56
  unless queue.class_names.empty?
57
57
  conditions.first << " AND #{connection.quote_column_name('record_class_name')} IN (?)"
@@ -67,7 +67,7 @@ module Sunspot
67
67
  end
68
68
 
69
69
  # Implementation of the next_batch! method.
70
- def next_batch! (queue)
70
+ def next_batch!(queue)
71
71
  conditions = ["#{connection.quote_column_name('run_at')} <= ?", Time.now.utc]
72
72
  unless queue.class_names.empty?
73
73
  conditions.first << " AND #{connection.quote_column_name('record_class_name')} IN (?)"
@@ -82,7 +82,7 @@ module Sunspot
82
82
  end
83
83
 
84
84
  # Implementation of the add method.
85
- def add (klass, id, delete, priority)
85
+ def add(klass, id, delete, priority)
86
86
  queue_entry_key = {:record_id => id, :record_class_name => klass.name, :lock => nil}
87
87
  queue_entry = first(:conditions => queue_entry_key) || new(queue_entry_key.merge(:priority => priority))
88
88
  queue_entry.is_delete = delete
@@ -92,8 +92,8 @@ module Sunspot
92
92
  end
93
93
 
94
94
  # Implementation of the delete_entries method.
95
- def delete_entries (ids)
96
- delete_all(:id => ids)
95
+ def delete_entries(entries)
96
+ delete_all(:id => entries)
97
97
  end
98
98
 
99
99
  # Create the table used to store the queue in the database.
@@ -115,7 +115,7 @@ module Sunspot
115
115
  end
116
116
 
117
117
  # Implementation of the set_error! method.
118
- def set_error! (error, retry_interval = nil)
118
+ def set_error!(error, retry_interval = nil)
119
119
  self.attempts += 1
120
120
  self.run_at = (retry_interval * attempts).from_now.utc if retry_interval
121
121
  self.error = "#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")[0, 4000]}"
@@ -28,40 +28,40 @@ module Sunspot
28
28
 
29
29
  class << self
30
30
  # Implementation of the total_count method.
31
- def total_count (queue)
31
+ def total_count(queue)
32
32
  conditions = queue.class_names.empty? ? {} : {:record_class_name => queue.class_names}
33
33
  count(conditions)
34
34
  end
35
35
 
36
36
  # Implementation of the ready_count method.
37
- def ready_count (queue)
37
+ def ready_count(queue)
38
38
  conditions = {:run_at.lte => Time.now.utc}
39
39
  conditions[:record_class_name] = queue.class_names unless queue.class_names.empty?
40
40
  count(conditions)
41
41
  end
42
42
 
43
43
  # Implementation of the error_count method.
44
- def error_count (queue)
44
+ def error_count(queue)
45
45
  conditions = {:error.not => nil}
46
46
  conditions[:record_class_name] = queue.class_names unless queue.class_names.empty?
47
47
  count(conditions)
48
48
  end
49
49
 
50
50
  # Implementation of the errors method.
51
- def errors (queue, limit, offset)
51
+ def errors(queue, limit, offset)
52
52
  conditions = {:error.not => nil}
53
53
  conditions[:record_class_name] = queue.class_names unless queue.class_names.empty?
54
54
  all(conditions.merge(:limit => limit, :offset => offset, :order => :id))
55
55
  end
56
56
 
57
57
  # Implementation of the reset! method.
58
- def reset! (queue)
58
+ def reset!(queue)
59
59
  conditions = queue.class_names.empty? ? {} : {:record_class_name => queue.class_names}
60
60
  all(conditions).update!(:run_at => Time.now.utc, :attempts => 0, :error => nil, :lock => nil)
61
61
  end
62
62
 
63
63
  # Implementation of the next_batch! method.
64
- def next_batch! (queue)
64
+ def next_batch!(queue)
65
65
  conditions = {:run_at.lte => Time.now.utc}
66
66
  conditions[:record_class_name] = queue.class_names unless queue.class_names.empty?
67
67
  batch_entries = all(conditions.merge(:fields => [:id], :limit => queue.batch_size, :order => [:priority.desc, :run_at]))
@@ -73,7 +73,7 @@ module Sunspot
73
73
  end
74
74
 
75
75
  # Implementation of the add method.
76
- def add (klass, id, delete, priority)
76
+ def add(klass, id, delete, priority)
77
77
  queue_entry_key = {:record_id => id, :record_class_name => klass.name, :lock => nil}
78
78
  queue_entry = first(:conditions => queue_entry_key) || new(queue_entry_key.merge(:priority => priority))
79
79
  queue_entry.is_delete = delete
@@ -83,13 +83,13 @@ module Sunspot
83
83
  end
84
84
 
85
85
  # Implementation of the delete_entries method.
86
- def delete_entries (ids)
87
- all(:id => ids).destroy!
86
+ def delete_entries(entries)
87
+ all(:id => entries.map(&:id)).destroy!
88
88
  end
89
89
  end
90
90
 
91
91
  # Implementation of the set_error! method.
92
- def set_error! (error, retry_interval = nil)
92
+ def set_error!(error, retry_interval = nil)
93
93
  self.attempts += 1
94
94
  self.run_at = Time.now.utc + (retry_interval * attempts) if retry_interval
95
95
  self.error = "#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")[0, 4000]}"
@@ -9,17 +9,17 @@ module Sunspot
9
9
  # To set it up, you need to set the connection and database that it will use.
10
10
  #
11
11
  # Sunspot::IndexQueue::Entry::MongoImpl.connection = 'localhost'
12
- # Sunspot::IndexQueue::Entry::MongoImpl.database = 'my_database'
12
+ # Sunspot::IndexQueue::Entry::MongoImpl.database_name = 'my_database'
13
13
  # # or
14
14
  # Sunspot::IndexQueue::Entry::MongoImpl.connection = Mongo::Connection.new('localhost', 27017)
15
- # Sunspot::IndexQueue::Entry::MongoImpl.database = 'my_database'
15
+ # Sunspot::IndexQueue::Entry::MongoImpl.database_name = 'my_database'
16
16
  class MongoImpl
17
17
  include Entry
18
18
 
19
19
  class << self
20
20
  # Set the connection to MongoDB. The args can either be a Mongo::Connection object, or the args
21
21
  # that can be used to create a new Mongo::Connection.
22
- def connection= (*args)
22
+ def connection=(*args)
23
23
  @connection = args.first.is_a?(Mongo::Connection) ? args.first : Mongo::Connection.new(*args)
24
24
  end
25
25
 
@@ -29,7 +29,7 @@ module Sunspot
29
29
  end
30
30
 
31
31
  # Set the name of the database which will contain the queue collection.
32
- def database_name= (name)
32
+ def database_name=(name)
33
33
  @collection = nil
34
34
  @database_name = name
35
35
  end
@@ -45,20 +45,20 @@ module Sunspot
45
45
  end
46
46
 
47
47
  # Create a new entry.
48
- def create (attributes)
48
+ def create(attributes)
49
49
  entry = new(attributes)
50
50
  entry.save
51
51
  entry
52
52
  end
53
53
 
54
54
  # Find one entry given a selector or object id.
55
- def find_one (spec_or_object_id=nil, opts={})
55
+ def find_one(spec_or_object_id=nil, opts={})
56
56
  doc = collection.find_one(spec_or_object_id, opts)
57
57
  doc ? new(doc) : nil
58
58
  end
59
59
 
60
60
  # Find an array of entries given a selector.
61
- def find (selector={}, opts={})
61
+ def find(selector={}, opts={})
62
62
  collection.find(selector, opts).collect{|doc| new(doc)}
63
63
  end
64
64
 
@@ -68,18 +68,18 @@ module Sunspot
68
68
  end
69
69
 
70
70
  # Set the logger used to log errors.
71
- def logger= (logger)
71
+ def logger=(logger)
72
72
  @logger = logger
73
73
  end
74
74
 
75
75
  # Implementation of the total_count method.
76
- def total_count (queue)
76
+ def total_count(queue)
77
77
  conditions = queue.class_names.empty? ? {} : {:record_class_name => {'$in' => queue.class_names}}
78
78
  collection.find(conditions).count
79
79
  end
80
80
 
81
81
  # Implementation of the ready_count method.
82
- def ready_count (queue)
82
+ def ready_count(queue)
83
83
  conditions = {:run_at => {'$lte' => Time.now.utc}}
84
84
  unless queue.class_names.empty?
85
85
  conditions[:record_class_name] = {'$in' => queue.class_names}
@@ -88,7 +88,7 @@ module Sunspot
88
88
  end
89
89
 
90
90
  # Implementation of the error_count method.
91
- def error_count (queue)
91
+ def error_count(queue)
92
92
  conditions = {:error => {'$ne' => nil}}
93
93
  unless queue.class_names.empty?
94
94
  conditions[:record_class_name] = {'$in' => queue.class_names}
@@ -97,7 +97,7 @@ module Sunspot
97
97
  end
98
98
 
99
99
  # Implementation of the errors method.
100
- def errors (queue, limit, offset)
100
+ def errors(queue, limit, offset)
101
101
  conditions = {:error => {'$ne' => nil}}
102
102
  unless queue.class_names.empty?
103
103
  conditions[:record_class_name] = {'$in' => queue.class_names}
@@ -106,13 +106,13 @@ module Sunspot
106
106
  end
107
107
 
108
108
  # Implementation of the reset! method.
109
- def reset! (queue)
109
+ def reset!(queue)
110
110
  conditions = queue.class_names.empty? ? {} : {:record_class_name => {'$in' => queue.class_names}}
111
111
  collection.update(conditions, {"$set" => {:run_at => Time.now.utc, :attempts => 0, :error => nil}}, :multi => true)
112
112
  end
113
113
 
114
114
  # Implementation of the next_batch! method.
115
- def next_batch! (queue)
115
+ def next_batch!(queue)
116
116
  conditions = {:run_at => {'$lte' => Time.now.utc}}
117
117
  unless queue.class_names.empty?
118
118
  conditions[:record_class_name] = {'$in' => queue.class_names}
@@ -131,7 +131,7 @@ module Sunspot
131
131
  end
132
132
 
133
133
  # Implementation of the add method.
134
- def add (klass, id, delete, priority)
134
+ def add(klass, id, delete, priority)
135
135
  queue_entry_key = {:record_id => id, :record_class_name => klass.name, :lock => nil}
136
136
  queue_entry = find_one(queue_entry_key) || new(queue_entry_key.merge(:priority => priority))
137
137
  queue_entry.is_delete = delete
@@ -141,15 +141,15 @@ module Sunspot
141
141
  end
142
142
 
143
143
  # Implementation of the delete_entries method.
144
- def delete_entries (ids)
145
- collection.remove(:_id => {'$in' => ids})
144
+ def delete_entries(entries)
145
+ collection.remove(:_id => {'$in' => entries.map(&:id)})
146
146
  end
147
147
  end
148
148
 
149
149
  attr_reader :doc
150
150
 
151
151
  # Create a new entry from a document hash.
152
- def initialize (attributes = {})
152
+ def initialize(attributes = {})
153
153
  @doc = {}
154
154
  attributes.each do |key, value|
155
155
  @doc[key.to_s] = value
@@ -169,7 +169,7 @@ module Sunspot
169
169
  end
170
170
 
171
171
  # Set the entry record_class_name.
172
- def record_class_name= (value)
172
+ def record_class_name=(value)
173
173
  doc['record_class_name'] = value.nil? ? nil : value.to_s
174
174
  end
175
175
 
@@ -179,7 +179,7 @@ module Sunspot
179
179
  end
180
180
 
181
181
  # Set the entry record_id.
182
- def record_id= (value)
182
+ def record_id=(value)
183
183
  doc['record_id'] = value
184
184
  end
185
185
 
@@ -189,7 +189,7 @@ module Sunspot
189
189
  end
190
190
 
191
191
  # Set the entry run_at time.
192
- def run_at= (value)
192
+ def run_at=(value)
193
193
  value = Time.parse(value.to_s) unless value.nil? || value.is_a?(Time)
194
194
  doc['run_at'] = value.nil? ? nil : value.utc
195
195
  end
@@ -200,7 +200,7 @@ module Sunspot
200
200
  end
201
201
 
202
202
  # Set the entry priority.
203
- def priority= (value)
203
+ def priority=(value)
204
204
  doc['priority'] = value.to_i
205
205
  end
206
206
 
@@ -210,7 +210,7 @@ module Sunspot
210
210
  end
211
211
 
212
212
  # Set the entry attempts.
213
- def attempts= (value)
213
+ def attempts=(value)
214
214
  doc['attempts'] = value.to_i
215
215
  end
216
216
 
@@ -220,7 +220,7 @@ module Sunspot
220
220
  end
221
221
 
222
222
  # Set the entry error.
223
- def error= (value)
223
+ def error=(value)
224
224
  doc['error'] = value.nil? ? nil : value.to_s
225
225
  end
226
226
 
@@ -230,7 +230,7 @@ module Sunspot
230
230
  end
231
231
 
232
232
  # Set the entry delete entry flag.
233
- def is_delete= (value)
233
+ def is_delete=(value)
234
234
  doc['is_delete'] = !!value
235
235
  end
236
236
 
@@ -241,7 +241,7 @@ module Sunspot
241
241
  end
242
242
 
243
243
  # Implementation of the set_error! method.
244
- def set_error! (error, retry_interval = nil)
244
+ def set_error!(error, retry_interval = nil)
245
245
  self.attempts += 1
246
246
  self.run_at = (retry_interval * attempts).from_now.utc if retry_interval
247
247
  self.error = "#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")[0, 4000]}"
@@ -14,7 +14,7 @@ module Sunspot
14
14
  # Create a new session proxy for a particular queue (default to a queue for all classes bound to the
15
15
  # default session configuration). You can specify the session argument if the session used for queries should be
16
16
  # different than the one the queue is bound to.
17
- def initialize (queue = nil, session = nil)
17
+ def initialize(queue = nil, session = nil)
18
18
  @queue = queue || IndexQueue.new
19
19
  @session = session || @queue.session
20
20
  end
@@ -50,20 +50,20 @@ module Sunspot
50
50
  end
51
51
 
52
52
  # Queues up the index operation for later.
53
- def index (*objects)
53
+ def index(*objects)
54
54
  objects.flatten.each do |object|
55
55
  queue.index(object)
56
56
  end
57
57
  end
58
58
 
59
59
  # Queues up the index operation for later.
60
- def index! (*objects)
60
+ def index!(*objects)
61
61
  index(*objects)
62
62
  end
63
63
 
64
64
  # Queues up the remove operation for later unless a block is passed. In that case it will
65
65
  # be performed immediately.
66
- def remove (*objects, &block)
66
+ def remove(*objects, &block)
67
67
  if block
68
68
  # Delete by query not supported by queue, so send to server
69
69
  queue.session.remove(*objects, &block)
@@ -76,7 +76,7 @@ module Sunspot
76
76
 
77
77
  # Queues up the remove operation for later unless a block is passed. In that case it will
78
78
  # be performed immediately.
79
- def remove! (*objects, &block)
79
+ def remove!(*objects, &block)
80
80
  if block
81
81
  # Delete by query not supported by queue, so send to server
82
82
  queue.session.remove!(*objects, &block)
@@ -86,24 +86,24 @@ module Sunspot
86
86
  end
87
87
 
88
88
  # Proxies remove_all to the queue session.
89
- def remove_all (*classes)
89
+ def remove_all(*classes)
90
90
  # Delete by query not supported by queue, so send to server
91
91
  queue.session.remove_all(*classes)
92
92
  end
93
93
 
94
94
  # Proxies remove_all! to the queue session.
95
- def remove_all! (*classes)
95
+ def remove_all!(*classes)
96
96
  # Delete by query not supported by queue, so send to server
97
97
  queue.session.remove_all!(*classes)
98
98
  end
99
99
 
100
100
  # Queues up the index operation for later.
101
- def remove_by_id (clazz, id)
101
+ def remove_by_id(clazz, id)
102
102
  queue.remove(:class => clazz, :id => id)
103
103
  end
104
104
 
105
105
  # Queues up the index operation for later.
106
- def remove_by_id! (clazz, id)
106
+ def remove_by_id!(clazz, id)
107
107
  remove_by_id(clazz, id)
108
108
  end
109
109
  end
@@ -43,6 +43,22 @@ describe Sunspot::IndexQueue::Batch do
43
43
  entry_2.processed?.should == true
44
44
  end
45
45
 
46
+ it "should only delete entries that are successfully committed" do
47
+ entry_1.stub!(:record).and_return(record_1)
48
+ def session.batch
49
+ yield
50
+ raise("solr rejects malformed batch")
51
+ end
52
+ session.should_receive(:index).with(record_1).twice
53
+ session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id)
54
+ session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id).and_raise("boom")
55
+ session.should_receive(:commit)
56
+ Sunspot::IndexQueue::Entry.implementation.should_receive(:delete_entries).with([entry_1])
57
+ subject.submit!
58
+ entry_1.processed?.should == true
59
+ entry_2.processed?.should == false
60
+ entry_2.error.should_not == nil
61
+ end
46
62
 
47
63
  it "should add error messages to each entry that errors out" do
48
64
  entry_1.stub!(:record).and_return(record_1)
@@ -116,7 +116,7 @@ shared_examples_for "Entry implementation" do
116
116
  end
117
117
 
118
118
  it "should delete a list of entry ids" do
119
- Sunspot::IndexQueue::Entry.implementation.delete_entries([@entry_1.id, @entry_2.id])
119
+ Sunspot::IndexQueue::Entry.implementation.delete_entries([@entry_1, @entry_2])
120
120
  factory.find(@entry_1.id).should == nil
121
121
  factory.find(@entry_2.id).should == nil
122
122
  factory.find(@entry_4.id).id.should == @entry_4.id
@@ -3,6 +3,10 @@ require 'active_record'
3
3
 
4
4
  describe "Sunspot::IndexQueue integration tests" do
5
5
  before :all do
6
+ ActiveRecord::Base.establish_connection("adapter" => "sqlite3", "database" => ":memory:")
7
+ Sunspot::IndexQueue::Entry.implementation = :active_record
8
+ Sunspot::IndexQueue::Entry::ActiveRecordImpl.create_table
9
+
6
10
  data_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp'))
7
11
  FileUtils.rm_rf(data_dir) if File.exist?(data_dir)
8
12
  Dir.mkdir(data_dir)
@@ -10,7 +14,7 @@ describe "Sunspot::IndexQueue integration tests" do
10
14
  `sunspot-solr start --port=18983 --data-directory=. --pid-dir=. --log-file=./solr.log --max-memory=64m`
11
15
  raise "Failed to start Solr on port 18983" unless $? == 0
12
16
  # Wait until the server is responding
13
- ping_uri = URI.parse("http://127.0.0.1:18983/solr/ping")
17
+ ping_uri = URI.parse("http://127.0.0.1:18983/solr/admin/ping")
14
18
  solr_started = false
15
19
  100.times do
16
20
  begin
@@ -23,10 +27,6 @@ describe "Sunspot::IndexQueue integration tests" do
23
27
  end
24
28
  raise "Solr failed to start after 10 seconds" unless solr_started
25
29
  end
26
- ActiveRecord::Base.establish_connection("adapter" => "sqlite3", "database" => ":memory:")
27
- Sunspot::IndexQueue::Entry.implementation = :active_record
28
- Sunspot::IndexQueue::Entry::ActiveRecordImpl.create_table
29
-
30
30
  @solr_session = Sunspot::Session.new do |config|
31
31
  config.solr.url = 'http://127.0.0.1:18983/solr'
32
32
  end
@@ -40,7 +40,7 @@ describe "Sunspot::IndexQueue integration tests" do
40
40
  end
41
41
  FileUtils.rm_rf(data_dir)
42
42
  end
43
- Sunspot::IndexQueue::Entry::ActiveRecordImpl.connection.drop_table(Sunspot::IndexQueue::Entry::ActiveRecordImpl.table_name)
43
+ Sunspot::IndexQueue::Entry::ActiveRecordImpl.connection.drop_table(Sunspot::IndexQueue::Entry::ActiveRecordImpl.table_name) if Sunspot::IndexQueue::Entry::ActiveRecordImpl.table_exists?
44
44
  ActiveRecord::Base.connection.disconnect!
45
45
  Sunspot::IndexQueue::Entry.implementation = nil
46
46
  end
@@ -1,5 +1,9 @@
1
1
  require 'rubygems'
2
2
 
3
+ require 'uri'
4
+ require 'fileutils'
5
+ require 'net/http'
6
+
3
7
  if ENV["ACTIVE_RECORD_VERSION"]
4
8
  gem 'activerecord', ENV["ACTIVE_RECORD_VERSION"]
5
9
  else
@@ -41,7 +45,7 @@ module Sunspot
41
45
  Thread.current[:mock_db]
42
46
  end
43
47
 
44
- def save (*objects)
48
+ def save(*objects)
45
49
  objects.each do |obj|
46
50
  db[obj.id.to_s] = obj.dup
47
51
  end
@@ -50,21 +54,21 @@ module Sunspot
50
54
 
51
55
  attr_reader :id
52
56
  attr_accessor :value
53
- def initialize (id, value=nil)
57
+ def initialize(id, value=nil)
54
58
  @id = id
55
59
  @value = value
56
60
  end
57
61
 
58
- def == (value)
62
+ def ==(value)
59
63
  value.is_a?(self.class) && @id == value.id
60
64
  end
61
65
 
62
66
  class DataAccessor < Sunspot::Adapters::DataAccessor
63
- def load (id)
67
+ def load(id)
64
68
  Searchable.db ? Searchable.db[id.to_s] : Searchable.new(id)
65
69
  end
66
70
 
67
- def load_all (ids)
71
+ def load_all(ids)
68
72
  ids.collect{|id| load(id)}.compact
69
73
  end
70
74
  end
@@ -92,7 +96,7 @@ module Sunspot
92
96
 
93
97
  attr_reader :record_class_name, :record_id, :error, :attempts
94
98
 
95
- def initialize (options = {})
99
+ def initialize(options = {})
96
100
  if options[:record]
97
101
  @record_class_name = options[:record].class.name
98
102
  @record_id = options[:record].id
@@ -110,6 +114,10 @@ module Sunspot
110
114
  def id
111
115
  object_id
112
116
  end
117
+
118
+ def set_error!(message, retry_interval = nil)
119
+ @error = message
120
+ end
113
121
  end
114
122
  end
115
123
  end
@@ -1,67 +1,65 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sunspot_index_queue}
8
- s.version = "1.1.0"
8
+ s.version = "1.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Durand"]
12
- s.date = %q{2011-01-01}
12
+ s.date = %q{2011-04-08}
13
13
  s.description = %q{This gem provides asynchronous indexing to Solr for the sunspot gem. It uses a pluggable model for the backing queue and provides support for ActiveRecord and MongoDB out of the box.}
14
14
  s.email = %q{brian@embellishedvisions.com}
15
15
  s.extra_rdoc_files = [
16
16
  "README.rdoc"
17
17
  ]
18
18
  s.files = [
19
- ".gitignore",
20
- "CHANGE_LOG.txt",
21
- "MIT_LICENSE",
22
- "README.rdoc",
23
- "Rakefile",
24
- "VERSION",
25
- "lib/sunspot/index_queue.rb",
26
- "lib/sunspot/index_queue/batch.rb",
27
- "lib/sunspot/index_queue/entry.rb",
28
- "lib/sunspot/index_queue/entry/active_record_impl.rb",
29
- "lib/sunspot/index_queue/entry/data_mapper_impl.rb",
30
- "lib/sunspot/index_queue/entry/mongo_impl.rb",
31
- "lib/sunspot/index_queue/session_proxy.rb",
32
- "lib/sunspot_index_queue.rb",
33
- "spec/active_record_impl_spec.rb",
34
- "spec/batch_spec.rb",
35
- "spec/data_mapper_impl_spec.rb",
36
- "spec/entry_impl_examples.rb",
37
- "spec/entry_spec.rb",
38
- "spec/index_queue_spec.rb",
39
- "spec/integration_spec.rb",
40
- "spec/mongo_impl_spec.rb",
41
- "spec/session_proxy_spec.rb",
42
- "spec/spec_helper.rb",
43
- "sunspot_index_queue.gemspec"
19
+ "CHANGE_LOG.txt",
20
+ "MIT_LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lib/sunspot/index_queue.rb",
25
+ "lib/sunspot/index_queue/batch.rb",
26
+ "lib/sunspot/index_queue/entry.rb",
27
+ "lib/sunspot/index_queue/entry/active_record_impl.rb",
28
+ "lib/sunspot/index_queue/entry/data_mapper_impl.rb",
29
+ "lib/sunspot/index_queue/entry/mongo_impl.rb",
30
+ "lib/sunspot/index_queue/session_proxy.rb",
31
+ "lib/sunspot_index_queue.rb",
32
+ "spec/active_record_impl_spec.rb",
33
+ "spec/batch_spec.rb",
34
+ "spec/data_mapper_impl_spec.rb",
35
+ "spec/entry_impl_examples.rb",
36
+ "spec/entry_spec.rb",
37
+ "spec/index_queue_spec.rb",
38
+ "spec/integration_spec.rb",
39
+ "spec/mongo_impl_spec.rb",
40
+ "spec/session_proxy_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "sunspot_index_queue.gemspec"
44
43
  ]
45
44
  s.homepage = %q{http://github.com/bdurand/sunspot_index_queue}
46
- s.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc"]
45
+ s.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc", "MIT_LICENSE"]
47
46
  s.require_paths = ["lib"]
48
- s.rubygems_version = %q{1.3.7}
47
+ s.rubygems_version = %q{1.5.2}
49
48
  s.summary = %q{Asynchronous Solr indexing support for the sunspot gem with an emphasis on reliablity and throughput.}
50
49
  s.test_files = [
51
50
  "spec/active_record_impl_spec.rb",
52
- "spec/batch_spec.rb",
53
- "spec/data_mapper_impl_spec.rb",
54
- "spec/entry_impl_examples.rb",
55
- "spec/entry_spec.rb",
56
- "spec/index_queue_spec.rb",
57
- "spec/integration_spec.rb",
58
- "spec/mongo_impl_spec.rb",
59
- "spec/session_proxy_spec.rb",
60
- "spec/spec_helper.rb"
51
+ "spec/batch_spec.rb",
52
+ "spec/data_mapper_impl_spec.rb",
53
+ "spec/entry_impl_examples.rb",
54
+ "spec/entry_spec.rb",
55
+ "spec/index_queue_spec.rb",
56
+ "spec/integration_spec.rb",
57
+ "spec/mongo_impl_spec.rb",
58
+ "spec/session_proxy_spec.rb",
59
+ "spec/spec_helper.rb"
61
60
  ]
62
61
 
63
62
  if s.respond_to? :specification_version then
64
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
63
  s.specification_version = 3
66
64
 
67
65
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -72,7 +70,7 @@ Gem::Specification.new do |s|
72
70
  s.add_development_dependency(%q<dm-aggregates>, [">= 1.0.0"])
73
71
  s.add_development_dependency(%q<dm-migrations>, [">= 1.0.0"])
74
72
  s.add_development_dependency(%q<mongo>, [">= 0"])
75
- s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
73
+ s.add_development_dependency(%q<rspec>, [">= 2.0.0"])
76
74
  s.add_development_dependency(%q<jeweler>, [">= 0"])
77
75
  else
78
76
  s.add_dependency(%q<sunspot>, [">= 1.1.0"])
@@ -82,7 +80,7 @@ Gem::Specification.new do |s|
82
80
  s.add_dependency(%q<dm-aggregates>, [">= 1.0.0"])
83
81
  s.add_dependency(%q<dm-migrations>, [">= 1.0.0"])
84
82
  s.add_dependency(%q<mongo>, [">= 0"])
85
- s.add_dependency(%q<rspec>, [">= 1.3.0"])
83
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
86
84
  s.add_dependency(%q<jeweler>, [">= 0"])
87
85
  end
88
86
  else
@@ -93,7 +91,7 @@ Gem::Specification.new do |s|
93
91
  s.add_dependency(%q<dm-aggregates>, [">= 1.0.0"])
94
92
  s.add_dependency(%q<dm-migrations>, [">= 1.0.0"])
95
93
  s.add_dependency(%q<mongo>, [">= 0"])
96
- s.add_dependency(%q<rspec>, [">= 1.3.0"])
94
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
97
95
  s.add_dependency(%q<jeweler>, [">= 0"])
98
96
  end
99
97
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunspot_index_queue
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
- prerelease: false
4
+ hash: 17
5
+ prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 0
10
- version: 1.1.0
9
+ - 1
10
+ version: 1.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brian Durand
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-01 00:00:00 -06:00
18
+ date: 2011-04-08 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -133,12 +133,12 @@ dependencies:
133
133
  requirements:
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
- hash: 27
136
+ hash: 15
137
137
  segments:
138
- - 1
139
- - 3
138
+ - 2
139
+ - 0
140
140
  - 0
141
- version: 1.3.0
141
+ version: 2.0.0
142
142
  type: :development
143
143
  version_requirements: *id008
144
144
  - !ruby/object:Gem::Dependency
@@ -164,7 +164,6 @@ extensions: []
164
164
  extra_rdoc_files:
165
165
  - README.rdoc
166
166
  files:
167
- - .gitignore
168
167
  - CHANGE_LOG.txt
169
168
  - MIT_LICENSE
170
169
  - README.rdoc
@@ -198,6 +197,7 @@ rdoc_options:
198
197
  - --charset=UTF-8
199
198
  - --main
200
199
  - README.rdoc
200
+ - MIT_LICENSE
201
201
  require_paths:
202
202
  - lib
203
203
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -221,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
221
  requirements: []
222
222
 
223
223
  rubyforge_project:
224
- rubygems_version: 1.3.7
224
+ rubygems_version: 1.5.2
225
225
  signing_key:
226
226
  specification_version: 3
227
227
  summary: Asynchronous Solr indexing support for the sunspot gem with an emphasis on reliablity and throughput.
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- pkg
2
- rdoc
3
- tmp
4
- *.rbc