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.
- 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!
|