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,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestEmbeddedInPolymorphicProxy < MassiveRecord::ORM::Relations::Proxy::EmbeddedInPolymorphic; end
|
4
|
+
|
5
|
+
describe TestEmbeddedInPolymorphicProxy do
|
6
|
+
include SetUpHbaseConnectionBeforeAll
|
7
|
+
include SetTableNamesToTestTable
|
8
|
+
|
9
|
+
let(:proxy_owner) { Address.new "address-1", :street => "Asker", :number => 1 }
|
10
|
+
let(:proxy_target) { TestClass.new "test-id-1" }
|
11
|
+
|
12
|
+
let(:metadata) { subject.metadata }
|
13
|
+
|
14
|
+
subject { proxy_owner.send(:relation_proxy, 'addressable') }
|
15
|
+
|
16
|
+
|
17
|
+
describe "generic behaviour" do
|
18
|
+
before do
|
19
|
+
# Little hack just to make one generic relation proxy test pass..
|
20
|
+
metadata.stub(:proxy_target_class).and_return TestClass
|
21
|
+
end
|
22
|
+
|
23
|
+
it_should_behave_like "relation proxy"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
describe "polymorphism" do
|
29
|
+
let(:person) { Person.new "person-id-1", :name => "Test", :age => 29 }
|
30
|
+
|
31
|
+
it "allows for polymorphism if configured for it" do
|
32
|
+
expect { proxy_owner.adressable = person }.not_to raise_error MassiveRecord::ORM::RelationTypeMismatch
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestEmbeddedInProxy < MassiveRecord::ORM::Relations::Proxy::EmbeddedIn; end
|
4
|
+
|
5
|
+
describe TestEmbeddedInProxy do
|
6
|
+
include SetUpHbaseConnectionBeforeAll
|
7
|
+
include SetTableNamesToTestTable
|
8
|
+
|
9
|
+
let(:proxy_owner) { Address.new "address-1", :street => "Asker", :number => 1 }
|
10
|
+
let(:proxy_target) { Person.new "person-id-1", :name => "Test", :age => 29 }
|
11
|
+
let(:proxy_target_2) { Person.new "person-id-2", :name => "Test", :age => 29 }
|
12
|
+
|
13
|
+
let(:metadata) { subject.metadata }
|
14
|
+
|
15
|
+
subject { proxy_owner.send(:relation_proxy, 'person') }
|
16
|
+
|
17
|
+
|
18
|
+
it_should_behave_like "relation proxy"
|
19
|
+
|
20
|
+
describe "#replace" do
|
21
|
+
context "current target being blank" do
|
22
|
+
it "adds itself to the targets embedded collection" do
|
23
|
+
subject.replace(proxy_target)
|
24
|
+
proxy_target.addresses.should include proxy_owner
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "current target existing" do
|
29
|
+
context "and target is the same as current" do
|
30
|
+
it "just push self to target once" do
|
31
|
+
proxy_target.should_receive(:addresses).twice.and_return([])
|
32
|
+
2.times { subject.replace(proxy_target) }
|
33
|
+
proxy_target.addresses.should include proxy_owner
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "and new target is different than previos" do
|
38
|
+
it "removes itself from old collection and inserts self into new" do
|
39
|
+
proxy_target.save!
|
40
|
+
proxy_target_2.save!
|
41
|
+
|
42
|
+
subject.replace(proxy_target)
|
43
|
+
subject.replace(proxy_target_2)
|
44
|
+
|
45
|
+
proxy_target.addresses.should_not include proxy_owner
|
46
|
+
proxy_target_2.addresses.should include proxy_owner
|
47
|
+
proxy_owner.should_not be_destroyed
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "raises error if inverse of does not exist" do
|
53
|
+
metadata.should_receive(:inverse_of).any_number_of_times.and_return("something_which_does_not_exist")
|
54
|
+
expect { subject.replace(proxy_target) }.to raise_error MassiveRecord::ORM::RelationMissing
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "polymorphism" do
|
59
|
+
let(:test_class) { TestClass.new }
|
60
|
+
|
61
|
+
it "raises an error if invalid type is assigned" do
|
62
|
+
expect { proxy_owner.person = test_class }.to raise_error MassiveRecord::ORM::RelationTypeMismatch
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,651 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestEmbedsManyProxy < MassiveRecord::ORM::Relations::Proxy::EmbedsMany; end
|
4
|
+
|
5
|
+
describe TestEmbedsManyProxy do
|
6
|
+
include SetUpHbaseConnectionBeforeAll
|
7
|
+
include SetTableNamesToTestTable
|
8
|
+
|
9
|
+
let(:proxy_owner) { Person.new "person-id-1", :name => "Test", :age => 29 }
|
10
|
+
let(:proxy_target) { Address.new "address-1", :street => "Asker", :number => 1 }
|
11
|
+
let(:proxy_target_2) { Address.new "address-2", :street => "Asker", :number => 2 }
|
12
|
+
let(:proxy_target_3) { Address.new "address-3", :street => "Asker", :number => 3 }
|
13
|
+
let(:metadata) { subject.metadata }
|
14
|
+
|
15
|
+
let(:raw_data) do
|
16
|
+
{
|
17
|
+
proxy_target.database_id => MassiveRecord::ORM::RawData.new(value: proxy_target.attributes_db_raw_data_hash, created_at: Time.now),
|
18
|
+
proxy_target_2.database_id => MassiveRecord::ORM::RawData.new(value: proxy_target_2.attributes_db_raw_data_hash, created_at: Time.now),
|
19
|
+
proxy_target_3.database_id => MassiveRecord::ORM::RawData.new(value: proxy_target_3.attributes_db_raw_data_hash, created_at: Time.now),
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:raw_data_transformed_ids) do
|
24
|
+
Hash[raw_data.collect do |database_id, value|
|
25
|
+
[MassiveRecord::ORM::Embedded.parse_database_id(database_id)[1], value]
|
26
|
+
end]
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
subject { proxy_owner.send(:relation_proxy, 'addresses') }
|
31
|
+
|
32
|
+
|
33
|
+
it_should_behave_like "relation proxy"
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
describe "#proxy_targets_raw" do
|
38
|
+
it "is a hash" do
|
39
|
+
subject.proxy_targets_raw.should be_instance_of Hash
|
40
|
+
end
|
41
|
+
|
42
|
+
context "proxy owner is new record" do
|
43
|
+
its(:proxy_targets_raw) { should be_empty }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "proxy owner is saved and has records" do
|
47
|
+
before do
|
48
|
+
proxy_owner.instance_variable_set(:@raw_data, {'addresses' => raw_data})
|
49
|
+
end
|
50
|
+
|
51
|
+
it "includes raw data from database" do
|
52
|
+
subject.proxy_targets_raw.should eq raw_data_transformed_ids
|
53
|
+
end
|
54
|
+
|
55
|
+
it "ignores values which keys does not seem to be parsable" do
|
56
|
+
raw_data_with_name = proxy_owner.instance_variable_get(:@raw_data)['addresses'].merge({'name' => 'Thorbjorn'})
|
57
|
+
|
58
|
+
proxy_owner.instance_variable_set(:@raw_data, {'addresses' => raw_data_with_name})
|
59
|
+
subject.proxy_targets_raw.should eq raw_data_transformed_ids
|
60
|
+
end
|
61
|
+
|
62
|
+
it "ignores values which kees seems to belong to other collections" do
|
63
|
+
raw_data_with_car = proxy_owner.instance_variable_get(:@raw_data)['addresses'].merge(
|
64
|
+
{"car#{MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR}123" => MassiveRecord::ORM::RawData.new(value: Car.new.attributes_db_raw_data_hash, created_at: Time.now)
|
65
|
+
})
|
66
|
+
|
67
|
+
proxy_owner.instance_variable_set(:@raw_data, {'addresses' => raw_data_with_car})
|
68
|
+
subject.proxy_targets_raw.should eq raw_data_transformed_ids
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#reload" do
|
74
|
+
it "forces the raw data to be reloaded from database" do
|
75
|
+
subject.should_receive(:reload_raw_data)
|
76
|
+
subject.reload
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#reload_raw_data" do
|
81
|
+
before do
|
82
|
+
subject << proxy_target
|
83
|
+
subject << proxy_target_2
|
84
|
+
end
|
85
|
+
|
86
|
+
it "loads only the raw data" do
|
87
|
+
proxy_owner.save!
|
88
|
+
proxy_owner.raw_data[metadata.store_in] = {}
|
89
|
+
subject.send(:reload_raw_data)
|
90
|
+
Hash[proxy_owner.raw_data[metadata.store_in].collect { |k,v| [k, v.to_s] }].should eq({
|
91
|
+
"address#{MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR}address-1" => "{\"street\":\"Asker\",\"number\":1,\"nice_place\":\"true\",\"postal_code\":null}",
|
92
|
+
"address#{MassiveRecord::ORM::Embedded::DATABASE_ID_SEPARATOR}address-2" => "{\"street\":\"Asker\",\"number\":2,\"nice_place\":\"true\",\"postal_code\":null}"
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
it "does nothing if proxy_owner is not persisted" do
|
97
|
+
proxy_owner.raw_data[metadata.store_in] = {}
|
98
|
+
subject.send(:reload_raw_data)
|
99
|
+
proxy_owner.raw_data[metadata.store_in].should eq({})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
describe "adding records to collection" do
|
105
|
+
[:<<, :push, :concat].each do |add_method|
|
106
|
+
describe "by ##{add_method}" do
|
107
|
+
it "includes added record in proxy target" do
|
108
|
+
subject.send add_method, proxy_target
|
109
|
+
subject.proxy_target.should include proxy_target
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns self so you can chain calls" do
|
113
|
+
subject.send(add_method, proxy_target).send(add_method, proxy_target_2)
|
114
|
+
subject.proxy_target.should include proxy_target, proxy_target_2
|
115
|
+
end
|
116
|
+
|
117
|
+
it "saves proxy owner if it is already persisted" do
|
118
|
+
proxy_owner.should_receive(:persisted?).any_number_of_times.and_return true
|
119
|
+
proxy_owner.should_receive(:save).once
|
120
|
+
subject.send add_method, proxy_target
|
121
|
+
end
|
122
|
+
|
123
|
+
it "does not save added records if owner is not persisted" do
|
124
|
+
subject.send add_method, proxy_target
|
125
|
+
proxy_target.should be_new_record
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is possible to add invalid record if parent is not persisted" do
|
129
|
+
subject.send add_method, proxy_target
|
130
|
+
subject.should include proxy_target
|
131
|
+
end
|
132
|
+
|
133
|
+
it "accepts invalid records, but does not save them" do
|
134
|
+
proxy_owner.save
|
135
|
+
proxy_target.should_receive(:valid?).and_return false
|
136
|
+
subject.send add_method, proxy_target
|
137
|
+
subject.should include proxy_target
|
138
|
+
proxy_target.should be_new_record
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
it "saves proxy target if it is a new record" do
|
143
|
+
proxy_owner.save
|
144
|
+
subject.send add_method, proxy_target
|
145
|
+
proxy_target.should be_persisted
|
146
|
+
end
|
147
|
+
|
148
|
+
it "does not add existing records" do
|
149
|
+
2.times { subject.send add_method, proxy_target }
|
150
|
+
subject.proxy_target.length.should eq 1
|
151
|
+
end
|
152
|
+
|
153
|
+
it "raises an error if there is a type mismatch" do
|
154
|
+
lambda { subject.send add_method, Person.new(:name => "Foo", :age => 2) }.should raise_error MassiveRecord::ORM::RelationTypeMismatch
|
155
|
+
end
|
156
|
+
|
157
|
+
it "sets the inverse of relation in target" do
|
158
|
+
subject.send add_method, proxy_target
|
159
|
+
proxy_target.person.should eq proxy_owner
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#destroy" do
|
166
|
+
before do
|
167
|
+
subject << proxy_target << proxy_target_2 << proxy_target_3
|
168
|
+
proxy_owner.save!
|
169
|
+
end
|
170
|
+
|
171
|
+
it "destroys one record" do
|
172
|
+
subject.destroy(proxy_target)
|
173
|
+
subject.should_not include proxy_target
|
174
|
+
end
|
175
|
+
|
176
|
+
it "destroys multiple records" do
|
177
|
+
subject.destroy(proxy_target, proxy_target_2)
|
178
|
+
subject.should_not include proxy_target, proxy_target_2
|
179
|
+
end
|
180
|
+
|
181
|
+
it "is destroyed from the database as well" do
|
182
|
+
subject.destroy(proxy_target)
|
183
|
+
subject.reload
|
184
|
+
subject.should_not include proxy_target
|
185
|
+
end
|
186
|
+
|
187
|
+
it "makes destroyed objects know about it after being destroyed" do
|
188
|
+
subject.destroy(proxy_target)
|
189
|
+
proxy_target.should be_destroyed
|
190
|
+
end
|
191
|
+
|
192
|
+
it "does not call save on proxy owner if it is not persisted" do
|
193
|
+
proxy_owner.should_receive(:persisted?).and_return false
|
194
|
+
proxy_owner.should_not_receive(:save)
|
195
|
+
subject.destroy(proxy_target)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#destroy_all" do
|
200
|
+
before do
|
201
|
+
subject << proxy_target << proxy_target_2 << proxy_target_3
|
202
|
+
proxy_owner.save!
|
203
|
+
end
|
204
|
+
|
205
|
+
it "destroys all records" do
|
206
|
+
subject.destroy_all
|
207
|
+
subject.should_not include proxy_target, proxy_target_2, proxy_target_3
|
208
|
+
end
|
209
|
+
|
210
|
+
it "returns all destroyed records" do
|
211
|
+
removed = subject.destroy_all
|
212
|
+
removed.should include proxy_target, proxy_target_2, proxy_target_3
|
213
|
+
removed.each { |r| r.should be_destroyed }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "#delete" do
|
218
|
+
before do
|
219
|
+
subject << proxy_target
|
220
|
+
subject << proxy_target_2
|
221
|
+
subject << proxy_target_3
|
222
|
+
proxy_owner.save!
|
223
|
+
end
|
224
|
+
|
225
|
+
it "deletes one record from the collection" do
|
226
|
+
subject.delete(proxy_target)
|
227
|
+
subject.should_not include proxy_target
|
228
|
+
end
|
229
|
+
|
230
|
+
it "deletes multiple records from collection" do
|
231
|
+
subject.delete(proxy_target, proxy_target_2)
|
232
|
+
subject.should_not include proxy_target, proxy_target_2
|
233
|
+
end
|
234
|
+
|
235
|
+
it "is not destroyed from the database as well" do
|
236
|
+
subject.delete(proxy_target)
|
237
|
+
subject.reload
|
238
|
+
subject.should include proxy_target
|
239
|
+
end
|
240
|
+
|
241
|
+
it "makes deleted objects not know about it after being deleted" do
|
242
|
+
subject.delete(proxy_target)
|
243
|
+
proxy_target.should_not be_destroyed
|
244
|
+
end
|
245
|
+
|
246
|
+
it "is being destroyed if parent are saved" do
|
247
|
+
subject.delete(proxy_target)
|
248
|
+
proxy_owner.save
|
249
|
+
subject.reload
|
250
|
+
subject.should_not include proxy_target
|
251
|
+
end
|
252
|
+
|
253
|
+
it "is being destroed on save" do
|
254
|
+
subject.delete(proxy_target)
|
255
|
+
proxy_owner.save
|
256
|
+
proxy_target.should be_destroyed
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "#delete_all" do
|
261
|
+
before do
|
262
|
+
subject << proxy_target << proxy_target_2 << proxy_target_3
|
263
|
+
proxy_owner.save!
|
264
|
+
end
|
265
|
+
|
266
|
+
it "deletes all records" do
|
267
|
+
subject.delete_all
|
268
|
+
subject.should_not include proxy_target, proxy_target_2, proxy_target_3
|
269
|
+
end
|
270
|
+
|
271
|
+
it "returns all removed records" do
|
272
|
+
removed = subject.delete_all
|
273
|
+
removed.should include proxy_target, proxy_target_2, proxy_target_3
|
274
|
+
removed.each { |r| r.should_not be_destroyed }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
describe "#can_find_proxy_target?" do
|
280
|
+
it "is true" do
|
281
|
+
subject.should be_can_find_proxy_target
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
describe "#find" do
|
287
|
+
let(:not_among_targets) { proxy_target_3 }
|
288
|
+
|
289
|
+
context "owner persisted" do
|
290
|
+
before { proxy_owner.save! }
|
291
|
+
|
292
|
+
context "and proxy loaded" do
|
293
|
+
before do
|
294
|
+
subject.concat proxy_target, proxy_target_2
|
295
|
+
end
|
296
|
+
|
297
|
+
it "finds record by id" do
|
298
|
+
subject.find(proxy_target.id).should eq proxy_target
|
299
|
+
end
|
300
|
+
|
301
|
+
it "finds records which are not new records" do
|
302
|
+
subject.find(proxy_target.id).should be_persisted
|
303
|
+
end
|
304
|
+
|
305
|
+
it "does not call load_proxy_target" do
|
306
|
+
subject.should_not_receive :load_proxy_target
|
307
|
+
subject.find(proxy_target.id)
|
308
|
+
end
|
309
|
+
|
310
|
+
it "raises error if record is not found" do
|
311
|
+
expect { subject.find(not_among_targets.id) }.to raise_error MassiveRecord::ORM::RecordNotFound
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
context "and proxy not loaded" do
|
316
|
+
before do
|
317
|
+
subject.concat proxy_target, proxy_target_2
|
318
|
+
subject.reset
|
319
|
+
end
|
320
|
+
|
321
|
+
context "with raw data loaded" do
|
322
|
+
it "finds record by id" do
|
323
|
+
subject.find(proxy_target.id).should eq proxy_target
|
324
|
+
end
|
325
|
+
|
326
|
+
it "does not load from target's class.table.get" do
|
327
|
+
subject.should_not_receive(:find_raw_data_for_id)
|
328
|
+
subject.find(proxy_target.id).should eq proxy_target
|
329
|
+
end
|
330
|
+
|
331
|
+
it "raises error if record is not found" do
|
332
|
+
expect { subject.find(not_among_targets.id) }.to raise_error MassiveRecord::ORM::RecordNotFound
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
context "without raw data loaded" do
|
337
|
+
before { proxy_owner.update_raw_data_for_column_family(metadata.store_in, {}) }
|
338
|
+
|
339
|
+
it "finds record by id" do
|
340
|
+
subject.find(proxy_target.id).should eq proxy_target
|
341
|
+
end
|
342
|
+
|
343
|
+
it "does not call load_proxy_target" do
|
344
|
+
subject.should_not_receive(:load_proxy_target)
|
345
|
+
subject.find(proxy_target.id)
|
346
|
+
end
|
347
|
+
|
348
|
+
it "raises error if record is not found" do
|
349
|
+
expect { subject.find(not_among_targets.id) }.to raise_error MassiveRecord::ORM::RecordNotFound
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context "owner new record" do
|
356
|
+
it "finds the added record" do
|
357
|
+
subject << proxy_target
|
358
|
+
subject.find(proxy_target.id).should eq proxy_target
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
describe "#limit" do
|
365
|
+
before do
|
366
|
+
subject << proxy_target_2
|
367
|
+
subject << proxy_target_3
|
368
|
+
subject << proxy_target
|
369
|
+
end
|
370
|
+
|
371
|
+
context "owner persisted" do
|
372
|
+
before { proxy_owner.save! }
|
373
|
+
|
374
|
+
context "and proxy loaded" do
|
375
|
+
it "returns the two first records" do
|
376
|
+
subject.limit(2).should eq [proxy_target, proxy_target_2]
|
377
|
+
end
|
378
|
+
|
379
|
+
it "does not call load_proxy_target" do
|
380
|
+
subject.should_not_receive :find_proxy_target
|
381
|
+
subject.limit(2)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
context "and proxy not loaded" do
|
386
|
+
before do
|
387
|
+
subject.reset
|
388
|
+
end
|
389
|
+
|
390
|
+
context "with raw data loaded" do
|
391
|
+
it "returns the two first records" do
|
392
|
+
subject.limit(2).should eq [proxy_target, proxy_target_2]
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
context "without raw data loaded" do
|
397
|
+
before { proxy_owner.update_raw_data_for_column_family(metadata.store_in, {}) }
|
398
|
+
|
399
|
+
it "returns the two first records" do
|
400
|
+
subject.limit(2).should eq [proxy_target, proxy_target_2]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context "owner new record" do
|
407
|
+
it "returns the two first records" do
|
408
|
+
subject.limit(2).should eq [proxy_target, proxy_target_2]
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
|
414
|
+
describe "#load_proxy_target" do
|
415
|
+
context "empty proxy targets raw" do
|
416
|
+
before { proxy_owner.instance_variable_set(:@raw_data, {'addresses' => {}}) }
|
417
|
+
|
418
|
+
its(:load_proxy_target) { should eq [] }
|
419
|
+
|
420
|
+
it "includes added records to collection" do
|
421
|
+
subject << proxy_target
|
422
|
+
subject.load_proxy_target.should include proxy_target
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context "filled proxy_targets_raw" do
|
427
|
+
before { proxy_owner.instance_variable_set(:@raw_data, {'addresses' => raw_data}) }
|
428
|
+
|
429
|
+
its(:load_proxy_target) { should include proxy_target, proxy_target_2, proxy_target_3 }
|
430
|
+
|
431
|
+
it "sets inverse of in loaded records" do
|
432
|
+
subject.load_proxy_target.all? { |r| r.person.should eq proxy_owner }.should be_true
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
|
438
|
+
|
439
|
+
|
440
|
+
describe "#parent_will_be_saved!" do
|
441
|
+
describe "building of proxy_target_update_hash" do
|
442
|
+
before do
|
443
|
+
proxy_owner.save!
|
444
|
+
end
|
445
|
+
|
446
|
+
context "no changes" do
|
447
|
+
before do
|
448
|
+
subject << proxy_target
|
449
|
+
proxy_target.should_receive(:destroyed?).and_return false
|
450
|
+
proxy_target.should_receive(:new_record?).and_return false
|
451
|
+
proxy_target.should_receive(:changed?).and_return false
|
452
|
+
|
453
|
+
subject.parent_will_be_saved!
|
454
|
+
end
|
455
|
+
|
456
|
+
its(:proxy_targets_update_hash) { should be_empty }
|
457
|
+
end
|
458
|
+
|
459
|
+
context "insert" do
|
460
|
+
before do
|
461
|
+
subject << proxy_target
|
462
|
+
proxy_target.should_receive(:destroyed?).and_return false
|
463
|
+
proxy_target.should_receive(:new_record?).any_number_of_times.and_return true
|
464
|
+
proxy_target.should_not_receive(:changed?)
|
465
|
+
|
466
|
+
subject.parent_will_be_saved!
|
467
|
+
end
|
468
|
+
|
469
|
+
it "includes id for record to be inserted" do
|
470
|
+
subject.proxy_targets_update_hash.keys.should eq [proxy_target.database_id]
|
471
|
+
end
|
472
|
+
|
473
|
+
it "includes attributes for record to be inserted" do
|
474
|
+
subject.proxy_targets_update_hash.values.should eq [MassiveRecord::ORM::Base.coder.dump(proxy_target.attributes_db_raw_data_hash)]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context "update" do
|
479
|
+
before do
|
480
|
+
subject << proxy_target
|
481
|
+
proxy_target.should_receive(:destroyed?).and_return false
|
482
|
+
proxy_target.should_receive(:new_record?).any_number_of_times.and_return false
|
483
|
+
proxy_target.should_receive(:changed?).and_return true
|
484
|
+
|
485
|
+
subject.parent_will_be_saved!
|
486
|
+
end
|
487
|
+
|
488
|
+
it "includes id for record to be updated" do
|
489
|
+
subject.proxy_targets_update_hash.keys.should eq [proxy_target.database_id]
|
490
|
+
end
|
491
|
+
|
492
|
+
it "includes attributes for record to be updated" do
|
493
|
+
subject.proxy_targets_update_hash.values.should eq [MassiveRecord::ORM::Base.coder.dump(proxy_target.attributes_db_raw_data_hash)]
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context "destroy" do
|
498
|
+
before do
|
499
|
+
subject << proxy_target
|
500
|
+
end
|
501
|
+
|
502
|
+
it "includes id for record to be updated" do
|
503
|
+
proxy_target.should_receive(:destroyed?).and_return true
|
504
|
+
subject.parent_will_be_saved!
|
505
|
+
subject.proxy_targets_update_hash.keys.should eq [proxy_target.database_id]
|
506
|
+
end
|
507
|
+
|
508
|
+
it "includes attributes for record to be updated" do
|
509
|
+
proxy_target.should_receive(:destroyed?).and_return true
|
510
|
+
subject.parent_will_be_saved!
|
511
|
+
subject.proxy_targets_update_hash.values.should eq [nil]
|
512
|
+
end
|
513
|
+
|
514
|
+
it "includes records in the to_be_destroyed array" do
|
515
|
+
# Don't want it to actually trigger save as that will
|
516
|
+
# clear out the update hash..
|
517
|
+
proxy_owner.should_receive(:save).and_return true
|
518
|
+
|
519
|
+
subject.destroy(proxy_target)
|
520
|
+
subject.parent_will_be_saved!
|
521
|
+
|
522
|
+
subject.proxy_targets_update_hash.keys.should eq [proxy_target.database_id]
|
523
|
+
subject.proxy_targets_update_hash.values.should eq [nil]
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
|
530
|
+
|
531
|
+
|
532
|
+
it "marks new records as persisted" do
|
533
|
+
subject << proxy_target
|
534
|
+
subject.parent_will_be_saved!
|
535
|
+
proxy_target.should be_persisted
|
536
|
+
end
|
537
|
+
|
538
|
+
it "resets dirty state of records" do
|
539
|
+
subject << proxy_target
|
540
|
+
proxy_target.street += "_NEW"
|
541
|
+
subject.parent_will_be_saved!
|
542
|
+
proxy_target.should_not be_changed
|
543
|
+
end
|
544
|
+
|
545
|
+
it "marks destroyed objects as destroyed" do
|
546
|
+
subject.send(:to_be_destroyed) << proxy_target
|
547
|
+
subject.parent_will_be_saved!
|
548
|
+
proxy_target.should be_destroyed
|
549
|
+
end
|
550
|
+
|
551
|
+
it "does not mark targets as destroyed if target's owner has been changed" do
|
552
|
+
subject << proxy_target
|
553
|
+
subject.send(:to_be_destroyed) << proxy_target
|
554
|
+
proxy_target.person = Person.new
|
555
|
+
subject.parent_will_be_saved!
|
556
|
+
proxy_target.should_not be_destroyed
|
557
|
+
end
|
558
|
+
|
559
|
+
it "clears to_be_destroyed array" do
|
560
|
+
subject.send(:to_be_destroyed) << proxy_target
|
561
|
+
subject.parent_will_be_saved!
|
562
|
+
subject.send(:to_be_destroyed).should be_empty
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
describe "#parent_has_been_saved!" do
|
567
|
+
it "clears the proxy_target_update_hash" do
|
568
|
+
hash = {}
|
569
|
+
hash.should_receive :clear
|
570
|
+
subject.should_receive(:proxy_targets_update_hash).and_return(hash)
|
571
|
+
subject.parent_has_been_saved!
|
572
|
+
end
|
573
|
+
|
574
|
+
it "reloads raw data" do
|
575
|
+
subject.should_receive(:reload_raw_data)
|
576
|
+
subject.parent_has_been_saved!
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
describe "#changed?" do
|
581
|
+
before do
|
582
|
+
subject << proxy_target
|
583
|
+
proxy_target.stub(:destroyed?).and_return false
|
584
|
+
proxy_target.stub(:new_record?).and_return false
|
585
|
+
proxy_target.stub(:changed?).and_return false
|
586
|
+
end
|
587
|
+
|
588
|
+
it "returns false if no changes has been made which needs persistence" do
|
589
|
+
should_not be_changed
|
590
|
+
end
|
591
|
+
|
592
|
+
it "returns true if it contains new records" do
|
593
|
+
proxy_target.should_receive(:new_record?).and_return true
|
594
|
+
should be_changed
|
595
|
+
end
|
596
|
+
|
597
|
+
it "returns true if it contains destroyed records" do
|
598
|
+
proxy_target.should_receive(:destroyed?).and_return true
|
599
|
+
should be_changed
|
600
|
+
end
|
601
|
+
|
602
|
+
it "returns true if it contains changed records" do
|
603
|
+
proxy_target.should_receive(:changed?).and_return true
|
604
|
+
should be_changed
|
605
|
+
end
|
606
|
+
|
607
|
+
it "returns true if some records has been asked to be destroyed through proxy" do
|
608
|
+
subject.destroy(proxy_target)
|
609
|
+
should be_changed
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
describe "#changes" do
|
614
|
+
before do
|
615
|
+
proxy_owner.save!
|
616
|
+
subject << proxy_target
|
617
|
+
end
|
618
|
+
|
619
|
+
it "has no changes when no changes has been made" do
|
620
|
+
subject.changes.should be_empty
|
621
|
+
end
|
622
|
+
|
623
|
+
it "accumelates the changes for the complete collection" do
|
624
|
+
proxy_target.street = proxy_target.street + "_NEW"
|
625
|
+
subject.changes.should eq({"address-1" => {"street" => ["Asker", "Asker_NEW"]}})
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
|
630
|
+
|
631
|
+
describe "sorting of records" do
|
632
|
+
describe "when added to a persisted proxy owner" do
|
633
|
+
before { proxy_owner.save! }
|
634
|
+
|
635
|
+
it "sorts record when proxy target is loaded" do
|
636
|
+
subject << proxy_target_2
|
637
|
+
subject << proxy_target
|
638
|
+
subject.reload
|
639
|
+
subject.load_proxy_target.should eq [proxy_target, proxy_target_2]
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
describe "When adding to a proxy owner which is a new record" do
|
644
|
+
it "sorts record in expected order" do
|
645
|
+
subject << proxy_target_2
|
646
|
+
subject << proxy_target
|
647
|
+
subject.load_proxy_target.should eq [proxy_target, proxy_target_2]
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end
|