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
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
3
|
require 'orm/models/test_class'
|
3
4
|
require 'orm/models/person'
|
@@ -27,22 +28,47 @@ describe "finders" do
|
|
27
28
|
lambda { Person.find(nil) }.should raise_error MassiveRecord::ORM::RecordNotFound
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
describe "conditions" do
|
32
|
+
it "should raise an error if conditions are given to first" do
|
33
|
+
lambda { Person.first(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise an error if conditions are given to all" do
|
37
|
+
lambda { Person.all(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
|
38
|
+
end
|
33
39
|
|
34
|
-
|
35
|
-
|
40
|
+
it "should raise an error if conditions are given to find" do
|
41
|
+
lambda { Person.find(:conditions => "foo = 'bar'") }.should raise_error ArgumentError
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
|
-
|
39
|
-
|
45
|
+
describe "default select" do
|
46
|
+
it "applies all the known column families to finder options as a default on all()" do
|
47
|
+
@mocked_table.should_receive(:all).with(hash_including(:select => Person.known_column_family_names)).and_return []
|
48
|
+
Person.all
|
49
|
+
end
|
50
|
+
|
51
|
+
it "applies all the known column families to finder options as a default on first()" do
|
52
|
+
@mocked_table.should_receive(:all).with(hash_including(:select => Person.known_column_family_names)).and_return []
|
53
|
+
Person.first
|
54
|
+
end
|
55
|
+
|
56
|
+
it "applies all the known column families to finder options as a default on first()" do
|
57
|
+
@mocked_table.should_receive(:find).with("ID1", hash_including(:select => Person.known_column_family_names)).and_return(@row)
|
58
|
+
Person.find("ID1")
|
59
|
+
end
|
40
60
|
end
|
41
61
|
|
42
62
|
it "should ask the table to look up by it's id" do
|
43
63
|
@mocked_table.should_receive(:find).with("ID1", anything).and_return(@row)
|
44
64
|
Person.find("ID1")
|
45
65
|
end
|
66
|
+
|
67
|
+
it "persists the raw values from table" do
|
68
|
+
@mocked_table.should_receive(:find).with("ID1", anything).and_return(@row)
|
69
|
+
person = Person.find("ID1")
|
70
|
+
person.raw_data.should eq @row.values_raw_data_hash
|
71
|
+
end
|
46
72
|
|
47
73
|
it "should ask the table to fetch rows from a list of ids given as array" do
|
48
74
|
@mocked_table.should_receive(:find).with(["ID1", "ID2"], anything).and_return([@row, @row_2])
|
@@ -67,9 +93,9 @@ describe "finders" do
|
|
67
93
|
lambda { Person.find("ID1", "ID2") }.should raise_error MassiveRecord::ORM::RecordNotFound
|
68
94
|
end
|
69
95
|
|
70
|
-
it "should call table's
|
71
|
-
@mocked_table.should_receive(:
|
72
|
-
Person.find(:first)
|
96
|
+
it "should call table's all with limit 1 on find(:first)" do
|
97
|
+
@mocked_table.should_receive(:all).with(hash_including(:limit => 1)).and_return([@row])
|
98
|
+
Person.find(:first).should be_instance_of Person
|
73
99
|
end
|
74
100
|
|
75
101
|
it "should call table's all on find(:all)" do
|
@@ -164,6 +190,12 @@ describe "finders" do
|
|
164
190
|
@person.age.should == 20
|
165
191
|
end
|
166
192
|
|
193
|
+
it "should maintain encoding of ids" do
|
194
|
+
id = "thorbjørn"
|
195
|
+
person = Person.create! id, :name => "Thorbjørn", :age => 20
|
196
|
+
Person.find(id).should eq person
|
197
|
+
end
|
198
|
+
|
167
199
|
it "should find first person" do
|
168
200
|
Person.first.should == @person
|
169
201
|
end
|
@@ -186,6 +218,21 @@ describe "finders" do
|
|
186
218
|
it "should return what it finds if asked to" do
|
187
219
|
lambda { Person.find(["ID1", "not exists"], :skip_expected_result_check => true) }.should_not raise_error MassiveRecord::ORM::RecordNotFound
|
188
220
|
end
|
221
|
+
|
222
|
+
|
223
|
+
describe "embedded records" do
|
224
|
+
subject { Person.find("ID1") }
|
225
|
+
let(:address) { Address.new "address-1", :street => "Asker", :number => 1 }
|
226
|
+
|
227
|
+
before do
|
228
|
+
subject.addresses << address
|
229
|
+
subject.reload
|
230
|
+
end
|
231
|
+
|
232
|
+
it "is able to load embeds many relations" do
|
233
|
+
subject.addresses.should eq [address]
|
234
|
+
end
|
235
|
+
end
|
189
236
|
end
|
190
237
|
|
191
238
|
describe "#find_in_batches" do
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'orm/models/person'
|
3
|
+
|
4
|
+
describe MassiveRecord::ORM::IdFactory::AtomicIncrementation do
|
5
|
+
include SetUpHbaseConnectionBeforeAll
|
6
|
+
include SetTableNamesToTestTable
|
7
|
+
|
8
|
+
subject { described_class.instance }
|
9
|
+
|
10
|
+
it_should_behave_like "an id factory"
|
11
|
+
|
12
|
+
|
13
|
+
it "has table name equal to id_factories" do
|
14
|
+
described_class.table_name.should eq "id_factories_test"
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#next_for" do
|
18
|
+
after do
|
19
|
+
MassiveRecord::ORM::IdFactory::AtomicIncrementation.destroy_all
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should increment start a new sequence on 1" do
|
23
|
+
subject.next_for(Person).should == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should increment value one by one" do
|
27
|
+
5.times do |index|
|
28
|
+
expected_id = index + 1
|
29
|
+
subject.next_for(Person).should == expected_id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should maintain ids separate for each table" do
|
34
|
+
3.times { subject.next_for(Person) }
|
35
|
+
subject.next_for("cars").should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "autoload ids as integers" do
|
39
|
+
subject.next_for(Person).should eq 1
|
40
|
+
|
41
|
+
family_for_person = subject.class.column_families.family_by_name(MassiveRecord::ORM::IdFactory::AtomicIncrementation::COLUMN_FAMILY_FOR_TABLES)
|
42
|
+
field_for_person = family_for_person.fields.delete_if { |f| f.name == Person.table_name }
|
43
|
+
|
44
|
+
subject.reload
|
45
|
+
subject.attributes_schema[Person.table_name].type.should eq :integer
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe "old string representation of integers" do
|
50
|
+
it "increments correctly when value is '1'" do
|
51
|
+
old_ensure = MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings
|
52
|
+
MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = true
|
53
|
+
|
54
|
+
subject.next_for(Person)
|
55
|
+
|
56
|
+
# Enter incompatible data, number as string.
|
57
|
+
MassiveRecord::ORM::IdFactory::AtomicIncrementation.table.first.tap do |row|
|
58
|
+
row.update_column(
|
59
|
+
MassiveRecord::ORM::IdFactory::AtomicIncrementation::COLUMN_FAMILY_FOR_TABLES,
|
60
|
+
Person.table_name,
|
61
|
+
MassiveRecord::ORM::Base.coder.dump(1)
|
62
|
+
)
|
63
|
+
row.save
|
64
|
+
end
|
65
|
+
|
66
|
+
subject.next_for(Person).should eq 2
|
67
|
+
|
68
|
+
MassiveRecord::ORM::Base.backward_compatibility_integers_might_be_persisted_as_strings = old_ensure
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'orm/models/person'
|
3
|
+
|
4
|
+
describe MassiveRecord::ORM::IdFactory::Timestamp do
|
5
|
+
subject { described_class.instance }
|
6
|
+
|
7
|
+
it_should_behave_like "an id factory"
|
8
|
+
|
9
|
+
|
10
|
+
describe "settings" do
|
11
|
+
after do
|
12
|
+
described_class.precision = :microseconds
|
13
|
+
described_class.reverse_time = true
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#precision" do
|
17
|
+
it "can be set to seconds" do
|
18
|
+
described_class.precision = :seconds
|
19
|
+
subject.next_for(Person).length.should eq 10
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can be set to milliseconds" do
|
23
|
+
described_class.precision = :milliseconds
|
24
|
+
subject.next_for(Person).length.should eq 13
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can be set to microseconds" do
|
28
|
+
described_class.precision = :microseconds
|
29
|
+
subject.next_for(Person).length.should eq 16
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#reverse_time" do
|
34
|
+
let(:time) { mock(Time) }
|
35
|
+
|
36
|
+
before do
|
37
|
+
time.stub_chain(:getutc, :to_f).and_return(1)
|
38
|
+
Time.stub(:now).and_return time
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can be normal time" do
|
42
|
+
described_class.reverse_time = false
|
43
|
+
described_class.precision = :seconds
|
44
|
+
|
45
|
+
subject.next_for(Person).should eq "1"
|
46
|
+
|
47
|
+
described_class.reverse_time = true
|
48
|
+
described_class.precision = :microseconds
|
49
|
+
end
|
50
|
+
|
51
|
+
it "can be reverse time" do
|
52
|
+
described_class.reverse_time = true
|
53
|
+
described_class.precision = :seconds
|
54
|
+
|
55
|
+
subject.next_for(Person).should eq "9999999998"
|
56
|
+
|
57
|
+
described_class.precision = :microseconds
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,357 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'orm/models/test_class'
|
3
|
+
require 'orm/models/friend'
|
4
|
+
require 'orm/models/best_friend'
|
5
|
+
|
6
|
+
describe MassiveRecord::ORM::IdentityMap do
|
7
|
+
before do
|
8
|
+
MassiveRecord::ORM::IdentityMap.clear
|
9
|
+
MassiveRecord::ORM::IdentityMap.enabled = true
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) { MassiveRecord::ORM::IdentityMap.enabled = false }
|
13
|
+
|
14
|
+
|
15
|
+
describe "class methods" do
|
16
|
+
subject { described_class }
|
17
|
+
|
18
|
+
describe "confirguration" do
|
19
|
+
describe ".enabled" do
|
20
|
+
context "when disabled" do
|
21
|
+
before { MassiveRecord::ORM::IdentityMap.enabled = false }
|
22
|
+
its(:enabled) { should be_false }
|
23
|
+
its(:enabled?) { should be_false }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when enabled" do
|
27
|
+
before { MassiveRecord::ORM::IdentityMap.enabled = true }
|
28
|
+
its(:enabled) { should be_true }
|
29
|
+
its(:enabled?) { should be_true }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it ".use sets enabled to true, yield block and ensure to reset it to what it was" do
|
34
|
+
MassiveRecord::ORM::IdentityMap.enabled = false
|
35
|
+
|
36
|
+
MassiveRecord::ORM::IdentityMap.use do
|
37
|
+
MassiveRecord::ORM::IdentityMap.should be_enabled
|
38
|
+
end
|
39
|
+
|
40
|
+
MassiveRecord::ORM::IdentityMap.should_not be_enabled
|
41
|
+
end
|
42
|
+
|
43
|
+
it ".without sets enabled to true, yield block and ensure to reset it to what it was" do
|
44
|
+
MassiveRecord::ORM::IdentityMap.enabled = true
|
45
|
+
|
46
|
+
MassiveRecord::ORM::IdentityMap.without do
|
47
|
+
MassiveRecord::ORM::IdentityMap.should_not be_enabled
|
48
|
+
end
|
49
|
+
|
50
|
+
MassiveRecord::ORM::IdentityMap.should be_enabled
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "persistence" do
|
55
|
+
let(:person) { Person.new "id1" }
|
56
|
+
let(:friend) { Friend.new "id2" }
|
57
|
+
let(:test_class) { TestClass.new "id2" }
|
58
|
+
|
59
|
+
describe ".repository" do
|
60
|
+
its(:repository) { should eq Hash.new }
|
61
|
+
|
62
|
+
it "has values as a hash by default for any key" do
|
63
|
+
subject.send(:repository)['some_class'].should eq Hash.new
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".clear" do
|
68
|
+
it "removes all values from repository" do
|
69
|
+
subject.send(:repository)['some_class']['an_id'] = Object.new
|
70
|
+
subject.clear
|
71
|
+
subject.send(:repository).should be_empty
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ".get" do
|
76
|
+
it "raises error if no ids are given" do
|
77
|
+
expect { subject.get(person.class) }.to raise_error ArgumentError
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when it does not has the record" do
|
81
|
+
it "returns nil" do
|
82
|
+
subject.get(person.class, person.id).should be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns empty array if asked for multiple records" do
|
86
|
+
subject.get(person.class, 1, 2).should eq []
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when it has the record" do
|
91
|
+
it "returns the record" do
|
92
|
+
subject.add person
|
93
|
+
subject.get(person.class, person.id).should eq person
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "single table inheritahce" do
|
97
|
+
before { subject.add friend }
|
98
|
+
|
99
|
+
it "returns the record when looked up by it's class" do
|
100
|
+
subject.get(friend.class, friend.id).should eq friend
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns the record when looked up by it's parent class" do
|
104
|
+
subject.get(person.class, friend.id).should eq friend
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises an error when you request a parent class via a descendant class" do
|
108
|
+
subject.add person
|
109
|
+
expect {
|
110
|
+
subject.get(friend.class, person.id)
|
111
|
+
}.to raise_error MassiveRecord::ORM::IdentityMap::RecordIsSuperClassOfQueriedClass
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "get multiple" do
|
116
|
+
it "returns multiple records when asked for multiple ids" do
|
117
|
+
subject.add person
|
118
|
+
subject.add friend
|
119
|
+
subject.get(person.class, person.id, friend.id).should include person, friend
|
120
|
+
end
|
121
|
+
|
122
|
+
it "returns multiple records when asked for multiple ids as an array" do
|
123
|
+
subject.add person
|
124
|
+
subject.add friend
|
125
|
+
subject.get(person.class, [person.id, friend.id]).should include person, friend
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns array when get got an array, even with only one id" do
|
129
|
+
subject.add friend
|
130
|
+
subject.get(person.class, [friend.id]).should eq [friend]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns nothing for unkown ids" do
|
134
|
+
subject.add person
|
135
|
+
subject.add friend
|
136
|
+
subject.get(person.class, person.id, friend.id, "unkown").length.should eq 2
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "returns the correct record when they have the same id" do
|
141
|
+
person.id = test_class.id = "same_id"
|
142
|
+
|
143
|
+
subject.add(person)
|
144
|
+
subject.add(test_class)
|
145
|
+
|
146
|
+
subject.get(person.class, person.id).should eq person
|
147
|
+
subject.get(test_class.class, "same_id").should eq test_class
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe ".add" do
|
153
|
+
it "does not do anything if trying to add nil" do
|
154
|
+
subject.add(nil).should be_nil
|
155
|
+
end
|
156
|
+
|
157
|
+
it "persists the record" do
|
158
|
+
subject.add person
|
159
|
+
subject.get(person.class, person.id).should eq person
|
160
|
+
end
|
161
|
+
|
162
|
+
it "persists a single table inheritance record" do
|
163
|
+
subject.add friend
|
164
|
+
subject.get(friend.class, friend.id).should eq friend
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe ".remove" do
|
169
|
+
it "returns nil if record was not found" do
|
170
|
+
subject.remove(person).should eq nil
|
171
|
+
end
|
172
|
+
|
173
|
+
it "removes the record" do
|
174
|
+
subject.add person
|
175
|
+
subject.remove person
|
176
|
+
subject.get(person.class, person.id).should be_nil
|
177
|
+
end
|
178
|
+
|
179
|
+
it "returns the removed record" do
|
180
|
+
subject.add person
|
181
|
+
subject.remove(person).should eq person
|
182
|
+
end
|
183
|
+
|
184
|
+
it "removes a single table inheritance record" do
|
185
|
+
subject.add friend
|
186
|
+
subject.remove friend
|
187
|
+
subject.get(friend.class, friend.id).should be_nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe ".remove_by_id" do
|
192
|
+
it "removes the record by it's class and id directly" do
|
193
|
+
subject.add person
|
194
|
+
subject.remove_by_id person.class, person.id
|
195
|
+
subject.get(person.class, person.id).should be_nil
|
196
|
+
end
|
197
|
+
|
198
|
+
it "returns the removed record" do
|
199
|
+
subject.add person
|
200
|
+
subject.remove_by_id(person.class, person.id).should eq person
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
describe "lifecycles on records" do
|
208
|
+
include SetUpHbaseConnectionBeforeAll
|
209
|
+
include SetTableNamesToTestTable
|
210
|
+
|
211
|
+
let(:id) { "ID1" }
|
212
|
+
let(:id_2) { "ID2" }
|
213
|
+
let(:person) { Person.create!(id, :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true) }
|
214
|
+
let(:friend) { Friend.create!(id_2, :name => "Person1", :email => "one@person.com", :age => 11, :points => 111, :status => true) }
|
215
|
+
|
216
|
+
describe "#find" do
|
217
|
+
describe "one" do
|
218
|
+
context "when the record is not in the identity map" do
|
219
|
+
it "asks do find for the record" do
|
220
|
+
Person.should_receive(:do_find).and_return(nil)
|
221
|
+
Person.find(id).should be_nil
|
222
|
+
end
|
223
|
+
|
224
|
+
it "adds the found record" do
|
225
|
+
MassiveRecord::ORM::IdentityMap.without { person }
|
226
|
+
|
227
|
+
MassiveRecord::ORM::IdentityMap.get(person.class, person.id).should be_nil
|
228
|
+
Person.find(id)
|
229
|
+
MassiveRecord::ORM::IdentityMap.get(person.class, person.id).should eq person
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "when record is in identity map" do
|
234
|
+
before { MassiveRecord::ORM::IdentityMap.add(person) }
|
235
|
+
|
236
|
+
it "returns that record" do
|
237
|
+
Person.table.should_not_receive(:find)
|
238
|
+
Person.find(person.id).should eq person
|
239
|
+
end
|
240
|
+
|
241
|
+
it "returns record from database when select option is used" do
|
242
|
+
MassiveRecord::ORM::IdentityMap.should_not_receive(:get)
|
243
|
+
Person.select(:info).find(person.id).should eq person
|
244
|
+
end
|
245
|
+
|
246
|
+
it "returns record from identity map when you ask for a sub class by its parent class" do
|
247
|
+
MassiveRecord::ORM::IdentityMap.add(friend)
|
248
|
+
Person.table.should_not_receive(:find)
|
249
|
+
Person.find(friend.id).should eq friend
|
250
|
+
end
|
251
|
+
|
252
|
+
it "returns nil when you ask for a parent class" do
|
253
|
+
Friend.table.should_not_receive(:find)
|
254
|
+
Friend.find(person.id).should be_nil
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe "many" do
|
260
|
+
it "returns records from database when select option is used" do
|
261
|
+
MassiveRecord::ORM::IdentityMap.should_not_receive(:get)
|
262
|
+
Person.select(:info).find([person.id, friend.id]).should include person, friend
|
263
|
+
end
|
264
|
+
|
265
|
+
context "when no records are in the identity map" do
|
266
|
+
it "asks find for the two records" do
|
267
|
+
Person.should_receive(:do_find).with([id, id_2], anything).and_return []
|
268
|
+
Person.find([id, id_2]).should eq []
|
269
|
+
end
|
270
|
+
|
271
|
+
it "adds the found recods" do
|
272
|
+
MassiveRecord::ORM::IdentityMap.without { person; friend }
|
273
|
+
MassiveRecord::ORM::IdentityMap.get(person.class, person.id, friend.id).should be_empty
|
274
|
+
|
275
|
+
Person.find([id, id_2])
|
276
|
+
MassiveRecord::ORM::IdentityMap.get(person.class, person.id, friend.id).should include person, friend
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context "when all records are in the identity map" do
|
281
|
+
before do
|
282
|
+
MassiveRecord::ORM::IdentityMap.add(person)
|
283
|
+
MassiveRecord::ORM::IdentityMap.add(friend)
|
284
|
+
end
|
285
|
+
|
286
|
+
it "returns records from identity map" do
|
287
|
+
Person.table.should_not_receive(:find)
|
288
|
+
Person.find([person.id, friend.id])
|
289
|
+
end
|
290
|
+
|
291
|
+
it "returns only records equal to or descendants of queried class" do
|
292
|
+
Friend.find([person.id, friend.id]).should eq [friend]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context "when some records are in the identity map" do
|
297
|
+
before do
|
298
|
+
MassiveRecord::ORM::IdentityMap.add(person)
|
299
|
+
MassiveRecord::ORM::IdentityMap.without { friend }
|
300
|
+
end
|
301
|
+
|
302
|
+
it "returns records from identity map" do
|
303
|
+
Person.should_receive(:query_hbase).with([friend.id], anything).and_return [friend]
|
304
|
+
Person.find([person.id, friend.id])
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe "#save" do
|
311
|
+
context "a new record" do
|
312
|
+
it "adds the record to the identity map after being created" do
|
313
|
+
person
|
314
|
+
Person.table.should_not_receive(:find)
|
315
|
+
Person.find(person.id).should eq person
|
316
|
+
end
|
317
|
+
|
318
|
+
it "does not add the record if validation fails" do
|
319
|
+
invalid_person = Person.create "ID2", :name => "Person2"
|
320
|
+
Person.should_not be_exists invalid_person.id
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "#destroy" do
|
326
|
+
it "removes the record from identiy map" do
|
327
|
+
person.destroy
|
328
|
+
Person.should_not be_exists person.id
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "#destroy_all" do
|
333
|
+
it "removes the record from identiy map" do
|
334
|
+
person
|
335
|
+
Person.destroy_all
|
336
|
+
Person.should_not be_exists person.id
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "#delete" do
|
341
|
+
it "removes the record from identiy map" do
|
342
|
+
person.delete
|
343
|
+
Person.should_not be_exists person.id
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe "#reload" do
|
348
|
+
it "reloads it's attributes" do
|
349
|
+
what_it_was = person.name
|
350
|
+
person.name = person.name.reverse
|
351
|
+
|
352
|
+
person.reload
|
353
|
+
person.name.should eq what_it_was
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|