jchris-couchrest 0.12.6 → 0.16

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