sam-dm-core 0.9.6 → 0.9.7

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 (56) hide show
  1. data/Manifest.txt +5 -0
  2. data/QUICKLINKS +1 -2
  3. data/Rakefile +3 -3
  4. data/SPECS +9 -10
  5. data/lib/dm-core.rb +15 -22
  6. data/lib/dm-core/adapters.rb +18 -0
  7. data/lib/dm-core/adapters/abstract_adapter.rb +17 -10
  8. data/lib/dm-core/adapters/data_objects_adapter.rb +18 -16
  9. data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
  10. data/lib/dm-core/adapters/postgres_adapter.rb +2 -2
  11. data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
  12. data/lib/dm-core/associations.rb +3 -2
  13. data/lib/dm-core/associations/many_to_many.rb +2 -2
  14. data/lib/dm-core/associations/many_to_one.rb +1 -1
  15. data/lib/dm-core/associations/one_to_many.rb +13 -7
  16. data/lib/dm-core/associations/relationship.rb +20 -15
  17. data/lib/dm-core/auto_migrations.rb +4 -12
  18. data/lib/dm-core/collection.rb +9 -5
  19. data/lib/dm-core/dependency_queue.rb +2 -1
  20. data/lib/dm-core/identity_map.rb +3 -6
  21. data/lib/dm-core/model.rb +44 -27
  22. data/lib/dm-core/property.rb +3 -13
  23. data/lib/dm-core/property_set.rb +29 -22
  24. data/lib/dm-core/query.rb +49 -47
  25. data/lib/dm-core/repository.rb +3 -3
  26. data/lib/dm-core/resource.rb +12 -12
  27. data/lib/dm-core/scope.rb +7 -7
  28. data/lib/dm-core/support/kernel.rb +6 -2
  29. data/lib/dm-core/transaction.rb +7 -7
  30. data/lib/dm-core/version.rb +1 -1
  31. data/script/performance.rb +109 -30
  32. data/script/profile.rb +2 -2
  33. data/spec/integration/association_spec.rb +13 -1
  34. data/spec/integration/associations/one_to_many_spec.rb +40 -3
  35. data/spec/integration/auto_migrations_spec.rb +16 -1
  36. data/spec/integration/dependency_queue_spec.rb +0 -12
  37. data/spec/integration/postgres_adapter_spec.rb +1 -1
  38. data/spec/integration/property_spec.rb +4 -4
  39. data/spec/integration/resource_spec.rb +6 -0
  40. data/spec/integration/sti_spec.rb +22 -0
  41. data/spec/integration/strategic_eager_loading_spec.rb +21 -6
  42. data/spec/integration/type_spec.rb +1 -1
  43. data/spec/lib/model_loader.rb +10 -1
  44. data/spec/models/zoo.rb +1 -0
  45. data/spec/spec_helper.rb +3 -2
  46. data/spec/unit/adapters/data_objects_adapter_spec.rb +3 -3
  47. data/spec/unit/associations/many_to_many_spec.rb +16 -1
  48. data/spec/unit/associations/many_to_one_spec.rb +9 -2
  49. data/spec/unit/model_spec.rb +12 -30
  50. data/spec/unit/property_set_spec.rb +8 -1
  51. data/spec/unit/query_spec.rb +41 -0
  52. data/spec/unit/resource_spec.rb +27 -4
  53. data/spec/unit/transaction_spec.rb +13 -13
  54. data/tasks/ci.rb +4 -36
  55. data/tasks/dm.rb +3 -3
  56. metadata +7 -16
@@ -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] }
@@ -157,14 +162,14 @@ module DataMapper
157
162
  end
158
163
 
159
164
  # @api private
160
- def with_repository(object = nil, &block)
165
+ def with_repository(object = nil)
161
166
  other_model = object.model == child_model ? parent_model : child_model if object.respond_to?(:model)
162
167
  other_model = object == child_model ? parent_model : child_model if object.kind_of?(DataMapper::Resource)
163
168
 
164
169
  if other_model && other_model.repository == object.repository && object.repository.name != @repository_name
165
- object.repository.scope(&block)
170
+ object.repository.scope { |block_args| yield(*block_args) }
166
171
  else
167
- repository(@repository_name, &block)
172
+ repository(@repository_name) { |block_args| yield(*block_args) }
168
173
  end
169
174
  end
170
175
 
@@ -73,12 +73,8 @@ module DataMapper
73
73
  # @api private
74
74
  def auto_migrate_down!(repository_name = self.repository_name)
75
75
  # repository_name ||= default_repository_name
76
- if self.superclass != Object
77
- self.superclass.auto_migrate!(repository_name)
78
- else
79
- repository(repository_name) do |r|
80
- r.adapter.destroy_model_storage(r, self)
81
- end
76
+ repository(repository_name) do |r|
77
+ r.adapter.destroy_model_storage(r, self.base_model)
82
78
  end
83
79
  end
84
80
 
@@ -88,12 +84,8 @@ module DataMapper
88
84
  # @param Symbol repository_name the repository to be migrated
89
85
  # @api private
90
86
  def auto_migrate_up!(repository_name = self.repository_name)
91
- if self.superclass != Object
92
- self.superclass.auto_migrate!(repository_name)
93
- else
94
- repository(repository_name) do |r|
95
- r.adapter.create_model_storage(r, self)
96
- end
87
+ repository(repository_name) do |r|
88
+ r.adapter.create_model_storage(r, self.base_model)
97
89
  end
98
90
  end
99
91
 
@@ -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
 
@@ -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,7 +153,8 @@ 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.
@@ -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,11 +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
194
214
  end
195
215
 
196
216
  def eager_properties(repository_name = default_repository_name)
197
- @properties[repository_name].defaults
217
+ properties(repository_name).defaults
198
218
  end
199
219
 
200
220
  # @api private
@@ -211,15 +231,12 @@ module DataMapper
211
231
  end
212
232
 
213
233
  def key(repository_name = default_repository_name)
214
- @properties[repository_name].key
215
- end
216
-
217
- def inheritance_property(repository_name = default_repository_name)
218
- @properties[repository_name].inheritance_property
234
+ properties(repository_name).key
219
235
  end
220
236
 
221
237
  def default_order(repository_name = default_repository_name)
222
- key(repository_name).map { |property| Query::Direction.new(property) }
238
+ @default_order ||= {}
239
+ @default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }
223
240
  end
224
241
 
225
242
  def get(*key)
@@ -292,8 +309,8 @@ module DataMapper
292
309
  # TODO SPEC
293
310
  def copy(source, destination, query = {})
294
311
  repository(destination) do
295
- repository(source).read_many(query).each do |resource|
296
- self.create(resource)
312
+ repository(source).read_many(scoped_query(query)).each do |resource|
313
+ self.create(resource.attributes)
297
314
  end
298
315
  end
299
316
  end
@@ -304,7 +321,7 @@ module DataMapper
304
321
  repository = query.repository
305
322
  model = self
306
323
 
307
- if inheritance_property_index = query.inheritance_property_index(repository)
324
+ if inheritance_property_index = query.inheritance_property_index
308
325
  model = values.at(inheritance_property_index) || model
309
326
  end
310
327
 
@@ -345,7 +362,7 @@ module DataMapper
345
362
 
346
363
  # TODO: spec this
347
364
  def to_query(repository, key, query = {})
348
- conditions = Hash[ *self.key(repository_name).zip(key).flatten ]
365
+ conditions = Hash[ *self.key(repository.name).zip(key).flatten ]
349
366
  Query.new(repository, self, query.merge(conditions))
350
367
  end
351
368
 
@@ -451,8 +468,8 @@ module DataMapper
451
468
  # @api public
452
469
  #
453
470
  # TODO: move to dm-more/dm-transactions
454
- def transaction(&block)
455
- DataMapper::Transaction.new(self, &block)
471
+ def transaction
472
+ DataMapper::Transaction.new(self) { |block_args| yield(*block_args) }
456
473
  end
457
474
  end # module Transaction
458
475
 
@@ -302,7 +302,7 @@ module DataMapper
302
302
  # -
303
303
  # @api semi-public
304
304
  def field(repository_name = nil)
305
- @field || fields[repository_name]
305
+ @field || @fields[repository_name] ||= self.model.field_naming_convention(repository_name).call(self)
306
306
  end
307
307
 
308
308
  def unique
@@ -432,13 +432,6 @@ module DataMapper
432
432
  new_value = typecast(value)
433
433
  old_value = get!(resource)
434
434
 
435
- # skip setting the property if the new value is equal
436
- # to the old value, and the old value was defined
437
- # ---
438
- # [YK] Why bother? Does this change the result at all?
439
- # ---
440
- # return if new_value == old_value && resource.attribute_loaded?(name)
441
-
442
435
  set_original_value(resource, old_value)
443
436
 
444
437
  set!(resource, new_value)
@@ -458,7 +451,7 @@ module DataMapper
458
451
  # If we're trying to load a lazy property, load it. Otherwise, lazy-load
459
452
  # any properties that should be eager-loaded but were not included
460
453
  # in the original :fields list
461
- contexts = lazy? ? name : model.eager_properties(resource.repository.name).map {|property| property.name}
454
+ contexts = lazy? ? name : model.eager_properties(resource.repository.name)
462
455
  resource.send(:lazy_load, contexts)
463
456
  end
464
457
 
@@ -565,6 +558,7 @@ module DataMapper
565
558
  @index = @options.fetch(:index, false)
566
559
  @unique_index = @options.fetch(:unique_index, false)
567
560
  @lazy = @options.fetch(:lazy, @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
561
+ @fields = {}
568
562
 
569
563
  @track = @options.fetch(:track) do
570
564
  if @custom && @type.respond_to?(:track) && @type.track
@@ -605,10 +599,6 @@ module DataMapper
605
599
  @model.property_serialization_setup(self) if @model.respond_to?(:property_serialization_setup)
606
600
  end
607
601
 
608
- def fields
609
- @fields ||= Hash.new { |h,k| h[k] = self.model.field_naming_conventions[k].call(self) }
610
- end
611
-
612
602
  def determine_visibility # :nodoc:
613
603
  @reader_visibility = @options[:reader] || @options[:accessor] || :public
614
604
  @writer_visibility = @options[:writer] || @options[:accessor] || :public
@@ -4,10 +4,11 @@ module DataMapper
4
4
  include Enumerable
5
5
 
6
6
  def [](name)
7
- @property_for[name] || raise(ArgumentError, "Unknown property '#{name}'", caller)
7
+ property_for(name) || raise(ArgumentError, "Unknown property '#{name}'", caller)
8
8
  end
9
9
 
10
10
  def []=(name, property)
11
+ @key, @defaults = nil
11
12
  if existing_property = detect { |p| p.name == name }
12
13
  property.hash
13
14
  @entries[@entries.index(existing_property)] = property
@@ -18,14 +19,23 @@ module DataMapper
18
19
  end
19
20
 
20
21
  def has_property?(name)
21
- !!@property_for[name]
22
+ !!property_for(name)
22
23
  end
23
24
 
24
25
  def slice(*names)
25
- @property_for.values_at(*names)
26
+ @key, @defaults = nil
27
+ names.map do |name|
28
+ property_for(name)
29
+ end
30
+ end
31
+
32
+ def clear
33
+ @key, @defaults = nil
34
+ @entries.clear
26
35
  end
27
36
 
28
37
  def add(*properties)
38
+ @key, @defaults = nil
29
39
  @entries.push(*properties)
30
40
  properties.each { |property| property.hash }
31
41
  self
@@ -47,11 +57,11 @@ module DataMapper
47
57
  end
48
58
 
49
59
  def defaults
50
- reject { |property| property.lazy? }
60
+ @defaults ||= reject { |property| property.lazy? }
51
61
  end
52
62
 
53
63
  def key
54
- select { |property| property.key? }
64
+ @key ||= select { |property| property.key? }
55
65
  end
56
66
 
57
67
  def indexes
@@ -68,10 +78,6 @@ module DataMapper
68
78
  index_hash
69
79
  end
70
80
 
71
- def inheritance_property
72
- detect { |property| property.type == DataMapper::Types::Discriminator }
73
- end
74
-
75
81
  def get(resource)
76
82
  map { |property| property.get(resource) }
77
83
  end
@@ -93,7 +99,7 @@ module DataMapper
93
99
  end
94
100
 
95
101
  def lazy_context(name)
96
- lazy_contexts[name]
102
+ lazy_contexts[name] ||= []
97
103
  end
98
104
 
99
105
  def lazy_load_context(names)
@@ -128,25 +134,17 @@ module DataMapper
128
134
  assert_kind_of 'properties', properties, Enumerable
129
135
 
130
136
  @entries = properties
131
- @property_for = hash_for_property_for
137
+ @property_for = {}
132
138
  end
133
139
 
134
140
  def initialize_copy(orig)
141
+ @key, @defaults = nil
135
142
  @entries = orig.entries.dup
136
- @property_for = hash_for_property_for
137
- end
138
-
139
- def hash_for_property_for
140
- Hash.new do |h,k|
141
- ksym = k.to_sym
142
- if property = detect { |property| property.name == ksym }
143
- h[ksym] = h[k.to_s] = property
144
- end
145
- end
143
+ @property_for = {}
146
144
  end
147
145
 
148
146
  def lazy_contexts
149
- @lazy_contexts ||= Hash.new { |h,context| h[context] = [] }
147
+ @lazy_contexts ||= {}
150
148
  end
151
149
 
152
150
  def parse_index(index, property, index_hash)
@@ -158,5 +156,14 @@ module DataMapper
158
156
  when Enumerable then index.each { |idx| parse_index(idx, property, index_hash) }
159
157
  end
160
158
  end
159
+
160
+ def property_for(name)
161
+ unless @property_for[name]
162
+ property = detect { |property| property.name == name.to_sym }
163
+ @property_for[name.to_s] = @property_for[name.to_sym] = property if property
164
+ end
165
+ @property_for[name]
166
+ end
167
+
161
168
  end # class PropertySet
162
169
  end # module DataMapper