couchrest 0.12.4 → 0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. data/README.md +33 -8
  2. data/Rakefile +11 -2
  3. data/examples/model/example.rb +19 -13
  4. data/lib/couchrest.rb +70 -11
  5. data/lib/couchrest/core/database.rb +121 -62
  6. data/lib/couchrest/core/design.rb +7 -17
  7. data/lib/couchrest/core/document.rb +42 -30
  8. data/lib/couchrest/core/response.rb +16 -0
  9. data/lib/couchrest/core/server.rb +47 -10
  10. data/lib/couchrest/helper/upgrade.rb +51 -0
  11. data/lib/couchrest/mixins.rb +4 -0
  12. data/lib/couchrest/mixins/attachments.rb +31 -0
  13. data/lib/couchrest/mixins/callbacks.rb +483 -0
  14. data/lib/couchrest/mixins/class_proxy.rb +108 -0
  15. data/lib/couchrest/mixins/design_doc.rb +90 -0
  16. data/lib/couchrest/mixins/document_queries.rb +44 -0
  17. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  18. data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
  19. data/lib/couchrest/mixins/properties.rb +129 -0
  20. data/lib/couchrest/mixins/validation.rb +242 -0
  21. data/lib/couchrest/mixins/views.rb +169 -0
  22. data/lib/couchrest/monkeypatches.rb +81 -6
  23. data/lib/couchrest/more/casted_model.rb +28 -0
  24. data/lib/couchrest/more/extended_document.rb +215 -0
  25. data/lib/couchrest/more/property.rb +40 -0
  26. data/lib/couchrest/support/blank.rb +42 -0
  27. data/lib/couchrest/support/class.rb +176 -0
  28. data/lib/couchrest/validation/auto_validate.rb +163 -0
  29. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  30. data/lib/couchrest/validation/validation_errors.rb +118 -0
  31. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  32. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  33. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  34. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  35. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  36. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  37. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  38. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  39. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  40. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  41. data/spec/couchrest/core/database_spec.rb +189 -124
  42. data/spec/couchrest/core/design_spec.rb +13 -6
  43. data/spec/couchrest/core/document_spec.rb +231 -177
  44. data/spec/couchrest/core/server_spec.rb +35 -0
  45. data/spec/couchrest/helpers/pager_spec.rb +1 -1
  46. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  47. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  48. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  49. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  50. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  51. data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
  52. data/spec/couchrest/more/property_spec.rb +136 -0
  53. data/spec/fixtures/more/article.rb +34 -0
  54. data/spec/fixtures/more/card.rb +20 -0
  55. data/spec/fixtures/more/course.rb +14 -0
  56. data/spec/fixtures/more/event.rb +6 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +8 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/spec_helper.rb +13 -7
  62. metadata +58 -4
  63. data/lib/couchrest/core/model.rb +0 -613
  64. data/spec/couchrest/core/model_spec.rb +0 -855
data/README.md CHANGED
@@ -31,7 +31,7 @@ Quick Start:
31
31
 
32
32
  # with !, it creates the database if it doesn't already exist
33
33
  @db = CouchRest.database!("http://127.0.0.1:5984/couchrest-test")
34
- response = @db.save({:key => 'value', 'another key' => 'another value'})
34
+ response = @db.save_doc({:key => 'value', 'another key' => 'another value'})
35
35
  doc = @db.get(response['id'])
36
36
  puts doc.inspect
37
37
 
@@ -47,7 +47,7 @@ Bulk Save:
47
47
 
48
48
  Creating and Querying Views:
49
49
 
50
- @db.save({
50
+ @db.save_doc({
51
51
  "_id" => "_design/first",
52
52
  :views => {
53
53
  :test => {
@@ -59,10 +59,35 @@ Creating and Querying Views:
59
59
 
60
60
  ## CouchRest::Model
61
61
 
62
- CouchRest::Model is a module designed along the lines of DataMapper::Resource.
63
- By subclassing, suddenly you get all sorts of powerful sugar, so that working
64
- with CouchDB in your Rails or Merb app is no harder than working with the
65
- standard SQL alternatives. See the CouchRest::Model documentation for an
66
- example article class that illustrates usage.
62
+ CouchRest::Model has been deprecated and replaced by CouchRest::ExtendedDocument
63
+
64
+
65
+ ## CouchRest::ExtendedDocument
66
+
67
+ ### Callbacks
68
+
69
+ `CouchRest::ExtendedDocuments` instances have 2 callbacks already defined for you:
70
+ `create_callback`, `save_callback`, `update_callback` and `destroy_callback`
71
+
72
+ In your document inherits from `CouchRest::ExtendedDocument`, define your callback as follows:
73
+
74
+ save_callback :before, :generate_slug_from_name
75
+
76
+ CouchRest uses a mixin you can find in lib/mixins/callbacks which is extracted from Rails 3, here are some simple usage examples:
77
+
78
+ save_callback :before, :before_method
79
+ save_callback :after, :after_method, :if => :condition
80
+ save_callback :around {|r| stuff; yield; stuff }
81
+
82
+ Check the mixin or the ExtendedDocument class to see how to implement your own callbacks.
83
+
84
+ ### Casting
85
+
86
+ Often, you will want to store multiple objects within a document, to be able to retrieve your objects when you load the document,
87
+ you can define some casting rules.
88
+
89
+ property :casted_attribute, :cast_as => 'WithCastedModelMixin'
90
+ property :keywords, :cast_as => ["String"]
91
+
92
+ If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"]
67
93
 
68
- CouchRest::Model will be removed from this package.
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ spec = Gem::Specification.new do |s|
23
23
  s.homepage = "http://github.com/jchris/couchrest"
24
24
  s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments."
25
25
  s.has_rdoc = true
26
- s.authors = ["J. Chris Anderson"]
26
+ s.authors = ["J. Chris Anderson", "Matt Aimonetti"]
27
27
  s.files = %w( LICENSE README.md Rakefile THANKS.md ) +
28
28
  Dir["{examples,lib,spec,utils}/**/*"] -
29
29
  Dir["spec/tmp"]
@@ -35,7 +35,7 @@ spec = Gem::Specification.new do |s|
35
35
  end
36
36
 
37
37
 
38
- desc "create .gemspec file (useful for github)"
38
+ desc "Create .gemspec file (useful for github)"
39
39
  task :gemspec do
40
40
  filename = "#{spec.name}.gemspec"
41
41
  File.open(filename, "w") do |f|
@@ -43,6 +43,15 @@ task :gemspec do
43
43
  end
44
44
  end
45
45
 
46
+ Rake::GemPackageTask.new(spec) do |pkg|
47
+ pkg.gem_spec = spec
48
+ end
49
+
50
+ desc "Install the gem locally"
51
+ task :install => [:package] do
52
+ sh %{sudo gem install pkg/couchrest-#{CouchRest::VERSION}}
53
+ end
54
+
46
55
  desc "Run all specs"
47
56
  Spec::Rake::SpecTask.new('spec') do |t|
48
57
  t.spec_files = FileList['spec/**/*_spec.rb']
@@ -1,31 +1,38 @@
1
- require 'rubygems'
2
- require 'couchrest'
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'couchrest')
3
2
 
4
3
  def show obj
5
4
  puts obj.inspect
6
5
  puts
7
6
  end
8
7
 
9
- CouchRest::Model.default_database = CouchRest.database!('couchrest-model-example')
8
+ SERVER = CouchRest.new
9
+ SERVER.default_database = 'couchrest-extendeddoc-example'
10
10
 
11
- class Author < CouchRest::Model
12
- key_accessor :name
11
+ class Author < CouchRest::ExtendedDocument
12
+ use_database SERVER.default_database
13
+ property :name
14
+
13
15
  def drink_scotch
14
16
  puts "... glug type glug ... I'm #{name} ... type glug glug ..."
15
17
  end
16
18
  end
17
19
 
18
- class Post < CouchRest::Model
19
- key_accessor :title, :body, :author
20
-
21
- cast :author, :as => 'Author'
20
+ class Post < CouchRest::ExtendedDocument
21
+ use_database SERVER.default_database
22
+
23
+ property :title
24
+ property :body
25
+ property :author, :cast_as => 'Author'
22
26
 
23
27
  timestamps!
24
28
  end
25
29
 
26
- class Comment < CouchRest::Model
27
- cast :commenter, :as => 'Author'
28
-
30
+ class Comment < CouchRest::ExtendedDocument
31
+ use_database SERVER.default_database
32
+
33
+ property :commenter, :cast_as => 'Author'
34
+ timestamps!
35
+
29
36
  def post= post
30
37
  self["post_id"] = post.id
31
38
  end
@@ -33,7 +40,6 @@ class Comment < CouchRest::Model
33
40
  Post.get(self['post_id']) if self['post_id']
34
41
  end
35
42
 
36
- timestamps!
37
43
  end
38
44
 
39
45
  puts "Act I: CRUD"
data/lib/couchrest.rb CHANGED
@@ -13,35 +13,74 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "rubygems"
16
+ gem 'json'
16
17
  require 'json'
18
+ gem 'rest-client'
17
19
  require 'rest_client'
18
20
 
19
21
  $:.unshift File.dirname(__FILE__) unless
20
22
  $:.include?(File.dirname(__FILE__)) ||
21
23
  $:.include?(File.expand_path(File.dirname(__FILE__)))
22
24
 
25
+ $COUCHREST_DEBUG ||= false
23
26
 
24
27
  require 'couchrest/monkeypatches'
25
28
 
26
29
  # = CouchDB, close to the metal
27
30
  module CouchRest
28
- VERSION = '0.12.4'
31
+ VERSION = '0.23' unless self.const_defined?("VERSION")
29
32
 
30
33
  autoload :Server, 'couchrest/core/server'
31
34
  autoload :Database, 'couchrest/core/database'
35
+ autoload :Response, 'couchrest/core/response'
32
36
  autoload :Document, 'couchrest/core/document'
33
- autoload :Design, 'couchrest/core/design'
37
+ autoload :Design, 'couchrest/core/design'
34
38
  autoload :View, 'couchrest/core/view'
35
39
  autoload :Model, 'couchrest/core/model'
36
40
  autoload :Pager, 'couchrest/helper/pager'
37
41
  autoload :FileManager, 'couchrest/helper/file_manager'
38
42
  autoload :Streamer, 'couchrest/helper/streamer'
43
+ autoload :Upgrade, 'couchrest/helper/upgrade'
44
+
45
+ autoload :ExtendedDocument, 'couchrest/more/extended_document'
46
+ autoload :CastedModel, 'couchrest/more/casted_model'
47
+
48
+ require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
39
49
 
40
50
  # The CouchRest module methods handle the basic JSON serialization
41
51
  # and deserialization, as well as query parameters. The module also includes
42
52
  # some helpers for tasks like instantiating a new Database or Server instance.
43
53
  class << self
44
54
 
55
+ # extracted from Extlib
56
+ #
57
+ # Constantize tries to find a declared constant with the name specified
58
+ # in the string. It raises a NameError when the name is not in CamelCase
59
+ # or is not initialized.
60
+ #
61
+ # @example
62
+ # "Module".constantize #=> Module
63
+ # "Class".constantize #=> Class
64
+ def constantize(camel_cased_word)
65
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
66
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
67
+ end
68
+
69
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
70
+ end
71
+
72
+ # extracted from Extlib
73
+ #
74
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
75
+ # Like titleize, this is meant for creating pretty output.
76
+ #
77
+ # @example
78
+ # "employee_salary" #=> "Employee salary"
79
+ # "author_id" #=> "Author"
80
+ def humanize(lower_case_and_underscored_word)
81
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
82
+ end
83
+
45
84
  # todo, make this parse the url and instantiate a Server or Database instance
46
85
  # depending on the specificity.
47
86
  def new(*opts)
@@ -99,18 +138,42 @@ module CouchRest
99
138
  cr.database(parsed[:database])
100
139
  end
101
140
 
102
- def put uri, doc = nil
141
+ def put(uri, doc = nil)
103
142
  payload = doc.to_json if doc
104
- JSON.parse(RestClient.put(uri, payload))
143
+ begin
144
+ JSON.parse(RestClient.put(uri, payload))
145
+ rescue Exception => e
146
+ if $COUCHREST_DEBUG == true
147
+ raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
148
+ else
149
+ raise e
150
+ end
151
+ end
105
152
  end
106
153
 
107
- def get uri
108
- JSON.parse(RestClient.get(uri), :max_nesting => false)
154
+ def get(uri)
155
+ begin
156
+ JSON.parse(RestClient.get(uri), :max_nesting => false)
157
+ rescue => e
158
+ if $COUCHREST_DEBUG == true
159
+ raise "Error while sending a GET request #{uri}\n: #{e}"
160
+ else
161
+ raise e
162
+ end
163
+ end
109
164
  end
110
165
 
111
166
  def post uri, doc = nil
112
167
  payload = doc.to_json if doc
113
- JSON.parse(RestClient.post(uri, payload))
168
+ begin
169
+ JSON.parse(RestClient.post(uri, payload))
170
+ rescue Exception => e
171
+ if $COUCHREST_DEBUG == true
172
+ raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
173
+ else
174
+ raise e
175
+ end
176
+ end
114
177
  end
115
178
 
116
179
  def delete uri
@@ -120,10 +183,6 @@ module CouchRest
120
183
  def copy uri, destination
121
184
  JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
122
185
  end
123
-
124
- def move uri, destination
125
- JSON.parse(RestClient.move(uri, {'Destination' => destination}))
126
- end
127
186
 
128
187
  def paramify_url url, params = {}
129
188
  if params && !params.empty?
@@ -3,7 +3,7 @@ require "base64"
3
3
 
4
4
  module CouchRest
5
5
  class Database
6
- attr_reader :server, :host, :name, :root
6
+ attr_reader :server, :host, :name, :root, :uri
7
7
  attr_accessor :bulk_save_cache_limit
8
8
 
9
9
  # Create a CouchRest::Database adapter for the supplied CouchRest::Server
@@ -13,30 +13,30 @@ module CouchRest
13
13
  # server<CouchRest::Server>:: database host
14
14
  # name<String>:: database name
15
15
  #
16
- def initialize server, name
16
+ def initialize(server, name)
17
17
  @name = name
18
18
  @server = server
19
19
  @host = server.uri
20
- @root = "#{host}/#{name}"
20
+ @uri = @root = "#{host}/#{name.gsub('/','%2F')}"
21
21
  @streamer = Streamer.new(self)
22
22
  @bulk_save_cache = []
23
- @bulk_save_cache_limit = 50
23
+ @bulk_save_cache_limit = 500 # must be smaller than the uuid count
24
24
  end
25
25
 
26
26
  # returns the database's uri
27
27
  def to_s
28
- @root
28
+ @uri
29
29
  end
30
30
 
31
31
  # GET the database info from CouchDB
32
32
  def info
33
- CouchRest.get @root
33
+ CouchRest.get @uri
34
34
  end
35
35
 
36
36
  # Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
37
- def documents params = {}
37
+ def documents(params = {})
38
38
  keys = params.delete(:keys)
39
- url = CouchRest.paramify_url "#{@root}/_all_docs", params
39
+ url = CouchRest.paramify_url "#{@uri}/_all_docs", params
40
40
  if keys
41
41
  CouchRest.post(url, {:keys => keys})
42
42
  else
@@ -47,10 +47,10 @@ module CouchRest
47
47
  # POST a temporary view function to CouchDB for querying. This is not
48
48
  # recommended, as you don't get any performance benefit from CouchDB's
49
49
  # materialized views. Can be quite slow on large databases.
50
- def slow_view funcs, params = {}
50
+ def slow_view(funcs, params = {})
51
51
  keys = params.delete(:keys)
52
52
  funcs = funcs.merge({:keys => keys}) if keys
53
- url = CouchRest.paramify_url "#{@root}/_slow_view", params
53
+ url = CouchRest.paramify_url "#{@uri}/_temp_view", params
54
54
  JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
55
55
  end
56
56
 
@@ -59,14 +59,17 @@ module CouchRest
59
59
 
60
60
  # Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
61
61
  # paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
62
- def view name, params = {}, &block
62
+ def view(name, params = {}, &block)
63
63
  keys = params.delete(:keys)
64
- url = CouchRest.paramify_url "#{@root}/_view/#{name}", params
64
+ name = name.split('/') # I think this will always be length == 2, but maybe not...
65
+ dname = name.shift
66
+ vname = name.join('/')
67
+ url = CouchRest.paramify_url "#{@uri}/_design/#{dname}/_view/#{vname}", params
65
68
  if keys
66
69
  CouchRest.post(url, {:keys => keys})
67
70
  else
68
71
  if block_given?
69
- @streamer.view(name, params, &block)
72
+ @streamer.view("_design/#{dname}/_view/#{vname}", params, &block)
70
73
  else
71
74
  CouchRest.get url
72
75
  end
@@ -74,38 +77,44 @@ module CouchRest
74
77
  end
75
78
 
76
79
  # GET a document from CouchDB, by id. Returns a Ruby Hash.
77
- def get id
80
+ def get(id, params = {})
78
81
  slug = escape_docid(id)
79
- hash = CouchRest.get("#{@root}/#{slug}")
80
- doc = if /^_design/ =~ hash["_id"]
81
- Design.new(hash)
82
+ url = CouchRest.paramify_url("#{@uri}/#{slug}", params)
83
+ result = CouchRest.get(url)
84
+ return result unless result.is_a?(Hash)
85
+ doc = if /^_design/ =~ result["_id"]
86
+ Design.new(result)
82
87
  else
83
- Document.new(hash)
88
+ Document.new(result)
84
89
  end
85
90
  doc.database = self
86
91
  doc
87
92
  end
88
93
 
89
94
  # GET an attachment directly from CouchDB
90
- def fetch_attachment docid, name
91
- slug = escape_docid(docid)
92
- name = CGI.escape(name)
93
- RestClient.get "#{@root}/#{slug}/#{name}"
95
+ def fetch_attachment(doc, name)
96
+ # slug = escape_docid(docid)
97
+ # name = CGI.escape(name)
98
+ uri = uri_for_attachment(doc, name)
99
+ RestClient.get uri
100
+ # "#{@uri}/#{slug}/#{name}"
94
101
  end
95
102
 
96
103
  # PUT an attachment directly to CouchDB
97
- def put_attachment doc, name, file, options = {}
104
+ def put_attachment(doc, name, file, options = {})
98
105
  docid = escape_docid(doc['_id'])
99
106
  name = CGI.escape(name)
100
- uri = if doc['_rev']
101
- "#{@root}/#{docid}/#{name}?rev=#{doc['_rev']}"
102
- else
103
- "#{@root}/#{docid}/#{name}"
104
- end
105
-
107
+ uri = uri_for_attachment(doc, name)
106
108
  JSON.parse(RestClient.put(uri, file, options))
107
109
  end
108
110
 
111
+ # DELETE an attachment directly from CouchDB
112
+ def delete_attachment doc, name
113
+ uri = uri_for_attachment(doc, name)
114
+ # this needs a rev
115
+ JSON.parse(RestClient.delete(uri))
116
+ end
117
+
109
118
  # Save a document to CouchDB. This will use the <tt>_id</tt> field from
110
119
  # the document as the id for PUT, or request a new UUID from CouchDB, if
111
120
  # no <tt>_id</tt> is present on the document. IDs are attached to
@@ -115,7 +124,7 @@ module CouchRest
115
124
  #
116
125
  # If <tt>bulk</tt> is true (false by default) the document is cached for bulk-saving later.
117
126
  # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
118
- def save (doc, bulk = false)
127
+ def save_doc(doc, bulk = false)
119
128
  if doc['_attachments']
120
129
  doc['_attachments'] = encode_attachments(doc['_attachments'])
121
130
  end
@@ -128,13 +137,13 @@ module CouchRest
128
137
  end
129
138
  result = if doc['_id']
130
139
  slug = escape_docid(doc['_id'])
131
- CouchRest.put "#{@root}/#{slug}", doc
140
+ CouchRest.put "#{@uri}/#{slug}", doc
132
141
  else
133
142
  begin
134
143
  slug = doc['_id'] = @server.next_uuid
135
- CouchRest.put "#{@root}/#{slug}", doc
144
+ CouchRest.put "#{@uri}/#{slug}", doc
136
145
  rescue #old version of couchdb
137
- CouchRest.post @root, doc
146
+ CouchRest.post @uri, doc
138
147
  end
139
148
  end
140
149
  if result['ok']
@@ -145,30 +154,40 @@ module CouchRest
145
154
  result
146
155
  end
147
156
 
157
+ ### DEPRECATION NOTICE
158
+ def save(doc, bulk=false)
159
+ puts "CouchRest::Database's save method is being deprecated, please use save_doc instead"
160
+ save_doc(doc, bulk)
161
+ end
162
+
163
+
148
164
  # POST an array of documents to CouchDB. If any of the documents are
149
165
  # missing ids, supply one from the uuid cache.
150
166
  #
151
167
  # If called with no arguments, bulk saves the cache of documents to be bulk saved.
152
- def bulk_save (docs = nil)
168
+ def bulk_save(docs = nil, use_uuids = true)
153
169
  if docs.nil?
154
170
  docs = @bulk_save_cache
155
171
  @bulk_save_cache = []
156
172
  end
157
- ids, noids = docs.partition{|d|d['_id']}
158
- uuid_count = [noids.length, @server.uuid_batch_count].max
159
- noids.each do |doc|
160
- nextid = @server.next_uuid(uuid_count) rescue nil
161
- doc['_id'] = nextid if nextid
173
+ if (use_uuids)
174
+ ids, noids = docs.partition{|d|d['_id']}
175
+ uuid_count = [noids.length, @server.uuid_batch_count].max
176
+ noids.each do |doc|
177
+ nextid = @server.next_uuid(uuid_count) rescue nil
178
+ doc['_id'] = nextid if nextid
179
+ end
162
180
  end
163
- CouchRest.post "#{@root}/_bulk_docs", {:docs => docs}
181
+ CouchRest.post "#{@uri}/_bulk_docs", {:docs => docs}
164
182
  end
183
+ alias :bulk_delete :bulk_save
165
184
 
166
185
  # DELETE the document from CouchDB that has the given <tt>_id</tt> and
167
186
  # <tt>_rev</tt>.
168
187
  #
169
188
  # If <tt>bulk</tt> is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later.
170
189
  # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
171
- def delete (doc, bulk = false)
190
+ def delete_doc(doc, bulk = false)
172
191
  raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
173
192
  if bulk
174
193
  @bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true }
@@ -176,13 +195,19 @@ module CouchRest
176
195
  return { "ok" => true } # Mimic the non-deferred version
177
196
  end
178
197
  slug = escape_docid(doc['_id'])
179
- CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
198
+ CouchRest.delete "#{@uri}/#{slug}?rev=#{doc['_rev']}"
199
+ end
200
+
201
+ ### DEPRECATION NOTICE
202
+ def delete(doc, bulk=false)
203
+ puts "CouchRest::Database's delete method is being deprecated, please use delete_doc instead"
204
+ delete_doc(doc, bulk)
180
205
  end
181
206
 
182
207
  # COPY an existing document to a new id. If the destination id currently exists, a rev must be provided.
183
208
  # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
184
209
  # hash with a '_rev' key
185
- def copy doc, dest
210
+ def copy_doc(doc, dest)
186
211
  raise ArgumentError, "_id is required for copying" unless doc['_id']
187
212
  slug = escape_docid(doc['_id'])
188
213
  destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
@@ -190,41 +215,75 @@ module CouchRest
190
215
  else
191
216
  dest
192
217
  end
193
- CouchRest.copy "#{@root}/#{slug}", destination
218
+ CouchRest.copy "#{@uri}/#{slug}", destination
194
219
  end
195
220
 
196
- # MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided.
197
- # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
198
- # hash with a '_rev' key
199
- def move doc, dest
200
- raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev']
201
- slug = escape_docid(doc['_id'])
202
- destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
203
- "#{dest['_id']}?rev=#{dest['_rev']}"
204
- else
205
- dest
206
- end
207
- CouchRest.move "#{@root}/#{slug}?rev=#{doc['_rev']}", destination
221
+ ### DEPRECATION NOTICE
222
+ def copy(doc, dest)
223
+ puts "CouchRest::Database's copy method is being deprecated, please use copy_doc instead"
224
+ copy_doc(doc, dest)
208
225
  end
209
226
 
210
227
  # Compact the database, removing old document revisions and optimizing space use.
211
228
  def compact!
212
- CouchRest.post "#{@root}/_compact"
229
+ CouchRest.post "#{@uri}/_compact"
213
230
  end
214
-
231
+
232
+ # Create the database
233
+ def create!
234
+ bool = server.create_db(@name) rescue false
235
+ bool && true
236
+ end
237
+
238
+ # Delete and re create the database
239
+ def recreate!
240
+ delete!
241
+ create!
242
+ rescue RestClient::ResourceNotFound
243
+ ensure
244
+ create!
245
+ end
246
+
247
+ # Replicates via "pulling" from another database to this database. Makes no attempt to deal with conflicts.
248
+ def replicate_from other_db
249
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
250
+ CouchRest.post "#{@host}/_replicate", :source => other_db.root, :target => name
251
+ end
252
+
253
+ # Replicates via "pushing" to another database. Makes no attempt to deal with conflicts.
254
+ def replicate_to other_db
255
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
256
+ CouchRest.post "#{@host}/_replicate", :target => other_db.root, :source => name
257
+ end
258
+
215
259
  # DELETE the database itself. This is not undoable and could be rather
216
260
  # catastrophic. Use with care!
217
261
  def delete!
218
- CouchRest.delete @root
262
+ CouchRest.delete @uri
219
263
  end
220
264
 
221
265
  private
222
-
266
+
267
+ def uri_for_attachment(doc, name)
268
+ if doc.is_a?(String)
269
+ puts "CouchRest::Database#fetch_attachment will eventually require a doc as the first argument, not a doc.id"
270
+ docid = doc
271
+ rev = nil
272
+ else
273
+ docid = doc['_id']
274
+ rev = doc['_rev']
275
+ end
276
+ docid = escape_docid(docid)
277
+ name = CGI.escape(name)
278
+ rev = "?rev=#{doc['_rev']}" if rev
279
+ "#{@root}/#{docid}/#{name}#{rev}"
280
+ end
281
+
223
282
  def escape_docid id
224
283
  /^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
225
284
  end
226
285
 
227
- def encode_attachments attachments
286
+ def encode_attachments(attachments)
228
287
  attachments.each do |k,v|
229
288
  next if v['stub']
230
289
  v['data'] = base64(v['data'])
@@ -232,7 +291,7 @@ module CouchRest
232
291
  attachments
233
292
  end
234
293
 
235
- def base64 data
294
+ def base64(data)
236
295
  Base64.encode64(data).gsub(/\s/,'')
237
296
  end
238
297
  end