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