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.
- data/README.md +33 -8
- data/Rakefile +11 -2
- data/examples/model/example.rb +19 -13
- data/lib/couchrest.rb +70 -11
- data/lib/couchrest/core/database.rb +121 -62
- data/lib/couchrest/core/design.rb +7 -17
- data/lib/couchrest/core/document.rb +42 -30
- data/lib/couchrest/core/response.rb +16 -0
- data/lib/couchrest/core/server.rb +47 -10
- data/lib/couchrest/helper/upgrade.rb +51 -0
- data/lib/couchrest/mixins.rb +4 -0
- data/lib/couchrest/mixins/attachments.rb +31 -0
- data/lib/couchrest/mixins/callbacks.rb +483 -0
- data/lib/couchrest/mixins/class_proxy.rb +108 -0
- data/lib/couchrest/mixins/design_doc.rb +90 -0
- data/lib/couchrest/mixins/document_queries.rb +44 -0
- data/lib/couchrest/mixins/extended_attachments.rb +68 -0
- data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
- data/lib/couchrest/mixins/properties.rb +129 -0
- data/lib/couchrest/mixins/validation.rb +242 -0
- data/lib/couchrest/mixins/views.rb +169 -0
- data/lib/couchrest/monkeypatches.rb +81 -6
- data/lib/couchrest/more/casted_model.rb +28 -0
- data/lib/couchrest/more/extended_document.rb +215 -0
- data/lib/couchrest/more/property.rb +40 -0
- data/lib/couchrest/support/blank.rb +42 -0
- data/lib/couchrest/support/class.rb +176 -0
- data/lib/couchrest/validation/auto_validate.rb +163 -0
- data/lib/couchrest/validation/contextual_validators.rb +78 -0
- data/lib/couchrest/validation/validation_errors.rb +118 -0
- data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
- data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
- data/lib/couchrest/validation/validators/format_validator.rb +117 -0
- data/lib/couchrest/validation/validators/formats/email.rb +66 -0
- data/lib/couchrest/validation/validators/formats/url.rb +43 -0
- data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
- data/lib/couchrest/validation/validators/length_validator.rb +134 -0
- data/lib/couchrest/validation/validators/method_validator.rb +89 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
- data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
- data/spec/couchrest/core/database_spec.rb +189 -124
- data/spec/couchrest/core/design_spec.rb +13 -6
- data/spec/couchrest/core/document_spec.rb +231 -177
- data/spec/couchrest/core/server_spec.rb +35 -0
- data/spec/couchrest/helpers/pager_spec.rb +1 -1
- data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
- data/spec/couchrest/more/casted_model_spec.rb +98 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
- data/spec/couchrest/more/extended_doc_spec.rb +509 -0
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
- data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
- data/spec/couchrest/more/property_spec.rb +136 -0
- data/spec/fixtures/more/article.rb +34 -0
- data/spec/fixtures/more/card.rb +20 -0
- data/spec/fixtures/more/course.rb +14 -0
- data/spec/fixtures/more/event.rb +6 -0
- data/spec/fixtures/more/invoice.rb +17 -0
- data/spec/fixtures/more/person.rb +8 -0
- data/spec/fixtures/more/question.rb +6 -0
- data/spec/fixtures/more/service.rb +12 -0
- data/spec/spec_helper.rb +13 -7
- metadata +58 -4
- data/lib/couchrest/core/model.rb +0 -613
- 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.
|
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.
|
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.
|
140
|
+
@r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
|
133
141
|
@docid = "http://example.com/stuff.cgi?things=and%20stuff"
|
134
|
-
@db.
|
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
|
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
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|
-
|
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.
|
282
|
+
@db.save_doc(doc)
|
283
|
+
@doc = @db.get("mydocwithattachment")
|
264
284
|
end
|
265
285
|
it "should save and be indicated" do
|
266
|
-
doc
|
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(
|
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.
|
288
|
-
doc
|
306
|
+
@db.save_doc(doc)
|
307
|
+
doc['_rev'].should_not be_nil
|
289
308
|
doc['field'] << 'another value'
|
290
|
-
@db.
|
309
|
+
@db.save_doc(doc)["ok"].should be_true
|
291
310
|
end
|
292
311
|
|
293
312
|
it 'should be there' do
|
294
|
-
|
295
|
-
attachment
|
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.
|
337
|
+
@db.save_doc(@doc)
|
338
|
+
@doc = @db.get("mydocwithattachment")
|
318
339
|
end
|
319
340
|
it "should save and be indicated" do
|
320
|
-
doc
|
321
|
-
doc['_attachments']['
|
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(
|
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(
|
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.
|
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
|
-
|
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.
|
362
|
-
lambda{@db.
|
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.
|
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.
|
379
|
-
lambda{@db.
|
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.
|
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.
|
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.
|
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.
|
404
|
-
lambda {@db.
|
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.
|
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.
|
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.
|
427
|
-
@db.
|
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.
|
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.
|
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.
|
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.
|
499
|
+
@r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
|
458
500
|
@docid = "http://example.com/stuff.cgi?things=and%20stuff"
|
459
|
-
@db.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
545
|
+
@db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
|
504
546
|
end
|
505
547
|
it "should fail without a rev" do
|
506
|
-
lambda{@db.
|
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.
|
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.
|
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.
|
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.
|
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
|