couchrest 0.12.4 → 0.23

Sign up to get free protection for your applications and to get access to all the features.
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