massive_record 0.2.1 → 0.2.2.rc1

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 (135) hide show
  1. data/CHANGELOG.md +58 -2
  2. data/Gemfile.lock +17 -17
  3. data/README.md +98 -41
  4. data/lib/massive_record.rb +2 -1
  5. data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
  6. data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
  7. data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
  8. data/lib/massive_record/adapters/thrift/row.rb +35 -4
  9. data/lib/massive_record/adapters/thrift/table.rb +49 -12
  10. data/lib/massive_record/orm/attribute_methods.rb +77 -5
  11. data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
  12. data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
  13. data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
  14. data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
  15. data/lib/massive_record/orm/base.rb +62 -8
  16. data/lib/massive_record/orm/column.rb +7 -11
  17. data/lib/massive_record/orm/default_id.rb +1 -1
  18. data/lib/massive_record/orm/embedded.rb +66 -0
  19. data/lib/massive_record/orm/errors.rb +17 -0
  20. data/lib/massive_record/orm/finders.rb +124 -71
  21. data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
  22. data/lib/massive_record/orm/finders/scope.rb +58 -34
  23. data/lib/massive_record/orm/id_factory.rb +22 -105
  24. data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
  25. data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
  26. data/lib/massive_record/orm/identity_map.rb +256 -0
  27. data/lib/massive_record/orm/log_subscriber.rb +18 -0
  28. data/lib/massive_record/orm/observer.rb +69 -0
  29. data/lib/massive_record/orm/persistence.rb +47 -119
  30. data/lib/massive_record/orm/persistence/operations.rb +100 -0
  31. data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
  32. data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
  33. data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
  34. data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
  35. data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
  36. data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
  37. data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
  38. data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
  39. data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
  40. data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
  41. data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
  42. data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
  43. data/lib/massive_record/orm/query_instrumentation.rb +26 -49
  44. data/lib/massive_record/orm/raw_data.rb +47 -0
  45. data/lib/massive_record/orm/relations.rb +4 -0
  46. data/lib/massive_record/orm/relations/interface.rb +134 -0
  47. data/lib/massive_record/orm/relations/metadata.rb +58 -12
  48. data/lib/massive_record/orm/relations/proxy.rb +17 -12
  49. data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
  50. data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
  51. data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
  52. data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
  53. data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
  54. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
  55. data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
  56. data/lib/massive_record/orm/schema/column_family.rb +3 -2
  57. data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
  58. data/lib/massive_record/orm/schema/field.rb +2 -0
  59. data/lib/massive_record/orm/schema/table_interface.rb +19 -2
  60. data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
  61. data/lib/massive_record/orm/timestamps.rb +17 -7
  62. data/lib/massive_record/orm/validations.rb +4 -0
  63. data/lib/massive_record/orm/validations/associated.rb +50 -0
  64. data/lib/massive_record/rails/railtie.rb +31 -0
  65. data/lib/massive_record/version.rb +1 -1
  66. data/lib/massive_record/wrapper/cell.rb +8 -1
  67. data/massive_record.gemspec +4 -4
  68. data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
  69. data/spec/adapter/thrift/table_find_spec.rb +14 -2
  70. data/spec/adapter/thrift/table_spec.rb +6 -6
  71. data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
  72. data/spec/orm/cases/attribute_methods_spec.rb +215 -22
  73. data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
  74. data/spec/orm/cases/change_id_spec.rb +62 -0
  75. data/spec/orm/cases/default_id_spec.rb +25 -6
  76. data/spec/orm/cases/default_values_spec.rb +6 -3
  77. data/spec/orm/cases/dirty_spec.rb +150 -102
  78. data/spec/orm/cases/embedded_spec.rb +250 -0
  79. data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
  80. data/spec/orm/cases/finder_scope_spec.rb +96 -29
  81. data/spec/orm/cases/finders_spec.rb +57 -10
  82. data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
  83. data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
  84. data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
  85. data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
  86. data/spec/orm/cases/log_subscriber_spec.rb +15 -2
  87. data/spec/orm/cases/observing_spec.rb +61 -0
  88. data/spec/orm/cases/persistence_spec.rb +151 -60
  89. data/spec/orm/cases/raw_data_spec.rb +58 -0
  90. data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
  91. data/spec/orm/cases/table_spec.rb +3 -3
  92. data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
  93. data/spec/orm/cases/timestamps_spec.rb +23 -109
  94. data/spec/orm/cases/validation_spec.rb +9 -0
  95. data/spec/orm/models/address.rb +5 -1
  96. data/spec/orm/models/address_with_timestamp.rb +12 -0
  97. data/spec/orm/models/car.rb +5 -0
  98. data/spec/orm/models/person.rb +13 -1
  99. data/spec/orm/models/person_with_timestamp.rb +4 -2
  100. data/spec/orm/models/test_class.rb +1 -0
  101. data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
  102. data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
  103. data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
  104. data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
  105. data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
  106. data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
  107. data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
  108. data/spec/orm/persistence/operations/insert_spec.rb +31 -0
  109. data/spec/orm/persistence/operations/reload_spec.rb +48 -0
  110. data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
  111. data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
  112. data/spec/orm/persistence/operations/update_spec.rb +25 -0
  113. data/spec/orm/persistence/operations_spec.rb +58 -0
  114. data/spec/orm/relations/interface_spec.rb +188 -0
  115. data/spec/orm/relations/metadata_spec.rb +92 -15
  116. data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
  117. data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
  118. data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
  119. data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
  120. data/spec/orm/schema/column_family_spec.rb +21 -0
  121. data/spec/orm/schema/embedded_interface_spec.rb +181 -0
  122. data/spec/orm/schema/field_spec.rb +7 -0
  123. data/spec/orm/schema/table_interface_spec.rb +31 -1
  124. data/spec/shared/orm/id_factories.rb +44 -0
  125. data/spec/shared/orm/model_with_timestamps.rb +132 -0
  126. data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
  127. data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
  128. data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
  129. data/spec/shared/orm/relations/proxy.rb +9 -2
  130. data/spec/spec_helper.rb +9 -0
  131. data/spec/support/mock_massive_record_connection.rb +2 -1
  132. metadata +106 -21
  133. data/spec/orm/cases/column_spec.rb +0 -49
  134. data/spec/orm/cases/id_factory_spec.rb +0 -92
  135. data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -105,6 +105,165 @@ describe TestReferencesManyProxy do
105
105
  end
106
106
 
107
107
 
108
+ describe "#find_in_batches" do
109
+ context "when the proxy is loaded" do
110
+ before do
111
+ proxy_owner.save!
112
+ proxy_owner.test_classes.concat(proxy_target, proxy_target_2, proxy_target_3)
113
+ end
114
+
115
+ it "returns records in batches of given size" do
116
+ result = []
117
+
118
+ subject.find_in_batches(:batch_size => 1) do |records_batch|
119
+ result << records_batch
120
+ end
121
+
122
+ result.should eq [[proxy_target], [proxy_target_2], [proxy_target_3]]
123
+ end
124
+
125
+ it "filters when given :starts_with" do
126
+ proxy_target_3_3 = TestClass.new("test-class-id-3-1")
127
+ proxy_owner.test_classes << proxy_target_3_3
128
+
129
+ result = []
130
+
131
+ subject.find_in_batches(:batch_size => 1, :starts_with => "test-class-id-3") do |records_batch|
132
+ result << records_batch
133
+ end
134
+
135
+ result.should eq [[proxy_target_3], [proxy_target_3_3]]
136
+ end
137
+ end
138
+
139
+ context "when persisted foreign keys" do
140
+ before do
141
+ proxy_owner.save!
142
+ proxy_owner.test_classes.concat(proxy_target, proxy_target_2, proxy_target_3)
143
+ subject.reset
144
+ end
145
+
146
+ it "returns records in batches of given size" do
147
+ result = []
148
+
149
+ subject.find_in_batches(:batch_size => 1) do |records_batch|
150
+ result << records_batch
151
+ end
152
+
153
+ result.should eq [[proxy_target], [proxy_target_2], [proxy_target_3]]
154
+ end
155
+
156
+ it "filters when given :starts_with" do
157
+ proxy_target_3_3 = TestClass.new("test-class-id-3-1")
158
+ proxy_owner.test_classes << proxy_target_3_3
159
+ subject.reset
160
+
161
+ result = []
162
+
163
+ subject.find_in_batches(:batch_size => 1, :starts_with => "test-class-id-3") do |records_batch|
164
+ result << records_batch
165
+ end
166
+
167
+ result.should eq [[proxy_target_3], [proxy_target_3_3]]
168
+ end
169
+
170
+ it "does not alter foreign keys in proxy owner" do
171
+ foreign_keys_before_batches = proxy_owner.test_class_ids.dup
172
+
173
+ subject.find_in_batches(:batch_size => 1, :starts_with => "test-class-id-3") do |records_batch|
174
+ end
175
+
176
+ proxy_owner.test_class_ids.should eq foreign_keys_before_batches
177
+ end
178
+ end
179
+
180
+ context "when finding with a given start id" do
181
+ (1..6).each do |i|
182
+ let("record_#{i}") { TestClass.create! "test-#{i}" }
183
+ end
184
+
185
+ let(:records) { (1..6).collect { |i| send("record_#{i}") } }
186
+
187
+ before do
188
+ records # touch to save
189
+
190
+ subject.metadata.records_starts_from = :test_classes_starts_from
191
+ subject.proxy_owner.should_receive(:test_classes_starts_from).and_return("test-")
192
+ end
193
+
194
+ after { subject.metadata.records_starts_from = nil }
195
+
196
+
197
+ it "returns records in one batch" do
198
+ result = []
199
+
200
+ subject.find_in_batches(:batch_size => 10) do |records_batch|
201
+ result << records_batch
202
+ end
203
+
204
+ result.should eq [records]
205
+ end
206
+
207
+ it "returns records in one batch" do
208
+ result = []
209
+
210
+ subject.find_in_batches(:batch_size => 3) do |records_batch|
211
+ result << records_batch
212
+ end
213
+
214
+ result.should eq [records.slice(0, 3), records.slice(3, 3)]
215
+ end
216
+
217
+ describe "an overridden start id" do
218
+ (11..16).each do |i|
219
+ let("record_#{i}") { TestClass.create! "test-1-#{i}" }
220
+ end
221
+
222
+ let(:records_above_10) { (11..16).collect { |i| send("record_#{i}") } }
223
+
224
+ before { records_above_10 }
225
+
226
+ it "returns only records with given start" do
227
+ result = []
228
+
229
+ subject.find_in_batches(:batch_size => 3, :starts_with => "test-1-") do |records_batch|
230
+ result << records_batch
231
+ end
232
+
233
+ result.should eq [records_above_10.slice(0, 3), records_above_10.slice(3, 3)]
234
+ end
235
+
236
+ it "raises an error if your start option starst with some incorrect value" do
237
+ expect {
238
+ subject.find_in_batches(:starts_with => 'incorrect_value') { |b| }
239
+ }.to raise_error MassiveRecord::ORM::Relations::InvalidStartsWithOption
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+
246
+ describe "#find_each" do
247
+ it "delegate to find_in_batches" do
248
+ subject.should_receive(:find_in_batches).with(:batch_size => 2, :starts_with => :from_here)
249
+ subject.find_each(:batch_size => 2, :starts_with => :from_here)
250
+ end
251
+
252
+ it "yields one and one record" do
253
+ proxy_owner.save!
254
+ proxy_owner.test_classes.concat(proxy_target, proxy_target_2, proxy_target_3)
255
+
256
+ result = []
257
+
258
+ subject.find_each do |record|
259
+ result << record
260
+ end
261
+
262
+ result.should eq [proxy_target, proxy_target_2, proxy_target_3]
263
+ end
264
+ end
265
+
266
+
108
267
  describe "adding records to collection" do
109
268
  [:<<, :push, :concat].each do |add_method|
110
269
  describe "by ##{add_method}" do
@@ -138,7 +297,7 @@ describe TestReferencesManyProxy do
138
297
  end
139
298
 
140
299
  it "should not update array of foreign keys in proxy_owner if it does not respond to it" do
141
- proxy_owner.should_receive(:respond_to?).twice.and_return(false)
300
+ proxy_owner.should_receive(:respond_to?).and_return(false)
142
301
  subject.send(add_method, proxy_target)
143
302
  proxy_owner.test_class_ids.should_not include(proxy_target.id)
144
303
  end
@@ -296,6 +455,11 @@ describe TestReferencesManyProxy do
296
455
  proxy_target_2.should_receive(:destroy)
297
456
  subject.destroy_all
298
457
  end
458
+
459
+ it "returns destroyed records" do
460
+ removed = subject.destroy_all
461
+ removed.should include proxy_target, proxy_target_2
462
+ end
299
463
  end
300
464
 
301
465
  describe "with delete_all" do
@@ -329,6 +493,11 @@ describe TestReferencesManyProxy do
329
493
  proxy_target_2.should_not_receive(:destroy)
330
494
  subject.delete_all
331
495
  end
496
+
497
+ it "returns deleted records" do
498
+ removed = subject.delete_all
499
+ removed.should include proxy_target, proxy_target_2
500
+ end
332
501
  end
333
502
  end
334
503
 
@@ -361,12 +530,182 @@ describe TestReferencesManyProxy do
361
530
  subject << proxy_target_2
362
531
  subject.send(method).should == 2
363
532
  end
533
+
534
+ it "returns correct length if new proxy gets records added" do
535
+ subject.destroy_all
536
+ subject.reset
537
+ proxy_owner.stub(:persisted?).and_return false
538
+ proxy_owner.stub(:new_record?).and_return true
539
+
540
+ new_proxy_target = proxy_target.class.new "id-1"
541
+ subject << new_proxy_target
542
+ subject.length.should eq 1
543
+ end
544
+ end
545
+ end
546
+
547
+ context "foreign keys persisted in owner" do
548
+ before do
549
+ proxy_owner.save!
550
+ subject << proxy_target << proxy_target_2
551
+ end
552
+
553
+ context "targets not loaded" do
554
+ before { subject.reset }
555
+
556
+ it "loads nothing" do
557
+ TestClass.should_not_receive(:find)
558
+ subject.length
559
+ end
560
+
561
+ it "reads length from proxy owner's foreign keys list" do
562
+ proxy_owner.should_receive(:test_class_ids).and_return([proxy_target.id, proxy_target_2.id])
563
+ subject.length
564
+ end
565
+
566
+ it "returns correct length" do
567
+ subject.length.should eq 2
568
+ end
569
+ end
570
+
571
+ context "targets loaded" do
572
+ before { subject.reload }
573
+
574
+ it "loads nothing" do
575
+ TestClass.should_not_receive(:find)
576
+ subject.length
577
+ end
578
+
579
+ it "reads length from proxy_target" do
580
+ subject.should_receive(:proxy_target).and_return([proxy_target, proxy_target_2])
581
+ subject.length
582
+ end
583
+
584
+ it "returns correct length" do
585
+ subject.length.should eq 2
586
+ end
587
+ end
588
+ end
589
+
590
+ context "when we are using a starts_with to find related records" do
591
+ let(:proxy_target) { Person.new proxy_owner.id+"-friend-1", :name => "T", :age => 2 }
592
+ let(:proxy_target_2) { Person.new proxy_owner.id+"-friend-2", :name => "H", :age => 9 }
593
+ let(:not_proxy_target) { Person.new "foo"+"-friend-2", :name => "H", :age => 1 }
594
+ let(:metadata) { subject.metadata }
595
+
596
+ subject { proxy_owner.send(:relation_proxy, 'friends') }
597
+
598
+ before do
599
+ proxy_owner.save!
600
+ subject << proxy_target << proxy_target_2
601
+ end
602
+
603
+ context "targets not loaded" do
604
+ before { subject.reset }
605
+
606
+ it "loads all the targets and returns it's length" do
607
+ subject.should_receive(:load_proxy_target).and_return [proxy_target, proxy_target_2]
608
+ subject.length
609
+ end
610
+
611
+ it "returns correct length" do
612
+ subject.length.should eq 2
613
+ end
614
+
615
+ it "returns correct length after adding a record" do
616
+ subject << Person.new(proxy_owner.id+"-friend-3", :name => "H", :age => 9)
617
+ subject.length.should eq 3
618
+ end
619
+ end
620
+
621
+ context "targets loaded" do
622
+ before { subject.reload }
623
+
624
+ it "loads nothing" do
625
+ subject.should_not_receive(:load_proxy_target)
626
+ subject.length
627
+ end
628
+
629
+ it "returns correct length" do
630
+ subject.length.should eq 2
631
+ end
364
632
  end
365
633
  end
366
634
  end
367
635
  end
368
636
 
369
- describe "#include" do
637
+ describe "#any?" do
638
+ before { subject.reset }
639
+
640
+ it "checks the length and return true if it is greater than 0" do
641
+ subject.should_receive(:length).and_return 1
642
+ subject.any?.should be_true
643
+ end
644
+
645
+ it "checks the length and return false if it is 0" do
646
+ subject.should_receive(:length).and_return 0
647
+ subject.any?.should be_false
648
+ end
649
+
650
+ context "when find with proc" do
651
+ before do
652
+ subject.should_receive(:loaded?).and_return false
653
+ subject.should_receive(:find_with_proc?).and_return true
654
+ end
655
+
656
+ it "asks for first and returns false if first is nil" do
657
+ subject.should_receive(:first).and_return nil
658
+ subject.any?.should be_false
659
+ end
660
+
661
+ it "asks for first and returns true if first is a record" do
662
+ subject.should_receive(:first).and_return proxy_target
663
+ subject.any?.should be_true
664
+ end
665
+ end
666
+ end
667
+
668
+ describe "#present?" do
669
+ before { subject.reset }
670
+
671
+ it "checks the length and return true if it is greater than 0" do
672
+ subject.should_receive(:length).and_return 1
673
+ subject.present?.should be_true
674
+ end
675
+
676
+ it "checks the length and return false if it is 0" do
677
+ subject.should_receive(:length).and_return 0
678
+ subject.present?.should be_false
679
+ end
680
+ end
681
+
682
+ describe "#include?" do
683
+ it "uses find as it's query method when loaded" do
684
+ subject.should_receive(:loaded?).and_return true
685
+ subject.should_receive(:find).with(proxy_target.id).and_return true
686
+ subject.should include proxy_target
687
+ end
688
+
689
+ it "uses find as it's query method when find with proc" do
690
+ subject.should_receive(:find_with_proc?).and_return true
691
+ subject.should_receive(:find).with(proxy_target.id).and_return true
692
+ subject.should include proxy_target
693
+ end
694
+
695
+ it "can answer to ids as well" do
696
+ subject.should_receive(:foreign_key_in_proxy_owner_exists?).with(proxy_target.id).and_return true
697
+ subject.should include proxy_target.id
698
+ end
699
+
700
+ it "does not load record if foreign keys are presisted in proxy owner" do
701
+ proxy_owner.save!
702
+ subject << proxy_target
703
+ subject.reset
704
+
705
+ TestClass.should_not_receive(:find)
706
+ subject.should include proxy_target
707
+ end
708
+
370
709
  [true, false].each do |should_persist_proxy_owner|
371
710
  describe "with proxy_owner " + (should_persist_proxy_owner ? "persisted" : "not persisted") do
372
711
  before do
@@ -521,6 +860,17 @@ describe TestReferencesManyProxy do
521
860
  lambda { subject.find(not_among_targets.id) }.should raise_error MassiveRecord::ORM::RecordNotFound
522
861
  end
523
862
 
863
+ it "returns record if in a dirty state, when no objects are saved" do
864
+ subject.destroy_all
865
+ subject.reset
866
+ proxy_owner.stub(:persisted?).and_return false
867
+ proxy_owner.stub(:new_record?).and_return true
868
+
869
+ new_proxy_target = Person.new proxy_owner.id+"-friend-2", :name => "H", :age => 9
870
+ subject << new_proxy_target
871
+ subject.find(new_proxy_target.id).should eq new_proxy_target
872
+ end
873
+
524
874
 
525
875
 
526
876
  it "should not hit database if proxy has been loaded" do
@@ -536,6 +886,94 @@ describe TestReferencesManyProxy do
536
886
  end
537
887
  end
538
888
 
889
+ describe "#all" do
890
+ let(:not_among_targets) { proxy_target_3 }
891
+
892
+ describe "stored foreign keys" do
893
+ before do
894
+ proxy_owner.save!
895
+ subject << proxy_target << proxy_target_2
896
+ subject.reset
897
+
898
+ not_among_targets.save!
899
+ end
900
+
901
+ it "returns all the targets" do
902
+ subject.all.should include proxy_target, proxy_target_2
903
+ subject.all.should_not include proxy_target_3
904
+ end
905
+
906
+ [:limit, :offset, :starts_with].each do |finder_option|
907
+ it "raises an error when asked to do a #{finder_option}" do
908
+ expect {
909
+ subject.all(finder_option => "value")
910
+ }.to raise_error MassiveRecord::ORM::Relations::Proxy::ReferencesMany::UnsupportedFinderOption
911
+ end
912
+ end
913
+
914
+ it "does not hit database if targets has been loaded" do
915
+ subject.load_proxy_target
916
+ subject.proxy_target_class.should_not_receive :find
917
+ subject.all
918
+ end
919
+ end
920
+
921
+ describe "with records starts from (proc)" do
922
+ let(:proxy_target) { Person.new proxy_owner.id+"-friend-1", :name => "T", :age => 2 }
923
+ let(:proxy_target_2) { Person.new proxy_owner.id+"-friend-2", :name => "H", :age => 9 }
924
+ let(:not_among_targets) { Person.new "NOT-friend-1", :name => "H", :age => 9 }
925
+ let(:metadata) { subject.metadata }
926
+
927
+ subject { proxy_owner.send(:relation_proxy, 'friends') }
928
+
929
+ before do
930
+ proxy_owner.save!
931
+ subject << proxy_target << proxy_target_2
932
+ subject.reset
933
+
934
+ not_among_targets.save!
935
+ end
936
+
937
+ it "returns all the targets" do
938
+ subject.all.should include proxy_target, proxy_target_2
939
+ subject.all.should_not include proxy_target_3
940
+ end
941
+
942
+ it "accepts to limit the result" do
943
+ subject.all(:limit => 1).should include proxy_target
944
+ subject.all.should_not include proxy_target_3, proxy_target_2
945
+ end
946
+
947
+ it "accepts to offset the result" do
948
+ subject.all(:offset => proxy_owner.id+"-friend-2").should include proxy_target_2
949
+ subject.all.should_not include proxy_target_3, proxy_target
950
+ end
951
+
952
+ it "accepts to modify the starts_with" do
953
+ subject.all(:starts_with => proxy_owner.id+"-friend-1").should include proxy_target
954
+ subject.all.should_not include proxy_target_3, proxy_target_2
955
+ end
956
+
957
+ it "raises an error on invalid starts_with option" do
958
+ expect {
959
+ subject.all(:starts_with => "foobar")
960
+ }.to raise_error MassiveRecord::ORM::Relations::InvalidStartsWithOption
961
+ end
962
+
963
+ it "accepts option offset" do
964
+ records = subject.all(:offset => proxy_owner.id+"-friend-2")
965
+ records.should_not include proxy_target, proxy_target_3
966
+ records.should include proxy_target_2
967
+ end
968
+
969
+ it "does not hit database if targets has been loaded" do
970
+ subject.load_proxy_target
971
+ Person.should_not_receive(:do_find)
972
+ subject.all
973
+ end
974
+ end
975
+ end
976
+
539
977
 
540
978
 
541
979
  describe "#limit" do
@@ -575,6 +1013,13 @@ describe TestReferencesManyProxy do
575
1013
  subject.load_proxy_target
576
1014
  subject.limit(1).should == [proxy_target]
577
1015
  end
1016
+
1017
+ it "returns first record if in a dirty state" do
1018
+ subject.delete(proxy_target_2)
1019
+ subject.reset
1020
+ subject << proxy_target_2
1021
+ subject.limit(1).should eq [proxy_target]
1022
+ end
578
1023
  end
579
1024
 
580
1025
 
@@ -619,6 +1064,25 @@ describe TestReferencesManyProxy do
619
1064
  subject.load_proxy_target
620
1065
  subject.limit(1).should == [proxy_target]
621
1066
  end
1067
+
1068
+ it "returns first record if in a dirty state" do
1069
+ proxy_target_2.destroy
1070
+ new_proxy_target = Person.new proxy_owner.id+"-friend-2", :name => "H", :age => 9
1071
+ subject.reset
1072
+ subject << new_proxy_target
1073
+ subject.limit(1).should eq [proxy_target]
1074
+ end
1075
+
1076
+ it "returns first record if in a dirty state, when no objects are saved" do
1077
+ subject.destroy_all
1078
+ subject.reset
1079
+ proxy_owner.stub(:persisted?).and_return false
1080
+ proxy_owner.stub(:new_record?).and_return true
1081
+
1082
+ new_proxy_target = Person.new proxy_owner.id+"-friend-2", :name => "H", :age => 9
1083
+ subject << new_proxy_target
1084
+ subject.limit(1).should eq [new_proxy_target]
1085
+ end
622
1086
  end
623
1087
  end
624
1088