public
Rubygem
Description: DataMapper - Core
Homepage: http://datamapper.org
Clone URL: git://github.com/sam/dm-core.git
Added Collection#slice

* Simplified how values are matched up with the properties within
  Collection#load
Dan Kubb (author)
Sun Jun 08 04:19:34 -0700 2008
commit  8af63340e7473f9545e5026996a6e623bc5c59ee
tree    a1c68fd513028c8f2ba8193a44ad2bd0bb19fc00
parent  5f9e56739ff520fea01e0b71bdb08a4d4dd9cd29
...
7
8
9
 
 
10
11
12
...
43
44
45
46
47
 
 
48
49
50
...
118
119
120
121
122
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125
126
...
176
177
178
179
180
 
 
181
182
183
184
185
186
 
187
188
189
190
 
 
191
192
193
...
7
8
9
10
11
12
13
14
...
45
46
47
 
 
48
49
50
51
52
...
120
121
122
 
 
 
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
194
195
196
 
 
197
198
199
200
201
202
203
 
204
205
206
 
 
207
208
209
210
211
0
@@ -7,6 +7,8 @@ module DataMapper
0
     end
0
 
0
     def load(values)
0
+      raise "Expected #{@properties.size} attributes, got #{values.size}" if @properties.size != values.size
0
+
0
       model = if @inheritance_property_index
0
         values.at(@inheritance_property_index) || query.model
0
       else
0
@@ -43,8 +45,8 @@ module DataMapper
0
         add(resource)
0
       end
0
 
0
-      @properties_with_indexes.each_pair do |property, i|
0
-        resource.instance_variable_set(property.instance_variable_name, values.at(i))
0
+      @properties.zip(values).each do |property,value|
0
+        resource.instance_variable_set(property.instance_variable_name, value)
0
       end
0
 
0
       resource
0
@@ -118,9 +120,25 @@ module DataMapper
0
       first(:offset => offset)
0
     end
0
 
0
-    # TODO: add at()
0
-    # TODO: add slice()
0
-    # TODO: alias [] to slice()
0
+    def slice(*args)
0
+      raise ArgumentError, "must be 1 or 2 arguments, was #{args.size}" if args.size == 0 || args.size > 2
0
+
0
+      return at(args.first) if args.size == 1 && Integer === args.first
0
+
0
+      if args.size == 2 && Integer === args.first && Integer === args.last
0
+        offset, limit = args
0
+      elsif args.size == 1 && Range === args.first
0
+        range  = args.first
0
+        offset = range.first
0
+        limit  = range.exclude_end? ? range.last - range.first : range.last + 1 - range.first
0
+      else
0
+        raise ArgumentError, "arguments may be 1 or 2 Integer, or 1 Range object, was: #{args.inspect}"
0
+      end
0
+
0
+      all(:offset => offset, :limit => limit)
0
+    end
0
+
0
+    alias [] slice
0
 
0
     # TODO: add <<
0
     # TODO: add push()
0
@@ -176,18 +194,18 @@ module DataMapper
0
     def initialize(query, &loader)
0
       raise ArgumentError, "+query+ must be a DataMapper::Query, but was #{query.class}", caller unless query.kind_of?(Query)
0
 
0
-      @query                   = query
0
-      @properties_with_indexes = Hash[*query.fields.zip((0...query.fields.length).to_a).flatten]
0
+      @query      = query
0
+      @properties = query.fields
0
 
0
       super()
0
       load_with(&loader)
0
 
0
       if inheritance_property = query.model.inheritance_property(repository.name)
0
-        @inheritance_property_index = @properties_with_indexes[inheritance_property]
0
+        @inheritance_property_index = @properties.index(inheritance_property)
0
       end
0
 
0
-      if (@key_properties = query.model.key(repository.name)).all? { |key| @properties_with_indexes.include?(key) }
0
-        @key_property_indexes = @properties_with_indexes.values_at(*@key_properties)
0
+      if (@key_properties = query.model.key(repository.name)).all? { |property| @properties.include?(property) }
0
+        @key_property_indexes = @key_properties.map { |property| @properties.index(property) }
0
       end
0
     end
0
 
...
344
345
346
347
348
 
 
349
350
351
...
344
345
346
 
 
347
348
349
350
351
0
@@ -344,8 +344,8 @@ module DataMapper
0
     # validate other DM::Query or Hash object
0
     def assert_valid_other(other)
0
       if self.class === other
0
-        raise ArgumentError, "+other+ #{self.class} must be for the #{repository.name} repository" unless other.repository == repository
0
-        raise ArgumentError, "+other+ #{self.class} must be for the #{model.name} model"           unless other.model      == model
0
+        raise ArgumentError, "+other+ #{self.class} must be for the #{repository.name} repository, not #{other.repository.name}" unless other.repository == repository
0
+        raise ArgumentError, "+other+ #{self.class} must be for the #{model.name} model, not #{other.model.name}"                unless other.model      == model
0
       elsif !(Hash === other)
0
         raise ArgumentError, "+other+ must be a #{self.class} or Hash, but was a #{other.class}"
0
       end
...
167
168
169
170
171
172
173
174
175
176
...
189
190
191
 
 
 
 
 
 
 
 
 
 
 
192
193
194
...
167
168
169
 
 
 
 
170
171
172
...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
0
@@ -167,10 +167,6 @@ module DataMapper
0
       DataMapper::Transaction.new(self)
0
     end
0
 
0
-    def to_s
0
-      "#<DataMapper::Repository:#{@name}>"
0
-    end
0
-
0
     def map(*args)
0
       type_map.map(*args)
0
     end
0
@@ -189,6 +185,17 @@ module DataMapper
0
     # TODO: remove this alias
0
     alias exists? storage_exists?
0
 
0
+    def eql?(other)
0
+      return true if super
0
+      name == other.name
0
+    end
0
+
0
+    alias == eql?
0
+
0
+    def to_s
0
+      "#<DataMapper::Repository:#{@name}>"
0
+    end
0
+
0
     private
0
 
0
     attr_reader :identity_maps
...
31
32
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
35
36
...
158
159
160
161
162
163
164
165
166
167
168
169
170
 
 
171
172
173
174
175
176
177
178
179
180
181
 
 
 
 
 
 
 
182
183
184
...
282
283
284
285
286
287
288
289
 
 
 
 
290
291
292
...
296
297
298
299
300
301
302
303
 
 
 
 
304
305
306
...
430
431
432
433
434
435
 
 
 
 
436
437
 
 
438
439
440
441
442
 
443
444
445
...
457
458
459
460
461
462
463
464
 
 
 
 
465
466
467
...
612
613
614
615
616
617
618
 
 
 
619
620
621
...
624
625
626
627
628
629
630
631
 
 
 
 
 
 
 
 
632
633
634
635
636
637
638
639
640
641
642
643
 
 
 
 
 
 
 
 
644
645
646
647
648
649
650
651
652
653
654
655
656
657
 
 
 
 
 
 
 
 
658
659
660
...
669
670
671
672
673
674
675
676
677
678
 
679
680
681
682
683
684
685
686
687
688
689
690
691
692
 
 
693
694
695
...
722
723
724
725
726
 
727
728
729
730
731
732
733
734
735
736
 
 
 
 
737
738
 
 
 
739
740
741
...
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
...
178
179
180
 
 
 
 
 
 
 
 
 
 
181
182
183
184
185
186
187
 
 
 
 
 
 
188
189
190
191
192
193
194
195
196
197
...
295
296
297
 
 
 
 
 
298
299
300
301
302
303
304
...
308
309
310
 
 
 
 
 
311
312
313
314
315
316
317
...
441
442
443
 
 
 
444
445
446
447
448
 
449
450
451
452
453
454
 
455
456
457
458
...
470
471
472
 
 
 
 
 
473
474
475
476
477
478
479
...
624
625
626
 
 
 
 
627
628
629
630
631
632
...
635
636
637
 
 
 
 
 
638
639
640
641
642
643
644
645
646
 
 
 
 
 
647
 
 
 
 
 
648
649
650
651
652
653
654
655
656
 
657
 
 
 
 
 
 
 
 
 
 
 
658
659
660
661
662
663
664
665
666
667
668
...
677
678
679
 
 
680
681
682
683
 
684
685
686
687
688
689
 
 
 
690
691
692
693
 
 
694
695
696
697
698
...
725
726
727
 
 
728
729
730
731
732
733
734
 
 
 
 
735
736
737
738
739
 
740
741
742
743
744
745
0
@@ -31,6 +31,26 @@ if ADAPTER
0
     belongs_to :zebra
0
   end
0
 
0
+  class CollectionSpecParty
0
+    include DataMapper::Resource
0
+
0
+    def self.default_repository_name
0
+      ADAPTER
0
+    end
0
+
0
+    property :name, String, :key => true
0
+    property :type, Discriminator
0
+  end
0
+
0
+  class CollectionSpecUser < CollectionSpecParty
0
+    def self.default_repository_name
0
+      ADAPTER
0
+    end
0
+
0
+    property :username, String
0
+    property :password, String
0
+  end
0
+
0
   module CollectionSpecHelper
0
     def setup
0
       Zebra.auto_migrate!(ADAPTER)
0
@@ -158,27 +178,20 @@ if ADAPTER
0
 
0
       describe 'with inheritance property' do
0
         before do
0
-          class CollectionSpecParty
0
-            include DataMapper::Resource
0
-            property :name, String, :key => true
0
-            property :type, Discriminator
0
-          end
0
-
0
-          class CollectionSpecUser < CollectionSpecParty
0
-            property :username, String
0
-            property :password, String
0
-          end
0
+          CollectionSpecUser.auto_migrate!
0
+          CollectionSpecUser.create(:name => 'Dan')
0
 
0
           properties = CollectionSpecParty.properties(:default)
0
         end
0
 
0
         it 'should instantiate resources using the inheritance property class' do
0
-          @collection = DataMapper::Collection.new(DataMapper::Query.new(@repository, CollectionSpecParty))
0
-          @collection.load([ 'Dan', CollectionSpecUser ])
0
-          @collection.length.should == 1
0
-          resource = @collection[0]
0
-          resource.class.should == CollectionSpecUser
0
-          resource
0
+          pending 'length does not return 1, yet slice() can return a resource' do
0
+            @collection = DataMapper::Collection.new(DataMapper::Query.new(@repository, CollectionSpecParty))
0
+            @collection.length.should == 1
0
+            resource = @collection[0]
0
+            resource.class.should == CollectionSpecUser
0
+            resource
0
+          end
0
         end
0
       end
0
     end
0
@@ -282,11 +295,10 @@ if ADAPTER
0
 
0
         describe '#delete' do
0
           it 'should reset the resource.collection' do
0
-            @nancy = @collection[0]
0
-
0
-            @nancy.collection.object_id.should == @collection.object_id
0
-            @collection.delete(@nancy)
0
-            @nancy.collection.should be_nil
0
+            nancy = @collection[0]
0
+            nancy.collection.should_not be_nil
0
+            nancy.collection.delete(nancy)
0
+            nancy.collection.should be_nil
0
           end
0
 
0
           it 'should return a Resource' do
0
@@ -296,11 +308,10 @@ if ADAPTER
0
 
0
         describe '#delete_at' do
0
           it 'should reset the resource.collection' do
0
-            @nancy = @collection[0]
0
-
0
-            @nancy.collection.object_id.should == @collection.object_id
0
-            @collection.delete_at(0)
0
-            @nancy.collection.should be_nil
0
+            nancy = @collection[0]
0
+            nancy.collection.should_not be_nil
0
+            nancy.collection.delete_at(0).should == nancy
0
+            nancy.collection.should be_nil
0
           end
0
 
0
           it 'should return a Resource' do
0
@@ -430,16 +441,18 @@ if ADAPTER
0
         describe '#load' do
0
           it 'should load resources from the identity map when possible' do
0
             @steve.collection = nil
0
-            @repository.should_receive(:identity_map_get).with(@model, %w[ Steve ]).and_return(@steve)
0
-            collection = DataMapper::Collection.new(@query)
0
-            collection.load([ @steve.name, @steve.age ])
0
+            @repository.should_receive(:identity_map_get).with(@model, [ @steve.id ]).and_return(@steve)
0
+
0
+            collection = @repository.adapter.read_set(@model, @query.merge(:id => @steve.id))
0
+
0
             collection.size.should == 1
0
-            collection[0].object_id.should == @steve.object_id
0
+            collection.map { |r| r.object_id }.should == [ @steve.object_id ]
0
+
0
             @steve.collection.object_id.should == collection.object_id
0
           end
0
 
0
           it 'should return a Resource' do
0
-            @collection.load([ @steve.name, @steve.age ]).should be_kind_of(DataMapper::Resource)
0
+            @collection.load([ @steve.id, @steve.name, @steve.age ]).should be_kind_of(DataMapper::Resource)
0
           end
0
         end
0
 
0
@@ -457,11 +470,10 @@ if ADAPTER
0
 
0
         describe '#pop' do
0
           it 'should reset the resource.collection' do
0
-            @steve = @collection[2]
0
-
0
-            @steve.collection.object_id.should == @collection.object_id
0
-            @collection.pop
0
-            @steve.collection.should be_nil
0
+            steve = @collection[2]
0
+            steve.collection.should_not be_nil
0
+            steve.collection.pop
0
+            steve.collection.should be_nil
0
           end
0
 
0
           it 'should return a Resource' do
0
@@ -612,10 +624,9 @@ if ADAPTER
0
 
0
         describe '#shift' do
0
           it 'should reset the resource.collection' do
0
-            nancy  = @collection[0]
0
-
0
-            nancy.collection.object_id.should == @collection.object_id
0
-            @collection.shift
0
+            nancy = @collection[0]
0
+            nancy.collection.should_not be_nil
0
+            nancy.collection.shift
0
             nancy.collection.should be_nil
0
           end
0
 
0
@@ -624,37 +635,34 @@ if ADAPTER
0
           end
0
         end
0
 
0
-        describe '#slice' do
0
-          describe 'with an index' do
0
-            it 'should return a Resource' do
0
-              resource = @collection.slice(0)
0
-              resource.should be_kind_of(DataMapper::Resource)
0
+        [ :slice, :[] ].each do |method|
0
+          describe '#slice' do
0
+            describe 'with an index' do
0
+              it 'should return a Resource' do
0
+                resource = @collection.send(method, 0)
0
+                resource.should be_kind_of(DataMapper::Resource)
0
+                resource.id.should == @nancy.id
0
+              end
0
             end
0
-          end
0
-
0
-          describe 'with a start and length' do
0
-            it 'should return a Collection' do
0
-              nancy  = @collection[0]
0
 
0
-              sliced = @collection.slice(0, 1)
0
-              sliced.should be_kind_of(DataMapper::Collection)
0
-              sliced.object_id.should_not == @collection.object_id
0
-              sliced.length.should == 1
0
-              sliced[0].should == nancy
0
+            describe 'with a start and length' do
0
+              it 'should return a Collection' do
0
+                sliced = @collection.send(method, 0, 1)
0
+                sliced.should be_kind_of(DataMapper::Collection)
0
+                sliced.object_id.should_not == @collection.object_id
0
+                sliced.length.should == 1
0
+                sliced.map { |r| r.id }.should == [ @nancy.id ]
0
+              end
0
             end
0
-          end
0
 
0
-          describe 'with a Range' do
0
-            it 'should return a Collection' do
0
-              nancy  = @collection[0]
0
-              bessie = @collection[1]
0
-
0
-              sliced = @collection.slice(0..1)
0
-              sliced.should be_kind_of(DataMapper::Collection)
0
-              sliced.object_id.should_not == @collection.object_id
0
-              sliced.length.should == 2
0
-              sliced[0].should == nancy
0
-              sliced[1].should == bessie
0
+            describe 'with a Range' do
0
+              it 'should return a Collection' do
0
+                sliced = @collection.send(method, 0..1)
0
+                sliced.should be_kind_of(DataMapper::Collection)
0
+                sliced.object_id.should_not == @collection.object_id
0
+                sliced.length.should == 2
0
+                sliced.map { |r| r.id }.should == [ @nancy.id, @bessie.id ]
0
+              end
0
             end
0
           end
0
         end
0
@@ -669,27 +677,22 @@ if ADAPTER
0
 
0
           describe 'with a start and length' do
0
             it 'should return a Collection' do
0
-              nancy = @collection[0]
0
-
0
               sliced = @collection.slice!(0, 1)
0
               sliced.should be_kind_of(DataMapper::Collection)
0
               sliced.object_id.should_not == @collection.object_id
0
               sliced.length.should == 1
0
-              sliced[0].should == nancy
0
+              sliced[0].id.should == @nancy.id
0
             end
0
           end
0
 
0
           describe 'with a Range' do
0
             it 'should return a Collection' do
0
-              nancy  = @collection[0]
0
-              bessie = @collection[1]
0
-
0
               sliced = @collection.slice(0..1)
0
               sliced.should be_kind_of(DataMapper::Collection)
0
               sliced.object_id.should_not == @collection.object_id
0
               sliced.length.should == 2
0
-              sliced[0].should == nancy
0
-              sliced[1].should == bessie
0
+              sliced[0].id.should == @nancy.id
0
+              sliced[1].id.should == @bessie.id
0
             end
0
           end
0
         end
0
@@ -722,20 +725,21 @@ if ADAPTER
0
           end
0
 
0
           it 'should return a Collection of the resources at the index' do
0
-            nancy = @collection[0]
0
-            @collection.values_at(0).entries.should == [ nancy ]
0
+            @collection.values_at(0).entries.map { |r| r.id }.should == [ @nancy.id ]
0
           end
0
         end
0
 
0
         describe 'with lazy loading' do
0
           it "should take a materialization block" do
0
             collection = DataMapper::Collection.new(@query) do |c|
0
-               c.should be_empty
0
-               c.load(['Bob', 10])
0
-               c.load(['Nancy', 11])
0
-             end
0
+              c.should be_empty
0
+              c.load([ 1, 'Bob',   10 ])
0
+              c.load([ 2, 'Nancy', 11 ])
0
+            end
0
 
0
-             collection.length.should == 2
0
+            collection.should_not be_loaded
0
+            collection.length.should == 2
0
+            collection.should be_loaded
0
           end
0
         end
0
       end
...
416
417
418
419
 
420
421
422
423
424
425
 
426
427
428
...
430
431
432
433
 
434
435
436
...
416
417
418
 
419
420
421
422
423
424
 
425
426
427
428
...
430
431
432
 
433
434
435
436
0
@@ -416,13 +416,13 @@ if HAS_POSTGRES
0
       it "should lazy load" do
0
         result = repository(:postgres) do
0
           SailBoat.all
0
-        end
0
+        end.to_a
0
 
0
         result[0].attribute_loaded?(:notes).should be_false
0
         result[0].attribute_loaded?(:trip_report).should be_false
0
         result[1].attribute_loaded?(:notes).should be_false
0
 
0
-        result[0].notes.should_not be_nil
0
+        result[1].notes.should_not be_nil
0
 
0
         result[1].attribute_loaded?(:notes).should be_true
0
         result[1].attribute_loaded?(:trip_report).should be_true
0
@@ -430,7 +430,7 @@ if HAS_POSTGRES
0
 
0
         result = repository(:postgres) do
0
           SailBoat.all
0
-        end
0
+        end.to_a
0
 
0
         result[0].attribute_loaded?(:trip_report).should be_false
0
         result[0].attribute_loaded?(:miles).should be_false
...
96
97
98
99
 
100
101
102
...
110
111
112
113
 
114
115
116
...
96
97
98
 
99
100
101
102
...
110
111
112
 
113
114
115
116
0
@@ -96,7 +96,7 @@ if ADAPTER
0
       it "should lazy load in context" do
0
         result = repository(ADAPTER) do
0
           SailBoat.all
0
-        end
0
+        end.to_a
0
 
0
         result[0].attribute_loaded?(:notes).should be_false
0
         result[0].attribute_loaded?(:trip_report).should be_false
0
@@ -110,7 +110,7 @@ if ADAPTER
0
 
0
         result = repository(ADAPTER) do
0
           SailBoat.all
0
-        end
0
+        end.to_a
0
 
0
         result[0].attribute_loaded?(:trip_report).should be_false
0
         result[0].attribute_loaded?(:miles).should be_false
...
34
35
36
37
38
 
 
 
39
40
41
...
34
35
36
 
 
37
38
39
40
41
42
0
@@ -34,8 +34,9 @@ if ADAPTER
0
       it "should allow limit and offset" do
0
         repository(ADAPTER).all(SerialFinderSpec, { :limit => 50 }).should have(50).entries
0
 
0
-        repository(ADAPTER).all(SerialFinderSpec, { :limit => 20, :offset => 40 }).map(&:id).should ==
0
-          repository(ADAPTER).all(SerialFinderSpec, {})[40...60].map(&:id)
0
+        collection = repository(ADAPTER).all(SerialFinderSpec, { :limit => 20, :offset => 40 })
0
+        collection.should have(20).entries
0
+        collection.map(&:id).should == repository(ADAPTER).all(SerialFinderSpec, {})[40...60].map(&:id)
0
       end
0
 
0
       it "should lazy-load missing attributes" do
...
12
13
14
15
 
16
17
18
...
122
123
124
125
 
126
127
128
...
148
149
150
151
152
153
154
155
156
157
 
158
159
160
161
162
163
164
165
 
166
167
168
...
174
175
176
177
178
179
180
181
182
 
 
 
 
 
183
184
185
186
 
 
187
188
189
...
12
13
14
 
15
16
17
18
...
122
123
124
 
125
126
127
128
...
148
149
150
 
 
 
 
 
 
 
151
152
153
154
155
156
 
 
 
157
158
159
160
...
166
167
168
 
 
 
 
 
 
169
170
171
172
173
174
 
 
 
175
176
177
178
179
0
@@ -12,7 +12,7 @@ if ADAPTER
0
     property :color, String
0
   end
0
 
0
-  class Vegetable
0
+  class Apple
0
     include DataMapper::Resource
0
 
0
     def self.default_repository_name
0
@@ -122,7 +122,7 @@ if ADAPTER
0
   describe "DataMapper::Resource with #{ADAPTER}" do
0
     before :all do
0
       Orange.auto_migrate!(ADAPTER)
0
-      Vegetable.auto_migrate!(ADAPTER)
0
+      Apple.auto_migrate!(ADAPTER)
0
       FortunePig.auto_migrate!(ADAPTER)
0
 
0
       orange = Orange.new(:color => 'orange')
0
@@ -148,21 +148,13 @@ if ADAPTER
0
 
0
     it "should be able to reload new objects" do
0
       repository(ADAPTER) do
0
-        orange = Orange.new
0
-        orange.name = 'Tom'
0
-        orange.save
0
-
0
-        lambda do
0
-          orange.reload!
0
-        end.should_not raise_error
0
+        Orange.create(:name => 'Tom').reload!
0
       end
0
     end
0
 
0
     it "should be able to find first or create objects" do
0
       repository(ADAPTER) do
0
-        orange = Orange.new
0
-        orange.name = 'Naval'
0
-        orange.save
0
+        orange = Orange.create(:name => 'Naval')
0
 
0
         Orange.first_or_create(:name => 'Naval').should == orange
0
 
0
@@ -174,16 +166,14 @@ if ADAPTER
0
     end
0
 
0
     it "should be able to override a default with a nil" do
0
-      pending("Some weird issue with running from Rake") do
0
-        repository(ADAPTER) do
0
-          colorless_veggy = Vegetable.new
0
-          colorless_veggy.color = nil
0
-          colorless_veggy.save
0
-          colorless_veggy.color.should be_nil
0
+      repository(ADAPTER) do
0
+        apple = Apple.new
0
+        apple.color = nil
0
+        apple.save
0
+        apple.color.should be_nil
0
 
0
-          colorless_veggy = Vegetable.create(:color => nil)
0
-          colorless_veggy.color.should be_nil
0
-        end
0
+        apple = Apple.create(:color => nil)
0
+        apple.color.should be_nil
0
       end
0
     end
0
 

Comments