couchrest_model 1.1.0.beta5 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/Rakefile +9 -12
  2. data/VERSION +1 -1
  3. data/couchrest_model.gemspec +6 -6
  4. data/history.md +23 -0
  5. data/lib/couchrest/model/associations.rb +50 -54
  6. data/lib/couchrest/model/base.rb +10 -15
  7. data/lib/couchrest/model/casted_array.rb +16 -0
  8. data/lib/couchrest/model/class_proxy.rb +8 -1
  9. data/lib/couchrest/model/collection.rb +1 -0
  10. data/lib/couchrest/model/design_doc.rb +0 -2
  11. data/lib/couchrest/model/document_queries.rb +8 -8
  12. data/lib/couchrest/model/errors.rb +2 -0
  13. data/lib/couchrest/model/persistence.rb +20 -15
  14. data/lib/couchrest/model/properties.rb +17 -29
  15. data/lib/couchrest/model/property.rb +23 -7
  16. data/lib/couchrest/model/proxyable.rb +11 -4
  17. data/lib/couchrest/model/support/couchrest_database.rb +13 -0
  18. data/lib/couchrest/model/support/couchrest_design.rb +1 -1
  19. data/lib/couchrest/model/typecast.rb +1 -2
  20. data/lib/couchrest/model/validations/uniqueness.rb +29 -14
  21. data/lib/couchrest_model.rb +1 -1
  22. data/spec/couchrest/assocations_spec.rb +28 -1
  23. data/spec/couchrest/base_spec.rb +64 -26
  24. data/spec/couchrest/class_proxy_spec.rb +29 -0
  25. data/spec/couchrest/collection_spec.rb +6 -7
  26. data/spec/couchrest/design_doc_spec.rb +5 -1
  27. data/spec/couchrest/dirty_spec.rb +52 -0
  28. data/spec/couchrest/inherited_spec.rb +23 -30
  29. data/spec/couchrest/persistence_spec.rb +40 -18
  30. data/spec/couchrest/property_spec.rb +90 -4
  31. data/spec/couchrest/proxyable_spec.rb +14 -7
  32. data/spec/couchrest/validations_spec.rb +18 -1
  33. data/spec/fixtures/base.rb +4 -3
  34. data/spec/fixtures/more/article.rb +1 -0
  35. data/spec/fixtures/more/cat.rb +4 -0
  36. data/spec/fixtures/more/key_chain.rb +5 -0
  37. metadata +22 -21
data/Rakefile CHANGED
@@ -1,23 +1,20 @@
1
1
  require 'rubygems'
2
2
  require 'bundler'
3
- Bundler::GemHelper.install_tasks
4
-
5
- require 'rake'
3
+ require 'rspec/core/rake_task'
6
4
  require "rake/rdoctask"
7
5
 
8
- require 'rspec'
9
- require 'rspec/core/rake_task'
6
+ Bundler::GemHelper.install_tasks
10
7
 
11
8
  desc "Run all specs"
12
- Rspec::Core::RakeTask.new(:spec) do |spec|
13
- spec.rspec_opts = ["--color"]
14
- spec.pattern = 'spec/**/*_spec.rb'
9
+ RSpec::Core::RakeTask.new(:spec) do |spec|
10
+ spec.rspec_opts = ["--color"]
11
+ spec.pattern = 'spec/**/*_spec.rb'
15
12
  end
16
13
 
17
14
  desc "Print specdocs"
18
- Rspec::Core::RakeTask.new(:doc) do |spec|
19
- spec.rspec_opts = ["--format", "specdoc"]
20
- spec.pattern = 'spec/*_spec.rb'
15
+ RSpec::Core::RakeTask.new(:doc) do |spec|
16
+ spec.rspec_opts = ["--format", "specdoc"]
17
+ spec.pattern = 'spec/*_spec.rb'
21
18
  end
22
19
 
23
20
  desc "Generate the rdoc"
@@ -38,4 +35,4 @@ module Rake
38
35
  end
39
36
 
40
37
  Rake.remove_task("github:release")
41
- Rake.remove_task("release")
38
+ Rake.remove_task("release")
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0.beta5
1
+ 1.1.0.rc1
@@ -17,18 +17,18 @@ Gem::Specification.new do |s|
17
17
  s.homepage = %q{http://github.com/couchrest/couchrest_model}
18
18
  s.rubygems_version = %q{1.3.7}
19
19
  s.summary = %q{Extends the CouchRest Document for advanced modelling.}
20
-
20
+
21
21
  s.files = `git ls-files`.split("\n")
22
22
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
23
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
24
  s.require_paths = ["lib"]
25
-
26
- s.add_dependency(%q<couchrest>, "1.1.0.pre2")
25
+
26
+ s.add_dependency(%q<couchrest>, "1.1.0.pre3")
27
27
  s.add_dependency(%q<mime-types>, "~> 1.15")
28
- s.add_dependency(%q<activemodel>, "~> 3.0.0")
28
+ s.add_dependency(%q<activemodel>, "~> 3.0")
29
29
  s.add_dependency(%q<tzinfo>, "~> 0.3.22")
30
- s.add_dependency(%q<railties>, "~> 3.0.0")
31
- s.add_development_dependency(%q<rspec>, ">= 2.0.0")
30
+ s.add_development_dependency(%q<rspec>, "~> 2.6.0")
31
+ s.add_development_dependency(%q<json>, ["~> 1.5.1"])
32
32
  s.add_development_dependency(%q<rack-test>, ">= 0.5.7")
33
33
  # s.add_development_dependency("jruby-openssl", ">= 0.7.3")
34
34
  end
data/history.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # CouchRest Model Change History
2
2
 
3
+ ## 1.1.0.rc - 2011-06-08
4
+
5
+ * New Features
6
+ * Properties with a nil value are now no longer sent to the database.
7
+ * Now possible to build new objects via CastedArray#build
8
+ * Implement #get! and #find! class methods
9
+ * Now is possible delete particular elements in casted array(Kostiantyn Kahanskyi)
10
+
11
+ * Minor fixes
12
+ * #as_json now correctly uses ActiveSupports methods.
13
+ * Rails 3.1 support (Peter Williams)
14
+ * Initialization blocks when creating new models (Peter Williams)
15
+ * Removed railties dependency (DAddYE)
16
+ * DesignDoc cache refreshed if a database is deleted.
17
+ * Fixing dirty tracking on collection_of association.
18
+ * Uniqueness Validation views created on initialization, not on demand!
19
+ * #destroy freezes object instead of removing _id and _rev, better for callbacks (pointer by karmi)
20
+ * #destroyed? method now available
21
+ * #reload no longer uses Hash#merge! which was causing issues with dirty tracking on casted models. (pointer by kostia)
22
+ * Non-property mass assignment on #new no longer possible without :directly_set_attributes option.
23
+ * Using CouchRest 1.1.0.pre3. (No more Hashes!)
24
+ * Fixing problem assigning a CastedHash to a property declared as a Hash (Kostiantyn Kahanskyi, gfmtim)
25
+
3
26
  ## 1.1.0.beta5 - 2011-04-30
4
27
 
5
28
  * Major changes:
@@ -153,7 +153,7 @@ module CouchRest
153
153
  def #{attrib}(reload = false)
154
154
  return @#{attrib} unless @#{attrib}.nil? or reload
155
155
  ary = self.#{options[:foreign_key]}.collect{|i| #{options[:proxy]}.get(i)}
156
- @#{attrib} = ::CouchRest::CollectionOfProxy.new(ary, self, '#{options[:foreign_key]}')
156
+ @#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(ary, find_property('#{options[:foreign_key]}'), self)
157
157
  end
158
158
  EOS
159
159
  end
@@ -161,7 +161,7 @@ module CouchRest
161
161
  def create_collection_of_setter(attrib, options)
162
162
  class_eval <<-EOS, __FILE__, __LINE__ + 1
163
163
  def #{attrib}=(value)
164
- @#{attrib} = ::CouchRest::CollectionOfProxy.new(value, self, '#{options[:foreign_key]}')
164
+ @#{attrib} = ::CouchRest::Model::CollectionOfProxy.new(value, find_property('#{options[:foreign_key]}'), self)
165
165
  end
166
166
  EOS
167
167
  end
@@ -169,67 +169,63 @@ module CouchRest
169
169
  end
170
170
 
171
171
  end
172
- end
173
172
 
174
- # Special proxy for a collection of items so that adding and removing
175
- # to the list automatically updates the associated property.
176
- class CollectionOfProxy < Array
177
- attr_accessor :property
178
- attr_accessor :casted_by
179
-
180
- def initialize(array, casted_by, property)
181
- self.property = property
182
- self.casted_by = casted_by
183
- (array ||= []).compact!
184
- casted_by[property.to_s] = [] # replace the original array!
185
- array.compact.each do |obj|
173
+ # Special proxy for a collection of items so that adding and removing
174
+ # to the list automatically updates the associated property.
175
+ class CollectionOfProxy < CastedArray
176
+
177
+ def initialize(array, property, parent)
178
+ (array ||= []).compact!
179
+ super(array, property, parent)
180
+ casted_by[casted_by_property.to_s] = [] # replace the original array!
181
+ array.compact.each do |obj|
182
+ check_obj(obj)
183
+ casted_by[casted_by_property.to_s] << obj.id
184
+ end
185
+ end
186
+
187
+ def << obj
186
188
  check_obj(obj)
187
- casted_by[property.to_s] << obj.id
189
+ casted_by[casted_by_property.to_s] << obj.id
190
+ super(obj)
191
+ end
192
+
193
+ def push(obj)
194
+ check_obj(obj)
195
+ casted_by[casted_by_property.to_s].push obj.id
196
+ super(obj)
197
+ end
198
+
199
+ def unshift(obj)
200
+ check_obj(obj)
201
+ casted_by[casted_by_property.to_s].unshift obj.id
202
+ super(obj)
188
203
  end
189
- super(array)
190
- end
191
-
192
- def << obj
193
- check_obj(obj)
194
- casted_by[property.to_s] << obj.id
195
- super(obj)
196
- end
197
-
198
- def push(obj)
199
- check_obj(obj)
200
- casted_by[property.to_s].push obj.id
201
- super(obj)
202
- end
203
-
204
- def unshift(obj)
205
- check_obj(obj)
206
- casted_by[property.to_s].unshift obj.id
207
- super(obj)
208
- end
209
204
 
210
- def []= index, obj
211
- check_obj(obj)
212
- casted_by[property.to_s][index] = obj.id
213
- super(index, obj)
214
- end
205
+ def []= index, obj
206
+ check_obj(obj)
207
+ casted_by[casted_by_property.to_s][index] = obj.id
208
+ super(index, obj)
209
+ end
215
210
 
216
- def pop
217
- casted_by[property.to_s].pop
218
- super
219
- end
220
-
221
- def shift
222
- casted_by[property.to_s].shift
223
- super
224
- end
211
+ def pop
212
+ casted_by[casted_by_property.to_s].pop
213
+ super
214
+ end
215
+
216
+ def shift
217
+ casted_by[casted_by_property.to_s].shift
218
+ super
219
+ end
225
220
 
226
- protected
221
+ protected
222
+
223
+ def check_obj(obj)
224
+ raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new?
225
+ end
227
226
 
228
- def check_obj(obj)
229
- raise "Object cannot be added to #{casted_by.class.to_s}##{property.to_s} collection unless saved" if obj.new?
230
227
  end
231
228
 
232
229
  end
233
230
 
234
-
235
231
  end
@@ -1,6 +1,6 @@
1
1
  module CouchRest
2
2
  module Model
3
- class Base < Document
3
+ class Base < CouchRest::Document
4
4
 
5
5
  extend ActiveModel::Naming
6
6
 
@@ -49,14 +49,19 @@ module CouchRest
49
49
  # * :directly_set_attributes: true when data comes directly from database
50
50
  # * :database: provide an alternative database
51
51
  #
52
- def initialize(doc = {}, options = {})
53
- doc = prepare_all_attributes(doc, options)
54
- # set the instances database, if provided
52
+ # If a block is provided the new model will be passed into the
53
+ # block so that it can be populated.
54
+ def initialize(attributes = {}, options = {})
55
+ super()
56
+ prepare_all_attributes(attributes, options)
57
+ # set the instance's database, if provided
55
58
  self.database = options[:database] unless options[:database].nil?
56
- super(doc)
57
59
  unless self['_id'] && self['_rev']
58
60
  self[self.model_type_key] = self.class.to_s
59
61
  end
62
+
63
+ yield self if block_given?
64
+
60
65
  after_initialize if respond_to?(:after_initialize)
61
66
  end
62
67
 
@@ -75,16 +80,6 @@ module CouchRest
75
80
  super
76
81
  end
77
82
 
78
- ## Compatibility with ActiveSupport and older frameworks
79
-
80
- # Hack so that CouchRest::Document, which descends from Hash,
81
- # doesn't appear to Rails routing as a Hash of options
82
- def is_a?(klass)
83
- return false if klass == Hash
84
- super
85
- end
86
- alias :kind_of? :is_a?
87
-
88
83
  def persisted?
89
84
  !new?
90
85
  end
@@ -50,6 +50,22 @@ module CouchRest::Model
50
50
  super
51
51
  end
52
52
 
53
+ def delete(obj)
54
+ couchrest_parent_will_change! if use_dirty? && self.length > 0
55
+ super(obj)
56
+ end
57
+
58
+ def delete_at(index)
59
+ couchrest_parent_will_change! if use_dirty? && self.length > 0
60
+ super(index)
61
+ end
62
+
63
+ def build(*args)
64
+ obj = casted_by_property.build(*args)
65
+ self.push(obj)
66
+ obj
67
+ end
68
+
53
69
  protected
54
70
 
55
71
  def instantiate_and_cast(obj, change = true)
@@ -87,7 +87,14 @@ module CouchRest
87
87
  doc
88
88
  end
89
89
  alias :find :get
90
-
90
+
91
+ def get!(id)
92
+ doc = @klass.get!(id, @database)
93
+ doc.database = @database if doc && doc.respond_to?(:database)
94
+ doc
95
+ end
96
+ alias :find! :get!
97
+
91
98
  # Views
92
99
 
93
100
  def has_view?(view)
@@ -244,6 +244,7 @@ module CouchRest
244
244
  else
245
245
  options = { :limit => per_page, :skip => per_page * (page - 1) }
246
246
  end
247
+ options[:include_docs] = true
247
248
  view_options.merge(options)
248
249
  end
249
250
 
@@ -108,8 +108,6 @@ module CouchRest
108
108
  }
109
109
  end
110
110
 
111
-
112
-
113
111
  end # module ClassMethods
114
112
 
115
113
  end
@@ -1,13 +1,10 @@
1
1
  module CouchRest
2
2
  module Model
3
3
  module DocumentQueries
4
-
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
4
+ extend ActiveSupport::Concern
5
+
9
6
  module ClassMethods
10
-
7
+
11
8
  # Load all documents that have the model_type_key's field equal to the
12
9
  # name of the current class. Take the standard set of
13
10
  # CouchRest::Database#view options.
@@ -73,7 +70,7 @@ module CouchRest
73
70
  end
74
71
  end
75
72
  alias :find :get
76
-
73
+
77
74
  # Load a document from the database by id
78
75
  # An exception will be raised if the document isn't found
79
76
  #
@@ -86,9 +83,12 @@ module CouchRest
86
83
  # id<String, Integer>:: Document ID
87
84
  # db<Database>:: optional option to pass a custom database to use
88
85
  def get!(id, db = database)
89
- raise "Missing or empty document ID" if id.to_s.empty?
86
+ raise CouchRest::Model::DocumentNotFound if id.blank?
87
+
90
88
  doc = db.get id
91
89
  build_from_database(doc)
90
+ rescue RestClient::ResourceNotFound
91
+ raise CouchRest::Model::DocumentNotFound
92
92
  end
93
93
  alias :find! :get!
94
94
 
@@ -19,5 +19,7 @@ module CouchRest
19
19
  end
20
20
  end
21
21
  end
22
+
23
+ class DocumentNotFound < Errors::CouchRestModelError; end
22
24
  end
23
25
  end
@@ -21,8 +21,8 @@ module CouchRest
21
21
 
22
22
  # Creates the document in the db. Raises an exception
23
23
  # if the document is not created properly.
24
- def create!
25
- self.class.fail_validate!(self) unless self.create
24
+ def create!(options = {})
25
+ self.class.fail_validate!(self) unless self.create(options)
26
26
  end
27
27
 
28
28
  # Trigger the callbacks (before, after, around)
@@ -54,19 +54,21 @@ module CouchRest
54
54
  end
55
55
 
56
56
  # Deletes the document from the database. Runs the :destroy callbacks.
57
- # Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
58
- # document to be saved to a new <tt>_id</tt> if required.
59
57
  def destroy
60
58
  _run_destroy_callbacks do
61
59
  result = database.delete_doc(self)
62
60
  if result['ok']
63
- self.delete('_rev')
64
- self.delete('_id')
61
+ @_destroyed = true
62
+ self.freeze
65
63
  end
66
64
  result['ok']
67
65
  end
68
66
  end
69
67
 
68
+ def destroyed?
69
+ !!@_destroyed
70
+ end
71
+
70
72
  # Update the document's attributes and save. For example:
71
73
  #
72
74
  # doc.update_attributes :name => "Fred"
@@ -85,7 +87,7 @@ module CouchRest
85
87
  #
86
88
  # Returns self.
87
89
  def reload
88
- merge!(self.class.get(id))
90
+ prepare_all_attributes(database.get(id), :directly_set_attributes => true)
89
91
  self
90
92
  end
91
93
 
@@ -104,22 +106,25 @@ module CouchRest
104
106
 
105
107
  module ClassMethods
106
108
 
107
- # Creates a new instance, bypassing attribute protection
109
+ # Creates a new instance, bypassing attribute protection and
110
+ # uses the type field to determine which model to use to instanatiate
111
+ # the new object.
108
112
  #
109
113
  # ==== Returns
110
114
  # a document instance
111
115
  #
112
- def build_from_database(doc = {})
113
- base = (doc[model_type_key].blank? || doc[model_type_key] == self.to_s) ? self : doc[model_type_key].constantize
114
- base.new(doc, :directly_set_attributes => true)
116
+ def build_from_database(doc = {}, options = {}, &block)
117
+ src = doc[model_type_key]
118
+ base = (src.blank? || src == self.to_s) ? self : src.constantize
119
+ base.new(doc, options.merge(:directly_set_attributes => true), &block)
115
120
  end
116
121
 
117
122
  # Defines an instance and save it directly to the database
118
123
  #
119
124
  # ==== Returns
120
125
  # returns the reloaded document
121
- def create(attributes = {})
122
- instance = new(attributes)
126
+ def create(attributes = {}, &block)
127
+ instance = new(attributes, &block)
123
128
  instance.create
124
129
  instance
125
130
  end
@@ -128,8 +133,8 @@ module CouchRest
128
133
  #
129
134
  # ==== Returns
130
135
  # returns the reloaded document or raises an exception
131
- def create!(attributes = {})
132
- instance = new(attributes)
136
+ def create!(attributes = {}, &block)
137
+ instance = new(attributes, &block)
133
138
  instance.create!
134
139
  instance
135
140
  end
@@ -5,25 +5,15 @@ module CouchRest
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
9
- extlib_inheritable_accessor(:properties_by_name) unless self.respond_to?(:properties_by_name)
8
+ class_attribute(:properties) unless self.respond_to?(:properties)
9
+ class_attribute(:properties_by_name) unless self.respond_to?(:properties_by_name)
10
10
  self.properties ||= []
11
11
  self.properties_by_name ||= {}
12
12
  raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=))
13
13
  end
14
14
 
15
- # Returns the Class properties
16
- #
17
- # ==== Returns
18
- # Array:: the list of properties for model's class
19
- def properties
20
- self.class.properties
21
- end
22
-
23
- # Returns all the class's properties as a Hash where the key is the name
24
- # of the property.
25
- def properties_by_name
26
- self.class.properties_by_name
15
+ def as_json(options = nil)
16
+ Hash[self].reject{|k,v| v.nil?}.as_json(options)
27
17
  end
28
18
 
29
19
  # Returns the Class properties with their values
@@ -90,17 +80,18 @@ module CouchRest
90
80
  self.disable_dirty = dirty
91
81
  end
92
82
 
93
- def prepare_all_attributes(doc = {}, options = {})
83
+ def prepare_all_attributes(attrs = {}, options = {})
94
84
  self.disable_dirty = !!options[:directly_set_attributes]
95
85
  apply_all_property_defaults
96
86
  if options[:directly_set_attributes]
97
- directly_set_read_only_attributes(doc)
87
+ directly_set_read_only_attributes(attrs)
88
+ directly_set_attributes(attrs, true)
98
89
  else
99
- doc = remove_protected_attributes(doc)
90
+ attrs = remove_protected_attributes(attrs)
91
+ directly_set_attributes(attrs)
100
92
  end
101
- res = doc.nil? ? doc : directly_set_attributes(doc)
102
93
  self.disable_dirty = false
103
- res
94
+ self
104
95
  end
105
96
 
106
97
  def find_property!(property)
@@ -111,16 +102,13 @@ module CouchRest
111
102
 
112
103
  # Set all the attributes and return a hash with the attributes
113
104
  # that have not been accepted.
114
- def directly_set_attributes(hash)
115
- hash.reject do |attribute_name, attribute_value|
116
- if self.respond_to?("#{attribute_name}=")
117
- self.send("#{attribute_name}=", attribute_value)
118
- true
119
- elsif mass_assign_any_attribute # config option
120
- self[attribute_name] = attribute_value
121
- true
122
- else
123
- false
105
+ def directly_set_attributes(hash, mass_assign = false)
106
+ return if hash.nil?
107
+ hash.reject do |key, value|
108
+ if self.respond_to?("#{key}=")
109
+ self.send("#{key}=", value)
110
+ elsif mass_assign || mass_assign_any_attribute
111
+ self[key] = value
124
112
  end
125
113
  end
126
114
  end
@@ -27,19 +27,15 @@ module CouchRest::Model
27
27
  if value.nil?
28
28
  value = []
29
29
  elsif [Hash, HashWithIndifferentAccess].include?(value.class)
30
- # Assume provided as a Hash where key is index!
31
- data = value
32
- value = [ ]
33
- data.keys.sort.each do |k|
34
- value << data[k]
35
- end
30
+ # Assume provided as a params hash where key is index
31
+ value = parameter_hash_to_array(value)
36
32
  elsif !value.is_a?(Array)
37
33
  raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}"
38
34
  end
39
35
  arr = value.collect { |data| cast_value(parent, data) }
40
36
  # allow casted_by calls to be passed up chain by wrapping in CastedArray
41
37
  CastedArray.new(arr, self, parent)
42
- elsif (type == Object || type == Hash) && (value.class == Hash)
38
+ elsif (type == Object || type == Hash) && (value.is_a?(Hash))
43
39
  # allow casted_by calls to be passed up chain by wrapping in CastedHash
44
40
  CastedHash[value, self, parent]
45
41
  elsif !value.nil?
@@ -64,8 +60,28 @@ module CouchRest::Model
64
60
  end
65
61
  end
66
62
 
63
+ # Initialize a new instance of a property's type ready to be
64
+ # used. If a proc is defined for the init method, it will be used instead of
65
+ # a normal call to the class.
66
+ def build(*args)
67
+ raise StandardError, "Cannot build property without a class" if @type_class.nil?
68
+ if @init_method.is_a?(Proc)
69
+ @init_method.call(*args)
70
+ else
71
+ @type_class.send(@init_method, *args)
72
+ end
73
+ end
74
+
67
75
  private
68
76
 
77
+ def parameter_hash_to_array(source)
78
+ value = [ ]
79
+ source.keys.each do |k|
80
+ value[k.to_i] = source[k]
81
+ end
82
+ value.compact
83
+ end
84
+
69
85
  def associate_casted_value_to_parent(parent, value)
70
86
  value.casted_by = parent if value.respond_to?(:casted_by)
71
87
  value.casted_by_property = self if value.respond_to?(:casted_by_property)
@@ -73,12 +73,12 @@ module CouchRest
73
73
  end
74
74
 
75
75
  # Base
76
- def new(*args)
77
- proxy_update(model.new(*args))
76
+ def new(attrs = {}, options = {}, &block)
77
+ proxy_block_update(:new, attrs, options, &block)
78
78
  end
79
79
 
80
- def build_from_database(doc = {})
81
- proxy_update(model.build_from_database(doc))
80
+ def build_from_database(attrs = {}, options = {}, &block)
81
+ proxy_block_update(:build_from_database, attrs, options, &block)
82
82
  end
83
83
 
84
84
  def method_missing(m, *args, &block)
@@ -170,6 +170,13 @@ module CouchRest
170
170
  end
171
171
  end
172
172
 
173
+ def proxy_block_update(method, *args, &block)
174
+ model.send(method, *args) do |doc|
175
+ proxy_update(doc)
176
+ yield doc if block_given?
177
+ end
178
+ end
179
+
173
180
  end
174
181
  end
175
182
  end
@@ -0,0 +1,13 @@
1
+ #
2
+ # Extend CouchRest's normal database delete! method to ensure any caches are
3
+ # also emptied. Given that this is a rare event, and the consequences are not
4
+ # very severe, we just completely empty the cache.
5
+ #
6
+ CouchRest::Database.class_eval do
7
+
8
+ def delete!
9
+ Thread.current[:couchrest_design_cache] = { }
10
+ CouchRest.delete @root
11
+ end
12
+
13
+ end
@@ -18,7 +18,7 @@ CouchRest::Design.class_eval do
18
18
  flatten =
19
19
  lambda {|r|
20
20
  (recurse = lambda {|v|
21
- if v.is_a?(Hash)
21
+ if v.is_a?(Hash) || v.is_a?(CouchRest::Document)
22
22
  v.to_a.map{|v| recurse.call(v)}.flatten
23
23
  elsif v.is_a?(Array)
24
24
  v.flatten.map{|v| recurse.call(v)}
@@ -14,8 +14,7 @@ module CouchRest
14
14
  elsif [String, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(klass)
15
15
  send('typecast_to_'+klass.to_s.downcase, value)
16
16
  else
17
- # Allow the init_method to be defined as a Proc for advanced conversion
18
- property.init_method.is_a?(Proc) ? property.init_method.call(value) : klass.send(property.init_method, value)
17
+ property.build(value)
19
18
  end
20
19
  end
21
20