couchrest 0.38 → 1.0.0.beta

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