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.
Files changed (77) hide show
  1. data/README.md +8 -8
  2. data/Rakefile +3 -4
  3. data/couchrest.gemspec +25 -105
  4. data/history.txt +5 -4
  5. data/lib/couchrest.rb +31 -52
  6. data/lib/couchrest/{core/database.rb → database.rb} +6 -11
  7. data/lib/couchrest/{core/design.rb → design.rb} +2 -2
  8. data/lib/couchrest/{core/document.rb → document.rb} +1 -1
  9. data/lib/couchrest/helper/attachments.rb +29 -0
  10. data/lib/couchrest/middlewares/logger.rb +3 -3
  11. data/lib/couchrest/monkeypatches.rb +1 -71
  12. data/lib/couchrest/{core/response.rb → response.rb} +0 -0
  13. data/lib/couchrest/{core/rest_api.rb → rest_api.rb} +8 -12
  14. data/lib/couchrest/{core/server.rb → server.rb} +0 -2
  15. data/spec/couchrest/{core/couchrest_spec.rb → couchrest_spec.rb} +15 -9
  16. data/spec/couchrest/{core/database_spec.rb → database_spec.rb} +4 -4
  17. data/spec/couchrest/{core/design_spec.rb → design_spec.rb} +2 -2
  18. data/spec/couchrest/{core/document_spec.rb → document_spec.rb} +1 -1
  19. data/spec/couchrest/{core/server_spec.rb → server_spec.rb} +2 -2
  20. data/spec/spec.opts +0 -1
  21. data/spec/spec_helper.rb +0 -4
  22. metadata +32 -133
  23. data/examples/model/example.rb +0 -144
  24. data/lib/couchrest/core/adapters/restclient.rb +0 -35
  25. data/lib/couchrest/core/http_abstraction.rb +0 -48
  26. data/lib/couchrest/core/view.rb +0 -4
  27. data/lib/couchrest/mixins.rb +0 -4
  28. data/lib/couchrest/mixins/attachments.rb +0 -31
  29. data/lib/couchrest/mixins/attribute_protection.rb +0 -74
  30. data/lib/couchrest/mixins/callbacks.rb +0 -532
  31. data/lib/couchrest/mixins/class_proxy.rb +0 -124
  32. data/lib/couchrest/mixins/collection.rb +0 -260
  33. data/lib/couchrest/mixins/design_doc.rb +0 -103
  34. data/lib/couchrest/mixins/document_queries.rb +0 -80
  35. data/lib/couchrest/mixins/extended_attachments.rb +0 -70
  36. data/lib/couchrest/mixins/extended_document_mixins.rb +0 -9
  37. data/lib/couchrest/mixins/properties.rb +0 -158
  38. data/lib/couchrest/mixins/validation.rb +0 -246
  39. data/lib/couchrest/mixins/views.rb +0 -173
  40. data/lib/couchrest/more/casted_model.rb +0 -58
  41. data/lib/couchrest/more/extended_document.rb +0 -310
  42. data/lib/couchrest/more/property.rb +0 -58
  43. data/lib/couchrest/more/typecast.rb +0 -180
  44. data/lib/couchrest/support/blank.rb +0 -42
  45. data/lib/couchrest/support/rails.rb +0 -42
  46. data/lib/couchrest/validation/auto_validate.rb +0 -157
  47. data/lib/couchrest/validation/contextual_validators.rb +0 -78
  48. data/lib/couchrest/validation/validation_errors.rb +0 -125
  49. data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
  50. data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -107
  51. data/lib/couchrest/validation/validators/format_validator.rb +0 -122
  52. data/lib/couchrest/validation/validators/formats/email.rb +0 -66
  53. data/lib/couchrest/validation/validators/formats/url.rb +0 -43
  54. data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
  55. data/lib/couchrest/validation/validators/length_validator.rb +0 -139
  56. data/lib/couchrest/validation/validators/method_validator.rb +0 -89
  57. data/lib/couchrest/validation/validators/numeric_validator.rb +0 -109
  58. data/lib/couchrest/validation/validators/required_field_validator.rb +0 -114
  59. data/spec/couchrest/more/attribute_protection_spec.rb +0 -150
  60. data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -73
  61. data/spec/couchrest/more/casted_model_spec.rb +0 -406
  62. data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -135
  63. data/spec/couchrest/more/extended_doc_inherited_spec.rb +0 -40
  64. data/spec/couchrest/more/extended_doc_spec.rb +0 -807
  65. data/spec/couchrest/more/extended_doc_subclass_spec.rb +0 -98
  66. data/spec/couchrest/more/extended_doc_view_spec.rb +0 -456
  67. data/spec/couchrest/more/property_spec.rb +0 -628
  68. data/spec/fixtures/more/article.rb +0 -35
  69. data/spec/fixtures/more/card.rb +0 -22
  70. data/spec/fixtures/more/cat.rb +0 -20
  71. data/spec/fixtures/more/course.rb +0 -22
  72. data/spec/fixtures/more/event.rb +0 -8
  73. data/spec/fixtures/more/invoice.rb +0 -17
  74. data/spec/fixtures/more/person.rb +0 -9
  75. data/spec/fixtures/more/question.rb +0 -6
  76. data/spec/fixtures/more/service.rb +0 -12
  77. 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