peterpunk-couchrest 0.23.1 → 0.33
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 +80 -10
- data/Rakefile +2 -2
- data/history.txt +63 -0
- data/lib/couchrest/core/adapters/restclient.rb +35 -0
- data/lib/couchrest/core/database.rb +54 -29
- data/lib/couchrest/core/document.rb +1 -5
- data/lib/couchrest/core/http_abstraction.rb +48 -0
- data/lib/couchrest/core/response.rb +1 -5
- data/lib/couchrest/core/rest_api.rb +49 -0
- data/lib/couchrest/middlewares/logger.rb +263 -0
- data/lib/couchrest/mixins/attachments.rb +2 -2
- data/lib/couchrest/mixins/class_proxy.rb +9 -1
- data/lib/couchrest/mixins/collection.rb +241 -0
- data/lib/couchrest/mixins/design_doc.rb +21 -8
- data/lib/couchrest/mixins/document_queries.rb +41 -1
- data/lib/couchrest/mixins/extended_attachments.rb +7 -1
- data/lib/couchrest/mixins/extended_document_mixins.rb +1 -0
- data/lib/couchrest/mixins/properties.rb +84 -18
- data/lib/couchrest/mixins/validation.rb +30 -2
- data/lib/couchrest/mixins/views.rb +29 -21
- data/lib/couchrest/monkeypatches.rb +61 -61
- data/lib/couchrest/more/casted_model.rb +9 -5
- data/lib/couchrest/more/extended_document.rb +37 -6
- data/lib/couchrest/more/property.rb +2 -2
- data/lib/couchrest/support/rails.rb +35 -0
- data/lib/couchrest/validation/auto_validate.rb +0 -2
- data/lib/couchrest/validation/validation_errors.rb +7 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +1 -1
- data/lib/couchrest/validation/validators/uniqueness_validator.rb +15 -2
- data/lib/couchrest.rb +16 -56
- data/spec/couchrest/core/couchrest_spec.rb +2 -2
- data/spec/couchrest/core/database_spec.rb +26 -20
- data/spec/couchrest/core/design_spec.rb +1 -1
- data/spec/couchrest/core/document_spec.rb +1 -1
- data/spec/couchrest/core/server_spec.rb +1 -1
- data/spec/couchrest/helpers/pager_spec.rb +1 -1
- data/spec/couchrest/helpers/streamer_spec.rb +1 -1
- data/spec/couchrest/more/casted_extended_doc_spec.rb +43 -8
- data/spec/couchrest/more/casted_model_spec.rb +81 -2
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +8 -3
- data/spec/couchrest/more/extended_doc_spec.rb +110 -21
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_view_spec.rb +104 -33
- data/spec/couchrest/more/property_spec.rb +199 -15
- data/spec/fixtures/more/article.rb +1 -1
- data/spec/fixtures/more/card.rb +3 -1
- data/spec/fixtures/more/cat.rb +18 -0
- data/spec/fixtures/more/invoice.rb +1 -1
- data/spec/fixtures/more/location.rb +1 -1
- data/spec/fixtures/more/service.rb +1 -1
- data/spec/spec_helper.rb +15 -4
- metadata +15 -15
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# CouchRest: CouchDB, close to the metal
|
|
2
2
|
|
|
3
3
|
CouchRest is based on [CouchDB's couch.js test
|
|
4
|
-
library](http://svn.apache.org/repos/asf/
|
|
4
|
+
library](http://svn.apache.org/repos/asf/couchdb/trunk/share/www/script/couch.js),
|
|
5
5
|
which I find to be concise, clear, and well designed. CouchRest lightly wraps
|
|
6
6
|
CouchDB's HTTP API, managing JSON serialization, and remembering the URI-paths
|
|
7
7
|
to CouchDB's API endpoints so you don't have to.
|
|
@@ -12,14 +12,17 @@ Note: CouchRest only support CouchDB 0.9.0 or newer.
|
|
|
12
12
|
|
|
13
13
|
## Easy Install
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
$ sudo gem install couchrest
|
|
16
|
+
|
|
17
|
+
Alternatively, you can install from Github:
|
|
18
|
+
|
|
19
|
+
$ gem sources -a http://gems.github.com (you only have to do this once)
|
|
20
|
+
$ sudo gem install couchrest-couchrest
|
|
16
21
|
|
|
17
22
|
### Relax, it's RESTful
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
while still giving you more control than Open-URI. I recommend it anytime
|
|
22
|
-
you’re interfacing with a well-defined web service.
|
|
24
|
+
CouchRest rests on top of a HTTP abstraction layer using by default Heroku’s excellent REST Client Ruby HTTP wrapper.
|
|
25
|
+
Other adapters can be added to support more http libraries.
|
|
23
26
|
|
|
24
27
|
### Running the Specs
|
|
25
28
|
|
|
@@ -27,7 +30,7 @@ The most complete documentation is the spec/ directory. To validate your
|
|
|
27
30
|
CouchRest install, from the project root directory run `rake`, or `autotest`
|
|
28
31
|
(requires RSpec and optionally ZenTest for autotest support).
|
|
29
32
|
|
|
30
|
-
## Examples
|
|
33
|
+
## Examples (CouchRest Core)
|
|
31
34
|
|
|
32
35
|
Quick Start:
|
|
33
36
|
|
|
@@ -59,12 +62,50 @@ Creating and Querying Views:
|
|
|
59
62
|
})
|
|
60
63
|
puts @db.view('first/test')['rows'].inspect
|
|
61
64
|
|
|
62
|
-
## CouchRest::Model
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
## CouchRest::ExtendedDocument
|
|
67
|
+
|
|
68
|
+
CouchRest::ExtendedDocument is a DSL/ORM for CouchDB. Basically, ExtendedDocument seats on top of CouchRest Core to add the concept of Model.
|
|
69
|
+
ExtendedDocument offers a lot of the usual ORM tools such as optional yet defined schema, validation, callbacks, pagination, casting and much more.
|
|
70
|
+
|
|
71
|
+
### Model example
|
|
72
|
+
|
|
73
|
+
Check spec/couchrest/more and spec/fixtures/more for more examples
|
|
74
|
+
|
|
75
|
+
class Article < CouchRest::ExtendedDocument
|
|
76
|
+
use_database DB
|
|
77
|
+
unique_id :slug
|
|
65
78
|
|
|
79
|
+
view_by :date, :descending => true
|
|
80
|
+
view_by :user_id, :date
|
|
66
81
|
|
|
67
|
-
|
|
82
|
+
view_by :tags,
|
|
83
|
+
:map =>
|
|
84
|
+
"function(doc) {
|
|
85
|
+
if (doc['couchrest-type'] == 'Article' && doc.tags) {
|
|
86
|
+
doc.tags.forEach(function(tag){
|
|
87
|
+
emit(tag, 1);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}",
|
|
91
|
+
:reduce =>
|
|
92
|
+
"function(keys, values, rereduce) {
|
|
93
|
+
return sum(values);
|
|
94
|
+
}"
|
|
95
|
+
|
|
96
|
+
property :date
|
|
97
|
+
property :slug, :read_only => true
|
|
98
|
+
property :title
|
|
99
|
+
property :tags, :cast_as => ['String']
|
|
100
|
+
|
|
101
|
+
timestamps!
|
|
102
|
+
|
|
103
|
+
save_callback :before, :generate_slug_from_title
|
|
104
|
+
|
|
105
|
+
def generate_slug_from_title
|
|
106
|
+
self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new_document?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
68
109
|
|
|
69
110
|
### Callbacks
|
|
70
111
|
|
|
@@ -93,3 +134,32 @@ you can define some casting rules.
|
|
|
93
134
|
|
|
94
135
|
If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"]
|
|
95
136
|
|
|
137
|
+
### Pagination
|
|
138
|
+
|
|
139
|
+
Pagination is available in any ExtendedDocument classes. Here are some usage examples:
|
|
140
|
+
|
|
141
|
+
basic usage:
|
|
142
|
+
|
|
143
|
+
Article.all.paginate(:page => 1, :per_page => 5)
|
|
144
|
+
|
|
145
|
+
note: the above query will look like: `GET /db/_design/Article/_view/all?include_docs=true&skip=0&limit=5&reduce=false` and only fetch 5 documents.
|
|
146
|
+
|
|
147
|
+
Slightly more advance usage:
|
|
148
|
+
|
|
149
|
+
Article.by_name(:startkey => 'a', :endkey => {}).paginate(:page => 1, :per_page => 5)
|
|
150
|
+
|
|
151
|
+
note: the above query will look like: `GET /db/_design/Article/_view/by_name?startkey=%22a%22&limit=5&skip=0&endkey=%7B%7D&include_docs=true`
|
|
152
|
+
Basically, you can paginate through the articles starting by the letter a, 5 articles at a time.
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
Low level usage:
|
|
156
|
+
|
|
157
|
+
Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
|
|
158
|
+
:per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Ruby on Rails
|
|
162
|
+
|
|
163
|
+
CouchRest is compatible with rails and can even be used a Rails plugin.
|
|
164
|
+
However, you might be interested in the CouchRest companion rails project:
|
|
165
|
+
[http://github.com/hpoydar/couchrest-rails](http://github.com/hpoydar/couchrest-rails)
|
data/Rakefile
CHANGED
|
@@ -24,12 +24,11 @@ spec = Gem::Specification.new do |s|
|
|
|
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
26
|
s.authors = ["J. Chris Anderson", "Matt Aimonetti"]
|
|
27
|
-
s.files = %w( LICENSE README.md Rakefile THANKS.md ) +
|
|
27
|
+
s.files = %w( LICENSE README.md Rakefile THANKS.md history.txt) +
|
|
28
28
|
Dir["{examples,lib,spec,utils}/**/*"] -
|
|
29
29
|
Dir["spec/tmp"]
|
|
30
30
|
s.extra_rdoc_files = %w( README.md LICENSE THANKS.md )
|
|
31
31
|
s.require_path = "lib"
|
|
32
|
-
s.add_dependency("json", ">= 1.1.2")
|
|
33
32
|
s.add_dependency("rest-client", ">= 0.5")
|
|
34
33
|
s.add_dependency("mime-types", ">= 1.15")
|
|
35
34
|
end
|
|
@@ -54,6 +53,7 @@ end
|
|
|
54
53
|
|
|
55
54
|
desc "Run all specs"
|
|
56
55
|
Spec::Rake::SpecTask.new('spec') do |t|
|
|
56
|
+
t.spec_opts = ["--color"]
|
|
57
57
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
|
58
58
|
end
|
|
59
59
|
|
data/history.txt
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
== 0.33
|
|
2
|
+
|
|
3
|
+
* Major enhancements
|
|
4
|
+
|
|
5
|
+
* Added a new Rack logger middleware letting you log/save requests/queries (Matt Aimonetti)
|
|
6
|
+
|
|
7
|
+
* Minor enhancements
|
|
8
|
+
|
|
9
|
+
* Added #amount_pages to a paginated result array (Matt Aimonetti)
|
|
10
|
+
* Ruby 1.9.2 compatible (Matt Aimonetti)
|
|
11
|
+
* Added a property? method for property cast as :boolean (John Wood)
|
|
12
|
+
* Added an option to force the deletion of a attachments (bypass 409s) (Matt Aimonetti)
|
|
13
|
+
* Created a new abstraction layer for the REST API (Matt Aimonetti)
|
|
14
|
+
* Bug fix: made ExtendedDocument#all compatible with Couch 0.10 (tc)
|
|
15
|
+
|
|
16
|
+
== 0.32
|
|
17
|
+
|
|
18
|
+
* Major enhancements
|
|
19
|
+
|
|
20
|
+
* ExtendedDocument.get doesn't raise an exception anymore. If no documents are found nil is returned.
|
|
21
|
+
* ExtendedDocument.get! works the say #get used to work and will raise an exception if a document isn't found.
|
|
22
|
+
|
|
23
|
+
* Minor enhancements
|
|
24
|
+
|
|
25
|
+
* Bug fix: Model.all(:keys => [1,2]) was not working (Matt Aimonetti)
|
|
26
|
+
* Added ValidationErrors#count in order to play nicely with Rails (Peter Wagenet)
|
|
27
|
+
* Bug fix: class proxy design doc refresh (Daniel Kirsh)
|
|
28
|
+
* Bug fix: the count method on the proxy collection was missing (Daniel Kirsch)
|
|
29
|
+
* Added #amount_pages to a paginated collection. (Matt Aimonetti)
|
|
30
|
+
|
|
31
|
+
== 0.31
|
|
32
|
+
|
|
33
|
+
* Major enhancements
|
|
34
|
+
|
|
35
|
+
* Created an abstraction HTTP layer to support different http adapters (Matt Aimonetti)
|
|
36
|
+
* Added ExtendedDocument.create({}) and #create!({}) so you don't have to do Model.new.create (Matt Aimonetti)
|
|
37
|
+
|
|
38
|
+
* Minor enhancements
|
|
39
|
+
|
|
40
|
+
* Added an init.rb file for easy usage as a Rails plugin (Aaron Quint)
|
|
41
|
+
* Bug fix: pagination shouldn't die on empty results (Arnaud Berthomier)
|
|
42
|
+
* Optimized ExtendedDocument.count to run about 3x faster (Matt Aimonetti)
|
|
43
|
+
* Added Float casting (Ryan Felton & Matt Aimonetti)
|
|
44
|
+
|
|
45
|
+
== 0.30
|
|
46
|
+
|
|
47
|
+
* Major enhancements
|
|
48
|
+
|
|
49
|
+
* Added support for pagination (John Wood)
|
|
50
|
+
* Improved performance when initializing documents with timestamps (Matt Aimonetti)
|
|
51
|
+
|
|
52
|
+
* Minor enhancements
|
|
53
|
+
|
|
54
|
+
* Extended the API to retrieve an attachment URI (Matt Aimonetti)
|
|
55
|
+
* Bug fix: default value should be able to be set as false (Alexander Uvarov)
|
|
56
|
+
* Bug fix: validates_is_numeric should be able to properly validate a Float instance (Rob Kaufman)
|
|
57
|
+
* Bug fix: fixed the Timeout implementation (Seth Falcon)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
Unfortunately, before 0.30 we did not keep a track of the modifications made to CouchRest.
|
|
63
|
+
You can see the full commit history on GitHub: http://github.com/couchrest/couchrest/commits/master/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module RestClientAdapter
|
|
2
|
+
|
|
3
|
+
module API
|
|
4
|
+
def proxy=(url)
|
|
5
|
+
RestClient.proxy = url
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def proxy
|
|
9
|
+
RestClient.proxy
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def get(uri, headers={})
|
|
13
|
+
RestClient.get(uri, headers)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def post(uri, payload, headers={})
|
|
17
|
+
RestClient.post(uri, payload, headers)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def put(uri, payload, headers={})
|
|
21
|
+
RestClient.put(uri, payload, headers)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete(uri, headers={})
|
|
25
|
+
RestClient.delete(uri, headers)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def copy(uri, headers)
|
|
29
|
+
RestClient::Request.execute( :method => :copy,
|
|
30
|
+
:url => uri,
|
|
31
|
+
:headers => headers)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -17,7 +17,8 @@ module CouchRest
|
|
|
17
17
|
@name = name
|
|
18
18
|
@server = server
|
|
19
19
|
@host = server.uri
|
|
20
|
-
@uri
|
|
20
|
+
@uri = "/#{name.gsub('/','%2F')}"
|
|
21
|
+
@root = host + uri
|
|
21
22
|
@streamer = Streamer.new(self)
|
|
22
23
|
@bulk_save_cache = []
|
|
23
24
|
@bulk_save_cache_limit = 500 # must be smaller than the uuid count
|
|
@@ -25,18 +26,18 @@ module CouchRest
|
|
|
25
26
|
|
|
26
27
|
# returns the database's uri
|
|
27
28
|
def to_s
|
|
28
|
-
@
|
|
29
|
+
@root
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
# GET the database info from CouchDB
|
|
32
33
|
def info
|
|
33
|
-
CouchRest.get @
|
|
34
|
+
CouchRest.get @root
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
# Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
|
|
37
38
|
def documents(params = {})
|
|
38
39
|
keys = params.delete(:keys)
|
|
39
|
-
url = CouchRest.paramify_url "#{@
|
|
40
|
+
url = CouchRest.paramify_url "#{@root}/_all_docs", params
|
|
40
41
|
if keys
|
|
41
42
|
CouchRest.post(url, {:keys => keys})
|
|
42
43
|
else
|
|
@@ -48,6 +49,7 @@ module CouchRest
|
|
|
48
49
|
def get_bulk(ids)
|
|
49
50
|
documents(:keys => ids, :include_docs => true)
|
|
50
51
|
end
|
|
52
|
+
alias :bulk_load :get_bulk
|
|
51
53
|
|
|
52
54
|
# POST a temporary view function to CouchDB for querying. This is not
|
|
53
55
|
# recommended, as you don't get any performance benefit from CouchDB's
|
|
@@ -55,8 +57,8 @@ module CouchRest
|
|
|
55
57
|
def slow_view(funcs, params = {})
|
|
56
58
|
keys = params.delete(:keys)
|
|
57
59
|
funcs = funcs.merge({:keys => keys}) if keys
|
|
58
|
-
url = CouchRest.paramify_url "#{@
|
|
59
|
-
JSON.parse(
|
|
60
|
+
url = CouchRest.paramify_url "#{@root}/_temp_view", params
|
|
61
|
+
JSON.parse(HttpAbstraction.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
# backwards compatibility is a plus
|
|
@@ -69,7 +71,7 @@ module CouchRest
|
|
|
69
71
|
name = name.split('/') # I think this will always be length == 2, but maybe not...
|
|
70
72
|
dname = name.shift
|
|
71
73
|
vname = name.join('/')
|
|
72
|
-
url = CouchRest.paramify_url "#{@
|
|
74
|
+
url = CouchRest.paramify_url "#{@root}/_design/#{dname}/_view/#{vname}", params
|
|
73
75
|
if keys
|
|
74
76
|
CouchRest.post(url, {:keys => keys})
|
|
75
77
|
else
|
|
@@ -84,7 +86,7 @@ module CouchRest
|
|
|
84
86
|
# GET a document from CouchDB, by id. Returns a Ruby Hash.
|
|
85
87
|
def get(id, params = {})
|
|
86
88
|
slug = escape_docid(id)
|
|
87
|
-
url = CouchRest.paramify_url("#{@
|
|
89
|
+
url = CouchRest.paramify_url("#{@root}/#{slug}", params)
|
|
88
90
|
result = CouchRest.get(url)
|
|
89
91
|
return result unless result.is_a?(Hash)
|
|
90
92
|
doc = if /^_design/ =~ result["_id"]
|
|
@@ -98,26 +100,34 @@ module CouchRest
|
|
|
98
100
|
|
|
99
101
|
# GET an attachment directly from CouchDB
|
|
100
102
|
def fetch_attachment(doc, name)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
uri = uri_for_attachment(doc, name)
|
|
104
|
-
RestClient.get uri
|
|
105
|
-
# "#{@uri}/#{slug}/#{name}"
|
|
103
|
+
uri = url_for_attachment(doc, name)
|
|
104
|
+
HttpAbstraction.get uri
|
|
106
105
|
end
|
|
107
106
|
|
|
108
107
|
# PUT an attachment directly to CouchDB
|
|
109
108
|
def put_attachment(doc, name, file, options = {})
|
|
110
109
|
docid = escape_docid(doc['_id'])
|
|
111
110
|
name = CGI.escape(name)
|
|
112
|
-
uri =
|
|
113
|
-
JSON.parse(
|
|
111
|
+
uri = url_for_attachment(doc, name)
|
|
112
|
+
JSON.parse(HttpAbstraction.put(uri, file, options))
|
|
114
113
|
end
|
|
115
114
|
|
|
116
115
|
# DELETE an attachment directly from CouchDB
|
|
117
|
-
def delete_attachment
|
|
118
|
-
uri =
|
|
116
|
+
def delete_attachment(doc, name, force=false)
|
|
117
|
+
uri = url_for_attachment(doc, name)
|
|
119
118
|
# this needs a rev
|
|
120
|
-
|
|
119
|
+
begin
|
|
120
|
+
JSON.parse(HttpAbstraction.delete(uri))
|
|
121
|
+
rescue Exception => error
|
|
122
|
+
if force
|
|
123
|
+
# get over a 409
|
|
124
|
+
doc = get(doc['_id'])
|
|
125
|
+
uri = url_for_attachment(doc, name)
|
|
126
|
+
JSON.parse(HttpAbstraction.delete(uri))
|
|
127
|
+
else
|
|
128
|
+
error
|
|
129
|
+
end
|
|
130
|
+
end
|
|
121
131
|
end
|
|
122
132
|
|
|
123
133
|
# Save a document to CouchDB. This will use the <tt>_id</tt> field from
|
|
@@ -141,14 +151,20 @@ module CouchRest
|
|
|
141
151
|
bulk_save
|
|
142
152
|
end
|
|
143
153
|
result = if doc['_id']
|
|
144
|
-
slug = escape_docid(doc['_id'])
|
|
145
|
-
|
|
154
|
+
slug = escape_docid(doc['_id'])
|
|
155
|
+
begin
|
|
156
|
+
CouchRest.put "#{@root}/#{slug}", doc
|
|
157
|
+
rescue HttpAbstraction::ResourceNotFound
|
|
158
|
+
p "resource not found when saving even tho an id was passed"
|
|
159
|
+
slug = doc['_id'] = @server.next_uuid
|
|
160
|
+
CouchRest.put "#{@root}/#{slug}", doc
|
|
161
|
+
end
|
|
146
162
|
else
|
|
147
163
|
begin
|
|
148
164
|
slug = doc['_id'] = @server.next_uuid
|
|
149
|
-
CouchRest.put "#{@
|
|
165
|
+
CouchRest.put "#{@root}/#{slug}", doc
|
|
150
166
|
rescue #old version of couchdb
|
|
151
|
-
CouchRest.post @
|
|
167
|
+
CouchRest.post @root, doc
|
|
152
168
|
end
|
|
153
169
|
end
|
|
154
170
|
if result['ok']
|
|
@@ -183,7 +199,7 @@ module CouchRest
|
|
|
183
199
|
doc['_id'] = nextid if nextid
|
|
184
200
|
end
|
|
185
201
|
end
|
|
186
|
-
CouchRest.post "#{@
|
|
202
|
+
CouchRest.post "#{@root}/_bulk_docs", {:docs => docs}
|
|
187
203
|
end
|
|
188
204
|
alias :bulk_delete :bulk_save
|
|
189
205
|
|
|
@@ -200,7 +216,7 @@ module CouchRest
|
|
|
200
216
|
return { "ok" => true } # Mimic the non-deferred version
|
|
201
217
|
end
|
|
202
218
|
slug = escape_docid(doc['_id'])
|
|
203
|
-
CouchRest.delete "#{@
|
|
219
|
+
CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
|
|
204
220
|
end
|
|
205
221
|
|
|
206
222
|
### DEPRECATION NOTICE
|
|
@@ -220,7 +236,7 @@ module CouchRest
|
|
|
220
236
|
else
|
|
221
237
|
dest
|
|
222
238
|
end
|
|
223
|
-
CouchRest.copy "#{@
|
|
239
|
+
CouchRest.copy "#{@root}/#{slug}", destination
|
|
224
240
|
end
|
|
225
241
|
|
|
226
242
|
### DEPRECATION NOTICE
|
|
@@ -231,7 +247,7 @@ module CouchRest
|
|
|
231
247
|
|
|
232
248
|
# Compact the database, removing old document revisions and optimizing space use.
|
|
233
249
|
def compact!
|
|
234
|
-
CouchRest.post "#{@
|
|
250
|
+
CouchRest.post "#{@root}/_compact"
|
|
235
251
|
end
|
|
236
252
|
|
|
237
253
|
# Create the database
|
|
@@ -244,7 +260,7 @@ module CouchRest
|
|
|
244
260
|
def recreate!
|
|
245
261
|
delete!
|
|
246
262
|
create!
|
|
247
|
-
rescue
|
|
263
|
+
rescue HttpAbstraction::ResourceNotFound
|
|
248
264
|
ensure
|
|
249
265
|
create!
|
|
250
266
|
end
|
|
@@ -264,11 +280,16 @@ module CouchRest
|
|
|
264
280
|
# DELETE the database itself. This is not undoable and could be rather
|
|
265
281
|
# catastrophic. Use with care!
|
|
266
282
|
def delete!
|
|
267
|
-
|
|
283
|
+
clear_extended_doc_fresh_cache
|
|
284
|
+
CouchRest.delete @root
|
|
268
285
|
end
|
|
269
286
|
|
|
270
287
|
private
|
|
271
288
|
|
|
289
|
+
def clear_extended_doc_fresh_cache
|
|
290
|
+
::CouchRest::ExtendedDocument.subclasses.each{|klass| klass.design_doc_fresh = false if klass.respond_to?(:design_doc_fresh=) }
|
|
291
|
+
end
|
|
292
|
+
|
|
272
293
|
def uri_for_attachment(doc, name)
|
|
273
294
|
if doc.is_a?(String)
|
|
274
295
|
puts "CouchRest::Database#fetch_attachment will eventually require a doc as the first argument, not a doc.id"
|
|
@@ -281,7 +302,11 @@ module CouchRest
|
|
|
281
302
|
docid = escape_docid(docid)
|
|
282
303
|
name = CGI.escape(name)
|
|
283
304
|
rev = "?rev=#{doc['_rev']}" if rev
|
|
284
|
-
"
|
|
305
|
+
"/#{docid}/#{name}#{rev}"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def url_for_attachment(doc, name)
|
|
309
|
+
@root + uri_for_attachment(doc, name)
|
|
285
310
|
end
|
|
286
311
|
|
|
287
312
|
def escape_docid id
|
|
@@ -3,10 +3,6 @@ require 'delegate'
|
|
|
3
3
|
module CouchRest
|
|
4
4
|
class Document < Response
|
|
5
5
|
include CouchRest::Mixins::Attachments
|
|
6
|
-
|
|
7
|
-
# def self.inherited(subklass)
|
|
8
|
-
# subklass.send(:extlib_inheritable_accessor, :database)
|
|
9
|
-
# end
|
|
10
6
|
|
|
11
7
|
extlib_inheritable_accessor :database
|
|
12
8
|
attr_accessor :database
|
|
@@ -68,7 +64,7 @@ module CouchRest
|
|
|
68
64
|
# Returns the CouchDB uri for the document
|
|
69
65
|
def uri(append_rev = false)
|
|
70
66
|
return nil if new_document?
|
|
71
|
-
couch_uri = "http://#{database.
|
|
67
|
+
couch_uri = "http://#{database.root}/#{CGI.escape(id)}"
|
|
72
68
|
if append_rev == true
|
|
73
69
|
couch_uri << "?rev=#{rev}"
|
|
74
70
|
elsif append_rev.kind_of?(Integer)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'couchrest/core/adapters/restclient'
|
|
2
|
+
|
|
3
|
+
# Abstraction layet for HTTP communications.
|
|
4
|
+
#
|
|
5
|
+
# By defining a basic API that CouchRest is relying on,
|
|
6
|
+
# it allows for easy experimentations and implementations of various libraries.
|
|
7
|
+
#
|
|
8
|
+
# Most of the API is based on the RestClient API that was used in the early version of CouchRest.
|
|
9
|
+
#
|
|
10
|
+
module HttpAbstraction
|
|
11
|
+
|
|
12
|
+
# here is the list of exception expected by CouchRest
|
|
13
|
+
# please convert the underlying errors in this set of known
|
|
14
|
+
# exceptions.
|
|
15
|
+
class ResourceNotFound < StandardError; end
|
|
16
|
+
class RequestFailed < StandardError; end
|
|
17
|
+
class RequestTimeout < StandardError; end
|
|
18
|
+
class ServerBrokeConnection < StandardError; end
|
|
19
|
+
class Conflict < StandardError; end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# # Here is the API you need to implement if you want to write a new adapter
|
|
23
|
+
# # See adapters/restclient.rb for more information.
|
|
24
|
+
#
|
|
25
|
+
# def self.proxy=(url)
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# def self.proxy
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# def self.get(uri, headers=nil)
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# def self.post(uri, payload, headers=nil)
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# def self.put(uri, payload, headers=nil)
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# def self.delete(uri, headers=nil)
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# def self.copy(uri, headers)
|
|
44
|
+
# end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
HttpAbstraction.extend(RestClientAdapter::API)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module RestAPI
|
|
2
|
+
|
|
3
|
+
def put(uri, doc = nil)
|
|
4
|
+
payload = doc.to_json if doc
|
|
5
|
+
begin
|
|
6
|
+
JSON.parse(HttpAbstraction.put(uri, payload))
|
|
7
|
+
rescue Exception => e
|
|
8
|
+
if $DEBUG
|
|
9
|
+
raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
10
|
+
else
|
|
11
|
+
raise e
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(uri)
|
|
17
|
+
begin
|
|
18
|
+
JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
|
|
19
|
+
rescue => e
|
|
20
|
+
if $DEBUG
|
|
21
|
+
raise "Error while sending a GET request #{uri}\n: #{e}"
|
|
22
|
+
else
|
|
23
|
+
raise e
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def post(uri, doc = nil)
|
|
29
|
+
payload = doc.to_json if doc
|
|
30
|
+
begin
|
|
31
|
+
JSON.parse(HttpAbstraction.post(uri, payload))
|
|
32
|
+
rescue Exception => e
|
|
33
|
+
if $DEBUG
|
|
34
|
+
raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
35
|
+
else
|
|
36
|
+
raise e
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete(uri)
|
|
42
|
+
JSON.parse(HttpAbstraction.delete(uri))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def copy(uri, destination)
|
|
46
|
+
JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|