sporkd-couchrest 0.31 → 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 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 mattetti-couchrest
20
+ $ sudo gem install couchrest-couchrest
21
21
 
22
22
  ### Relax, it's RESTful
23
23
 
data/history.txt CHANGED
@@ -1,18 +1,31 @@
1
- == 0.40
2
-
3
- === Notes
4
-
5
- This release slightly modifies the API and if you were using a previous version of CouchRest,
6
- you will need to make the following modifications.
7
-
1
+ == 0.33
8
2
 
3
+ * Major enhancements
9
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
+
10
18
  * Major enhancements
11
19
 
12
- *
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.
13
22
 
14
23
  * Minor enhancements
15
-
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)
16
29
  * Added #amount_pages to a paginated collection. (Matt Aimonetti)
17
30
 
18
31
  == 0.31
@@ -47,4 +60,4 @@ you will need to make the following modifications.
47
60
  ---
48
61
 
49
62
  Unfortunately, before 0.30 we did not keep a track of the modifications made to CouchRest.
50
- You can see the full commit history on GitHub: http://github.com/mattetti/couchrest/commits/master/
63
+ You can see the full commit history on GitHub: http://github.com/couchrest/couchrest/commits/master/
@@ -113,10 +113,21 @@ module CouchRest
113
113
  end
114
114
 
115
115
  # DELETE an attachment directly from CouchDB
116
- def delete_attachment doc, name
116
+ def delete_attachment(doc, name, force=false)
117
117
  uri = url_for_attachment(doc, name)
118
118
  # this needs a rev
119
- JSON.parse(HttpAbstraction.delete(uri))
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
@@ -59,6 +59,10 @@ module CouchRest
59
59
  @klass.all({:database => @database}.merge(opts), &block)
60
60
  end
61
61
 
62
+ def count(opts = {}, &block)
63
+ @klass.all({:database => @database, :raw => true, :limit => 0}.merge(opts), &block)['total_rows']
64
+ end
65
+
62
66
  def first(opts = {})
63
67
  @klass.first({:database => @database}.merge(opts))
64
68
  end
@@ -100,7 +104,7 @@ module CouchRest
100
104
  end
101
105
 
102
106
  def refresh_design_doc
103
- @klass.refresh_design_doc
107
+ @klass.refresh_design_doc_on(@database)
104
108
  end
105
109
 
106
110
  def save_design_doc
@@ -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
@@ -35,7 +35,7 @@ module CouchRest
35
35
  'all' => {
36
36
  'map' => "function(doc) {
37
37
  if (doc['couchrest-type'] == '#{self.to_s}') {
38
- emit(null,1);
38
+ emit(doc['_id'],1);
39
39
  }
40
40
  }"
41
41
  }
@@ -48,6 +48,11 @@ module CouchRest
48
48
  save_design_doc
49
49
  end
50
50
 
51
+ def refresh_design_doc_on(db)
52
+ reset_design_doc
53
+ save_design_doc_on(db)
54
+ end
55
+
51
56
  # Save the design doc onto the default database, and update the
52
57
  # design_doc attribute
53
58
  def save_design_doc
@@ -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, {:reduce => false}.merge(opts), &block)
15
+ view(:all, opts, &block)
16
16
  end
17
17
 
18
18
  # Returns the number of documents that have the "couchrest-type" field
@@ -39,7 +39,38 @@ module CouchRest
39
39
  end
40
40
 
41
41
  # Load a document from the database by id
42
+ # No exceptions will be raised if the document isn't found
43
+ #
44
+ # ==== Returns
45
+ # Object:: if the document was found
46
+ # or
47
+ # Nil::
48
+ #
49
+ # === Parameters
50
+ # id<String, Integer>:: Document ID
51
+ # db<Database>:: optional option to pass a custom database to use
42
52
  def get(id, db = database)
53
+ begin
54
+ doc = db.get id
55
+ rescue
56
+ nil
57
+ else
58
+ new(doc)
59
+ end
60
+ end
61
+
62
+ # Load a document from the database by id
63
+ # An exception will be raised if the document isn't found
64
+ #
65
+ # ==== Returns
66
+ # Object:: if the document was found
67
+ # or
68
+ # Exception
69
+ #
70
+ # === Parameters
71
+ # id<String, Integer>:: Document ID
72
+ # db<Database>:: optional option to pass a custom database to use
73
+ def get!(id, db = database)
43
74
  doc = db.get id
44
75
  new(doc)
45
76
  end
@@ -65,9 +65,8 @@ module CouchRest
65
65
  key = self.has_key?(property.name) ? property.name : property.name.to_sym
66
66
  # Don't cast the property unless it has a value
67
67
  return unless self[key]
68
- target = property.type
69
- if target.is_a?(Array)
70
- klass = ::CouchRest.constantize(target[0])
68
+ if property.type.is_a?(Array)
69
+ klass = ::CouchRest.constantize(property.type[0])
71
70
  arr = self[key].dup.collect do |value|
72
71
  unless value.instance_of?(klass)
73
72
  value = convert_property_value(property, klass, value)
@@ -78,7 +77,12 @@ module CouchRest
78
77
  self[key] = klass != String ? CastedArray.new(arr) : arr
79
78
  self[key].casted_by = self if self[key].respond_to?(:casted_by)
80
79
  else
81
- klass = ::CouchRest.constantize(target)
80
+ if property.type == 'boolean'
81
+ klass = TrueClass
82
+ else
83
+ klass = ::CouchRest.constantize(property.type)
84
+ end
85
+
82
86
  unless self[key].instance_of?(klass)
83
87
  self[key] = convert_property_value(property, klass, self[property.name])
84
88
  end
@@ -93,12 +97,15 @@ module CouchRest
93
97
  end
94
98
 
95
99
  def convert_property_value(property, klass, value)
96
- if ((property.init_method == 'new') && klass.to_s == 'Time')
100
+ if ((property.init_method == 'new') && klass == Time)
97
101
  # Using custom time parsing method because Ruby's default method is toooo slow
98
102
  value.is_a?(String) ? Time.mktime_with_offset(value.dup) : value
99
103
  # Float instances don't get initialized with #new
100
- elsif ((property.init_method == 'new') && klass.to_s == 'Float')
104
+ elsif ((property.init_method == 'new') && klass == Float)
101
105
  cast_float(value)
106
+ # 'boolean' type is simply used to generate a property? accessor method
107
+ elsif ((property.init_method == 'new') && klass == TrueClass)
108
+ value
102
109
  else
103
110
  klass.send(property.init_method, value.dup)
104
111
  end
@@ -150,6 +157,18 @@ module CouchRest
150
157
  end
151
158
  EOS
152
159
 
160
+ if property.type == 'boolean'
161
+ class_eval <<-EOS, __FILE__, __LINE__
162
+ def #{property.name}?
163
+ if self['#{property.name}'].nil? || self['#{property.name}'] == false || self['#{property.name}'].to_s.downcase == 'false'
164
+ false
165
+ else
166
+ true
167
+ end
168
+ end
169
+ EOS
170
+ end
171
+
153
172
  if property.alias
154
173
  class_eval <<-EOS, __FILE__, __LINE__ + 1
155
174
  alias #{property.alias.to_sym} #{property.name.to_sym}
@@ -95,11 +95,11 @@ module CouchRest
95
95
 
96
96
  # Dispatches to any named view.
97
97
  def view(name, query={}, &block)
98
- unless design_doc_fresh
99
- refresh_design_doc
98
+ db = query.delete(:database) || database
99
+ unless design_doc_fresh
100
+ refresh_design_doc_on(db)
100
101
  end
101
102
  query[:raw] = true if query[:reduce]
102
- db = query.delete(:database) || database
103
103
  raw = query.delete(:raw)
104
104
  fetch_view_with_docs(db, name, query, raw, &block)
105
105
  end
@@ -1,11 +1,12 @@
1
- require File.join(File.dirname(__FILE__), '..', 'mixins', 'properties')
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::Callbacks)
8
- base.send(:include, CouchRest::Mixins::Properties)
8
+ base.send(:include, ::CouchRest::Callbacks)
9
+ base.send(:include, ::CouchRest::Mixins::Properties)
9
10
  base.send(:attr_accessor, :casted_by)
10
11
  base.send(:attr_accessor, :document_saved)
11
12
  end
@@ -104,6 +104,13 @@ module CouchRest
104
104
  entries.empty?
105
105
  end
106
106
 
107
+ # Return size of errors hash
108
+ #
109
+ # Allows us to play nicely with Rails' helpers
110
+ def count
111
+ errors.size
112
+ end
113
+
107
114
  def method_missing(meth, *args, &block)
108
115
  errors.send(meth, *args, &block)
109
116
  end
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' unless self.const_defined?("VERSION")
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,9 +45,14 @@ 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')
50
51
  require File.join(File.dirname(__FILE__), 'couchrest', 'support', 'rails') if defined?(Rails)
52
+
53
+ # we extend CouchRest with the RestAPI module which gives us acess to
54
+ # the get, post, put, delete and copy
55
+ CouchRest.extend(::RestAPI)
51
56
 
52
57
  # The CouchRest module methods handle the basic JSON serialization
53
58
  # and deserialization, as well as query parameters. The module also includes
@@ -140,52 +145,6 @@ module CouchRest
140
145
  cr.database(parsed[:database])
141
146
  end
142
147
 
143
- def put(uri, doc = nil)
144
- payload = doc.to_json if doc
145
- begin
146
- JSON.parse(HttpAbstraction.put(uri, payload))
147
- rescue Exception => e
148
- if $DEBUG
149
- raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
150
- else
151
- raise e
152
- end
153
- end
154
- end
155
-
156
- def get(uri)
157
- begin
158
- JSON.parse(HttpAbstraction.get(uri), :max_nesting => false)
159
- rescue => e
160
- if $DEBUG
161
- raise "Error while sending a GET request #{uri}\n: #{e}"
162
- else
163
- raise e
164
- end
165
- end
166
- end
167
-
168
- def post uri, doc = nil
169
- payload = doc.to_json if doc
170
- begin
171
- JSON.parse(HttpAbstraction.post(uri, payload))
172
- rescue Exception => e
173
- if $DEBUG
174
- raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
175
- else
176
- raise e
177
- end
178
- end
179
- end
180
-
181
- def delete uri
182
- JSON.parse(HttpAbstraction.delete(uri))
183
- end
184
-
185
- def copy uri, destination
186
- JSON.parse(HttpAbstraction.copy(uri, {'Destination' => destination}))
187
- end
188
-
189
148
  def paramify_url url, params = {}
190
149
  if params && !params.empty?
191
150
  query = params.collect do |k,v|
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  describe CouchRest do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
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.should == image
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
- lambda { @db.fetch_attachment(@doc,'test.html') }.should raise_error(RestClient::ResourceNotFound)
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,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  describe CouchRest::Design do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  class Video < CouchRest::Document; end
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  describe CouchRest::Server do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  describe CouchRest::Pager do
4
4
  before(:all) do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
 
3
3
  describe CouchRest::Streamer do
4
4
  before(:all) do
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
2
  require File.join(FIXTURE_PATH, 'more', 'card')
3
3
 
4
4
  class Car < CouchRest::ExtendedDocument
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
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.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
2
 
3
3
  describe "ExtendedDocument attachments" do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
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
  require File.join(FIXTURE_PATH, 'more', 'cat')
@@ -270,6 +270,15 @@ describe "ExtendedDocument" do
270
270
  foundart = Article.get @art.id
271
271
  foundart.title.should == "All About Getting"
272
272
  end
273
+
274
+ it "should return nil if `get` is used and the document doesn't exist" do
275
+ foundart = Article.get 'matt aimonetti'
276
+ foundart.should be_nil
277
+ end
278
+
279
+ it "should raise an error if `get!` is used and the document doesn't exist" do
280
+ lambda{foundart = Article.get!('matt aimonetti')}.should raise_error
281
+ end
273
282
  end
274
283
 
275
284
  describe "getting a model with a subobjects array" do
@@ -561,7 +570,7 @@ describe "ExtendedDocument" do
561
570
  end
562
571
  it "should make it go away" do
563
572
  @dobj.destroy
564
- lambda{Basic.get(@dobj.id)}.should raise_error
573
+ lambda{Basic.get!(@dobj.id)}.should raise_error
565
574
  end
566
575
  end
567
576
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
2
  require File.join(FIXTURE_PATH, 'more', 'card')
3
3
  require File.join(FIXTURE_PATH, 'more', 'course')
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
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
 
@@ -168,8 +168,11 @@ describe "ExtendedDocument views" do
168
168
  end
169
169
  things[0]["doc"]["title"].should =='aaa'
170
170
  end
171
- it "should barf on get if no database given" do
172
- lambda{Unattached.get("aaa")}.should raise_error
171
+ it "should return nil on get if no database given" do
172
+ Unattached.get("aaa").should be_nil
173
+ end
174
+ it "should barf on get! if no database given" do
175
+ lambda{Unattached.get!("aaa")}.should raise_error
173
176
  end
174
177
  it "should get from specific database" do
175
178
  u = Unattached.get(@first_id, @db)
@@ -200,7 +203,7 @@ describe "ExtendedDocument views" do
200
203
  before(:all) do
201
204
  reset_test_db!
202
205
  # setup the class default doc to save the design doc
203
- Unattached.use_database DB
206
+ Unattached.use_database nil # just to be sure it is really unattached
204
207
  @us = Unattached.on(DB)
205
208
  %w{aaa bbb ddd eee}.each do |title|
206
209
  u = @us.new(:title => title)
@@ -212,6 +215,9 @@ describe "ExtendedDocument views" do
212
215
  rs = @us.all
213
216
  rs.length.should == 4
214
217
  end
218
+ it "should count" do
219
+ @us.count.should == 4
220
+ end
215
221
  it "should make the design doc upon first query" do
216
222
  @us.by_title
217
223
  doc = @us.design_doc
@@ -348,7 +354,8 @@ describe "ExtendedDocument views" do
348
354
  a = Article.new(:title => title, :date => Date.today)
349
355
  a.save
350
356
  end
351
- end
357
+ end
358
+ require 'date'
352
359
  it "should return a proxy that looks like an array of 7 Article objects" do
353
360
  articles = Article.by_date :key => Date.today
354
361
  articles.class.should == Array
@@ -368,8 +375,7 @@ describe "ExtendedDocument views" do
368
375
  end
369
376
  it "should have the amount of paginated pages" do
370
377
  articles = Article.by_date :key => Date.today
371
- articles.paginate(:per_page => 3)
372
- articles.amount_pages.should == 3
378
+ articles.paginate(:per_page => 3).amount_pages.should == 3
373
379
  end
374
380
  it "should provide a class method to access the collection directly" do
375
381
  articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
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')
@@ -164,6 +164,33 @@ describe "ExtendedDocument properties" do
164
164
  end
165
165
  end
166
166
 
167
+ describe "casting to a boolean value" do
168
+ class RootBeerFloat < CouchRest::ExtendedDocument
169
+ use_database DB
170
+ property :tasty, :cast_as => :boolean
171
+ end
172
+
173
+ it "should add an accessor with a '?' for boolean attributes that returns true or false" do
174
+ RootBeerFloat.new(:tasty => true).tasty?.should == true
175
+ RootBeerFloat.new(:tasty => 'you bet').tasty?.should == true
176
+ RootBeerFloat.new(:tasty => 123).tasty?.should == true
177
+
178
+ RootBeerFloat.new(:tasty => false).tasty?.should == false
179
+ RootBeerFloat.new(:tasty => 'false').tasty?.should == false
180
+ RootBeerFloat.new(:tasty => 'FaLsE').tasty?.should == false
181
+ RootBeerFloat.new(:tasty => nil).tasty?.should == false
182
+ end
183
+
184
+ it "should return the real value when the default accessor is used" do
185
+ RootBeerFloat.new(:tasty => true).tasty.should == true
186
+ RootBeerFloat.new(:tasty => 'you bet').tasty.should == 'you bet'
187
+ RootBeerFloat.new(:tasty => 123).tasty.should == 123
188
+ RootBeerFloat.new(:tasty => 'false').tasty.should == 'false'
189
+ RootBeerFloat.new(:tasty => false).tasty.should == false
190
+ RootBeerFloat.new(:tasty => nil).tasty.should == nil
191
+ end
192
+ end
193
+
167
194
  end
168
195
  end
169
196
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sporkd-couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.31"
4
+ version: "0.33"
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -78,12 +78,15 @@ files:
78
78
  - lib/couchrest/core/document.rb
79
79
  - lib/couchrest/core/http_abstraction.rb
80
80
  - lib/couchrest/core/response.rb
81
+ - lib/couchrest/core/rest_api.rb
81
82
  - lib/couchrest/core/server.rb
82
83
  - lib/couchrest/core/view.rb
83
84
  - lib/couchrest/helper
84
85
  - lib/couchrest/helper/pager.rb
85
86
  - lib/couchrest/helper/streamer.rb
86
87
  - lib/couchrest/helper/upgrade.rb
88
+ - lib/couchrest/middlewares
89
+ - lib/couchrest/middlewares/logger.rb
87
90
  - lib/couchrest/mixins
88
91
  - lib/couchrest/mixins/attachments.rb
89
92
  - lib/couchrest/mixins/callbacks.rb
@@ -169,6 +172,7 @@ files:
169
172
  - utils/subset.rb
170
173
  has_rdoc: false
171
174
  homepage: http://github.com/jchris/couchrest
175
+ licenses:
172
176
  post_install_message:
173
177
  rdoc_options: []
174
178
 
@@ -189,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
193
  requirements: []
190
194
 
191
195
  rubyforge_project:
192
- rubygems_version: 1.2.0
196
+ rubygems_version: 1.3.5
193
197
  signing_key:
194
198
  specification_version: 3
195
199
  summary: Lean and RESTful interface to CouchDB.