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,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MassiveRecord::ORM::Persistence::Operations::Reload do
|
4
|
+
include MockMassiveRecordConnection
|
5
|
+
|
6
|
+
let(:record) { TestClass.new("id-1") }
|
7
|
+
let(:options) { {:this => 'hash', :has => 'options'} }
|
8
|
+
|
9
|
+
subject { described_class.new(record, options) }
|
10
|
+
|
11
|
+
it_should_behave_like "a persistence table operation class"
|
12
|
+
|
13
|
+
before { record.save! }
|
14
|
+
|
15
|
+
describe "#execute" do
|
16
|
+
context "new record" do
|
17
|
+
before { record.stub(:persisted?).and_return false }
|
18
|
+
|
19
|
+
its(:execute) { should be_false }
|
20
|
+
|
21
|
+
it "does no find" do
|
22
|
+
subject.klass.should_not_receive(:find)
|
23
|
+
subject.execute
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "persisted" do
|
28
|
+
its(:execute) { should be_true }
|
29
|
+
|
30
|
+
it "asks class to find it's id" do
|
31
|
+
subject.klass.should_receive(:find).with(record.id).and_return(record)
|
32
|
+
subject.execute
|
33
|
+
end
|
34
|
+
|
35
|
+
it "reinit record with found record's attributes and raw_data" do
|
36
|
+
subject.klass.should_receive(:find).with(record.id).and_return(record)
|
37
|
+
record.should_receive(:attributes).and_return('attributes' => {})
|
38
|
+
record.should_receive(:raw_data).and_return('raw_data' => {})
|
39
|
+
record.should_receive(:reinit_with).with({
|
40
|
+
'attributes' => {'attributes' => {}},
|
41
|
+
'raw_data' => {'raw_data' => {}}
|
42
|
+
})
|
43
|
+
subject.execute
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MassiveRecord::ORM::Persistence::Operations::Suppress do
|
4
|
+
include MockMassiveRecordConnection
|
5
|
+
|
6
|
+
let(:record) { TestClass.new("id-1") }
|
7
|
+
let(:options) { {:this => 'hash', :has => 'options'} }
|
8
|
+
|
9
|
+
subject { described_class.new(record, options) }
|
10
|
+
|
11
|
+
|
12
|
+
describe "#execute" do
|
13
|
+
it "returns true" do
|
14
|
+
subject.execute.should be_true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MassiveRecord
|
4
|
+
module ORM
|
5
|
+
module Persistence
|
6
|
+
module Operations
|
7
|
+
|
8
|
+
|
9
|
+
class TestTableOperationHelpers
|
10
|
+
include Operations, TableOperationHelpers
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
describe TableOperationHelpers do
|
15
|
+
include MockMassiveRecordConnection
|
16
|
+
|
17
|
+
let(:person) { Person.new("id-1", :name => "Thorbjorn", :age => 30) }
|
18
|
+
let(:address) { Address.new "address-1", :street => "Asker", :number => 1 }
|
19
|
+
let(:options) { {:this => 'hash', :has => 'options'} }
|
20
|
+
|
21
|
+
subject { TestTableOperationHelpers.new(person, options) }
|
22
|
+
|
23
|
+
before do
|
24
|
+
person.addresses << address
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
describe "#row_for_record" do
|
29
|
+
it "raises an error if id for person is blank" do
|
30
|
+
person.id = nil
|
31
|
+
expect { subject.row_for_record }.to raise_error MassiveRecord::ORM::IdMissing
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns a row with id and table set" do
|
35
|
+
row = subject.row_for_record
|
36
|
+
row.id.should eq person.id
|
37
|
+
row.table.should eq person.class.table
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#attributes_to_row_values_hash" do
|
42
|
+
before { person.addresses.parent_will_be_saved! }
|
43
|
+
|
44
|
+
it "should include the 'pts' field in the database which has 'points' as an alias" do
|
45
|
+
subject.attributes_to_row_values_hash["base"].keys.should include("pts")
|
46
|
+
subject.attributes_to_row_values_hash["base"].keys.should_not include("points")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should include integer value, even if it is set as string" do
|
50
|
+
person.age = "20"
|
51
|
+
subject.attributes_to_row_values_hash["info"]["age"].should == 20
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "embedded attributes" do
|
55
|
+
it "includes the column family for the embedded relation" do
|
56
|
+
subject.attributes_to_row_values_hash.keys.should include "addresses"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "asks the proxy for update hash and uses whatever it delivers" do
|
60
|
+
dummy_hash = {:foo => {:bar => :dummy}}
|
61
|
+
person.addresses.should_receive(:proxy_targets_update_hash).any_number_of_times.and_return(dummy_hash)
|
62
|
+
subject.attributes_to_row_values_hash["addresses"].should eq dummy_hash
|
63
|
+
end
|
64
|
+
|
65
|
+
it "merges embedded collections in to existing column families" do
|
66
|
+
attributes_from_person = subject.attributes_to_row_values_hash["info"]
|
67
|
+
attributes_from_cars = {:foo => {:bar => :dummy}}
|
68
|
+
person.cars.should_receive(:proxy_targets_update_hash).any_number_of_times.and_return(attributes_from_cars)
|
69
|
+
subject.attributes_to_row_values_hash["info"].should eq attributes_from_person.merge(attributes_from_cars)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
describe "#store_record_to_database" do
|
77
|
+
let(:row) { mock(Object, :save => true, :values= => true) }
|
78
|
+
|
79
|
+
before { subject.should_receive(:row_for_record).and_return(row) }
|
80
|
+
|
81
|
+
it "assigns row it's values from what attributes_to_row_values_hash returns" do
|
82
|
+
row.should_receive(:values=).with(subject.attributes_to_row_values_hash)
|
83
|
+
subject.store_record_to_database('create')
|
84
|
+
end
|
85
|
+
|
86
|
+
it "calls save on the row" do
|
87
|
+
row.should_receive(:save)
|
88
|
+
subject.store_record_to_database('create')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MassiveRecord::ORM::Persistence::Operations::Update do
|
4
|
+
include MockMassiveRecordConnection
|
5
|
+
|
6
|
+
let(:record) { TestClass.new("id-1") }
|
7
|
+
let(:options) { {:attribute_names_to_update => ['foo']} }
|
8
|
+
|
9
|
+
subject { described_class.new(record, options) }
|
10
|
+
|
11
|
+
it_should_behave_like "a persistence table operation class"
|
12
|
+
|
13
|
+
|
14
|
+
describe "#execute" do
|
15
|
+
it "ensures that we have table and column families" do
|
16
|
+
subject.should_receive(:ensure_that_we_have_table_and_column_families!)
|
17
|
+
subject.execute
|
18
|
+
end
|
19
|
+
|
20
|
+
it "calls upon store_record_to_database for help with actually update job" do
|
21
|
+
subject.should_receive(:store_record_to_database).with('update', ['foo'])
|
22
|
+
subject.execute
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'orm/models/test_class'
|
3
|
+
|
4
|
+
describe MassiveRecord::ORM::Persistence::Operations do
|
5
|
+
let(:options) { {:this => 'hash', :has => 'options'} }
|
6
|
+
|
7
|
+
describe "factory method" do
|
8
|
+
context "table record" do
|
9
|
+
let(:record) { TestClass.new }
|
10
|
+
|
11
|
+
[:insert, :update, :destroy, :atomic_operation, :reload].each do |method|
|
12
|
+
describe "##{method}" do
|
13
|
+
subject { described_class.send(method, record, options) }
|
14
|
+
|
15
|
+
its(:record) { should eq record }
|
16
|
+
its(:klass) { should eq record.class }
|
17
|
+
its(:options) { should eq options }
|
18
|
+
|
19
|
+
it "is an instance of Persistence::Operations::#{method.to_s.classify}" do
|
20
|
+
klass = "MassiveRecord::ORM::Persistence::Operations::#{method.to_s.classify}".constantize
|
21
|
+
should be_instance_of klass
|
22
|
+
end
|
23
|
+
|
24
|
+
it "is possible to suppress" do
|
25
|
+
MassiveRecord::ORM::Persistence::Operations.suppress do
|
26
|
+
subject.should be_instance_of MassiveRecord::ORM::Persistence::Operations::Suppress
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "embedded record" do
|
34
|
+
let(:record) { Address.new }
|
35
|
+
|
36
|
+
[:insert, :update, :destroy, :reload].each do |method|
|
37
|
+
describe "##{method}" do
|
38
|
+
subject { described_class.send(method, record, options) }
|
39
|
+
|
40
|
+
its(:record) { should eq record }
|
41
|
+
its(:klass) { should eq record.class }
|
42
|
+
its(:options) { should eq options }
|
43
|
+
|
44
|
+
it "is an instance of Persistence::Operations::#{method.to_s.classify}" do
|
45
|
+
klass = "MassiveRecord::ORM::Persistence::Operations::Embedded::#{method.to_s.classify}".constantize
|
46
|
+
should be_instance_of klass
|
47
|
+
end
|
48
|
+
|
49
|
+
it "is possible to suppress" do
|
50
|
+
MassiveRecord::ORM::Persistence::Operations.suppress do
|
51
|
+
subject.should be_instance_of MassiveRecord::ORM::Persistence::Operations::Suppress
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -221,4 +221,192 @@ describe MassiveRecord::ORM::Relations::Interface do
|
|
221
221
|
end
|
222
222
|
end
|
223
223
|
end
|
224
|
+
|
225
|
+
|
226
|
+
describe "embeds many" do
|
227
|
+
context "inside of it's own column family" do
|
228
|
+
describe "relation's meta data" do
|
229
|
+
subject { Person.relations.detect { |relation| relation.name == "addresses" } }
|
230
|
+
|
231
|
+
it "stores the relation on the class" do
|
232
|
+
Person.relations.detect { |relation| relation.name == "addresses" }.should_not be_nil
|
233
|
+
end
|
234
|
+
|
235
|
+
it "has correct type on relation" do
|
236
|
+
subject.relation_type.should == "embeds_many"
|
237
|
+
end
|
238
|
+
|
239
|
+
it "raises error if relation defined twice" do
|
240
|
+
expect { Person.embeds_many :addresses }.to raise_error MassiveRecord::ORM::RelationAlreadyDefined
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "instance" do
|
245
|
+
subject { Person.new :name => "Thorbjorn", :email => "thhermansen@skalar.no", :age => 30 }
|
246
|
+
let(:address) { Address.new :street => "Asker" }
|
247
|
+
let(:proxy) { subject.send(:relation_proxy, "addresses") }
|
248
|
+
|
249
|
+
it { should respond_to :addresses }
|
250
|
+
|
251
|
+
it "should be empty when no addresses has been added" do
|
252
|
+
subject.addresses.should be_empty
|
253
|
+
end
|
254
|
+
|
255
|
+
it "has a known column family for the embedded records" do
|
256
|
+
subject.column_families.collect(&:name).should include "addresses"
|
257
|
+
end
|
258
|
+
|
259
|
+
it "is assignable" do
|
260
|
+
subject.addresses = [address]
|
261
|
+
subject.addresses.should == [address]
|
262
|
+
end
|
263
|
+
|
264
|
+
it "is assignable in initializer" do
|
265
|
+
person = Person.new :addresses => [address]
|
266
|
+
person.addresses.should == [address]
|
267
|
+
end
|
268
|
+
|
269
|
+
it "parent is invalid when one of embedded records is" do
|
270
|
+
subject.addresses << address
|
271
|
+
subject.save!
|
272
|
+
address.street = nil
|
273
|
+
subject.should_not be_valid
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context "inside of a shared column family" do
|
279
|
+
describe "relation's meta data" do
|
280
|
+
subject { Person.relations.detect { |relation| relation.name == "cars" } }
|
281
|
+
|
282
|
+
it "stores the relation on the class" do
|
283
|
+
Person.relations.detect { |relation| relation.name == "cars" }.should_not be_nil
|
284
|
+
end
|
285
|
+
|
286
|
+
it "has correct type on relation" do
|
287
|
+
subject.relation_type.should == "embeds_many"
|
288
|
+
end
|
289
|
+
|
290
|
+
it "raises error if relation defined twice" do
|
291
|
+
expect { Person.embeds_many :cars }.to raise_error MassiveRecord::ORM::RelationAlreadyDefined
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "instance" do
|
296
|
+
subject { Person.new :name => "Thorbjorn", :email => "thhermansen@skalar.no", :age => 30 }
|
297
|
+
let(:car) { Car.new :color => "blue" }
|
298
|
+
let(:proxy) { subject.send(:relation_proxy, "cars") }
|
299
|
+
|
300
|
+
it { should respond_to :cars }
|
301
|
+
|
302
|
+
it "should be empty when no cars has been added" do
|
303
|
+
subject.cars.should be_empty
|
304
|
+
end
|
305
|
+
|
306
|
+
it "has a known column family for the embedded records" do
|
307
|
+
subject.column_families.collect(&:name).should include "info"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "is assignable" do
|
311
|
+
subject.cars = [car]
|
312
|
+
subject.cars.should == [car]
|
313
|
+
end
|
314
|
+
|
315
|
+
it "is assignable in initializer" do
|
316
|
+
person = Person.new :cars => [car]
|
317
|
+
person.cars.should == [car]
|
318
|
+
end
|
319
|
+
|
320
|
+
it "is persistable" do
|
321
|
+
subject.cars << car
|
322
|
+
subject.save!
|
323
|
+
from_database = Person.find subject.id
|
324
|
+
|
325
|
+
from_database.name.should eq subject.name
|
326
|
+
from_database.email.should eq subject.email
|
327
|
+
from_database.age.should eq subject.age
|
328
|
+
|
329
|
+
from_database.cars.should eq subject.cars
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
describe "embedded in" do
|
337
|
+
describe "non polymorphism" do
|
338
|
+
describe "metadata" do
|
339
|
+
subject { Address.relations.detect { |relation| relation.name == "person" } }
|
340
|
+
|
341
|
+
it "stores the relation on the class" do
|
342
|
+
subject.should_not be_nil
|
343
|
+
end
|
344
|
+
|
345
|
+
it "has correct type on relation" do
|
346
|
+
subject.relation_type.should == "embedded_in"
|
347
|
+
end
|
348
|
+
|
349
|
+
it "raises error if relation defined twice" do
|
350
|
+
expect { Address.embedded_in :person }.to raise_error MassiveRecord::ORM::RelationAlreadyDefined
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe "instance" do
|
355
|
+
subject { Address.new "id1", :street => "Asker" }
|
356
|
+
let(:person) { Person.new "person-id-1", :name => "Test", :age => 29 }
|
357
|
+
let(:proxy) { subject.send(:relation_proxy, "person") }
|
358
|
+
|
359
|
+
it "sets and gets the person" do
|
360
|
+
subject.person = person
|
361
|
+
subject.person.should eq person
|
362
|
+
end
|
363
|
+
|
364
|
+
it "adds itself to the collection within the target's class" do
|
365
|
+
person.stub(:valid?).and_return true
|
366
|
+
subject.person = person
|
367
|
+
person.addresses.should include subject
|
368
|
+
end
|
369
|
+
|
370
|
+
it "assigns embedded in attributes with initialize" do
|
371
|
+
address = Address.new "id1", :person => person, :street => "Asker"
|
372
|
+
address.person.should eq person
|
373
|
+
person.addresses.should include address
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
describe "polymorphism" do
|
379
|
+
describe "metadata" do
|
380
|
+
subject { Address.relations.detect { |relation| relation.name == "addressable" } }
|
381
|
+
|
382
|
+
it "stores the relation on the class" do
|
383
|
+
subject.should_not be_nil
|
384
|
+
end
|
385
|
+
|
386
|
+
it "has correct type on relation" do
|
387
|
+
subject.relation_type.should == "embedded_in_polymorphic"
|
388
|
+
end
|
389
|
+
|
390
|
+
it "raises error if relation defined twice" do
|
391
|
+
expect { Address.embedded_in :addressable }.to raise_error MassiveRecord::ORM::RelationAlreadyDefined
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "instance" do
|
396
|
+
subject { Address.new "id1", :street => "Asker" }
|
397
|
+
let(:test_class) { TestClass.new }
|
398
|
+
let(:proxy) { subject.send(:relation_proxy, "addressable") }
|
399
|
+
|
400
|
+
it "sets and gets the test class" do
|
401
|
+
subject.addressable = test_class
|
402
|
+
subject.addressable.should eq test_class
|
403
|
+
end
|
404
|
+
|
405
|
+
it "adds itself to the collection within the target's class" do
|
406
|
+
subject.addressable = test_class
|
407
|
+
test_class.addresses.should include subject
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
224
412
|
end
|
@@ -4,19 +4,23 @@ require 'orm/models/person'
|
|
4
4
|
describe MassiveRecord::ORM::Relations::Metadata do
|
5
5
|
subject { MassiveRecord::ORM::Relations::Metadata.new(nil) }
|
6
6
|
|
7
|
-
%w(name foreign_key class_name relation_type find_with polymorphic records_starts_from).each do |attr|
|
7
|
+
%w(name foreign_key class_name relation_type find_with polymorphic records_starts_from inverse_of).each do |attr|
|
8
8
|
it { should respond_to attr }
|
9
9
|
it { should respond_to attr+"=" }
|
10
10
|
end
|
11
11
|
|
12
12
|
|
13
13
|
it "should be setting values by initializer" do
|
14
|
-
metadata = subject.class.new
|
14
|
+
metadata = subject.class.new(:car, {
|
15
|
+
:foreign_key => :my_car_id, :class_name => "Vehicle", :store_in => :info,
|
16
|
+
:polymorphic => true, :records_starts_from => :records_starts_from, :inverse_of => :inverse_of
|
17
|
+
})
|
15
18
|
metadata.name.should == "car"
|
16
19
|
metadata.foreign_key.should == "my_car_id"
|
17
20
|
metadata.class_name.should == "Vehicle"
|
18
21
|
metadata.store_in.should == "info"
|
19
22
|
metadata.records_starts_from.should == :records_starts_from
|
23
|
+
metadata.inverse_of.should eq 'inverse_of'
|
20
24
|
metadata.should be_polymorphic
|
21
25
|
end
|
22
26
|
|
@@ -91,26 +95,99 @@ describe MassiveRecord::ORM::Relations::Metadata do
|
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
98
|
+
describe "#embedded?" do
|
99
|
+
%w(references_one references_one_polymorphic, references_many).each do |type|
|
100
|
+
context type do
|
101
|
+
before { subject.relation_type = type }
|
94
102
|
|
103
|
+
its(:embedded?) { should be_false }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
%w(embeds_many).each do |type|
|
108
|
+
context type do
|
109
|
+
before { subject.relation_type = type }
|
110
|
+
|
111
|
+
its(:embedded?) { should be_true }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
95
115
|
|
96
116
|
describe "#store_in" do
|
97
|
-
|
117
|
+
context "references" do
|
118
|
+
before { subject.relation_type = :references_many }
|
119
|
+
|
120
|
+
its(:store_in) { should be_nil }
|
98
121
|
|
99
|
-
|
100
|
-
|
101
|
-
|
122
|
+
it "should be able to set column family to store foreign key in" do
|
123
|
+
subject.store_in = :info
|
124
|
+
subject.store_in.should == "info"
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should know its persisting foreign key if foreign key stored in has been set" do
|
128
|
+
subject.store_in = :info
|
129
|
+
should be_persisting_foreign_key
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should not be storing the foreign key if records_starts_from is defined" do
|
133
|
+
subject.store_in = :info
|
134
|
+
subject.records_starts_from = :method_which_returns_a_starting_point
|
135
|
+
should_not be_persisting_foreign_key
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "embedded" do
|
140
|
+
before do
|
141
|
+
subject.name = :addresses
|
142
|
+
subject.relation_type = :embeds_many
|
143
|
+
end
|
144
|
+
|
145
|
+
its(:store_in) { should eq "addresses" }
|
146
|
+
its(:persisting_foreign_key?) { should be_false }
|
102
147
|
end
|
103
148
|
end
|
104
149
|
|
105
|
-
|
106
|
-
|
107
|
-
|
150
|
+
|
151
|
+
describe "owner_class" do
|
152
|
+
it "is settable" do
|
153
|
+
subject.owner_class = Address
|
154
|
+
end
|
155
|
+
|
156
|
+
it "is readable" do
|
157
|
+
subject.owner_class = Address
|
158
|
+
subject.owner_class.should == Address
|
159
|
+
end
|
108
160
|
end
|
109
161
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
162
|
+
describe "#inverse_of" do
|
163
|
+
it "returns whatever it is set to" do
|
164
|
+
subject.inverse_of = :addresses
|
165
|
+
subject.inverse_of.should eq 'addresses'
|
166
|
+
end
|
167
|
+
|
168
|
+
it "calculates inverse of from the owner_class for embedded_in" do
|
169
|
+
subject.relation_type = :embedded_in
|
170
|
+
subject.owner_class = Address
|
171
|
+
subject.inverse_of.should eq 'addresses'
|
172
|
+
end
|
173
|
+
|
174
|
+
it "calculates inverse of from the owner_class for embedded_in" do
|
175
|
+
subject.relation_type = :embedded_in
|
176
|
+
subject.owner_class = AddressWithTimestamp
|
177
|
+
subject.inverse_of.should eq 'address_with_timestamps'
|
178
|
+
end
|
179
|
+
|
180
|
+
it "calculates inverse of from the owner_class for embeds_many" do
|
181
|
+
subject.relation_type = :embeds_many
|
182
|
+
subject.owner_class = Person
|
183
|
+
subject.inverse_of.should eq 'person'
|
184
|
+
end
|
185
|
+
|
186
|
+
it "raises an error if not set nor owner class" do
|
187
|
+
subject.inverse_of = nil
|
188
|
+
subject.owner_class = nil
|
189
|
+
expect { subject.inverse_of }.to raise_error "Can't return inverse of without it being explicitly set or without an owner_class"
|
190
|
+
end
|
114
191
|
end
|
115
192
|
|
116
193
|
|
@@ -189,12 +266,12 @@ describe MassiveRecord::ORM::Relations::Metadata do
|
|
189
266
|
end
|
190
267
|
|
191
268
|
it "should call proxy_target class with all, start with proxy_owner's start from id response" do
|
192
|
-
Person.should_receive(:all).with(hash_including(:
|
269
|
+
Person.should_receive(:all).with(hash_including(:starts_with => proxy_owner.friends_records_starts_from_id))
|
193
270
|
find_with_proc.call(proxy_owner)
|
194
271
|
end
|
195
272
|
|
196
273
|
it "should be possible to send in options to the proc" do
|
197
|
-
Person.should_receive(:all).with(hash_including(:limit => 10, :
|
274
|
+
Person.should_receive(:all).with(hash_including(:limit => 10, :starts_with => proxy_owner.friends_records_starts_from_id))
|
198
275
|
find_with_proc.call(proxy_owner, {:limit => 10})
|
199
276
|
end
|
200
277
|
end
|