jchris-couchrest 0.12.6 → 0.16

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.
Files changed (59) hide show
  1. data/README.md +33 -8
  2. data/Rakefile +1 -1
  3. data/examples/model/example.rb +19 -13
  4. data/lib/couchrest.rb +27 -2
  5. data/lib/couchrest/core/database.rb +113 -41
  6. data/lib/couchrest/core/document.rb +48 -27
  7. data/lib/couchrest/core/response.rb +15 -0
  8. data/lib/couchrest/core/server.rb +47 -10
  9. data/lib/couchrest/mixins.rb +4 -0
  10. data/lib/couchrest/mixins/attachments.rb +31 -0
  11. data/lib/couchrest/mixins/callbacks.rb +442 -0
  12. data/lib/couchrest/mixins/design_doc.rb +63 -0
  13. data/lib/couchrest/mixins/document_queries.rb +48 -0
  14. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  15. data/lib/couchrest/mixins/extended_document_mixins.rb +6 -0
  16. data/lib/couchrest/mixins/properties.rb +120 -0
  17. data/lib/couchrest/mixins/validation.rb +234 -0
  18. data/lib/couchrest/mixins/views.rb +168 -0
  19. data/lib/couchrest/monkeypatches.rb +75 -0
  20. data/lib/couchrest/more/casted_model.rb +28 -0
  21. data/lib/couchrest/more/extended_document.rb +215 -0
  22. data/lib/couchrest/more/property.rb +40 -0
  23. data/lib/couchrest/support/blank.rb +42 -0
  24. data/lib/couchrest/support/class.rb +175 -0
  25. data/lib/couchrest/validation/auto_validate.rb +163 -0
  26. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  27. data/lib/couchrest/validation/validation_errors.rb +118 -0
  28. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  29. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  30. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  31. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  32. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  33. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  34. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  35. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  36. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  37. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  38. data/spec/couchrest/core/database_spec.rb +183 -67
  39. data/spec/couchrest/core/design_spec.rb +1 -1
  40. data/spec/couchrest/core/document_spec.rb +271 -173
  41. data/spec/couchrest/core/server_spec.rb +35 -0
  42. data/spec/couchrest/helpers/pager_spec.rb +1 -1
  43. data/spec/couchrest/more/casted_model_spec.rb +97 -0
  44. data/spec/couchrest/more/extended_doc_attachment_spec.rb +129 -0
  45. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  46. data/spec/couchrest/more/extended_doc_view_spec.rb +204 -0
  47. data/spec/couchrest/more/property_spec.rb +129 -0
  48. data/spec/fixtures/more/article.rb +34 -0
  49. data/spec/fixtures/more/card.rb +20 -0
  50. data/spec/fixtures/more/course.rb +14 -0
  51. data/spec/fixtures/more/event.rb +6 -0
  52. data/spec/fixtures/more/invoice.rb +17 -0
  53. data/spec/fixtures/more/person.rb +8 -0
  54. data/spec/fixtures/more/question.rb +6 -0
  55. data/spec/fixtures/more/service.rb +12 -0
  56. data/spec/spec_helper.rb +13 -7
  57. metadata +76 -3
  58. data/lib/couchrest/core/model.rb +0 -613
  59. data/spec/couchrest/core/model_spec.rb +0 -855
data/README.md CHANGED
@@ -31,7 +31,7 @@ Quick Start:
31
31
 
32
32
  # with !, it creates the database if it doesn't already exist
33
33
  @db = CouchRest.database!("http://127.0.0.1:5984/couchrest-test")
34
- response = @db.save({:key => 'value', 'another key' => 'another value'})
34
+ response = @db.save_doc({:key => 'value', 'another key' => 'another value'})
35
35
  doc = @db.get(response['id'])
36
36
  puts doc.inspect
37
37
 
@@ -47,7 +47,7 @@ Bulk Save:
47
47
 
48
48
  Creating and Querying Views:
49
49
 
50
- @db.save({
50
+ @db.save_doc({
51
51
  "_id" => "_design/first",
52
52
  :views => {
53
53
  :test => {
@@ -59,10 +59,35 @@ Creating and Querying Views:
59
59
 
60
60
  ## CouchRest::Model
61
61
 
62
- CouchRest::Model is a module designed along the lines of DataMapper::Resource.
63
- By subclassing, suddenly you get all sorts of powerful sugar, so that working
64
- with CouchDB in your Rails or Merb app is no harder than working with the
65
- standard SQL alternatives. See the CouchRest::Model documentation for an
66
- example article class that illustrates usage.
62
+ CouchRest::Model has been deprecated and replaced by CouchRest::ExtendedDocument
63
+
64
+
65
+ ## CouchRest::ExtendedDocument
66
+
67
+ ### Callbacks
68
+
69
+ `CouchRest::ExtendedDocuments` instances have 2 callbacks already defined for you:
70
+ `create_callback`, `save_callback`, `update_callback` and `destroy_callback`
71
+
72
+ In your document inherits from `CouchRest::ExtendedDocument`, define your callback as follows:
73
+
74
+ save_callback :before, :generate_slug_from_name
75
+
76
+ CouchRest uses a mixin you can find in lib/mixins/callbacks which is extracted from Rails 3, here are some simple usage examples:
77
+
78
+ save_callback :before, :before_method
79
+ save_callback :after, :after_method, :if => :condition
80
+ save_callback :around {|r| stuff; yield; stuff }
81
+
82
+ Check the mixin or the ExtendedDocument class to see how to implement your own callbacks.
83
+
84
+ ### Casting
85
+
86
+ Often, you will want to store multiple objects within a document, to be able to retrieve your objects when you load the document,
87
+ you can define some casting rules.
88
+
89
+ property :casted_attribute, :cast_as => 'WithCastedModelMixin'
90
+ property :keywords, :cast_as => ["String"]
91
+
92
+ If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"]
67
93
 
68
- CouchRest::Model will be removed from this package.
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ spec = Gem::Specification.new do |s|
23
23
  s.homepage = "http://github.com/jchris/couchrest"
24
24
  s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments."
25
25
  s.has_rdoc = true
26
- s.authors = ["J. Chris Anderson"]
26
+ s.authors = ["J. Chris Anderson", "Matt Aimonetti"]
27
27
  s.files = %w( LICENSE README.md Rakefile THANKS.md ) +
28
28
  Dir["{examples,lib,spec,utils}/**/*"] -
29
29
  Dir["spec/tmp"]
@@ -1,31 +1,38 @@
1
- require 'rubygems'
2
- require 'couchrest'
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'couchrest')
3
2
 
4
3
  def show obj
5
4
  puts obj.inspect
6
5
  puts
7
6
  end
8
7
 
9
- CouchRest::Model.default_database = CouchRest.database!('couchrest-model-example')
8
+ SERVER = CouchRest.new
9
+ SERVER.default_database = 'couchrest-extendeddoc-example'
10
10
 
11
- class Author < CouchRest::Model
12
- key_accessor :name
11
+ class Author < CouchRest::ExtendedDocument
12
+ use_database SERVER.default_database
13
+ property :name
14
+
13
15
  def drink_scotch
14
16
  puts "... glug type glug ... I'm #{name} ... type glug glug ..."
15
17
  end
16
18
  end
17
19
 
18
- class Post < CouchRest::Model
19
- key_accessor :title, :body, :author
20
-
21
- cast :author, :as => 'Author'
20
+ class Post < CouchRest::ExtendedDocument
21
+ use_database SERVER.default_database
22
+
23
+ property :title
24
+ property :body
25
+ property :author, :cast_as => 'Author'
22
26
 
23
27
  timestamps!
24
28
  end
25
29
 
26
- class Comment < CouchRest::Model
27
- cast :commenter, :as => 'Author'
28
-
30
+ class Comment < CouchRest::ExtendedDocument
31
+ use_database SERVER.default_database
32
+
33
+ property :commenter, :cast_as => 'Author'
34
+ timestamps!
35
+
29
36
  def post= post
30
37
  self["post_id"] = post.id
31
38
  end
@@ -33,7 +40,6 @@ class Comment < CouchRest::Model
33
40
  Post.get(self['post_id']) if self['post_id']
34
41
  end
35
42
 
36
- timestamps!
37
43
  end
38
44
 
39
45
  puts "Act I: CRUD"
data/lib/couchrest.rb CHANGED
@@ -13,7 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "rubygems"
16
+ gem 'json'
16
17
  require 'json'
18
+ gem 'rest-client'
17
19
  require 'rest_client'
18
20
 
19
21
  $:.unshift File.dirname(__FILE__) unless
@@ -25,23 +27,46 @@ require 'couchrest/monkeypatches'
25
27
 
26
28
  # = CouchDB, close to the metal
27
29
  module CouchRest
28
- VERSION = '0.12.6'
30
+ VERSION = '0.16' unless self.const_defined?("VERSION")
29
31
 
30
32
  autoload :Server, 'couchrest/core/server'
31
33
  autoload :Database, 'couchrest/core/database'
34
+ autoload :Response, 'couchrest/core/response'
32
35
  autoload :Document, 'couchrest/core/document'
33
- autoload :Design, 'couchrest/core/design'
36
+ autoload :Design, 'couchrest/core/design'
34
37
  autoload :View, 'couchrest/core/view'
35
38
  autoload :Model, 'couchrest/core/model'
36
39
  autoload :Pager, 'couchrest/helper/pager'
37
40
  autoload :FileManager, 'couchrest/helper/file_manager'
38
41
  autoload :Streamer, 'couchrest/helper/streamer'
39
42
 
43
+ autoload :ExtendedDocument, 'couchrest/more/extended_document'
44
+ autoload :CastedModel, 'couchrest/more/casted_model'
45
+
46
+ require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
47
+
40
48
  # The CouchRest module methods handle the basic JSON serialization
41
49
  # and deserialization, as well as query parameters. The module also includes
42
50
  # some helpers for tasks like instantiating a new Database or Server instance.
43
51
  class << self
44
52
 
53
+ # extracted from Extlib
54
+ #
55
+ # Constantize tries to find a declared constant with the name specified
56
+ # in the string. It raises a NameError when the name is not in CamelCase
57
+ # or is not initialized.
58
+ #
59
+ # @example
60
+ # "Module".constantize #=> Module
61
+ # "Class".constantize #=> Class
62
+ def constantize(camel_cased_word)
63
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
64
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
65
+ end
66
+
67
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
68
+ end
69
+
45
70
  # todo, make this parse the url and instantiate a Server or Database instance
46
71
  # depending on the specificity.
47
72
  def new(*opts)
@@ -3,7 +3,7 @@ require "base64"
3
3
 
4
4
  module CouchRest
5
5
  class Database
6
- attr_reader :server, :host, :name, :root
6
+ attr_reader :server, :host, :name, :root, :uri
7
7
  attr_accessor :bulk_save_cache_limit
8
8
 
9
9
  # Create a CouchRest::Database adapter for the supplied CouchRest::Server
@@ -13,11 +13,11 @@ module CouchRest
13
13
  # server<CouchRest::Server>:: database host
14
14
  # name<String>:: database name
15
15
  #
16
- def initialize server, name
16
+ def initialize(server, name)
17
17
  @name = name
18
18
  @server = server
19
19
  @host = server.uri
20
- @root = "#{host}/#{name}"
20
+ @uri = @root = "#{host}/#{name}"
21
21
  @streamer = Streamer.new(self)
22
22
  @bulk_save_cache = []
23
23
  @bulk_save_cache_limit = 50
@@ -25,18 +25,18 @@ module CouchRest
25
25
 
26
26
  # returns the database's uri
27
27
  def to_s
28
- @root
28
+ @uri
29
29
  end
30
30
 
31
31
  # GET the database info from CouchDB
32
32
  def info
33
- CouchRest.get @root
33
+ CouchRest.get @uri
34
34
  end
35
35
 
36
36
  # Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
37
- def documents params = {}
37
+ def documents(params = {})
38
38
  keys = params.delete(:keys)
39
- url = CouchRest.paramify_url "#{@root}/_all_docs", params
39
+ url = CouchRest.paramify_url "#{@uri}/_all_docs", params
40
40
  if keys
41
41
  CouchRest.post(url, {:keys => keys})
42
42
  else
@@ -47,10 +47,10 @@ module CouchRest
47
47
  # POST a temporary view function to CouchDB for querying. This is not
48
48
  # recommended, as you don't get any performance benefit from CouchDB's
49
49
  # materialized views. Can be quite slow on large databases.
50
- def slow_view funcs, params = {}
50
+ def slow_view(funcs, params = {})
51
51
  keys = params.delete(:keys)
52
52
  funcs = funcs.merge({:keys => keys}) if keys
53
- url = CouchRest.paramify_url "#{@root}/_temp_view", params
53
+ url = CouchRest.paramify_url "#{@uri}/_temp_view", params
54
54
  JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
55
55
  end
56
56
 
@@ -59,9 +59,9 @@ module CouchRest
59
59
 
60
60
  # Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
61
61
  # paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
62
- def view name, params = {}, &block
62
+ def view(name, params = {}, &block)
63
63
  keys = params.delete(:keys)
64
- url = CouchRest.paramify_url "#{@root}/_view/#{name}", params
64
+ url = CouchRest.paramify_url "#{@uri}/_view/#{name}", params
65
65
  if keys
66
66
  CouchRest.post(url, {:keys => keys})
67
67
  else
@@ -74,9 +74,9 @@ module CouchRest
74
74
  end
75
75
 
76
76
  # GET a document from CouchDB, by id. Returns a Ruby Hash.
77
- def get id
77
+ def get(id)
78
78
  slug = escape_docid(id)
79
- hash = CouchRest.get("#{@root}/#{slug}")
79
+ hash = CouchRest.get("#{@uri}/#{slug}")
80
80
  doc = if /^_design/ =~ hash["_id"]
81
81
  Design.new(hash)
82
82
  else
@@ -87,25 +87,29 @@ module CouchRest
87
87
  end
88
88
 
89
89
  # GET an attachment directly from CouchDB
90
- def fetch_attachment docid, name
91
- slug = escape_docid(docid)
92
- name = CGI.escape(name)
93
- RestClient.get "#{@root}/#{slug}/#{name}"
90
+ def fetch_attachment(doc, name)
91
+ # slug = escape_docid(docid)
92
+ # name = CGI.escape(name)
93
+ uri = uri_for_attachment(doc, name)
94
+ RestClient.get uri
95
+ # "#{@uri}/#{slug}/#{name}"
94
96
  end
95
97
 
96
98
  # PUT an attachment directly to CouchDB
97
- def put_attachment doc, name, file, options = {}
99
+ def put_attachment(doc, name, file, options = {})
98
100
  docid = escape_docid(doc['_id'])
99
101
  name = CGI.escape(name)
100
- uri = if doc['_rev']
101
- "#{@root}/#{docid}/#{name}?rev=#{doc['_rev']}"
102
- else
103
- "#{@root}/#{docid}/#{name}"
104
- end
105
-
102
+ uri = uri_for_attachment(doc, name)
106
103
  JSON.parse(RestClient.put(uri, file, options))
107
104
  end
108
105
 
106
+ # DELETE an attachment directly from CouchDB
107
+ def delete_attachment doc, name
108
+ uri = uri_for_attachment(doc, name)
109
+ # this needs a rev
110
+ JSON.parse(RestClient.delete(uri))
111
+ end
112
+
109
113
  # Save a document to CouchDB. This will use the <tt>_id</tt> field from
110
114
  # the document as the id for PUT, or request a new UUID from CouchDB, if
111
115
  # no <tt>_id</tt> is present on the document. IDs are attached to
@@ -115,7 +119,7 @@ module CouchRest
115
119
  #
116
120
  # If <tt>bulk</tt> is true (false by default) the document is cached for bulk-saving later.
117
121
  # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
118
- def save (doc, bulk = false)
122
+ def save_doc(doc, bulk = false)
119
123
  if doc['_attachments']
120
124
  doc['_attachments'] = encode_attachments(doc['_attachments'])
121
125
  end
@@ -128,13 +132,13 @@ module CouchRest
128
132
  end
129
133
  result = if doc['_id']
130
134
  slug = escape_docid(doc['_id'])
131
- CouchRest.put "#{@root}/#{slug}", doc
135
+ CouchRest.put "#{@uri}/#{slug}", doc
132
136
  else
133
137
  begin
134
138
  slug = doc['_id'] = @server.next_uuid
135
- CouchRest.put "#{@root}/#{slug}", doc
139
+ CouchRest.put "#{@uri}/#{slug}", doc
136
140
  rescue #old version of couchdb
137
- CouchRest.post @root, doc
141
+ CouchRest.post @uri, doc
138
142
  end
139
143
  end
140
144
  if result['ok']
@@ -145,6 +149,13 @@ module CouchRest
145
149
  result
146
150
  end
147
151
 
152
+ ### DEPRECATION NOTICE
153
+ def save(doc, bulk=false)
154
+ puts "CouchRest::Database's save method is being deprecated, please use save_doc instead"
155
+ save_doc(doc, bulk)
156
+ end
157
+
158
+
148
159
  # POST an array of documents to CouchDB. If any of the documents are
149
160
  # missing ids, supply one from the uuid cache.
150
161
  #
@@ -162,15 +173,16 @@ module CouchRest
162
173
  doc['_id'] = nextid if nextid
163
174
  end
164
175
  end
165
- CouchRest.post "#{@root}/_bulk_docs", {:docs => docs}
176
+ CouchRest.post "#{@uri}/_bulk_docs", {:docs => docs}
166
177
  end
178
+ alias :bulk_delete :bulk_save
167
179
 
168
180
  # DELETE the document from CouchDB that has the given <tt>_id</tt> and
169
181
  # <tt>_rev</tt>.
170
182
  #
171
183
  # If <tt>bulk</tt> is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later.
172
184
  # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
173
- def delete (doc, bulk = false)
185
+ def delete_doc(doc, bulk = false)
174
186
  raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
175
187
  if bulk
176
188
  @bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true }
@@ -178,13 +190,19 @@ module CouchRest
178
190
  return { "ok" => true } # Mimic the non-deferred version
179
191
  end
180
192
  slug = escape_docid(doc['_id'])
181
- CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
193
+ CouchRest.delete "#{@uri}/#{slug}?rev=#{doc['_rev']}"
194
+ end
195
+
196
+ ### DEPRECATION NOTICE
197
+ def delete(doc, bulk=false)
198
+ puts "CouchRest::Database's delete method is being deprecated, please use delete_doc instead"
199
+ delete_doc(doc, bulk)
182
200
  end
183
201
 
184
202
  # COPY an existing document to a new id. If the destination id currently exists, a rev must be provided.
185
203
  # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
186
204
  # hash with a '_rev' key
187
- def copy doc, dest
205
+ def copy_doc(doc, dest)
188
206
  raise ArgumentError, "_id is required for copying" unless doc['_id']
189
207
  slug = escape_docid(doc['_id'])
190
208
  destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
@@ -192,13 +210,19 @@ module CouchRest
192
210
  else
193
211
  dest
194
212
  end
195
- CouchRest.copy "#{@root}/#{slug}", destination
213
+ CouchRest.copy "#{@uri}/#{slug}", destination
214
+ end
215
+
216
+ ### DEPRECATION NOTICE
217
+ def copy(doc, dest)
218
+ puts "CouchRest::Database's copy method is being deprecated, please use copy_doc instead"
219
+ copy_doc(doc, dest)
196
220
  end
197
221
 
198
222
  # MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided.
199
223
  # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
200
224
  # hash with a '_rev' key
201
- def move doc, dest
225
+ def move_doc(doc, dest)
202
226
  raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev']
203
227
  slug = escape_docid(doc['_id'])
204
228
  destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
@@ -206,27 +230,75 @@ module CouchRest
206
230
  else
207
231
  dest
208
232
  end
209
- CouchRest.move "#{@root}/#{slug}?rev=#{doc['_rev']}", destination
233
+ CouchRest.move "#{@uri}/#{slug}?rev=#{doc['_rev']}", destination
234
+ end
235
+
236
+ ### DEPRECATION NOTICE
237
+ def move(doc, dest)
238
+ puts "CouchRest::Database's move method is being deprecated, please use move_doc instead"
239
+ move_doc(doc, dest)
210
240
  end
211
241
 
212
242
  # Compact the database, removing old document revisions and optimizing space use.
213
243
  def compact!
214
- CouchRest.post "#{@root}/_compact"
244
+ CouchRest.post "#{@uri}/_compact"
215
245
  end
216
-
246
+
247
+ # Create the database
248
+ def create!
249
+ bool = server.create_db(@name) rescue false
250
+ bool && true
251
+ end
252
+
253
+ # Delete and re create the database
254
+ def recreate!
255
+ delete!
256
+ create!
257
+ rescue RestClient::ResourceNotFound
258
+ ensure
259
+ create!
260
+ end
261
+
262
+ # Replicates via "pulling" from another database to this database. Makes no attempt to deal with conflicts.
263
+ def replicate_from other_db
264
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
265
+ CouchRest.post "#{@host}/_replicate", :source => other_db.root, :target => name
266
+ end
267
+
268
+ # Replicates via "pushing" to another database. Makes no attempt to deal with conflicts.
269
+ def replicate_to other_db
270
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
271
+ CouchRest.post "#{@host}/_replicate", :target => other_db.root, :source => name
272
+ end
273
+
217
274
  # DELETE the database itself. This is not undoable and could be rather
218
275
  # catastrophic. Use with care!
219
276
  def delete!
220
- CouchRest.delete @root
277
+ CouchRest.delete @uri
221
278
  end
222
279
 
223
280
  private
224
-
281
+
282
+ def uri_for_attachment(doc, name)
283
+ if doc.is_a?(String)
284
+ puts "CouchRest::Database#fetch_attachment will eventually require a doc as the first argument, not a doc.id"
285
+ docid = doc
286
+ rev = nil
287
+ else
288
+ docid = doc['_id']
289
+ rev = doc['_rev']
290
+ end
291
+ docid = escape_docid(docid)
292
+ name = CGI.escape(name)
293
+ rev = "?rev=#{doc['_rev']}" if rev
294
+ "#{@root}/#{docid}/#{name}#{rev}"
295
+ end
296
+
225
297
  def escape_docid id
226
298
  /^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
227
299
  end
228
300
 
229
- def encode_attachments attachments
301
+ def encode_attachments(attachments)
230
302
  attachments.each do |k,v|
231
303
  next if v['stub']
232
304
  v['data'] = base64(v['data'])
@@ -234,7 +306,7 @@ module CouchRest
234
306
  attachments
235
307
  end
236
308
 
237
- def base64 data
309
+ def base64(data)
238
310
  Base64.encode64(data).gsub(/\s/,'')
239
311
  end
240
312
  end