couchrest 0.33 → 0.34

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 (45) hide show
  1. data/README.md +8 -127
  2. data/Rakefile +20 -36
  3. data/THANKS.md +2 -1
  4. data/history.txt +25 -0
  5. data/lib/couchrest.rb +5 -4
  6. data/lib/couchrest/core/database.rb +26 -21
  7. data/lib/couchrest/core/document.rb +4 -3
  8. data/lib/couchrest/helper/streamer.rb +11 -4
  9. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  10. data/lib/couchrest/mixins/callbacks.rb +187 -138
  11. data/lib/couchrest/mixins/collection.rb +3 -16
  12. data/lib/couchrest/mixins/extended_attachments.rb +1 -1
  13. data/lib/couchrest/mixins/extended_document_mixins.rb +1 -0
  14. data/lib/couchrest/mixins/properties.rb +71 -44
  15. data/lib/couchrest/mixins/validation.rb +18 -29
  16. data/lib/couchrest/more/casted_model.rb +29 -1
  17. data/lib/couchrest/more/extended_document.rb +73 -25
  18. data/lib/couchrest/more/property.rb +20 -1
  19. data/lib/couchrest/support/class.rb +81 -67
  20. data/lib/couchrest/support/rails.rb +12 -5
  21. data/lib/couchrest/validation/auto_validate.rb +5 -9
  22. data/lib/couchrest/validation/validators/confirmation_validator.rb +11 -3
  23. data/lib/couchrest/validation/validators/format_validator.rb +8 -3
  24. data/lib/couchrest/validation/validators/length_validator.rb +10 -5
  25. data/lib/couchrest/validation/validators/numeric_validator.rb +6 -1
  26. data/lib/couchrest/validation/validators/required_field_validator.rb +8 -3
  27. data/spec/couchrest/core/couchrest_spec.rb +48 -2
  28. data/spec/couchrest/core/database_spec.rb +22 -10
  29. data/spec/couchrest/core/document_spec.rb +9 -1
  30. data/spec/couchrest/helpers/streamer_spec.rb +31 -2
  31. data/spec/couchrest/more/attribute_protection_spec.rb +94 -0
  32. data/spec/couchrest/more/casted_extended_doc_spec.rb +2 -4
  33. data/spec/couchrest/more/casted_model_spec.rb +230 -1
  34. data/spec/couchrest/more/extended_doc_attachment_spec.rb +2 -2
  35. data/spec/couchrest/more/extended_doc_spec.rb +173 -15
  36. data/spec/couchrest/more/extended_doc_view_spec.rb +17 -10
  37. data/spec/couchrest/more/property_spec.rb +97 -3
  38. data/spec/fixtures/more/article.rb +4 -3
  39. data/spec/fixtures/more/card.rb +1 -1
  40. data/spec/fixtures/more/cat.rb +5 -3
  41. data/spec/fixtures/more/event.rb +4 -1
  42. data/spec/fixtures/more/invoice.rb +2 -2
  43. data/spec/fixtures/more/person.rb +1 -0
  44. data/spec/fixtures/more/user.rb +22 -0
  45. metadata +46 -13
@@ -1,14 +1,5 @@
1
1
  module CouchRest
2
2
  module Mixins
3
- module PaginatedResults
4
- def amount_pages
5
- @amount_pages ||= 0
6
- end
7
- def amount_pages=(value)
8
- @amount_pages = value
9
- end
10
- end
11
-
12
3
  module Collection
13
4
 
14
5
  def self.included(base)
@@ -93,8 +84,6 @@ module CouchRest
93
84
  DEFAULT_PAGE = 1
94
85
  DEFAULT_PER_PAGE = 30
95
86
 
96
- attr_accessor :amount_pages
97
-
98
87
  # Create a new CollectionProxy to represent the specified view. If a
99
88
  # container class is specified, the proxy will create an object of the
100
89
  # given type for each row that comes back from the view. If no
@@ -122,11 +111,8 @@ module CouchRest
122
111
  def paginate(options = {})
123
112
  page, per_page = parse_options(options)
124
113
  results = @database.view(@view_name, pagination_options(page, per_page))
125
- @amount_pages ||= (results['total_rows'].to_f / per_page.to_f).ceil
126
114
  remember_where_we_left_off(results, page)
127
115
  results = convert_to_container_array(results)
128
- results.extend(PaginatedResults)
129
- results.amount_pages = @amount_pages
130
116
  results
131
117
  end
132
118
 
@@ -204,8 +190,9 @@ module CouchRest
204
190
  def pagination_options(page, per_page)
205
191
  view_options = @view_options.clone
206
192
  if @last_key && @last_docid && @last_page == page - 1
207
- view_options.delete(:key)
208
- options = { :startkey => @last_key, :startkey_docid => @last_docid, :limit => per_page, :skip => 1 }
193
+ key = view_options.delete(:key)
194
+ end_key = view_options[:endkey] || key
195
+ options = { :startkey => @last_key, :endkey => end_key, :startkey_docid => @last_docid, :limit => per_page, :skip => 1 }
209
196
  else
210
197
  options = { :limit => per_page, :skip => per_page * (page - 1) }
211
198
  end
@@ -64,7 +64,7 @@ module CouchRest
64
64
  def set_attachment_attr(args)
65
65
  content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file])
66
66
  self['_attachments'][args[:name]] = {
67
- 'content-type' => content_type,
67
+ 'content_type' => content_type,
68
68
  'data' => encode_attachment(args[:file].read)
69
69
  }
70
70
  end
@@ -6,3 +6,4 @@ require File.join(File.dirname(__FILE__), 'validation')
6
6
  require File.join(File.dirname(__FILE__), 'extended_attachments')
7
7
  require File.join(File.dirname(__FILE__), 'class_proxy')
8
8
  require File.join(File.dirname(__FILE__), 'collection')
9
+ require File.join(File.dirname(__FILE__), 'attribute_protection')
@@ -27,7 +27,7 @@ module CouchRest
27
27
  class IncludeError < StandardError; end
28
28
 
29
29
  def self.included(base)
30
- base.class_eval <<-EOS, __FILE__, __LINE__
30
+ base.class_eval <<-EOS, __FILE__, __LINE__ + 1
31
31
  extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
32
32
  self.properties ||= []
33
33
  EOS
@@ -36,7 +36,7 @@ module CouchRest
36
36
  end
37
37
 
38
38
  def apply_defaults
39
- return if self.respond_to?(:new_document?) && (new_document? == false)
39
+ return if self.respond_to?(:new?) && (new? == false)
40
40
  return unless self.class.respond_to?(:properties)
41
41
  return if self.class.properties.empty?
42
42
  # TODO: cache the default object
@@ -56,50 +56,76 @@ module CouchRest
56
56
  def cast_keys
57
57
  return unless self.class.properties
58
58
  self.class.properties.each do |property|
59
- next unless property.casted
60
- key = self.has_key?(property.name) ? property.name : property.name.to_sym
61
- # Don't cast the property unless it has a value
62
- next unless self[key]
63
- target = property.type
64
- if target.is_a?(Array)
65
- klass = ::CouchRest.constantize(target[0])
66
- self[property.name] = self[key].collect do |value|
67
- # Auto parse Time objects
68
- obj = ( (property.init_method == 'new') && klass == Time) ? Time.parse(value) : klass.send(property.init_method, value)
69
- obj.casted_by = self if obj.respond_to?(:casted_by)
70
- obj
59
+ cast_property(property)
60
+ end
61
+ end
62
+
63
+ def cast_property(property, assigned=false)
64
+ return unless property.casted
65
+ key = self.has_key?(property.name) ? property.name : property.name.to_sym
66
+ # Don't cast the property unless it has a value
67
+ return unless self[key]
68
+ if property.type.is_a?(Array)
69
+ klass = ::CouchRest.constantize(property.type[0])
70
+ arr = self[key].dup.collect do |value|
71
+ unless value.instance_of?(klass)
72
+ value = convert_property_value(property, klass, value)
71
73
  end
74
+ associate_casted_to_parent(value, assigned)
75
+ value
76
+ end
77
+ self[key] = klass != String ? CastedArray.new(arr) : arr
78
+ self[key].casted_by = self if self[key].respond_to?(:casted_by)
79
+ else
80
+ if property.type == 'boolean'
81
+ klass = TrueClass
72
82
  else
73
- # Auto parse Time objects
74
- self[property.name] = if ((property.init_method == 'new') && target == 'Time')
75
- # Using custom time parsing method because Ruby's default method is toooo slow
76
- self[key].is_a?(String) ? Time.mktime_with_offset(self[key].dup) : self[key]
77
- # Float instances don't get initialized with #new
78
- elsif ((property.init_method == 'new') && target == 'Float')
79
- cast_float(self[key])
80
- # 'boolean' type is simply used to generate a property? accessor method
81
- elsif ((property.init_method == 'new') && target == 'boolean')
82
- self[key]
83
- else
84
- # Let people use :send as a Time parse arg
85
- klass = ::CouchRest.constantize(target)
86
- klass.send(property.init_method, self[key].dup)
87
- end
88
- self[property.name].casted_by = self if self[property.name].respond_to?(:casted_by)
89
- end
83
+ klass = ::CouchRest.constantize(property.type)
84
+ end
90
85
 
91
- end
92
-
93
- def cast_float(value)
94
- begin
95
- Float(value)
96
- rescue
97
- value
86
+ unless self[key].instance_of?(klass)
87
+ self[key] = convert_property_value(property, klass, self[property.name])
98
88
  end
89
+ associate_casted_to_parent(self[property.name], assigned)
99
90
  end
100
91
 
101
92
  end
102
93
 
94
+ def associate_casted_to_parent(casted, assigned)
95
+ casted.casted_by = self if casted.respond_to?(:casted_by)
96
+ casted.document_saved = true if !assigned && casted.respond_to?(:document_saved)
97
+ end
98
+
99
+ def convert_property_value(property, klass, value)
100
+ if ((property.init_method == 'new') && klass == Time)
101
+ # Using custom time parsing method because Ruby's default method is toooo slow
102
+ value.is_a?(String) ? Time.mktime_with_offset(value.dup) : value
103
+ # Float instances don't get initialized with #new
104
+ elsif ((property.init_method == 'new') && klass == Float)
105
+ cast_float(value)
106
+ # 'boolean' type is simply used to generate a property? accessor method
107
+ elsif ((property.init_method == 'new') && klass == TrueClass)
108
+ value
109
+ else
110
+ klass.send(property.init_method, value.dup)
111
+ end
112
+ end
113
+
114
+ def cast_property_by_name(property_name)
115
+ return unless self.class.properties
116
+ property = self.class.properties.detect{|property| property.name == property_name}
117
+ return unless property
118
+ cast_property(property, true)
119
+ end
120
+
121
+ def cast_float(value)
122
+ begin
123
+ Float(value)
124
+ rescue
125
+ value
126
+ end
127
+ end
128
+
103
129
  module ClassMethods
104
130
 
105
131
  def property(name, options={})
@@ -125,7 +151,7 @@ module CouchRest
125
151
  # defines the getter for the property (and optional aliases)
126
152
  def create_property_getter(property)
127
153
  # meth = property.name
128
- class_eval <<-EOS, __FILE__, __LINE__
154
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
129
155
  def #{property.name}
130
156
  self['#{property.name}']
131
157
  end
@@ -144,7 +170,7 @@ module CouchRest
144
170
  end
145
171
 
146
172
  if property.alias
147
- class_eval <<-EOS, __FILE__, __LINE__
173
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
148
174
  alias #{property.alias.to_sym} #{property.name.to_sym}
149
175
  EOS
150
176
  end
@@ -152,16 +178,17 @@ module CouchRest
152
178
 
153
179
  # defines the setter for the property (and optional aliases)
154
180
  def create_property_setter(property)
155
- meth = property.name
181
+ property_name = property.name
156
182
  class_eval <<-EOS
157
- def #{meth}=(value)
158
- self['#{meth}'] = value
183
+ def #{property_name}=(value)
184
+ self['#{property_name}'] = value
185
+ cast_property_by_name('#{property_name}')
159
186
  end
160
187
  EOS
161
188
 
162
189
  if property.alias
163
190
  class_eval <<-EOS
164
- alias #{property.alias.to_sym}= #{meth.to_sym}=
191
+ alias #{property.alias.to_sym}= #{property_name.to_sym}=
165
192
  EOS
166
193
  end
167
194
  end
@@ -50,7 +50,10 @@ module CouchRest
50
50
 
51
51
  def self.included(base)
52
52
  base.extlib_inheritable_accessor(:auto_validation)
53
- base.class_eval <<-EOS, __FILE__, __LINE__
53
+ base.class_eval <<-EOS, __FILE__, __LINE__ + 1
54
+ # Callbacks
55
+ define_callbacks :validate
56
+
54
57
  # Turn off auto validation by default
55
58
  self.auto_validation ||= false
56
59
 
@@ -71,9 +74,10 @@ module CouchRest
71
74
  EOS
72
75
 
73
76
  base.extend(ClassMethods)
74
- base.class_eval <<-EOS, __FILE__, __LINE__
77
+ base.class_eval <<-EOS, __FILE__, __LINE__ + 1
78
+ define_callbacks :validate
75
79
  if method_defined?(:_run_save_callbacks)
76
- save_callback :before, :check_validations
80
+ set_callback :save, :before, :check_validations
77
81
  end
78
82
  EOS
79
83
  base.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -115,8 +119,7 @@ module CouchRest
115
119
  # Check if a resource is valid in a given context
116
120
  #
117
121
  def valid?(context = :default)
118
- result = self.class.validators.execute(context, self)
119
- result && validate_casted_arrays
122
+ recursive_valid?(self, context, true)
120
123
  end
121
124
 
122
125
  # checking on casted objects
@@ -133,29 +136,24 @@ module CouchRest
133
136
  result
134
137
  end
135
138
 
136
- # Begin a recursive walk of the model checking validity
137
- #
138
- def all_valid?(context = :default)
139
- recursive_valid?(self, context, true)
140
- end
141
-
142
139
  # Do recursive validity checking
143
140
  #
144
141
  def recursive_valid?(target, context, state)
145
142
  valid = state
146
- target.instance_variables.each do |ivar|
147
- ivar_value = target.instance_variable_get(ivar)
148
- if ivar_value.validatable?
149
- valid = valid && recursive_valid?(ivar_value, context, valid)
150
- elsif ivar_value.respond_to?(:each)
151
- ivar_value.each do |item|
143
+ target.each do |key, prop|
144
+ if prop.is_a?(Array)
145
+ prop.each do |item|
152
146
  if item.validatable?
153
- valid = valid && recursive_valid?(item, context, valid)
147
+ valid = recursive_valid?(item, context, valid) && valid
154
148
  end
155
149
  end
150
+ elsif prop.validatable?
151
+ valid = recursive_valid?(prop, context, valid) && valid
156
152
  end
157
153
  end
158
- return valid && target.valid?
154
+ target._run_validate_callbacks do
155
+ target.class.validators.execute(context, target) && valid
156
+ end
159
157
  end
160
158
 
161
159
 
@@ -212,21 +210,12 @@ module CouchRest
212
210
  def create_context_instance_methods(context)
213
211
  name = "valid_for_#{context.to_s}?" # valid_for_signup?
214
212
  if !self.instance_methods.include?(name)
215
- class_eval <<-EOS, __FILE__, __LINE__
213
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
216
214
  def #{name} # def valid_for_signup?
217
215
  valid?('#{context.to_s}'.to_sym) # valid?('signup'.to_sym)
218
216
  end # end
219
217
  EOS
220
218
  end
221
-
222
- all = "all_valid_for_#{context.to_s}?" # all_valid_for_signup?
223
- if !self.instance_methods.include?(all)
224
- class_eval <<-EOS, __FILE__, __LINE__
225
- def #{all} # def all_valid_for_signup?
226
- all_valid?('#{context.to_s}'.to_sym) # all_valid?('signup'.to_sym)
227
- end # end
228
- EOS
229
- end
230
219
  end
231
220
 
232
221
  # Create a new validator of the given klazz and push it onto the
@@ -5,8 +5,10 @@ module CouchRest
5
5
  module CastedModel
6
6
 
7
7
  def self.included(base)
8
+ base.send(:include, ::CouchRest::Callbacks)
8
9
  base.send(:include, ::CouchRest::Mixins::Properties)
9
10
  base.send(:attr_accessor, :casted_by)
11
+ base.send(:attr_accessor, :document_saved)
10
12
  end
11
13
 
12
14
  def initialize(keys={})
@@ -26,5 +28,31 @@ module CouchRest
26
28
  def [] key
27
29
  super(key.to_s)
28
30
  end
31
+
32
+ # Gets a reference to the top level extended
33
+ # document that a model is saved inside of
34
+ def base_doc
35
+ return nil unless @casted_by
36
+ @casted_by.base_doc
37
+ end
38
+
39
+ # False if the casted model has already
40
+ # been saved in the containing document
41
+ def new?
42
+ !@document_saved
43
+ end
44
+ alias :new_record? :new?
45
+
46
+ # Sets the attributes from a hash
47
+ def update_attributes_without_saving(hash)
48
+ hash.each do |k, v|
49
+ raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=")
50
+ end
51
+ hash.each do |k, v|
52
+ self.send("#{k}=",v)
53
+ end
54
+ end
55
+ alias :attributes= :update_attributes_without_saving
56
+
29
57
  end
30
- end
58
+ end
@@ -14,6 +14,7 @@ module CouchRest
14
14
  include CouchRest::Mixins::ExtendedAttachments
15
15
  include CouchRest::Mixins::ClassProxy
16
16
  include CouchRest::Mixins::Collection
17
+ include CouchRest::Mixins::AttributeProtection
17
18
 
18
19
  def self.subclasses
19
20
  @subclasses ||= []
@@ -21,7 +22,7 @@ module CouchRest
21
22
 
22
23
  def self.inherited(subklass)
23
24
  subklass.send(:include, CouchRest::Mixins::Properties)
24
- subklass.class_eval <<-EOS, __FILE__, __LINE__
25
+ subklass.class_eval <<-EOS, __FILE__, __LINE__ + 1
25
26
  def self.inherited(subklass)
26
27
  subklass.properties = self.properties.dup
27
28
  end
@@ -33,18 +34,14 @@ module CouchRest
33
34
  attr_accessor :casted_by
34
35
 
35
36
  # Callbacks
36
- define_callbacks :create
37
- define_callbacks :save
38
- define_callbacks :update
39
- define_callbacks :destroy
37
+ define_callbacks :create, "result == :halt"
38
+ define_callbacks :save, "result == :halt"
39
+ define_callbacks :update, "result == :halt"
40
+ define_callbacks :destroy, "result == :halt"
40
41
 
41
42
  def initialize(passed_keys={})
42
43
  apply_defaults # defined in CouchRest::Mixins::Properties
43
- passed_keys.each do |k,v|
44
- if self.respond_to?("#{k}=")
45
- self.send("#{k}=", passed_keys.delete(k))
46
- end
47
- end if passed_keys
44
+ set_attributes(passed_keys) unless passed_keys.nil?
48
45
  super
49
46
  cast_keys # defined in CouchRest::Mixins::Properties
50
47
  unless self['_id'] && self['_rev']
@@ -76,17 +73,17 @@ module CouchRest
76
73
  # on the document whenever saving occurs. CouchRest uses a pretty
77
74
  # decent time format by default. See Time#to_json
78
75
  def self.timestamps!
79
- class_eval <<-EOS, __FILE__, __LINE__
76
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
80
77
  property(:updated_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
81
78
  property(:created_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
82
79
 
83
- save_callback :before do |object|
80
+ set_callback :save, :before do |object|
84
81
  object['updated_at'] = Time.now
85
- object['created_at'] = object['updated_at'] if object.new_document?
82
+ object['created_at'] = object['updated_at'] if object.new?
86
83
  end
87
84
  EOS
88
85
  end
89
-
86
+
90
87
  # Name a method that will be called before the document is first saved,
91
88
  # which returns a string to be used for the document's <tt>_id</tt>.
92
89
  # Because CouchDB enforces a constraint that each id must be unique,
@@ -128,17 +125,32 @@ module CouchRest
128
125
  self.class.properties
129
126
  end
130
127
 
128
+ # Gets a reference to the actual document in the DB
129
+ # Calls up to the next document if there is one,
130
+ # Otherwise we're at the top and we return self
131
+ def base_doc
132
+ return self if base_doc?
133
+ @casted_by.base_doc
134
+ end
135
+
136
+ # Checks if we're the top document
137
+ def base_doc?
138
+ !@casted_by
139
+ end
140
+
131
141
  # Takes a hash as argument, and applies the values by using writer methods
132
142
  # for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are
133
143
  # missing. In case of error, no attributes are changed.
134
144
  def update_attributes_without_saving(hash)
135
- hash.each do |k, v|
136
- raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=")
137
- end
138
- hash.each do |k, v|
139
- self.send("#{k}=",v)
140
- end
145
+ # remove attributes that cannot be updated, silently ignoring them
146
+ # which matches Rails behavior when, for instance, setting created_at.
147
+ # make a copy, we don't want to change arguments
148
+ attrs = hash.dup
149
+ %w[_id _rev created_at updated_at].each {|attr| attrs.delete(attr)}
150
+ check_properties_exist(attrs)
151
+ set_attributes(attrs)
141
152
  end
153
+ alias :attributes= :update_attributes_without_saving
142
154
 
143
155
  # Takes a hash as argument, and applies the values by using writer methods
144
156
  # for each key. Raises a NoMethodError if the corresponding methods are
@@ -149,7 +161,8 @@ module CouchRest
149
161
  end
150
162
 
151
163
  # for compatibility with old-school frameworks
152
- alias :new_record? :new_document?
164
+ alias :new_record? :new?
165
+ alias :new_document? :new?
153
166
 
154
167
  # Trigger the callbacks (before, after, around)
155
168
  # and create the document
@@ -170,7 +183,7 @@ module CouchRest
170
183
  # unlike save, create returns the newly created document
171
184
  def create_without_callbacks(bulk =false)
172
185
  raise ArgumentError, "a document requires a database to be created to (The document or the #{self.class} default database were not set)" unless database
173
- set_unique_id if new_document? && self.respond_to?(:set_unique_id)
186
+ set_unique_id if new? && self.respond_to?(:set_unique_id)
174
187
  result = database.save_doc(self, bulk)
175
188
  (result["ok"] == true) ? self : false
176
189
  end
@@ -185,7 +198,7 @@ module CouchRest
185
198
  # only if the document isn't new
186
199
  def update(bulk = false)
187
200
  caught = catch(:halt) do
188
- if self.new_document?
201
+ if self.new?
189
202
  save(bulk)
190
203
  else
191
204
  _run_update_callbacks do
@@ -201,7 +214,7 @@ module CouchRest
201
214
  # and save the document
202
215
  def save(bulk = false)
203
216
  caught = catch(:halt) do
204
- if self.new_document?
217
+ if self.new?
205
218
  _run_save_callbacks do
206
219
  save_without_callbacks(bulk)
207
220
  end
@@ -215,8 +228,9 @@ module CouchRest
215
228
  # Returns a boolean value
216
229
  def save_without_callbacks(bulk = false)
217
230
  raise ArgumentError, "a document requires a database to be saved to (The document or the #{self.class} default database were not set)" unless database
218
- set_unique_id if new_document? && self.respond_to?(:set_unique_id)
231
+ set_unique_id if new? && self.respond_to?(:set_unique_id)
219
232
  result = database.save_doc(self, bulk)
233
+ mark_as_saved
220
234
  result["ok"] == true
221
235
  end
222
236
 
@@ -224,6 +238,7 @@ module CouchRest
224
238
  # if the document is not saved properly.
225
239
  def save!
226
240
  raise "#{self.inspect} failed to save" unless self.save
241
+ true
227
242
  end
228
243
 
229
244
  # Deletes the document from the database. Runs the :destroy callbacks.
@@ -242,5 +257,38 @@ module CouchRest
242
257
  end
243
258
  end
244
259
 
260
+ protected
261
+
262
+ # Set document_saved flag on all casted models to true
263
+ def mark_as_saved
264
+ self.each do |key, prop|
265
+ if prop.is_a?(Array)
266
+ prop.each do |item|
267
+ if item.respond_to?(:document_saved)
268
+ item.send(:document_saved=, true)
269
+ end
270
+ end
271
+ elsif prop.respond_to?(:document_saved)
272
+ prop.send(:document_saved=, true)
273
+ end
274
+ end
275
+ end
276
+
277
+ private
278
+
279
+ def check_properties_exist(attrs)
280
+ attrs.each do |attribute_name, attribute_value|
281
+ raise NoMethodError, "#{attribute_name}= method not available, use property :#{attribute_name}" unless self.respond_to?("#{attribute_name}=")
282
+ end
283
+ end
284
+
285
+ def set_attributes(hash)
286
+ attrs = remove_protected_attributes(hash)
287
+ attrs.each do |attribute_name, attribute_value|
288
+ if self.respond_to?("#{attribute_name}=")
289
+ self.send("#{attribute_name}=", attrs.delete(attribute_name))
290
+ end
291
+ end
292
+ end
245
293
  end
246
294
  end