dm-core 0.9.5 → 0.9.6

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 (54) hide show
  1. data/Manifest.txt +3 -0
  2. data/lib/dm-core.rb +14 -20
  3. data/lib/dm-core/adapters.rb +18 -0
  4. data/lib/dm-core/adapters/abstract_adapter.rb +17 -10
  5. data/lib/dm-core/adapters/data_objects_adapter.rb +17 -22
  6. data/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  7. data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
  8. data/lib/dm-core/adapters/postgres_adapter.rb +2 -2
  9. data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
  10. data/lib/dm-core/associations.rb +3 -2
  11. data/lib/dm-core/associations/many_to_many.rb +3 -3
  12. data/lib/dm-core/associations/one_to_many.rb +10 -2
  13. data/lib/dm-core/associations/relationship.rb +20 -16
  14. data/lib/dm-core/auto_migrations.rb +5 -4
  15. data/lib/dm-core/collection.rb +10 -6
  16. data/lib/dm-core/dependency_queue.rb +2 -1
  17. data/lib/dm-core/identity_map.rb +3 -6
  18. data/lib/dm-core/model.rb +48 -27
  19. data/lib/dm-core/property.rb +57 -37
  20. data/lib/dm-core/property_set.rb +29 -22
  21. data/lib/dm-core/query.rb +57 -49
  22. data/lib/dm-core/repository.rb +3 -3
  23. data/lib/dm-core/resource.rb +17 -15
  24. data/lib/dm-core/scope.rb +7 -7
  25. data/lib/dm-core/support/kernel.rb +6 -2
  26. data/lib/dm-core/transaction.rb +7 -7
  27. data/lib/dm-core/version.rb +1 -1
  28. data/script/performance.rb +114 -22
  29. data/spec/integration/association_spec.rb +31 -2
  30. data/spec/integration/association_through_spec.rb +2 -0
  31. data/spec/integration/associations/many_to_many_spec.rb +152 -0
  32. data/spec/integration/associations/one_to_many_spec.rb +40 -3
  33. data/spec/integration/dependency_queue_spec.rb +0 -12
  34. data/spec/integration/postgres_adapter_spec.rb +1 -1
  35. data/spec/integration/property_spec.rb +3 -3
  36. data/spec/integration/query_spec.rb +39 -8
  37. data/spec/integration/resource_spec.rb +10 -6
  38. data/spec/integration/sti_spec.rb +22 -0
  39. data/spec/integration/strategic_eager_loading_spec.rb +21 -6
  40. data/spec/integration/type_spec.rb +1 -0
  41. data/spec/lib/model_loader.rb +10 -1
  42. data/spec/models/content.rb +16 -0
  43. data/spec/spec_helper.rb +4 -1
  44. data/spec/unit/adapters/data_objects_adapter_spec.rb +11 -11
  45. data/spec/unit/adapters/in_memory_adapter_spec.rb +98 -0
  46. data/spec/unit/associations/many_to_many_spec.rb +16 -1
  47. data/spec/unit/model_spec.rb +0 -16
  48. data/spec/unit/property_set_spec.rb +8 -1
  49. data/spec/unit/property_spec.rb +476 -240
  50. data/spec/unit/query_spec.rb +41 -0
  51. data/spec/unit/resource_spec.rb +75 -56
  52. data/tasks/ci.rb +4 -36
  53. data/tasks/dm.rb +3 -3
  54. metadata +5 -2
@@ -74,25 +74,29 @@ module DataMapper
74
74
 
75
75
  # @api private
76
76
  def get_children(parent, options = {}, finder = :all, *args)
77
- bind_values = parent_key.get(parent)
78
- parent_values = bind_values.dup
77
+ parent_value = parent_key.get(parent)
78
+ bind_values = [ parent_value ]
79
79
 
80
80
  with_repository(child_model) do |r|
81
81
  parent_identity_map = parent.repository.identity_map(parent_model)
82
82
  child_identity_map = r.identity_map(child_model)
83
83
 
84
- query_values = parent_identity_map.keys.flatten
85
- query_values.reject! { |k| child_identity_map[[k]] }
84
+ query_values = parent_identity_map.keys
85
+ query_values.reject! { |k| child_identity_map[k] }
86
86
 
87
87
  bind_values = query_values unless query_values.empty?
88
- query = child_key.map { |k| [ k, bind_values ] }.to_hash
88
+ query = child_key.zip(bind_values.transpose).to_hash
89
89
 
90
90
  collection = child_model.send(finder, *(args.dup << @query.merge(options).merge(query)))
91
+
91
92
  return collection unless collection.kind_of?(Collection) && collection.any?
92
93
 
93
- grouped_collection = Hash.new { |h,k| h[k] = [] }
94
+ grouped_collection = {}
94
95
  collection.each do |resource|
95
- grouped_collection[get_parent(resource, parent)] << resource
96
+ child_value = child_key.get(resource)
97
+ parent_obj = parent_identity_map[child_value]
98
+ grouped_collection[parent_obj] ||= []
99
+ grouped_collection[parent_obj] << resource
96
100
  end
97
101
 
98
102
  association_accessor = "#{self.name}_association"
@@ -112,14 +116,14 @@ module DataMapper
112
116
  parents_children = Collection.new(query)
113
117
  children.each { |child| parents_children.send(:add, child) }
114
118
 
115
- if parent_key.get(parent) == parent_values
119
+ if parent_key.get(parent) == parent_value
116
120
  ret = parents_children
117
121
  else
118
122
  association.instance_variable_set(:@children, parents_children)
119
123
  end
120
124
  end
121
125
 
122
- ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip(parent_values).to_hash)))
126
+ ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip([ parent_value ]).to_hash)))
123
127
  end
124
128
  end
125
129
 
@@ -129,14 +133,15 @@ module DataMapper
129
133
  return nil unless child_value.nitems == child_value.size
130
134
 
131
135
  with_repository(parent || parent_model) do
132
- parent_identity_map = (parent || parent_model).repository.identity_map(parent_model)
133
- child_identity_map = child.repository.identity_map(child_model)
136
+ parent_identity_map = (parent || parent_model).repository.identity_map(parent_model.base_model)
137
+ child_identity_map = child.repository.identity_map(child_model.base_model)
134
138
 
135
139
  if parent = parent_identity_map[child_value]
136
140
  return parent
137
141
  end
138
142
 
139
- children = child_identity_map.values | [child]
143
+ children = child_identity_map.values
144
+ children << child unless child_identity_map[child.key]
140
145
 
141
146
  bind_values = children.map { |c| child_key.get(c) }.uniq
142
147
  query_values = bind_values.reject { |k| parent_identity_map[k] }
@@ -151,21 +156,20 @@ module DataMapper
151
156
  children.each do |c|
152
157
  c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
153
158
  end
154
-
155
159
  child.send(association_accessor).instance_variable_get(:@parent)
156
160
  end
157
161
  end
158
162
  end
159
163
 
160
164
  # @api private
161
- def with_repository(object = nil, &block)
165
+ def with_repository(object = nil)
162
166
  other_model = object.model == child_model ? parent_model : child_model if object.respond_to?(:model)
163
167
  other_model = object == child_model ? parent_model : child_model if object.kind_of?(DataMapper::Resource)
164
168
 
165
169
  if other_model && other_model.repository == object.repository && object.repository.name != @repository_name
166
- object.repository.scope(&block)
170
+ object.repository.scope { |block_args| yield(*block_args) }
167
171
  else
168
- repository(@repository_name, &block)
172
+ repository(@repository_name) { |block_args| yield(*block_args) }
169
173
  end
170
174
  end
171
175
 
@@ -59,7 +59,7 @@ module DataMapper
59
59
  # REPEAT: THIS IS DESTRUCTIVE
60
60
  #
61
61
  # @param Symbol repository_name the repository to be migrated
62
- def auto_migrate!(repository_name = nil)
62
+ def auto_migrate!(repository_name = self.repository_name)
63
63
  auto_migrate_down!(repository_name)
64
64
  auto_migrate_up!(repository_name)
65
65
  end
@@ -71,7 +71,8 @@ module DataMapper
71
71
  #
72
72
  # @param Symbol repository_name the repository to be migrated
73
73
  # @api private
74
- def auto_migrate_down!(repository_name = nil)
74
+ def auto_migrate_down!(repository_name = self.repository_name)
75
+ # repository_name ||= default_repository_name
75
76
  if self.superclass != Object
76
77
  self.superclass.auto_migrate!(repository_name)
77
78
  else
@@ -86,7 +87,7 @@ module DataMapper
86
87
  #
87
88
  # @param Symbol repository_name the repository to be migrated
88
89
  # @api private
89
- def auto_migrate_up!(repository_name = nil)
90
+ def auto_migrate_up!(repository_name = self.repository_name)
90
91
  if self.superclass != Object
91
92
  self.superclass.auto_migrate!(repository_name)
92
93
  else
@@ -101,7 +102,7 @@ module DataMapper
101
102
  # preserving data already in the data-store
102
103
  #
103
104
  # @param Symbol repository_name the repository to be migrated
104
- def auto_upgrade!(repository_name = nil)
105
+ def auto_upgrade!(repository_name = self.repository_name)
105
106
  repository(repository_name) do |r|
106
107
  r.adapter.upgrade_model_storage(r, self)
107
108
  end
@@ -52,8 +52,9 @@ module DataMapper
52
52
  def get(*key)
53
53
  key = model.typecast_key(key)
54
54
  if loaded?
55
- # loop over the collection to find the matching resource
56
- detect { |resource| resource.key == key }
55
+ # find indexed resource (create index first if it does not exist)
56
+ each {|r| @cache[r.key] = r } if @cache.empty?
57
+ @cache[key]
57
58
  elsif query.limit || query.offset > 0
58
59
  # current query is exclusive, find resource within the set
59
60
 
@@ -167,7 +168,7 @@ module DataMapper
167
168
  end
168
169
 
169
170
  ##
170
- # Simulates Array#at and returns the entrie at that index.
171
+ # Simulates Array#at and returns the entry at that index.
171
172
  # Also accepts negative indexes and appropriate reverses
172
173
  # the order of the query
173
174
  #
@@ -295,7 +296,7 @@ module DataMapper
295
296
  # @see Array#delete
296
297
  #
297
298
  # @api public
298
- def delete(resource, &block)
299
+ def delete(resource)
299
300
  orphan_resource(super)
300
301
  end
301
302
 
@@ -515,6 +516,7 @@ module DataMapper
515
516
 
516
517
  @query = query
517
518
  @key_properties = model.key(repository.name)
519
+ @cache = {}
518
520
 
519
521
  super()
520
522
 
@@ -533,6 +535,7 @@ module DataMapper
533
535
  def relate_resource(resource)
534
536
  return unless resource
535
537
  resource.collection = self
538
+ @cache[resource.key] = resource
536
539
  resource
537
540
  end
538
541
 
@@ -540,7 +543,8 @@ module DataMapper
540
543
  # @api private
541
544
  def orphan_resource(resource)
542
545
  return unless resource
543
- resource.collection = nil if resource.collection == self
546
+ resource.collection = nil if resource.collection.object_id == self.object_id
547
+ @cache.delete(resource.key)
544
548
  resource
545
549
  end
546
550
 
@@ -626,7 +630,7 @@ module DataMapper
626
630
 
627
631
  query.update(
628
632
  :fields => klass.properties(repository.name).defaults,
629
- :links => self.query.links + [ relationship ]
633
+ :links => [ relationship ] + self.query.links
630
634
  )
631
635
 
632
636
  klass.all(query, &block)
@@ -6,10 +6,11 @@ module DataMapper
6
6
  #
7
7
  class DependencyQueue
8
8
  def initialize
9
- @dependencies = Hash.new { |h,k| h[k] = [] }
9
+ @dependencies = {}
10
10
  end
11
11
 
12
12
  def add(class_name, &callback)
13
+ @dependencies[class_name] ||= []
13
14
  @dependencies[class_name] << callback
14
15
  resolve!
15
16
  end
@@ -5,7 +5,7 @@ module DataMapper
5
5
  class IdentityMap
6
6
  # Get a resource from the IdentityMap
7
7
  def get(key)
8
- @cache[key]
8
+ @cache[key] || (@second_level_cache && @second_level_cache.get(key))
9
9
  end
10
10
 
11
11
  alias [] get
@@ -27,11 +27,8 @@ module DataMapper
27
27
  private
28
28
 
29
29
  def initialize(second_level_cache = nil)
30
- @cache = if @second_level_cache = second_level_cache
31
- Hash.new { |h,key| h[key] = @second_level_cache.get(key) }
32
- else
33
- Hash.new
34
- end
30
+ @cache = {}
31
+ @second_level_cache = second_level_cache
35
32
  end
36
33
 
37
34
  def cache
data/lib/dm-core/model.rb CHANGED
@@ -29,15 +29,15 @@ module DataMapper
29
29
  end
30
30
 
31
31
  def self.extended(model)
32
- model.instance_variable_set(:@storage_names, Hash.new { |h,k| h[k] = repository(k).adapter.resource_naming_convention.call(model.send(:default_storage_name)) })
33
- model.instance_variable_set(:@properties, Hash.new { |h,k| h[k] = k == Repository.default_name ? PropertySet.new : h[Repository.default_name].dup })
34
- model.instance_variable_set(:@field_naming_conventions, Hash.new { |h,k| h[k] = repository(k).adapter.field_naming_convention })
32
+ model.instance_variable_set(:@storage_names, {})
33
+ model.instance_variable_set(:@properties, {})
34
+ model.instance_variable_set(:@field_naming_conventions, {})
35
35
  extra_extensions.each { |extension| model.extend(extension) }
36
36
  end
37
37
 
38
38
  def inherited(target)
39
39
  target.instance_variable_set(:@storage_names, @storage_names.dup)
40
- target.instance_variable_set(:@properties, Hash.new { |h,k| h[k] = k == Repository.default_name ? PropertySet.new : h[Repository.default_name].dup })
40
+ target.instance_variable_set(:@properties, {})
41
41
  target.instance_variable_set(:@base_model, self.base_model)
42
42
  target.instance_variable_set(:@paranoid_properties, @paranoid_properties)
43
43
  target.instance_variable_set(:@field_naming_conventions, @field_naming_conventions.dup)
@@ -58,12 +58,13 @@ module DataMapper
58
58
  end
59
59
 
60
60
  if @relationships
61
- duped_relationships = Hash.new { |h,k| h[k] = {} }
61
+ duped_relationships = {}
62
62
  @relationships.each do |repository_name,relationships|
63
63
  relationships.each do |name, relationship|
64
64
  dup = relationship.dup
65
65
  dup.instance_variable_set(:@child_model, target) if dup.instance_variable_get(:@child_model) == self
66
66
  dup.instance_variable_set(:@parent_model, target) if dup.instance_variable_get(:@parent_model) == self
67
+ duped_relationships[repository_name] ||= {}
67
68
  duped_relationships[repository_name][name] = dup
68
69
  end
69
70
  end
@@ -102,13 +103,17 @@ module DataMapper
102
103
  # if given a block, otherwise the requested repository.
103
104
  #-
104
105
  # @api public
105
- def repository(name = nil, &block)
106
+ def repository(name = nil)
106
107
  #
107
108
  # There has been a couple of different strategies here, but me (zond) and dkubb are at least
108
109
  # united in the concept of explicitness over implicitness. That is - the explicit wish of the
109
110
  # caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last).
110
111
  #
111
- DataMapper.repository(name || repository_name, &block)
112
+ if block_given?
113
+ DataMapper.repository(name || repository_name) { |*block_args| yield(*block_args) }
114
+ else
115
+ DataMapper.repository(name || repository_name)
116
+ end
112
117
  end
113
118
 
114
119
  ##
@@ -116,7 +121,7 @@ module DataMapper
116
121
  #
117
122
  # @return <String> the storage name (IE table name, for database stores) associated with this resource in the given repository
118
123
  def storage_name(repository_name = default_repository_name)
119
- @storage_names[repository_name]
124
+ @storage_names[repository_name] ||= repository(repository_name).adapter.resource_naming_convention.call(base_model.send(:default_storage_name))
120
125
  end
121
126
 
122
127
  ##
@@ -130,9 +135,9 @@ module DataMapper
130
135
  ##
131
136
  # The field naming conventions for this resource across all repositories.
132
137
  #
133
- # @return <Hash(Symbol => String)> All available field naming conventions
134
- def field_naming_conventions
135
- @field_naming_conventions
138
+ # @return <String> The naming convention for the given repository
139
+ def field_naming_convention(repository_name = default_storage_name)
140
+ @field_naming_conventions[repository_name] ||= repository(repository_name).adapter.field_naming_convention
136
141
  end
137
142
 
138
143
  ##
@@ -148,14 +153,15 @@ module DataMapper
148
153
  create_property_getter(property)
149
154
  create_property_setter(property)
150
155
 
151
- @properties[repository_name][property.name] = property
156
+ properties(repository_name)[property.name] = property
157
+ @_valid_relations = false
152
158
 
153
159
  # Add property to the other mappings as well if this is for the default
154
160
  # repository.
155
161
  if repository_name == default_repository_name
156
162
  @properties.each_pair do |repository_name, properties|
157
163
  next if repository_name == default_repository_name
158
- properties << property
164
+ properties << property unless properties.has_property?(property.name)
159
165
  end
160
166
  end
161
167
 
@@ -168,7 +174,7 @@ module DataMapper
168
174
  context = :default if context == true
169
175
 
170
176
  Array(context).each do |item|
171
- @properties[repository_name].lazy_context(item) << name
177
+ properties(repository_name).lazy_context(item) << name
172
178
  end
173
179
  end
174
180
 
@@ -190,7 +196,25 @@ module DataMapper
190
196
  end
191
197
 
192
198
  def properties(repository_name = default_repository_name)
193
- @properties[repository_name]
199
+ # We need to check whether all relations are already set up.
200
+ # If this isn't the case, we try to reload them here
201
+ if !@_valid_relations && respond_to?(:many_to_one_relationships)
202
+ @_valid_relations = true
203
+ begin
204
+ many_to_one_relationships.each do |r|
205
+ r.child_key
206
+ end
207
+ rescue NameError
208
+ # Apparently not all relations are loaded,
209
+ # so we will try again later on
210
+ @_valid_relations = false
211
+ end
212
+ end
213
+ @properties[repository_name] ||= repository_name == Repository.default_name ? PropertySet.new : properties(Repository.default_name).dup
214
+ end
215
+
216
+ def eager_properties(repository_name = default_repository_name)
217
+ properties(repository_name).defaults
194
218
  end
195
219
 
196
220
  # @api private
@@ -207,15 +231,12 @@ module DataMapper
207
231
  end
208
232
 
209
233
  def key(repository_name = default_repository_name)
210
- @properties[repository_name].key
211
- end
212
-
213
- def inheritance_property(repository_name = default_repository_name)
214
- @properties[repository_name].inheritance_property
234
+ properties(repository_name).key
215
235
  end
216
236
 
217
- def default_order
218
- @default_order ||= key.map { |property| Query::Direction.new(property) }
237
+ def default_order(repository_name = default_repository_name)
238
+ @default_order ||= {}
239
+ @default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }
219
240
  end
220
241
 
221
242
  def get(*key)
@@ -288,8 +309,8 @@ module DataMapper
288
309
  # TODO SPEC
289
310
  def copy(source, destination, query = {})
290
311
  repository(destination) do
291
- repository(source).read_many(query).each do |resource|
292
- self.create(resource)
312
+ repository(source).read_many(scoped_query(query)).each do |resource|
313
+ self.create(resource.attributes)
293
314
  end
294
315
  end
295
316
  end
@@ -300,7 +321,7 @@ module DataMapper
300
321
  repository = query.repository
301
322
  model = self
302
323
 
303
- if inheritance_property_index = query.inheritance_property_index(repository)
324
+ if inheritance_property_index = query.inheritance_property_index
304
325
  model = values.at(inheritance_property_index) || model
305
326
  end
306
327
 
@@ -447,8 +468,8 @@ module DataMapper
447
468
  # @api public
448
469
  #
449
470
  # TODO: move to dm-more/dm-transactions
450
- def transaction(&block)
451
- DataMapper::Transaction.new(self, &block)
471
+ def transaction
472
+ DataMapper::Transaction.new(self) { |block_args| yield(*block_args) }
452
473
  end
453
474
  end # module Transaction
454
475
 
@@ -220,6 +220,21 @@ module DataMapper
220
220
  # dm-more bundle. For more information about validations, check the
221
221
  # documentation for dm-validations.
222
222
  #
223
+ # == Default Values
224
+ # To set a default for a property, use the <tt>:default</tt> key. The
225
+ # property will be set to the value associated with that key the first time
226
+ # it is accessed, or when the resource is saved if it hasn't been set with
227
+ # another value already. This value can be a static value, such as 'hello'
228
+ # but it can also be a proc that will be evaluated when the property is read
229
+ # before its value has been set. The property is set to the return of the
230
+ # proc. The proc is passed two values, the resource the property is being set
231
+ # for and the property itself.
232
+ #
233
+ # property :display_name, String, :default => { |r, p| r.login }
234
+ #
235
+ # Word of warning. Don't try to read the value of the property you're setting
236
+ # the default for in the proc. An infinite loop will ensue.
237
+ #
223
238
  # == Embedded Values
224
239
  # As an alternative to extraneous has_one relationships, consider using an
225
240
  # EmbeddedValue.
@@ -287,7 +302,7 @@ module DataMapper
287
302
  # -
288
303
  # @api semi-public
289
304
  def field(repository_name = nil)
290
- @field || fields[repository_name || model.repository_name]
305
+ @field || @fields[repository_name] ||= self.model.field_naming_convention(repository_name).call(self)
291
306
  end
292
307
 
293
308
  def unique
@@ -372,20 +387,15 @@ module DataMapper
372
387
  #-
373
388
  # @api private
374
389
  def get(resource)
375
- new_record = resource.new_record?
376
-
377
- lazy_load(resource) unless new_record || resource.attribute_loaded?(name)
390
+ lazy_load(resource)
378
391
 
379
392
  value = get!(resource)
380
393
 
381
- case track
382
- when :hash
383
- resource.original_values[name] = value.dup.hash unless resource.original_values.has_key?(name) rescue value.hash
384
- when :get
385
- resource.original_values[name] = value.dup unless resource.original_values.has_key?(name) rescue value
386
- end
394
+ set_original_value(resource, value)
387
395
 
388
- if value.nil? && new_record && !options[:default].nil? && !resource.attribute_loaded?(name)
396
+ # [YK] Why did we previously care whether options[:default] is nil.
397
+ # The default value of nil will be applied either way
398
+ if value.nil? && resource.new_record? && !resource.attribute_loaded?(name)
389
399
  value = default_for(resource)
390
400
  set(resource, value)
391
401
  end
@@ -397,21 +407,32 @@ module DataMapper
397
407
  resource.instance_variable_get(instance_variable_name)
398
408
  end
399
409
 
410
+ def set_original_value(resource, val)
411
+ unless resource.original_values.key?(name)
412
+ val = val.try_dup
413
+ val = val.hash if track == :hash
414
+ resource.original_values[name] = val
415
+ end
416
+ end
417
+
400
418
  # Provides a standardized setter method for the property
401
419
  #
402
420
  # @raise <ArgumentError> "+resource+ should be a DataMapper::Resource, but was ...."
403
421
  #-
404
422
  # @api private
405
423
  def set(resource, value)
406
- lazy_load(resource) unless resource.new_record? || resource.attribute_loaded?(name)
424
+ # [YK] We previously checked for new_record? here, but lazy loading
425
+ # is blocked anyway if we're in a new record by by
426
+ # Resource#reload_attributes. This may eventually be useful for
427
+ # optimizing, but let's (a) benchmark it first, and (b) do
428
+ # whatever refactoring is necessary, which will benefit from the
429
+ # centralize checking
430
+ lazy_load(resource)
431
+
407
432
  new_value = typecast(value)
408
433
  old_value = get!(resource)
409
434
 
410
- # skip setting the property if the new value is equal
411
- # to the old value, and the old value was defined
412
- return if new_value == old_value && resource.attribute_loaded?(name)
413
-
414
- resource.original_values[name] = old_value unless resource.original_values.has_key?(name)
435
+ set_original_value(resource, old_value)
415
436
 
416
437
  set!(resource, new_value)
417
438
  end
@@ -424,14 +445,13 @@ module DataMapper
424
445
  #-
425
446
  # @api private
426
447
  def lazy_load(resource)
427
- # TODO: refactor this section
428
- contexts = if lazy?
429
- name
430
- else
431
- model.properties(resource.repository.name).reject do |property|
432
- property.lazy? || resource.attribute_loaded?(property.name)
433
- end
434
- end
448
+ # It is faster to bail out at at a new_record? rather than to process
449
+ # which properties would be loaded and then not load them.
450
+ return if resource.new_record? || resource.attribute_loaded?(name)
451
+ # If we're trying to load a lazy property, load it. Otherwise, lazy-load
452
+ # any properties that should be eager-loaded but were not included
453
+ # in the original :fields list
454
+ contexts = lazy? ? name : model.eager_properties(resource.repository.name)
435
455
  resource.send(:lazy_load, contexts)
436
456
  end
437
457
 
@@ -457,14 +477,13 @@ module DataMapper
457
477
  # these two alternatives:
458
478
  # * Integer(value) rescue nil
459
479
  # * Integer(value_to_s =~ /(\d+)/ ? $1 : value_to_s) rescue nil
480
+ #
481
+ # [YK] The previous implementation used a rescue. Why use a rescue
482
+ # when the list of cases where a valid string other than "0" could
483
+ # produce 0 is known?
460
484
  value_to_i = value.to_i
461
- if value_to_i == 0 && value != '0'
462
- value_to_s = value.to_s
463
- begin
464
- Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
465
- rescue ArgumentError
466
- nil
467
- end
485
+ if value_to_i == 0
486
+ value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
468
487
  else
469
488
  value_to_i
470
489
  end
@@ -485,6 +504,10 @@ module DataMapper
485
504
  @default.respond_to?(:call) ? @default.call(resource, self) : @default
486
505
  end
487
506
 
507
+ def value(val)
508
+ custom? ? self.type.dump(val, self) : val
509
+ end
510
+
488
511
  def inspect
489
512
  "#<Property:#{@model}:#{@name}>"
490
513
  end
@@ -535,6 +558,7 @@ module DataMapper
535
558
  @index = @options.fetch(:index, false)
536
559
  @unique_index = @options.fetch(:unique_index, false)
537
560
  @lazy = @options.fetch(:lazy, @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
561
+ @fields = {}
538
562
 
539
563
  @track = @options.fetch(:track) do
540
564
  if @custom && @type.respond_to?(:track) && @type.track
@@ -575,10 +599,6 @@ module DataMapper
575
599
  @model.property_serialization_setup(self) if @model.respond_to?(:property_serialization_setup)
576
600
  end
577
601
 
578
- def fields
579
- @fields ||= Hash.new { |h,k| h[k] = self.model.field_naming_conventions[k].call(self) }
580
- end
581
-
582
602
  def determine_visibility # :nodoc:
583
603
  @reader_visibility = @options[:reader] || @options[:accessor] || :public
584
604
  @writer_visibility = @options[:writer] || @options[:accessor] || :public
@@ -615,7 +635,7 @@ module DataMapper
615
635
  def typecast_hash_to_datetime(hash)
616
636
  args = extract_time_args_from_hash(hash, :year, :month, :day, :hour, :min, :sec)
617
637
  DateTime.new(*args)
618
- rescue ArgumentError
638
+ rescue ArgumentError => e
619
639
  t = typecast_hash_to_time(hash)
620
640
  DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec)
621
641
  end