couch-client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/LICENSE +22 -0
- data/Manifest +33 -0
- data/README.markdown +176 -0
- data/Rakefile +17 -0
- data/TODO +11 -0
- data/couch-client.gemspec +33 -0
- data/lib/couch-client.rb +50 -0
- data/lib/couch-client/attachment.rb +32 -0
- data/lib/couch-client/attachment_list.rb +10 -0
- data/lib/couch-client/collection.rb +20 -0
- data/lib/couch-client/connection.rb +104 -0
- data/lib/couch-client/connection_handler.rb +70 -0
- data/lib/couch-client/consistent_hash.rb +98 -0
- data/lib/couch-client/database.rb +39 -0
- data/lib/couch-client/design.rb +79 -0
- data/lib/couch-client/document.rb +151 -0
- data/lib/couch-client/hookup.rb +107 -0
- data/lib/couch-client/row.rb +10 -0
- data/spec/attachment_list_spec.rb +7 -0
- data/spec/attachment_spec.rb +57 -0
- data/spec/collection_spec.rb +43 -0
- data/spec/conection_handler_spec.rb +66 -0
- data/spec/connection_spec.rb +93 -0
- data/spec/consistent_hash_spec.rb +171 -0
- data/spec/couch-client_spec.rb +11 -0
- data/spec/database_spec.rb +44 -0
- data/spec/design_spec.rb +100 -0
- data/spec/document_spec.rb +196 -0
- data/spec/files/image.png +0 -0
- data/spec/files/plain.txt +1 -0
- data/spec/hookup_spec.rb +122 -0
- data/spec/row_spec.rb +35 -0
- data/spec/spec_helper.rb +11 -0
- metadata +130 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.0.1. first release
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2010 Robert Sosinski (http://www.robertsosinski.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
LICENSE
|
3
|
+
Manifest
|
4
|
+
README.markdown
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
lib/couch-client.rb
|
8
|
+
lib/couch-client/attachment.rb
|
9
|
+
lib/couch-client/attachment_list.rb
|
10
|
+
lib/couch-client/collection.rb
|
11
|
+
lib/couch-client/connection.rb
|
12
|
+
lib/couch-client/connection_handler.rb
|
13
|
+
lib/couch-client/consistent_hash.rb
|
14
|
+
lib/couch-client/database.rb
|
15
|
+
lib/couch-client/design.rb
|
16
|
+
lib/couch-client/document.rb
|
17
|
+
lib/couch-client/hookup.rb
|
18
|
+
lib/couch-client/row.rb
|
19
|
+
spec/attachment_list_spec.rb
|
20
|
+
spec/attachment_spec.rb
|
21
|
+
spec/collection_spec.rb
|
22
|
+
spec/conection_handler_spec.rb
|
23
|
+
spec/connection_spec.rb
|
24
|
+
spec/consistent_hash_spec.rb
|
25
|
+
spec/couch-client_spec.rb
|
26
|
+
spec/database_spec.rb
|
27
|
+
spec/design_spec.rb
|
28
|
+
spec/document_spec.rb
|
29
|
+
spec/files/image.png
|
30
|
+
spec/files/plain.txt
|
31
|
+
spec/hookup_spec.rb
|
32
|
+
spec/row_spec.rb
|
33
|
+
spec/spec_helper.rb
|
data/README.markdown
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
Introduction
|
2
|
+
============
|
3
|
+
|
4
|
+
CouchClient is Ruby library that can be used to interact with CouchDB. The goal of CouchClient is to make documents feel as much as possible as what they already represent, a Hash of primitives, Arrays and other Hashes. As such, the interface for documents closely represents that of Hash and Array, but also includes additional methods and state in order to manage documents and interface with the CouchDB Server.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
gem install couch-client
|
10
|
+
|
11
|
+
# In your ruby application
|
12
|
+
require 'couch-client'
|
13
|
+
|
14
|
+
Connecting to CouchDB
|
15
|
+
---------------------
|
16
|
+
|
17
|
+
# Using hash syntax
|
18
|
+
Couch = CouchClient::Conection.new({
|
19
|
+
:scheme => "http",
|
20
|
+
:host => "localhost",
|
21
|
+
:port => 5984,
|
22
|
+
:database => "sandbox",
|
23
|
+
:username => "admin",
|
24
|
+
:password => "nimda"
|
25
|
+
})
|
26
|
+
|
27
|
+
# Using block syntax
|
28
|
+
Couch = CouchClient::Connection.new do |c|
|
29
|
+
c.scheme = "http"
|
30
|
+
c.host = "localhost"
|
31
|
+
c.port = 5984
|
32
|
+
c.database = "sandbox"
|
33
|
+
c.username = "admin"
|
34
|
+
c.password = "nimda"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Using both hash and block syntax
|
38
|
+
# NOTE: Hash parameters take precedence over block parameters
|
39
|
+
Couch = CouchClient::Connection.new(:username => "admin", :password => "nidma") do |c|
|
40
|
+
c.scheme = "http"
|
41
|
+
c.host = "localhost"
|
42
|
+
c.port = 5984
|
43
|
+
c.database = "sandbox"
|
44
|
+
end
|
45
|
+
|
46
|
+
Fetching a Document
|
47
|
+
-------------------
|
48
|
+
|
49
|
+
# Using Hash syntax
|
50
|
+
person = Couch["a3b556796203eab59c31fa21b00043e3"]
|
51
|
+
|
52
|
+
# You can also pass options if desired
|
53
|
+
person = Couch["a3b556796203eab59c31fa21b00043e3", :include_docs => true]
|
54
|
+
|
55
|
+
Getting a Document's id, rev and attachments
|
56
|
+
--------------------------------------------
|
57
|
+
# A document's id
|
58
|
+
person.id # => "a3b556796203eab59c31fa21b00043e3"
|
59
|
+
|
60
|
+
# A document's rev
|
61
|
+
person.rev # => "1-6665e6330ba75e757ce1f6d793305d67"
|
62
|
+
|
63
|
+
# A document's attachments
|
64
|
+
# NOTE: This will be an CouchClient::AttachmentList, and attachments will be CouchClient::Attachment objects
|
65
|
+
person.attachments # => {"plain.txt"=>{"content_type"=>"text/plain", "revpos"=>2, "length"=>406, "stub"=>true}}
|
66
|
+
|
67
|
+
|
68
|
+
Working with a Document
|
69
|
+
-----------------------
|
70
|
+
|
71
|
+
# Building new documents
|
72
|
+
# Couch.build({:name => "alice"})
|
73
|
+
|
74
|
+
# Getting and setting fields (with indifferent access and value "stringification")
|
75
|
+
# person[:name] # => "alice"
|
76
|
+
# person[:city] = :nyc
|
77
|
+
# person["city"] # => "nyc"
|
78
|
+
|
79
|
+
# Fetching the same document on the server
|
80
|
+
person.saved_doc # => {"_id" => "7f22af967b04d1b88212d3d26b017ff6", "_rev" => "1-f867d6b9aa0a5c31d647d57110fa7d36", "name" => "alice"}
|
81
|
+
|
82
|
+
# Saving a document
|
83
|
+
person.save # => true
|
84
|
+
person.rev # => "2-1734c07abaf18db573706bc1db59e09d"
|
85
|
+
|
86
|
+
# Deleting a field
|
87
|
+
person.delete(:city) # => true
|
88
|
+
person[:city] # => nil
|
89
|
+
|
90
|
+
# Attaching a file (documents must be refreshed for attachments to be available)
|
91
|
+
person.attach("plain.txt", "Hello World", "plain/text") # => true
|
92
|
+
person = person.saved_doc
|
93
|
+
person.attachments # => {"plain.txt"=>{"content_type"=>"text/plain", "revpos"=>2, "length"=>406, "stub"=>true}}
|
94
|
+
|
95
|
+
# Getting attached files
|
96
|
+
attachment = person.attachments["plain.txt"]
|
97
|
+
attachment.uri # => http://localhost:5984/sandbox/7f22af967b04d1b88212d3d26b017ff6/plain.txt
|
98
|
+
attachment.path # => /sandbox/7f22af967b04d1b88212d3d26b017ff6/plain.txt
|
99
|
+
attachment.data # => "Hello World"
|
100
|
+
|
101
|
+
# Deleting a document
|
102
|
+
person.delete!
|
103
|
+
person.deleted? # => true
|
104
|
+
|
105
|
+
# Identifying a design document
|
106
|
+
person.design? # => false
|
107
|
+
c["_design/people"].design? # => true
|
108
|
+
|
109
|
+
# Identifying errors and conflicts
|
110
|
+
person.error # => {"conflict"=>"Document update conflict."}
|
111
|
+
person.error? # => true
|
112
|
+
person.conflict? # => true
|
113
|
+
person.invalid? # => false
|
114
|
+
|
115
|
+
Working with Collections
|
116
|
+
------------------------
|
117
|
+
|
118
|
+
# Getting all documents
|
119
|
+
Couch.all_docs # => [{"id"=>"7f22af967b04d1b88212d3d26b017ff6", "key"=>"7f22af967b04d1b88212d3d26b017ff6", "value"=>{"rev"=>"1-f867d6b9aa0a5c31d647d57110fa7d36"}},
|
120
|
+
# {"id"=>"7f22af967b04d1b88212d3d26b018e89", "key"=>"7f22af967b04d1b88212d3d26b018e89", "value"=>{"rev"=>"3-3a635c1a2b5a8ff94bb5d63eee3cd6ef"}}]
|
121
|
+
|
122
|
+
# Getting all documents with document fields
|
123
|
+
Couch.all_docs(:include_docs => true)
|
124
|
+
|
125
|
+
# Specifying a `key`, `start_key` or `end_key`
|
126
|
+
couch.all_docs(:key => "7f22af967b04d1b88212d3d26b018e89")
|
127
|
+
couch.all_docs(:start_key => 200)
|
128
|
+
couch.all_docs(:end_key => [2010, 01, 01])
|
129
|
+
|
130
|
+
# Getting additional collection information
|
131
|
+
Couch.all_docs.info # => {"total_rows" => 2, "offset" => 0}
|
132
|
+
|
133
|
+
Using Design Documents
|
134
|
+
----------------------
|
135
|
+
|
136
|
+
# Map Views
|
137
|
+
Couch.design(:people).view(:all) # => [{"id"=>"7f22af967b04d1b88212d3d26b017ff6", "key"=>"7f22af967b04d1b88212d3d26b017ff6", "value"=>{"name" => "alice"}},
|
138
|
+
# {"id"=>"7f22af967b04d1b88212d3d26b018e89", "key"=>"7f22af967b04d1b88212d3d26b018e89", "value"=>{"name" => "bob"}}]
|
139
|
+
|
140
|
+
# MapReduce Views
|
141
|
+
Couch.design(:people).view(:sum) # => [{"key" => "male", "value" => 1}, {"key" => "female", "value" => 1}]
|
142
|
+
|
143
|
+
Using FullText Searching (Must Have CouchDB-Lucene Installed)
|
144
|
+
-------------------------------------------------------------
|
145
|
+
|
146
|
+
# Getting search results
|
147
|
+
Couch.design(:people).fulltext(:by_name, :q => "alice") # => [{"id"=>"a6c92090bbee241e892be1ac4464b9d9", "score"=>4.505526065826416, "fields"=>{"default"=>"alice"}}]
|
148
|
+
|
149
|
+
# Getting additional search results information
|
150
|
+
Couch.design(:people).fulltext(:by_name, :q => "alice").info # => {"q"=>"default:alice", "etag"=>"11e1541e20d9b860", "skip"=>0, "limit"=>25,
|
151
|
+
# "total_rows"=>7, "search_duration"=>0, "fetch_duration"=>1}
|
152
|
+
|
153
|
+
# Getting search index information
|
154
|
+
Couch.design(:people).fulltext(:by_name) # => {"current"=>true, "disk_size"=>3759, "doc_count"=>25, "doc_del_count"=>3, "fields"=>["default"],
|
155
|
+
# "last_modified"=>"1288403429000", "optimized"=>false, "ref_count"=>2}
|
156
|
+
|
157
|
+
Database Administration
|
158
|
+
-----------------------
|
159
|
+
|
160
|
+
# Create a database
|
161
|
+
Couch.database.create
|
162
|
+
|
163
|
+
# See if a database exists
|
164
|
+
Couch.database.exists
|
165
|
+
|
166
|
+
# Get database stats
|
167
|
+
Couch.database.stats
|
168
|
+
|
169
|
+
# Compact the database
|
170
|
+
Couch.database.compact!
|
171
|
+
|
172
|
+
# Delete the database
|
173
|
+
Couch.databse.delete!
|
174
|
+
|
175
|
+
|
176
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
desc "Run all specs"
|
6
|
+
RSpec::Core::RakeTask.new do |t|
|
7
|
+
t.rspec_opts = %w[--colour --format progress]
|
8
|
+
end
|
9
|
+
|
10
|
+
Echoe.new("couch-client") do |p|
|
11
|
+
p.author = "Robert Sosinski"
|
12
|
+
p.email = "email@robertsosinski.com"
|
13
|
+
p.url = "http://github.com/robertsosinski/couch-client"
|
14
|
+
p.description = "CouchClient is Ruby library that can be used to interact with CouchDB"
|
15
|
+
p.summary = "The goal of CouchClient is to make documents feel as much as possible as what they already represent, a Hash of primitives, Arrays and other Hashes."
|
16
|
+
p.runtime_dependencies = ["json"]
|
17
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{couch-client}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Robert Sosinski"]
|
9
|
+
s.date = %q{2010-10-29}
|
10
|
+
s.description = %q{CouchClient is Ruby library that can be used to interact with CouchDB}
|
11
|
+
s.email = %q{email@robertsosinski.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.markdown", "TODO", "lib/couch-client.rb", "lib/couch-client/attachment.rb", "lib/couch-client/attachment_list.rb", "lib/couch-client/collection.rb", "lib/couch-client/connection.rb", "lib/couch-client/connection_handler.rb", "lib/couch-client/consistent_hash.rb", "lib/couch-client/database.rb", "lib/couch-client/design.rb", "lib/couch-client/document.rb", "lib/couch-client/hookup.rb", "lib/couch-client/row.rb"]
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.markdown", "Rakefile", "TODO", "lib/couch-client.rb", "lib/couch-client/attachment.rb", "lib/couch-client/attachment_list.rb", "lib/couch-client/collection.rb", "lib/couch-client/connection.rb", "lib/couch-client/connection_handler.rb", "lib/couch-client/consistent_hash.rb", "lib/couch-client/database.rb", "lib/couch-client/design.rb", "lib/couch-client/document.rb", "lib/couch-client/hookup.rb", "lib/couch-client/row.rb", "spec/attachment_list_spec.rb", "spec/attachment_spec.rb", "spec/collection_spec.rb", "spec/conection_handler_spec.rb", "spec/connection_spec.rb", "spec/consistent_hash_spec.rb", "spec/couch-client_spec.rb", "spec/database_spec.rb", "spec/design_spec.rb", "spec/document_spec.rb", "spec/files/image.png", "spec/files/plain.txt", "spec/hookup_spec.rb", "spec/row_spec.rb", "spec/spec_helper.rb", "couch-client.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/robertsosinski/couch-client}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Couch-client", "--main", "README.markdown"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{couch-client}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{The goal of CouchClient is to make documents feel as much as possible as what they already represent, a Hash of primitives, Arrays and other Hashes.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
27
|
+
else
|
28
|
+
s.add_dependency(%q<json>, [">= 0"])
|
29
|
+
end
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<json>, [">= 0"])
|
32
|
+
end
|
33
|
+
end
|
data/lib/couch-client.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
$:.unshift(File.dirname(File.expand_path(__FILE__)))
|
2
|
+
|
3
|
+
# require '../core_ext/hash'
|
4
|
+
|
5
|
+
require 'couch-client/consistent_hash'
|
6
|
+
require 'couch-client/connection'
|
7
|
+
require 'couch-client/connection_handler'
|
8
|
+
require 'couch-client/hookup'
|
9
|
+
require 'couch-client/database'
|
10
|
+
require 'couch-client/document'
|
11
|
+
require 'couch-client/attachment_list'
|
12
|
+
require 'couch-client/attachment'
|
13
|
+
require 'couch-client/design'
|
14
|
+
require 'couch-client/collection'
|
15
|
+
require 'couch-client/row'
|
16
|
+
|
17
|
+
# The CouchClient module is the overall container of all CouchClient logic.
|
18
|
+
module CouchClient
|
19
|
+
VERSION = "0.0.1"
|
20
|
+
|
21
|
+
class Error < Exception; end
|
22
|
+
|
23
|
+
# Start using CouchClient by constructing a new CouchClient::Connection object with a Hash:
|
24
|
+
#
|
25
|
+
# CouchClient.connect(:database => "db_name")
|
26
|
+
#
|
27
|
+
# with a block:
|
28
|
+
#
|
29
|
+
# CouchClient.connect do |c|
|
30
|
+
# c.database = "db_name"
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# or with both:
|
34
|
+
#
|
35
|
+
# CouchClient.connect(:username => "user", :password => "pass") do |c|
|
36
|
+
# c.database = "db_name"
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# CouchClient.connect takes the following options.
|
40
|
+
#
|
41
|
+
# scheme: Protocol used (e.g. http or https), default being "http".
|
42
|
+
# username: Username used by HTTP Basic authentication.
|
43
|
+
# password: Password used by HTTP Basic authentication.
|
44
|
+
# host: The domain for your CouchDB erver, default being "localhost".
|
45
|
+
# port: The port for your CouchDB server, default being 5984.
|
46
|
+
# database: The database you wish to connect to.
|
47
|
+
def self.connect(args = {}, &block)
|
48
|
+
Connection.new(args, &block)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CouchClient
|
2
|
+
# The Attachment is an extended Hash that provides additional methods to
|
3
|
+
# interact with attached files saved within a document.
|
4
|
+
class Attachment < ConsistentHash
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# Attachment is constructed the id of the document it is attached to,
|
8
|
+
# the filename, file stub and connection object.
|
9
|
+
def initialize(id, name, stub, connection)
|
10
|
+
self.merge!(stub)
|
11
|
+
|
12
|
+
@id = id
|
13
|
+
@name = name
|
14
|
+
@connection = connection
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the path for the attachment
|
18
|
+
def path
|
19
|
+
@connection.hookup.handler.path([@id, @name])
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the uri for the attachment
|
23
|
+
def uri
|
24
|
+
@connection.hookup.handler.uri([@id, @name])
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a string that contains file data
|
28
|
+
def data
|
29
|
+
@connection.hookup.get([@id, @name], nil, self["content_type"]).last
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module CouchClient
|
2
|
+
# The AttachmentList prevents ConsistentHash from absorbing
|
3
|
+
# instances of Attachment and making them a ConsistentHash.
|
4
|
+
class AttachmentList < ConsistentHash
|
5
|
+
# AttachmentList is constructed with a hash of attachments.
|
6
|
+
def initialize(attachments)
|
7
|
+
self.merge!(attachments)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CouchClient
|
2
|
+
# The Document is an extended Array that provides additional methods
|
3
|
+
# and state to get status codes, info and connect documents to the server.
|
4
|
+
class Collection < Array
|
5
|
+
attr_reader :code, :info
|
6
|
+
|
7
|
+
# Collection is constructed with a status code, response body,
|
8
|
+
# and connection object.
|
9
|
+
def initialize(code, body, connection)
|
10
|
+
# Iterate over each row to set them a CouchClient::Row object.
|
11
|
+
body.delete("rows").each.with_index do |row, idx|
|
12
|
+
self[idx] = Row.new(code, row, connection)
|
13
|
+
end
|
14
|
+
|
15
|
+
@code = code
|
16
|
+
@info = body
|
17
|
+
@connection = connection
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module CouchClient
|
2
|
+
class DatabaseNotGiven < Exception; end
|
3
|
+
class DocumentNotValid < Exception; end
|
4
|
+
class DocumentNotFound < Exception; end
|
5
|
+
|
6
|
+
# The Connection is the high-level interface used to interact with the CouchDB Server.
|
7
|
+
class Connection
|
8
|
+
attr_reader :hookup, :database
|
9
|
+
|
10
|
+
# Connection is constructed with a Hash or with a block specifying connection parameters.
|
11
|
+
# An error will be raised if a database is not specified.
|
12
|
+
def initialize(args = {})
|
13
|
+
handler = ConnectionHandler.new
|
14
|
+
|
15
|
+
# Set ConnectionHandler settings via a block
|
16
|
+
if block_given?
|
17
|
+
yield(handler)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set remaining ConnectionHandler settings via a Hash
|
21
|
+
args.each_pair do |key, value|
|
22
|
+
handler.send("#{key}=", value)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Ensure a database is provided
|
26
|
+
unless handler.database
|
27
|
+
raise DatabaseNotGiven.new("specify a database to connect to")
|
28
|
+
end
|
29
|
+
|
30
|
+
# `@hookup` is used as the HTTP interface and `@database` is a namespace for all
|
31
|
+
# database specific commands such as creation, deletion, compaction and replication.
|
32
|
+
@hookup = Hookup.new(handler)
|
33
|
+
@database = Database.new(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Fetches documents from the CouchDB server. Although `[]` makes get requests and therefore
|
37
|
+
# could fetch design views and more, anything received that is not a valid document will
|
38
|
+
# raise an error. As such, fetching designs can only be done through the `design` method.
|
39
|
+
def [](id, options = {})
|
40
|
+
code, body = @hookup.get([id], options)
|
41
|
+
|
42
|
+
case code
|
43
|
+
# If something was found
|
44
|
+
when 200
|
45
|
+
# And that something is a document
|
46
|
+
if body["_id"] && body["_rev"]
|
47
|
+
# Make a new document object
|
48
|
+
Document.new(code, body, self)
|
49
|
+
else
|
50
|
+
# Else raise an error as `[]` should only return document objects
|
51
|
+
raise DocumentNotValid.new("the id '#{id}' does not correspond to a document")
|
52
|
+
end
|
53
|
+
# If nothing was found
|
54
|
+
when 404
|
55
|
+
case body["reason"]
|
56
|
+
# Because the document was deleted
|
57
|
+
when "deleted"
|
58
|
+
# Tell the user it was deleted
|
59
|
+
raise DocumentNotFound.new("the document with id '#{id}' has been deleted")
|
60
|
+
else
|
61
|
+
# Else tell the user it was never there to begin with
|
62
|
+
raise DocumentNotFound.new("a document could not be found with id '#{id}'")
|
63
|
+
end
|
64
|
+
# If something else happened
|
65
|
+
else
|
66
|
+
# Raise an error
|
67
|
+
raise Error.new("code: #{code}, error: #{body["error"]}, reason: #{body["reason"]}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Constructs a new design factory that manages `views`, `shows`, `lists` and `fulltext` searches.
|
72
|
+
def design(id)
|
73
|
+
Design.new(id, self)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Acts as the interface to CouchDB's `_all_docs` map view.
|
77
|
+
def all_docs(options = {})
|
78
|
+
# key, startkey and endkey must be JSON encoded
|
79
|
+
["key", "startkey", "endkey"].each do |key|
|
80
|
+
options[key] &&= options[key].to_json
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create a new Collection with the response code, body and connection.
|
84
|
+
Collection.new(*@hookup.get(["_all_docs"], options), self)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The interface used to construct new CouchDB documents. Once constructed
|
88
|
+
# these documents can be saved, updated, validated and deleted.
|
89
|
+
def build(body = {})
|
90
|
+
Document.new(nil, body, self)
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
head = "#<#{self.class}: "
|
95
|
+
body = []
|
96
|
+
body << "username: #{@hookup.handler.username}" if @hookup.handler.username
|
97
|
+
body << "password: #{@hookup.handler.password}" if @hookup.handler.password
|
98
|
+
body << "uri: #{@hookup.handler.uri}"
|
99
|
+
tail = ">"
|
100
|
+
|
101
|
+
head + body.join(", ") + tail
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|