massive_record 0.1.0

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 (81) hide show
  1. data/.autotest +15 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +38 -0
  6. data/Manifest +24 -0
  7. data/README.md +225 -0
  8. data/Rakefile +16 -0
  9. data/TODO.md +8 -0
  10. data/autotest/discover.rb +1 -0
  11. data/lib/massive_record.rb +18 -0
  12. data/lib/massive_record/exceptions.rb +11 -0
  13. data/lib/massive_record/orm/attribute_methods.rb +61 -0
  14. data/lib/massive_record/orm/attribute_methods/dirty.rb +80 -0
  15. data/lib/massive_record/orm/attribute_methods/read.rb +23 -0
  16. data/lib/massive_record/orm/attribute_methods/write.rb +24 -0
  17. data/lib/massive_record/orm/base.rb +176 -0
  18. data/lib/massive_record/orm/callbacks.rb +52 -0
  19. data/lib/massive_record/orm/column.rb +18 -0
  20. data/lib/massive_record/orm/config.rb +47 -0
  21. data/lib/massive_record/orm/errors.rb +47 -0
  22. data/lib/massive_record/orm/finders.rb +125 -0
  23. data/lib/massive_record/orm/id_factory.rb +133 -0
  24. data/lib/massive_record/orm/persistence.rb +199 -0
  25. data/lib/massive_record/orm/schema.rb +4 -0
  26. data/lib/massive_record/orm/schema/column_families.rb +48 -0
  27. data/lib/massive_record/orm/schema/column_family.rb +102 -0
  28. data/lib/massive_record/orm/schema/column_interface.rb +91 -0
  29. data/lib/massive_record/orm/schema/common_interface.rb +48 -0
  30. data/lib/massive_record/orm/schema/field.rb +128 -0
  31. data/lib/massive_record/orm/schema/fields.rb +37 -0
  32. data/lib/massive_record/orm/schema/table_interface.rb +96 -0
  33. data/lib/massive_record/orm/table.rb +9 -0
  34. data/lib/massive_record/orm/validations.rb +52 -0
  35. data/lib/massive_record/spec/support/simple_database_cleaner.rb +52 -0
  36. data/lib/massive_record/thrift/hbase.rb +2307 -0
  37. data/lib/massive_record/thrift/hbase_constants.rb +14 -0
  38. data/lib/massive_record/thrift/hbase_types.rb +225 -0
  39. data/lib/massive_record/version.rb +3 -0
  40. data/lib/massive_record/wrapper/base.rb +28 -0
  41. data/lib/massive_record/wrapper/cell.rb +45 -0
  42. data/lib/massive_record/wrapper/column_families_collection.rb +19 -0
  43. data/lib/massive_record/wrapper/column_family.rb +22 -0
  44. data/lib/massive_record/wrapper/connection.rb +71 -0
  45. data/lib/massive_record/wrapper/row.rb +170 -0
  46. data/lib/massive_record/wrapper/scanner.rb +50 -0
  47. data/lib/massive_record/wrapper/table.rb +148 -0
  48. data/lib/massive_record/wrapper/tables_collection.rb +13 -0
  49. data/massive_record.gemspec +28 -0
  50. data/spec/config.yml.example +4 -0
  51. data/spec/orm/cases/attribute_methods_spec.rb +47 -0
  52. data/spec/orm/cases/auto_generate_id_spec.rb +54 -0
  53. data/spec/orm/cases/base_spec.rb +176 -0
  54. data/spec/orm/cases/callbacks_spec.rb +309 -0
  55. data/spec/orm/cases/column_spec.rb +49 -0
  56. data/spec/orm/cases/config_spec.rb +103 -0
  57. data/spec/orm/cases/dirty_spec.rb +129 -0
  58. data/spec/orm/cases/encoding_spec.rb +49 -0
  59. data/spec/orm/cases/finders_spec.rb +208 -0
  60. data/spec/orm/cases/hbase/connection_spec.rb +13 -0
  61. data/spec/orm/cases/i18n_spec.rb +32 -0
  62. data/spec/orm/cases/id_factory_spec.rb +75 -0
  63. data/spec/orm/cases/persistence_spec.rb +479 -0
  64. data/spec/orm/cases/table_spec.rb +81 -0
  65. data/spec/orm/cases/validation_spec.rb +92 -0
  66. data/spec/orm/models/address.rb +7 -0
  67. data/spec/orm/models/person.rb +15 -0
  68. data/spec/orm/models/test_class.rb +5 -0
  69. data/spec/orm/schema/column_families_spec.rb +186 -0
  70. data/spec/orm/schema/column_family_spec.rb +131 -0
  71. data/spec/orm/schema/column_interface_spec.rb +115 -0
  72. data/spec/orm/schema/field_spec.rb +196 -0
  73. data/spec/orm/schema/fields_spec.rb +126 -0
  74. data/spec/orm/schema/table_interface_spec.rb +171 -0
  75. data/spec/spec_helper.rb +15 -0
  76. data/spec/support/connection_helpers.rb +76 -0
  77. data/spec/support/mock_massive_record_connection.rb +80 -0
  78. data/spec/thrift/cases/encoding_spec.rb +48 -0
  79. data/spec/wrapper/cases/connection_spec.rb +53 -0
  80. data/spec/wrapper/cases/table_spec.rb +231 -0
  81. metadata +228 -0
@@ -0,0 +1,479 @@
1
+ require 'spec_helper'
2
+ require 'orm/models/test_class'
3
+ require 'active_support/secure_random'
4
+
5
+ describe "persistence" do
6
+ describe "state" do
7
+ include MockMassiveRecordConnection
8
+
9
+ it "should be a new record when calling new" do
10
+ TestClass.new.should be_new_record
11
+ end
12
+
13
+ it "should not be persisted when new record" do
14
+ TestClass.new.should_not be_persisted
15
+ end
16
+
17
+ it "should be persisted if saved" do
18
+ model = TestClass.new :id => "id1"
19
+ model.save
20
+ model.should be_persisted
21
+ end
22
+
23
+ it "should be destroyed when destroyed" do
24
+ model = TestClass.new :id => "id1"
25
+ model.save
26
+ model.destroy
27
+ model.should be_destroyed
28
+ end
29
+
30
+ it "should not be persisted if destroyed" do
31
+ model = TestClass.new :id => "id1"
32
+ model.save
33
+ model.destroy
34
+ model.should_not be_persisted
35
+ end
36
+
37
+ it "should be possible to create new objects" do
38
+ TestClass.create(:id => "id1").should be_persisted
39
+ end
40
+
41
+ it "should raise an error if validation fails on save!" do
42
+ model = TestClass.new
43
+ model.should_receive(:create_or_update).and_return(false)
44
+ lambda { model.save! }.should raise_error MassiveRecord::ORM::RecordNotSaved
45
+ end
46
+
47
+ it "should respond to reload" do
48
+ TestClass.new.should respond_to :reload
49
+ end
50
+ end
51
+
52
+
53
+
54
+ describe "#reload" do
55
+ include CreatePersonBeforeEach
56
+
57
+ before do
58
+ @person = Person.find("ID1")
59
+ end
60
+
61
+ it "should reload models attribute" do
62
+ original_name = @person.name
63
+ @person.name = original_name + original_name
64
+ @person.reload
65
+ @person.name.should == original_name
66
+ end
67
+
68
+ it "should not be considered changed after reload" do
69
+ original_name = @person.name
70
+ @person.name = original_name + original_name
71
+ @person.reload
72
+ @person.should_not be_changed
73
+ end
74
+
75
+ it "should return self" do
76
+ @person.reload.should == @person
77
+ end
78
+
79
+ it "should raise error on new record" do
80
+ lambda { Person.new.reload }.should raise_error MassiveRecord::ORM::RecordNotFound
81
+ end
82
+ end
83
+
84
+
85
+ describe "#row_for_record" do
86
+ include MockMassiveRecordConnection
87
+
88
+ it "should raise error if id is not set" do
89
+ lambda { Person.new.send(:row_for_record) }.should raise_error MassiveRecord::ORM::IdMissing
90
+ end
91
+
92
+ it "should return a row with id set" do
93
+ Person.new({:id => "foo"}).send(:row_for_record).id.should == "foo"
94
+ end
95
+
96
+ it "should return a row with table set" do
97
+ Person.new({:id => "foo"}).send(:row_for_record).table.should == Person.table
98
+ end
99
+ end
100
+
101
+ describe "#attributes_to_row_values_hash" do
102
+ before do
103
+ @person = Person.new({ :id => "new_id", :name => "Vincent", :points => 15 })
104
+ end
105
+
106
+ it "should include the 'pts' field in the database which has 'points' as an alias" do
107
+ @person.send(:attributes_to_row_values_hash)["info"].keys.should include("pts")
108
+ @person.send(:attributes_to_row_values_hash)["info"].keys.should_not include("points")
109
+ end
110
+ end
111
+
112
+
113
+ describe "update attribute" do
114
+ describe "dry run" do
115
+ include MockMassiveRecordConnection
116
+
117
+ before do
118
+ @person = Person.create! :id => "new_id", :name => "Thorbjorn", :age => "22"
119
+ end
120
+
121
+ it "should update given attriubte when valid" do
122
+ @person.update_attribute(:name, "new").should be_true
123
+ end
124
+
125
+ it "should update given attribute when invalid" do
126
+ @person.update_attribute(:name, nil).should be_true
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "update attributes" do
132
+ describe "dry run" do
133
+ include MockMassiveRecordConnection
134
+
135
+ before do
136
+ @person = Person.create! :id => "new_id", :name => "Thorbjorn", :age => "22"
137
+ end
138
+
139
+ it "should update given attriubtes when valid" do
140
+ @person.update_attributes(:name => "new", :age => "66").should be_true
141
+ end
142
+
143
+ it "should not update given attributes when one is invalid" do
144
+ @person.update_attributes(:name => nil, :age => "66").should be_false
145
+ end
146
+
147
+ it "should raise error when called with a bang" do
148
+ lambda { @person.update_attributes!(:name => nil, :age => "66") }.should raise_error MassiveRecord::ORM::RecordInvalid
149
+ end
150
+ end
151
+ end
152
+
153
+ describe "save" do
154
+ describe "dry test" do
155
+ include MockMassiveRecordConnection
156
+
157
+ it "should delegate save to create if its a new record" do
158
+ person = Person.new :name => "Bob", :age => 33
159
+ person.should_receive(:create)
160
+ person.save
161
+ end
162
+
163
+ it "should delegate save to update if its a persisted record" do
164
+ person = Person.new :id => 14, :name => "Bob", :age => 33
165
+ person.should_receive(:new_record?).and_return(false)
166
+ person.should_receive(:update)
167
+ person.save
168
+ end
169
+ end
170
+
171
+ describe "database test" do
172
+ include SetUpHbaseConnectionBeforeAll
173
+
174
+ describe "create" do
175
+ describe "when table does not exists" do
176
+ before do
177
+ @new_class = "Person_new_#{ActiveSupport::SecureRandom.hex(5)}"
178
+ @new_class = Object.const_set(@new_class, Class.new(MassiveRecord::ORM::Table))
179
+
180
+ @new_class.instance_eval do
181
+ column_family :bar do
182
+ field :foo
183
+ end
184
+ end
185
+
186
+ @new_instance = @new_class.new :id => "id_of_foo", :foo => "bar"
187
+ end
188
+
189
+ after do
190
+ @new_class.table.destroy if @connection.tables.include? @new_class.table_name
191
+ end
192
+
193
+
194
+ it "it should not exists" do
195
+ @connection.tables.should_not include @new_class.table_name
196
+ end
197
+
198
+ it "should create the table" do
199
+ @new_instance.save
200
+ @connection.tables.should include @new_class.table_name
201
+ end
202
+
203
+ it "should create correct column families" do
204
+ @new_instance.save
205
+ @new_class.table.fetch_column_families.collect(&:name).should == ["bar"]
206
+ end
207
+
208
+ it "should store the new instance" do
209
+ @new_instance.save
210
+ @new_class.find(@new_instance.id).should == @new_instance
211
+ end
212
+ end
213
+
214
+
215
+ describe "when table exists" do
216
+ include CreatePersonBeforeEach
217
+
218
+ it "should store (create) new objects" do
219
+ person = Person.new :id => "new_id", :name => "Thorbjorn", :age => "22"
220
+ person.save!
221
+ person_from_db = Person.find(person.id)
222
+ person_from_db.should == person
223
+ person_from_db.name.should == "Thorbjorn"
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "update" do
229
+ include CreatePersonBeforeEach
230
+
231
+ before do
232
+ @person = Person.find("ID1")
233
+ @original_name = @person.name
234
+ @new_name = @original_name + @original_name
235
+ end
236
+
237
+ it "should not ask for row for record when no changes have been made (update is done through this object)" do
238
+ @person.should_not_receive(:row_for_record)
239
+ @person.save
240
+ end
241
+
242
+ it "should only include changed attributes" do
243
+ row = MassiveRecord::Wrapper::Row.new({:id => @person.id, :table => @person.class.table})
244
+ row.should_receive(:values=).with({"info" => {"name" => @new_name}})
245
+ @person.should_receive(:row_for_record).and_return(row)
246
+
247
+ @person.name = @new_name
248
+ @person.save
249
+ end
250
+
251
+ it "should persist the changes" do
252
+ @person.name = @new_name
253
+ @person.save
254
+
255
+ Person.find(@person.id).name.should == @new_name
256
+ end
257
+
258
+ it "should not have any changes after save" do
259
+ @person.name = @new_name
260
+ @person.save
261
+ @person.should_not be_changed # ..as it has been stored..
262
+ end
263
+
264
+ it "should raise error if column familiy needed does not exist" do
265
+ Person.instance_eval do
266
+ column_family :new do
267
+ field :new
268
+ end
269
+ end
270
+
271
+ @person = Person.find(@person.id)
272
+ @person.new = "new"
273
+ lambda { @person.save }.should raise_error MassiveRecord::ORM::ColumnFamiliesMissingError
274
+
275
+ # Clen up the inserted column family above
276
+ # TODO Might want to wrap this inside of the column families object?
277
+ Person.instance_eval do
278
+ column_families.delete_if { |family| family.name == "new" }
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+
286
+ describe "remove record" do
287
+ describe "dry run" do
288
+ include MockMassiveRecordConnection
289
+
290
+ before do
291
+ @person = Person.new :id => "id1"
292
+ @person.stub!(:new_record?).and_return(false)
293
+ @row = MassiveRecord::Wrapper::Row.new({:id => @person.id, :table => @person.class.table})
294
+ @person.should_receive(:row_for_record).and_return(@row)
295
+ end
296
+
297
+
298
+ it "should not be destroyed if wrapper returns false" do
299
+ @row.should_receive(:destroy).and_return(false)
300
+ @person.destroy
301
+ @person.should_not be_destroyed
302
+ end
303
+
304
+ it "should be destroyed if wrapper returns true" do
305
+ @row.should_receive(:destroy).and_return(true)
306
+ @person.destroy
307
+ @person.should be_destroyed
308
+ end
309
+
310
+ it "should be frozen after destroy" do
311
+ @person.destroy
312
+ @person.should be_frozen
313
+ end
314
+
315
+ it "should be frozen after delete" do
316
+ @person.delete
317
+ @person.should be_frozen
318
+ end
319
+
320
+ it "should not be frozen if wrapper returns false" do
321
+ @row.should_receive(:destroy).and_return(false)
322
+ @person.destroy
323
+ @person.should_not be_frozen
324
+ end
325
+ end
326
+
327
+ describe "database test" do
328
+ include SetUpHbaseConnectionBeforeAll
329
+ include SetTableNamesToTestTable
330
+
331
+ before do
332
+ @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
333
+ end
334
+
335
+ it "should be removed by destroy" do
336
+ @person.destroy
337
+ @person.should be_destroyed
338
+ Person.all.length.should == 0
339
+ end
340
+
341
+ it "should be removed by delete" do
342
+ @person.delete
343
+ @person.should be_destroyed
344
+ Person.all.length.should == 0
345
+ end
346
+
347
+ describe "#destroy_all" do
348
+ it "should remove all when calling remove_all" do
349
+ Person.create! :id => "id2", :name => "Going to die :-(", :age => 99
350
+ Person.destroy_all
351
+ Person.all.length.should == 0
352
+ end
353
+
354
+ it "should return an array of all removed objects" do
355
+ Person.destroy_all.should == [@person]
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+
362
+
363
+ describe "increment" do
364
+ describe "dry" do
365
+ include MockMassiveRecordConnection
366
+
367
+ before do
368
+ @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
369
+ end
370
+
371
+ it "should being able to increment age" do
372
+ @person.increment(:age)
373
+ @person.age.should == 30
374
+ end
375
+
376
+ it "should be able to increment age by given value" do
377
+ @person.increment(:age, 2)
378
+ @person.age.should == 31
379
+ end
380
+
381
+ it "should return object self" do
382
+ @person.increment(:age, 2).should == @person
383
+ end
384
+
385
+ it "should support increment values which currently are nil" do
386
+ @person.age = nil
387
+ @person.increment(:age)
388
+ @person.age.should == 1
389
+ end
390
+
391
+ it "should complain if users tries to increment non-integer fields" do
392
+ @person.name = nil
393
+ lambda { @person.increment(:name) }.should raise_error MassiveRecord::ORM::NotNumericalFieldError
394
+ end
395
+ end
396
+
397
+ describe "with database" do
398
+ include SetUpHbaseConnectionBeforeAll
399
+ include SetTableNamesToTestTable
400
+
401
+ before do
402
+ @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
403
+ end
404
+
405
+ it "should delegate it's call to increment" do
406
+ @person.should_receive(:increment).with(:age, 1).and_return(@person)
407
+ @person.increment! :age
408
+ end
409
+
410
+ it "should update object in database when called with a bang" do
411
+ @person.increment! :age
412
+ @person.reload
413
+ @person.age.should == 30
414
+ end
415
+
416
+ it "should be able to do atomic increments" do
417
+ @person.atomic_increment!(:age).should == 30
418
+ @person.age.should == 30
419
+ @person.reload
420
+ @person.age.should == 30
421
+ end
422
+ end
423
+ end
424
+
425
+ describe "decrement" do
426
+ describe "dry" do
427
+ include MockMassiveRecordConnection
428
+
429
+ before do
430
+ @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
431
+ end
432
+
433
+ it "should being able to decrement age" do
434
+ @person.decrement(:age)
435
+ @person.age.should == 28
436
+ end
437
+
438
+ it "should be able to decrement age by given value" do
439
+ @person.decrement(:age, 2)
440
+ @person.age.should == 27
441
+ end
442
+
443
+ it "should return object self" do
444
+ @person.decrement(:age, 2).should == @person
445
+ end
446
+
447
+ it "should support decrement values which currently are nil" do
448
+ @person.age = nil
449
+ @person.decrement(:age)
450
+ @person.age.should == -1
451
+ end
452
+
453
+ it "should complain if users tries to decrement non-integer fields" do
454
+ @person.name = nil
455
+ lambda { @person.decrement(:name) }.should raise_error MassiveRecord::ORM::NotNumericalFieldError
456
+ end
457
+ end
458
+
459
+ describe "with database" do
460
+ include SetUpHbaseConnectionBeforeAll
461
+ include SetTableNamesToTestTable
462
+
463
+ before do
464
+ @person = Person.create! :id => "id1", :name => "Thorbjorn", :age => 29
465
+ end
466
+
467
+ it "should delegate it's call to decrement" do
468
+ @person.should_receive(:decrement).with(:age, 1).and_return(@person)
469
+ @person.decrement! :age
470
+ end
471
+
472
+ it "should update object in database when called with a bang" do
473
+ @person.decrement! :age
474
+ @person.reload
475
+ @person.age.should == 28
476
+ end
477
+ end
478
+ end
479
+ end