couchrest 0.32 → 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 +1 -1
- data/history.txt +16 -1
- data/lib/couchrest.rb +6 -47
- data/lib/couchrest/core/database.rb +13 -2
- 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/collection.rb +14 -2
- data/lib/couchrest/mixins/document_queries.rb +1 -1
- data/lib/couchrest/mixins/properties.rb +15 -0
- data/lib/couchrest/more/casted_model.rb +3 -2
- data/spec/couchrest/core/couchrest_spec.rb +1 -1
- data/spec/couchrest/core/database_spec.rb +18 -4
- 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 +1 -1
- data/spec/couchrest/more/casted_model_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +1 -1
- data/spec/couchrest/more/extended_doc_view_spec.rb +4 -4
- data/spec/couchrest/more/property_spec.rb +28 -2
- metadata +3 -1
data/README.md
CHANGED
@@ -17,7 +17,7 @@ Note: CouchRest only support CouchDB 0.9.0 or newer.
|
|
17
17
|
Alternatively, you can install from Github:
|
18
18
|
|
19
19
|
$ gem sources -a http://gems.github.com (you only have to do this once)
|
20
|
-
$ sudo gem install
|
20
|
+
$ sudo gem install couchrest-couchrest
|
21
21
|
|
22
22
|
### Relax, it's RESTful
|
23
23
|
|
data/history.txt
CHANGED
@@ -1,3 +1,18 @@
|
|
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
|
+
|
1
16
|
== 0.32
|
2
17
|
|
3
18
|
* Major enhancements
|
@@ -45,4 +60,4 @@
|
|
45
60
|
---
|
46
61
|
|
47
62
|
Unfortunately, before 0.30 we did not keep a track of the modifications made to CouchRest.
|
48
|
-
You can see the full commit history on GitHub: http://github.com/
|
63
|
+
You can see the full commit history on GitHub: http://github.com/couchrest/couchrest/commits/master/
|
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.33' unless self.const_defined?("VERSION")
|
32
32
|
|
33
33
|
autoload :Server, 'couchrest/core/server'
|
34
34
|
autoload :Database, 'couchrest/core/database'
|
@@ -45,8 +45,13 @@ 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', 'rest_api')
|
48
49
|
require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'http_abstraction')
|
49
50
|
require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
|
51
|
+
|
52
|
+
# we extend CouchRest with the RestAPI module which gives us acess to
|
53
|
+
# the get, post, put, delete and copy
|
54
|
+
CouchRest.extend(::RestAPI)
|
50
55
|
|
51
56
|
# The CouchRest module methods handle the basic JSON serialization
|
52
57
|
# and deserialization, as well as query parameters. The module also includes
|
@@ -139,52 +144,6 @@ module CouchRest
|
|
139
144
|
cr.database(parsed[:database])
|
140
145
|
end
|
141
146
|
|
142
|
-
def put(uri, doc = nil)
|
143
|
-
payload = doc.to_json if doc
|
144
|
-
begin
|
145
|
-
JSON.parse(HttpAbstraction.put(uri, payload))
|
146
|
-
rescue Exception => e
|
147
|
-
if $DEBUG
|
148
|
-
raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
149
|
-
else
|
150
|
-
raise e
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def get(uri)
|
156
|
-
begin
|
157
|
-
JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
|
158
|
-
rescue => e
|
159
|
-
if $DEBUG
|
160
|
-
raise "Error while sending a GET request #{uri}\n: #{e}"
|
161
|
-
else
|
162
|
-
raise e
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def post uri, doc = nil
|
168
|
-
payload = doc.to_json if doc
|
169
|
-
begin
|
170
|
-
JSON.parse(HttpAbstraction.post(uri, payload))
|
171
|
-
rescue Exception => e
|
172
|
-
if $DEBUG
|
173
|
-
raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
174
|
-
else
|
175
|
-
raise e
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def delete uri
|
181
|
-
JSON.parse(HttpAbstraction.delete(uri))
|
182
|
-
end
|
183
|
-
|
184
|
-
def copy uri, destination
|
185
|
-
JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
|
186
|
-
end
|
187
|
-
|
188
147
|
def paramify_url url, params = {}
|
189
148
|
if params && !params.empty?
|
190
149
|
query = params.collect do |k,v|
|
@@ -113,10 +113,21 @@ module CouchRest
|
|
113
113
|
end
|
114
114
|
|
115
115
|
# DELETE an attachment directly from CouchDB
|
116
|
-
def delete_attachment
|
116
|
+
def delete_attachment(doc, name, force=false)
|
117
117
|
uri = url_for_attachment(doc, name)
|
118
118
|
# this needs a rev
|
119
|
-
|
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
|
120
131
|
end
|
121
132
|
|
122
133
|
# Save a document to CouchDB. This will use the <tt>_id</tt> field from
|
@@ -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
|
@@ -0,0 +1,263 @@
|
|
1
|
+
####################################
|
2
|
+
# USAGE
|
3
|
+
#
|
4
|
+
# in your rack.rb file
|
5
|
+
# require this file and then:
|
6
|
+
#
|
7
|
+
# couch = CouchRest.new
|
8
|
+
# LOG_DB = couch.database!('couchrest-logger')
|
9
|
+
# use CouchRest::Logger, LOG_DB
|
10
|
+
#
|
11
|
+
# Note:
|
12
|
+
# to require just this middleware, if you have the gem installed do:
|
13
|
+
# require 'couchrest/middlewares/logger'
|
14
|
+
#
|
15
|
+
# For log processing examples, see examples at the bottom of this file
|
16
|
+
|
17
|
+
module CouchRest
|
18
|
+
class Logger
|
19
|
+
|
20
|
+
def self.log
|
21
|
+
Thread.current["couchrest.logger"] ||= {:queries => []}
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(app, db=nil)
|
25
|
+
@app = app
|
26
|
+
@db = db
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.record(log_info)
|
30
|
+
log[:queries] << log_info
|
31
|
+
end
|
32
|
+
|
33
|
+
def log
|
34
|
+
Thread.current["couchrest.logger"] ||= {:queries => []}
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset_log
|
38
|
+
Thread.current["couchrest.logger"] = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(env)
|
42
|
+
reset_log
|
43
|
+
log['started_at'] = Time.now
|
44
|
+
log['env'] = env
|
45
|
+
log['url'] = 'http://' + env['HTTP_HOST'] + env['REQUEST_URI']
|
46
|
+
response = @app.call(env)
|
47
|
+
log['ended_at'] = Time.now
|
48
|
+
log['duration'] = log['ended_at'] - log['started_at']
|
49
|
+
# let's report the log in a different thread so we don't slow down the app
|
50
|
+
@db ? Thread.new(@db, log){|db, rlog| db.save_doc(rlog);} : p(log.inspect)
|
51
|
+
response
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# inject our logger into CouchRest HTTP abstraction layer
|
57
|
+
module HttpAbstraction
|
58
|
+
|
59
|
+
def self.get(uri, headers=nil)
|
60
|
+
start_query = Time.now
|
61
|
+
log = {:method => :get, :uri => uri, :headers => headers}
|
62
|
+
response = super(uri, headers=nil)
|
63
|
+
end_query = Time.now
|
64
|
+
log[:duration] = (end_query - start_query)
|
65
|
+
CouchRest::Logger.record(log)
|
66
|
+
response
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.post(uri, payload, headers=nil)
|
70
|
+
start_query = Time.now
|
71
|
+
log = {:method => :post, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
|
72
|
+
response = super(uri, payload, headers=nil)
|
73
|
+
end_query = Time.now
|
74
|
+
log[:duration] = (end_query - start_query)
|
75
|
+
CouchRest::Logger.record(log)
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.put(uri, payload, headers=nil)
|
80
|
+
start_query = Time.now
|
81
|
+
log = {:method => :put, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
|
82
|
+
response = super(uri, payload, headers=nil)
|
83
|
+
end_query = Time.now
|
84
|
+
log[:duration] = (end_query - start_query)
|
85
|
+
CouchRest::Logger.record(log)
|
86
|
+
response
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.delete(uri, headers=nil)
|
90
|
+
start_query = Time.now
|
91
|
+
log = {:method => :delete, :uri => uri, :headers => headers}
|
92
|
+
response = super(uri, headers=nil)
|
93
|
+
end_query = Time.now
|
94
|
+
log[:duration] = (end_query - start_query)
|
95
|
+
CouchRest::Logger.record(log)
|
96
|
+
response
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Advanced usage example
|
103
|
+
#
|
104
|
+
#
|
105
|
+
# # DB VIEWS
|
106
|
+
# by_url = {
|
107
|
+
# :map =>
|
108
|
+
# "function(doc) {
|
109
|
+
# if(doc['url']){ emit(doc['url'], 1) };
|
110
|
+
# }",
|
111
|
+
# :reduce =>
|
112
|
+
# 'function (key, values, rereduce) {
|
113
|
+
# return(sum(values));
|
114
|
+
# };'
|
115
|
+
# }
|
116
|
+
# req_duration = {
|
117
|
+
# :map =>
|
118
|
+
# "function(doc) {
|
119
|
+
# if(doc['duration']){ emit(doc['url'], doc['duration']) };
|
120
|
+
# }",
|
121
|
+
# :reduce =>
|
122
|
+
# 'function (key, values, rereduce) {
|
123
|
+
# return(sum(values)/values.length);
|
124
|
+
# };'
|
125
|
+
# }
|
126
|
+
#
|
127
|
+
# query_duration = {
|
128
|
+
# :map =>
|
129
|
+
# "function(doc) {
|
130
|
+
# if(doc['queries']){
|
131
|
+
# doc.queries.forEach(function(query){
|
132
|
+
# if(query['duration'] && query['method']){
|
133
|
+
# emit(query['method'], query['duration'])
|
134
|
+
# }
|
135
|
+
# });
|
136
|
+
# };
|
137
|
+
# }" ,
|
138
|
+
# :reduce =>
|
139
|
+
# 'function (key, values, rereduce) {
|
140
|
+
# return(sum(values)/values.length);
|
141
|
+
# };'
|
142
|
+
# }
|
143
|
+
#
|
144
|
+
# action_queries = {
|
145
|
+
# :map =>
|
146
|
+
# "function(doc) {
|
147
|
+
# if(doc['queries']){
|
148
|
+
# emit(doc['url'], doc['queries'].length)
|
149
|
+
# };
|
150
|
+
# }",
|
151
|
+
# :reduce =>
|
152
|
+
# 'function (key, values, rereduce) {
|
153
|
+
# return(sum(values)/values.length);
|
154
|
+
# };'
|
155
|
+
# }
|
156
|
+
#
|
157
|
+
# action_time_spent_in_db = {
|
158
|
+
# :map =>
|
159
|
+
# "function(doc) {
|
160
|
+
# if(doc['queries']){
|
161
|
+
# var totalDuration = 0;
|
162
|
+
# doc.queries.forEach(function(query){
|
163
|
+
# totalDuration += query['duration']
|
164
|
+
# })
|
165
|
+
# emit(doc['url'], totalDuration)
|
166
|
+
# };
|
167
|
+
# }",
|
168
|
+
# :reduce =>
|
169
|
+
# 'function (key, values, rereduce) {
|
170
|
+
# return(sum(values)/values.length);
|
171
|
+
# };'
|
172
|
+
# }
|
173
|
+
#
|
174
|
+
# show_queries = %Q~function(doc, req) {
|
175
|
+
# var body = ""
|
176
|
+
# body += "<h1>" + doc['url'] + "</h1>"
|
177
|
+
# body += "<h2>Request duration in seconds: " + doc['duration'] + "</h2>"
|
178
|
+
# body += "<h3>" + doc['queries'].length + " queries</h3><ul>"
|
179
|
+
# if (doc.queries){
|
180
|
+
# doc.queries.forEach(function(query){
|
181
|
+
# body += "<li>"+ query['uri'] +"</li>"
|
182
|
+
# });
|
183
|
+
# };
|
184
|
+
# body += "</ul>"
|
185
|
+
# if(doc){ return { body: body} }
|
186
|
+
# }~
|
187
|
+
#
|
188
|
+
#
|
189
|
+
# couch = CouchRest.new
|
190
|
+
# LOG_DB = couch.database!('couchrest-logger')
|
191
|
+
# design_doc = LOG_DB.get("_design/stats") rescue nil
|
192
|
+
# LOG_DB.delete_doc design_doc rescue nil
|
193
|
+
# LOG_DB.save_doc({
|
194
|
+
# "_id" => "_design/stats",
|
195
|
+
# :views => {
|
196
|
+
# :by_url => by_url,
|
197
|
+
# :request_duration => req_duration,
|
198
|
+
# :query_duration => query_duration,
|
199
|
+
# :action_queries => action_queries,
|
200
|
+
# :action_time_spent_in_db => action_time_spent_in_db
|
201
|
+
# },
|
202
|
+
# :shows => {
|
203
|
+
# :queries => show_queries
|
204
|
+
# }
|
205
|
+
# })
|
206
|
+
#
|
207
|
+
# module CouchRest
|
208
|
+
# class Logger
|
209
|
+
#
|
210
|
+
# def self.roundup(value)
|
211
|
+
# begin
|
212
|
+
# value = Float(value)
|
213
|
+
# (value * 100).round.to_f / 100
|
214
|
+
# rescue
|
215
|
+
# value
|
216
|
+
# end
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# # Usage example:
|
220
|
+
# # CouchRest::Logger.average_request_duration(LOG_DB)['rows'].first['value']
|
221
|
+
# def self.average_request_duration(db)
|
222
|
+
# raw = db.view('stats/request_duration', :reduce => true)
|
223
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
224
|
+
# end
|
225
|
+
#
|
226
|
+
# def self.average_query_duration(db)
|
227
|
+
# raw = db.view('stats/query_duration', :reduce => true)
|
228
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# def self.average_get_query_duration(db)
|
232
|
+
# raw = db.view('stats/query_duration', :key => 'get', :reduce => true)
|
233
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
234
|
+
# end
|
235
|
+
#
|
236
|
+
# def self.average_post_query_duration(db)
|
237
|
+
# raw = db.view('stats/query_duration', :key => 'post', :reduce => true)
|
238
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
239
|
+
# end
|
240
|
+
#
|
241
|
+
# def self.average_queries_per_action(db)
|
242
|
+
# raw = db.view('stats/action_queries', :reduce => true)
|
243
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# def self.average_db_time_per_action(db)
|
247
|
+
# raw = db.view('stats/action_time_spent_in_db', :reduce => true)
|
248
|
+
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# def self.stats(db)
|
252
|
+
# Thread.new(db){|db|
|
253
|
+
# puts "=== STATS ===\n"
|
254
|
+
# puts "average request duration: #{average_request_duration(db)}\n"
|
255
|
+
# puts "average query duration: #{average_query_duration(db)}\n"
|
256
|
+
# puts "average queries per action : #{average_queries_per_action(db)}\n"
|
257
|
+
# puts "average time spent in DB (per action): #{average_db_time_per_action(db)}\n"
|
258
|
+
# puts "===============\n"
|
259
|
+
# }
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# end
|
263
|
+
# end
|
@@ -19,9 +19,9 @@ module CouchRest
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# deletes an attachment directly from couchdb
|
22
|
-
def delete_attachment(name)
|
22
|
+
def delete_attachment(name, force=false)
|
23
23
|
raise ArgumentError, "doc.database required to delete_attachment" unless database
|
24
|
-
result = database.delete_attachment(self, name)
|
24
|
+
result = database.delete_attachment(self, name, force)
|
25
25
|
self['_rev'] = result['rev']
|
26
26
|
result['ok']
|
27
27
|
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module CouchRest
|
2
2
|
module Mixins
|
3
|
+
module PaginatedResults
|
4
|
+
def amount_pages
|
5
|
+
@amount_pages ||= 0
|
6
|
+
end
|
7
|
+
def amount_pages=(value)
|
8
|
+
@amount_pages = value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
module Collection
|
4
13
|
|
5
14
|
def self.included(base)
|
@@ -115,7 +124,10 @@ module CouchRest
|
|
115
124
|
results = @database.view(@view_name, pagination_options(page, per_page))
|
116
125
|
@amount_pages ||= (results['total_rows'].to_f / per_page.to_f).ceil
|
117
126
|
remember_where_we_left_off(results, page)
|
118
|
-
convert_to_container_array(results)
|
127
|
+
results = convert_to_container_array(results)
|
128
|
+
results.extend(PaginatedResults)
|
129
|
+
results.amount_pages = @amount_pages
|
130
|
+
results
|
119
131
|
end
|
120
132
|
|
121
133
|
# See Collection.paginated_each
|
@@ -181,7 +193,7 @@ module CouchRest
|
|
181
193
|
@target.inspect
|
182
194
|
end
|
183
195
|
|
184
|
-
def convert_to_container_array(results)
|
196
|
+
def convert_to_container_array(results)
|
185
197
|
if @container_class.nil?
|
186
198
|
results
|
187
199
|
else
|
@@ -12,7 +12,7 @@ module CouchRest
|
|
12
12
|
# name of the current class. Take the standard set of
|
13
13
|
# CouchRest::Database#view options.
|
14
14
|
def all(opts = {}, &block)
|
15
|
-
view(:all,
|
15
|
+
view(:all, opts, &block)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Returns the number of documents that have the "couchrest-type" field
|
@@ -77,6 +77,9 @@ module CouchRest
|
|
77
77
|
# Float instances don't get initialized with #new
|
78
78
|
elsif ((property.init_method == 'new') && target == 'Float')
|
79
79
|
cast_float(self[key])
|
80
|
+
# 'boolean' type is simply used to generate a property? accessor method
|
81
|
+
elsif ((property.init_method == 'new') && target == 'boolean')
|
82
|
+
self[key]
|
80
83
|
else
|
81
84
|
# Let people use :send as a Time parse arg
|
82
85
|
klass = ::CouchRest.constantize(target)
|
@@ -128,6 +131,18 @@ module CouchRest
|
|
128
131
|
end
|
129
132
|
EOS
|
130
133
|
|
134
|
+
if property.type == 'boolean'
|
135
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
136
|
+
def #{property.name}?
|
137
|
+
if self['#{property.name}'].nil? || self['#{property.name}'] == false || self['#{property.name}'].to_s.downcase == 'false'
|
138
|
+
false
|
139
|
+
else
|
140
|
+
true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
EOS
|
144
|
+
end
|
145
|
+
|
131
146
|
if property.alias
|
132
147
|
class_eval <<-EOS, __FILE__, __LINE__
|
133
148
|
alias #{property.alias.to_sym} #{property.name.to_sym}
|
@@ -1,10 +1,11 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../mixins/properties', __FILE__)
|
2
|
+
|
2
3
|
|
3
4
|
module CouchRest
|
4
5
|
module CastedModel
|
5
6
|
|
6
7
|
def self.included(base)
|
7
|
-
base.send(:include, CouchRest::Mixins::Properties)
|
8
|
+
base.send(:include, ::CouchRest::Mixins::Properties)
|
8
9
|
base.send(:attr_accessor, :casted_by)
|
9
10
|
end
|
10
11
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
2
2
|
|
3
3
|
describe CouchRest::Database do
|
4
4
|
before(:each) do
|
@@ -263,7 +263,11 @@ describe CouchRest::Database do
|
|
263
263
|
r['ok'].should == true
|
264
264
|
doc = @db.get("attach-this")
|
265
265
|
attachment = @db.fetch_attachment(doc,"couchdb.png")
|
266
|
-
attachment.
|
266
|
+
if attachment.respond_to?(:net_http_res)
|
267
|
+
attachment.net_http_res.body.should == image
|
268
|
+
else
|
269
|
+
attachment.should == image
|
270
|
+
end
|
267
271
|
end
|
268
272
|
end
|
269
273
|
|
@@ -368,8 +372,18 @@ describe CouchRest::Database do
|
|
368
372
|
end
|
369
373
|
it "should delete the attachment" do
|
370
374
|
lambda { @db.fetch_attachment(@doc,'test.html') }.should_not raise_error
|
371
|
-
@db.delete_attachment(@doc, "test.html")
|
372
|
-
|
375
|
+
@db.delete_attachment(@doc, "test.html")
|
376
|
+
@doc = @db.get('mydocwithattachment') # avoid getting a 409
|
377
|
+
lambda{ @db.fetch_attachment(@doc,'test.html')}.should raise_error
|
378
|
+
end
|
379
|
+
|
380
|
+
it "should force a delete even if we get a 409" do
|
381
|
+
@doc['new_attribute'] = 'something new'
|
382
|
+
@db.put_attachment(@doc, 'test', File.open(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'attachments', 'test.html')).read)
|
383
|
+
# at this point the revision number changed, if we try to save doc one more time
|
384
|
+
# we would get a 409.
|
385
|
+
lambda{ @db.save_doc(@doc) }.should raise_error
|
386
|
+
lambda{ @db.delete_attachment(@doc, "test", true) }.should_not raise_error
|
373
387
|
end
|
374
388
|
end
|
375
389
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require File.
|
3
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
4
4
|
require File.join(FIXTURE_PATH, 'more', 'card')
|
5
5
|
require File.join(FIXTURE_PATH, 'more', 'cat')
|
6
6
|
require File.join(FIXTURE_PATH, 'more', 'person')
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
2
2
|
require File.join(FIXTURE_PATH, 'more', 'article')
|
3
3
|
require File.join(FIXTURE_PATH, 'more', 'course')
|
4
4
|
|
@@ -354,7 +354,8 @@ describe "ExtendedDocument views" do
|
|
354
354
|
a = Article.new(:title => title, :date => Date.today)
|
355
355
|
a.save
|
356
356
|
end
|
357
|
-
end
|
357
|
+
end
|
358
|
+
require 'date'
|
358
359
|
it "should return a proxy that looks like an array of 7 Article objects" do
|
359
360
|
articles = Article.by_date :key => Date.today
|
360
361
|
articles.class.should == Array
|
@@ -374,8 +375,7 @@ describe "ExtendedDocument views" do
|
|
374
375
|
end
|
375
376
|
it "should have the amount of paginated pages" do
|
376
377
|
articles = Article.by_date :key => Date.today
|
377
|
-
articles.paginate(:per_page => 3)
|
378
|
-
articles.amount_pages.should == 3
|
378
|
+
articles.paginate(:per_page => 3).amount_pages.should == 3
|
379
379
|
end
|
380
380
|
it "should provide a class method to access the collection directly" do
|
381
381
|
articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
2
|
require File.join(FIXTURE_PATH, 'more', 'person')
|
3
3
|
require File.join(FIXTURE_PATH, 'more', 'card')
|
4
4
|
require File.join(FIXTURE_PATH, 'more', 'invoice')
|
@@ -161,9 +161,35 @@ describe "ExtendedDocument properties" do
|
|
161
161
|
it "should work fine when a float is being passed" do
|
162
162
|
RootBeerFloat.new(:price => 9.99).price.should == 9.99
|
163
163
|
end
|
164
|
-
|
165
164
|
end
|
166
165
|
|
166
|
+
describe "casting to a boolean value" do
|
167
|
+
class RootBeerFloat < CouchRest::ExtendedDocument
|
168
|
+
use_database DB
|
169
|
+
property :tasty, :cast_as => :boolean
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should add an accessor with a '?' for boolean attributes that returns true or false" do
|
173
|
+
RootBeerFloat.new(:tasty => true).tasty?.should == true
|
174
|
+
RootBeerFloat.new(:tasty => 'you bet').tasty?.should == true
|
175
|
+
RootBeerFloat.new(:tasty => 123).tasty?.should == true
|
176
|
+
|
177
|
+
RootBeerFloat.new(:tasty => false).tasty?.should == false
|
178
|
+
RootBeerFloat.new(:tasty => 'false').tasty?.should == false
|
179
|
+
RootBeerFloat.new(:tasty => 'FaLsE').tasty?.should == false
|
180
|
+
RootBeerFloat.new(:tasty => nil).tasty?.should == false
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should return the real value when the default accessor is used" do
|
184
|
+
RootBeerFloat.new(:tasty => true).tasty.should == true
|
185
|
+
RootBeerFloat.new(:tasty => 'you bet').tasty.should == 'you bet'
|
186
|
+
RootBeerFloat.new(:tasty => 123).tasty.should == 123
|
187
|
+
RootBeerFloat.new(:tasty => 'false').tasty.should == 'false'
|
188
|
+
RootBeerFloat.new(:tasty => false).tasty.should == false
|
189
|
+
RootBeerFloat.new(:tasty => nil).tasty.should == nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
167
193
|
end
|
168
194
|
|
169
195
|
end
|
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.33"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Chris Anderson
|
@@ -68,11 +68,13 @@ files:
|
|
68
68
|
- lib/couchrest/core/document.rb
|
69
69
|
- lib/couchrest/core/http_abstraction.rb
|
70
70
|
- lib/couchrest/core/response.rb
|
71
|
+
- lib/couchrest/core/rest_api.rb
|
71
72
|
- lib/couchrest/core/server.rb
|
72
73
|
- lib/couchrest/core/view.rb
|
73
74
|
- lib/couchrest/helper/pager.rb
|
74
75
|
- lib/couchrest/helper/streamer.rb
|
75
76
|
- lib/couchrest/helper/upgrade.rb
|
77
|
+
- lib/couchrest/middlewares/logger.rb
|
76
78
|
- lib/couchrest/mixins/attachments.rb
|
77
79
|
- lib/couchrest/mixins/callbacks.rb
|
78
80
|
- lib/couchrest/mixins/class_proxy.rb
|