couchrest 0.38 → 1.0.0.beta
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 +8 -8
- data/Rakefile +3 -4
- data/couchrest.gemspec +25 -105
- data/history.txt +5 -4
- data/lib/couchrest.rb +31 -52
- data/lib/couchrest/{core/database.rb → database.rb} +6 -11
- data/lib/couchrest/{core/design.rb → design.rb} +2 -2
- data/lib/couchrest/{core/document.rb → document.rb} +1 -1
- data/lib/couchrest/helper/attachments.rb +29 -0
- data/lib/couchrest/middlewares/logger.rb +3 -3
- data/lib/couchrest/monkeypatches.rb +1 -71
- data/lib/couchrest/{core/response.rb → response.rb} +0 -0
- data/lib/couchrest/{core/rest_api.rb → rest_api.rb} +8 -12
- data/lib/couchrest/{core/server.rb → server.rb} +0 -2
- data/spec/couchrest/{core/couchrest_spec.rb → couchrest_spec.rb} +15 -9
- data/spec/couchrest/{core/database_spec.rb → database_spec.rb} +4 -4
- data/spec/couchrest/{core/design_spec.rb → design_spec.rb} +2 -2
- data/spec/couchrest/{core/document_spec.rb → document_spec.rb} +1 -1
- data/spec/couchrest/{core/server_spec.rb → server_spec.rb} +2 -2
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -4
- metadata +32 -133
- data/examples/model/example.rb +0 -144
- data/lib/couchrest/core/adapters/restclient.rb +0 -35
- data/lib/couchrest/core/http_abstraction.rb +0 -48
- data/lib/couchrest/core/view.rb +0 -4
- data/lib/couchrest/mixins.rb +0 -4
- data/lib/couchrest/mixins/attachments.rb +0 -31
- data/lib/couchrest/mixins/attribute_protection.rb +0 -74
- data/lib/couchrest/mixins/callbacks.rb +0 -532
- data/lib/couchrest/mixins/class_proxy.rb +0 -124
- data/lib/couchrest/mixins/collection.rb +0 -260
- data/lib/couchrest/mixins/design_doc.rb +0 -103
- data/lib/couchrest/mixins/document_queries.rb +0 -80
- data/lib/couchrest/mixins/extended_attachments.rb +0 -70
- data/lib/couchrest/mixins/extended_document_mixins.rb +0 -9
- data/lib/couchrest/mixins/properties.rb +0 -158
- data/lib/couchrest/mixins/validation.rb +0 -246
- data/lib/couchrest/mixins/views.rb +0 -173
- data/lib/couchrest/more/casted_model.rb +0 -58
- data/lib/couchrest/more/extended_document.rb +0 -310
- data/lib/couchrest/more/property.rb +0 -58
- data/lib/couchrest/more/typecast.rb +0 -180
- data/lib/couchrest/support/blank.rb +0 -42
- data/lib/couchrest/support/rails.rb +0 -42
- data/lib/couchrest/validation/auto_validate.rb +0 -157
- data/lib/couchrest/validation/contextual_validators.rb +0 -78
- data/lib/couchrest/validation/validation_errors.rb +0 -125
- data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
- data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -107
- data/lib/couchrest/validation/validators/format_validator.rb +0 -122
- data/lib/couchrest/validation/validators/formats/email.rb +0 -66
- data/lib/couchrest/validation/validators/formats/url.rb +0 -43
- data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
- data/lib/couchrest/validation/validators/length_validator.rb +0 -139
- data/lib/couchrest/validation/validators/method_validator.rb +0 -89
- data/lib/couchrest/validation/validators/numeric_validator.rb +0 -109
- data/lib/couchrest/validation/validators/required_field_validator.rb +0 -114
- data/spec/couchrest/more/attribute_protection_spec.rb +0 -150
- data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -73
- data/spec/couchrest/more/casted_model_spec.rb +0 -406
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -135
- data/spec/couchrest/more/extended_doc_inherited_spec.rb +0 -40
- data/spec/couchrest/more/extended_doc_spec.rb +0 -807
- data/spec/couchrest/more/extended_doc_subclass_spec.rb +0 -98
- data/spec/couchrest/more/extended_doc_view_spec.rb +0 -456
- data/spec/couchrest/more/property_spec.rb +0 -628
- data/spec/fixtures/more/article.rb +0 -35
- data/spec/fixtures/more/card.rb +0 -22
- data/spec/fixtures/more/cat.rb +0 -20
- data/spec/fixtures/more/course.rb +0 -22
- data/spec/fixtures/more/event.rb +0 -8
- data/spec/fixtures/more/invoice.rb +0 -17
- data/spec/fixtures/more/person.rb +0 -9
- data/spec/fixtures/more/question.rb +0 -6
- data/spec/fixtures/more/service.rb +0 -12
- data/spec/fixtures/more/user.rb +0 -22
@@ -1,109 +0,0 @@
|
|
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 = (defined?(BigDecimal) && value.kind_of?(BigDecimal)) ? 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_numericality_of(*fields)
|
98
|
-
opts = opts_from_validator_args(fields)
|
99
|
-
add_validator_to_context(opts, fields, CouchRest::Validation::NumericValidator)
|
100
|
-
end
|
101
|
-
|
102
|
-
def validates_is_number(*fields)
|
103
|
-
warn "[DEPRECATION] `validates_is_number` is deprecated. Please use `validates_numericality_of` instead."
|
104
|
-
validates_numericality_of(*fields)
|
105
|
-
end
|
106
|
-
|
107
|
-
end # module ValidatesIsNumber
|
108
|
-
end # module Validation
|
109
|
-
end # module CouchRest
|
@@ -1,114 +0,0 @@
|
|
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.to_s)
|
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 == 'Boolean' : 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_presence_of :required_attribute
|
97
|
-
# validates_presence_of :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_presence_of(*fields)
|
103
|
-
opts = opts_from_validator_args(fields)
|
104
|
-
add_validator_to_context(opts, fields, CouchRest::Validation::RequiredFieldValidator)
|
105
|
-
end
|
106
|
-
|
107
|
-
def validates_present(*fields)
|
108
|
-
warn "[DEPRECATION] `validates_present` is deprecated. Please use `validates_presence_of` instead."
|
109
|
-
validates_presence_of(*fields)
|
110
|
-
end
|
111
|
-
|
112
|
-
end # module ValidatesPresent
|
113
|
-
end # module Validation
|
114
|
-
end # module CouchRest
|
@@ -1,150 +0,0 @@
|
|
1
|
-
require File.expand_path("../../../spec_helper", __FILE__)
|
2
|
-
|
3
|
-
describe "ExtendedDocument", "no declarations" do
|
4
|
-
class NoProtection < CouchRest::ExtendedDocument
|
5
|
-
use_database TEST_SERVER.default_database
|
6
|
-
property :name
|
7
|
-
property :phone
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should not protect anything through new" do
|
11
|
-
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
12
|
-
|
13
|
-
user.name.should == "will"
|
14
|
-
user.phone.should == "555-5555"
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should not protect anything through attributes=" do
|
18
|
-
user = NoProtection.new
|
19
|
-
user.attributes = {:name => "will", :phone => "555-5555"}
|
20
|
-
|
21
|
-
user.name.should == "will"
|
22
|
-
user.phone.should == "555-5555"
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should recreate from the database properly" do
|
26
|
-
user = NoProtection.new
|
27
|
-
user.name = "will"
|
28
|
-
user.phone = "555-5555"
|
29
|
-
user.save!
|
30
|
-
|
31
|
-
user = NoProtection.get(user.id)
|
32
|
-
user.name.should == "will"
|
33
|
-
user.phone.should == "555-5555"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "ExtendedDocument", "accessible flag" do
|
38
|
-
class WithAccessible < CouchRest::ExtendedDocument
|
39
|
-
use_database TEST_SERVER.default_database
|
40
|
-
property :name, :accessible => true
|
41
|
-
property :admin, :default => false
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should recognize accessible properties" do
|
45
|
-
props = WithAccessible.accessible_properties.map { |prop| prop.name}
|
46
|
-
props.should include("name")
|
47
|
-
props.should_not include("admin")
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should protect non-accessible properties set through new" do
|
51
|
-
user = WithAccessible.new(:name => "will", :admin => true)
|
52
|
-
|
53
|
-
user.name.should == "will"
|
54
|
-
user.admin.should == false
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should protect non-accessible properties set through attributes=" do
|
58
|
-
user = WithAccessible.new
|
59
|
-
user.attributes = {:name => "will", :admin => true}
|
60
|
-
|
61
|
-
user.name.should == "will"
|
62
|
-
user.admin.should == false
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "ExtendedDocument", "protected flag" do
|
67
|
-
class WithProtected < CouchRest::ExtendedDocument
|
68
|
-
use_database TEST_SERVER.default_database
|
69
|
-
property :name
|
70
|
-
property :admin, :default => false, :protected => true
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should recognize protected properties" do
|
74
|
-
props = WithProtected.protected_properties.map { |prop| prop.name}
|
75
|
-
props.should_not include("name")
|
76
|
-
props.should include("admin")
|
77
|
-
end
|
78
|
-
|
79
|
-
it "should protect non-accessible properties set through new" do
|
80
|
-
user = WithProtected.new(:name => "will", :admin => true)
|
81
|
-
|
82
|
-
user.name.should == "will"
|
83
|
-
user.admin.should == false
|
84
|
-
end
|
85
|
-
|
86
|
-
it "should protect non-accessible properties set through attributes=" do
|
87
|
-
user = WithProtected.new
|
88
|
-
user.attributes = {:name => "will", :admin => true}
|
89
|
-
|
90
|
-
user.name.should == "will"
|
91
|
-
user.admin.should == false
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
describe "ExtendedDocument", "protected flag" do
|
96
|
-
class WithBoth < CouchRest::ExtendedDocument
|
97
|
-
use_database TEST_SERVER.default_database
|
98
|
-
property :name, :accessible => true
|
99
|
-
property :admin, :default => false, :protected => true
|
100
|
-
end
|
101
|
-
|
102
|
-
it "should raise an error when both are set" do
|
103
|
-
lambda { WithBoth.new }.should raise_error
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
describe "ExtendedDocument", "from database" do
|
108
|
-
class WithProtected < CouchRest::ExtendedDocument
|
109
|
-
use_database TEST_SERVER.default_database
|
110
|
-
property :name
|
111
|
-
property :admin, :default => false, :protected => true
|
112
|
-
view_by :name
|
113
|
-
end
|
114
|
-
|
115
|
-
before(:each) do
|
116
|
-
@user = WithProtected.new
|
117
|
-
@user.name = "will"
|
118
|
-
@user.admin = true
|
119
|
-
@user.save!
|
120
|
-
end
|
121
|
-
|
122
|
-
def verify_attrs(user)
|
123
|
-
user.name.should == "will"
|
124
|
-
user.admin.should == true
|
125
|
-
end
|
126
|
-
|
127
|
-
it "ExtendedDocument#get should not strip protected attributes" do
|
128
|
-
reloaded = WithProtected.get( @user.id )
|
129
|
-
verify_attrs reloaded
|
130
|
-
end
|
131
|
-
|
132
|
-
it "ExtendedDocument#get! should not strip protected attributes" do
|
133
|
-
reloaded = WithProtected.get!( @user.id )
|
134
|
-
verify_attrs reloaded
|
135
|
-
end
|
136
|
-
|
137
|
-
it "ExtendedDocument#all should not strip protected attributes" do
|
138
|
-
# all creates a CollectionProxy
|
139
|
-
docs = WithProtected.all(:key => @user.id)
|
140
|
-
docs.size.should == 1
|
141
|
-
reloaded = docs.first
|
142
|
-
verify_attrs reloaded
|
143
|
-
end
|
144
|
-
|
145
|
-
it "views should not strip protected attributes" do
|
146
|
-
docs = WithProtected.by_name(:startkey => "will", :endkey => "will")
|
147
|
-
reloaded = docs.first
|
148
|
-
verify_attrs reloaded
|
149
|
-
end
|
150
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
-
require File.join(FIXTURE_PATH, 'more', 'card')
|
3
|
-
|
4
|
-
class Car < CouchRest::ExtendedDocument
|
5
|
-
use_database TEST_SERVER.default_database
|
6
|
-
|
7
|
-
property :name
|
8
|
-
property :driver, :cast_as => 'Driver'
|
9
|
-
end
|
10
|
-
|
11
|
-
class Driver < CouchRest::ExtendedDocument
|
12
|
-
use_database TEST_SERVER.default_database
|
13
|
-
# You have to add a casted_by accessor if you want to reach a casted extended doc parent
|
14
|
-
attr_accessor :casted_by
|
15
|
-
|
16
|
-
property :name
|
17
|
-
end
|
18
|
-
|
19
|
-
describe "casting an extended document" do
|
20
|
-
|
21
|
-
before(:each) do
|
22
|
-
@driver = Driver.new(:name => 'Matt')
|
23
|
-
@car = Car.new(:name => 'Renault 306', :driver => @driver)
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should retain all properties of the casted attribute" do
|
27
|
-
@car.driver.should == @driver
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should let the casted document know who casted it" do
|
31
|
-
@car.driver.casted_by.should == @car
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
describe "assigning a value to casted attribute after initializing an object" do
|
36
|
-
|
37
|
-
before(:each) do
|
38
|
-
@car = Car.new(:name => 'Renault 306')
|
39
|
-
@driver = Driver.new(:name => 'Matt')
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should not create an empty casted object" do
|
43
|
-
@car.driver.should be_nil
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should let you assign the value" do
|
47
|
-
@car.driver = @driver
|
48
|
-
@car.driver.name.should == 'Matt'
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should cast attribute" do
|
52
|
-
@car.driver = JSON.parse(@driver.to_json)
|
53
|
-
@car.driver.should be_instance_of(Driver)
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
describe "casting an extended document from parsed JSON" do
|
59
|
-
|
60
|
-
before(:each) do
|
61
|
-
@driver = Driver.new(:name => 'Matt')
|
62
|
-
@car = Car.new(:name => 'Renault 306', :driver => @driver)
|
63
|
-
@new_car = Car.new(JSON.parse(@car.to_json))
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should cast casted attribute" do
|
67
|
-
@new_car.driver.should be_instance_of(Driver)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "should retain all properties of the casted attribute" do
|
71
|
-
@new_car.driver.should == @driver
|
72
|
-
end
|
73
|
-
end
|
@@ -1,406 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require File.expand_path('../../../spec_helper', __FILE__)
|
4
|
-
require File.join(FIXTURE_PATH, 'more', 'card')
|
5
|
-
require File.join(FIXTURE_PATH, 'more', 'cat')
|
6
|
-
require File.join(FIXTURE_PATH, 'more', 'person')
|
7
|
-
require File.join(FIXTURE_PATH, 'more', 'question')
|
8
|
-
require File.join(FIXTURE_PATH, 'more', 'course')
|
9
|
-
|
10
|
-
|
11
|
-
class WithCastedModelMixin < Hash
|
12
|
-
include CouchRest::CastedModel
|
13
|
-
property :name
|
14
|
-
property :no_value
|
15
|
-
property :details, :type => 'Object', :default => {}
|
16
|
-
property :casted_attribute, :cast_as => 'WithCastedModelMixin'
|
17
|
-
end
|
18
|
-
|
19
|
-
class DummyModel < CouchRest::ExtendedDocument
|
20
|
-
use_database TEST_SERVER.default_database
|
21
|
-
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
22
|
-
property :casted_attribute, :cast_as => 'WithCastedModelMixin'
|
23
|
-
property :keywords, :cast_as => ["String"]
|
24
|
-
end
|
25
|
-
|
26
|
-
class CastedCallbackDoc < CouchRest::ExtendedDocument
|
27
|
-
use_database TEST_SERVER.default_database
|
28
|
-
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
29
|
-
property :callback_model, :cast_as => 'WithCastedCallBackModel'
|
30
|
-
end
|
31
|
-
class WithCastedCallBackModel < Hash
|
32
|
-
include CouchRest::CastedModel
|
33
|
-
include CouchRest::Validation
|
34
|
-
property :name
|
35
|
-
property :run_before_validate
|
36
|
-
property :run_after_validate
|
37
|
-
|
38
|
-
before_validate do |object|
|
39
|
-
object.run_before_validate = true
|
40
|
-
end
|
41
|
-
after_validate do |object|
|
42
|
-
object.run_after_validate = true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe CouchRest::CastedModel do
|
47
|
-
|
48
|
-
describe "A non hash class including CastedModel" do
|
49
|
-
it "should fail raising and include error" do
|
50
|
-
lambda do
|
51
|
-
class NotAHashButWithCastedModelMixin
|
52
|
-
include CouchRest::CastedModel
|
53
|
-
property :name
|
54
|
-
end
|
55
|
-
|
56
|
-
end.should raise_error
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe "isolated" do
|
61
|
-
before(:each) do
|
62
|
-
@obj = WithCastedModelMixin.new
|
63
|
-
end
|
64
|
-
it "should automatically include the property mixin and define getters and setters" do
|
65
|
-
@obj.name = 'Matt'
|
66
|
-
@obj.name.should == 'Matt'
|
67
|
-
end
|
68
|
-
|
69
|
-
it "should allow override of default" do
|
70
|
-
@obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'})
|
71
|
-
@obj.name.should == 'Eric'
|
72
|
-
@obj.details['color'].should == 'orange'
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
describe "casted as an attribute, but without a value" do
|
77
|
-
before(:each) do
|
78
|
-
@obj = DummyModel.new
|
79
|
-
@casted_obj = @obj.casted_attribute
|
80
|
-
end
|
81
|
-
it "should be nil" do
|
82
|
-
@casted_obj.should == nil
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
describe "casted as attribute" do
|
87
|
-
before(:each) do
|
88
|
-
casted = {:name => 'not whatever'}
|
89
|
-
@obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted})
|
90
|
-
@casted_obj = @obj.casted_attribute
|
91
|
-
end
|
92
|
-
|
93
|
-
it "should be available from its parent" do
|
94
|
-
@casted_obj.should be_an_instance_of(WithCastedModelMixin)
|
95
|
-
end
|
96
|
-
|
97
|
-
it "should have the getters defined" do
|
98
|
-
@casted_obj.name.should == 'whatever'
|
99
|
-
end
|
100
|
-
|
101
|
-
it "should know who casted it" do
|
102
|
-
@casted_obj.casted_by.should == @obj
|
103
|
-
end
|
104
|
-
|
105
|
-
it "should return nil for the 'no_value' attribute" do
|
106
|
-
@casted_obj.no_value.should be_nil
|
107
|
-
end
|
108
|
-
|
109
|
-
it "should return nil for the unknown attribute" do
|
110
|
-
@casted_obj["unknown"].should be_nil
|
111
|
-
end
|
112
|
-
|
113
|
-
it "should return {} for the hash attribute" do
|
114
|
-
@casted_obj.details.should == {}
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should cast its own attributes" do
|
118
|
-
@casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
describe "casted as an array of a different type" do
|
123
|
-
before(:each) do
|
124
|
-
@obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
|
125
|
-
end
|
126
|
-
|
127
|
-
it "should cast the array propery" do
|
128
|
-
@obj.keywords.should be_an_instance_of(Array)
|
129
|
-
@obj.keywords.first.should == 'couch'
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "update attributes without saving" do
|
134
|
-
before(:each) do
|
135
|
-
@question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
|
136
|
-
end
|
137
|
-
it "should work for attribute= methods" do
|
138
|
-
@question.q.should == "What is your quest?"
|
139
|
-
@question['a'].should == "To seek the Holy Grail"
|
140
|
-
@question.update_attributes_without_saving(:q => "What is your favorite color?", 'a' => "Blue")
|
141
|
-
@question['q'].should == "What is your favorite color?"
|
142
|
-
@question.a.should == "Blue"
|
143
|
-
end
|
144
|
-
|
145
|
-
it "should also work for attributes= alias" do
|
146
|
-
@question.respond_to?(:attributes=).should be_true
|
147
|
-
@question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
|
148
|
-
@question['q'].should == "What is your favorite color?"
|
149
|
-
@question.a.should == "Blue"
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should flip out if an attribute= method is missing" do
|
153
|
-
lambda {
|
154
|
-
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
155
|
-
}.should raise_error(NoMethodError)
|
156
|
-
end
|
157
|
-
|
158
|
-
it "should not change any attributes if there is an error" do
|
159
|
-
lambda {
|
160
|
-
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
161
|
-
}.should raise_error(NoMethodError)
|
162
|
-
@question.q.should == "What is your quest?"
|
163
|
-
@question.a.should == "To seek the Holy Grail"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
describe "saved document with casted models" do
|
168
|
-
before(:each) do
|
169
|
-
reset_test_db!
|
170
|
-
@obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
|
171
|
-
@obj.save.should be_true
|
172
|
-
@obj = DummyModel.get(@obj.id)
|
173
|
-
end
|
174
|
-
|
175
|
-
it "should be able to load with the casted models" do
|
176
|
-
casted_obj = @obj.casted_attribute
|
177
|
-
casted_obj.should_not be_nil
|
178
|
-
casted_obj.should be_an_instance_of(WithCastedModelMixin)
|
179
|
-
end
|
180
|
-
|
181
|
-
it "should have defined getters for the casted model" do
|
182
|
-
casted_obj = @obj.casted_attribute
|
183
|
-
casted_obj.name.should == "whatever"
|
184
|
-
end
|
185
|
-
|
186
|
-
it "should have defined setters for the casted model" do
|
187
|
-
casted_obj = @obj.casted_attribute
|
188
|
-
casted_obj.name = "test"
|
189
|
-
casted_obj.name.should == "test"
|
190
|
-
end
|
191
|
-
|
192
|
-
it "should retain an override of a casted model attribute's default" do
|
193
|
-
casted_obj = @obj.casted_attribute
|
194
|
-
casted_obj.details['color'] = 'orange'
|
195
|
-
@obj.save
|
196
|
-
casted_obj = DummyModel.get(@obj.id).casted_attribute
|
197
|
-
casted_obj.details['color'].should == 'orange'
|
198
|
-
end
|
199
|
-
|
200
|
-
end
|
201
|
-
|
202
|
-
describe "saving document with array of casted models and validation" do
|
203
|
-
before :each do
|
204
|
-
@cat = Cat.new
|
205
|
-
@cat.save
|
206
|
-
end
|
207
|
-
|
208
|
-
it "should save" do
|
209
|
-
toy = CatToy.new :name => "Mouse"
|
210
|
-
@cat.toys.push(toy)
|
211
|
-
@cat.save.should be_true
|
212
|
-
@cat = Cat.get @cat.id
|
213
|
-
@cat.toys.class.should == CastedArray
|
214
|
-
@cat.toys.first.class.should == CatToy
|
215
|
-
@cat.toys.first.should === toy
|
216
|
-
end
|
217
|
-
|
218
|
-
it "should fail because name is not present" do
|
219
|
-
toy = CatToy.new
|
220
|
-
@cat.toys.push(toy)
|
221
|
-
@cat.should_not be_valid
|
222
|
-
@cat.save.should be_false
|
223
|
-
end
|
224
|
-
|
225
|
-
it "should not fail if the casted model doesn't have validation" do
|
226
|
-
Cat.property :masters, :cast_as => ['Person'], :default => []
|
227
|
-
Cat.validates_presence_of :name
|
228
|
-
cat = Cat.new(:name => 'kitty')
|
229
|
-
cat.should be_valid
|
230
|
-
cat.masters.push Person.new
|
231
|
-
cat.should be_valid
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
describe "calling valid?" do
|
236
|
-
before :each do
|
237
|
-
@cat = Cat.new
|
238
|
-
@toy1 = CatToy.new
|
239
|
-
@toy2 = CatToy.new
|
240
|
-
@toy3 = CatToy.new
|
241
|
-
@cat.favorite_toy = @toy1
|
242
|
-
@cat.toys << @toy2
|
243
|
-
@cat.toys << @toy3
|
244
|
-
end
|
245
|
-
|
246
|
-
describe "on the top document" do
|
247
|
-
it "should put errors on all invalid casted models" do
|
248
|
-
@cat.should_not be_valid
|
249
|
-
@cat.errors.should_not be_empty
|
250
|
-
@toy1.errors.should_not be_empty
|
251
|
-
@toy2.errors.should_not be_empty
|
252
|
-
@toy3.errors.should_not be_empty
|
253
|
-
end
|
254
|
-
|
255
|
-
it "should not put errors on valid casted models" do
|
256
|
-
@toy1.name = "Feather"
|
257
|
-
@toy2.name = "Twine"
|
258
|
-
@cat.should_not be_valid
|
259
|
-
@cat.errors.should_not be_empty
|
260
|
-
@toy1.errors.should be_empty
|
261
|
-
@toy2.errors.should be_empty
|
262
|
-
@toy3.errors.should_not be_empty
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
describe "on a casted model property" do
|
267
|
-
it "should only validate itself" do
|
268
|
-
@toy1.should_not be_valid
|
269
|
-
@toy1.errors.should_not be_empty
|
270
|
-
@cat.errors.should be_empty
|
271
|
-
@toy2.errors.should be_empty
|
272
|
-
@toy3.errors.should be_empty
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
describe "on a casted model inside a casted collection" do
|
277
|
-
it "should only validate itself" do
|
278
|
-
@toy2.should_not be_valid
|
279
|
-
@toy2.errors.should_not be_empty
|
280
|
-
@cat.errors.should be_empty
|
281
|
-
@toy1.errors.should be_empty
|
282
|
-
@toy3.errors.should be_empty
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
describe "calling new? on a casted model" do
|
288
|
-
before :each do
|
289
|
-
reset_test_db!
|
290
|
-
@cat = Cat.new(:name => 'Sockington')
|
291
|
-
@favorite_toy = CatToy.new(:name => 'Catnip Ball')
|
292
|
-
@cat.favorite_toy = @favorite_toy
|
293
|
-
@cat.toys << CatToy.new(:name => 'Fuzzy Stick')
|
294
|
-
end
|
295
|
-
|
296
|
-
it "should be true on new" do
|
297
|
-
CatToy.new.should be_new
|
298
|
-
CatToy.new.new_record?.should be_true
|
299
|
-
end
|
300
|
-
|
301
|
-
it "should be true after assignment" do
|
302
|
-
@cat.should be_new
|
303
|
-
@cat.favorite_toy.should be_new
|
304
|
-
@cat.toys.first.should be_new
|
305
|
-
end
|
306
|
-
|
307
|
-
it "should not be true after create or save" do
|
308
|
-
@cat.create
|
309
|
-
@cat.save
|
310
|
-
@cat.favorite_toy.should_not be_new
|
311
|
-
@cat.toys.first.should_not be_new
|
312
|
-
end
|
313
|
-
|
314
|
-
it "should not be true after get from the database" do
|
315
|
-
@cat.save
|
316
|
-
@cat = Cat.get(@cat.id)
|
317
|
-
@cat.favorite_toy.should_not be_new
|
318
|
-
@cat.toys.first.should_not be_new
|
319
|
-
end
|
320
|
-
|
321
|
-
it "should still be true after a failed create or save" do
|
322
|
-
@cat.name = nil
|
323
|
-
@cat.create.should be_false
|
324
|
-
@cat.save.should be_false
|
325
|
-
@cat.favorite_toy.should be_new
|
326
|
-
@cat.toys.first.should be_new
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
describe "calling base_doc from a nested casted model" do
|
331
|
-
before :each do
|
332
|
-
@course = Course.new(:title => 'Science 101')
|
333
|
-
@professor = Person.new(:name => 'Professor Plum')
|
334
|
-
@cat = Cat.new(:name => 'Scratchy')
|
335
|
-
@toy1 = CatToy.new
|
336
|
-
@toy2 = CatToy.new
|
337
|
-
@course.professor = @professor
|
338
|
-
@professor.pet = @cat
|
339
|
-
@cat.favorite_toy = @toy1
|
340
|
-
@cat.toys << @toy2
|
341
|
-
end
|
342
|
-
|
343
|
-
it "should reference the top document for" do
|
344
|
-
@course.base_doc.should === @course
|
345
|
-
@professor.casted_by.should === @course
|
346
|
-
@professor.base_doc.should === @course
|
347
|
-
@cat.base_doc.should === @course
|
348
|
-
@toy1.base_doc.should === @course
|
349
|
-
@toy2.base_doc.should === @course
|
350
|
-
end
|
351
|
-
|
352
|
-
it "should call setter on top document" do
|
353
|
-
@toy1.base_doc.should_not be_nil
|
354
|
-
@toy1.base_doc.title = 'Tom Foolery'
|
355
|
-
@course.title.should == 'Tom Foolery'
|
356
|
-
end
|
357
|
-
|
358
|
-
it "should return nil if not yet casted" do
|
359
|
-
person = Person.new
|
360
|
-
person.base_doc.should == nil
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
describe "calling base_doc.save from a nested casted model" do
|
365
|
-
before :each do
|
366
|
-
reset_test_db!
|
367
|
-
@cat = Cat.new(:name => 'Snowball')
|
368
|
-
@toy = CatToy.new
|
369
|
-
@cat.favorite_toy = @toy
|
370
|
-
end
|
371
|
-
|
372
|
-
it "should not save parent document when casted model is invalid" do
|
373
|
-
@toy.should_not be_valid
|
374
|
-
@toy.base_doc.save.should be_false
|
375
|
-
lambda{@toy.base_doc.save!}.should raise_error
|
376
|
-
end
|
377
|
-
|
378
|
-
it "should save parent document when nested casted model is valid" do
|
379
|
-
@toy.name = "Mr Squeaks"
|
380
|
-
@toy.should be_valid
|
381
|
-
@toy.base_doc.save.should be_true
|
382
|
-
lambda{@toy.base_doc.save!}.should_not raise_error
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
describe "callbacks" do
|
387
|
-
before(:each) do
|
388
|
-
@doc = CastedCallbackDoc.new
|
389
|
-
@model = WithCastedCallBackModel.new
|
390
|
-
@doc.callback_model = @model
|
391
|
-
end
|
392
|
-
|
393
|
-
describe "validate" do
|
394
|
-
it "should run before_validate before validating" do
|
395
|
-
@model.run_before_validate.should be_nil
|
396
|
-
@model.should be_valid
|
397
|
-
@model.run_before_validate.should be_true
|
398
|
-
end
|
399
|
-
it "should run after_validate after validating" do
|
400
|
-
@model.run_after_validate.should be_nil
|
401
|
-
@model.should be_valid
|
402
|
-
@model.run_after_validate.should be_true
|
403
|
-
end
|
404
|
-
end
|
405
|
-
end
|
406
|
-
end
|