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.
- data/README.md +33 -8
- data/Rakefile +11 -2
- data/examples/model/example.rb +19 -13
- data/lib/couchrest.rb +70 -11
- data/lib/couchrest/core/database.rb +121 -62
- data/lib/couchrest/core/design.rb +7 -17
- data/lib/couchrest/core/document.rb +42 -30
- data/lib/couchrest/core/response.rb +16 -0
- data/lib/couchrest/core/server.rb +47 -10
- data/lib/couchrest/helper/upgrade.rb +51 -0
- data/lib/couchrest/mixins.rb +4 -0
- data/lib/couchrest/mixins/attachments.rb +31 -0
- data/lib/couchrest/mixins/callbacks.rb +483 -0
- data/lib/couchrest/mixins/class_proxy.rb +108 -0
- data/lib/couchrest/mixins/design_doc.rb +90 -0
- data/lib/couchrest/mixins/document_queries.rb +44 -0
- data/lib/couchrest/mixins/extended_attachments.rb +68 -0
- data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
- data/lib/couchrest/mixins/properties.rb +129 -0
- data/lib/couchrest/mixins/validation.rb +242 -0
- data/lib/couchrest/mixins/views.rb +169 -0
- data/lib/couchrest/monkeypatches.rb +81 -6
- data/lib/couchrest/more/casted_model.rb +28 -0
- data/lib/couchrest/more/extended_document.rb +215 -0
- data/lib/couchrest/more/property.rb +40 -0
- data/lib/couchrest/support/blank.rb +42 -0
- data/lib/couchrest/support/class.rb +176 -0
- data/lib/couchrest/validation/auto_validate.rb +163 -0
- data/lib/couchrest/validation/contextual_validators.rb +78 -0
- data/lib/couchrest/validation/validation_errors.rb +118 -0
- data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
- data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
- data/lib/couchrest/validation/validators/format_validator.rb +117 -0
- data/lib/couchrest/validation/validators/formats/email.rb +66 -0
- data/lib/couchrest/validation/validators/formats/url.rb +43 -0
- data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
- data/lib/couchrest/validation/validators/length_validator.rb +134 -0
- data/lib/couchrest/validation/validators/method_validator.rb +89 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
- data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
- data/spec/couchrest/core/database_spec.rb +189 -124
- data/spec/couchrest/core/design_spec.rb +13 -6
- data/spec/couchrest/core/document_spec.rb +231 -177
- data/spec/couchrest/core/server_spec.rb +35 -0
- data/spec/couchrest/helpers/pager_spec.rb +1 -1
- data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
- data/spec/couchrest/more/casted_model_spec.rb +98 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
- data/spec/couchrest/more/extended_doc_spec.rb +509 -0
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
- data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
- data/spec/couchrest/more/property_spec.rb +136 -0
- data/spec/fixtures/more/article.rb +34 -0
- data/spec/fixtures/more/card.rb +20 -0
- data/spec/fixtures/more/course.rb +14 -0
- data/spec/fixtures/more/event.rb +6 -0
- data/spec/fixtures/more/invoice.rb +17 -0
- data/spec/fixtures/more/person.rb +8 -0
- data/spec/fixtures/more/question.rb +6 -0
- data/spec/fixtures/more/service.rb +12 -0
- data/spec/spec_helper.rb +13 -7
- metadata +58 -4
- data/lib/couchrest/core/model.rb +0 -613
- 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.
|
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.
|
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
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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 "
|
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']
|
data/examples/model/example.rb
CHANGED
@@ -1,31 +1,38 @@
|
|
1
|
-
require '
|
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
|
-
|
8
|
+
SERVER = CouchRest.new
|
9
|
+
SERVER.default_database = 'couchrest-extendeddoc-example'
|
10
10
|
|
11
|
-
class Author < CouchRest::
|
12
|
-
|
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::
|
19
|
-
|
20
|
-
|
21
|
-
|
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::
|
27
|
-
|
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
|
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,
|
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
|
141
|
+
def put(uri, doc = nil)
|
103
142
|
payload = doc.to_json if doc
|
104
|
-
|
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
|
108
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
@
|
28
|
+
@uri
|
29
29
|
end
|
30
30
|
|
31
31
|
# GET the database info from CouchDB
|
32
32
|
def info
|
33
|
-
CouchRest.get @
|
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
|
37
|
+
def documents(params = {})
|
38
38
|
keys = params.delete(:keys)
|
39
|
-
url = CouchRest.paramify_url "#{@
|
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
|
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 "#{@
|
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
|
62
|
+
def view(name, params = {}, &block)
|
63
63
|
keys = params.delete(:keys)
|
64
|
-
|
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(
|
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
|
80
|
+
def get(id, params = {})
|
78
81
|
slug = escape_docid(id)
|
79
|
-
|
80
|
-
|
81
|
-
|
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(
|
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
|
91
|
-
slug = escape_docid(docid)
|
92
|
-
name = CGI.escape(name)
|
93
|
-
|
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
|
104
|
+
def put_attachment(doc, name, file, options = {})
|
98
105
|
docid = escape_docid(doc['_id'])
|
99
106
|
name = CGI.escape(name)
|
100
|
-
uri =
|
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
|
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 "#{@
|
140
|
+
CouchRest.put "#{@uri}/#{slug}", doc
|
132
141
|
else
|
133
142
|
begin
|
134
143
|
slug = doc['_id'] = @server.next_uuid
|
135
|
-
CouchRest.put "#{@
|
144
|
+
CouchRest.put "#{@uri}/#{slug}", doc
|
136
145
|
rescue #old version of couchdb
|
137
|
-
CouchRest.post @
|
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
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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 "#{@
|
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
|
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 "#{@
|
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
|
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 "#{@
|
218
|
+
CouchRest.copy "#{@uri}/#{slug}", destination
|
194
219
|
end
|
195
220
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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 "#{@
|
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 @
|
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
|
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
|
294
|
+
def base64(data)
|
236
295
|
Base64.encode64(data).gsub(/\s/,'')
|
237
296
|
end
|
238
297
|
end
|