couchrest_model 1.1.0.beta5 → 1.1.0.rc1

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 (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