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
@@ -5,7 +5,7 @@ module DataMapper
5
5
  #
6
6
  # The Conditions module contains two types of class used for filtering
7
7
  # queries: Comparison and Operation. Although these are used on all
8
- # repositorie types -- not just SQL-based repos -- these classes are best
8
+ # repository types -- not just SQL-based repos -- these classes are best
9
9
  # thought of as being the DataMapper counterpart to an SQL WHERE clause.
10
10
  #
11
11
  # Comparisons compare properties and relationships with values, while
@@ -59,36 +59,17 @@ module DataMapper
59
59
  if klass = comparison_class(slug)
60
60
  klass.new(subject, value)
61
61
  else
62
- raise ArgumentError,
63
- "No Comparison class for `#{slug.inspect}' has been defined"
62
+ raise ArgumentError, "No Comparison class for #{slug.inspect} has been defined"
64
63
  end
65
64
  end
66
65
 
67
- # Returns the comparison class identified by the given slug
68
- #
69
- # @param [Symbol] slug
70
- # See slug parameter for Comparison.new
71
- #
72
- # @return [AbstractComparison, nil]
73
- #
74
- # @api private
75
- def self.comparison_class(slug)
76
- comparison_classes[slug] ||=
77
- AbstractComparison.descendants.detect do |comparison_class|
78
- comparison_class.slug == slug
79
- end
80
- end
81
-
82
66
  # Returns an array of all slugs registered with Comparison
83
67
  #
84
68
  # @return [Array<Symbol>]
85
69
  #
86
70
  # @api private
87
71
  def self.slugs
88
- @slugs ||=
89
- AbstractComparison.descendants.map do |comparison_class|
90
- comparison_class.slug
91
- end.freeze
72
+ AbstractComparison.descendants.map { |comparison_class| comparison_class.slug }
92
73
  end
93
74
 
94
75
  class << self
@@ -102,6 +83,18 @@ module DataMapper
102
83
  def comparison_classes
103
84
  @comparison_classes ||= {}
104
85
  end
86
+
87
+ # Returns the comparison class identified by the given slug
88
+ #
89
+ # @param [Symbol] slug
90
+ # See slug parameter for Comparison.new
91
+ #
92
+ # @return [AbstractComparison, nil]
93
+ #
94
+ # @api private
95
+ def comparison_class(slug)
96
+ comparison_classes[slug] ||= AbstractComparison.descendants.detect { |comparison_class| comparison_class.slug == slug }
97
+ end
105
98
  end
106
99
  end # class Comparison
107
100
 
@@ -114,6 +107,9 @@ module DataMapper
114
107
 
115
108
  equalize :slug, :subject, :value
116
109
 
110
+ # @api semipublic
111
+ attr_accessor :parent
112
+
117
113
  # The property or relationship which is being matched against
118
114
  #
119
115
  # @return [Property, Associations::Relationship]
@@ -133,7 +129,9 @@ module DataMapper
133
129
  # @return [Object]
134
130
  #
135
131
  # @api semipublic
136
- attr_reader :value
132
+ def value
133
+ dumped_value
134
+ end
137
135
 
138
136
  # The loaded/typecast value
139
137
  #
@@ -201,6 +199,18 @@ module DataMapper
201
199
  self.class.slug
202
200
  end
203
201
 
202
+ # Test that the record value matches the comparison
203
+ #
204
+ # @param [Resource, Hash] record
205
+ # The record containing the value to be matched
206
+ #
207
+ # @return [Boolean]
208
+ #
209
+ # @api semipublic
210
+ def matches?(record)
211
+ match_property?(record)
212
+ end
213
+
204
214
  # Tests that the Comparison is valid
205
215
  #
206
216
  # Subclasses can overload this to customise the means by which they
@@ -215,11 +225,7 @@ module DataMapper
215
225
  #
216
226
  # @api semipublic
217
227
  def valid?
218
- # This needs to be deferred until the last moment because the value
219
- # could be a reference to a Resource, that when the comparison was
220
- # created was invalid, but has since been saved and has it's key
221
- # set.
222
- subject.valid?(loaded_value)
228
+ valid_for_subject?(loaded_value)
223
229
  end
224
230
 
225
231
  # Returns whether the subject is a Relationship
@@ -247,7 +253,7 @@ module DataMapper
247
253
  # @api semipublic
248
254
  def inspect
249
255
  "#<#{self.class} @subject=#{@subject.inspect} " \
250
- "@value=#{@value.inspect} @loaded_value=#{@loaded_value.inspect}>"
256
+ "@dumped_value=#{@dumped_value.inspect} @loaded_value=#{@loaded_value.inspect}>"
251
257
  end
252
258
 
253
259
  # Returns a string version of this Comparison object
@@ -260,17 +266,19 @@ module DataMapper
260
266
  #
261
267
  # @api semipublic
262
268
  def to_s
263
- "#{@subject} #{comparator_string} #{@value}"
269
+ "#{subject.name} #{comparator_string} #{dumped_value.inspect}"
264
270
  end
265
271
 
266
- private # ============================================================
272
+ # @api private
273
+ def negated?
274
+ parent = self.parent
275
+ parent ? parent.negated? : false
276
+ end
267
277
 
268
- # Holds the actual value of the given property or relationship
269
- #
270
- # @return [Object]
271
- #
272
- # @api semipublic
273
- attr_reader :expected
278
+ private
279
+
280
+ # @api private
281
+ attr_reader :dumped_value
274
282
 
275
283
  # Creates a new AbstractComparison instance with +subject+ and +value+
276
284
  #
@@ -283,17 +291,13 @@ module DataMapper
283
291
  # @api semipublic
284
292
  def initialize(subject, value)
285
293
  @subject = subject
286
- @loaded_value = typecast_value(value)
287
- @value = dumped_value(@loaded_value)
288
- @expected = expected_value
294
+ @loaded_value = typecast(value)
295
+ @dumped_value = dump
289
296
  end
290
297
 
291
- # Used by Ruby when creating a copy of the comparison
292
- #
293
298
  # @api private
294
- def initialize_copy(*)
295
- @value = @value.dup
296
- @loaded_value = @loaded_value.dup
299
+ def match_property?(record, operator = :===)
300
+ expected.send(operator, record_value(record))
297
301
  end
298
302
 
299
303
  # Typecasts the given +val+ using subject#typecast
@@ -310,34 +314,33 @@ module DataMapper
310
314
  # @see Property#typecast
311
315
  #
312
316
  # @api private
313
- def typecast_value(val)
314
- if subject.respond_to?(:typecast)
315
- subject.typecast(val)
316
- else
317
- val
318
- end
317
+ def typecast(value)
318
+ typecast_property(value)
319
319
  end
320
320
 
321
- # Dumps the given +val+ using subject#value
321
+ # @api private
322
+ def typecast_property(value)
323
+ subject.typecast(value)
324
+ end
325
+
326
+ # Dumps the given loaded_value using subject#value
322
327
  #
323
328
  # This converts property values to the primitive as stored in the
324
329
  # repository.
325
330
  #
326
- # @param [Object] val
327
- # The object to attempt to typecast.
328
- #
329
331
  # @return [Object]
330
332
  # The raw (dumped) object.
331
333
  #
332
334
  # @see Property#value
333
335
  #
334
336
  # @api private
335
- def dumped_value(val)
336
- if subject.respond_to?(:value)
337
- subject.value(val)
338
- else
339
- val
340
- end
337
+ def dump
338
+ dump_property(loaded_value)
339
+ end
340
+
341
+ # @api private
342
+ def dump_property(value)
343
+ subject.value(value)
341
344
  end
342
345
 
343
346
  # Returns a value for the comparison +subject+
@@ -357,7 +360,8 @@ module DataMapper
357
360
  # @return [Object]
358
361
  #
359
362
  # @api semipublic
360
- def record_value(record, subject = @subject, key_type = :source_key)
363
+ def record_value(record, key_type = :source_key)
364
+ subject = self.subject
361
365
  case record
362
366
  when Hash
363
367
  record_value_from_hash(record, subject, key_type)
@@ -413,28 +417,23 @@ module DataMapper
413
417
  # @return [Object]
414
418
  #
415
419
  # @api semipublic
416
- def expected_value(val = @loaded_value)
417
- expected_value = record_value(val, @subject, :target_key)
420
+ def expected(value = @loaded_value)
421
+ expected = record_value(value, :target_key)
418
422
 
419
423
  if @subject.respond_to?(:source_key)
420
- @subject.source_key.typecast(expected_value)
424
+ @subject.source_key.typecast(expected)
421
425
  else
422
- expected_value
426
+ expected
423
427
  end
424
428
  end
425
429
 
426
- # Returns the name of this comparison
427
- #
428
- # @return [String]
429
- # The name of the comparison class minus the trailing "Comparison".
430
+ # Test the value to see if it is valid
430
431
  #
431
- # @example
432
- # Comparison.new(:eql, ...).comparator_string
433
- # # => Equal
432
+ # @return [Boolean] true if the value is valid
434
433
  #
435
- # @api private
436
- def comparator_string
437
- self.class.name.chomp('Comparison')
434
+ # @api semipublic
435
+ def valid_for_subject?(loaded_value)
436
+ subject.valid?(loaded_value, negated?)
438
437
  end
439
438
  end # class AbstractComparison
440
439
 
@@ -450,38 +449,113 @@ module DataMapper
450
449
  subject.kind_of?(Associations::Relationship)
451
450
  end
452
451
 
452
+ # Tests that the record value matches the comparison
453
+ #
454
+ # @param [Resource, Hash] record
455
+ # The record containing the value to be matched
456
+ #
457
+ # @return [Boolean]
458
+ #
459
+ # @api semipublic
460
+ def matches?(record)
461
+ if relationship? && expected.respond_to?(:query)
462
+ match_relationship?(record)
463
+ else
464
+ super
465
+ end
466
+ end
467
+
453
468
  # Returns the conditions required to match the subject relationship
454
469
  #
455
470
  # @return [Hash]
456
471
  #
457
472
  # @api semipublic
458
473
  def foreign_key_mapping
459
- relationship = subject.inverse
474
+ inverse = subject.inverse
475
+ Query.target_conditions(value, inverse.source_key, inverse.target_key)
476
+ end
477
+
478
+ private
479
+
480
+ # @api private
481
+ def match_relationship?(record)
482
+ expected.query.conditions.matches?(record_value(record))
483
+ end
484
+
485
+ # Typecasts each value in the inclusion set
486
+ #
487
+ # @return [Array<Object>]
488
+ #
489
+ # @see AbtractComparison#typecast
490
+ #
491
+ # @api private
492
+ def typecast(value)
493
+ if relationship?
494
+ typecast_relationship(value)
495
+ else
496
+ super
497
+ end
498
+ end
460
499
 
461
- Query.target_conditions(value, relationship.source_key, relationship.target_key)
500
+ # @api private
501
+ def dump
502
+ if relationship?
503
+ dump_relationship(loaded_value)
504
+ else
505
+ super
506
+ end
507
+ end
508
+
509
+ # @api private
510
+ def dump_relationship(value)
511
+ value
462
512
  end
463
513
  end # module RelationshipHandler
464
514
 
465
- # Tests whether the value in the record is equal to the expected_value
515
+ # Tests whether the value in the record is equal to the expected
466
516
  # set for the Comparison.
467
517
  class EqualToComparison < AbstractComparison
468
518
  include RelationshipHandler
469
519
 
470
520
  slug :eql
471
521
 
472
- # Asserts that the record value matches the comparison
522
+ # Tests that the record value matches the comparison
473
523
  #
474
524
  # @param [Resource, Hash] record
475
525
  # The record containing the value to be matched
476
526
  #
477
527
  # @return [Boolean]
528
+ #
478
529
  # @api semipublic
479
530
  def matches?(record)
480
- record_value(record) == expected
531
+ if expected.nil?
532
+ record_value(record).nil?
533
+ else
534
+ super
535
+ end
481
536
  end
482
537
 
483
538
  private
484
539
 
540
+ # @api private
541
+ def typecast_relationship(value)
542
+ case value
543
+ when Hash then typecast_hash(value)
544
+ when Resource then typecast_resource(value)
545
+ end
546
+ end
547
+
548
+ # @api private
549
+ def typecast_hash(hash)
550
+ subject = self.subject
551
+ subject.target_model.new(subject.query.merge(hash))
552
+ end
553
+
554
+ # @api private
555
+ def typecast_resource(resource)
556
+ resource
557
+ end
558
+
485
559
  # @return [String]
486
560
  #
487
561
  # @see AbstractComparison#to_s
@@ -493,26 +567,13 @@ module DataMapper
493
567
  end # class EqualToComparison
494
568
 
495
569
  # Tests whether the value in the record is contained in the
496
- # expected_value set for the Comparison, where expected_value is an
570
+ # expected set for the Comparison, where expected is an
497
571
  # Array, Range, or Set.
498
572
  class InclusionComparison < AbstractComparison
499
573
  include RelationshipHandler
500
574
 
501
575
  slug :in
502
576
 
503
- # Asserts that the record value matches the comparison
504
- #
505
- # @param [Resource, Hash] record
506
- # The record containing the value to be matched
507
- #
508
- # @return [Boolean]
509
- #
510
- # @api semipublic
511
- def matches?(record)
512
- record_value = record_value(record)
513
- !record_value.nil? && expected.include?(record_value)
514
- end
515
-
516
577
  # Checks that the Comparison is valid
517
578
  #
518
579
  # @see DataMapper::Query::Conditions::AbstractComparison#valid?
@@ -521,11 +582,11 @@ module DataMapper
521
582
  #
522
583
  # @api semipublic
523
584
  def valid?
524
- case value
525
- when Array, Set
526
- loaded_value.any? && loaded_value.all? { |val| subject.valid?(val) }
527
- when Range
528
- loaded_value.any? && subject.valid?(loaded_value.first) && subject.valid?(loaded_value.last)
585
+ loaded_value = self.loaded_value
586
+ case loaded_value
587
+ when Collection then valid_collection?(loaded_value)
588
+ when Range then valid_range?(loaded_value)
589
+ when Enumerable then valid_enumerable?(loaded_value)
529
590
  else
530
591
  false
531
592
  end
@@ -533,57 +594,115 @@ module DataMapper
533
594
 
534
595
  private
535
596
 
536
- # Overloads AbtractComparison#expected_value
597
+ # @api private
598
+ def match_property?(record)
599
+ super(record, :include?)
600
+ end
601
+
602
+ # Overloads AbtractComparison#expected
537
603
  #
538
604
  # @return [Array<Object>]
539
- # @see AbtractComparison#expected_value
605
+ # @see AbtractComparison#expected
540
606
  #
541
607
  # @api private
542
- def expected_value
543
- if loaded_value.is_a?(Range)
544
- Range.new(super(loaded_value.first), super(loaded_value.last), loaded_value.exclude_end?)
545
- else
608
+ def expected
609
+ loaded_value = self.loaded_value
610
+ if loaded_value.kind_of?(Range)
611
+ typecast_range(loaded_value)
612
+ elsif loaded_value.respond_to?(:map)
613
+ # FIXME: causes a lazy load when a Collection
546
614
  loaded_value.map { |val| super(val) }
615
+ else
616
+ super
547
617
  end
548
618
  end
549
619
 
550
- # Typecasts each value in the inclusion set
551
- #
552
- # @return [Array<Object>]
553
- #
554
- # @see AbtractComparison#typecast_value
555
- #
556
620
  # @api private
557
- def typecast_value(val)
558
- if subject.respond_to?(:typecast) && val.is_a?(Range)
559
- if subject.primitive?(val.first)
560
- # If the range type matches, nothing to do
561
- val
621
+ def valid_collection?(collection)
622
+ valid_for_subject?(collection)
623
+ end
624
+
625
+ # @api private
626
+ def valid_range?(range)
627
+ (!range.empty? || negated?) && valid_for_subject?(range.first) && valid_for_subject?(range.last)
628
+ end
629
+
630
+ # @api private
631
+ def valid_enumerable?(enumerable)
632
+ (!enumerable.empty? || negated?) && enumerable.all? { |entry| valid_for_subject?(entry) }
633
+ end
634
+
635
+ # @api private
636
+ def typecast_property(value)
637
+ if value.kind_of?(Range)
638
+ typecast_range(value)
639
+ elsif value.respond_to?(:map) && !value.kind_of?(String)
640
+ value.map { |entry| super(entry) }
641
+ else
642
+ super
643
+ end
644
+ end
645
+
646
+ # @api private
647
+ def typecast_range(range)
648
+ range.class.new(typecast_property(range.first), typecast_property(range.last), range.exclude_end?)
649
+ end
650
+
651
+ # @api private
652
+ def typecast_relationship(value)
653
+ case value
654
+ when Hash then typecast_hash(value)
655
+ when Resource then typecast_resource(value)
656
+ when Collection then typecast_collection(value)
657
+ when Enumerable then typecast_enumerable(value)
658
+ end
659
+ end
660
+
661
+ # @api private
662
+ def typecast_hash(hash)
663
+ subject = self.subject
664
+ subject.target_model.all(subject.query.merge(hash))
665
+ end
666
+
667
+ # @api private
668
+ def typecast_resource(resource)
669
+ resource.collection_for_self
670
+ end
671
+
672
+ # @api private
673
+ def typecast_collection(collection)
674
+ collection
675
+ end
676
+
677
+ # @api private
678
+ def typecast_enumerable(enumerable)
679
+ collection = nil
680
+ enumerable.each do |entry|
681
+ typecasted = typecast_relationship(entry)
682
+ if collection
683
+ collection |= typecasted
562
684
  else
563
- # Create a new range with the new type
564
- Range.new(subject.typecast(val.first), subject.typecast(val.last), val.exclude_end?)
685
+ collection = typecasted
565
686
  end
566
- elsif subject.respond_to?(:typecast) && val.respond_to?(:map)
567
- val.map { |el| subject.typecast(el) }
568
- else
569
- val
570
687
  end
688
+ collection
571
689
  end
572
690
 
573
691
  # Dumps the given +val+ using subject#value
574
692
  #
575
693
  # @return [Array<Object>]
576
694
  #
577
- # @see AbtractComparison#dumped_value
695
+ # @see AbtractComparison#dump
578
696
  #
579
697
  # @api private
580
- def dumped_value(val)
581
- if subject.respond_to?(:value) && val.is_a?(Range) && !subject.custom?
582
- val
583
- elsif subject.respond_to?(:value) && val.respond_to?(:map)
584
- val.map { |el| subject.value(el) }
698
+ def dump
699
+ loaded_value = self.loaded_value
700
+ if subject.respond_to?(:value) && loaded_value.respond_to?(:map) && !loaded_value.kind_of?(Range)
701
+ dumped_value = loaded_value.map { |value| dump_property(value) }
702
+ dumped_value.uniq!
703
+ dumped_value
585
704
  else
586
- val
705
+ super
587
706
  end
588
707
  end
589
708
 
@@ -597,31 +716,18 @@ module DataMapper
597
716
  end
598
717
  end # class InclusionComparison
599
718
 
600
- # Tests whether the value in the record matches the expected_value
719
+ # Tests whether the value in the record matches the expected
601
720
  # regexp set for the Comparison.
602
721
  class RegexpComparison < AbstractComparison
603
722
  slug :regexp
604
723
 
605
- # Asserts that the record value matches the comparison
606
- #
607
- # @param [Resource, Hash] record
608
- # The record containing the value to be matched
609
- #
610
- # @return [Boolean]
611
- #
612
- # @api semipublic
613
- def matches?(record)
614
- record_value = record_value(record)
615
- !record_value.nil? && record_value =~ expected
616
- end
617
-
618
724
  # Checks that the Comparison is valid
619
725
  #
620
726
  # @see AbstractComparison#valid?
621
727
  #
622
728
  # @api semipublic
623
729
  def valid?
624
- value.kind_of?(Regexp)
730
+ loaded_value.kind_of?(Regexp)
625
731
  end
626
732
 
627
733
  private
@@ -631,8 +737,8 @@ module DataMapper
631
737
  # @return [Object]
632
738
  #
633
739
  # @api private
634
- def typecast_value(val)
635
- val
740
+ def typecast(value)
741
+ value
636
742
  end
637
743
 
638
744
  # @return [String]
@@ -645,40 +751,27 @@ module DataMapper
645
751
  end
646
752
  end # class RegexpComparison
647
753
 
648
- # Tests whether the value in the record is like the expected_value set
754
+ # Tests whether the value in the record is like the expected set
649
755
  # for the Comparison. Equivalent to a LIKE clause in an SQL database.
650
756
  #
651
757
  # TODO: move this to dm-more with DataObjectsAdapter plugins
652
758
  class LikeComparison < AbstractComparison
653
759
  slug :like
654
760
 
655
- # Asserts that the record value matches the comparison
656
- #
657
- # @param [Resource, Hash] record
658
- # The record containing the value to be matched
659
- #
660
- # @return [Boolean]
661
- #
662
- # @api semipublic
663
- def matches?(record)
664
- record_value = record_value(record)
665
- !record_value.nil? && record_value =~ expected
666
- end
667
-
668
761
  private
669
762
 
670
- # Overloads the +expected_value+ method in AbstractComparison
763
+ # Overloads the +expected+ method in AbstractComparison
671
764
  #
672
765
  # Return a regular expression suitable for matching against the
673
766
  # records value.
674
767
  #
675
768
  # @return [Regexp]
676
769
  #
677
- # @see AbtractComparison#expected_value
770
+ # @see AbtractComparison#expected
678
771
  #
679
772
  # @api semipublic
680
- def expected_value
681
- Regexp.new(@value.to_s.gsub('%', '.*').gsub('_', '.'))
773
+ def expected
774
+ Regexp.new('\A' << super.gsub('%', '.*').tr('_', '.') << '\z')
682
775
  end
683
776
 
684
777
  # @return [String]
@@ -692,11 +785,11 @@ module DataMapper
692
785
  end # class LikeComparison
693
786
 
694
787
  # Tests whether the value in the record is greater than the
695
- # expected_value set for the Comparison.
788
+ # expected set for the Comparison.
696
789
  class GreaterThanComparison < AbstractComparison
697
790
  slug :gt
698
791
 
699
- # Asserts that the record value matches the comparison
792
+ # Tests that the record value matches the comparison
700
793
  #
701
794
  # @param [Resource, Hash] record
702
795
  # The record containing the value to be matched
@@ -721,12 +814,12 @@ module DataMapper
721
814
  end
722
815
  end # class GreaterThanComparison
723
816
 
724
- # Tests whether the value in the record is less than the expected_value
817
+ # Tests whether the value in the record is less than the expected
725
818
  # set for the Comparison.
726
819
  class LessThanComparison < AbstractComparison
727
820
  slug :lt
728
821
 
729
- # Asserts that the record value matches the comparison
822
+ # Tests that the record value matches the comparison
730
823
  #
731
824
  # @param [Resource, Hash] record
732
825
  # The record containing the value to be matched
@@ -752,11 +845,11 @@ module DataMapper
752
845
  end # class LessThanComparison
753
846
 
754
847
  # Tests whether the value in the record is greater than, or equal to,
755
- # the expected_value set for the Comparison.
848
+ # the expected set for the Comparison.
756
849
  class GreaterThanOrEqualToComparison < AbstractComparison
757
850
  slug :gte
758
851
 
759
- # Asserts that the record value matches the comparison
852
+ # Tests that the record value matches the comparison
760
853
  #
761
854
  # @param [Resource, Hash] record
762
855
  # The record containing the value to be matched
@@ -780,11 +873,11 @@ module DataMapper
780
873
  end # class GreaterThanOrEqualToComparison
781
874
 
782
875
  # Tests whether the value in the record is less than, or equal to, the
783
- # expected_value set for the Comparison.
876
+ # expected set for the Comparison.
784
877
  class LessThanOrEqualToComparison < AbstractComparison
785
878
  slug :lte
786
879
 
787
- # Asserts that the record value matches the comparison
880
+ # Tests that the record value matches the comparison
788
881
  #
789
882
  # @param [Resource, Hash] record
790
883
  # The record containing the value to be matched