couch-client 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
- v0.0.2. fixing gemspec
1
+ v0.1.0. Adding rake tasks to sync and manage database functions and design documents. Also added support for list and show functions; as well as fulltext administration.
2
2
 
3
- v0.0.1. first release
3
+ v0.0.2. Fixing gemspec
4
+
5
+ v0.0.1. First release
@@ -1,7 +1,7 @@
1
1
  Introduction
2
2
  ============
3
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.
4
+ CouchClient is a Ruby interface for CouchDB that provides easy configuration, state management and utility methods.
5
5
 
6
6
  Installation
7
7
  ------------
@@ -50,7 +50,7 @@ Fetching a Document
50
50
  person = Couch["a3b556796203eab59c31fa21b00043e3"]
51
51
 
52
52
  # You can also pass options if desired
53
- person = Couch["a3b556796203eab59c31fa21b00043e3", :include_docs => true]
53
+ person = Couch["a3b556796203eab59c31fa21b00043e3", :attachments => true]
54
54
 
55
55
  Getting a Document's id, rev and attachments
56
56
  --------------------------------------------
@@ -61,7 +61,7 @@ Getting a Document's id, rev and attachments
61
61
  person.rev # => "1-6665e6330ba75e757ce1f6d793305d67"
62
62
 
63
63
  # A document's attachments
64
- # NOTE: This will be an CouchClient::AttachmentList, and attachments will be CouchClient::Attachment objects
64
+ # NOTE: This will be a CouchClient::AttachmentList, and attachments will be CouchClient::Attachment objects
65
65
  person.attachments # => {"plain.txt"=>{"content_type"=>"text/plain", "revpos"=>2, "length"=>406, "stub"=>true}}
66
66
 
67
67
 
@@ -122,10 +122,10 @@ Working with Collections
122
122
  # Getting all documents with document fields
123
123
  Couch.all_docs(:include_docs => true)
124
124
 
125
- # Specifying a `key`, `start_key` or `end_key`
125
+ # Specifying a `key`, `startkey` or `endkey`
126
126
  couch.all_docs(:key => "7f22af967b04d1b88212d3d26b018e89")
127
- couch.all_docs(:start_key => 200)
128
- couch.all_docs(:end_key => [2010, 01, 01])
127
+ couch.all_docs(:startkey => 200)
128
+ couch.all_docs(:endkey => [2010, 01, 01])
129
129
 
130
130
  # Getting additional collection information
131
131
  Couch.all_docs.info # => {"total_rows" => 2, "offset" => 0}
@@ -140,28 +140,102 @@ Using Design Documents
140
140
  # MapReduce Views
141
141
  Couch.design(:people).view(:sum) # => [{"key" => "male", "value" => 1}, {"key" => "female", "value" => 1}]
142
142
 
143
- Using FullText Searching (Must Have CouchDB-Lucene Installed)
144
- -------------------------------------------------------------
143
+ Using Show and List Functions
144
+ -----------------------------
145
+
146
+ # Show Functions
147
+ Couch.design(:people).show(:html) # => "<h1>alice</h1>"
148
+ Couch.design(:people).show(:json) # => {"name" => "alice"}
149
+
150
+ # List Functions
151
+ Couch.design(:people).list(:json, :people, :all) # => ["alice", "bob", "charlie"]
152
+
153
+ Using FullText Search (Must Have CouchDB-Lucene Installed)
154
+ ----------------------------------------------------------
145
155
 
146
156
  # Getting search results
147
- Couch.design(:people).fulltext(:by_name, :q => "alice") # => [{"id"=>"a6c92090bbee241e892be1ac4464b9d9", "score"=>4.505526065826416, "fields"=>{"default"=>"alice"}}]
157
+ Couch.design(:people).fulltext(:by_name, :q => "ali*") # => [{"id"=>"a6c92090bbee241e892be1ac4464b9d9", "score"=>4.505526065826416, "fields"=>{"default"=>"alice"}}]
148
158
 
149
159
  # Getting additional search results information
150
- Couch.design(:people).fulltext(:by_name, :q => "alice").info # => {"q"=>"default:alice", "etag"=>"11e1541e20d9b860", "skip"=>0, "limit"=>25,
160
+ Couch.design(:people).fulltext(:by_name, :q => "ali*").info # => {"q"=>"default:alice", "etag"=>"11e1541e20d9b860", "skip"=>0, "limit"=>25,
151
161
  # "total_rows"=>7, "search_duration"=>0, "fetch_duration"=>1}
152
162
 
153
163
  # Getting search index information
154
164
  Couch.design(:people).fulltext(:by_name) # => {"current"=>true, "disk_size"=>3759, "doc_count"=>25, "doc_del_count"=>3, "fields"=>["default"],
155
165
  # "last_modified"=>"1288403429000", "optimized"=>false, "ref_count"=>2}
166
+
167
+ # Optimizing an index
168
+ Couch.design(:people).fulltext(:by_name, :optimize) # => true
169
+
170
+ # Expunging an index
171
+ Couch.design(:people).fulltext(:by_name, :expunge) # => true
156
172
 
157
- Database Administration
158
- -----------------------
173
+ Convenience Rake Tasks
174
+ ----------------------
175
+
176
+ CouchClient rake tasks can be enabled by adding the following to your `Rakefile`.
177
+
178
+ CouchClient::RakeTask.new do |c|
179
+ c.connection = Couch
180
+ c.design_path = "./designs"
181
+ end
182
+
183
+ Two parameters are available, `connection` should be the actual variable used for your CouchDB interface and `design_path` should be the application's location where design documents will be stored.
184
+
185
+ Within the design path, you should format each design document with folders and files corresponding to the fields in your design document.
186
+
187
+ designs
188
+ ├── people
189
+ │ ├── fulltext
190
+ │ │ └── by_name
191
+ │ │ └── index.js
192
+ │ ├── lists
193
+ │ │ ├── html.js
194
+ │ │ └── json.js
195
+ │ ├── shows
196
+ │ │ ├── html.js
197
+ │ │ ├── json.js
198
+ │ │ └── xml.js
199
+ │ ├── validate_on_update.js
200
+ │ └── views
201
+ │ ├── all
202
+ │ │ └── map.js
203
+ │ └── sum
204
+ │ ├── map.js
205
+ │ └── reduce.js
206
+ └── robots
207
+ ├── fulltext
208
+ │ └── by_name
209
+ │ └── index.js
210
+ ├── validate_on_update.js
211
+ └── views
212
+ ├── all
213
+ │ └── map.js
214
+ └── sum
215
+ ├── map.js
216
+ └── reduce.js
217
+
218
+ Once you have your design documents created, you can rum `rake couch:sync`, and CouchClient will create new documents, update existing documents (only if there are changes) and delete documents that no longer exist.
219
+
220
+ CouchClient also offers tasks that help in maintaining CouchDB.
221
+
222
+ # Create a database
223
+ rake couch:create
224
+
225
+ # Delete a database
226
+ rake couch:delete
227
+
228
+ # Compact a database
229
+ rake couch:compact
230
+
231
+ Performing Database Administration
232
+ ----------------------------------
159
233
 
160
234
  # Create a database
161
235
  Couch.database.create
162
236
 
163
237
  # See if a database exists
164
- Couch.database.exists
238
+ Couch.database.exists?
165
239
 
166
240
  # Get database stats
167
241
  Couch.database.stats
data/Rakefile CHANGED
@@ -2,7 +2,10 @@ require 'rake'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'echoe'
4
4
 
5
- desc "Run all specs"
5
+ # Prevent Echoe from running spec tasks, especially as
6
+ # Spec should be removed in later versions of Rspec 2.
7
+ Object.send(:remove_const, :Spec)
8
+
6
9
  RSpec::Core::RakeTask.new do |t|
7
10
  t.rspec_opts = %w[--colour --format progress]
8
11
  end
@@ -11,7 +14,8 @@ Echoe.new("couch-client") do |p|
11
14
  p.author = "Robert Sosinski"
12
15
  p.email = "email@robertsosinski.com"
13
16
  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."
17
+ p.description = "A Ruby interface for CouchDB"
18
+ p.summary = "A Ruby interface for CouchDB that provides easy configuration, state management and utility methods."
16
19
  p.runtime_dependencies = ["json >=1.4.6", "curb >=0.7.8"]
20
+ p.development_dependencies = ["echoe >=4.3.1", "rspec >=2.0.0"]
17
21
  end
data/TODO CHANGED
@@ -1,11 +1,5 @@
1
- Need
2
- * design save rake task
3
-
4
- Next
1
+ * replication
5
2
  * batch insert
6
3
  * batch update
7
4
  * batch delete
8
- * batch upload
9
- * show design
10
- * list design
11
- * replication
5
+ * batch upload
@@ -2,12 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{couch-client}
5
- s.version = "0.0.2"
5
+ s.version = "0.1.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Robert Sosinski"]
9
- s.date = %q{2010-10-30}
10
- s.description = %q{CouchClient is Ruby library that can be used to interact with CouchDB}
9
+ s.date = %q{2010-11-05}
10
+ s.description = %q{A Ruby interface for CouchDB}
11
11
  s.email = %q{email@robertsosinski.com}
12
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
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"]
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = %q{couch-client}
18
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.}
19
+ s.summary = %q{A Ruby interface for CouchDB that provides easy configuration, state management and utility methods.}
20
20
 
21
21
  if s.respond_to? :specification_version then
22
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -25,12 +25,18 @@ Gem::Specification.new do |s|
25
25
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
26
  s.add_runtime_dependency(%q<json>, [">= 1.4.6"])
27
27
  s.add_runtime_dependency(%q<curb>, [">= 0.7.8"])
28
+ s.add_development_dependency(%q<echoe>, [">= 4.3.1"])
29
+ s.add_development_dependency(%q<rspec>, [">= 2.0.0"])
28
30
  else
29
31
  s.add_dependency(%q<json>, [">= 1.4.6"])
30
32
  s.add_dependency(%q<curb>, [">= 0.7.8"])
33
+ s.add_dependency(%q<echoe>, [">= 4.3.1"])
34
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
31
35
  end
32
36
  else
33
37
  s.add_dependency(%q<json>, [">= 1.4.6"])
34
38
  s.add_dependency(%q<curb>, [">= 0.7.8"])
39
+ s.add_dependency(%q<echoe>, [">= 4.3.1"])
40
+ s.add_dependency(%q<rspec>, [">= 2.0.0"])
35
41
  end
36
42
  end
@@ -1,7 +1,5 @@
1
1
  $:.unshift(File.dirname(File.expand_path(__FILE__)))
2
2
 
3
- # require '../core_ext/hash'
4
-
5
3
  require 'couch-client/consistent_hash'
6
4
  require 'couch-client/connection'
7
5
  require 'couch-client/connection_handler'
@@ -13,11 +11,11 @@ require 'couch-client/attachment'
13
11
  require 'couch-client/design'
14
12
  require 'couch-client/collection'
15
13
  require 'couch-client/row'
16
- require 'tasks/couch'
14
+ require "couch-client/rake_task" if defined?(Rake)
17
15
 
18
16
  # The CouchClient module is the overall container of all CouchClient logic.
19
17
  module CouchClient
20
- VERSION = "0.0.2"
18
+ VERSION = "0.1.0"
21
19
 
22
20
  class Error < Exception; end
23
21
 
@@ -1,10 +1,10 @@
1
1
  module CouchClient
2
- # The Attachment is an extended Hash that provides additional methods to
3
- # interact with attached files saved within a document.
2
+ # Attachment is an extended Hash that provides additional methods
3
+ # to interact with attached files saved within a document.
4
4
  class Attachment < ConsistentHash
5
5
  attr_reader :name
6
6
 
7
- # Attachment is constructed the id of the document it is attached to,
7
+ # Attachment is constructed with the id of the document it is attached to,
8
8
  # the filename, file stub and connection object.
9
9
  def initialize(id, name, stub, connection)
10
10
  self.merge!(stub)
@@ -26,7 +26,7 @@ module CouchClient
26
26
 
27
27
  # Returns a string that contains attachment data
28
28
  def data
29
- @connection.hookup.get([@id, @name], nil, self["content_type"]).last
29
+ @data ||= @connection.hookup.get([@id, @name], nil, self["content_type"]).last
30
30
  end
31
31
  end
32
32
  end
@@ -1,5 +1,5 @@
1
1
  module CouchClient
2
- # The AttachmentList prevents ConsistentHash from absorbing
2
+ # AttachmentList prevents ConsistentHash from absorbing
3
3
  # instances of Attachment and making them a ConsistentHash.
4
4
  class AttachmentList < ConsistentHash
5
5
  # AttachmentList is constructed with a hash of attachments.
@@ -1,6 +1,6 @@
1
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.
2
+ # Collection is an extended Array that provides additional methods and
3
+ # state to get status codes, info and connect documents to the server.
4
4
  class Collection < Array
5
5
  attr_reader :code, :info
6
6
 
@@ -3,7 +3,7 @@ module CouchClient
3
3
  class DocumentNotValid < Exception; end
4
4
  class DocumentNotFound < Exception; end
5
5
 
6
- # The Connection is the high-level interface used to interact with the CouchDB Server.
6
+ # Connection is the high-level interface used to interact with the CouchDB Server.
7
7
  class Connection
8
8
  attr_reader :hookup, :database
9
9
 
@@ -75,16 +75,16 @@ module CouchClient
75
75
 
76
76
  # Acts as the interface to CouchDB's `_all_docs` map view.
77
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
78
  # Create a new Collection with the response code, body and connection.
84
79
  Collection.new(*@hookup.get(["_all_docs"], options), self)
85
80
  end
86
81
 
87
- # The interface used to construct new CouchDB documents. Once constructed
82
+ # Returns a list of all _design documents.
83
+ def all_design_docs(options = {})
84
+ all_docs({"startkey" => "_design/", "endkey" => "_design0"}.merge(options))
85
+ end
86
+
87
+ # The interface used to construct new CouchDB documents. Once constructed,
88
88
  # these documents can be saved, updated, validated and deleted.
89
89
  def build(body = {})
90
90
  Document.new(nil, body, self)
@@ -5,7 +5,7 @@ module CouchClient
5
5
  class InvalidQueryObject < Exception; end
6
6
  class InvalidDatabaseName < Exception; end
7
7
 
8
- # The ConnectionHandler creates properly formed URIs and paths, while also
8
+ # ConnectionHandler creates properly formed URIs and paths, while also
9
9
  # specifying sensible defaults for CouchDB. Once initialized, parameters
10
10
  # can be wrote and read using getter and setter syntax.
11
11
  class ConnectionHandler
@@ -50,6 +50,11 @@ module CouchClient
50
50
  raise InvalidPathObject.new("path must be of type 'Array' not of type '#{path_obj.class}'")
51
51
  end
52
52
 
53
+ # key, startkey and endkey must be JSON encoded to be valid.
54
+ ["key", :key, "startkey", :startkey, "endkey", :endkey].each do |key|
55
+ query_obj[key] &&= query_obj[key].to_json
56
+ end
57
+
53
58
  query_str = if query_obj.is_a?(Hash)
54
59
  # If a Hash, stringify and escape each object, join each key/value with a "=" and each pair with a "&"
55
60
  query_obj.to_a.map{|q| q.map{|r| CGI.escape(r.to_s)}.join("=")}.join("&")
@@ -1,5 +1,5 @@
1
1
  module CouchClient
2
- # The Database is just a organized collection of functions that interact with the
2
+ # Database is just an organized collection of functions that interact with the
3
3
  # CouchDB database such as stats, creation, compaction, replication and deletion.
4
4
  class Database
5
5
  # Database is constructed with a connection that is used to make HTTP requests to the server.
@@ -3,8 +3,9 @@ module CouchClient
3
3
  class ShowNotFound < Exception; end
4
4
  class ListNotFound < Exception; end
5
5
  class FullTextNotFound < Exception; end
6
+ class FullTextRequestBad < Exception; end
6
7
 
7
- # The Design is the interface used to interact with design documents
8
+ # Design is the interface used to interact with design documents
8
9
  # in order make view, show, list and fulltext requests.
9
10
  class Design
10
11
  attr_accessor :id
@@ -18,11 +19,6 @@ module CouchClient
18
19
 
19
20
  # Makes requests to the server that return mappped/reduced view collections.
20
21
  def view(name, options = {})
21
- # key, startkey and endkey must be JSON encoded
22
- ["key", "startkey", "endkey"].each do |key|
23
- options[key] &&= options[key].to_json
24
- end
25
-
26
22
  code, body = @connection.hookup.get(["_design", id, "_view", name], options)
27
23
 
28
24
  case code
@@ -37,34 +33,73 @@ module CouchClient
37
33
  raise Error.new("code: #{code}, error: #{body["error"]}, reason: #{body["reason"]}")
38
34
  end
39
35
  end
36
+
37
+ # Makes requests to the server that return show objects.
38
+ def show(name, document_id, options = {})
39
+ code, body = @connection.hookup.get(["_design", id, "_show", name, document_id], options, nil)
40
40
 
41
- # TODO: add show method
42
- def show(name, options = {})
43
- raise "pending"
41
+ case code
42
+ when 200
43
+ body.is_a?(Hash) ? ConsistentHash.new(body) : body
44
+ when 404
45
+ # Raise an error if nothing was found
46
+ raise ViewNotFound.new("could not find show field '#{name}' for design '#{id}'")
47
+ else
48
+ # Also raise an error if something else happens
49
+ raise Error.new("code: #{code}, error: #{body["error"]}, reason: #{body["reason"]}")
50
+ end
44
51
  end
52
+
53
+ # Makes requests to the server that list objects.
54
+ def list(name, document_id, view_name, options = {})
55
+ code, body = @connection.hookup.get(["_design", id, "_list", name, document_id, view_name], options)
45
56
 
46
- # TODO: add list method
47
- def list(name, options = {})
48
- raise "pending"
57
+ case code
58
+ when 200
59
+ body.is_a?(Hash) ? ConsistentHash.new(body) : body
60
+ when 404
61
+ # Raise an error if nothing was found
62
+ raise ViewNotFound.new("could not find list field '#{name}' for design '#{id}'")
63
+ else
64
+ # Also raise an error if something else happens
65
+ raise Error.new("code: #{code}, error: #{body["error"]}, reason: #{body["reason"]}")
66
+ end
49
67
  end
50
68
 
51
69
  # Makes requests to the server that return lucene search results.
52
70
  def fulltext(name, options = {})
53
- code, body = @connection.hookup.get(["_fti", "_design", id, name], options)
71
+ path = ["_fti", "_design", id, name]
72
+ verb = :get
73
+
74
+ # Options may be a Hash or a String. Hashes are used for fulltext queries,
75
+ # while String are used for administration operations (such as optimizing).
76
+ if [String, Symbol].include?(options.class)
77
+ path << "_#{options}"
78
+ verb = :post
79
+ options = {}
80
+ end
81
+
82
+ code, body = @connection.hookup.send(verb, path, options)
54
83
 
55
84
  case code
56
85
  when 200
57
86
  if body["rows"]
58
- # Return a serch result if a query was provided
87
+ # Return a serch result if a query was provided.
59
88
  Collection.new(code, body, self)
60
89
  else
61
- # Return a status hash if a query was not provided
90
+ # Return a status hash if a query was not provided.
62
91
  body
63
92
  end
93
+ when 202
94
+ true # Return true when administration operations are successfully performed.
64
95
  else
65
- if body["reason"] == "no_such_view"
66
- # Raise an error if a fulltext function was not found
96
+ case body["reason"]
97
+ when "no_such_view"
98
+ # Raise an error if a fulltext function was not found.
67
99
  raise FullTextNotFound.new("could not find fulltext field '#{name}' for design '#{id}'")
100
+ when "bad_request"
101
+ # Raise an error if a request was not formated properly (i.e. is bad).
102
+ raise FullTextRequestBad.new("bad request made for fulltext field '#{name}' for design '#{id}'")
68
103
  else
69
104
  # Also raise an error if something else happens
70
105
  raise Error.new("code: #{code}, error: #{body["error"]}, reason: #{body["reason"]}")
@@ -3,7 +3,7 @@ module CouchClient
3
3
  class AttachmentError < Exception; end
4
4
  class DocumentNotAvailable < Exception; end
5
5
 
6
- # The Document is an extended Hash that provides additional methods to
6
+ # Document is an extended Hash that provides additional methods to
7
7
  # save, update (with attachments), and delete documents on the CouchDB.
8
8
  class Document < ConsistentHash
9
9
  attr_reader :code, :error
@@ -42,14 +42,14 @@ module CouchClient
42
42
  # Returns a copy of the same document that is currently saved on the server.
43
43
  def saved_doc(query = {})
44
44
  if new?
45
- raise DocumentNotAvailable.new('this document is new and therefore has not been saved yet')
45
+ raise DocumentNotAvailable.new("this document is new and therefore has not been saved yet")
46
46
  else
47
47
  @connection[self.id, query]
48
48
  end
49
49
  end
50
50
 
51
51
  # Tries to save the document to the server. If it us unable to,
52
- # it will save the error and make it available with via #error.
52
+ # it will save the error and make it available via #error.
53
53
  def save
54
54
  # Ensure that "_id" is a String if it is defined.
55
55
  if self.key?("_id") && !self["_id"].is_a?(String)
@@ -71,14 +71,14 @@ module CouchClient
71
71
  @deleted = false
72
72
  true
73
73
  else
74
- # Save error message and return `false`.
74
+ # Else save error message and return `false`.
75
75
  @error = {body["error"] => body["reason"]}
76
76
  false
77
77
  end
78
78
  end
79
79
 
80
80
  # Tries to attach a file to the document. If it us unable to,
81
- # it will save the error and make it available with via #error.
81
+ # it will save the error and make it available via #error.
82
82
  def attach(name, content, content_type)
83
83
  # The document must already be saved to the server before a file can be attached.
84
84
  if self.rev
@@ -90,7 +90,7 @@ module CouchClient
90
90
  self.rev = body["rev"]
91
91
  true
92
92
  else
93
- # Save error message and return `false`.
93
+ # Else save error message and return `false`.
94
94
  @error = {body["error"] => body["reason"]}
95
95
  false
96
96
  end
@@ -101,7 +101,7 @@ module CouchClient
101
101
  end
102
102
 
103
103
  # Tries to delete a file from the server. If it us unable to,
104
- # it will save the error and make it available with via #error.
104
+ # it will save the error and make it available via #error.
105
105
  def delete!
106
106
  @code, body = @connection.hookup.delete([id], {"rev" => rev})
107
107
 
@@ -112,7 +112,7 @@ module CouchClient
112
112
  @deleted = true
113
113
  true
114
114
  else
115
- # Save error message and return `false`.
115
+ # Else save error message and return `false`.
116
116
  @error = {body["error"] => body["reason"]}
117
117
  false
118
118
  end
@@ -6,7 +6,7 @@ module CouchClient
6
6
  class InvalidJSONData < Exception; end
7
7
  class SymbolUsedInField < Exception; end
8
8
 
9
- # The Hookup is the basic HTTP interface that connects CouchClient to CouchDB.
9
+ # Hookup is the basic HTTP interface that connects CouchClient to CouchDB.
10
10
  # Hookup can use any HTTP library if the conventions listed below are followed.
11
11
  #
12
12
  # If modified, Hookup must have head, get, post, put and delete instance methods.
@@ -66,8 +66,7 @@ module CouchClient
66
66
  # Setup curb options block
67
67
  options = lambda do |easy|
68
68
  easy.headers["User-Agent"] = "couch-client v#{VERSION}"
69
- easy.headers["Content-Type"] = content_type if content_type
70
- easy.headers["Accepts"] = content_type if content_type
69
+ easy.headers["Content-Type"] = content_type if content_type && [:post, :put].include?(verb)
71
70
  easy.username = handler.username
72
71
  easy.userpwd = handler.password
73
72
  end
@@ -91,7 +90,7 @@ module CouchClient
91
90
  # body is either a nil, a hash or a string containing attachment data
92
91
  body = if easy.body_str == "" || easy.body_str.nil?
93
92
  nil
94
- elsif content_type == "application/json" || [:post, :put, :delete].include?(verb)
93
+ elsif [content_type, easy.content_type].include?("application/json") || [:post, :put, :delete].include?(verb)
95
94
  begin
96
95
  JSON.parse(easy.body_str)
97
96
  rescue
@@ -1,5 +1,5 @@
1
1
  module CouchClient
2
- # The Row is an extended Hash that provides additional state to
2
+ # Row is an extended Hash that provides additional state to
3
3
  # get status codes and connect documents to the server.
4
4
  class Row < ConsistentHash
5
5
  def initialize(code, row, connection)
@@ -60,6 +60,10 @@ describe CouchClient::ConnectionHandler do
60
60
  @ch.uri(["path"]).should eql("https://couchone.com:8080/abc123%2F_%24%28%29%2B-/path")
61
61
  end
62
62
 
63
+ it 'should properly json encode key, startkey and endkey query paramenters' do
64
+ @ch.uri(["path"], {:key => "7f22af967b04d1b88212d3d26b018e89", "startkey" => 2010, :endkey => [2010, 01, 01]}).should eql("https://couchone.com:8080/abc123%2F_%24%28%29%2B-/path?key=%227f22af967b04d1b88212d3d26b018e89%22&startkey=2010&endkey=%5B2010%2C1%2C1%5D")
65
+ end
66
+
63
67
  it 'should raise an error if an invalid name is given' do
64
68
  lambda{@ch.database = "ABC!@#"}.should raise_error(CouchClient::InvalidDatabaseName)
65
69
  end
@@ -71,6 +71,17 @@ describe CouchClient::Connection do
71
71
  end
72
72
  end
73
73
 
74
+ describe '#all_design_docs' do
75
+ it 'should return a list of all documents stored' do
76
+ all_docs = @couch.all_design_docs("include_docs" => true)
77
+ all_docs.should be_a(CouchClient::Collection)
78
+ docs = all_docs.map{|doc| doc["doc"].id}
79
+ docs.should_not include(@alice.id)
80
+ docs.should_not include(@bob.id)
81
+ docs.should include(@design.id)
82
+ end
83
+ end
84
+
74
85
  describe '#build' do
75
86
  before(:all) do
76
87
  @charlie = @couch.build({"name" => "charlie", "city" => "san fran"})
@@ -19,6 +19,13 @@ describe CouchClient::Connection do
19
19
  "all" => {"map" => "function(doc){emit(doc._id, doc)}"},
20
20
  "sum" => {"map" => "function(doc){emit(null, 1)}", "reduce" => "function(id, values, rereduce){return sum(values)}"},
21
21
  },
22
+ "shows" => {
23
+ "html" => "function(doc, req){return{body: '<h1>' + doc.name + '</h1>', headers: {'Content-Type': 'text/html'}}}",
24
+ "json" => "function(doc, req){return{body: JSON.stringify({'name': doc.name}), headers: {'Content-Type': 'application/json'}}}"
25
+ },
26
+ "lists" => {
27
+ "json" => "function(head, req){var row;var rows = [];while(row = getRow()){rows.push(row.value.name);}send(JSON.stringify(rows));}"
28
+ },
22
29
  "fulltext" => {
23
30
  "by_name" => {
24
31
  "index" => "function(doc){var ret = new Document();ret.add(doc.name);return ret;}"
@@ -78,11 +85,19 @@ describe CouchClient::Connection do
78
85
  end
79
86
 
80
87
  describe '#show' do
81
- pending 'will be built in another release'
88
+ it 'should return valid html for a html show function' do
89
+ @people.show("html", "123").should eql("<h1>alice</h1>")
90
+ end
91
+
92
+ it 'should return valid json for a json show function' do
93
+ @people.show("json", "123").should eql({"name"=>"alice"})
94
+ end
82
95
  end
83
96
 
84
97
  describe '#list' do
85
- pending 'will be built in another release'
98
+ it 'should return valid json for a json list function' do
99
+ @people.list("json", "people", "all").should eql(["alice", "bob", "charlie"])
100
+ end
86
101
  end
87
102
 
88
103
  describe '#fulltext' do
@@ -96,5 +111,17 @@ describe CouchClient::Connection do
96
111
  fulltext.info.should be_a(Hash)
97
112
  fulltext.first["id"].should eql(@alice.id)
98
113
  end
114
+
115
+ it 'should return true when administration operations are successfully performed' do
116
+ @people.fulltext("by_name", "optimize").should be_true
117
+ end
118
+
119
+ it 'should raise a "not found" error if the fulltext field was not found' do
120
+ lambda{@people.fulltext("not_found")}.should raise_error(CouchClient::FullTextNotFound)
121
+ end
122
+
123
+ it 'should raise a "bad request" error if the requested administration operation was not valid' do
124
+ lambda{@people.fulltext("by_name", "bad_request")}.should raise_error(CouchClient::FullTextRequestBad)
125
+ end
99
126
  end
100
127
  end
@@ -1,5 +1,7 @@
1
1
  require 'rubygems'
2
+ require 'tempfile'
2
3
  require 'rspec'
4
+ require 'rake'
3
5
 
4
6
  Rspec.configure do |c|
5
7
  c.mock_with :rspec
@@ -8,4 +10,22 @@ end
8
10
  COUCHDB_TEST_SETTINGS = {:database => "couch-client_test"}
9
11
  COUCHDB_TEST_DATABASE = COUCHDB_TEST_SETTINGS[:database]
10
12
 
13
+ def suppress
14
+ temp_f = Tempfile.new("suppress")
15
+ save_stdout = $stdout.dup
16
+ save_stderr = $stderr.dup
17
+ begin
18
+ $stdout.reopen(temp_f)
19
+ $stderr.reopen(temp_f)
20
+ yield
21
+ rescue
22
+ temp_f.flush
23
+ save_stdout.puts File.open(temp_f.path).read
24
+ raise
25
+ ensure
26
+ $stdout.reopen(save_stdout)
27
+ $stderr.reopen(save_stderr)
28
+ end
29
+ end
30
+
11
31
  require File.join(File.dirname(File.expand_path(__FILE__)), "..", "lib", "couch-client")
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 2
9
- version: 0.0.2
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Robert Sosinski
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-30 00:00:00 -04:00
17
+ date: 2010-11-05 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -47,7 +47,37 @@ dependencies:
47
47
  version: 0.7.8
48
48
  type: :runtime
49
49
  version_requirements: *id002
50
- description: CouchClient is Ruby library that can be used to interact with CouchDB
50
+ - !ruby/object:Gem::Dependency
51
+ name: echoe
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 4
60
+ - 3
61
+ - 1
62
+ version: 4.3.1
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rspec
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 2
75
+ - 0
76
+ - 0
77
+ version: 2.0.0
78
+ type: :development
79
+ version_requirements: *id004
80
+ description: A Ruby interface for CouchDB
51
81
  email: email@robertsosinski.com
52
82
  executables: []
53
83
 
@@ -142,6 +172,6 @@ rubyforge_project: couch-client
142
172
  rubygems_version: 1.3.7
143
173
  signing_key:
144
174
  specification_version: 3
145
- 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.
175
+ summary: A Ruby interface for CouchDB that provides easy configuration, state management and utility methods.
146
176
  test_files: []
147
177