dm-core 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +27 -0
  4. data/LICENSE +20 -0
  5. data/{README.txt → README.rdoc} +14 -3
  6. data/Rakefile +23 -22
  7. data/VERSION +1 -0
  8. data/dm-core.gemspec +201 -10
  9. data/lib/dm-core.rb +32 -23
  10. data/lib/dm-core/adapters.rb +0 -1
  11. data/lib/dm-core/adapters/data_objects_adapter.rb +230 -151
  12. data/lib/dm-core/adapters/mysql_adapter.rb +7 -8
  13. data/lib/dm-core/adapters/oracle_adapter.rb +39 -59
  14. data/lib/dm-core/adapters/postgres_adapter.rb +0 -1
  15. data/lib/dm-core/adapters/sqlite3_adapter.rb +5 -0
  16. data/lib/dm-core/adapters/sqlserver_adapter.rb +114 -0
  17. data/lib/dm-core/adapters/yaml_adapter.rb +0 -5
  18. data/lib/dm-core/associations/many_to_many.rb +118 -56
  19. data/lib/dm-core/associations/many_to_one.rb +48 -21
  20. data/lib/dm-core/associations/one_to_many.rb +8 -30
  21. data/lib/dm-core/associations/one_to_one.rb +1 -5
  22. data/lib/dm-core/associations/relationship.rb +89 -97
  23. data/lib/dm-core/collection.rb +299 -184
  24. data/lib/dm-core/core_ext/enumerable.rb +28 -0
  25. data/lib/dm-core/core_ext/kernel.rb +0 -2
  26. data/lib/dm-core/migrations.rb +314 -170
  27. data/lib/dm-core/model.rb +97 -66
  28. data/lib/dm-core/model/descendant_set.rb +1 -1
  29. data/lib/dm-core/model/hook.rb +0 -3
  30. data/lib/dm-core/model/property.rb +7 -10
  31. data/lib/dm-core/model/relationship.rb +79 -26
  32. data/lib/dm-core/model/scope.rb +3 -4
  33. data/lib/dm-core/property.rb +152 -90
  34. data/lib/dm-core/property_set.rb +18 -37
  35. data/lib/dm-core/query.rb +452 -153
  36. data/lib/dm-core/query/conditions/comparison.rb +266 -173
  37. data/lib/dm-core/query/conditions/operation.rb +499 -57
  38. data/lib/dm-core/query/direction.rb +0 -3
  39. data/lib/dm-core/query/operator.rb +0 -4
  40. data/lib/dm-core/query/path.rb +10 -12
  41. data/lib/dm-core/query/sort.rb +4 -10
  42. data/lib/dm-core/repository.rb +10 -6
  43. data/lib/dm-core/resource.rb +343 -148
  44. data/lib/dm-core/spec/adapter_shared_spec.rb +17 -1
  45. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +277 -17
  46. data/lib/dm-core/support/chainable.rb +0 -2
  47. data/lib/dm-core/support/equalizer.rb +27 -3
  48. data/lib/dm-core/transaction.rb +75 -75
  49. data/lib/dm-core/type.rb +19 -5
  50. data/lib/dm-core/types/discriminator.rb +4 -4
  51. data/lib/dm-core/types/object.rb +2 -7
  52. data/lib/dm-core/types/paranoid_boolean.rb +8 -2
  53. data/lib/dm-core/types/paranoid_datetime.rb +8 -2
  54. data/lib/dm-core/version.rb +1 -1
  55. data/script/performance.rb +7 -7
  56. data/script/profile.rb +6 -6
  57. data/spec/lib/collection_helpers.rb +2 -2
  58. data/spec/lib/pending_helpers.rb +22 -3
  59. data/spec/lib/rspec_immediate_feedback_formatter.rb +1 -0
  60. data/spec/public/associations/many_to_many_spec.rb +6 -4
  61. data/spec/public/associations/many_to_one_spec.rb +10 -1
  62. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +39 -0
  63. data/spec/public/associations/one_to_many_spec.rb +4 -3
  64. data/spec/public/associations/one_to_one_spec.rb +19 -1
  65. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +45 -0
  66. data/spec/public/collection_spec.rb +4 -3
  67. data/spec/public/migrations_spec.rb +144 -0
  68. data/spec/public/model/relationship_spec.rb +115 -55
  69. data/spec/public/model_spec.rb +13 -13
  70. data/spec/public/property/object_spec.rb +106 -0
  71. data/spec/public/property_spec.rb +18 -14
  72. data/spec/public/resource_spec.rb +10 -1
  73. data/spec/public/sel_spec.rb +16 -49
  74. data/spec/public/setup_spec.rb +1 -1
  75. data/spec/public/shared/association_collection_shared_spec.rb +6 -14
  76. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  77. data/spec/public/shared/collection_shared_spec.rb +214 -217
  78. data/spec/public/shared/finder_shared_spec.rb +259 -365
  79. data/spec/public/shared/resource_shared_spec.rb +524 -248
  80. data/spec/public/transaction_spec.rb +27 -3
  81. data/spec/public/types/discriminator_spec.rb +1 -1
  82. data/spec/rcov.opts +6 -0
  83. data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +17 -0
  84. data/spec/semipublic/associations/many_to_one_spec.rb +3 -20
  85. data/spec/semipublic/associations_spec.rb +2 -2
  86. data/spec/semipublic/collection_spec.rb +0 -32
  87. data/spec/semipublic/model_spec.rb +96 -0
  88. data/spec/semipublic/property_spec.rb +3 -3
  89. data/spec/semipublic/query/conditions/comparison_spec.rb +1719 -0
  90. data/spec/semipublic/query/conditions/operation_spec.rb +1292 -0
  91. data/spec/semipublic/query_spec.rb +1285 -144
  92. data/spec/semipublic/resource_spec.rb +0 -24
  93. data/spec/semipublic/shared/resource_shared_spec.rb +103 -38
  94. data/spec/spec.opts +1 -1
  95. data/spec/spec_helper.rb +15 -6
  96. data/tasks/ci.rake +1 -0
  97. data/tasks/metrics.rake +37 -0
  98. data/tasks/spec.rake +41 -0
  99. data/tasks/yard.rake +9 -0
  100. data/tasks/yardstick.rake +19 -0
  101. metadata +99 -29
  102. data/CONTRIBUTING +0 -51
  103. data/FAQ +0 -93
  104. data/History.txt +0 -27
  105. data/MIT-LICENSE +0 -22
  106. data/Manifest.txt +0 -121
  107. data/QUICKLINKS +0 -11
  108. data/SPECS +0 -35
  109. data/TODO +0 -1
  110. data/spec/semipublic/query/conditions_spec.rb +0 -528
  111. data/tasks/ci.rb +0 -24
  112. data/tasks/dm.rb +0 -58
  113. data/tasks/doc.rb +0 -17
  114. data/tasks/gemspec.rb +0 -23
  115. data/tasks/hoe.rb +0 -45
  116. data/tasks/install.rb +0 -18
data/lib/dm-core/model.rb CHANGED
@@ -50,7 +50,7 @@ module DataMapper
50
50
  # @return [DescendantSet]
51
51
  # Set containing the descendant models
52
52
  #
53
- # @api private
53
+ # @api semipublic
54
54
  def self.descendants
55
55
  @descendants ||= DescendantSet.new
56
56
  end
@@ -69,7 +69,7 @@ module DataMapper
69
69
  # @return [Set]
70
70
  # Set containing the descendant classes
71
71
  #
72
- # @api private
72
+ # @api semipublic
73
73
  attr_reader :descendants
74
74
 
75
75
  # Appends a module for inclusion into the model class after Resource.
@@ -86,6 +86,12 @@ module DataMapper
86
86
  # @api semipublic
87
87
  def self.append_inclusions(*inclusions)
88
88
  extra_inclusions.concat inclusions
89
+
90
+ # Add the inclusion to existing descendants
91
+ descendants.each do |model|
92
+ inclusions.each { |inclusion| model.send :include, inclusion }
93
+ end
94
+
89
95
  true
90
96
  end
91
97
 
@@ -111,6 +117,12 @@ module DataMapper
111
117
  # @api semipublic
112
118
  def self.append_extensions(*extensions)
113
119
  extra_extensions.concat extensions
120
+
121
+ # Add the extension to existing descendants
122
+ descendants.each do |model|
123
+ extensions.each { |extension| model.extend(extension) }
124
+ end
125
+
114
126
  true
115
127
  end
116
128
 
@@ -123,9 +135,10 @@ module DataMapper
123
135
  @extra_extensions ||= []
124
136
  end
125
137
 
126
- # TODO: document
127
138
  # @api private
128
139
  def self.extended(model)
140
+ descendants = self.descendants
141
+
129
142
  descendants << model
130
143
 
131
144
  model.instance_variable_set(:@valid, false)
@@ -138,10 +151,11 @@ module DataMapper
138
151
  extra_inclusions.each { |mod| model.send(:include, mod) }
139
152
  end
140
153
 
141
- # TODO: document
142
154
  # @api private
143
155
  chainable do
144
156
  def inherited(model)
157
+ descendants = self.descendants
158
+
145
159
  descendants << model
146
160
 
147
161
  model.instance_variable_set(:@valid, false)
@@ -192,12 +206,14 @@ module DataMapper
192
206
  # @param [Object] *key
193
207
  # The primary key or keys to use for lookup
194
208
  #
195
- # @return [Resource,NilClass]
209
+ # @return [Resource, nil]
196
210
  # A single model that was found
197
211
  # If no instance was found matching +key+
198
212
  #
199
213
  # @api public
200
214
  def get(*key)
215
+ assert_valid_key_size(key)
216
+
201
217
  repository = self.repository
202
218
  key = self.key(repository.name).typecast(key)
203
219
 
@@ -256,6 +272,8 @@ module DataMapper
256
272
  #
257
273
  # @api public
258
274
  def all(query = nil)
275
+ # TODO: update this not to accept a nil value, and instead either
276
+ # accept a Hash/Query and nothing else
259
277
  if query.nil? || (query.kind_of?(Hash) && query.empty?)
260
278
  # TODO: after adding Enumerable methods to Model, try to return self here
261
279
  new_collection(self.query.dup)
@@ -282,22 +300,18 @@ module DataMapper
282
300
  #
283
301
  # @api public
284
302
  def first(*args)
285
- last_arg = args.last
303
+ first_arg = args.first
304
+ last_arg = args.last
286
305
 
287
- limit = args.first if args.first.kind_of?(Integer)
288
- with_query = last_arg.respond_to?(:merge) && !last_arg.blank?
306
+ limit_specified = first_arg.kind_of?(Integer)
307
+ with_query = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query)
289
308
 
290
- query = with_query ? last_arg : {}
309
+ limit = limit_specified ? first_arg : 1
310
+ query = with_query ? last_arg : {}
291
311
 
292
- query = if query.kind_of?(Query)
293
- query.slice(0, limit || 1)
294
- else
295
- offset = query.fetch(:offset, 0)
296
- query = query.except(:offset)
297
- scoped_query(query).slice(offset, limit || 1)
298
- end
312
+ query = self.query.slice(0, limit).update(query)
299
313
 
300
- if limit
314
+ if limit_specified
301
315
  all(query)
302
316
  else
303
317
  query.repository.read(query).first
@@ -322,22 +336,18 @@ module DataMapper
322
336
  #
323
337
  # @api public
324
338
  def last(*args)
325
- last_arg = args.last
339
+ first_arg = args.first
340
+ last_arg = args.last
326
341
 
327
- limit = args.first if args.first.kind_of?(Integer)
328
- with_query = last_arg.respond_to?(:merge) && !last_arg.blank?
342
+ limit_specified = first_arg.kind_of?(Integer)
343
+ with_query = (last_arg.kind_of?(Hash) && !last_arg.empty?) || last_arg.kind_of?(Query)
329
344
 
330
- query = with_query ? last_arg : {}
345
+ limit = limit_specified ? first_arg : 1
346
+ query = with_query ? last_arg : {}
331
347
 
332
- query = if query.kind_of?(Query)
333
- query.slice(0, limit || 1).reverse!
334
- else
335
- offset = query.fetch(:offset, 0)
336
- query = query.except(:offset)
337
- scoped_query(query).slice(offset, limit || 1).reverse!
338
- end
348
+ query = self.query.slice(0, limit).update(query).reverse!
339
349
 
340
- if limit
350
+ if limit_specified
341
351
  all(query)
342
352
  else
343
353
  query.repository.read(query).last
@@ -418,9 +428,9 @@ module DataMapper
418
428
 
419
429
  # Copy a set of records from one repository to another.
420
430
  #
421
- # @param [String] source
431
+ # @param [String] source_repository_name
422
432
  # The name of the Repository the resources should be copied _from_
423
- # @param [String] destination
433
+ # @param [String] target_repository_name
424
434
  # The name of the Repository the resources should be copied _to_
425
435
  # @param [Hash] query
426
436
  # The conditions with which to find the records to copy. These
@@ -430,16 +440,23 @@ module DataMapper
430
440
  # A Collection of the Resource instances created in the operation
431
441
  #
432
442
  # @api public
433
- def copy(source, destination, query = {})
443
+ def copy(source_repository_name, target_repository_name, query = {})
444
+ target_properties = properties(target_repository_name)
434
445
 
435
- # get the list of properties that exist in the source and destination
436
- destination_properties = properties(destination)
437
- fields = query[:fields] ||= properties(source).select { |property| destination_properties.include?(property) }
446
+ query[:fields] ||= properties(source_repository_name).select do |property|
447
+ target_properties.include?(property)
448
+ end
449
+
450
+ repository(target_repository_name) do |repository|
451
+ resources = []
438
452
 
439
- repository(destination) do
440
- all(query.merge(:repository => source)).map do |resource|
441
- create(fields.map { |property| [ property.name, property.get(resource) ] }.to_hash)
453
+ all(query.merge(:repository => source_repository_name)).each do |resource|
454
+ new_resource = new
455
+ query[:fields].each { |property| property.set(new_resource, property.get(resource)) }
456
+ resources << new_resource if new_resource.save
442
457
  end
458
+
459
+ all(Query.target_query(repository, self, resources))
443
460
  end
444
461
  end
445
462
 
@@ -473,9 +490,10 @@ module DataMapper
473
490
  record = record.dup
474
491
  field_map.each { |property, field| record[property] = record.delete(field) if record.key?(field) }
475
492
 
476
- model = discriminator && record[discriminator] || self
493
+ model = discriminator && record[discriminator] || self
494
+ model_key = model.key(repository_name)
477
495
 
478
- resource = if (key_values = record.values_at(*model.key(repository_name))).all?
496
+ resource = if model_key.valid?(key_values = record.values_at(*model_key))
479
497
  identity_map = repository.identity_map(model)
480
498
  identity_map[key_values]
481
499
  end
@@ -497,9 +515,10 @@ module DataMapper
497
515
  end
498
516
 
499
517
  when Resource
500
- model = record.model
518
+ model = record.model
519
+ model_key = model.key(repository_name)
501
520
 
502
- resource = if (key_values = record.key).all?
521
+ resource = if model_key.valid?(key_values = record.key)
503
522
  identity_map = repository.identity_map(model)
504
523
  identity_map[key_values]
505
524
  end
@@ -513,32 +532,29 @@ module DataMapper
513
532
  end
514
533
  end
515
534
 
516
- resource.instance_variable_set(:@repository, repository)
517
- resource.instance_variable_set(:@saved, true)
535
+ resource.instance_variable_set(:@_repository, repository)
536
+ resource.instance_variable_set(:@_saved, true)
518
537
 
519
538
  if identity_map
520
539
  # defer setting the IdentityMap so second level caches can
521
540
  # record the state of the resource after loaded
522
541
  identity_map[key_values] = resource
523
542
  else
524
- resource.freeze
543
+ resource.instance_variable_set(:@_readonly, true)
525
544
  end
526
545
 
527
546
  resource
528
547
  end
529
548
  end
530
549
 
531
- # TODO: document
532
550
  # @api semipublic
533
551
  attr_reader :base_model
534
552
 
535
- # TODO: document
536
553
  # @api semipublic
537
554
  def default_repository_name
538
555
  Repository.default_name
539
556
  end
540
557
 
541
- # TODO: document
542
558
  # @api semipublic
543
559
  def default_order(repository_name = default_repository_name)
544
560
  @default_order[repository_name] ||= key(repository_name).map { |property| Query::Direction.new(property) }.freeze
@@ -557,17 +573,14 @@ module DataMapper
557
573
  # otherwise the requested repository.
558
574
  #
559
575
  # @api private
560
- def repository(name = nil)
576
+ def repository(name = nil, &block)
561
577
  #
562
578
  # There has been a couple of different strategies here, but me (zond) and dkubb are at least
563
579
  # united in the concept of explicitness over implicitness. That is - the explicit wish of the
564
580
  # caller (+name+) should be given more priority than the implicit wish of the caller (Repository.context.last).
565
581
  #
566
- if block_given?
567
- DataMapper.repository(name || repository_name) { |*block_args| yield(*block_args) }
568
- else
569
- DataMapper.repository(name || repository_name)
570
- end
582
+
583
+ DataMapper.repository(name || repository_name, &block)
571
584
  end
572
585
 
573
586
  # Get the current +repository_name+ for this Model.
@@ -580,7 +593,8 @@ module DataMapper
580
593
  #
581
594
  # @api private
582
595
  def repository_name
583
- Repository.context.any? ? Repository.context.last.name : default_repository_name
596
+ context = Repository.context
597
+ context.any? ? context.last.name : default_repository_name
584
598
  end
585
599
 
586
600
  # Gets the current Set of repositories for which
@@ -595,13 +609,11 @@ module DataMapper
595
609
  [ repository ].to_set + @properties.keys.map { |repository_name| DataMapper.repository(repository_name) }
596
610
  end
597
611
 
598
- # TODO: document
599
612
  # @api private
600
613
  def model_method_defined?(method)
601
614
  model_methods.include?(method.to_s)
602
615
  end
603
616
 
604
- # TODO: document
605
617
  # @api private
606
618
  def resource_method_defined?(method)
607
619
  resource_methods.include?(method.to_s)
@@ -609,15 +621,13 @@ module DataMapper
609
621
 
610
622
  private
611
623
 
612
- # TODO: document
613
624
  # @api private
614
625
  def _create(safe, attributes)
615
626
  resource = new(attributes)
616
- resource.send(safe ? :save : :save!)
627
+ resource.__send__(safe ? :save : :save!)
617
628
  resource
618
629
  end
619
630
 
620
- # TODO: document
621
631
  # @api private
622
632
  def const_missing(name)
623
633
  if name == :DM
@@ -632,7 +642,6 @@ module DataMapper
632
642
  end
633
643
  end
634
644
 
635
- # TODO: document
636
645
  # @api private
637
646
  def default_storage_name
638
647
  base_model.name
@@ -667,10 +676,12 @@ module DataMapper
667
676
  self.repository
668
677
  end
669
678
 
679
+ query = self.query.merge(query)
680
+
670
681
  if self.query.repository == repository
671
- self.query.merge(query)
682
+ query
672
683
  else
673
- Query.new(repository, self, self.query.merge(query).options)
684
+ Query.new(repository, self, query.options)
674
685
  end
675
686
  end
676
687
  end
@@ -680,6 +691,9 @@ module DataMapper
680
691
  return if @valid
681
692
  @valid = true
682
693
 
694
+ name = self.name
695
+ repository_name = self.repository_name
696
+
683
697
  if properties(repository_name).empty? &&
684
698
  !relationships(repository_name).any? { |(relationship_name, relationship)| relationship.kind_of?(Associations::ManyToOne::Relationship) }
685
699
  raise IncompleteModelError, "#{name} must have at least one property or many to one relationship to be valid"
@@ -699,19 +713,16 @@ module DataMapper
699
713
  end
700
714
  end
701
715
 
702
- # TODO: document
703
716
  # @api private
704
717
  def model_methods
705
718
  @model_methods ||= ancestor_instance_methods { |mod| mod.meta_class }
706
719
  end
707
720
 
708
- # TODO: document
709
721
  # @api private
710
722
  def resource_methods
711
723
  @resource_methods ||= ancestor_instance_methods { |mod| mod }
712
724
  end
713
725
 
714
- # TODO: document
715
726
  # @api private
716
727
  def ancestor_instance_methods
717
728
  methods = Set.new
@@ -723,5 +734,25 @@ module DataMapper
723
734
 
724
735
  methods
725
736
  end
737
+
738
+ # Raises an exception if #get receives the wrong number of arguments
739
+ #
740
+ # @param [Array] key
741
+ # the key value
742
+ #
743
+ # @return [undefined]
744
+ #
745
+ # @raise [UpdateConflictError]
746
+ # raise if the resource is dirty
747
+ #
748
+ # @api private
749
+ def assert_valid_key_size(key)
750
+ expected_key_size = self.key(repository_name).size
751
+ actual_key_size = key.size
752
+
753
+ if actual_key_size != expected_key_size
754
+ raise ArgumentError, "The number of arguments for the key is invalid, expected #{expected_key_size} but was #{actual_key_size}"
755
+ end
756
+ end
726
757
  end # module Model
727
758
  end # module DataMapper
@@ -41,7 +41,7 @@ module DataMapper
41
41
  # @param [Model] model
42
42
  # the model to remove
43
43
  #
44
- # @return [Model, NilClass]
44
+ # @return [Model, nil]
45
45
  # the model is return if it is a descendant
46
46
  #
47
47
  # @api private
@@ -10,7 +10,6 @@ module DataMapper
10
10
  end
11
11
 
12
12
  module Methods
13
- # TODO: document
14
13
  # @api public
15
14
  def before(target_method, *args, &block)
16
15
  remap_target_method(target_method).each do |target_method|
@@ -18,7 +17,6 @@ module DataMapper
18
17
  end
19
18
  end
20
19
 
21
- # TODO: document
22
20
  # @api public
23
21
  def after(target_method, *args, &block)
24
22
  remap_target_method(target_method).each do |target_method|
@@ -28,7 +26,6 @@ module DataMapper
28
26
 
29
27
  private
30
28
 
31
- # TODO: document
32
29
  # @api private
33
30
  def remap_target_method(target_method)
34
31
  case target_method
@@ -50,7 +50,10 @@ module DataMapper
50
50
  def property(name, type, options = {})
51
51
  property = DataMapper::Property.new(self, name, type, options)
52
52
 
53
- properties(repository_name) << property
53
+ repository_name = self.repository_name
54
+ properties = properties(repository_name)
55
+
56
+ properties << property
54
57
 
55
58
  # Add property to the other mappings as well if this is for the default
56
59
  # repository.
@@ -73,8 +76,6 @@ module DataMapper
73
76
  context = options.fetch(:lazy, :default)
74
77
  context = :default if context == true
75
78
 
76
- properties = properties(repository_name)
77
-
78
79
  Array(context).each do |context|
79
80
  properties.lazy_context(context) << self
80
81
  end
@@ -109,6 +110,8 @@ module DataMapper
109
110
  # new Relationship objects to a supplied repository and model. dup does not really
110
111
  # do what is needed
111
112
 
113
+ default_repository_name = self.default_repository_name
114
+
112
115
  @properties[repository_name] ||= if repository_name == default_repository_name
113
116
  PropertySet.new
114
117
  else
@@ -129,7 +132,6 @@ module DataMapper
129
132
  properties(repository_name).key
130
133
  end
131
134
 
132
- # TODO: document
133
135
  # @api public
134
136
  def serial(repository_name = default_repository_name)
135
137
  key(repository_name).detect { |property| property.serial? }
@@ -149,7 +151,6 @@ module DataMapper
149
151
  @field_naming_conventions[repository_name] ||= repository(repository_name).adapter.field_naming_convention
150
152
  end
151
153
 
152
- # TODO: document
153
154
  # @api private
154
155
  def properties_with_subclasses(repository_name = default_repository_name)
155
156
  properties = PropertySet.new
@@ -163,22 +164,19 @@ module DataMapper
163
164
  properties
164
165
  end
165
166
 
166
- # TODO: document
167
167
  # @api private
168
168
  def paranoid_properties
169
169
  @paranoid_properties
170
170
  end
171
171
 
172
- # TODO: document
173
172
  # @api private
174
173
  def set_paranoid_property(name, &block)
175
174
  paranoid_properties[name] = block
176
175
  end
177
176
 
178
- # TODO: document
179
177
  # @api private
180
178
  def key_conditions(repository, key)
181
- self.key(repository.name).zip(key).to_hash
179
+ self.key(repository.name).zip(key.nil? ? [] : key).to_hash
182
180
  end
183
181
 
184
182
  private
@@ -232,7 +230,6 @@ module DataMapper
232
230
  end
233
231
 
234
232
  chainable do
235
- # TODO: document
236
233
  # @api public
237
234
  def method_missing(method, *args, &block)
238
235
  if property = properties(repository_name)[method]