couchwatcher 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "json", "> 1.1.5"
4
+ gem "typhoeus", "~> 0.2"
5
+
6
+ group :development do
7
+ gem "shoulda", ">= 0"
8
+ gem "bundler", "~> 1.0.0"
9
+ gem "jeweler", "~> 1.6.2"
10
+ gem "rcov", ">= 0"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.2)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ json (1.5.2)
10
+ mime-types (1.16)
11
+ rake (0.8.7)
12
+ rcov (0.9.9)
13
+ shoulda (2.11.3)
14
+ typhoeus (0.2.4)
15
+ mime-types
16
+ mime-types
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ bundler (~> 1.0.0)
23
+ jeweler (~> 1.6.2)
24
+ json (> 1.1.5)
25
+ rcov
26
+ shoulda
27
+ typhoeus (~> 0.2)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Kevin Malakoff
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.rdoc ADDED
@@ -0,0 +1,31 @@
1
+ = couchwatcher
2
+
3
+ This gem provides a simple library to watch a couchdb database or specific documents and to receive notifications when they change.
4
+
5
+ Install:
6
+ gem install couchwatcher
7
+
8
+ Usage:
9
+
10
+ require 'couchwatcher'
11
+
12
+ couch_watcher = CouchWatcher::CouchWatcher.new()
13
+ couch_watcher.add_database_watcher('http://localhost:5984/my_database', true) {|url, id, rev, doc| puts("DATABASE: Changes detected at: #{url} id: #{id} and rev: #{rev}. \n For document: #{doc.inspect}")}
14
+ couch_watcher.add_document_watcher('http://localhost:5984/my_database', '_design/mydocument', true) {|url, id, rev, doc| puts("DOCUMENT: Changes detected at: #{url} id: #{id} and rev: #{rev}. \n For document: #{doc.inspect}")}
15
+ couch_watcher.start_watching()
16
+
17
+ == Contributing to couchwatcher
18
+
19
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
20
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
21
+ * Fork the project
22
+ * Start a feature/bugfix branch
23
+ * Commit and push until you are happy with your contribution
24
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
25
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
26
+
27
+ == Copyright
28
+
29
+ Copyright (c) 2011 Kevin Malakoff. See LICENSE.txt for
30
+ further details.
31
+
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "couchwatcher"
18
+ gem.homepage = "http://github.com/kmalakoff/couchwatcher-gem"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Couch document uploading utility}
21
+ gem.description = %Q{This gem provides a simple library to watch a couchdb database or specific documents and to receieve notifications when they change.}
22
+ gem.email = "xmann.intl@gmail.com"
23
+ gem.authors = ["Kevin Malakoff"]
24
+
25
+ gem.add_dependency 'json', '> 1.1.5'
26
+ gem.add_dependency 'typhoeus', '~>0.2'
27
+ gem.files.include 'lib/couchwatcher/database_listener.rb'
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ test.rcov_opts << '--exclude "gems/*"'
44
+ end
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "couchwatcher #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,72 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{couchwatcher}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kevin Malakoff"]
12
+ s.date = %q{2011-06-18}
13
+ s.description = %q{This gem provides a simple library to watch a couchdb database or specific documents and to receieve notifications when they change.}
14
+ s.email = %q{xmann.intl@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "couchwatcher.gemspec",
28
+ "lib/couchwatcher.rb",
29
+ "lib/couchwatcher/database_listener.rb",
30
+ "test/helper.rb",
31
+ "test/test_couchwatcher.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/kmalakoff/couchwatcher-gem}
34
+ s.licenses = ["MIT"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.5.2}
37
+ s.summary = %q{Couch document uploading utility}
38
+
39
+ if s.respond_to? :specification_version then
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<json>, ["> 1.1.5"])
44
+ s.add_runtime_dependency(%q<typhoeus>, ["~> 0.2"])
45
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
46
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
47
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
48
+ s.add_development_dependency(%q<rcov>, [">= 0"])
49
+ s.add_runtime_dependency(%q<json>, ["> 1.1.5"])
50
+ s.add_runtime_dependency(%q<typhoeus>, ["~> 0.2"])
51
+ else
52
+ s.add_dependency(%q<json>, ["> 1.1.5"])
53
+ s.add_dependency(%q<typhoeus>, ["~> 0.2"])
54
+ s.add_dependency(%q<shoulda>, [">= 0"])
55
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
57
+ s.add_dependency(%q<rcov>, [">= 0"])
58
+ s.add_dependency(%q<json>, ["> 1.1.5"])
59
+ s.add_dependency(%q<typhoeus>, ["~> 0.2"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<json>, ["> 1.1.5"])
63
+ s.add_dependency(%q<typhoeus>, ["~> 0.2"])
64
+ s.add_dependency(%q<shoulda>, [">= 0"])
65
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
67
+ s.add_dependency(%q<rcov>, [">= 0"])
68
+ s.add_dependency(%q<json>, ["> 1.1.5"])
69
+ s.add_dependency(%q<typhoeus>, ["~> 0.2"])
70
+ end
71
+ end
72
+
@@ -0,0 +1,160 @@
1
+ require 'json'
2
+
3
+ module CouchWatcher
4
+
5
+ class DatabaseListener
6
+
7
+ def initialize(database_url, polling_interval = 3)
8
+ @database_url = database_url
9
+ @polling_interval = polling_interval
10
+ @last_sequence = nil
11
+ @connection_stopped = false
12
+ @thread = nil
13
+ @DatabaseCallback = Struct.new(:callback_proc, :include_document)
14
+ @database_callbacks = []
15
+ @DocumentCallback = Struct.new(:document_id, :callback_proc, :include_document)
16
+ @document_callbacks = []
17
+
18
+ data = get_info()
19
+ if data.nil?
20
+ puts "Error: the database cannnot be watched: #{@database_url}. Check that the couchdb is running, the database exists, and you have adequate permissions."
21
+ @connection_stopped = true
22
+ return
23
+ end
24
+
25
+ @last_sequence = data["update_seq"]
26
+ end
27
+
28
+ def add_database_callback(callback_proc, include_document)
29
+ if !@thread.nil?
30
+ puts "Error: cannot add callbacks to a running listener"
31
+ return nil
32
+ end
33
+
34
+ database_callback = @DatabaseCallback.new(callback_proc, include_document)
35
+ @database_callbacks << database_callback
36
+ return database_callback
37
+ end
38
+
39
+ def remove_database_callback(database_callback)
40
+ if !@thread.nil?
41
+ puts "Error: cannot remove callbacks from a running listener"
42
+ return
43
+ end
44
+
45
+ @database_callbacks.delete(database_callback)
46
+ end
47
+
48
+ def add_document_callback(document_id, callback_proc, include_document)
49
+ if !@thread.nil?
50
+ puts "Error: cannot add callbacks to a running listener"
51
+ return nil
52
+ end
53
+
54
+ document_callback = @DocumentCallback.new(document_id, callback_proc, include_document)
55
+ @document_callbacks << document_callback
56
+ return document_callback
57
+ end
58
+
59
+ def remove_document_callback(document_callback)
60
+ if !@thread.nil?
61
+ puts "Error: cannot remove callbacks from a running listener"
62
+ return
63
+ end
64
+
65
+ @document_callbacks.delete(document_callback)
66
+ end
67
+
68
+ def start_watching
69
+ if !@thread.nil?
70
+ puts "Error: listener is already running listener"
71
+ return
72
+ end
73
+
74
+ puts "Database watching started for: #{@database_url}"
75
+ @thread = Thread.new do
76
+ while true
77
+
78
+ # stopped the connection
79
+ if @connection_stopped
80
+ return
81
+
82
+ # the sequence number has been returned so start polling
83
+ elsif @last_sequence
84
+ results = get_changes()
85
+
86
+ # print a message
87
+ # puts "Get changes heartbeat"
88
+ puts "Change detected in database: #{@database_url}" if (results && !results.empty?)
89
+
90
+ results.each do |result|
91
+
92
+ document_id = result["id"]
93
+ document_rev = result["changes"][0]["rev"]
94
+ document = result["doc"]
95
+
96
+ # call the database watchers
97
+ @database_callbacks.each do |callback|
98
+ if callback.include_document
99
+ callback.callback_proc.call(@database_url, document_id, document_rev, document)
100
+ else
101
+ callback.callback_proc.call(@database_url, document_id, document_rev)
102
+ end
103
+ end
104
+
105
+ # call the document watchers
106
+ @document_callbacks.each do |callback|
107
+ if (callback.document_id==document_id)
108
+ if callback.include_document
109
+ callback.callback_proc.call(@database_url, document_id, document_rev, document)
110
+ else
111
+ callback.callback_proc.call(@database_url, document_id, document_rev)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ sleep @polling_interval
119
+ end
120
+ end
121
+ end
122
+
123
+ def stop_watching
124
+ if @thread.nil?
125
+ puts "Error: listener is is not running"
126
+ return
127
+ end
128
+
129
+ # give the thread time to finish
130
+ @connection_stopped = true
131
+ sleep @polling_interval + 2
132
+
133
+ @thread.kill
134
+ @thread = nil
135
+ puts "Database watching stopped for: #{@database_url}"
136
+ end
137
+
138
+ private
139
+
140
+ def get_info
141
+ # puts "GET #{@database_url + "/"}"
142
+ response = Typhoeus::Request.get(@database_url + "/")
143
+ # puts "Response: #{response.code} #{JSON.parse(response.body).inspect}"
144
+ json = response.code == 200 ? JSON.parse(response.body) : nil
145
+ end
146
+
147
+ def get_changes
148
+ # puts "GET #{@database_url + "/_changes?since=#{@last_sequence}"}"
149
+ response = Typhoeus::Request.get(@database_url + "/_changes?since=#{@last_sequence}&include_docs=true")
150
+ # puts "Response: #{response.code} #{JSON.parse(response.body).inspect}"
151
+ json = response.code == 200 ? JSON.parse(response.body) : nil
152
+ if !json.nil?
153
+ @last_sequence = json["last_seq"]
154
+ return json["results"]
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+ end
@@ -0,0 +1,50 @@
1
+ require 'typhoeus'
2
+
3
+ require 'couchwatcher/database_listener'
4
+
5
+ module CouchWatcher
6
+
7
+ class CouchWatcher
8
+ def initialize()
9
+ @database_listeners = {}
10
+ end
11
+
12
+ def add_database_watcher(database_url, include_document, &callback_block)
13
+ database_listener(database_url).add_database_callback(callback_block, include_document)
14
+ end
15
+
16
+ def remove_database_watcher(callback_object)
17
+ database_listener(database_url).remove_database_callback(callback_object)
18
+ end
19
+
20
+ def add_document_watcher(database_url, document_id, include_document, &callback_block)
21
+ database_listener(database_url).add_document_callback(document_id, callback_block, include_document)
22
+ end
23
+
24
+ def remove_document_watcher(callback_object)
25
+ database_listener(database_url).remove_document_callback(callback_object)
26
+ end
27
+
28
+ def start_watching()
29
+ @database_listeners.each {|database_url, database_listener| database_listener.start_watching()}
30
+ end
31
+
32
+ def stop_watching()
33
+ @database_listeners.each {|database_url, database_listener| database_listener.stop_watching()}
34
+ end
35
+
36
+ private
37
+
38
+ def database_listener(database_url)
39
+ # not yet listening, create a new listener
40
+ database_listener = @database_listeners[database_url]
41
+ if database_listener.nil?
42
+ database_listener = ::CouchWatcher::DatabaseListener.new(database_url)
43
+ @database_listeners[database_url] = database_listener
44
+ end
45
+
46
+ return database_listener
47
+ end
48
+
49
+ end
50
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'couchwatcher'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestCouchwatcher < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: couchwatcher
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Kevin Malakoff
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-18 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">"
27
+ - !ruby/object:Gem::Version
28
+ hash: 25
29
+ segments:
30
+ - 1
31
+ - 1
32
+ - 5
33
+ version: 1.1.5
34
+ name: json
35
+ version_requirements: *id001
36
+ prerelease: false
37
+ - !ruby/object:Gem::Dependency
38
+ type: :runtime
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 0
47
+ - 2
48
+ version: "0.2"
49
+ name: typhoeus
50
+ version_requirements: *id002
51
+ prerelease: false
52
+ - !ruby/object:Gem::Dependency
53
+ type: :development
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ name: shoulda
64
+ version_requirements: *id003
65
+ prerelease: false
66
+ - !ruby/object:Gem::Dependency
67
+ type: :development
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 23
74
+ segments:
75
+ - 1
76
+ - 0
77
+ - 0
78
+ version: 1.0.0
79
+ name: bundler
80
+ version_requirements: *id004
81
+ prerelease: false
82
+ - !ruby/object:Gem::Dependency
83
+ type: :development
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 11
90
+ segments:
91
+ - 1
92
+ - 6
93
+ - 2
94
+ version: 1.6.2
95
+ name: jeweler
96
+ version_requirements: *id005
97
+ prerelease: false
98
+ - !ruby/object:Gem::Dependency
99
+ type: :development
100
+ requirement: &id006 !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ name: rcov
110
+ version_requirements: *id006
111
+ prerelease: false
112
+ - !ruby/object:Gem::Dependency
113
+ type: :runtime
114
+ requirement: &id007 !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">"
118
+ - !ruby/object:Gem::Version
119
+ hash: 25
120
+ segments:
121
+ - 1
122
+ - 1
123
+ - 5
124
+ version: 1.1.5
125
+ name: json
126
+ version_requirements: *id007
127
+ prerelease: false
128
+ - !ruby/object:Gem::Dependency
129
+ type: :runtime
130
+ requirement: &id008 !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ~>
134
+ - !ruby/object:Gem::Version
135
+ hash: 15
136
+ segments:
137
+ - 0
138
+ - 2
139
+ version: "0.2"
140
+ name: typhoeus
141
+ version_requirements: *id008
142
+ prerelease: false
143
+ description: This gem provides a simple library to watch a couchdb database or specific documents and to receieve notifications when they change.
144
+ email: xmann.intl@gmail.com
145
+ executables: []
146
+
147
+ extensions: []
148
+
149
+ extra_rdoc_files:
150
+ - LICENSE.txt
151
+ - README.rdoc
152
+ files:
153
+ - .document
154
+ - Gemfile
155
+ - Gemfile.lock
156
+ - LICENSE.txt
157
+ - README.rdoc
158
+ - Rakefile
159
+ - VERSION
160
+ - couchwatcher.gemspec
161
+ - lib/couchwatcher.rb
162
+ - lib/couchwatcher/database_listener.rb
163
+ - test/helper.rb
164
+ - test/test_couchwatcher.rb
165
+ has_rdoc: true
166
+ homepage: http://github.com/kmalakoff/couchwatcher-gem
167
+ licenses:
168
+ - MIT
169
+ post_install_message:
170
+ rdoc_options: []
171
+
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ none: false
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ hash: 3
180
+ segments:
181
+ - 0
182
+ version: "0"
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ hash: 3
189
+ segments:
190
+ - 0
191
+ version: "0"
192
+ requirements: []
193
+
194
+ rubyforge_project:
195
+ rubygems_version: 1.5.2
196
+ signing_key:
197
+ specification_version: 3
198
+ summary: Couch document uploading utility
199
+ test_files: []
200
+