couchrest 0.30 → 0.31
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 +73 -13
- data/history.txt +14 -0
- data/lib/couchrest.rb +9 -8
- data/lib/couchrest/core/adapters/restclient.rb +35 -0
- data/lib/couchrest/core/database.rb +6 -9
- data/lib/couchrest/core/document.rb +0 -4
- data/lib/couchrest/core/http_abstraction.rb +48 -0
- data/lib/couchrest/mixins/collection.rb +5 -3
- data/lib/couchrest/mixins/design_doc.rb +0 -3
- data/lib/couchrest/mixins/document_queries.rb +1 -3
- data/lib/couchrest/mixins/properties.rb +12 -1
- data/lib/couchrest/mixins/views.rb +2 -10
- data/lib/couchrest/monkeypatches.rb +59 -59
- data/lib/couchrest/more/extended_document.rb +18 -1
- data/spec/couchrest/core/couchrest_spec.rb +1 -1
- data/spec/couchrest/core/database_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_spec.rb +16 -0
- data/spec/couchrest/more/extended_doc_view_spec.rb +5 -4
- data/spec/couchrest/more/property_spec.rb +23 -0
- data/spec/spec_helper.rb +1 -1
- metadata +3 -1
data/README.md
CHANGED
|
@@ -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 mattetti-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
|
|
78
|
+
|
|
79
|
+
view_by :date, :descending => true
|
|
80
|
+
view_by :user_id, :date
|
|
81
|
+
|
|
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']
|
|
65
100
|
|
|
101
|
+
timestamps!
|
|
66
102
|
|
|
67
|
-
|
|
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
|
|
|
@@ -97,9 +138,28 @@ If you want to cast an array of instances from a specific Class, use the trick s
|
|
|
97
138
|
|
|
98
139
|
Pagination is available in any ExtendedDocument classes. Here are some usage examples:
|
|
99
140
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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:
|
|
103
156
|
|
|
104
157
|
Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
|
|
105
|
-
:per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
|
|
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/history.txt
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
== 0.31
|
|
2
|
+
|
|
3
|
+
* Major enhancements
|
|
4
|
+
|
|
5
|
+
* Created an abstraction HTTP layer to support different http adapters (Matt Aimonetti)
|
|
6
|
+
* Added ExtendedDocument.create({}) and #create!({}) so you don't have to do Model.new.create (Matt Aimonetti)
|
|
7
|
+
|
|
8
|
+
* Minor enhancements
|
|
9
|
+
|
|
10
|
+
* Added an init.rb file for easy usage as a Rails plugin (Aaron Quint)
|
|
11
|
+
* Bug fix: pagination shouldn't die on empty results (Arnaud Berthomier)
|
|
12
|
+
* Optimized ExtendedDocument.count to run about 3x faster (Matt Aimonetti)
|
|
13
|
+
* Added Float casting (Ryan Felton & Matt Aimonetti)
|
|
14
|
+
|
|
1
15
|
== 0.30
|
|
2
16
|
|
|
3
17
|
* Major enhancements
|
data/lib/couchrest.rb
CHANGED
|
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
|
|
|
28
28
|
|
|
29
29
|
# = CouchDB, close to the metal
|
|
30
30
|
module CouchRest
|
|
31
|
-
VERSION = '0.
|
|
31
|
+
VERSION = '0.31' unless self.const_defined?("VERSION")
|
|
32
32
|
|
|
33
33
|
autoload :Server, 'couchrest/core/server'
|
|
34
34
|
autoload :Database, 'couchrest/core/database'
|
|
@@ -45,6 +45,7 @@ module CouchRest
|
|
|
45
45
|
autoload :ExtendedDocument, 'couchrest/more/extended_document'
|
|
46
46
|
autoload :CastedModel, 'couchrest/more/casted_model'
|
|
47
47
|
|
|
48
|
+
require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'http_abstraction')
|
|
48
49
|
require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
|
|
49
50
|
|
|
50
51
|
# The CouchRest module methods handle the basic JSON serialization
|
|
@@ -118,9 +119,9 @@ module CouchRest
|
|
|
118
119
|
}
|
|
119
120
|
end
|
|
120
121
|
|
|
121
|
-
# set proxy
|
|
122
|
+
# set proxy to use
|
|
122
123
|
def proxy url
|
|
123
|
-
|
|
124
|
+
HttpAbstraction.proxy = url
|
|
124
125
|
end
|
|
125
126
|
|
|
126
127
|
# ensure that a database exists
|
|
@@ -141,7 +142,7 @@ module CouchRest
|
|
|
141
142
|
def put(uri, doc = nil)
|
|
142
143
|
payload = doc.to_json if doc
|
|
143
144
|
begin
|
|
144
|
-
JSON.parse(
|
|
145
|
+
JSON.parse(HttpAbstraction.put(uri, payload))
|
|
145
146
|
rescue Exception => e
|
|
146
147
|
if $DEBUG
|
|
147
148
|
raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
@@ -153,7 +154,7 @@ module CouchRest
|
|
|
153
154
|
|
|
154
155
|
def get(uri)
|
|
155
156
|
begin
|
|
156
|
-
JSON.parse(
|
|
157
|
+
JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
|
|
157
158
|
rescue => e
|
|
158
159
|
if $DEBUG
|
|
159
160
|
raise "Error while sending a GET request #{uri}\n: #{e}"
|
|
@@ -166,7 +167,7 @@ module CouchRest
|
|
|
166
167
|
def post uri, doc = nil
|
|
167
168
|
payload = doc.to_json if doc
|
|
168
169
|
begin
|
|
169
|
-
JSON.parse(
|
|
170
|
+
JSON.parse(HttpAbstraction.post(uri, payload))
|
|
170
171
|
rescue Exception => e
|
|
171
172
|
if $DEBUG
|
|
172
173
|
raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
@@ -177,11 +178,11 @@ module CouchRest
|
|
|
177
178
|
end
|
|
178
179
|
|
|
179
180
|
def delete uri
|
|
180
|
-
JSON.parse(
|
|
181
|
+
JSON.parse(HttpAbstraction.delete(uri))
|
|
181
182
|
end
|
|
182
183
|
|
|
183
184
|
def copy uri, destination
|
|
184
|
-
JSON.parse(
|
|
185
|
+
JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
|
|
185
186
|
end
|
|
186
187
|
|
|
187
188
|
def paramify_url url, params = {}
|
|
@@ -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
|
|
@@ -58,7 +58,7 @@ module CouchRest
|
|
|
58
58
|
keys = params.delete(:keys)
|
|
59
59
|
funcs = funcs.merge({:keys => keys}) if keys
|
|
60
60
|
url = CouchRest.paramify_url "#{@root}/_temp_view", params
|
|
61
|
-
JSON.parse(
|
|
61
|
+
JSON.parse(HttpAbstraction.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# backwards compatibility is a plus
|
|
@@ -100,11 +100,8 @@ module CouchRest
|
|
|
100
100
|
|
|
101
101
|
# GET an attachment directly from CouchDB
|
|
102
102
|
def fetch_attachment(doc, name)
|
|
103
|
-
# slug = escape_docid(docid)
|
|
104
|
-
# name = CGI.escape(name)
|
|
105
103
|
uri = url_for_attachment(doc, name)
|
|
106
|
-
|
|
107
|
-
# "#{@uri}/#{slug}/#{name}"
|
|
104
|
+
HttpAbstraction.get uri
|
|
108
105
|
end
|
|
109
106
|
|
|
110
107
|
# PUT an attachment directly to CouchDB
|
|
@@ -112,14 +109,14 @@ module CouchRest
|
|
|
112
109
|
docid = escape_docid(doc['_id'])
|
|
113
110
|
name = CGI.escape(name)
|
|
114
111
|
uri = url_for_attachment(doc, name)
|
|
115
|
-
JSON.parse(
|
|
112
|
+
JSON.parse(HttpAbstraction.put(uri, file, options))
|
|
116
113
|
end
|
|
117
114
|
|
|
118
115
|
# DELETE an attachment directly from CouchDB
|
|
119
116
|
def delete_attachment doc, name
|
|
120
117
|
uri = url_for_attachment(doc, name)
|
|
121
118
|
# this needs a rev
|
|
122
|
-
JSON.parse(
|
|
119
|
+
JSON.parse(HttpAbstraction.delete(uri))
|
|
123
120
|
end
|
|
124
121
|
|
|
125
122
|
# Save a document to CouchDB. This will use the <tt>_id</tt> field from
|
|
@@ -146,7 +143,7 @@ module CouchRest
|
|
|
146
143
|
slug = escape_docid(doc['_id'])
|
|
147
144
|
begin
|
|
148
145
|
CouchRest.put "#{@root}/#{slug}", doc
|
|
149
|
-
rescue
|
|
146
|
+
rescue HttpAbstraction::ResourceNotFound
|
|
150
147
|
p "resource not found when saving even tho an id was passed"
|
|
151
148
|
slug = doc['_id'] = @server.next_uuid
|
|
152
149
|
CouchRest.put "#{@root}/#{slug}", doc
|
|
@@ -252,7 +249,7 @@ module CouchRest
|
|
|
252
249
|
def recreate!
|
|
253
250
|
delete!
|
|
254
251
|
create!
|
|
255
|
-
rescue
|
|
252
|
+
rescue HttpAbstraction::ResourceNotFound
|
|
256
253
|
ensure
|
|
257
254
|
create!
|
|
258
255
|
end
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -209,12 +209,14 @@ module CouchRest
|
|
|
209
209
|
|
|
210
210
|
def remember_where_we_left_off(results, page)
|
|
211
211
|
last_row = results['rows'].last
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
if last_row
|
|
213
|
+
@last_key = last_row['key']
|
|
214
|
+
@last_docid = last_row['id']
|
|
215
|
+
end
|
|
214
216
|
@last_page = page
|
|
215
217
|
end
|
|
216
218
|
end
|
|
217
219
|
|
|
218
220
|
end
|
|
219
221
|
end
|
|
220
|
-
end
|
|
222
|
+
end
|
|
@@ -19,9 +19,7 @@ module CouchRest
|
|
|
19
19
|
# equal to the name of the current class. Takes the standard set of
|
|
20
20
|
# CouchRest::Database#view options
|
|
21
21
|
def count(opts = {}, &block)
|
|
22
|
-
|
|
23
|
-
return 0 if result.empty?
|
|
24
|
-
result.first['value']
|
|
22
|
+
all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows']
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
# Load the first document that have the "couchrest-type" field equal to
|
|
@@ -56,7 +56,6 @@ module CouchRest
|
|
|
56
56
|
def cast_keys
|
|
57
57
|
return unless self.class.properties
|
|
58
58
|
self.class.properties.each do |property|
|
|
59
|
-
|
|
60
59
|
next unless property.casted
|
|
61
60
|
key = self.has_key?(property.name) ? property.name : property.name.to_sym
|
|
62
61
|
# Don't cast the property unless it has a value
|
|
@@ -75,6 +74,9 @@ module CouchRest
|
|
|
75
74
|
self[property.name] = if ((property.init_method == 'new') && target == 'Time')
|
|
76
75
|
# Using custom time parsing method because Ruby's default method is toooo slow
|
|
77
76
|
self[key].is_a?(String) ? Time.mktime_with_offset(self[key].dup) : self[key]
|
|
77
|
+
# Float instances don't get initialized with #new
|
|
78
|
+
elsif ((property.init_method == 'new') && target == 'Float')
|
|
79
|
+
cast_float(self[key])
|
|
78
80
|
else
|
|
79
81
|
# Let people use :send as a Time parse arg
|
|
80
82
|
klass = ::CouchRest.constantize(target)
|
|
@@ -84,6 +86,15 @@ module CouchRest
|
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
end
|
|
89
|
+
|
|
90
|
+
def cast_float(value)
|
|
91
|
+
begin
|
|
92
|
+
Float(value)
|
|
93
|
+
rescue
|
|
94
|
+
value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
87
98
|
end
|
|
88
99
|
|
|
89
100
|
module ClassMethods
|
|
@@ -72,7 +72,7 @@ module CouchRest
|
|
|
72
72
|
#
|
|
73
73
|
# To understand the capabilities of this view system more completely,
|
|
74
74
|
# it is recommended that you read the RSpec file at
|
|
75
|
-
# <tt>spec/
|
|
75
|
+
# <tt>spec/couchrest/more/extended_doc_spec.rb</tt>.
|
|
76
76
|
|
|
77
77
|
def view_by(*keys)
|
|
78
78
|
opts = keys.pop if keys.last.is_a?(Hash)
|
|
@@ -124,14 +124,6 @@ module CouchRest
|
|
|
124
124
|
# potentially large indexes.
|
|
125
125
|
def cleanup_design_docs!(db = database)
|
|
126
126
|
save_design_doc_on(db)
|
|
127
|
-
# db.refresh_design_doc
|
|
128
|
-
# db.save_design_doc
|
|
129
|
-
# design_doc = model_design_doc(db)
|
|
130
|
-
# if design_doc
|
|
131
|
-
# db.delete_doc(design_doc)
|
|
132
|
-
# else
|
|
133
|
-
# false
|
|
134
|
-
# end
|
|
135
127
|
end
|
|
136
128
|
|
|
137
129
|
private
|
|
@@ -162,7 +154,7 @@ module CouchRest
|
|
|
162
154
|
begin
|
|
163
155
|
design_doc.view_on(db, view_name, opts, &block)
|
|
164
156
|
# the design doc may not have been saved yet on this database
|
|
165
|
-
rescue
|
|
157
|
+
rescue HttpAbstraction::ResourceNotFound => e
|
|
166
158
|
if retryable
|
|
167
159
|
save_design_doc_on(db)
|
|
168
160
|
retryable = false
|
|
@@ -51,63 +51,63 @@ if RUBY_VERSION.to_f < 1.9
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
module RestClient
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# class Request
|
|
62
|
-
#
|
|
63
|
-
# def establish_connection(uri)
|
|
64
|
-
# Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
|
|
65
|
-
# p net_http_class
|
|
66
|
-
# net = net_http_class.new(uri.host, uri.port)
|
|
67
|
-
# net.use_ssl = uri.is_a?(URI::HTTPS)
|
|
68
|
-
# net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
69
|
-
# Thread.current[:connection] = net
|
|
70
|
-
# Thread.current[:connection].start
|
|
71
|
-
# Thread.current[:connection]
|
|
72
|
-
# end
|
|
73
|
-
#
|
|
74
|
-
# def transmit(uri, req, payload)
|
|
75
|
-
# setup_credentials(req)
|
|
76
|
-
#
|
|
77
|
-
# Thread.current[:host] ||= uri.host
|
|
78
|
-
# Thread.current[:port] ||= uri.port
|
|
79
|
-
#
|
|
80
|
-
# if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
|
|
81
|
-
# p "establishing a connection"
|
|
82
|
-
# establish_connection(uri)
|
|
83
|
-
# end
|
|
54
|
+
# module RestClient
|
|
55
|
+
# # def self.copy(url, headers={})
|
|
56
|
+
# # Request.execute(:method => :copy,
|
|
57
|
+
# # :url => url,
|
|
58
|
+
# # :headers => headers)
|
|
59
|
+
# # end
|
|
84
60
|
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
61
|
+
# # class Request
|
|
62
|
+
# #
|
|
63
|
+
# # def establish_connection(uri)
|
|
64
|
+
# # Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
|
|
65
|
+
# # p net_http_class
|
|
66
|
+
# # net = net_http_class.new(uri.host, uri.port)
|
|
67
|
+
# # net.use_ssl = uri.is_a?(URI::HTTPS)
|
|
68
|
+
# # net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
69
|
+
# # Thread.current[:connection] = net
|
|
70
|
+
# # Thread.current[:connection].start
|
|
71
|
+
# # Thread.current[:connection]
|
|
72
|
+
# # end
|
|
73
|
+
# #
|
|
74
|
+
# # def transmit(uri, req, payload)
|
|
75
|
+
# # setup_credentials(req)
|
|
76
|
+
# #
|
|
77
|
+
# # Thread.current[:host] ||= uri.host
|
|
78
|
+
# # Thread.current[:port] ||= uri.port
|
|
79
|
+
# #
|
|
80
|
+
# # if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
|
|
81
|
+
# # p "establishing a connection"
|
|
82
|
+
# # establish_connection(uri)
|
|
83
|
+
# # end
|
|
84
|
+
# #
|
|
85
|
+
# # display_log request_log
|
|
86
|
+
# # http = Thread.current[:connection]
|
|
87
|
+
# # http.read_timeout = @timeout if @timeout
|
|
88
|
+
# #
|
|
89
|
+
# # begin
|
|
90
|
+
# # res = http.request(req, payload)
|
|
91
|
+
# # rescue
|
|
92
|
+
# # p "Net::HTTP connection failed, reconnecting"
|
|
93
|
+
# # establish_connection(uri)
|
|
94
|
+
# # http = Thread.current[:connection]
|
|
95
|
+
# # require 'ruby-debug'
|
|
96
|
+
# # req.body_stream = nil
|
|
97
|
+
# #
|
|
98
|
+
# # res = http.request(req, payload)
|
|
99
|
+
# # display_log response_log(res)
|
|
100
|
+
# # result res
|
|
101
|
+
# # else
|
|
102
|
+
# # display_log response_log(res)
|
|
103
|
+
# # process_result res
|
|
104
|
+
# # end
|
|
105
|
+
# #
|
|
106
|
+
# # rescue EOFError
|
|
107
|
+
# # raise RestClient::ServerBrokeConnection
|
|
108
|
+
# # rescue Timeout::Error
|
|
109
|
+
# # raise RestClient::RequestTimeout
|
|
110
|
+
# # end
|
|
111
|
+
# # end
|
|
112
|
+
#
|
|
113
|
+
# end
|
|
@@ -52,8 +52,25 @@ module CouchRest
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
# Defines an instance and save it directly to the database
|
|
56
|
+
#
|
|
57
|
+
# ==== Returns
|
|
58
|
+
# returns the reloaded document
|
|
59
|
+
def self.create(options)
|
|
60
|
+
instance = new(options)
|
|
61
|
+
instance.create
|
|
62
|
+
instance
|
|
63
|
+
end
|
|
56
64
|
|
|
65
|
+
# Defines an instance and save it directly to the database
|
|
66
|
+
#
|
|
67
|
+
# ==== Returns
|
|
68
|
+
# returns the reloaded document or raises an exception
|
|
69
|
+
def self.create!(options)
|
|
70
|
+
instance = new(options)
|
|
71
|
+
instance.create!
|
|
72
|
+
instance
|
|
73
|
+
end
|
|
57
74
|
|
|
58
75
|
# Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
|
|
59
76
|
# on the document whenever saving occurs. CouchRest uses a pretty
|
|
@@ -191,7 +191,7 @@ describe CouchRest do
|
|
|
191
191
|
describe "using a proxy for RestClient connections" do
|
|
192
192
|
it "should set proxy url for RestClient" do
|
|
193
193
|
CouchRest.proxy 'http://localhost:8888/'
|
|
194
|
-
proxy_uri = URI.parse(
|
|
194
|
+
proxy_uri = URI.parse(HttpAbstraction.proxy)
|
|
195
195
|
proxy_uri.host.should eql( 'localhost' )
|
|
196
196
|
proxy_uri.port.should eql( 8888 )
|
|
197
197
|
CouchRest.proxy nil
|
|
@@ -690,7 +690,7 @@ describe CouchRest::Database do
|
|
|
690
690
|
|
|
691
691
|
it "should recreate a db even tho it doesn't exist" do
|
|
692
692
|
@cr.databases.should_not include(@db2.name)
|
|
693
|
-
@db2.recreate!
|
|
693
|
+
begin @db2.recreate! rescue nil end
|
|
694
694
|
@cr.databases.should include(@db2.name)
|
|
695
695
|
end
|
|
696
696
|
|
|
@@ -97,6 +97,22 @@ describe "ExtendedDocument" do
|
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
+
describe "creating a new document" do
|
|
101
|
+
it "should instantialize and save a document" do
|
|
102
|
+
article = Article.create(:title => 'my test')
|
|
103
|
+
article.title.should == 'my test'
|
|
104
|
+
article.should_not be_new_document
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should trigger the create callbacks" do
|
|
108
|
+
doc = WithCallBacks.create(:name => 'my other test')
|
|
109
|
+
doc.run_before_create.should be_true
|
|
110
|
+
doc.run_after_create.should be_true
|
|
111
|
+
doc.run_before_save.should be_true
|
|
112
|
+
doc.run_after_save.should be_true
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
100
116
|
describe "update attributes without saving" do
|
|
101
117
|
before(:each) do
|
|
102
118
|
a = Article.get "big-bad-danger" rescue nil
|
|
@@ -121,7 +121,7 @@ describe "ExtendedDocument views" do
|
|
|
121
121
|
describe "a model class not tied to a database" do
|
|
122
122
|
before(:all) do
|
|
123
123
|
reset_test_db!
|
|
124
|
-
@db = DB
|
|
124
|
+
@db = DB
|
|
125
125
|
%w{aaa bbb ddd eee}.each do |title|
|
|
126
126
|
u = Unattached.new(:title => title)
|
|
127
127
|
u.database = @db
|
|
@@ -133,14 +133,15 @@ describe "ExtendedDocument views" do
|
|
|
133
133
|
lambda{Unattached.all}.should raise_error
|
|
134
134
|
end
|
|
135
135
|
it "should query all" do
|
|
136
|
-
|
|
136
|
+
Unattached.cleanup_design_docs!(@db)
|
|
137
|
+
rs = Unattached.all :database => @db
|
|
137
138
|
rs.length.should == 4
|
|
138
139
|
end
|
|
139
140
|
it "should barf on query if no database given" do
|
|
140
141
|
lambda{Unattached.view :by_title}.should raise_error
|
|
141
142
|
end
|
|
142
143
|
it "should make the design doc upon first query" do
|
|
143
|
-
Unattached.by_title :database
|
|
144
|
+
Unattached.by_title :database => @db
|
|
144
145
|
doc = Unattached.design_doc
|
|
145
146
|
doc['views']['all']['map'].should include('Unattached')
|
|
146
147
|
end
|
|
@@ -157,7 +158,7 @@ describe "ExtendedDocument views" do
|
|
|
157
158
|
things = []
|
|
158
159
|
Unattached.view(:by_title, :database=>@db) do |thing|
|
|
159
160
|
things << thing
|
|
160
|
-
end
|
|
161
|
+
end
|
|
161
162
|
things[0]["doc"]["title"].should =='aaa'
|
|
162
163
|
end
|
|
163
164
|
it "should yield with by_key method" do
|
|
@@ -141,6 +141,29 @@ describe "ExtendedDocument properties" do
|
|
|
141
141
|
@event['occurs_at'].should be_an_instance_of(Time)
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
|
+
|
|
145
|
+
describe "casting to Float object" do
|
|
146
|
+
class RootBeerFloat < CouchRest::ExtendedDocument
|
|
147
|
+
use_database DB
|
|
148
|
+
property :price, :cast_as => 'Float'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should convert a string into a float if casted as so" do
|
|
152
|
+
RootBeerFloat.new(:price => '12.50').price.should == 12.50
|
|
153
|
+
RootBeerFloat.new(:price => '9').price.should == 9.0
|
|
154
|
+
RootBeerFloat.new(:price => '-9').price.should == -9.0
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should not convert a string if it's not a string that can be cast as a float" do
|
|
158
|
+
RootBeerFloat.new(:price => 'test').price.should == 'test'
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should work fine when a float is being passed" do
|
|
162
|
+
RootBeerFloat.new(:price => 9.99).price.should == 9.99
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
|
|
144
167
|
end
|
|
145
168
|
|
|
146
169
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: couchrest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.31"
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- J. Chris Anderson
|
|
@@ -62,9 +62,11 @@ files:
|
|
|
62
62
|
- examples/word_count/word_count_views.rb
|
|
63
63
|
- lib/couchrest/commands/generate.rb
|
|
64
64
|
- lib/couchrest/commands/push.rb
|
|
65
|
+
- lib/couchrest/core/adapters/restclient.rb
|
|
65
66
|
- lib/couchrest/core/database.rb
|
|
66
67
|
- lib/couchrest/core/design.rb
|
|
67
68
|
- lib/couchrest/core/document.rb
|
|
69
|
+
- lib/couchrest/core/http_abstraction.rb
|
|
68
70
|
- lib/couchrest/core/response.rb
|
|
69
71
|
- lib/couchrest/core/server.rb
|
|
70
72
|
- lib/couchrest/core/view.rb
|