dm-core 0.10.0 → 0.10.1

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 (39) hide show
  1. data/History.txt +25 -5
  2. data/Manifest.txt +1 -0
  3. data/README.txt +67 -23
  4. data/Rakefile +0 -2
  5. data/deps.rip +1 -1
  6. data/dm-core.gemspec +6 -6
  7. data/lib/dm-core/adapters/abstract_adapter.rb +3 -76
  8. data/lib/dm-core/adapters/data_objects_adapter.rb +8 -39
  9. data/lib/dm-core/associations/many_to_many.rb +28 -16
  10. data/lib/dm-core/associations/many_to_one.rb +1 -45
  11. data/lib/dm-core/associations/one_to_many.rb +1 -38
  12. data/lib/dm-core/associations/relationship.rb +43 -20
  13. data/lib/dm-core/collection.rb +33 -32
  14. data/lib/dm-core/model/property.rb +8 -8
  15. data/lib/dm-core/model/relationship.rb +10 -12
  16. data/lib/dm-core/property.rb +20 -85
  17. data/lib/dm-core/property_set.rb +8 -8
  18. data/lib/dm-core/query/conditions/comparison.rb +13 -71
  19. data/lib/dm-core/query/conditions/operation.rb +73 -47
  20. data/lib/dm-core/query/operator.rb +3 -45
  21. data/lib/dm-core/query/path.rb +5 -41
  22. data/lib/dm-core/query.rb +37 -108
  23. data/lib/dm-core/repository.rb +3 -79
  24. data/lib/dm-core/resource.rb +54 -49
  25. data/lib/dm-core/support/chainable.rb +0 -2
  26. data/lib/dm-core/support/equalizer.rb +23 -0
  27. data/lib/dm-core/types/object.rb +4 -4
  28. data/lib/dm-core/version.rb +1 -1
  29. data/lib/dm-core.rb +3 -11
  30. data/spec/public/model/relationship_spec.rb +4 -4
  31. data/spec/public/property_spec.rb +5 -449
  32. data/spec/public/sel_spec.rb +52 -2
  33. data/spec/public/shared/collection_shared_spec.rb +79 -26
  34. data/spec/public/shared/finder_shared_spec.rb +6 -6
  35. data/spec/public/shared/resource_shared_spec.rb +2 -2
  36. data/spec/semipublic/property_spec.rb +524 -9
  37. data/spec/semipublic/query_spec.rb +6 -6
  38. data/tasks/hoe.rb +2 -2
  39. metadata +24 -4
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
3
3
 
4
+ # instance methods
4
5
  describe DataMapper::Property do
5
6
 
6
7
  # define the model prior to supported_by
@@ -21,19 +22,8 @@ describe DataMapper::Property do
21
22
  property :md5hash, String, :key => true, :length => 32
22
23
  property :title, String, :nullable => false, :unique => true
23
24
  property :description, Text, :length => 1..1024, :lazy => [ :detail ]
24
-
25
25
  property :format, String, :default => 'jpeg'
26
- # WxH, stored as a dumped Ruby pair
27
- property :size, Object
28
- property :filesize, Float
29
- property :width, Integer
30
- property :quality, BigDecimal
31
-
32
- property :taken_on, Date
33
- property :taken_at, Time, :default => lambda { |resource, property| Time.now }
34
- property :retouched_at, DateTime
35
- property :type, Class
36
- property :visible, Boolean, :default => true
26
+ property :taken_at, Time, :default => proc { Time.now }
37
27
  end
38
28
  end
39
29
 
@@ -335,10 +325,9 @@ describe DataMapper::Property do
335
325
  it 'triggers lazy loading for given resource'
336
326
 
337
327
  it 'type casts given value' do
338
- # set it to a float
339
- @property.set(@image, 1.0)
340
- # get a string that has been typecasted
341
- @image.title.should == '1.0'
328
+ @property.set(@image, Addressable::URI.parse('http://test.example/'))
329
+ # get a string that has been typecasted using #to_str
330
+ @image.title.should == 'http://test.example/'
342
331
  end
343
332
 
344
333
  it 'stores original value' do
@@ -367,413 +356,6 @@ describe DataMapper::Property do
367
356
  end
368
357
  end
369
358
 
370
- describe '#typecast' do
371
- describe "when type is able to do typecasting on it's own" do
372
- it 'delegates all the work to the type'
373
- end
374
-
375
- describe 'when value is nil' do
376
- it 'returns value unchanged' do
377
- Image.properties[:size].typecast(nil).should be(nil)
378
- end
379
- end
380
-
381
- describe 'when value is a Ruby primitive' do
382
- it 'returns value unchanged' do
383
- Image.properties[:size].typecast([3200, 2400]).should == [3200, 2400]
384
- end
385
- end
386
-
387
- describe 'when type primitive is a String' do
388
- before :all do
389
- @property = Image.properties[:title]
390
- end
391
-
392
- it 'returns same value if a string' do
393
- @value = '1.0'
394
- @property.typecast(@value).should equal(@value)
395
- end
396
-
397
- it 'returns string representation of the new value' do
398
- @property.typecast(1.0).should eql('1.0')
399
- end
400
- end
401
-
402
- describe 'when type primitive is a Float' do
403
- before :all do
404
- @property = Image.properties[:filesize]
405
- end
406
-
407
- it 'returns same value if a float' do
408
- @value = 24.0
409
- @property.typecast(@value).should equal(@value)
410
- end
411
-
412
- it 'returns float representation of a zero string integer' do
413
- @property.typecast('0').should eql(0.0)
414
- end
415
-
416
- it 'returns float representation of a positive string integer' do
417
- @property.typecast('24').should eql(24.0)
418
- end
419
-
420
- it 'returns float representation of a negative string integer' do
421
- @property.typecast('-24').should eql(-24.0)
422
- end
423
-
424
- it 'returns float representation of a zero string float' do
425
- @property.typecast('0.0').should eql(0.0)
426
- end
427
-
428
- it 'returns float representation of a positive string float' do
429
- @property.typecast('24.35').should eql(24.35)
430
- end
431
-
432
- it 'returns float representation of a negative string float' do
433
- @property.typecast('-24.35').should eql(-24.35)
434
- end
435
-
436
- it 'returns float representation of a zero string float, with no leading digits' do
437
- @property.typecast('.0').should eql(0.0)
438
- end
439
-
440
- it 'returns float representation of a positive string float, with no leading digits' do
441
- @property.typecast('.41').should eql(0.41)
442
- end
443
-
444
- it 'returns float representation of a zero integer' do
445
- @property.typecast(0).should eql(0.0)
446
- end
447
-
448
- it 'returns float representation of a positive integer' do
449
- @property.typecast(24).should eql(24.0)
450
- end
451
-
452
- it 'returns float representation of a negative integer' do
453
- @property.typecast(-24).should eql(-24.0)
454
- end
455
-
456
- it 'returns float representation of a zero decimal' do
457
- @property.typecast(BigDecimal('0.0')).should eql(0.0)
458
- end
459
-
460
- it 'returns float representation of a positive decimal' do
461
- @property.typecast(BigDecimal('24.35')).should eql(24.35)
462
- end
463
-
464
- it 'returns float representation of a negative decimal' do
465
- @property.typecast(BigDecimal('-24.35')).should eql(-24.35)
466
- end
467
-
468
- [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
469
- it "does not typecast non-numeric value #{value.inspect}" do
470
- @property.typecast(value).should equal(value)
471
- end
472
- end
473
- end
474
-
475
- describe 'when type primitive is a Integer' do
476
- before :all do
477
- @property = Image.properties[:width]
478
- end
479
-
480
- it 'returns same value if an integer' do
481
- @value = 24
482
- @property.typecast(@value).should equal(@value)
483
- end
484
-
485
- it 'returns integer representation of a zero string integer' do
486
- @property.typecast('0').should eql(0)
487
- end
488
-
489
- it 'returns integer representation of a positive string integer' do
490
- @property.typecast('24').should eql(24)
491
- end
492
-
493
- it 'returns integer representation of a negative string integer' do
494
- @property.typecast('-24').should eql(-24)
495
- end
496
-
497
- it 'returns integer representation of a zero string float' do
498
- @property.typecast('0.0').should eql(0)
499
- end
500
-
501
- it 'returns integer representation of a positive string float' do
502
- @property.typecast('24.35').should eql(24)
503
- end
504
-
505
- it 'returns integer representation of a negative string float' do
506
- @property.typecast('-24.35').should eql(-24)
507
- end
508
-
509
- it 'returns integer representation of a zero string float, with no leading digits' do
510
- @property.typecast('.0').should eql(0)
511
- end
512
-
513
- it 'returns integer representation of a positive string float, with no leading digits' do
514
- @property.typecast('.41').should eql(0)
515
- end
516
-
517
- it 'returns integer representation of a zero float' do
518
- @property.typecast(0.0).should eql(0)
519
- end
520
-
521
- it 'returns integer representation of a positive float' do
522
- @property.typecast(24.35).should eql(24)
523
- end
524
-
525
- it 'returns integer representation of a negative float' do
526
- @property.typecast(-24.35).should eql(-24)
527
- end
528
-
529
- it 'returns integer representation of a zero decimal' do
530
- @property.typecast(BigDecimal('0.0')).should eql(0)
531
- end
532
-
533
- it 'returns integer representation of a positive decimal' do
534
- @property.typecast(BigDecimal('24.35')).should eql(24)
535
- end
536
-
537
- it 'returns integer representation of a negative decimal' do
538
- @property.typecast(BigDecimal('-24.35')).should eql(-24)
539
- end
540
-
541
- [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
542
- it "does not typecast non-numeric value #{value.inspect}" do
543
- @property.typecast(value).should equal(value)
544
- end
545
- end
546
- end
547
-
548
- describe 'when type primitive is a BigDecimal' do
549
- before :all do
550
- @property = Image.properties[:quality]
551
- end
552
-
553
- it 'returns same value if a decimal' do
554
- @value = BigDecimal('24.0')
555
- @property.typecast(@value).should equal(@value)
556
- end
557
-
558
- it 'returns decimal representation of a zero string integer' do
559
- @property.typecast('0').should eql(BigDecimal('0.0'))
560
- end
561
-
562
- it 'returns decimal representation of a positive string integer' do
563
- @property.typecast('24').should eql(BigDecimal('24.0'))
564
- end
565
-
566
- it 'returns decimal representation of a negative string integer' do
567
- @property.typecast('-24').should eql(BigDecimal('-24.0'))
568
- end
569
-
570
- it 'returns decimal representation of a zero string float' do
571
- @property.typecast('0.0').should eql(BigDecimal('0.0'))
572
- end
573
-
574
- it 'returns decimal representation of a positive string float' do
575
- @property.typecast('24.35').should eql(BigDecimal('24.35'))
576
- end
577
-
578
- it 'returns decimal representation of a negative string float' do
579
- @property.typecast('-24.35').should eql(BigDecimal('-24.35'))
580
- end
581
-
582
- it 'returns decimal representation of a zero string float, with no leading digits' do
583
- @property.typecast('.0').should eql(BigDecimal('0.0'))
584
- end
585
-
586
- it 'returns decimal representation of a positive string float, with no leading digits' do
587
- @property.typecast('.41').should eql(BigDecimal('0.41'))
588
- end
589
-
590
- it 'returns decimal representation of a zero integer' do
591
- @property.typecast(0).should eql(BigDecimal('0.0'))
592
- end
593
-
594
- it 'returns decimal representation of a positive integer' do
595
- @property.typecast(24).should eql(BigDecimal('24.0'))
596
- end
597
-
598
- it 'returns decimal representation of a negative integer' do
599
- @property.typecast(-24).should eql(BigDecimal('-24.0'))
600
- end
601
-
602
- it 'returns decimal representation of a zero float' do
603
- @property.typecast(0.0).should eql(BigDecimal('0.0'))
604
- end
605
-
606
- it 'returns decimal representation of a positive float' do
607
- @property.typecast(24.35).should eql(BigDecimal('24.35'))
608
- end
609
-
610
- it 'returns decimal representation of a negative float' do
611
- @property.typecast(-24.35).should eql(BigDecimal('-24.35'))
612
- end
613
-
614
- [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
615
- it "does not typecast non-numeric value #{value.inspect}" do
616
- @property.typecast(value).should equal(value)
617
- end
618
- end
619
- end
620
-
621
- describe 'when type primitive is a DateTime' do
622
- before do
623
- @property = Image.properties[:retouched_at]
624
- end
625
-
626
- describe 'and value given as a hash with keys like :year, :month, etc' do
627
- it 'builds a DateTime instance from hash values' do
628
- result = @property.typecast(
629
- 'year' => '2006',
630
- 'month' => '11',
631
- 'day' => '23',
632
- 'hour' => '12',
633
- 'min' => '0',
634
- 'sec' => '0'
635
- )
636
-
637
- result.should be_kind_of(DateTime)
638
- result.year.should eql(2006)
639
- result.month.should eql(11)
640
- result.day.should eql(23)
641
- result.hour.should eql(12)
642
- result.min.should eql(0)
643
- result.sec.should eql(0)
644
- end
645
- end
646
-
647
- describe 'and value is a string' do
648
- it 'parses the string' do
649
- Image.properties[:retouched_at].typecast('Dec, 2006').month.should == 12
650
- end
651
- end
652
-
653
- it 'does not typecast non-datetime values' do
654
- @property.typecast('not-datetime').should eql('not-datetime')
655
- end
656
- end
657
-
658
- describe 'when type primitive is a Date' do
659
- before do
660
- @property = Image.properties[:taken_on]
661
- end
662
-
663
- describe 'and value given as a hash with keys like :year, :month, etc' do
664
- it 'builds a Date instance from hash values' do
665
- result = @property.typecast(
666
- 'year' => '2007',
667
- 'month' => '3',
668
- 'day' => '25'
669
- )
670
-
671
- result.should be_kind_of(Date)
672
- result.year.should eql(2007)
673
- result.month.should eql(3)
674
- result.day.should eql(25)
675
- end
676
- end
677
-
678
- describe 'and value is a string' do
679
- it 'parses the string' do
680
- result = @property.typecast('Dec 20th, 2006')
681
- result.month.should == 12
682
- result.day.should == 20
683
- result.year.should == 2006
684
- end
685
- end
686
-
687
- it 'does not typecast non-date values' do
688
- @property.typecast('not-date').should eql('not-date')
689
- end
690
- end
691
-
692
- describe 'when type primitive is a Time' do
693
- before do
694
- @property = Image.properties[:taken_at]
695
- end
696
-
697
- describe 'and value given as a hash with keys like :year, :month, etc' do
698
- it 'builds a Time instance from hash values' do
699
- result = @property.typecast(
700
- 'year' => '2006',
701
- 'month' => '11',
702
- 'day' => '23',
703
- 'hour' => '12',
704
- 'min' => '0',
705
- 'sec' => '0'
706
- )
707
-
708
- result.should be_kind_of(Time)
709
- result.year.should eql(2006)
710
- result.month.should eql(11)
711
- result.day.should eql(23)
712
- result.hour.should eql(12)
713
- result.min.should eql(0)
714
- result.sec.should eql(0)
715
- end
716
- end
717
-
718
- describe 'and value is a string' do
719
- it 'parses the string' do
720
- result = @property.typecast('22:24')
721
- result.hour.should eql(22)
722
- result.min.should eql(24)
723
- end
724
- end
725
-
726
- it 'does not typecast non-time values' do
727
- pending 'Time#parse is too permissive' do
728
- @property.typecast('not-time').should eql('not-time')
729
- end
730
- end
731
- end
732
-
733
- describe 'when type primitive is a Class' do
734
- before do
735
- @property = Image.properties[:type]
736
- end
737
-
738
- it 'returns same value if a class' do
739
- @value = Image
740
- @property.typecast(@value).should equal(@value)
741
- end
742
-
743
- it 'returns the class if found' do
744
- @property.typecast('Image').should eql(Image)
745
- end
746
-
747
- it 'does not typecast non-class values' do
748
- @property.typecast('NoClass').should eql('NoClass')
749
- end
750
- end
751
-
752
- describe 'when type primitive is a Boolean' do
753
- before do
754
- @property = Image.properties[:visible]
755
- end
756
-
757
- [ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
758
- it "returns true when value is #{value.inspect}" do
759
- @property.typecast(value).should be_true
760
- end
761
- end
762
-
763
- [ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
764
- it "returns false when value is #{value.inspect}" do
765
- @property.typecast(value).should be_false
766
- end
767
- end
768
-
769
- [ 'string', 2, 1.0, BigDecimal('1.0'), DateTime.now, Time.now, Date.today, Class, Object.new, ].each do |value|
770
- it "does not typecast value #{value.inspect}" do
771
- @property.typecast(value).should equal(value)
772
- end
773
- end
774
- end
775
- end # #typecase
776
-
777
359
  describe '#unique?' do
778
360
  it 'is true for fields that explicitly given uniq index' do
779
361
  Track.properties[:musicbrainz_hash].unique?.should be_true
@@ -799,31 +381,5 @@ describe DataMapper::Property do
799
381
  Image.properties[:title].unique_index.should be_nil
800
382
  end
801
383
  end
802
-
803
- describe '#valid?' do
804
- describe 'when type primitive is a Boolean' do
805
- before do
806
- @property = Image.properties[:visible]
807
- end
808
-
809
- [ true, false ].each do |value|
810
- it "returns true when value is #{value.inspect}" do
811
- @property.valid?(value).should be_true
812
- end
813
- end
814
-
815
- [ 'true', 'TRUE', '1', 1, 't', 'T', 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
816
- it "returns false for #{value.inspect}" do
817
- @property.valid?(value).should be_false
818
- end
819
- end
820
- end
821
- end
822
-
823
- describe '#value' do
824
- it 'returns value for core types'
825
-
826
- it 'triggers dump operation for custom types'
827
- end
828
384
  end
829
385
  end # DataMapper::Property
@@ -15,8 +15,8 @@ describe 'SEL', 'with different key types' do
15
15
  class Article
16
16
  include DataMapper::Resource
17
17
 
18
- property :id, Serial
19
- property :title, String, :nullable => false
18
+ property :id, Serial
19
+ property :title, String, :nullable => false
20
20
 
21
21
  property :author_id, String # different type
22
22
 
@@ -42,3 +42,53 @@ describe 'SEL', 'with different key types' do
42
42
  end
43
43
  end
44
44
  end
45
+
46
+ describe 'SEL', 'with STI subclasses' do
47
+ before :all do
48
+ module ::Blog
49
+ class Author
50
+ include DataMapper::Resource
51
+
52
+ property :id, Serial
53
+ property :name, String
54
+
55
+ has n, :messages
56
+ end
57
+
58
+ class Message
59
+ include DataMapper::Resource
60
+
61
+ property :id, Serial
62
+ property :type, Discriminator
63
+ property :title, String, :nullable => false
64
+
65
+ belongs_to :author
66
+ end
67
+
68
+ class Article < Message; end
69
+ class Comment < Message; end
70
+ end
71
+
72
+ @author_model = Blog::Author
73
+ @message_model = Blog::Message
74
+ @article_model = Blog::Article
75
+ @comment_model = Blog::Comment
76
+ end
77
+
78
+ supported_by :all do
79
+ before :all do
80
+ author1 = @author_model.create(:name => 'Dan Kubb')
81
+ author2 = @author_model.create(:name => 'Sindre Aarsaether')
82
+
83
+ @article_model.create(:title => 'SEL', :author => author1)
84
+ @article_model.create(:title => 'STI', :author => author1)
85
+ @comment_model.create(:title => 'SEL and STI error', :author => author2)
86
+ end
87
+
88
+ it 'should allow STI loading of mixed relationships' do
89
+ lambda {
90
+ @message_model.all.each { |message| message.author }
91
+ }.should_not raise_error(ArgumentError)
92
+ end
93
+ end
94
+ end
@@ -618,24 +618,52 @@ share_examples_for 'A public Collection' do
618
618
  describe '#pop' do
619
619
  before :all do
620
620
  @new = @articles.create(:title => 'Sample Article') # TODO: freeze @new
621
-
622
- @return = @resource = @articles.pop
623
621
  end
624
622
 
625
- it 'should return a Resource' do
626
- @return.should be_kind_of(DataMapper::Resource)
627
- end
623
+ describe 'with no arguments' do
624
+ before :all do
625
+ @return = @articles.pop
626
+ end
628
627
 
629
- it 'should be the last Resource in the Collection' do
630
- @resource.should == @new
631
- end
628
+ it 'should return a Resource' do
629
+ @return.should be_kind_of(DataMapper::Resource)
630
+ end
631
+
632
+ it 'should be the last Resource in the Collection' do
633
+ @return.should == @new
634
+ end
632
635
 
633
- it 'should remove the Resource from the Collection' do
634
- @articles.should_not be_include(@resource)
636
+ it 'should remove the Resource from the Collection' do
637
+ @articles.should_not be_include(@new)
638
+ end
639
+
640
+ it 'should orphan the Resource' do
641
+ @return.collection.should_not equal(@articles)
642
+ end
635
643
  end
636
644
 
637
- it 'should orphan the Resource' do
638
- @resource.collection.should_not equal(@articles)
645
+ if RUBY_VERSION >= '1.8.7'
646
+ describe 'with a limit specified' do
647
+ before :all do
648
+ @return = @articles.pop(1)
649
+ end
650
+
651
+ it 'should return an Array' do
652
+ @return.should be_kind_of(Array)
653
+ end
654
+
655
+ it 'should return the expected Resources' do
656
+ @return.should == [ @new ]
657
+ end
658
+
659
+ it 'should remove the Resource from the Collection' do
660
+ @articles.should_not be_include(@new)
661
+ end
662
+
663
+ it 'should orphan the Resource' do
664
+ @return.each { |resource| resource.collection.should_not equal(@articles) }
665
+ end
666
+ end
639
667
  end
640
668
  end
641
669
 
@@ -941,26 +969,51 @@ share_examples_for 'A public Collection' do
941
969
  it { @articles.should respond_to(:shift) }
942
970
 
943
971
  describe '#shift' do
944
- before :all do
945
- @new = @articles.create(:title => 'Sample Article')
972
+ describe 'with no arguments' do
973
+ before :all do
974
+ @return = @articles.shift
975
+ end
946
976
 
947
- @return = @resource = @articles.shift
948
- end
977
+ it 'should return a Resource' do
978
+ @return.should be_kind_of(DataMapper::Resource)
979
+ end
949
980
 
950
- it 'should return a Resource' do
951
- @return.should be_kind_of(DataMapper::Resource)
952
- end
981
+ it 'should be the first Resource in the Collection' do
982
+ @return.key.should == @article.key
983
+ end
953
984
 
954
- it 'should be the first Resource in the Collection' do
955
- @resource.key.should == @article.key
956
- end
985
+ it 'should remove the Resource from the Collection' do
986
+ @articles.should_not be_include(@return)
987
+ end
957
988
 
958
- it 'should remove the Resource from the Collection' do
959
- @articles.should_not be_include(@resource)
989
+ it 'should orphan the Resource' do
990
+ @return.collection.should_not equal(@articles)
991
+ end
960
992
  end
961
993
 
962
- it 'should orphan the Resource' do
963
- @resource.collection.should_not equal(@articles)
994
+ if RUBY_VERSION >= '1.8.7'
995
+ describe 'with a limit specified' do
996
+ before :all do
997
+ @return = @articles.shift(1)
998
+ end
999
+
1000
+ it 'should return an Array' do
1001
+ @return.should be_kind_of(Array)
1002
+ end
1003
+
1004
+ it 'should return the expected Resources' do
1005
+ @return.size.should == 1
1006
+ @return.first.key.should == @article.key
1007
+ end
1008
+
1009
+ it 'should remove the Resource from the Collection' do
1010
+ @articles.should_not be_include(@article)
1011
+ end
1012
+
1013
+ it 'should orphan the Resource' do
1014
+ @return.each { |resource| resource.collection.should_not equal(@articles) }
1015
+ end
1016
+ end
964
1017
  end
965
1018
  end
966
1019