massive_record 0.2.1 → 0.2.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/address'
3
+
4
+ describe MassiveRecord::ORM::Embedded do
5
+ subject { Address.new(:street => "Asker", :number => 5) }
6
+ let(:person) { Person.new "person-id", :name => "Thorbjorn", :age => "22" }
7
+
8
+ it "should have known_attribute_names" do
9
+ Address.should have(4).known_attribute_names
10
+ Address.known_attribute_names.should include("street", "number", "nice_place")
11
+ end
12
+
13
+ it "should have default_attributes_from_schema" do
14
+ Address.default_attributes_from_schema["street"].should be_nil
15
+ Address.default_attributes_from_schema["number"].should be_nil
16
+ Address.default_attributes_from_schema["nice_place"].should be_true
17
+ end
18
+
19
+ it "should have attributes_schema" do
20
+ Address.attributes_schema["street"].should be_instance_of MassiveRecord::ORM::Schema::Field
21
+ end
22
+
23
+ it "should have a default value set" do
24
+ subject.nice_place.should be_true
25
+ end
26
+
27
+
28
+ # TODO We might want to remove this when we have implemented
29
+ # associations correctly. Since Columns are contained within
30
+ # tables, calling save should do something on it's proxy_owner object.
31
+ describe "not be possible to persist (at least for now...)" do
32
+ %w(first last all exists? destroy_all).each do |method|
33
+ it "should not respond to class method #{method}" do
34
+ Address.should_not respond_to method
35
+ end
36
+ end
37
+
38
+ %w(
39
+ reload save save! save
40
+ update_attribute update_attributes update_attributes! touch destroy
41
+ delete increment! decrement! atomic_increment! atomic_decrement!
42
+ ).each do |method|
43
+ it "do respond to #{method}" do
44
+ subject.should respond_to method
45
+ end
46
+ end
47
+ end
48
+
49
+
50
+ describe "persistence" do
51
+ include SetUpHbaseConnectionBeforeAll
52
+ include SetTableNamesToTestTable
53
+
54
+ describe "#save" do
55
+ context "not embedded" do
56
+ before { subject.person = nil }
57
+
58
+ it "raises error" do
59
+ expect { subject.save }.to raise_error MassiveRecord::ORM::NotAssignedToEmbeddedCollection
60
+ end
61
+ end
62
+
63
+ context "embedded in a collection" do
64
+ context "collection owner not persisted" do
65
+ before { subject.person = person }
66
+
67
+ it "gets assigned an id" do
68
+ subject.save
69
+ subject.id.should_not be_blank
70
+ end
71
+
72
+ it "saves both embedded record and embedded in record" do
73
+ subject.save
74
+
75
+ person.should be_persisted
76
+ subject.should be_persisted
77
+ end
78
+
79
+ it "does nothing if validations fail on embedded" do
80
+ subject.street = nil
81
+ subject.save
82
+ subject.errors.should_not be_empty
83
+
84
+ person.should_not be_persisted
85
+ subject.should_not be_persisted
86
+ end
87
+
88
+ describe "validations fails on owner" do
89
+ before { person.name = nil }
90
+
91
+ it "does not persist owner" do
92
+ subject.save
93
+ person.should_not be_persisted
94
+ end
95
+
96
+ it "does not mark embedded as persisted" do
97
+ subject.save
98
+ subject.should_not be_persisted
99
+ end
100
+ end
101
+ end
102
+
103
+ context "colletion owner persisted" do
104
+ before do
105
+ person.save!
106
+ subject.person = person
107
+ end
108
+
109
+ it "gets assigned an id" do
110
+ subject.id.should_not be_blank
111
+ end
112
+
113
+ it "persists address" do
114
+ subject.street = "new_address"
115
+ subject.save
116
+ person.reload.addresses.first.street.should eq "new_address"
117
+ end
118
+
119
+ it "will not save changes in owner when embedded is saved" do
120
+ subject.street += "_NEW"
121
+ person.name += "_NEW"
122
+
123
+ subject.save
124
+
125
+ person.should be_name_changed
126
+ end
127
+
128
+ it "does nothing if validations fail on embedded" do
129
+ subject.street = nil
130
+ subject.save
131
+ subject.errors.should_not be_empty
132
+
133
+ subject.should be_changed
134
+ end
135
+
136
+ it "save even if validations fails on owner of collection embedded in" do
137
+ person.name = nil
138
+ subject.street += "_NEW"
139
+ subject.save
140
+
141
+ subject.should_not be_changed
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+
148
+ describe "#destroy" do
149
+ context "not emedded" do
150
+ before { subject.person = nil }
151
+
152
+ it "marks itself as destroyed" do
153
+ subject.destroy
154
+ subject.should be_destroyed
155
+ end
156
+ end
157
+
158
+ context "embedded" do
159
+ context "collection owner new record" do
160
+ before { subject.person = person }
161
+
162
+ it "marks itself as destroyed" do
163
+ subject.destroy
164
+ subject.should be_destroyed
165
+ end
166
+ end
167
+
168
+ context "collection owner persisted" do
169
+ before do
170
+ subject.person = person
171
+ person.save!
172
+ end
173
+
174
+ it "marks itself as destroyed" do
175
+ subject.destroy
176
+ subject.should be_destroyed
177
+ end
178
+
179
+ it "is removed from embeds_many collection" do
180
+ subject.destroy
181
+ person.addresses.should be_empty
182
+ end
183
+
184
+ it "is actually removed from collection" do
185
+ subject.destroy
186
+ person.reload.addresses.should be_empty
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ describe "id" do
194
+ include SetUpHbaseConnectionBeforeAll
195
+ include SetTableNamesToTestTable
196
+
197
+ describe "assignment on first save" do
198
+ it "has no id when first instantiated" do
199
+ subject.id.should be_nil
200
+ end
201
+
202
+ it "gets an id on explicit save" do
203
+ subject.person = person
204
+ subject.save
205
+ subject.id.should_not be_nil
206
+ end
207
+
208
+ it "gets an id when saved through persisted parent" do
209
+ person.save
210
+ person.addresses << subject
211
+ subject.id.should_not be_nil
212
+ end
213
+ end
214
+
215
+ describe "#database_id" do
216
+ let(:base_class) { Address.base_class.to_s.underscore }
217
+
218
+ describe "reader" do
219
+ it "has non when first instantiated" do
220
+ subject.database_id.should be_nil
221
+ end
222
+
223
+ it "gets one on explicit save" do
224
+ subject.person = person
225
+ subject.save
226
+ subject.database_id.should eq [base_class, subject.id].join(MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR)
227
+ end
228
+
229
+ it "gets one when saved through persisted parent" do
230
+ person.save
231
+ person.addresses << subject
232
+ subject.database_id.should eq [base_class, subject.id].join(MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR)
233
+ end
234
+ end
235
+
236
+ describe "writer" do
237
+ it "splits base_class and id and assigns id to id" do
238
+ subject.database_id = "address#{MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR}166"
239
+ subject.id.should eq "166"
240
+ end
241
+
242
+ it "raises an error if database id could not be parsed" do
243
+ expect {
244
+ subject.database_id = "address-166"
245
+ }.to raise_error MassiveRecord::ORM::InvalidEmbeddedDatabaseId
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -56,5 +56,9 @@ describe "Default scope in" do
56
56
  Person.default_scoping.should be_instance_of MassiveRecord::ORM::Finders::Scope
57
57
  TestClass.default_scoping.should be_nil
58
58
  end
59
+
60
+ it "returns a new scope object when default_scope are set" do
61
+ Person.finder_scope.should_not eq Person.finder_scope
62
+ end
59
63
  end
60
64
  end
@@ -21,8 +21,8 @@ describe MassiveRecord::ORM::Finders::Scope do
21
21
  (MassiveRecord::ORM::Finders::Scope::MULTI_VALUE_METHODS + MassiveRecord::ORM::Finders::Scope::SINGLE_VALUE_METHODS).each do |method|
22
22
  it { should respond_to(method) }
23
23
 
24
- it "should return self after #{method}()" do
25
- subject.send(method, nil).should == subject
24
+ it "should return an instance of self #{method}()" do
25
+ subject.send(method, nil).should be_instance_of described_class
26
26
  end
27
27
  end
28
28
 
@@ -31,33 +31,27 @@ describe MassiveRecord::ORM::Finders::Scope do
31
31
  describe "multi value methods" do
32
32
  describe "select" do
33
33
  it "should not add nil values" do
34
- subject.select(nil)
35
- subject.select_values.should be_empty
34
+ subject.select(nil).select_values.should be_empty
36
35
  end
37
36
 
38
37
  it "should add incomming value to list" do
39
- subject.select(:info)
40
- subject.select_values.should include 'info'
38
+ subject.select(:info).select_values.should include 'info'
41
39
  end
42
40
 
43
41
  it "should be adding values if called twice" do
44
- subject.select(:info).select(:base)
45
- subject.select_values.should include 'info', 'base'
42
+ subject.select(:info).select(:base).select_values.should include 'info', 'base'
46
43
  end
47
44
 
48
45
  it "should add multiple arguments" do
49
- subject.select(:info, :base)
50
- subject.select_values.should include 'info', 'base'
46
+ subject.select(:info, :base).select_values.should include 'info', 'base'
51
47
  end
52
48
 
53
49
  it "should add multiple values given as array" do
54
- subject.select([:info, :base])
55
- subject.select_values.should include 'info', 'base'
50
+ subject.select([:info, :base]).select_values.should include 'info', 'base'
56
51
  end
57
52
 
58
53
  it "should not add same value twice" do
59
- subject.select(:info).select('info')
60
- subject.select_values.should == ['info']
54
+ subject.select(:info).select('info').select_values.should == ['info']
61
55
  end
62
56
  end
63
57
  end
@@ -67,13 +61,31 @@ describe MassiveRecord::ORM::Finders::Scope do
67
61
  describe "singel value methods" do
68
62
  describe "limit" do
69
63
  it "should set a limit" do
70
- subject.limit(5)
71
- subject.limit_value.should == 5
64
+ subject.limit(5).limit_value.should == 5
72
65
  end
73
66
 
74
67
  it "should be set to the last value set" do
75
- subject.limit(1).limit(5)
76
- subject.limit_value.should == 5
68
+ subject.limit(1).limit(5).limit_value.should == 5
69
+ end
70
+ end
71
+
72
+ describe "starts_with" do
73
+ it "should set a starts_with" do
74
+ subject.starts_with(5).starts_with_value.should == 5
75
+ end
76
+
77
+ it "should be set to the last value set" do
78
+ subject.starts_with(1).starts_with(5).starts_with_value.should == 5
79
+ end
80
+ end
81
+
82
+ describe "offset" do
83
+ it "should set a offset" do
84
+ subject.offset(5).offset_value.should == 5
85
+ end
86
+
87
+ it "should be set to the last value set" do
88
+ subject.offset(1).offset(5).offset_value.should == 5
77
89
  end
78
90
  end
79
91
  end
@@ -92,6 +104,14 @@ describe MassiveRecord::ORM::Finders::Scope do
92
104
  it "should include selection when asked for it" do
93
105
  subject.select(:info).send(:find_options).should include :select => ['info']
94
106
  end
107
+
108
+ it "includes a starts_with when asked for it" do
109
+ subject.starts_with("id-something").send(:find_options).should include :starts_with => "id-something"
110
+ end
111
+
112
+ it "includes an offset when asked for it" do
113
+ subject.offset("id-something").send(:find_options).should include :offset => "id-something"
114
+ end
95
115
  end
96
116
 
97
117
 
@@ -104,13 +124,31 @@ describe MassiveRecord::ORM::Finders::Scope do
104
124
  end
105
125
  end
106
126
 
107
-
108
127
  describe "#reset" do
109
- it "should reset loaded status" do
128
+ it "resets the loaded status" do
110
129
  subject.loaded = true
111
130
  subject.reset
112
131
  should_not be_loaded
113
132
  end
133
+
134
+ it "resets the loaded records" do
135
+ records = [:foo]
136
+ subject.instance_variable_set(:@records, records)
137
+ subject.reset
138
+ subject.instance_variable_get(:@records).should be_empty
139
+ end
140
+ end
141
+
142
+ describe "a clone" do
143
+ it "resets the state after being cloned" do
144
+ records = [:foo]
145
+ subject.instance_variable_set(:@records, records)
146
+ subject.loaded = true
147
+
148
+ cloned = subject.clone
149
+ cloned.should_not be_loaded
150
+ cloned.instance_variable_get(:@records).should be_empty
151
+ end
114
152
  end
115
153
 
116
154
  describe "#to_a" do
@@ -123,7 +161,7 @@ describe MassiveRecord::ORM::Finders::Scope do
123
161
  end
124
162
 
125
163
 
126
- [:to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?].each do |method|
164
+ [:to_xml, :to_yaml, :length, :size, :collect, :map, :each, :all?, :include?].each do |method|
127
165
  it "should delegate #{method} to to_a" do
128
166
  records = []
129
167
  records.should_receive(method)
@@ -157,6 +195,15 @@ describe MassiveRecord::ORM::Finders::Scope do
157
195
  subject.should_receive(:klass).and_return(klass)
158
196
  subject.select(:foo).find(1)
159
197
  end
198
+
199
+ it "should include finder options" do
200
+ extra_options = {:select => ["foo"], :conditions => 'should_be_passed_on_to_finder'}
201
+ klass = mock(Object)
202
+ klass.should_receive(:do_find).with("ID", hash_including(extra_options)).and_return([])
203
+ subject.instance_variable_set(:@klass, klass)
204
+
205
+ subject.find("ID", extra_options)
206
+ end
160
207
  end
161
208
 
162
209
 
@@ -176,7 +223,7 @@ describe MassiveRecord::ORM::Finders::Scope do
176
223
 
177
224
  klass = mock(Object)
178
225
  klass.should_receive(:do_find).with(anything, hash_including(extra_options)).and_return([])
179
- subject.should_receive(:klass).and_return(klass)
226
+ subject.instance_variable_set(:@klass, klass)
180
227
 
181
228
  subject.first(extra_options)
182
229
  end
@@ -194,7 +241,7 @@ describe MassiveRecord::ORM::Finders::Scope do
194
241
 
195
242
  klass = mock(Object)
196
243
  klass.should_receive(:do_find).with(anything, extra_options)
197
- subject.should_receive(:klass).and_return(klass)
244
+ subject.instance_variable_set(:@klass, klass)
198
245
 
199
246
  subject.all(extra_options)
200
247
  end
@@ -219,10 +266,12 @@ describe MassiveRecord::ORM::Finders::Scope do
219
266
  describe "with a person" do
220
267
  let(:person_1) { Person.create "ID1", :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true }
221
268
  let(:person_2) { Person.create "ID2", :name => "Person2", :email => "two@person.com", :age => 22, :points => 222, :status => false }
269
+ let(:person_3) { Person.create "other", :name => "Person3", :email => "three@person.com", :age => 33, :points => 333, :status => true }
222
270
 
223
271
  before do
224
272
  person_1.save!
225
273
  person_2.save!
274
+ person_3.save!
226
275
  end
227
276
 
228
277
  (MassiveRecord::ORM::Finders::Scope::MULTI_VALUE_METHODS + MassiveRecord::ORM::Finders::Scope::SINGLE_VALUE_METHODS).each do |method|
@@ -244,15 +293,33 @@ describe MassiveRecord::ORM::Finders::Scope do
244
293
  person_from_db.status.should be_nil
245
294
  end
246
295
 
296
+ it "applying scope on a loaded scope returns a cloned and reset scope" do
297
+ scope = Person.limit(2)
298
+ scope.all.should eq [person_1, person_2]
299
+ scope.should be_loaded
300
+
301
+ new_scope = scope.offset("ID2")
302
+ new_scope.should_not be_loaded
303
+ new_scope.all.should eq [person_2, person_3]
304
+ end
305
+
247
306
  it "should not return read only objects when select is used" do
248
307
  person = Person.select(:info).first
249
308
  person.should_not be_readonly
250
309
  end
251
310
 
311
+ it "ensures records starts with string" do
312
+ Person.starts_with("ID").should == [person_1, person_2]
313
+ end
314
+
315
+ it "sets an offset point to begin read rows from" do
316
+ Person.offset("ID2").should == [person_2, person_3]
317
+ end
318
+
252
319
  it "should be possible to iterate over a collection with each" do
253
320
  result = []
254
321
 
255
- Person.limit(5).each do |person|
322
+ Person.starts_with("ID").limit(2).each do |person|
256
323
  result << person.name
257
324
  end
258
325
 
@@ -260,7 +327,7 @@ describe MassiveRecord::ORM::Finders::Scope do
260
327
  end
261
328
 
262
329
  it "should be possible to collect" do
263
- Person.select(:info).collect(&:name).should == ["Person1", "Person2"]
330
+ Person.select(:info).collect(&:name).should == ["Person1", "Person2", "Person3"]
264
331
  end
265
332
 
266
333
  it "should be possible to checkc if it includes something" do
@@ -272,13 +339,13 @@ describe MassiveRecord::ORM::Finders::Scope do
272
339
 
273
340
  describe "#apply_finder_options" do
274
341
  it "should apply limit correctly" do
275
- subject.should_receive(:limit).with(30)
276
- subject.send :apply_finder_options, :limit => 30
342
+ scope = subject.send :apply_finder_options, :limit => 30
343
+ scope.limit_value.should eq 30
277
344
  end
278
345
 
279
346
  it "should apply select correctly" do
280
- subject.should_receive(:select).with(:foo)
281
- subject.send :apply_finder_options, :select => :foo
347
+ scope = subject.send :apply_finder_options, :select => :foo
348
+ scope.select_values.should include 'foo'
282
349
  end
283
350
 
284
351
  it "should raise unknown scope error if options is unkown" do