~free.ekanayaka/storm/infoheritance

« back to all changes in this revision

Viewing changes to tests/store/base.py

  • Committer: Free Ekanayaka
  • Date: 2011-10-25 10:17:32 UTC
  • Revision ID: free.ekanayaka@canonical.com-20111025101732-chgjhm27mg3rw8o4
Add polymorphic support to Reference

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from uuid import uuid4
28
28
import weakref
29
29
 
30
 
from storm.references import Reference, ReferenceSet, Proxy
 
30
from storm.references import (
 
31
    Reference, ReferenceSet, Proxy, poly_type, register_poly_type,
 
32
    unregister_poly_type)
31
33
from storm.database import Result, STATE_DISCONNECTED
32
34
from storm.properties import (
33
35
    Int, Float, RawStr, Unicode, Property, Pickle, UUID)
125
127
    foo = Reference(foo_id, Foo.id)
126
128
    foo_title = Proxy(foo, Foo.title)
127
129
 
 
130
 
 
131
class Egg(object):
 
132
    __storm_table__ = "egg"
 
133
    id = Int(primary=True, default=AutoReload)
 
134
    info_type = Int()
 
135
    info = Reference(id, "egg_id", on_remote=True, poly_type=info_type)
 
136
 
 
137
    def __init__(self, info_class=None):
 
138
        if info_class is not None:
 
139
            self.info = info_class()
 
140
 
 
141
@poly_type(1, on=Egg.info)
 
142
class StoredEggInfo(object):
 
143
    __storm_table__ = "stored_egg_info"
 
144
    egg_id = Int(primary=True)
 
145
    egg = Reference(egg_id, Egg.id)
 
146
 
 
147
@poly_type(2, on=Egg.info)
 
148
class MemoryEggInfo(object):
 
149
    def __init__(self):
 
150
        self.created = True
 
151
 
 
152
    def link(self, egg):
 
153
        self.created = False
 
154
        self.egg = egg
 
155
 
128
156
class Money(object):
129
157
    __storm_table__ = "money"
130
158
    id = Int(primary=True)
267
295
 
268
296
    def drop_tables(self):
269
297
        for table in ["foo", "bar", "bin", "link", "money", "selfref",
270
 
                      "foovalue", "unique_id"]:
 
298
                      "foovalue", "unique_id", "egg", "stored_egg_info"]:
271
299
            try:
272
300
                self.connection.execute("DROP TABLE %s" % table)
273
301
                self.connection.commit()
5756
5784
            self.assertEquals(bar.foo_id, 20)
5757
5785
            self.assertEquals(link.foo_id, 20)
5758
5786
 
 
5787
    def test_poly_register(self):
 
5788
        """
 
5789
        It's possible to register new polymorphic types using the C{poly_type}
 
5790
        decorator.
 
5791
        """
 
5792
        self.assertEqual(1, StoredEggInfo.info_type)
 
5793
        self.assertEqual(2, MemoryEggInfo.info_type)
 
5794
 
 
5795
    def test_poly_register_with_duplicate_type(self):
 
5796
        """
 
5797
        An error is raised when trying to register the same info type twice
 
5798
        for the same Reference.
 
5799
        """
 
5800
 
 
5801
        class OtherEggInfo(object):
 
5802
            pass
 
5803
 
 
5804
        self.assertRaises(RuntimeError, register_poly_type, OtherEggInfo,
 
5805
                          MemoryEggInfo.info_type, Egg.info)
 
5806
 
 
5807
    def test_poly_unregister(self):
 
5808
        """
 
5809
        It's possible to unregister a poly type.
 
5810
        """
 
5811
 
 
5812
        class OtherEggInfo(object):
 
5813
            pass
 
5814
 
 
5815
        register_poly_type(OtherEggInfo, 3, Egg.info)
 
5816
        self.assertEqual(3, OtherEggInfo.info_type)
 
5817
        self.assertIs(OtherEggInfo, Egg.info._poly_lookup[3])
 
5818
        unregister_poly_type(OtherEggInfo, Egg.info)
 
5819
        self.assertIs(None, Egg.info._poly_lookup.get(3))
 
5820
 
 
5821
 
 
5822
    def test_poly_in_memory_init(self):
 
5823
        """
 
5824
        In-memory remote objects are cached while the local object is alive,
 
5825
        but when they're recreated, their C{__init__} should not be called
 
5826
        again.
 
5827
        """
 
5828
        egg = Egg()
 
5829
        egg.info = MemoryEggInfo()
 
5830
        self.store.add(egg)
 
5831
        self.store.flush()
 
5832
        egg_id = egg.id
 
5833
        self.assertTrue(egg.info.created)
 
5834
        self.store.invalidate(egg)
 
5835
        del egg
 
5836
        gc.collect()
 
5837
        egg = self.store.get(Egg, egg_id)
 
5838
        self.assertFalse(egg.info.created)
 
5839
        self.assertIs(egg, egg.info.egg)
 
5840
 
 
5841
    def test_poly_wb_relations_are_built_lazily(self):
 
5842
        """
 
5843
        The relations for the registered poly types are built lazily.
 
5844
        """
 
5845
        Egg.info._relation.clear()
 
5846
        egg = Egg()
 
5847
        egg.info = StoredEggInfo()
 
5848
        self.assertEqual([1], Egg.info._relation.keys())
 
5849
        egg.info = MemoryEggInfo()
 
5850
        self.assertEqual(sorted([1, 2]), sorted(Egg.info._relation.keys()))
 
5851
 
 
5852
    def test_poly_with_type_override(self):
 
5853
        """
 
5854
        When the local and remote poly type values don't match, the local one
 
5855
        is overriden when setting the reference.
 
5856
        """
 
5857
        egg = Egg()
 
5858
        egg.info_type = 9999
 
5859
        egg.info = StoredEggInfo()
 
5860
        self.assertEqual(StoredEggInfo.info_type, egg.info_type)
 
5861
 
 
5862
    def test_poly_create_in_memory_object(self):
 
5863
        """
 
5864
        When setting the local reference to a new in-memory remote object, the
 
5865
        C{link} method of the remote object is not invoked.
 
5866
        """
 
5867
        egg = Egg(MemoryEggInfo)
 
5868
        self.store.add(egg)
 
5869
        self.store.flush()
 
5870
        self.assertEqual(MemoryEggInfo.info_type, egg.info_type)
 
5871
        self.assertTrue(egg.info.created)
 
5872
 
 
5873
    def test_poly_link_in_memory_remote(self):
 
5874
        """
 
5875
        If the remote object is an in memory object, its C{link} method is
 
5876
        invoked when it's linked to a Storm object loaded from the store.
 
5877
        """
 
5878
        egg = Egg()
 
5879
        egg.info_type = MemoryEggInfo.info_type
 
5880
        self.store.add(egg)
 
5881
        self.store.flush()
 
5882
        self.assertFalse(egg.info.created)
 
5883
        self.assertIs(egg, egg.info.egg)
 
5884
 
 
5885
    def test_poly_create_stored_remote(self):
 
5886
        """
 
5887
        When a setting the local reference to a remote object, the remote
 
5888
        key is set to the local key.
 
5889
        """
 
5890
        egg = Egg()
 
5891
        egg_info = StoredEggInfo()
 
5892
        egg.info = egg_info
 
5893
        self.store.add(egg)
 
5894
        self.store.flush()
 
5895
        self.assertEqual(StoredEggInfo.info_type, egg.info_type)
 
5896
        self.assertEqual(egg.id, egg_info.egg_id)
 
5897
        self.assertIs(egg, egg_info.egg)
 
5898
        self.assertEqual(egg.id, egg_info.egg.id)
 
5899
 
 
5900
    def test_poly_link_stored_remote(self):
 
5901
        """
 
5902
        When the remote object is a Storm object, the local key value is used
 
5903
        to find the matching remote key.
 
5904
        """
 
5905
        egg = Egg()
 
5906
        egg.id = 1234
 
5907
        egg.info_type = StoredEggInfo.info_type
 
5908
        egg_info1 = StoredEggInfo()
 
5909
        egg_info1.egg_id = egg.id
 
5910
        egg_info2 = StoredEggInfo()
 
5911
        egg_info2.egg_id = 9999
 
5912
        self.store.add(egg)
 
5913
        self.store.add(egg_info1)
 
5914
        self.store.add(egg_info2)
 
5915
        self.store.flush()
 
5916
        self.assertIs(egg_info1, egg.info)
 
5917
 
 
5918
    def test_poly_link_with_change_type(self):
 
5919
        """
 
5920
        When changing the type of the linked remote object, the poly type
 
5921
        column on the local object gets changed accordingly.
 
5922
        """
 
5923
        egg = Egg(StoredEggInfo)
 
5924
        self.assertEqual(StoredEggInfo.info_type, egg.info_type)
 
5925
 
 
5926
        egg.info = MemoryEggInfo()
 
5927
        self.assertEqual(MemoryEggInfo.info_type, egg.info_type)
 
5928
 
 
5929
    def test_poly_with_unlink_stored_remote(self):
 
5930
        """
 
5931
        If the reference is linked to a remote object, when setting it to
 
5932
        C{None} the poly type on the local object gets reset too.
 
5933
        """
 
5934
        egg = Egg(StoredEggInfo)
 
5935
        egg.id = 1234
 
5936
        egg_info = egg.info
 
5937
        self.assertEqual(StoredEggInfo.info_type, egg.info_type)
 
5938
        self.assertEqual(1234, egg_info.egg_id)
 
5939
 
 
5940
        egg.info = None
 
5941
        self.assertEqual(None, egg.info_type)
 
5942
        self.assertEqual(None, egg_info.egg_id)
 
5943
 
5759
5944
    def test_get_decimal_property(self):
5760
5945
        money = self.store.get(Money, 10)
5761
5946
        self.assertEquals(money.value, decimal.Decimal("12.3455"))