Skip to content

Commit

Permalink
Cross-collection referencing now working in its basic form.
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotcm committed Mar 20, 2010
1 parent 4bb029b commit c140a4f
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 45 deletions.
1 change: 0 additions & 1 deletion integration/cross_collection_relationships_spec.rb
Expand Up @@ -22,7 +22,6 @@ class Person
end

it "retains a reference to the embedded document in its container document." do
pending
Article.find.first.author.should == @dave
end

Expand Down
11 changes: 3 additions & 8 deletions lib/document/persistence.rb
Expand Up @@ -51,14 +51,8 @@ def create(params)
new_object.save
return new_object
end

def collection=(collection)
@@collection = collection
end

def collection
@@collection
end

attr_accessor :collection

def index(key_name, options={})
return if Util.blank?(key_name)
Expand All @@ -76,6 +70,7 @@ def index(key_name, options={})
end

def find(query=nil)
query = {'_id' => query} unless query.nil? or query.is_a?(Hash)
collection.find(query).map{|bson_hash| new(bson_hash)}
end

Expand Down
31 changes: 4 additions & 27 deletions lib/document/serialization.rb
Expand Up @@ -21,31 +21,6 @@ def self.marshal_depth

module Document
module Serialization
class <<self
def from_hash(hash, object)
hash.each_pair do |attribute_key, attribute_value|
if attribute_value.is_a?(Hash) and attribute_value.has_key?('_class_name')
if attribute_value.has_key?('_data')
attribute_value = Marshal.load(attribute_value['_data'])
else
embedded_hash = attribute_value
klass = embedded_hash.delete('_class_name')
attribute_value = from_hash(embedded_hash, Kernel.const_get(klass).new)
end
end

object.instance_variable_set('@'+attribute_key.to_s, attribute_value)
end

return object
end

def recursively_hash_object(object)
# LightMongo::Document
return object.export if persistable?(object)
end
end

def initialize(params={})
self.from_hash(params)
end
Expand All @@ -55,13 +30,15 @@ def to_hash(current_depth=0)
end

def from_hash(hash)
Serialization.from_hash(hash, self)
Serializer.deserialize(hash).each_pair do |attr_name, attr_value|
self.instance_variable_set '@'+attr_name.to_s, attr_value
end
end

def export
return self unless self.class.include?(LightMongo::Document::Persistence)
self.save
{'_class_name' => self.class.name, '_id' => self.id}
{'_class_name' => self.class.name, '_id' => self.id, '_embed' => true}
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/document/serialization/hash_serializer.rb
Expand Up @@ -36,20 +36,20 @@ def serialize_object(object_to_serialize, current_depth)
return object_to_serialize if natively_embeddable?(object_to_serialize)

return object_to_serialize.export if object_to_serialize.is_a? LightMongo::Document and current_depth > 0


return hashify(object_to_serialize, current_depth)
end

def hashify(object_to_serialize, current_depth)
hashed_object = {'_class_name' => object_to_serialize.class.name}
hashed_object = {}
hashed_object['_class_name'] = object_to_serialize.class.name if current_depth > 0

object_to_serialize.instance_variables.each do |attribute_name|
new_hash_key = attribute_name.sub(/^@/, '')
nested_object = object_to_serialize.instance_variable_get(attribute_name)
hashed_object[new_hash_key] = Serializer.serialize(nested_object, current_depth + 1)
end

return hashed_object
end

Expand Down
45 changes: 40 additions & 5 deletions lib/document/serialization/serializer.rb
Expand Up @@ -6,10 +6,45 @@ module Serialization
class Serializer
attr_accessor :depth

def self.serialize(object_to_serialize, depth=0)
serializer = Serializer.new(object_to_serialize, depth)
return serializer.hash_serialize if LightMongo.slow_serialization or depth < LightMongo.marshal_depth
serializer.marshal
class <<self
def deserialize(object_to_deserialize)
return array_deserialize(object_to_deserialize) if object_to_deserialize.is_a?(Array)
if object_to_deserialize.is_a?(Hash)
if object_to_deserialize.has_key?('_data')
return Marshal.load(object_to_deserialize['_data'])
end

if object_to_deserialize.has_key?('_embed') and object_to_deserialize['_embed'] == true
class_name = object_to_deserialize.delete('_class_name')
return Object.const_get(class_name).find(object_to_deserialize['_id']).first
end

return hash_deserialize(object_to_deserialize)
end

return object_to_deserialize
end

def serialize(object_to_serialize, depth=0)
serializer = Serializer.new(object_to_serialize, depth)
return serializer.hash_serialize if LightMongo.slow_serialization or depth < LightMongo.marshal_depth
serializer.marshal
end

private
def array_deserialize(array)
array.map do |entry|
deserialize(entry)
end
end

def hash_deserialize(hash)
deserialized_hash = {}
hash.each_pair do |key, value|
deserialized_hash[key] = deserialize(value)
end
return deserialized_hash
end
end

def initialize(object_to_serialize, depth=0)
Expand All @@ -18,7 +53,7 @@ def initialize(object_to_serialize, depth=0)
end

def marshal
Marshal.dump(@object_to_serialize)
{'_data' => Marshal.dump(@object_to_serialize)}
end

def hash_serialize
Expand Down
119 changes: 118 additions & 1 deletion spec/document/serialization/serializer_spec.rb
Expand Up @@ -5,6 +5,123 @@
@object = mock(:object)
end

describe ".array_deserialize(object)" do
before(:each) do
@object = [
@ivar1 = mock(:ivar1),
@ivar2 = mock(:ivar2)
]
end

it "map-deserializes each element in the array" do
LightMongo::Document::Serialization::Serializer.
should_receive(:deserialize).with(@ivar1).
and_return(desel_ivar1 = mock(:desel_ivar1))

LightMongo::Document::Serialization::Serializer.
should_receive(:deserialize).with(@ivar2).
and_return(desel_ivar2 = mock(:desel_ivar2))

LightMongo::Document::Serialization::Serializer.send(:array_deserialize, @object).
should == [desel_ivar1, desel_ivar2]
end
end

describe ".hash_deserialize(object)" do
before(:each) do
@object = {
:ivar1 => (@ivar1 = mock(:ivar1)),
:ivar2 => (@ivar2 = mock(:ivar2))
}
end

it "map-deserializes each element in the hash" do
LightMongo::Document::Serialization::Serializer.
should_receive(:deserialize).with(@ivar1).
and_return(desel_ivar1 = mock(:desel_ivar1))

LightMongo::Document::Serialization::Serializer.
should_receive(:deserialize).with(@ivar2).
and_return(desel_ivar2 = mock(:desel_ivar2))

LightMongo::Document::Serialization::Serializer.send(:hash_deserialize, @object).
should == {:ivar1 => desel_ivar1, :ivar2 => desel_ivar2}
end
end

describe ".deserialize(object)" do
context "when the object is an array" do
before(:each) do
@object = []
end

it "deserializes the array" do
LightMongo::Document::Serialization::Serializer.
should_receive(:array_deserialize).with(@object).
and_return(desel_array = mock(:desel_array))

LightMongo::Document::Serialization::Serializer.deserialize(@object).
should == desel_array
end
end

context "when the object is a hash" do
before(:each) do
@object = {}
end

context "and nothing more" do
it "deserializes the hash" do
LightMongo::Document::Serialization::Serializer.should_receive(:hash_deserialize).with(@object)
LightMongo::Document::Serialization::Serializer.deserialize(@object)
end
end

context "and a Marshal dump" do
before(:each) do
@marshal_dump = mock(:marshal_dump)
@object['_data'] = @marshal_dump
end

it "unmarshals the dump" do
Marshal.should_receive(:load).with(@marshal_dump).
and_return(unmarshalled_data = mock(:unmarshalled_data))

LightMongo::Document::Serialization::Serializer.deserialize(@object).
should == unmarshalled_data
end
end

context "and a LightMongo::Document" do
before(:each) do
class TestClass
end

@id = mock(:id)
@object = {'_id' => @id, '_class_name' => 'TestClass'}
end

it "recovers the linked document" do
TestClass.stub!(:find).with(@id).and_return(test_instance = mock(:test_instance))

LightMongo::Document::Serialization::Serializer.deserialize(@object).
should == test_instance
end
end
end

context "when the object is anything else" do
before(:each) do
@object = 3
end

it "returns the object unharmed" do
LightMongo::Document::Serialization::Serializer.deserialize(@object).
should == @object
end
end
end

describe ".serialize(object, current_depth)" do
before(:each) do
@depth = 0
Expand Down Expand Up @@ -102,7 +219,7 @@ def self.it_returns_the_hash_serialized_object
Marshal.stub!(:dump).
with(@object).
and_return(marshalled_object = mock(:marshalled_object))
LightMongo::Document::Serialization::Serializer.new(@object).marshal.should == marshalled_object
LightMongo::Document::Serialization::Serializer.new(@object).marshal.should == {'_data' => marshalled_object}
end
end

Expand Down

0 comments on commit c140a4f

Please sign in to comment.