sam-dm-core 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
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