dm-core 0.10.0 → 0.10.1

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