couchobject 0.5.0 → 0.6.0
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/History.txt +10 -0
- data/Manifest.txt +30 -6
- data/README.txt +580 -42
- data/TODO +2 -2
- data/config/hoe.rb +1 -1
- data/lib/couch_object.rb +7 -2
- data/lib/couch_object/database.rb +19 -34
- data/lib/couch_object/document.rb +13 -6
- data/lib/couch_object/error_classes.rb +110 -0
- data/lib/couch_object/persistable.rb +954 -36
- data/lib/couch_object/persistable/has_many_relations_array.rb +91 -0
- data/lib/couch_object/persistable/meta_classes.rb +568 -0
- data/lib/couch_object/persistable/overloaded_methods.rb +209 -0
- data/lib/couch_object/server.rb +1 -1
- data/lib/couch_object/utils.rb +44 -0
- data/lib/couch_object/version.rb +1 -1
- data/lib/couch_object/view.rb +129 -6
- data/script/console +0 -0
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/txt2html +0 -0
- data/spec/database_spec.rb +23 -31
- data/spec/database_spec.rb.orig +173 -0
- data/spec/document_spec.rb +21 -3
- data/spec/integration/database_integration_spec.rb +46 -15
- data/spec/integration/integration_helper.rb +3 -3
- data/spec/persistable/callback.rb +44 -0
- data/spec/persistable/callback_spec.rb +44 -0
- data/spec/persistable/cloning.rb +77 -0
- data/spec/persistable/cloning_spec.rb +77 -0
- data/spec/persistable/comparing_objects.rb +350 -0
- data/spec/persistable/comparing_objects_spec.rb +350 -0
- data/spec/persistable/deleting.rb +113 -0
- data/spec/persistable/deleting_spec.rb +113 -0
- data/spec/persistable/error_messages.rb +32 -0
- data/spec/persistable/error_messages_spec.rb +32 -0
- data/spec/persistable/loading.rb +339 -0
- data/spec/persistable/loading_spec.rb +339 -0
- data/spec/persistable/new_methods.rb +70 -0
- data/spec/persistable/new_methods_spec.rb +70 -0
- data/spec/persistable/persistable_helper.rb +194 -0
- data/spec/persistable/relations.rb +470 -0
- data/spec/persistable/relations_spec.rb +470 -0
- data/spec/persistable/saving.rb +137 -0
- data/spec/persistable/saving_spec.rb +137 -0
- data/spec/persistable/setting_storage_location.rb +65 -0
- data/spec/persistable/setting_storage_location_spec.rb +65 -0
- data/spec/persistable/timestamps.rb +76 -0
- data/spec/persistable/timestamps_spec.rb +76 -0
- data/spec/persistable/unsaved_changes.rb +211 -0
- data/spec/persistable/unsaved_changes_spec.rb +211 -0
- data/spec/server_spec.rb +5 -5
- data/spec/utils_spec.rb +60 -0
- data/spec/view_spec.rb +40 -7
- data/website/index.html +22 -7
- data/website/index.txt +13 -5
- metadata +93 -61
- data/bin/couch_ruby_view_requestor +0 -81
- data/lib/couch_object/model.rb +0 -5
- data/lib/couch_object/proc_condition.rb +0 -14
- data/spec/model_spec.rb +0 -5
- data/spec/persistable_spec.rb +0 -91
- data/spec/proc_condition_spec.rb +0 -26
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/persistable_helper.rb'
|
2
|
+
|
3
|
+
describe CouchObject::Persistable, "deleting objects" do
|
4
|
+
before(:each) do
|
5
|
+
@building = ApartmentBuilding.new
|
6
|
+
@apartment = Apartment.new
|
7
|
+
|
8
|
+
@db = mock("mock db")
|
9
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
10
|
+
|
11
|
+
@content_apartment = %{
|
12
|
+
{
|
13
|
+
"_id":"123BAC",
|
14
|
+
"_rev":"946B7D1C",
|
15
|
+
"class":"Apartment"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
19
|
+
|
20
|
+
content = HTTPResponse.new(JSON.unparse({
|
21
|
+
"_id" => "123BAC",
|
22
|
+
"_rev" => "946B7D1C",
|
23
|
+
}))
|
24
|
+
|
25
|
+
@document_response = CouchObject::Response.new(content)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to delete itself." do
|
29
|
+
response = HTTPResponse.new(@content_apartment)
|
30
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
31
|
+
@db.should_receive(:delete).with("123BAC", "946B7D1C")
|
32
|
+
|
33
|
+
building = Apartment.get_by_id("123BAC", "foo")
|
34
|
+
building.delete.should == true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "deleting an unsaved document should be possible" do
|
38
|
+
@building.delete.should == true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "the deleted object should be reset" do
|
42
|
+
response = HTTPResponse.new(@content_apartment)
|
43
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
44
|
+
@db.should_receive(:delete).with("123BAC", "946B7D1C")
|
45
|
+
|
46
|
+
building = Apartment.get_by_id("123BAC", "foo")
|
47
|
+
building.delete.should == true
|
48
|
+
building.id.should == nil
|
49
|
+
building.revision.should == nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it "when deleting an object that has relations, the relations should also be deleted" do
|
53
|
+
# Receives open seven times. First to store the documents, then to delete.
|
54
|
+
CouchObject::Database.should_receive(:open).exactly(6).and_return(@db)
|
55
|
+
|
56
|
+
# Store the new documents
|
57
|
+
@db.should_receive(:post).exactly(3).and_return(@document_response)
|
58
|
+
|
59
|
+
# Delete all the objects
|
60
|
+
@db.should_receive(:delete).twice.with("123BAC", "946B7D1C")
|
61
|
+
@db.should_receive(:delete).with("new_ID_foo", "new_Revision_foo")
|
62
|
+
|
63
|
+
@view_content_no_rows = %{
|
64
|
+
{"total_rows":3,"rows":[]}
|
65
|
+
}
|
66
|
+
response = HTTPResponse.new(@view_content_no_rows)
|
67
|
+
|
68
|
+
another_apartment = @apartment.clone
|
69
|
+
@apartment.new?.should == true
|
70
|
+
another_apartment.new?.should == true
|
71
|
+
|
72
|
+
@building.apartments << @apartment << another_apartment
|
73
|
+
@building.save
|
74
|
+
|
75
|
+
@apartment.new?.should == false
|
76
|
+
another_apartment.new?.should == false
|
77
|
+
|
78
|
+
# They have been set up identically, so we have to cheat and change
|
79
|
+
# the ID of one of the apartments
|
80
|
+
another_apartment.instance_variable_set("@id", "new_ID_foo")
|
81
|
+
another_apartment.instance_variable_set("@revision", "new_Revision_foo")
|
82
|
+
|
83
|
+
@building.delete
|
84
|
+
@building.id.should == nil
|
85
|
+
@building.revision.should == nil
|
86
|
+
|
87
|
+
@apartment.id.should == nil
|
88
|
+
@apartment.revision.should == nil
|
89
|
+
|
90
|
+
another_apartment.id.should == nil
|
91
|
+
another_apartment.revision.should == nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "deleting an object that is in a relation should notify the owner" do
|
95
|
+
@building.apartments << @apartment
|
96
|
+
@apartment.apartment_building.should_not == nil
|
97
|
+
@building.apartments.size.should == 1
|
98
|
+
@apartment.delete
|
99
|
+
@building.apartments.size.should == 0
|
100
|
+
@apartment.apartment_building.should == nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should be able to delete has_many relations" do
|
104
|
+
@building.apartments << @apartment
|
105
|
+
@apartment.apartment_building.should_not == nil
|
106
|
+
@building.apartments.size.should == 1
|
107
|
+
@building.apartments.delete(@apartment)
|
108
|
+
@building.apartments.size.should == 0
|
109
|
+
@apartment.apartment_building.should == nil
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/persistable_helper.rb'
|
2
|
+
|
3
|
+
describe CouchObject::Persistable, "deleting objects" do
|
4
|
+
before(:each) do
|
5
|
+
@building = ApartmentBuilding.new
|
6
|
+
@apartment = Apartment.new
|
7
|
+
|
8
|
+
@db = mock("mock db")
|
9
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
10
|
+
|
11
|
+
@content_apartment = %{
|
12
|
+
{
|
13
|
+
"_id":"123BAC",
|
14
|
+
"_rev":"946B7D1C",
|
15
|
+
"class":"Apartment"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
19
|
+
|
20
|
+
content = HTTPResponse.new(JSON.unparse({
|
21
|
+
"_id" => "123BAC",
|
22
|
+
"_rev" => "946B7D1C",
|
23
|
+
}))
|
24
|
+
|
25
|
+
@document_response = CouchObject::Response.new(content)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to delete itself." do
|
29
|
+
response = HTTPResponse.new(@content_apartment)
|
30
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
31
|
+
@db.should_receive(:delete).with("123BAC", "946B7D1C")
|
32
|
+
|
33
|
+
building = Apartment.get_by_id("123BAC", "foo")
|
34
|
+
building.delete.should == true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "deleting an unsaved document should be possible" do
|
38
|
+
@building.delete.should == true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "the deleted object should be reset" do
|
42
|
+
response = HTTPResponse.new(@content_apartment)
|
43
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
44
|
+
@db.should_receive(:delete).with("123BAC", "946B7D1C")
|
45
|
+
|
46
|
+
building = Apartment.get_by_id("123BAC", "foo")
|
47
|
+
building.delete.should == true
|
48
|
+
building.id.should == nil
|
49
|
+
building.revision.should == nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it "when deleting an object that has relations, the relations should also be deleted" do
|
53
|
+
# Receives open seven times. First to store the documents, then to delete.
|
54
|
+
CouchObject::Database.should_receive(:open).exactly(6).and_return(@db)
|
55
|
+
|
56
|
+
# Store the new documents
|
57
|
+
@db.should_receive(:post).exactly(3).and_return(@document_response)
|
58
|
+
|
59
|
+
# Delete all the objects
|
60
|
+
@db.should_receive(:delete).twice.with("123BAC", "946B7D1C")
|
61
|
+
@db.should_receive(:delete).with("new_ID_foo", "new_Revision_foo")
|
62
|
+
|
63
|
+
@view_content_no_rows = %{
|
64
|
+
{"total_rows":3,"rows":[]}
|
65
|
+
}
|
66
|
+
response = HTTPResponse.new(@view_content_no_rows)
|
67
|
+
|
68
|
+
another_apartment = @apartment.clone
|
69
|
+
@apartment.new?.should == true
|
70
|
+
another_apartment.new?.should == true
|
71
|
+
|
72
|
+
@building.apartments << @apartment << another_apartment
|
73
|
+
@building.save
|
74
|
+
|
75
|
+
@apartment.new?.should == false
|
76
|
+
another_apartment.new?.should == false
|
77
|
+
|
78
|
+
# They have been set up identically, so we have to cheat and change
|
79
|
+
# the ID of one of the apartments
|
80
|
+
another_apartment.instance_variable_set("@id", "new_ID_foo")
|
81
|
+
another_apartment.instance_variable_set("@revision", "new_Revision_foo")
|
82
|
+
|
83
|
+
@building.delete
|
84
|
+
@building.id.should == nil
|
85
|
+
@building.revision.should == nil
|
86
|
+
|
87
|
+
@apartment.id.should == nil
|
88
|
+
@apartment.revision.should == nil
|
89
|
+
|
90
|
+
another_apartment.id.should == nil
|
91
|
+
another_apartment.revision.should == nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "deleting an object that is in a relation should notify the owner" do
|
95
|
+
@building.apartments << @apartment
|
96
|
+
@apartment.apartment_building.should_not == nil
|
97
|
+
@building.apartments.size.should == 1
|
98
|
+
@apartment.delete
|
99
|
+
@building.apartments.size.should == 0
|
100
|
+
@apartment.apartment_building.should == nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should be able to delete has_many relations" do
|
104
|
+
@building.apartments << @apartment
|
105
|
+
@apartment.apartment_building.should_not == nil
|
106
|
+
@building.apartments.size.should == 1
|
107
|
+
@building.apartments.delete(@apartment)
|
108
|
+
@building.apartments.size.should == 0
|
109
|
+
@apartment.apartment_building.should == nil
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/persistable_helper.rb'
|
2
|
+
|
3
|
+
# This tests are bullshit really...
|
4
|
+
# Check if the errors exist and call their message parameters too...
|
5
|
+
# gives us good rcov coverage...
|
6
|
+
|
7
|
+
describe CouchObject::Errors, "errormessages: " do
|
8
|
+
it "CantCompareSize" do
|
9
|
+
error = CouchObject::Errors::CantCompareSize.new
|
10
|
+
error.message.should == "Can't compare the size of two objects unless at least one of them has been saved and both have timestamps."
|
11
|
+
end
|
12
|
+
|
13
|
+
it "CantCompareSize" do
|
14
|
+
error = CouchObject::Errors::NoDatabaseLocationSet.new
|
15
|
+
error.message.should == "Unless the document has previously been saved you need to supply the full URI to the CouchDB server where the document is to be saved."
|
16
|
+
end
|
17
|
+
|
18
|
+
it "HasManyAssociationError" do
|
19
|
+
error = CouchObject::Errors::HasManyAssociationError.new
|
20
|
+
error.message
|
21
|
+
end
|
22
|
+
it "BelongsToAssociationError" do
|
23
|
+
error = CouchObject::Errors::BelongsToAssociationError.new
|
24
|
+
error.message
|
25
|
+
end
|
26
|
+
it "MissingView" do
|
27
|
+
error = CouchObject::Errors::MissingView.new
|
28
|
+
error.message
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/persistable_helper.rb'
|
2
|
+
|
3
|
+
# This tests are bullshit really...
|
4
|
+
# Check if the errors exist and call their message parameters too...
|
5
|
+
# gives us good rcov coverage...
|
6
|
+
|
7
|
+
describe CouchObject::Errors, "errormessages: " do
|
8
|
+
it "CantCompareSize" do
|
9
|
+
error = CouchObject::Errors::CantCompareSize.new
|
10
|
+
error.message.should == "Can't compare the size of two objects unless at least one of them has been saved and both have timestamps."
|
11
|
+
end
|
12
|
+
|
13
|
+
it "CantCompareSize" do
|
14
|
+
error = CouchObject::Errors::NoDatabaseLocationSet.new
|
15
|
+
error.message.should == "Unless the document has previously been saved you need to supply the full URI to the CouchDB server where the document is to be saved."
|
16
|
+
end
|
17
|
+
|
18
|
+
it "HasManyAssociationError" do
|
19
|
+
error = CouchObject::Errors::HasManyAssociationError.new
|
20
|
+
error.message
|
21
|
+
end
|
22
|
+
it "BelongsToAssociationError" do
|
23
|
+
error = CouchObject::Errors::BelongsToAssociationError.new
|
24
|
+
error.message
|
25
|
+
end
|
26
|
+
it "MissingView" do
|
27
|
+
error = CouchObject::Errors::MissingView.new
|
28
|
+
error.message
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/persistable_helper.rb'
|
2
|
+
|
3
|
+
describe CouchObject::Persistable, "for loading objects:" do
|
4
|
+
before(:each) do
|
5
|
+
@bike = Bike.new
|
6
|
+
@with_location = WithStorageLocation.new
|
7
|
+
|
8
|
+
@db = mock("mock db")
|
9
|
+
|
10
|
+
@content_bike = %{
|
11
|
+
{
|
12
|
+
"_id":"123BAC",
|
13
|
+
"_rev":"946B7D1C",
|
14
|
+
"class":"Bike",
|
15
|
+
"attributes":
|
16
|
+
{
|
17
|
+
"wheels":2
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
@content_motor_bike = %{
|
23
|
+
{
|
24
|
+
"_id":"123BAC",
|
25
|
+
"_rev":"946B7D1C",
|
26
|
+
"class":"MotorBike",
|
27
|
+
"attributes":
|
28
|
+
{
|
29
|
+
"wheels":2
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
@content_with_location = %{
|
35
|
+
{
|
36
|
+
"_id":"123BAC",
|
37
|
+
"_rev":"946B7D1C",
|
38
|
+
"class":"WithStorageLocation",
|
39
|
+
"attributes":
|
40
|
+
{}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should get document by id" do
|
48
|
+
response = HTTPResponse.new(@content_bike)
|
49
|
+
|
50
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
51
|
+
|
52
|
+
bike = Bike.get_by_id("123BAC", "foo")
|
53
|
+
bike.id.should == "123BAC"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should instantiate a new object WITH a :from_couch method when loaded from the document store" do
|
57
|
+
response = HTTPResponse.new(@content_bike)
|
58
|
+
|
59
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
60
|
+
|
61
|
+
bike = Bike.get_by_id("123BAC", "foo")
|
62
|
+
bike.class.should == Bike
|
63
|
+
bike.wheels.should == 2
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should instantiate a new object WITHOUT a :from_couch method when loaded from the document store" do
|
67
|
+
response = HTTPResponse.new(@content_motor_bike)
|
68
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
69
|
+
|
70
|
+
motorbike = MotorBike.get_by_id("123BAC", "foo")
|
71
|
+
motorbike.class.should == MotorBike
|
72
|
+
motorbike.wheels.should == 2
|
73
|
+
end
|
74
|
+
|
75
|
+
it "classes with the storage location preset should be able to load documents by only supplying the ID" do
|
76
|
+
response = HTTPResponse.new(@content_with_location)
|
77
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
78
|
+
|
79
|
+
with_content = WithStorageLocation.get_by_id("123BAC")
|
80
|
+
with_content.id.should == "123BAC"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should raise an error if a class that does not have the database uri set by default tries to laod a document only by ID" do
|
84
|
+
lambda{ bike = Bike.get_by_id("123BAC") }.
|
85
|
+
should raise_error(CouchObject::Errors::NoDatabaseLocationSet)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should raise an ID if the object doesn't exist in the database" do
|
89
|
+
response_missing = HTTPResponse.
|
90
|
+
new(%{{"error":"not_found","reason":"missing"}})
|
91
|
+
response_deleted = HTTPResponse.
|
92
|
+
new(%{{"error":"not_found","reason":"deleted"}})
|
93
|
+
|
94
|
+
@db.should_receive(:get).with("123BAC_1").and_return(response_missing)
|
95
|
+
@db.should_receive(:get).with("123BAC_2").and_return(response_deleted)
|
96
|
+
|
97
|
+
lambda{ bike = Bike.get_by_id("123BAC_1", "foo_db") }.
|
98
|
+
should raise_error(CouchObject::Errors::DocumentNotFound)
|
99
|
+
|
100
|
+
lambda{ bike = Bike.get_by_id("123BAC_2", "foo_db") }.
|
101
|
+
should raise_error(CouchObject::Errors::DocumentNotFound)
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
describe CouchObject::Persistable, "for loading object with subobject:" do
|
108
|
+
before(:each) do
|
109
|
+
@obj = WithSubobject.new
|
110
|
+
|
111
|
+
@db = mock("mock db")
|
112
|
+
|
113
|
+
@json_string = %{
|
114
|
+
{
|
115
|
+
"_id":"88F411C76D243294A8108DB25F2F8315",
|
116
|
+
"_rev":"4221814729",
|
117
|
+
"class":"WithSubobject",
|
118
|
+
"attributes":
|
119
|
+
{
|
120
|
+
"a_class":
|
121
|
+
{
|
122
|
+
"updated_at":
|
123
|
+
{
|
124
|
+
"json_class":"Time",
|
125
|
+
"s":1202756456,
|
126
|
+
"u":955933
|
127
|
+
},
|
128
|
+
"class":"Subobject",
|
129
|
+
"attributes":
|
130
|
+
{
|
131
|
+
"some_other_class":
|
132
|
+
{
|
133
|
+
"updated_at":
|
134
|
+
{
|
135
|
+
"json_class":"Time",
|
136
|
+
"s":1202756456,
|
137
|
+
"u":955986
|
138
|
+
},
|
139
|
+
"class":"SubSubobject",
|
140
|
+
"attributes":
|
141
|
+
{
|
142
|
+
"value":"New value"
|
143
|
+
},
|
144
|
+
"created_at":
|
145
|
+
{
|
146
|
+
"json_class":"Time",
|
147
|
+
"s":1202756456,
|
148
|
+
"u":955989
|
149
|
+
}
|
150
|
+
}
|
151
|
+
},
|
152
|
+
"created_at":
|
153
|
+
{
|
154
|
+
"json_class":"Time",
|
155
|
+
"s":1202756456,
|
156
|
+
"u":955936
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should initialize all levels of subclasses" do
|
167
|
+
response = HTTPResponse.new(@json_string)
|
168
|
+
@db.should_receive(:get).with("123BAC").and_return(response)
|
169
|
+
|
170
|
+
with_subobject = WithSubobject.get_by_id("123BAC")
|
171
|
+
with_subobject.a_class.class.should == Subobject
|
172
|
+
with_subobject.a_class.some_other_class.class.should == SubSubobject
|
173
|
+
with_subobject.a_class.some_other_class.value.should == "New value"
|
174
|
+
with_subobject.a_class.some_other_class.created_at.class.should == Time
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe CouchObject::Persistable, "for loading objects from a view" do
|
179
|
+
before(:each) do
|
180
|
+
@bike = Bike.new
|
181
|
+
@with_location = WithStorageLocation.new
|
182
|
+
|
183
|
+
@db = mock("mock db")
|
184
|
+
|
185
|
+
@content_bike = %{
|
186
|
+
{
|
187
|
+
"_id":"123BAC",
|
188
|
+
"_rev":"946B7D1C",
|
189
|
+
"class":"Bike",
|
190
|
+
"attributes":
|
191
|
+
{
|
192
|
+
"wheels":2
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
@content_motor_bike = %{
|
198
|
+
{
|
199
|
+
"_id":"123BAC",
|
200
|
+
"_rev":"946B7D1C",
|
201
|
+
"class":"MotorBike",
|
202
|
+
"attributes":
|
203
|
+
{
|
204
|
+
"wheels":2
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
@content_with_location = %{
|
210
|
+
{
|
211
|
+
"_id":"123BAC",
|
212
|
+
"_rev":"946B7D1C",
|
213
|
+
"class":"WithStorageLocation",
|
214
|
+
"attributes":
|
215
|
+
{}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
@view_content = %{
|
220
|
+
{"total_rows":4,"offset":0,"rows":[{"id":"1657A08F51BC66AEFD8630C27BD18E17","key":"1657A08F51BC66AEFD8630C27BD18E17","value":{"_id":"1657A08F51BC66AEFD8630C27BD18E17","_rev":"1626884106","class":"House","attributes":{}}},{"id":"1D7E2C45B946C1C4A7E76016FDAB104B","key":"1D7E2C45B946C1C4A7E76016FDAB104B","value":{"_id":"1D7E2C45B946C1C4A7E76016FDAB104B","_rev":"1069657208","class":"House","attributes":{}}},{"id":"D646E0796DF90D62AE402208E4043216","key":"D646E0796DF90D62AE402208E4043216","value":{"_id":"D646E0796DF90D62AE402208E4043216","_rev":"633260494","class":"House","attributes":{}}},{"id":"D770670E2219EC767BBB55FFB7763ADA","key":"D770670E2219EC767BBB55FFB7763ADA","value":{"_id":"D770670E2219EC767BBB55FFB7763ADA","_rev":"3666401817","class":"House","attributes":{}}}]}
|
221
|
+
}
|
222
|
+
|
223
|
+
@view_content_for_id = %{
|
224
|
+
{"total_rows":4,"offset":0,"rows":[{"id":"1657A08F51BC66AEFD8630C27BD18E17","key":"1657A08F51BC66AEFD8630C27BD18E17","value":{"_id":"1657A08F51BC66AEFD8630C27BD18E17","_rev":"1626884106","class":"House","attributes":{}}}]}
|
225
|
+
}
|
226
|
+
|
227
|
+
@view_content_mixed = %{
|
228
|
+
{"total_rows":2,"offset":0,"rows":[{"id":"5030848C1F2E31820ADF6B22832037DE","key":"5030848C1F2E31820ADF6B22832037DE","value":{"_id":"5030848C1F2E31820ADF6B22832037DE","_rev":"2247680957","class":"Bike","attributes":{}}},{"id":"D646E0796DF90D62AE402208E4043216","key":"D646E0796DF90D62AE402208E4043216","value":{"_id":"D646E0796DF90D62AE402208E4043216","_rev":"633260494","class":"House","attributes":{}}}]}
|
229
|
+
}
|
230
|
+
|
231
|
+
@view_content_subclasses = %{
|
232
|
+
{"total_rows":1,"offset":0,"rows":[{"id":"5A89FEF09C2E63E040B65909E277B604","key":"5A89FEF09C2E63E040B65909E277B604","value":{"_id":"5A89FEF09C2E63E040B65909E277B604","_rev":"4002434601","class":"WithSubobject","attributes":{"a_class":{"updated_at":{"json_class":"Time","s":1203104797,"u":954565},"class":"Subobject","attributes":{"some_other_class":{"updated_at":{"json_class":"Time","s":1203104797,"u":954624},"class":"SubSubobject","attributes":{"value":"Initialized"},"created_at":{"json_class":"Time","s":1203104797,"u":954627}}},"created_at":{"json_class":"Time","s":1203104797,"u":954569}}}}}]}
|
233
|
+
}
|
234
|
+
|
235
|
+
@view_content_no_rows = %{
|
236
|
+
{"total_rows":3,"rows":[]}
|
237
|
+
}
|
238
|
+
|
239
|
+
@view_content_missing_view = %{
|
240
|
+
{"error":"not_found","reason":"missing"}
|
241
|
+
}
|
242
|
+
|
243
|
+
CouchObject::Database.stub!(:open).and_return(@db)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should raise errors for missing arguments" do
|
247
|
+
lambda{bike = Bike.get_from_view()}.should \
|
248
|
+
raise_error(ArgumentError)
|
249
|
+
|
250
|
+
lambda{bike = Bike.get_from_view("foo_view")}.should \
|
251
|
+
raise_error(CouchObject::Errors::NoDatabaseLocationSet)
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should return an array with four elements of class type House" do
|
255
|
+
response = HTTPResponse.new(@view_content)
|
256
|
+
@db.should_receive(:get).with("foo").and_return(response)
|
257
|
+
|
258
|
+
new_instances = Bike.get_from_view("foo", {:db_uri => "foo"})
|
259
|
+
|
260
|
+
new_instances.should be_a_kind_of(Array)
|
261
|
+
new_instances.size.should == 4
|
262
|
+
new_instances.each do |instance|
|
263
|
+
instance.class.should == House
|
264
|
+
instance.new?.should == false
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should be able to get an element by key" do
|
269
|
+
response = HTTPResponse.new(@view_content_for_id)
|
270
|
+
@db.should_receive(:get).with("foo?key=%221657A08F51BC66AEFD8630C27BD18E17%22").and_return(response)
|
271
|
+
|
272
|
+
new_instances = Bike.get_from_view("foo", {:db_uri => "foo", :key => "1657A08F51BC66AEFD8630C27BD18E17"})
|
273
|
+
|
274
|
+
new_instances.should be_a_kind_of(Array)
|
275
|
+
new_instances.size.should == 1
|
276
|
+
new_instances.first.class.should == House
|
277
|
+
new_instances.first.new?.should == false
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
it "should return a blank array when sending in a non existent key" do
|
282
|
+
response = HTTPResponse.new(@view_content_no_rows)
|
283
|
+
@db.should_receive(:get).with("foo?key=%22D18E17%22").and_return(response)
|
284
|
+
|
285
|
+
new_instances = Bike.get_from_view("foo",
|
286
|
+
{:db_uri => "foo", :key => "D18E17"})
|
287
|
+
|
288
|
+
new_instances.should be_a_kind_of(Array)
|
289
|
+
new_instances.size.should == 0
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
it "should be able to return instances from a view with different objects" do
|
295
|
+
response = HTTPResponse.new(@view_content_mixed)
|
296
|
+
@db.should_receive(:get).with("foo").and_return(response)
|
297
|
+
|
298
|
+
new_instances = Bike.get_from_view("foo", {:db_uri => "foo"})
|
299
|
+
|
300
|
+
new_instances.should be_a_kind_of(Array)
|
301
|
+
new_instances.size.should == 2
|
302
|
+
new_instances[0].class.should == Bike
|
303
|
+
new_instances[0].new?.should == false
|
304
|
+
|
305
|
+
new_instances[1].class.should == House
|
306
|
+
new_instances[1].new?.should == false
|
307
|
+
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should be able to load classes that have subclasses from a view" do
|
312
|
+
response = HTTPResponse.new(@view_content_subclasses)
|
313
|
+
@db.should_receive(:get).with("foo").and_return(response)
|
314
|
+
|
315
|
+
new_instances = WithSubobject.get_from_view("foo", {:db_uri => "foo"})
|
316
|
+
|
317
|
+
new_instances.should be_a_kind_of(Array)
|
318
|
+
new_instances.size.should == 1
|
319
|
+
new_instances.first.class.should == WithSubobject
|
320
|
+
new_instances.first.new?.should == false
|
321
|
+
|
322
|
+
new_instances.first.a_class.class.should == Subobject
|
323
|
+
new_instances.first.a_class.some_other_class.class.should == SubSubobject
|
324
|
+
new_instances.first.a_class.some_other_class.value.should == "Initialized"
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should raise an error for non existing views" do
|
329
|
+
response = HTTPResponse.new(@view_content_missing_view)
|
330
|
+
@db.should_receive(:get).with("_view/doesnt_exist").and_return(response)
|
331
|
+
|
332
|
+
lambda{Bike.get_from_view("_view/doesnt_exist",
|
333
|
+
{:db_uri => "foo"})}.
|
334
|
+
should raise_error(CouchObject::Errors::MissingView)
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|