couchrest 0.12.4 → 0.23

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 (64) hide show
  1. data/README.md +33 -8
  2. data/Rakefile +11 -2
  3. data/examples/model/example.rb +19 -13
  4. data/lib/couchrest.rb +70 -11
  5. data/lib/couchrest/core/database.rb +121 -62
  6. data/lib/couchrest/core/design.rb +7 -17
  7. data/lib/couchrest/core/document.rb +42 -30
  8. data/lib/couchrest/core/response.rb +16 -0
  9. data/lib/couchrest/core/server.rb +47 -10
  10. data/lib/couchrest/helper/upgrade.rb +51 -0
  11. data/lib/couchrest/mixins.rb +4 -0
  12. data/lib/couchrest/mixins/attachments.rb +31 -0
  13. data/lib/couchrest/mixins/callbacks.rb +483 -0
  14. data/lib/couchrest/mixins/class_proxy.rb +108 -0
  15. data/lib/couchrest/mixins/design_doc.rb +90 -0
  16. data/lib/couchrest/mixins/document_queries.rb +44 -0
  17. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  18. data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
  19. data/lib/couchrest/mixins/properties.rb +129 -0
  20. data/lib/couchrest/mixins/validation.rb +242 -0
  21. data/lib/couchrest/mixins/views.rb +169 -0
  22. data/lib/couchrest/monkeypatches.rb +81 -6
  23. data/lib/couchrest/more/casted_model.rb +28 -0
  24. data/lib/couchrest/more/extended_document.rb +215 -0
  25. data/lib/couchrest/more/property.rb +40 -0
  26. data/lib/couchrest/support/blank.rb +42 -0
  27. data/lib/couchrest/support/class.rb +176 -0
  28. data/lib/couchrest/validation/auto_validate.rb +163 -0
  29. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  30. data/lib/couchrest/validation/validation_errors.rb +118 -0
  31. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  32. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  33. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  34. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  35. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  36. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  37. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  38. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  39. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  40. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  41. data/spec/couchrest/core/database_spec.rb +189 -124
  42. data/spec/couchrest/core/design_spec.rb +13 -6
  43. data/spec/couchrest/core/document_spec.rb +231 -177
  44. data/spec/couchrest/core/server_spec.rb +35 -0
  45. data/spec/couchrest/helpers/pager_spec.rb +1 -1
  46. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  47. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  48. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  49. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  50. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  51. data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
  52. data/spec/couchrest/more/property_spec.rb +136 -0
  53. data/spec/fixtures/more/article.rb +34 -0
  54. data/spec/fixtures/more/card.rb +20 -0
  55. data/spec/fixtures/more/course.rb +14 -0
  56. data/spec/fixtures/more/event.rb +6 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +8 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/spec_helper.rb +13 -7
  62. metadata +58 -4
  63. data/lib/couchrest/core/model.rb +0 -613
  64. data/spec/couchrest/core/model_spec.rb +0 -855
@@ -0,0 +1,89 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ # @since 0.9
31
+ class MethodValidator < GenericValidator
32
+
33
+ def initialize(field_name, options={})
34
+ super
35
+ @field_name, @options = field_name, options.clone
36
+ @options[:method] = @field_name unless @options.has_key?(:method)
37
+ end
38
+
39
+ def call(target)
40
+ result, message = target.send(@options[:method])
41
+ add_error(target, message, field_name) unless result
42
+ result
43
+ end
44
+
45
+ def ==(other)
46
+ @options[:method] == other.instance_variable_get(:@options)[:method] && super
47
+ end
48
+ end # class MethodValidator
49
+
50
+ module ValidatesWithMethod
51
+
52
+ ##
53
+ # Validate using the given method. The method given needs to return:
54
+ # [result::<Boolean>, Error Message::<String>]
55
+ #
56
+ # @example [Usage]
57
+ #
58
+ # class Page
59
+ #
60
+ # property :zip_code, String
61
+ #
62
+ # validates_with_method :in_the_right_location?
63
+ #
64
+ # def in_the_right_location?
65
+ # if @zip_code == "94301"
66
+ # return true
67
+ # else
68
+ # return [false, "You're in the wrong zip code"]
69
+ # end
70
+ # end
71
+ #
72
+ # # A call to valid? will return false and
73
+ # # populate the object's errors with "You're in the
74
+ # # wrong zip code" unless zip_code == "94301"
75
+ #
76
+ # # You can also specify field:
77
+ #
78
+ # validates_with_method :zip_code, :in_the_right_location?
79
+ #
80
+ # # it will add returned error message to :zip_code field
81
+ #
82
+ def validates_with_method(*fields)
83
+ opts = opts_from_validator_args(fields)
84
+ add_validator_to_context(opts, fields, CouchRest::Validation::MethodValidator)
85
+ end
86
+
87
+ end # module ValidatesWithMethod
88
+ end # module Validation
89
+ end # module CouchRest
@@ -0,0 +1,104 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ # @since 0.9
31
+ class NumericValidator < GenericValidator
32
+
33
+ def initialize(field_name, options={})
34
+ super
35
+ @field_name, @options = field_name, options
36
+ @options[:integer_only] = false unless @options.has_key?(:integer_only)
37
+ end
38
+
39
+ def call(target)
40
+ value = target.send(field_name)
41
+ return true if @options[:allow_nil] && value.nil?
42
+
43
+ value = value.kind_of?(Float) ? value.to_s('F') : value.to_s
44
+
45
+ error_message = @options[:message]
46
+ precision = @options[:precision]
47
+ scale = @options[:scale]
48
+
49
+ if @options[:integer_only]
50
+ return true if value =~ /\A[+-]?\d+\z/
51
+ error_message ||= ValidationErrors.default_error_message(:not_an_integer, field_name)
52
+ else
53
+ # FIXME: if precision and scale are not specified, can we assume that it is an integer?
54
+ # probably not, as floating point numbers don't have hard
55
+ # defined scale. the scale floats with the length of the
56
+ # integral and precision. Ie. if precision = 10 and integral
57
+ # portion of the number is 9834 (4 digits), the max scale will
58
+ # be 6 (10 - 4). But if the integral length is 1, max scale
59
+ # will be (10 - 1) = 9, so 1.234567890.
60
+ if precision && scale
61
+ #handles both Float when it has scale specified and BigDecimal
62
+ if precision > scale && scale > 0
63
+ return true if value =~ /\A[+-]?(?:\d{1,#{precision - scale}}|\d{0,#{precision - scale}}\.\d{1,#{scale}})\z/
64
+ elsif precision > scale && scale == 0
65
+ return true if value =~ /\A[+-]?(?:\d{1,#{precision}}(?:\.0)?)\z/
66
+ elsif precision == scale
67
+ return true if value =~ /\A[+-]?(?:0(?:\.\d{1,#{scale}})?)\z/
68
+ else
69
+ raise ArgumentError, "Invalid precision #{precision.inspect} and scale #{scale.inspect} for #{field_name} (value: #{value.inspect} #{value.class})"
70
+ end
71
+ elsif precision && scale.nil?
72
+ # for floats, if scale is not set
73
+
74
+ #total number of digits is less or equal precision
75
+ return true if value.gsub(/[^\d]/, '').length <= precision
76
+
77
+ #number of digits before decimal == precision, and the number is x.0. same as scale = 0
78
+ return true if value =~ /\A[+-]?(?:\d{1,#{precision}}(?:\.0)?)\z/
79
+ else
80
+ return true if value =~ /\A[+-]?(?:\d+|\d*\.\d+)\z/
81
+ end
82
+ error_message ||= ValidationErrors.default_error_message(:not_a_number, field_name)
83
+ end
84
+
85
+ add_error(target, error_message, field_name)
86
+
87
+ # TODO: check the gt, gte, lt, lte, and eq options
88
+
89
+ return false
90
+ end
91
+ end # class NumericValidator
92
+
93
+ module ValidatesIsNumber
94
+
95
+ # Validate whether a field is numeric
96
+ #
97
+ def validates_is_number(*fields)
98
+ opts = opts_from_validator_args(fields)
99
+ add_validator_to_context(opts, fields, CouchRest::Validation::NumericValidator)
100
+ end
101
+
102
+ end # module ValidatesIsNumber
103
+ end # module Validation
104
+ end # module CouchRest
@@ -0,0 +1,109 @@
1
+ # Extracted from dm-validations 0.9.10
2
+ #
3
+ # Copyright (c) 2007 Guy van den Berg
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module CouchRest
25
+ module Validation
26
+
27
+ ##
28
+ #
29
+ # @author Guy van den Berg
30
+ # @since 0.9
31
+ class RequiredFieldValidator < GenericValidator
32
+
33
+ def initialize(field_name, options={})
34
+ super
35
+ @field_name, @options = field_name, options
36
+ end
37
+
38
+ def call(target)
39
+ value = target.validation_property_value(field_name)
40
+ property = target.validation_property(field_name)
41
+ return true if present?(value, property)
42
+
43
+ error_message = @options[:message] || default_error(property)
44
+ add_error(target, error_message, field_name)
45
+
46
+ false
47
+ end
48
+
49
+ protected
50
+
51
+ # Boolean property types are considered present if non-nil.
52
+ # Other property types are considered present if non-blank.
53
+ # Non-properties are considered present if non-blank.
54
+ def present?(value, property)
55
+ boolean_type?(property) ? !value.nil? : !value.blank?
56
+ end
57
+
58
+ def default_error(property)
59
+ actual = boolean_type?(property) ? :nil : :blank
60
+ ValidationErrors.default_error_message(actual, field_name)
61
+ end
62
+
63
+ # Is +property+ a boolean property?
64
+ #
65
+ # Returns true for Boolean, ParanoidBoolean, TrueClass, etc. properties.
66
+ # Returns false for other property types.
67
+ # Returns false for non-properties.
68
+ def boolean_type?(property)
69
+ property ? property.type == TrueClass : false
70
+ end
71
+
72
+ end # class RequiredFieldValidator
73
+
74
+ module ValidatesPresent
75
+
76
+ ##
77
+ # Validates that the specified attribute is present.
78
+ #
79
+ # For most property types "being present" is the same as being "not
80
+ # blank" as determined by the attribute's #blank? method. However, in
81
+ # the case of Boolean, "being present" means not nil; i.e. true or
82
+ # false.
83
+ #
84
+ # @note
85
+ # dm-core's support lib adds the blank? method to many classes,
86
+ # @see lib/dm-core/support/blank.rb (dm-core) for more information.
87
+ #
88
+ # @example [Usage]
89
+ #
90
+ # class Page
91
+ #
92
+ # property :required_attribute, String
93
+ # property :another_required, String
94
+ # property :yet_again, String
95
+ #
96
+ # validates_present :required_attribute
97
+ # validates_present :another_required, :yet_again
98
+ #
99
+ # # a call to valid? will return false unless
100
+ # # all three attributes are !blank?
101
+ # end
102
+ def validates_present(*fields)
103
+ opts = opts_from_validator_args(fields)
104
+ add_validator_to_context(opts, fields, CouchRest::Validation::RequiredFieldValidator)
105
+ end
106
+
107
+ end # module ValidatesPresent
108
+ end # module Validation
109
+ end # module CouchRest
@@ -7,7 +7,15 @@ describe CouchRest::Database do
7
7
  @db.delete! rescue nil
8
8
  @db = @cr.create_db(TESTDB) rescue nil
9
9
  end
10
-
10
+
11
+ describe "database name including slash" do
12
+ it "should escape the name in the URI" do
13
+ db = @cr.database("foo/bar")
14
+ db.name.should == "foo/bar"
15
+ db.uri.should == "#{COUCHHOST}/foo%2Fbar"
16
+ end
17
+ end
18
+
11
19
  describe "map query with _temp_view in Javascript" do
12
20
  before(:each) do
13
21
  @db.bulk_save([
@@ -61,7 +69,7 @@ describe CouchRest::Database do
61
69
  emit(doc.word,null);
62
70
  }
63
71
  }'}}
64
- @db.save({
72
+ @db.save_doc({
65
73
  "_id" => "_design/test",
66
74
  :views => @view
67
75
  })
@@ -80,7 +88,7 @@ describe CouchRest::Database do
80
88
 
81
89
  describe "select from an existing view" do
82
90
  before(:each) do
83
- r = @db.save({
91
+ r = @db.save_doc({
84
92
  "_id" => "_design/first",
85
93
  :views => {
86
94
  :test => {
@@ -129,9 +137,9 @@ describe CouchRest::Database do
129
137
 
130
138
  describe "GET (document by id) when the doc exists" do
131
139
  before(:each) do
132
- @r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
140
+ @r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
133
141
  @docid = "http://example.com/stuff.cgi?things=and%20stuff"
134
- @db.save({'_id' => @docid, 'will-exist' => 'here'})
142
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
135
143
  end
136
144
  it "should get the document" do
137
145
  doc = @db.get(@r['id'])
@@ -149,8 +157,8 @@ describe CouchRest::Database do
149
157
  {"mild" => "yet local"},
150
158
  {"another" => ["set","of","keys"]}
151
159
  ])
152
- rs['new_revs'].each do |r|
153
- @db.get(r['id'])
160
+ rs.each do |r|
161
+ @db.get(r['id']).rev.should == r["rev"]
154
162
  end
155
163
  end
156
164
 
@@ -170,29 +178,13 @@ describe CouchRest::Database do
170
178
  {"_id" => "twoB", "mild" => "yet local"},
171
179
  {"another" => ["set","of","keys"]}
172
180
  ])
173
- rs['new_revs'].each do |r|
174
- @db.get(r['id'])
181
+ rs.each do |r|
182
+ @db.get(r['id']).rev.should == r["rev"]
175
183
  end
176
184
  end
177
-
178
- it "in the case of an id conflict should not insert anything" do
179
- @r = @db.save({'lemons' => 'from texas', 'and' => 'how', "_id" => "oneB"})
180
-
181
- lambda do
182
- rs = @db.bulk_save([
183
- {"_id" => "oneB", "wild" => "and random"},
184
- {"_id" => "twoB", "mild" => "yet local"},
185
- {"another" => ["set","of","keys"]}
186
- ])
187
- end.should raise_error(RestClient::RequestFailed)
188
-
189
- lambda do
190
- @db.get('twoB')
191
- end.should raise_error(RestClient::ResourceNotFound)
192
- end
193
185
 
194
186
  it "should empty the bulk save cache if no documents are given" do
195
- @db.save({"_id" => "bulk_cache_1", "val" => "test"}, true)
187
+ @db.save_doc({"_id" => "bulk_cache_1", "val" => "test"}, true)
196
188
  lambda do
197
189
  @db.get('bulk_cache_1')
198
190
  end.should raise_error(RestClient::ResourceNotFound)
@@ -201,7 +193,7 @@ describe CouchRest::Database do
201
193
  end
202
194
 
203
195
  it "should raise an error that is useful for recovery" do
204
- @r = @db.save({"_id" => "taken", "field" => "stuff"})
196
+ @r = @db.save_doc({"_id" => "taken", "field" => "stuff"})
205
197
  begin
206
198
  rs = @db.bulk_save([
207
199
  {"_id" => "taken", "wild" => "and random"},
@@ -220,17 +212,43 @@ describe CouchRest::Database do
220
212
  @db.documents["total_rows"].should == 0
221
213
  end
222
214
  it "should create the document and return the id" do
223
- r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
215
+ r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
224
216
  r2 = @db.get(r['id'])
225
217
  r2["lemons"].should == "from texas"
226
218
  end
227
219
  it "should use PUT with UUIDs" do
228
220
  CouchRest.should_receive(:put).and_return({"ok" => true, "id" => "100", "rev" => "55"})
229
- r = @db.save({'just' => ['another document']})
221
+ r = @db.save_doc({'just' => ['another document']})
230
222
  end
231
223
 
232
224
  end
233
-
225
+
226
+ describe "fetch_attachment" do
227
+ before do
228
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
229
+ @doc = {
230
+ "_id" => "mydocwithattachment",
231
+ "field" => ["some value"],
232
+ "_attachments" => {
233
+ "test.html" => {
234
+ "type" => "text/html",
235
+ "data" => @attach
236
+ }
237
+ }
238
+ }
239
+ @db.save_doc(@doc)
240
+ end
241
+
242
+ # Depreacated
243
+ # it "should get the attachment with the doc's _id" do
244
+ # @db.fetch_attachment("mydocwithattachment", "test.html").should == @attach
245
+ # end
246
+
247
+ it "should get the attachment with the doc itself" do
248
+ @db.fetch_attachment(@db.get('mydocwithattachment'), 'test.html').should == @attach
249
+ end
250
+ end
251
+
234
252
  describe "PUT attachment from file" do
235
253
  before(:each) do
236
254
  filename = FIXTURE_PATH + '/attachments/couchdb.png'
@@ -242,7 +260,8 @@ describe CouchRest::Database do
242
260
  it "should save the attachment to a new doc" do
243
261
  r = @db.put_attachment({'_id' => 'attach-this'}, 'couchdb.png', image = @file.read, {:content_type => 'image/png'})
244
262
  r['ok'].should == true
245
- attachment = @db.fetch_attachment("attach-this","couchdb.png")
263
+ doc = @db.get("attach-this")
264
+ attachment = @db.fetch_attachment(doc,"couchdb.png")
246
265
  attachment.should == image
247
266
  end
248
267
  end
@@ -250,7 +269,7 @@ describe CouchRest::Database do
250
269
  describe "PUT document with attachment" do
251
270
  before(:each) do
252
271
  @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
253
- @doc = {
272
+ doc = {
254
273
  "_id" => "mydocwithattachment",
255
274
  "field" => ["some value"],
256
275
  "_attachments" => {
@@ -260,14 +279,14 @@ describe CouchRest::Database do
260
279
  }
261
280
  }
262
281
  }
263
- @db.save(@doc)
282
+ @db.save_doc(doc)
283
+ @doc = @db.get("mydocwithattachment")
264
284
  end
265
285
  it "should save and be indicated" do
266
- doc = @db.get("mydocwithattachment")
267
- doc['_attachments']['test.html']['length'].should == @attach.length
286
+ @doc['_attachments']['test.html']['length'].should == @attach.length
268
287
  end
269
288
  it "should be there" do
270
- attachment = @db.fetch_attachment("mydocwithattachment","test.html")
289
+ attachment = @db.fetch_attachment(@doc,"test.html")
271
290
  attachment.should == @attach
272
291
  end
273
292
  end
@@ -284,15 +303,16 @@ describe CouchRest::Database do
284
303
  }
285
304
  }
286
305
  }
287
- @db.save(doc)
288
- doc = @db.get('mydocwithattachment')
306
+ @db.save_doc(doc)
307
+ doc['_rev'].should_not be_nil
289
308
  doc['field'] << 'another value'
290
- @db.save(doc)
309
+ @db.save_doc(doc)["ok"].should be_true
291
310
  end
292
311
 
293
312
  it 'should be there' do
294
- attachment = @db.fetch_attachment('mydocwithattachment', 'test.html')
295
- attachment.should == @attach
313
+ doc = @db.get('mydocwithattachment')
314
+ attachment = @db.fetch_attachment(doc, 'test.html')
315
+ Base64.decode64(attachment).should == @attach
296
316
  end
297
317
  end
298
318
 
@@ -314,22 +334,43 @@ describe CouchRest::Database do
314
334
  }
315
335
  }
316
336
  }
317
- @db.save(@doc)
337
+ @db.save_doc(@doc)
338
+ @doc = @db.get("mydocwithattachment")
318
339
  end
319
340
  it "should save and be indicated" do
320
- doc = @db.get("mydocwithattachment")
321
- doc['_attachments']['test.html']['length'].should == @attach.length
322
- doc['_attachments']['other.html']['length'].should == @attach2.length
341
+ @doc['_attachments']['test.html']['length'].should == @attach.length
342
+ @doc['_attachments']['other.html']['length'].should == @attach2.length
323
343
  end
324
344
  it "should be there" do
325
- attachment = @db.fetch_attachment("mydocwithattachment","test.html")
345
+ attachment = @db.fetch_attachment(@doc,"test.html")
326
346
  attachment.should == @attach
327
347
  end
328
348
  it "should be there" do
329
- attachment = @db.fetch_attachment("mydocwithattachment","other.html")
349
+ attachment = @db.fetch_attachment(@doc,"other.html")
330
350
  attachment.should == @attach2
331
351
  end
332
352
  end
353
+
354
+ describe "DELETE an attachment directly from the database" do
355
+ before(:each) do
356
+ doc = {
357
+ '_id' => 'mydocwithattachment',
358
+ '_attachments' => {
359
+ 'test.html' => {
360
+ 'type' => 'text/html',
361
+ 'data' => "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
362
+ }
363
+ }
364
+ }
365
+ @db.save_doc(doc)
366
+ @doc = @db.get('mydocwithattachment')
367
+ end
368
+ it "should delete the attachment" do
369
+ lambda { @db.fetch_attachment(@doc,'test.html') }.should_not raise_error
370
+ @db.delete_attachment(@doc, "test.html")
371
+ lambda { @db.fetch_attachment(@doc,'test.html') }.should raise_error(RestClient::ResourceNotFound)
372
+ end
373
+ end
333
374
 
334
375
  describe "POST document with attachment (with funky name)" do
335
376
  before(:each) do
@@ -343,14 +384,15 @@ describe CouchRest::Database do
343
384
  }
344
385
  }
345
386
  }
346
- @docid = @db.save(@doc)['id']
387
+ @docid = @db.save_doc(@doc)['id']
347
388
  end
348
389
  it "should save and be indicated" do
349
390
  doc = @db.get(@docid)
350
391
  doc['_attachments']['http://example.com/stuff.cgi?things=and%20stuff']['length'].should == @attach.length
351
392
  end
352
393
  it "should be there" do
353
- attachment = @db.fetch_attachment(@docid,"http://example.com/stuff.cgi?things=and%20stuff")
394
+ doc = @db.get(@docid)
395
+ attachment = @db.fetch_attachment(doc,"http://example.com/stuff.cgi?things=and%20stuff")
354
396
  attachment.should == @attach
355
397
  end
356
398
  end
@@ -358,15 +400,15 @@ describe CouchRest::Database do
358
400
  describe "PUT (new document with url id)" do
359
401
  it "should create the document" do
360
402
  @docid = "http://example.com/stuff.cgi?things=and%20stuff"
361
- @db.save({'_id' => @docid, 'will-exist' => 'here'})
362
- lambda{@db.save({'_id' => @docid})}.should raise_error(RestClient::Request::RequestFailed)
403
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
404
+ lambda{@db.save_doc({'_id' => @docid})}.should raise_error(RestClient::Request::RequestFailed)
363
405
  @db.get(@docid)['will-exist'].should == 'here'
364
406
  end
365
407
  end
366
408
 
367
409
  describe "PUT (new document with id)" do
368
410
  it "should start without the document" do
369
- # r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
411
+ # r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
370
412
  @db.documents['rows'].each do |doc|
371
413
  doc['id'].should_not == 'my-doc'
372
414
  end
@@ -375,17 +417,17 @@ describe CouchRest::Database do
375
417
  # or instead make it return something with a fancy <=> method
376
418
  end
377
419
  it "should create the document" do
378
- @db.save({'_id' => 'my-doc', 'will-exist' => 'here'})
379
- lambda{@db.save({'_id' => 'my-doc'})}.should raise_error(RestClient::Request::RequestFailed)
420
+ @db.save_doc({'_id' => 'my-doc', 'will-exist' => 'here'})
421
+ lambda{@db.save_doc({'_id' => 'my-doc'})}.should raise_error(RestClient::Request::RequestFailed)
380
422
  end
381
423
  end
382
424
 
383
425
  describe "PUT (existing document with rev)" do
384
426
  before(:each) do
385
- @db.save({'_id' => 'my-doc', 'will-exist' => 'here'})
427
+ @db.save_doc({'_id' => 'my-doc', 'will-exist' => 'here'})
386
428
  @doc = @db.get('my-doc')
387
429
  @docid = "http://example.com/stuff.cgi?things=and%20stuff"
388
- @db.save({'_id' => @docid, 'now' => 'save'})
430
+ @db.save_doc({'_id' => @docid, 'now' => 'save'})
389
431
  end
390
432
  it "should start with the document" do
391
433
  @doc['will-exist'].should == 'here'
@@ -394,18 +436,18 @@ describe CouchRest::Database do
394
436
  it "should save with url id" do
395
437
  doc = @db.get(@docid)
396
438
  doc['yaml'] = ['json', 'word.']
397
- @db.save doc
439
+ @db.save_doc doc
398
440
  @db.get(@docid)['yaml'].should == ['json', 'word.']
399
441
  end
400
442
  it "should fail to resave without the rev" do
401
443
  @doc['them-keys'] = 'huge'
402
444
  @doc['_rev'] = 'wrong'
403
- # @db.save(@doc)
404
- lambda {@db.save(@doc)}.should raise_error
445
+ # @db.save_doc(@doc)
446
+ lambda {@db.save_doc(@doc)}.should raise_error
405
447
  end
406
448
  it "should update the document" do
407
449
  @doc['them-keys'] = 'huge'
408
- @db.save(@doc)
450
+ @db.save_doc(@doc)
409
451
  now = @db.get('my-doc')
410
452
  now['them-keys'].should == 'huge'
411
453
  end
@@ -414,7 +456,7 @@ describe CouchRest::Database do
414
456
  describe "cached bulk save" do
415
457
  it "stores documents in a database-specific cache" do
416
458
  td = {"_id" => "btd1", "val" => "test"}
417
- @db.save(td, true)
459
+ @db.save_doc(td, true)
418
460
  @db.instance_variable_get("@bulk_save_cache").should == [td]
419
461
 
420
462
  end
@@ -423,8 +465,8 @@ describe CouchRest::Database do
423
465
  @db.bulk_save_cache_limit = 3
424
466
  td1 = {"_id" => "td1", "val" => true}
425
467
  td2 = {"_id" => "td2", "val" => 4}
426
- @db.save(td1, true)
427
- @db.save(td2, true)
468
+ @db.save_doc(td1, true)
469
+ @db.save_doc(td2, true)
428
470
  lambda do
429
471
  @db.get(td1["_id"])
430
472
  end.should raise_error(RestClient::ResourceNotFound)
@@ -432,7 +474,7 @@ describe CouchRest::Database do
432
474
  @db.get(td2["_id"])
433
475
  end.should raise_error(RestClient::ResourceNotFound)
434
476
  td3 = {"_id" => "td3", "val" => "foo"}
435
- @db.save(td3, true)
477
+ @db.save_doc(td3, true)
436
478
  @db.get(td1["_id"])["val"].should == td1["val"]
437
479
  @db.get(td2["_id"])["val"].should == td2["val"]
438
480
  @db.get(td3["_id"])["val"].should == td3["val"]
@@ -442,11 +484,11 @@ describe CouchRest::Database do
442
484
  td1 = {"_id" => "blah", "val" => true}
443
485
  td2 = {"_id" => "steve", "val" => 3}
444
486
  @db.bulk_save_cache_limit = 50
445
- @db.save(td1, true)
487
+ @db.save_doc(td1, true)
446
488
  lambda do
447
489
  @db.get(td1["_id"])
448
490
  end.should raise_error(RestClient::ResourceNotFound)
449
- @db.save(td2)
491
+ @db.save_doc(td2)
450
492
  @db.get(td1["_id"])["val"].should == td1["val"]
451
493
  @db.get(td2["_id"])["val"].should == td2["val"]
452
494
  end
@@ -454,27 +496,27 @@ describe CouchRest::Database do
454
496
 
455
497
  describe "DELETE existing document" do
456
498
  before(:each) do
457
- @r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
499
+ @r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
458
500
  @docid = "http://example.com/stuff.cgi?things=and%20stuff"
459
- @db.save({'_id' => @docid, 'will-exist' => 'here'})
501
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
460
502
  end
461
503
  it "should work" do
462
504
  doc = @db.get(@r['id'])
463
505
  doc['and'].should == 'spain'
464
- @db.delete doc
506
+ @db.delete_doc doc
465
507
  lambda{@db.get @r['id']}.should raise_error
466
508
  end
467
509
  it "should work with uri id" do
468
510
  doc = @db.get(@docid)
469
- @db.delete doc
511
+ @db.delete_doc doc
470
512
  lambda{@db.get @docid}.should raise_error
471
513
  end
472
514
  it "should fail without an _id" do
473
- lambda{@db.delete({"not"=>"a real doc"})}.should raise_error(ArgumentError)
515
+ lambda{@db.delete_doc({"not"=>"a real doc"})}.should raise_error(ArgumentError)
474
516
  end
475
517
  it "should defer actual deletion when using bulk save" do
476
518
  doc = @db.get(@docid)
477
- @db.delete doc, true
519
+ @db.delete_doc doc, true
478
520
  lambda{@db.get @docid}.should_not raise_error
479
521
  @db.bulk_save
480
522
  lambda{@db.get @docid}.should raise_error
@@ -484,13 +526,13 @@ describe CouchRest::Database do
484
526
 
485
527
  describe "COPY existing document" do
486
528
  before :each do
487
- @r = @db.save({'artist' => 'Zappa', 'title' => 'Muffin Man'})
529
+ @r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'})
488
530
  @docid = 'tracks/zappa/muffin-man'
489
531
  @doc = @db.get(@r['id'])
490
532
  end
491
533
  describe "to a new location" do
492
534
  it "should work" do
493
- @db.copy @doc, @docid
535
+ @db.copy_doc @doc, @docid
494
536
  newdoc = @db.get(@docid)
495
537
  newdoc['artist'].should == 'Zappa'
496
538
  end
@@ -500,73 +542,30 @@ describe CouchRest::Database do
500
542
  end
501
543
  describe "to an existing location" do
502
544
  before :each do
503
- @db.save({'_id' => @docid, 'will-exist' => 'here'})
545
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
504
546
  end
505
547
  it "should fail without a rev" do
506
- lambda{@db.copy @doc, @docid}.should raise_error(RestClient::RequestFailed)
548
+ lambda{@db.copy_doc @doc, @docid}.should raise_error(RestClient::RequestFailed)
507
549
  end
508
550
  it "should succeed with a rev" do
509
551
  @to_be_overwritten = @db.get(@docid)
510
- @db.copy @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
552
+ @db.copy_doc @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
511
553
  newdoc = @db.get(@docid)
512
554
  newdoc['artist'].should == 'Zappa'
513
555
  end
514
556
  it "should succeed given the doc to overwrite" do
515
557
  @to_be_overwritten = @db.get(@docid)
516
- @db.copy @doc, @to_be_overwritten
558
+ @db.copy_doc @doc, @to_be_overwritten
517
559
  newdoc = @db.get(@docid)
518
560
  newdoc['artist'].should == 'Zappa'
519
561
  end
520
562
  end
521
563
  end
522
564
 
523
- describe "MOVE existing document" do
524
- before :each do
525
- @r = @db.save({'artist' => 'Zappa', 'title' => 'Muffin Man'})
526
- @docid = 'tracks/zappa/muffin-man'
527
- @doc = @db.get(@r['id'])
528
- end
529
- describe "to a new location" do
530
- it "should work" do
531
- @db.move @doc, @docid
532
- newdoc = @db.get(@docid)
533
- newdoc['artist'].should == 'Zappa'
534
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
535
- end
536
- it "should fail without an _id or _rev" do
537
- lambda{@db.move({"not"=>"a real doc"})}.should raise_error(ArgumentError)
538
- lambda{@db.move({"_id"=>"not a real doc"})}.should raise_error(ArgumentError)
539
- end
540
- end
541
- describe "to an existing location" do
542
- before :each do
543
- @db.save({'_id' => @docid, 'will-exist' => 'here'})
544
- end
545
- it "should fail without a rev" do
546
- lambda{@db.move @doc, @docid}.should raise_error(RestClient::RequestFailed)
547
- lambda{@db.get(@r['id'])}.should_not raise_error
548
- end
549
- it "should succeed with a rev" do
550
- @to_be_overwritten = @db.get(@docid)
551
- @db.move @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
552
- newdoc = @db.get(@docid)
553
- newdoc['artist'].should == 'Zappa'
554
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
555
- end
556
- it "should succeed given the doc to overwrite" do
557
- @to_be_overwritten = @db.get(@docid)
558
- @db.move @doc, @to_be_overwritten
559
- newdoc = @db.get(@docid)
560
- newdoc['artist'].should == 'Zappa'
561
- lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound)
562
- end
563
- end
564
- end
565
-
566
565
 
567
566
  it "should list documents" do
568
567
  5.times do
569
- @db.save({'another' => 'doc', 'will-exist' => 'anywhere'})
568
+ @db.save_doc({'another' => 'doc', 'will-exist' => 'anywhere'})
570
569
  end
571
570
  ds = @db.documents
572
571
  ds['rows'].should be_an_instance_of(Array)
@@ -577,7 +576,7 @@ describe CouchRest::Database do
577
576
  describe "documents / _all_docs" do
578
577
  before(:each) do
579
578
  9.times do |i|
580
- @db.save({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
579
+ @db.save_doc({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
581
580
  end
582
581
  end
583
582
  it "should list documents with keys and such" do
@@ -624,6 +623,72 @@ describe CouchRest::Database do
624
623
  @cr.databases.should_not include('couchrest-test')
625
624
  end
626
625
  end
626
+
627
+ describe "replicating a database" do
628
+ before do
629
+ @db.save_doc({'_id' => 'test_doc', 'some-value' => 'foo'})
630
+ @other_db = @cr.database 'couchrest-test-replication'
631
+ @other_db.delete! rescue nil
632
+ @other_db = @cr.create_db 'couchrest-test-replication'
633
+ end
634
+
635
+ describe "via pulling" do
636
+ before do
637
+ @other_db.replicate_from @db
638
+ end
639
+
640
+ it "contains the document from the original database" do
641
+ doc = @other_db.get('test_doc')
642
+ doc['some-value'].should == 'foo'
643
+ end
644
+ end
645
+
646
+ describe "via pushing" do
647
+ before do
648
+ @db.replicate_to @other_db
649
+ end
650
+
651
+ it "copies the document to the other database" do
652
+ doc = @other_db.get('test_doc')
653
+ doc['some-value'].should == 'foo'
654
+ end
655
+ end
656
+ end
657
+
658
+ describe "creating a database" do
659
+ before(:each) do
660
+ @db = @cr.database('couchrest-test-db_to_create')
661
+ @db.delete! if @cr.databases.include?('couchrest-test-db_to_create')
662
+ end
663
+
664
+ it "should just work fine" do
665
+ @cr.databases.should_not include('couchrest-test-db_to_create')
666
+ @db.create!
667
+ @cr.databases.should include('couchrest-test-db_to_create')
668
+ end
669
+ end
670
+
671
+ describe "recreating a database" do
672
+ before(:each) do
673
+ @db = @cr.database('couchrest-test-db_to_create')
674
+ @db2 = @cr.database('couchrest-test-db_to_recreate')
675
+ @cr.databases.include?(@db.name) ? nil : @db.create!
676
+ @cr.databases.include?(@db2.name) ? @db2.delete! : nil
677
+ end
678
+
679
+ it "should drop and recreate a database" do
680
+ @cr.databases.should include(@db.name)
681
+ @db.recreate!
682
+ @cr.databases.should include(@db.name)
683
+ end
684
+
685
+ it "should recreate a db even tho it doesn't exist" do
686
+ @cr.databases.should_not include(@db2.name)
687
+ @db2.recreate!
688
+ @cr.databases.should include(@db2.name)
689
+ end
690
+
691
+ end
627
692
 
628
693
 
629
- end
694
+ end