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.
- data/CHANGELOG.md +58 -2
- data/Gemfile.lock +17 -17
- data/README.md +98 -41
- data/lib/massive_record.rb +2 -1
- data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
- data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
- data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
- data/lib/massive_record/adapters/thrift/row.rb +35 -4
- data/lib/massive_record/adapters/thrift/table.rb +49 -12
- data/lib/massive_record/orm/attribute_methods.rb +77 -5
- data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
- data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
- data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
- data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
- data/lib/massive_record/orm/base.rb +62 -8
- data/lib/massive_record/orm/column.rb +7 -11
- data/lib/massive_record/orm/default_id.rb +1 -1
- data/lib/massive_record/orm/embedded.rb +66 -0
- data/lib/massive_record/orm/errors.rb +17 -0
- data/lib/massive_record/orm/finders.rb +124 -71
- data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
- data/lib/massive_record/orm/finders/scope.rb +58 -34
- data/lib/massive_record/orm/id_factory.rb +22 -105
- data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
- data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
- data/lib/massive_record/orm/identity_map.rb +256 -0
- data/lib/massive_record/orm/log_subscriber.rb +18 -0
- data/lib/massive_record/orm/observer.rb +69 -0
- data/lib/massive_record/orm/persistence.rb +47 -119
- data/lib/massive_record/orm/persistence/operations.rb +100 -0
- data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
- data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
- data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
- data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
- data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
- data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
- data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
- data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
- data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
- data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
- data/lib/massive_record/orm/query_instrumentation.rb +26 -49
- data/lib/massive_record/orm/raw_data.rb +47 -0
- data/lib/massive_record/orm/relations.rb +4 -0
- data/lib/massive_record/orm/relations/interface.rb +134 -0
- data/lib/massive_record/orm/relations/metadata.rb +58 -12
- data/lib/massive_record/orm/relations/proxy.rb +17 -12
- data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
- data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
- data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
- data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
- data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
- data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
- data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
- data/lib/massive_record/orm/schema/column_family.rb +3 -2
- data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
- data/lib/massive_record/orm/schema/field.rb +2 -0
- data/lib/massive_record/orm/schema/table_interface.rb +19 -2
- data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
- data/lib/massive_record/orm/timestamps.rb +17 -7
- data/lib/massive_record/orm/validations.rb +4 -0
- data/lib/massive_record/orm/validations/associated.rb +50 -0
- data/lib/massive_record/rails/railtie.rb +31 -0
- data/lib/massive_record/version.rb +1 -1
- data/lib/massive_record/wrapper/cell.rb +8 -1
- data/massive_record.gemspec +4 -4
- data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
- data/spec/adapter/thrift/table_find_spec.rb +14 -2
- data/spec/adapter/thrift/table_spec.rb +6 -6
- data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
- data/spec/orm/cases/attribute_methods_spec.rb +215 -22
- data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
- data/spec/orm/cases/change_id_spec.rb +62 -0
- data/spec/orm/cases/default_id_spec.rb +25 -6
- data/spec/orm/cases/default_values_spec.rb +6 -3
- data/spec/orm/cases/dirty_spec.rb +150 -102
- data/spec/orm/cases/embedded_spec.rb +250 -0
- data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
- data/spec/orm/cases/finder_scope_spec.rb +96 -29
- data/spec/orm/cases/finders_spec.rb +57 -10
- data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
- data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
- data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
- data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
- data/spec/orm/cases/log_subscriber_spec.rb +15 -2
- data/spec/orm/cases/observing_spec.rb +61 -0
- data/spec/orm/cases/persistence_spec.rb +151 -60
- data/spec/orm/cases/raw_data_spec.rb +58 -0
- data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
- data/spec/orm/cases/table_spec.rb +3 -3
- data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
- data/spec/orm/cases/timestamps_spec.rb +23 -109
- data/spec/orm/cases/validation_spec.rb +9 -0
- data/spec/orm/models/address.rb +5 -1
- data/spec/orm/models/address_with_timestamp.rb +12 -0
- data/spec/orm/models/car.rb +5 -0
- data/spec/orm/models/person.rb +13 -1
- data/spec/orm/models/person_with_timestamp.rb +4 -2
- data/spec/orm/models/test_class.rb +1 -0
- data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
- data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
- data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
- data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
- data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
- data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
- data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
- data/spec/orm/persistence/operations/insert_spec.rb +31 -0
- data/spec/orm/persistence/operations/reload_spec.rb +48 -0
- data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
- data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
- data/spec/orm/persistence/operations/update_spec.rb +25 -0
- data/spec/orm/persistence/operations_spec.rb +58 -0
- data/spec/orm/relations/interface_spec.rb +188 -0
- data/spec/orm/relations/metadata_spec.rb +92 -15
- data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
- data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
- data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
- data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
- data/spec/orm/schema/column_family_spec.rb +21 -0
- data/spec/orm/schema/embedded_interface_spec.rb +181 -0
- data/spec/orm/schema/field_spec.rb +7 -0
- data/spec/orm/schema/table_interface_spec.rb +31 -1
- data/spec/shared/orm/id_factories.rb +44 -0
- data/spec/shared/orm/model_with_timestamps.rb +132 -0
- data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
- data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
- data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
- data/spec/shared/orm/relations/proxy.rb +9 -2
- data/spec/spec_helper.rb +9 -0
- data/spec/support/mock_massive_record_connection.rb +2 -1
- metadata +106 -21
- data/spec/orm/cases/column_spec.rb +0 -49
- data/spec/orm/cases/id_factory_spec.rb +0 -92
- data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MassiveRecord::ORM::IdentityMap
|
4
|
+
describe Middleware do
|
5
|
+
before do
|
6
|
+
@status_before = MassiveRecord::ORM::IdentityMap.enabled?
|
7
|
+
MassiveRecord::ORM::IdentityMap.enabled = false
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
MassiveRecord::ORM::IdentityMap.enabled = @status_before
|
12
|
+
MassiveRecord::ORM::IdentityMap.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
it "delegates" do
|
17
|
+
called = false
|
18
|
+
mw = Middleware.new lambda { |env|
|
19
|
+
called = true
|
20
|
+
}
|
21
|
+
mw.call({})
|
22
|
+
|
23
|
+
called.should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "is enabled during delegation" do
|
27
|
+
mw = Middleware.new lambda { |env|
|
28
|
+
MassiveRecord::ORM::IdentityMap.should be_enabled
|
29
|
+
}
|
30
|
+
mw.call({})
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class Enum < Struct.new(:iter)
|
35
|
+
def each(&b)
|
36
|
+
iter.call(&b)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
it "is enabled during body each" do
|
42
|
+
mw = Middleware.new lambda { |env|
|
43
|
+
[200, {}, Enum.new(lambda { |&b|
|
44
|
+
MassiveRecord::ORM::IdentityMap.should be_enabled
|
45
|
+
b.call "hello"
|
46
|
+
})]
|
47
|
+
}
|
48
|
+
|
49
|
+
body = mw.call({}).last
|
50
|
+
body.each { |x| x.should eq "hello" }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "disables after close" do
|
54
|
+
mw = Middleware.new lambda { |env| [200, {}, []] }
|
55
|
+
body = mw.call({}).last
|
56
|
+
MassiveRecord::ORM::IdentityMap.should be_enabled
|
57
|
+
body.close
|
58
|
+
MassiveRecord::ORM::IdentityMap.should_not be_enabled
|
59
|
+
end
|
60
|
+
|
61
|
+
it "is cleared after close" do
|
62
|
+
mw = Middleware.new lambda { |env| [200, {}, []] }
|
63
|
+
body = mw.call({}).last
|
64
|
+
|
65
|
+
|
66
|
+
MassiveRecord::ORM::IdentityMap.send(:repository)['class'] = 'record'
|
67
|
+
MassiveRecord::ORM::IdentityMap.send(:repository).should_not be_empty
|
68
|
+
|
69
|
+
body.close
|
70
|
+
MassiveRecord::ORM::IdentityMap.send(:repository).should be_empty
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -57,13 +57,26 @@ describe "log subscriber" do
|
|
57
57
|
it "should have some clue written that it is first" do
|
58
58
|
Person.first
|
59
59
|
wait
|
60
|
-
subject.logged(:debug).first.should include "
|
60
|
+
subject.logged(:debug).first.should include ":limit=>1"
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should have one log when doing find" do
|
64
64
|
Person.find("dummy") rescue nil
|
65
65
|
wait
|
66
|
-
subject.logged(:debug).first.should include 'options: ["dummy",
|
66
|
+
subject.logged(:debug).first.should include 'options: ["dummy",'
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
it "includes information about loading from identity map" do
|
73
|
+
MassiveRecord::ORM::IdentityMap.use do
|
74
|
+
person = Person.create! "ID1", :name => "Name", :age => 20
|
75
|
+
Person.find(person.id)
|
76
|
+
|
77
|
+
wait
|
78
|
+
subject.logged(:debug).second.should match /Person.+?loaded from identity map/
|
79
|
+
end
|
67
80
|
end
|
68
81
|
end
|
69
82
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class PersonObserver < MassiveRecord::ORM::Observer
|
5
|
+
[:after_create].each do |observer|
|
6
|
+
define_method observer do |person|
|
7
|
+
send("calls_to_#{observer}") << person
|
8
|
+
end
|
9
|
+
|
10
|
+
define_method "calls_to_#{observer}" do
|
11
|
+
instance_variable_get("@calls_to_#{observer}") or
|
12
|
+
instance_variable_set("@calls_to_#{observer}", [])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class AuditObserver < MassiveRecord::ORM::Observer
|
18
|
+
observe :test_class
|
19
|
+
|
20
|
+
def changes_log
|
21
|
+
@changes_log ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_save(record)
|
25
|
+
changes_log << record.changes
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "Observers" do
|
31
|
+
include SetUpHbaseConnectionBeforeAll
|
32
|
+
include SetTableNamesToTestTable
|
33
|
+
|
34
|
+
context "when having an implicit target" do
|
35
|
+
subject { PersonObserver.instance }
|
36
|
+
before { subject } # Tap to initialize observer
|
37
|
+
|
38
|
+
it "calls after_save on observer" do
|
39
|
+
person_1 = Person.create! name: "Thorbjorn Hermansen", age: 30
|
40
|
+
person_2 = Person.create! name: "Thorbjorn Hermansen", age: 30
|
41
|
+
|
42
|
+
subject.calls_to_after_create.should eq [person_1, person_2]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "having an explicit target" do
|
47
|
+
subject { AuditObserver.instance }
|
48
|
+
before { subject } # Tap to initialize observer
|
49
|
+
|
50
|
+
it "logs changes to the test class" do
|
51
|
+
test = TestClass.create! :foo => 'bar'
|
52
|
+
subject.changes_log.clear
|
53
|
+
|
54
|
+
test.foo = 'barbar'
|
55
|
+
changes = test.changes
|
56
|
+
test.save!
|
57
|
+
|
58
|
+
subject.changes_log.should eq [changes]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -20,6 +20,15 @@ describe "persistence" do
|
|
20
20
|
model.should be_persisted
|
21
21
|
end
|
22
22
|
|
23
|
+
it "is still a new record if saved to database failed" do
|
24
|
+
operation = mock(Object, :execute => false)
|
25
|
+
MassiveRecord::ORM::Persistence::Operations.should_receive(:insert).and_return(operation)
|
26
|
+
|
27
|
+
model = TestClass.new "id1"
|
28
|
+
model.save
|
29
|
+
model.should_not be_persisted
|
30
|
+
end
|
31
|
+
|
23
32
|
it "should be destroyed when destroyed" do
|
24
33
|
model = TestClass.new "id1"
|
25
34
|
model.save
|
@@ -34,6 +43,17 @@ describe "persistence" do
|
|
34
43
|
model.should_not be_persisted
|
35
44
|
end
|
36
45
|
|
46
|
+
it "should not be marked as destroyed if operation failed" do
|
47
|
+
operation = mock(Object, :execute => false)
|
48
|
+
MassiveRecord::ORM::Persistence::Operations.should_receive(:destroy).and_return(operation)
|
49
|
+
|
50
|
+
model = TestClass.new "id1"
|
51
|
+
model.save
|
52
|
+
model.destroy
|
53
|
+
model.should_not be_destroyed
|
54
|
+
model.should_not be_frozen
|
55
|
+
end
|
56
|
+
|
37
57
|
it "should be possible to create new objects" do
|
38
58
|
TestClass.create("id1").should be_persisted
|
39
59
|
end
|
@@ -65,6 +85,13 @@ describe "persistence" do
|
|
65
85
|
@person.name.should == original_name
|
66
86
|
end
|
67
87
|
|
88
|
+
it "should reload the raw data" do
|
89
|
+
@person.name += "_NEW"
|
90
|
+
@person.save!
|
91
|
+
@person.reload
|
92
|
+
@person.raw_data.should eq Person.find("ID1").raw_data
|
93
|
+
end
|
94
|
+
|
68
95
|
it "should not be considered changed after reload" do
|
69
96
|
original_name = @person.name
|
70
97
|
@person.name = original_name + original_name
|
@@ -81,40 +108,7 @@ describe "persistence" do
|
|
81
108
|
Person.new.reload
|
82
109
|
end
|
83
110
|
end
|
84
|
-
|
85
|
-
|
86
|
-
describe "#row_for_record" do
|
87
|
-
include MockMassiveRecordConnection
|
88
|
-
|
89
|
-
it "should raise error if id is not set" do
|
90
|
-
lambda { Person.new.send(:row_for_record) }.should raise_error MassiveRecord::ORM::IdMissing
|
91
|
-
end
|
92
|
-
|
93
|
-
it "should return a row with id set" do
|
94
|
-
Person.new("foo").send(:row_for_record).id.should == "foo"
|
95
|
-
end
|
96
|
-
|
97
|
-
it "should return a row with table set" do
|
98
|
-
Person.new("foo").send(:row_for_record).table.should == Person.table
|
99
|
-
end
|
100
|
-
end
|
101
111
|
|
102
|
-
describe "#attributes_to_row_values_hash" do
|
103
|
-
before do
|
104
|
-
@person = Person.new("new_id", :name => "Vincent", :points => 15)
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should include the 'pts' field in the database which has 'points' as an alias" do
|
108
|
-
@person.send(:attributes_to_row_values_hash)["base"].keys.should include("pts")
|
109
|
-
@person.send(:attributes_to_row_values_hash)["base"].keys.should_not include("points")
|
110
|
-
end
|
111
|
-
|
112
|
-
it "should include integer value, even if it is set as string" do
|
113
|
-
@person.age = "20"
|
114
|
-
@person.send(:attributes_to_row_values_hash)["info"]["age"].should == 20
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
112
|
|
119
113
|
describe "update attribute" do
|
120
114
|
describe "dry run" do
|
@@ -168,7 +162,7 @@ describe "persistence" do
|
|
168
162
|
|
169
163
|
it "should delegate save to update if its a persisted record" do
|
170
164
|
person = Person.new '14', :name => "Bob", :age => 33
|
171
|
-
person.should_receive(:new_record?).and_return(false)
|
165
|
+
person.should_receive(:new_record?).any_number_of_times.and_return(false)
|
172
166
|
person.should_receive(:update)
|
173
167
|
person.save
|
174
168
|
end
|
@@ -232,6 +226,15 @@ describe "persistence" do
|
|
232
226
|
person_from_db.should == person
|
233
227
|
person_from_db.name.should == "Thorbjorn"
|
234
228
|
end
|
229
|
+
|
230
|
+
it "creates persists embedded documents" do
|
231
|
+
person = Person.new "new_id", :name => "Thorbjorn", :age => "22"
|
232
|
+
address = Address.new "address-1", :street => "Asker", :number => 1
|
233
|
+
person.addresses << address
|
234
|
+
person.save!
|
235
|
+
person_from_db = Person.find(person.id)
|
236
|
+
person_from_db.addresses.should eq [address]
|
237
|
+
end
|
235
238
|
end
|
236
239
|
|
237
240
|
it "raises an error if id already exists" do
|
@@ -275,14 +278,34 @@ describe "persistence" do
|
|
275
278
|
end
|
276
279
|
|
277
280
|
it "should only include changed attributes" do
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
+
MassiveRecord::ORM::Persistence::Operations.should_receive(:update).with(
|
282
|
+
@person, hash_including(:attribute_names_to_update => ["positive_as_default", "name"])
|
283
|
+
).and_return(mock(Object, :execute => true))
|
284
|
+
|
281
285
|
|
282
286
|
@person.name = @new_name
|
283
287
|
@person.save
|
284
288
|
end
|
285
289
|
|
290
|
+
it "should include changed attributes for embedded objects" do
|
291
|
+
MassiveRecord::ORM::Persistence::Operations.should_receive(:update).with(
|
292
|
+
@person, hash_including(:attribute_names_to_update => ["positive_as_default", "name", "addresses"])
|
293
|
+
).and_return(mock(Object, :execute => true))
|
294
|
+
|
295
|
+
# Makes the reload raw data do nothing. Reason for this is as follows:
|
296
|
+
# We are stubbing out the update operaitons, thus no address are being
|
297
|
+
# inserted to the database for this person.
|
298
|
+
#
|
299
|
+
# The reload_raw_data does a find with select on addresses column family only.
|
300
|
+
# When that is being done, and no data is found it will return nil back (Thrift
|
301
|
+
# api does this). This will in turn result in a record not found error, which is
|
302
|
+
# kinda not what we want.
|
303
|
+
@person.addresses.should_receive(:reload_raw_data).any_number_of_times
|
304
|
+
|
305
|
+
@person.name = @new_name
|
306
|
+
@person.addresses << Address.new("id1", :street => "foo")
|
307
|
+
end
|
308
|
+
|
286
309
|
it "should persist the changes" do
|
287
310
|
@person.name = @new_name
|
288
311
|
@person.save
|
@@ -290,10 +313,29 @@ describe "persistence" do
|
|
290
313
|
Person.find(@person.id).name.should == @new_name
|
291
314
|
end
|
292
315
|
|
316
|
+
it "persists changes in embedded documents" do
|
317
|
+
address = Address.new "address-1", :street => "Asker", :number => 1
|
318
|
+
@person.addresses << address
|
319
|
+
@person.save!
|
320
|
+
|
321
|
+
@person_from_db = Person.find(@person.id)
|
322
|
+
@person_from_db.addresses[0].street = "Heggedal"
|
323
|
+
@person_from_db.save!
|
324
|
+
|
325
|
+
@person_from_db = Person.find(@person.id)
|
326
|
+
@person_from_db.addresses[0].street.should eq "Heggedal"
|
327
|
+
end
|
328
|
+
|
293
329
|
it "should not have any changes after save" do
|
294
330
|
@person.name = @new_name
|
295
331
|
@person.save
|
296
|
-
@person.should_not be_changed
|
332
|
+
@person.should_not be_changed
|
333
|
+
end
|
334
|
+
|
335
|
+
it "has no changes after an embedded object is added and saved" do
|
336
|
+
@person.addresses << Address.new("address-1", :street => "Asker", :number => 1)
|
337
|
+
@person.save
|
338
|
+
@person.should_not be_changed
|
297
339
|
end
|
298
340
|
|
299
341
|
it "should raise error if column familiy needed does not exist" do
|
@@ -303,9 +345,7 @@ describe "persistence" do
|
|
303
345
|
end
|
304
346
|
end
|
305
347
|
|
306
|
-
@person = Person.find(@person.id)
|
307
|
-
@person.new = "new"
|
308
|
-
lambda { @person.save }.should raise_error MassiveRecord::ORM::ColumnFamiliesMissingError
|
348
|
+
expect { @person = Person.find(@person.id) }.to raise_error MassiveRecord::ORM::ColumnFamiliesMissingError
|
309
349
|
|
310
350
|
# Clen up the inserted column family above
|
311
351
|
# TODO Might want to wrap this inside of the column families object?
|
@@ -322,40 +362,44 @@ describe "persistence" do
|
|
322
362
|
describe "dry run" do
|
323
363
|
include MockMassiveRecordConnection
|
324
364
|
|
365
|
+
let(:person) { Person.new "id1" }
|
366
|
+
let(:operation) { MassiveRecord::ORM::Persistence::Operations::Destroy.new(person) }
|
367
|
+
|
325
368
|
before do
|
326
|
-
|
327
|
-
|
328
|
-
@row = MassiveRecord::Wrapper::Row.new({:id => @person.id, :table => @person.class.table})
|
329
|
-
@person.should_receive(:row_for_record).and_return(@row)
|
369
|
+
person.stub(:new_record?).and_return(false)
|
370
|
+
MassiveRecord::ORM::Persistence::Operations.stub(:destroy).and_return operation
|
330
371
|
end
|
331
372
|
|
332
373
|
|
333
374
|
it "should not be destroyed if wrapper returns false" do
|
334
|
-
|
335
|
-
|
336
|
-
|
375
|
+
operation.should_receive(:execute).and_return false
|
376
|
+
person.destroy
|
377
|
+
person.should_not be_destroyed
|
337
378
|
end
|
338
379
|
|
339
380
|
it "should be destroyed if wrapper returns true" do
|
340
|
-
|
341
|
-
|
342
|
-
|
381
|
+
person.destroy
|
382
|
+
person.should be_destroyed
|
383
|
+
end
|
384
|
+
|
385
|
+
it "returns destroyed record" do
|
386
|
+
person.destroy.should eq person
|
343
387
|
end
|
344
388
|
|
345
389
|
it "should be frozen after destroy" do
|
346
|
-
|
347
|
-
|
390
|
+
person.destroy
|
391
|
+
person.should be_frozen
|
348
392
|
end
|
349
393
|
|
350
394
|
it "should be frozen after delete" do
|
351
|
-
|
352
|
-
|
395
|
+
person.delete
|
396
|
+
person.should be_frozen
|
353
397
|
end
|
354
398
|
|
355
399
|
it "should not be frozen if wrapper returns false" do
|
356
|
-
|
357
|
-
|
358
|
-
|
400
|
+
operation.should_receive(:execute).and_return false
|
401
|
+
person.destroy
|
402
|
+
person.should_not be_frozen
|
359
403
|
end
|
360
404
|
end
|
361
405
|
|
@@ -461,6 +505,10 @@ describe "persistence" do
|
|
461
505
|
|
462
506
|
|
463
507
|
describe "atomic increments" do
|
508
|
+
it "raises error if called on non integer fields" do
|
509
|
+
lambda { @person.atomic_increment!(:name) }.should raise_error MassiveRecord::ORM::NotNumericalFieldError
|
510
|
+
end
|
511
|
+
|
464
512
|
it "should be able to do atomic increments on existing objects" do
|
465
513
|
@person.atomic_increment!(:age).should == 30
|
466
514
|
@person.age.should == 30
|
@@ -498,6 +546,49 @@ describe "persistence" do
|
|
498
546
|
MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = old_ensure
|
499
547
|
end
|
500
548
|
end
|
549
|
+
|
550
|
+
describe "atomic decrements" do
|
551
|
+
it "raises error if called on non integer fields" do
|
552
|
+
lambda { @person.atomic_decrement!(:name) }.should raise_error MassiveRecord::ORM::NotNumericalFieldError
|
553
|
+
end
|
554
|
+
|
555
|
+
it "should be able to do atomic decrements on existing objects" do
|
556
|
+
@person.atomic_decrement!(:age).should == 28
|
557
|
+
@person.age.should == 28
|
558
|
+
@person.reload
|
559
|
+
@person.age.should == 28
|
560
|
+
end
|
561
|
+
|
562
|
+
it "is a persisted record after decrementation" do
|
563
|
+
person = Person.new('id2')
|
564
|
+
person.atomic_decrement!(:age).should eq -1
|
565
|
+
person.should be_persisted
|
566
|
+
end
|
567
|
+
|
568
|
+
it "decrementss correctly when value is '1'" do
|
569
|
+
old_ensure = MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings
|
570
|
+
MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = true
|
571
|
+
|
572
|
+
person = Person.new('id2')
|
573
|
+
person.atomic_increment!(:age).should eq 1
|
574
|
+
|
575
|
+
atomic_field = Person.attributes_schema['age']
|
576
|
+
|
577
|
+
# Enter incompatible data, number as string.
|
578
|
+
Person.table.find("id2").tap do |row|
|
579
|
+
row.update_column(
|
580
|
+
atomic_field.column_family.name,
|
581
|
+
atomic_field.name,
|
582
|
+
MassiveRecord::ORM::Base.coder.dump(1)
|
583
|
+
)
|
584
|
+
row.save
|
585
|
+
end
|
586
|
+
|
587
|
+
person.atomic_decrement!(:age).should eq 0
|
588
|
+
|
589
|
+
MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = old_ensure
|
590
|
+
end
|
591
|
+
end
|
501
592
|
end
|
502
593
|
end
|
503
594
|
|
@@ -599,12 +690,12 @@ describe "persistence" do
|
|
599
690
|
:name => "Thorbjorn",
|
600
691
|
:age => 22,
|
601
692
|
:points => 1,
|
602
|
-
:
|
693
|
+
:dictionary => {'home' => 'Here'},
|
603
694
|
:status => true
|
604
695
|
})
|
605
696
|
end
|
606
697
|
|
607
|
-
%w(points
|
698
|
+
%w(points dictionary status).each do |attr|
|
608
699
|
it "removes the cell from hbase when #{attr} is set to nil" do
|
609
700
|
subject[attr] = nil
|
610
701
|
subject.save!
|