sunspot-queue 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Chris Gaffney
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # sunspot-queue
2
+
3
+ Background search indexing using existing worker systems.
4
+
5
+ ## Install
6
+
7
+ $ gem install sunspot-queue
8
+
9
+ ## Note on Patches/Pull Requests
10
+
11
+ * Fork the project.
12
+ * Add tests to show the problem or test your feature
13
+ * Make your feature addition or bug fix.
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ Please don't make changes to the Rakefile, version, or history.
17
+
18
+ ## Development
19
+
20
+ $ gem install bundler (if you don't have it)
21
+ $ bundle install
22
+ $ guard
23
+
24
+ ## Copyright
25
+
26
+ See LICENSE for details.
@@ -0,0 +1 @@
1
+ require "sunspot/queue"
@@ -0,0 +1,12 @@
1
+ module Sunspot
2
+ module Queue
3
+ class Error < StandardError; end
4
+ class NotPersistedError < Error; end
5
+
6
+ VERSION = "0.9.0"
7
+ end
8
+ end
9
+
10
+ require "sunspot/queue/session_proxy"
11
+ require "sunspot/queue/index_job"
12
+ require "sunspot/queue/removal_job"
@@ -0,0 +1,23 @@
1
+ require "sunspot/queue/session_proxy"
2
+
3
+ module Sunspot::Queue
4
+ module Helpers
5
+ def without_proxy
6
+ # Pop off the queueing proxy for the block if it's in place so we don't
7
+ # requeue the same job multiple times.
8
+ if Sunspot.session.instance_of?(SessionProxy)
9
+ proxy = Sunspot.session
10
+ Sunspot.session = proxy.session
11
+
12
+ begin
13
+ yield
14
+ ensure
15
+ Sunspot.commit rescue nil
16
+ Sunspot.session = proxy
17
+ end
18
+ else
19
+ yield
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require "sunspot/queue/helpers"
2
+
3
+ module Sunspot::Queue
4
+ class IndexJob
5
+ extend Helpers
6
+
7
+ def self.queue
8
+ :sunspot
9
+ end
10
+
11
+ def self.perform(klass, id)
12
+ without_proxy do
13
+ record = ::Resque.constantize(klass).find(id)
14
+
15
+ Sunspot.index!(record)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ require "sunspot/queue/helpers"
2
+
3
+ module Sunspot::Queue
4
+ class RemovalJob
5
+ extend Helpers
6
+
7
+ def self.queue
8
+ :sunspot
9
+ end
10
+
11
+ def self.perform(klass, id)
12
+ without_proxy do
13
+ ::Sunspot.remove_by_id(klass, id)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,110 @@
1
+ require "resque"
2
+ require "sunspot/queue/index_job"
3
+ require "sunspot/queue/removal_job"
4
+
5
+ module Sunspot::Queue
6
+ class SessionProxy
7
+ attr_reader :session
8
+
9
+ def initialize(session)
10
+ @session = session
11
+ end
12
+
13
+ def index(*objects)
14
+ objects.flatten!
15
+
16
+ if !objects.all?(&:persisted?)
17
+ raise NotPersistedError.new("Cannot index records that have not been saved")
18
+ end
19
+
20
+ objects.each do |object|
21
+ ::Resque.enqueue(IndexJob, object.class.name, object.id)
22
+ end
23
+ end
24
+ alias :index! :index
25
+
26
+ def remove(*objects, &block)
27
+ if block
28
+ # Delete by query not supported by queue, so send to server
29
+ session.remove(*objects, &block)
30
+ else
31
+ objects.flatten.each do |object|
32
+ # Remove cannot remove an object from Solr if it doesn't have an id.
33
+ # We're assuming if it doesn't have an id then it hasn't been
34
+ # persisted and can safely be ignored since it shouldn't exist in the
35
+ # search index.
36
+ if object.id
37
+ ::Resque.enqueue(RemovalJob, object.class.name, object.id)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def remove!(*objects, &block)
44
+ if block
45
+ # Delete by query not supported by queue, so send to server
46
+ session.remove!(*objects, &block)
47
+ else
48
+ remove(*objects)
49
+ end
50
+ end
51
+
52
+ # Enqueues a removal job based on class and id.
53
+ def remove_by_id(klass, id)
54
+ ::Resque.enqueue(RemovalJob, klass.to_s, id)
55
+ end
56
+ alias :remove_by_id! :remove_by_id
57
+
58
+ # The following are all delegated to session
59
+ def new_search(*args, &block)
60
+ session.new_search(*args, &block)
61
+ end
62
+
63
+ def search(*args, &block)
64
+ session.search(*args, &block)
65
+ end
66
+
67
+ def new_more_like_this(*args, &block)
68
+ session.new_more_like_this(*args, &block)
69
+ end
70
+
71
+ def more_like_this(*args, &block)
72
+ session.more_like_this(*args, &block)
73
+ end
74
+
75
+ def config
76
+ session.config
77
+ end
78
+
79
+ def remove_all(*args)
80
+ session.remove_all(*args)
81
+ end
82
+
83
+ def remove_all!(*args)
84
+ session.remove_all(*args)
85
+ end
86
+
87
+ # All of the following are are here to match API but don't make sense to be
88
+ # implemented
89
+ def batch
90
+ yield if block_given?
91
+ end
92
+
93
+ def commit
94
+ end
95
+
96
+ def commit_if_delete_dirty
97
+ end
98
+
99
+ def commit_if_dirty
100
+ end
101
+
102
+ def delete_dirty?
103
+ false
104
+ end
105
+
106
+ def dirty?
107
+ false
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe Sunspot::Queue::IndexJob do
4
+ context "ActiveRecord" do
5
+ it "indexes an ActiveRecord model" do
6
+ # This will queue a job but we'll just ignore it to isolate the job
7
+ person = Person.create(:name => "The Grandson")
8
+
9
+ expect do
10
+ Sunspot::Queue::IndexJob.perform(Person, person.id)
11
+ end.to change { Person.search.hits.size }.by(1)
12
+
13
+ results = Person.search { fulltext "grandson" }.results
14
+ results.first.should == person
15
+ end
16
+
17
+ it "raises an error if the record could not be found" do
18
+ expect do
19
+ Sunspot::Queue::IndexJob.perform(Person, 404)
20
+ end.to raise_error(ActiveRecord::RecordNotFound)
21
+ end
22
+
23
+ it "maintains the existing proxy if there was an error" do
24
+ expect do
25
+ Sunspot::Queue::IndexJob.perform(Person, 404) rescue nil
26
+ end.to_not change { Sunspot.session }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+ require "sunspot/queue/helpers"
3
+
4
+ describe "Queued Indexing" do
5
+ include Sunspot::Queue::Helpers
6
+
7
+ # Given an active record model
8
+ # When I save the model
9
+ # Then the record should not be in solr
10
+ #
11
+ # When resque jobs are run
12
+ # Then the record should be in solr
13
+ it "queues indexing of ActiveRecord models using resque" do
14
+ person = Person.create(:name => "Inigo Montoya")
15
+ Person.search.results.should be_empty
16
+
17
+ expect do
18
+ ResqueSpec.perform_all(:sunspot)
19
+ end.to change { Person.search.hits.size }.by(1)
20
+
21
+ results = Person.search { fulltext "Montoya" }.results
22
+ results.first.should == person
23
+ end
24
+
25
+ # Given an active record model that is in Solr
26
+ # When I destroy the model
27
+ # Then the record should be in solr
28
+ #
29
+ # When resque jobs are run
30
+ # Then the record should not be in solr
31
+ it "queues removal of ActiveRecord models using Resque" do
32
+ person = Person.create(:name => "Fezzik")
33
+ ResqueSpec.perform_all(:sunspot)
34
+ Person.search.hits.size.should == 1
35
+
36
+ expect do
37
+ person.destroy
38
+ end.to_not change { Person.search.hits.size }
39
+
40
+ expect do
41
+ ResqueSpec.perform_all(:sunspot)
42
+ end.to change { Person.search.hits.size }.by(-1)
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+ require "sunspot/queue/helpers"
3
+
4
+ describe Sunspot::Queue::IndexJob do
5
+ include Sunspot::Queue::Helpers
6
+
7
+ it "removes a job from the search index" do
8
+ person =
9
+ without_proxy do
10
+ Person.create(:name => "The Albino")
11
+ end
12
+
13
+ Person.search.hits.size.should == 1
14
+
15
+ expect do
16
+ Sunspot::Queue::RemovalJob.perform(Person, person.id)
17
+ end.to change { Person.search.hits.size }.by(-1)
18
+ end
19
+ end
@@ -0,0 +1,116 @@
1
+ require "spec_helper"
2
+
3
+ describe Sunspot::Queue::SessionProxy do
4
+ let(:proxy) { Sunspot::Queue::SessionProxy.new(mock) }
5
+
6
+ context "#index" do
7
+ it "enqueues a single job for each class" do
8
+ people = 5.times.map do |i|
9
+ Person.create(:name => "#{i} of 5")
10
+ end
11
+
12
+ Resque.should_receive(:enqueue).with do |job, klass, id|
13
+ job.should == ::Sunspot::Queue::IndexJob
14
+ klass.should == "Person"
15
+ end.exactly(5).times
16
+
17
+ proxy.index(people)
18
+ end
19
+
20
+ it "handles an array of objects given to index" do
21
+ people = 2.times.map do |i|
22
+ Person.create(:name => "Clone ##{i}")
23
+ end
24
+
25
+ Resque.should_receive(:enqueue).exactly(2).times
26
+
27
+ proxy.index(people)
28
+ end
29
+
30
+ it "handles a single object being enqueued" do
31
+ person = Person.create(:name => "Buttercup")
32
+
33
+ Resque.should_receive(:enqueue).with do |_,_,id|
34
+ id.should == person.id
35
+ end
36
+
37
+ proxy.index(person)
38
+ end
39
+
40
+ it "raises an error if object is not persisted" do
41
+ person = Person.new(:name => "Vizzini")
42
+
43
+ expect do
44
+ proxy.index(person)
45
+ end.to raise_error(Sunspot::Queue::NotPersistedError)
46
+ end
47
+
48
+ it "raises an error if one of a set is not persisted" do
49
+ people = 5.times.map do |i|
50
+ Person.new(:name => "Minion ##{i}")
51
+ end
52
+
53
+ # This will leave 1 person not saved to the database
54
+ 4.times { |i| people[i].save }
55
+
56
+ expect do
57
+ proxy.index(people)
58
+ end.to raise_error(Sunspot::Queue::NotPersistedError)
59
+ end
60
+
61
+ it "does not process any records if one is not persisted" do
62
+ people = 2.times.map { |i| Person.new(:name => i) }
63
+ people.first.save
64
+
65
+ Resque.should_not_receive(:enqueue)
66
+ expect do
67
+ proxy.index(people)
68
+ end.to raise_error(Sunspot::Queue::NotPersistedError)
69
+ end
70
+ end
71
+
72
+ context "#remove" do
73
+ it "enqueues a single job for each class" do
74
+ people = 5.times.map do |i|
75
+ Person.create(:name => "#{i} of 5")
76
+ end
77
+
78
+ Resque.should_receive(:enqueue).with do |job, klass, id|
79
+ job.should == ::Sunspot::Queue::RemovalJob
80
+ klass.should == "Person"
81
+ end.exactly(5).times
82
+
83
+ proxy.remove(people)
84
+ end
85
+
86
+ it "handles an array of objects" do
87
+ people = 2.times.map do |i|
88
+ Person.create(:name => "Clone ##{i}")
89
+ end
90
+
91
+ Resque.should_receive(:enqueue).exactly(2).times
92
+
93
+ proxy.remove(people)
94
+ end
95
+
96
+ it "handles a single object" do
97
+ person = Person.create(:name => "Buttercup")
98
+
99
+ Resque.should_receive(:enqueue).with do |job, _, id|
100
+ job.should == ::Sunspot::Queue::RemovalJob
101
+ id.should == person.id
102
+ end
103
+
104
+ proxy.remove(person)
105
+ end
106
+
107
+ it "silently ignores records that do not have an id" do
108
+ people = 2.times.map { |i| Person.new(:name => "Thing #{i}") }
109
+ people.first.save
110
+
111
+ Resque.should_receive(:enqueue).once
112
+
113
+ proxy.remove(people)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,72 @@
1
+ require "sunspot/queue"
2
+
3
+ require "socket"
4
+ require "active_record"
5
+ require "sunspot"
6
+ require "sunspot/rails"
7
+ require "sunspot/solr/server"
8
+ require "resque_spec"
9
+
10
+ # Configure ActiveRecord and Sunspot to work with ActiveRecord
11
+ ActiveRecord::Base.establish_connection(
12
+ :adapter => "sqlite3",
13
+ :database => ":memory:"
14
+ )
15
+
16
+ ActiveRecord::Schema.define do
17
+ create_table :people, :force => true do |t|
18
+ t.string :name
19
+ end
20
+ end
21
+
22
+ # Pulled from the sunspot_rails Rails 3 railtie
23
+ Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveRecordInstanceAdapter, ActiveRecord::Base)
24
+ Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveRecordDataAccessor, ActiveRecord::Base)
25
+
26
+ class Person < ActiveRecord::Base
27
+ include Sunspot::Rails::Searchable
28
+
29
+ searchable do
30
+ text :name
31
+ end
32
+ end
33
+
34
+ RSpec.configure do |config|
35
+ config.treat_symbols_as_metadata_keys_with_true_values = true
36
+ config.run_all_when_everything_filtered = true
37
+ config.filter_run :focus
38
+
39
+ # Original sunspot session not wrapped in our proxy object
40
+ session = Sunspot.session
41
+
42
+ config.before(:each) do
43
+ session.remove_all!
44
+ ResqueSpec.reset!
45
+
46
+ Sunspot.session = Sunspot::Queue::SessionProxy.new(session)
47
+ end
48
+
49
+ # Configure Solr and run a server in the background for the duration of the
50
+ # tests.
51
+ config.before(:suite) do
52
+ solr_pid = fork do
53
+ STDERR.reopen("/dev/null")
54
+ STDOUT.reopen("/dev/null")
55
+
56
+ server = Sunspot::Solr::Server.new
57
+ server.run
58
+ end
59
+
60
+ at_exit { Process.kill("TERM", solr_pid) }
61
+
62
+ # Wait for up to 5 seconds for Solr to boot
63
+ 10.times do
64
+ begin
65
+ TCPSocket.new("localhost", 8983)
66
+ break
67
+ rescue
68
+ sleep 0.5
69
+ end
70
+ end
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sunspot-queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chris Gaffney
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: resque
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sunspot_rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.3.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.3.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.10.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.10.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: resque_spec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: sunspot_solr
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: sqlite3
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
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'
126
+ - !ruby/object:Gem::Dependency
127
+ name: activerecord
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: guard
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: guard-rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: guard-bundler
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description:
191
+ email:
192
+ - gaffneyc@gmail.com
193
+ executables: []
194
+ extensions: []
195
+ extra_rdoc_files: []
196
+ files:
197
+ - lib/sunspot/queue/helpers.rb
198
+ - lib/sunspot/queue/index_job.rb
199
+ - lib/sunspot/queue/removal_job.rb
200
+ - lib/sunspot/queue/session_proxy.rb
201
+ - lib/sunspot/queue.rb
202
+ - lib/sunspot-queue.rb
203
+ - spec/index_job_spec.rb
204
+ - spec/integration/indexing_spec.rb
205
+ - spec/removal_job_spec.rb
206
+ - spec/session_proxy_spec.rb
207
+ - spec/spec_helper.rb
208
+ - LICENSE
209
+ - README.md
210
+ homepage: https://github.com/gaffneyc/sunspot-queue
211
+ licenses: []
212
+ post_install_message:
213
+ rdoc_options: []
214
+ require_paths:
215
+ - lib
216
+ required_ruby_version: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ segments:
223
+ - 0
224
+ hash: -3459963560005130599
225
+ required_rubygems_version: !ruby/object:Gem::Requirement
226
+ none: false
227
+ requirements:
228
+ - - ! '>='
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ segments:
232
+ - 0
233
+ hash: -3459963560005130599
234
+ requirements: []
235
+ rubyforge_project:
236
+ rubygems_version: 1.8.24
237
+ signing_key:
238
+ specification_version: 3
239
+ summary: Background search indexing using existing worker systems.
240
+ test_files: []