def __init__(self, event_store, use_cache=False, snapshot_strategy=None):
        self._cache = {}
        self._use_cache = use_cache
        self._snapshot_strategy = snapshot_strategy

        # Check we got an event store.
        assert isinstance(event_store, AbstractEventStore)
        self.event_store = event_store

        # Check domain class is a type of event sourced entity.
        assert issubclass(self.domain_class, EventSourcedEntity)

        # Instantiate an event player for this repo, with
        # repo-specific mutate function, page size, etc.
        self.event_player = EventPlayer(
            event_store=self.event_store,
            id_prefix=id_prefix_from_entity_class(self.domain_class),
            mutate_func=self.domain_class.mutate,
            page_size=self.__page_size__ or self.domain_class.__page_size__,
            is_short=self.__is_short__ or self.domain_class.__is_short__,
            snapshot_strategy=self._snapshot_strategy,
        )
    def test_get_entity(self):
        # Setup an event store, using Python objects.
        event_store = EventStore(stored_event_repo=PythonObjectsStoredEventRepository())

        # Store example events.
        event1 = Example.Created(entity_id='entity1', a=1, b=2)
        event_store.append(event1)
        event2 = Example.Created(entity_id='entity2', a=2, b=4)
        event_store.append(event2)
        event3 = Example.Created(entity_id='entity3', a=3, b=6)
        event_store.append(event3)
        event4 = Example.Discarded(entity_id='entity3', entity_version=1)
        event_store.append(event4)

        # Check the event sourced entities are correct.
        # - just use a trivial mutate that always instantiates the 'Example'.
        event_player = EventPlayer(event_store=event_store, id_prefix='Example', mutate_func=Example.mutate)

        # The the reconstituted entity has correct attribute values.
        self.assertEqual('entity1', event_player.replay_events('entity1').id)
        self.assertEqual(1, event_player.replay_events('entity1').a)
        self.assertEqual(2, event_player.replay_events('entity2').a)
        self.assertEqual(None, event_player.replay_events('entity3'))

        # Check entity3 raises KeyError.
        self.assertEqual(event_player.replay_events('entity3'), None)

        # Check it works for "short" entities (should be faster, but the main thing is that it still works).
        # - just use a trivial mutate that always instantiates the 'Example'.
        event5 = Example.AttributeChanged(entity_id='entity1', entity_version=1, name='a', value=10)
        event_store.append(event5)

        event_player = EventPlayer(event_store=event_store, id_prefix='Example', mutate_func=Example.mutate)
        self.assertEqual(10, event_player.replay_events('entity1').a)

        event_player = EventPlayer(
            event_store=event_store,
            id_prefix='Example',
            mutate_func=Example.mutate,
            is_short=True,
        )
        self.assertEqual(10, event_player.replay_events('entity1').a)
    def test_with_snapshots(self):
        # Check the EventPlayer's take_snapshot() method.
        stored_event_repo = PythonObjectsStoredEventRepository()
        event_store = EventStore(stored_event_repo)
        self.ps = PersistenceSubscriber(event_store)
        event_player = EventPlayer(
            event_store=event_store,
            id_prefix='Example',
            mutate_func=Example.mutate,
            snapshot_strategy=EventSourcedSnapshotStrategy(event_store=event_store)
        )

        # Check the method returns None when there are no events.
        snapshot = event_player.take_snapshot('wrong')
        self.assertIsNone(snapshot)

        # Create a new entity.
        example = register_new_example(a=123, b=234)

        # Take a snapshot with the entity.
        snapshot1 = event_player.take_snapshot(example.id)
        self.assertIsInstance(snapshot1, Snapshot)

        # Take another snapshot with the entity.
        snapshot2 = event_player.take_snapshot(example.id)
        # - should return the previous snapshot
        self.assertIsInstance(snapshot2, Snapshot)
        self.assertEqual(snapshot2.at_event_id, snapshot1.at_event_id)

        # Generate a domain event.
        example.beat_heart()

        # Take another snapshot with the entity.
        # - should use the previous snapshot and the heartbeat event
        snapshot3 = event_player.take_snapshot(example.id)
        self.assertNotEqual(snapshot3.at_event_id, snapshot1.at_event_id)
    def __init__(self, event_store, use_cache=False, snapshot_strategy=None):
        self._cache = {}
        self._use_cache = use_cache
        self._snapshot_strategy = snapshot_strategy

        # Check we got an event store.
        assert isinstance(event_store, AbstractEventStore)
        self.event_store = event_store

        # Check domain class is a type of event sourced entity.
        assert issubclass(self.domain_class, EventSourcedEntity)

        # Instantiate an event player for this repo, with
        # repo-specific mutate function, page size, etc.
        self.event_player = EventPlayer(
            event_store=self.event_store,
            id_prefix=id_prefix_from_entity_class(self.domain_class),
            mutate_func=self.domain_class.mutate,
            page_size=self.__page_size__ or self.domain_class.__page_size__,
            is_short=self.__is_short__ or self.domain_class.__is_short__,
            snapshot_strategy=self._snapshot_strategy,
        )
    def test_with_snapshots(self):
        # Check the EventPlayer's take_snapshot() method.
        stored_event_repo = PythonObjectsStoredEventRepository()
        event_store = EventStore(stored_event_repo)
        self.ps = PersistenceSubscriber(event_store)
        event_player = EventPlayer(
            event_store=event_store,
            id_prefix='Example',
            mutate_func=Example.mutate,
            snapshot_strategy=EventSourcedSnapshotStrategy(
                event_store=event_store))

        # Check the method returns None when there are no events.
        snapshot = event_player.take_snapshot('wrong')
        self.assertIsNone(snapshot)

        # Create a new entity.
        example = register_new_example(a=123, b=234)

        # Take a snapshot with the entity.
        snapshot1 = event_player.take_snapshot(example.id)
        self.assertIsInstance(snapshot1, Snapshot)

        # Take another snapshot with the entity.
        snapshot2 = event_player.take_snapshot(example.id)
        # - should return the previous snapshot
        self.assertIsInstance(snapshot2, Snapshot)
        self.assertEqual(snapshot2.at_event_id, snapshot1.at_event_id)

        # Generate a domain event.
        example.beat_heart()

        # Take another snapshot with the entity.
        # - should use the previous snapshot and the heartbeat event
        snapshot3 = event_player.take_snapshot(example.id)
        self.assertNotEqual(snapshot3.at_event_id, snapshot1.at_event_id)
    def test_snapshots(self):
        stored_event_repo = PythonObjectsStoredEventRepository()
        event_store = EventStore(stored_event_repo)
        self.ps = PersistenceSubscriber(event_store)
        event_player = EventPlayer(event_store=event_store,
                                   id_prefix='Example',
                                   mutate_func=Example.mutate)

        # Create a new entity.
        registered_example = register_new_example(a=123, b=234)

        # Take a snapshot.
        snapshot = take_snapshot(registered_example, uuid1().hex)

        # Replay from this snapshot.
        after = snapshot.at_event_id
        initial_state = entity_from_snapshot(snapshot)
        retrieved_example = event_player.replay_events(
            registered_example.id, initial_state=initial_state, after=after)

        # Check the attributes are correct.
        self.assertEqual(retrieved_example.a, 123)

        # Remember the time now.
        timecheck1 = uuid1().hex

        # Change attribute value.
        retrieved_example.a = 999

        # Check the initial state doesn't move.
        self.assertEqual(initial_state.a, 123)

        # Remember the time now.
        timecheck2 = uuid1().hex

        # Change attribute value.
        retrieved_example.a = 9999

        # Remember the time now.
        timecheck3 = uuid1().hex

        # Check the event sourced entities are correct.
        assert initial_state.a == 123
        retrieved_example = event_player.replay_events(registered_example.id)
        self.assertEqual(retrieved_example.a, 9999)

        # Take another snapshot.
        snapshot2 = take_snapshot(retrieved_example, uuid1().hex)

        # Check we can replay from this snapshot.
        initial_state2 = entity_from_snapshot(snapshot2)
        after2 = snapshot2.domain_event_id
        retrieved_example = event_player.replay_events(
            registered_example.id, initial_state=initial_state2, after=after2)
        # Check the attributes are correct.
        self.assertEqual(retrieved_example.a, 9999)

        # Check we can get historical state at timecheck1.
        retrieved_example = event_player.replay_events(registered_example.id,
                                                       until=timecheck1)
        self.assertEqual(retrieved_example.a, 123)

        # Check we can get historical state at timecheck2.
        retrieved_example = event_player.replay_events(registered_example.id,
                                                       until=timecheck2)
        self.assertEqual(retrieved_example.a, 999)

        # Check we can get historical state at timecheck3.
        retrieved_example = event_player.replay_events(registered_example.id,
                                                       until=timecheck3)
        self.assertEqual(retrieved_example.a, 9999)

        # Similarly, check we can get historical state using a snapshot
        retrieved_example = event_player.replay_events(
            registered_example.id,
            initial_state=initial_state,
            after=after,
            until=timecheck2)
        self.assertEqual(retrieved_example.a, 999)
    def test_get_entity(self):
        # Setup an event store, using Python objects.
        event_store = EventStore(
            stored_event_repo=PythonObjectsStoredEventRepository())

        # Store example events.
        event1 = Example.Created(entity_id='entity1', a=1, b=2)
        event_store.append(event1)
        event2 = Example.Created(entity_id='entity2', a=2, b=4)
        event_store.append(event2)
        event3 = Example.Created(entity_id='entity3', a=3, b=6)
        event_store.append(event3)
        event4 = Example.Discarded(entity_id='entity3', entity_version=1)
        event_store.append(event4)

        # Check the event sourced entities are correct.
        # - just use a trivial mutate that always instantiates the 'Example'.
        event_player = EventPlayer(event_store=event_store,
                                   id_prefix='Example',
                                   mutate_func=Example.mutate)

        # The the reconstituted entity has correct attribute values.
        self.assertEqual('entity1', event_player.replay_events('entity1').id)
        self.assertEqual(1, event_player.replay_events('entity1').a)
        self.assertEqual(2, event_player.replay_events('entity2').a)
        self.assertEqual(None, event_player.replay_events('entity3'))

        # Check entity3 raises KeyError.
        self.assertEqual(event_player.replay_events('entity3'), None)

        # Check it works for "short" entities (should be faster, but the main thing is that it still works).
        # - just use a trivial mutate that always instantiates the 'Example'.
        event5 = Example.AttributeChanged(entity_id='entity1',
                                          entity_version=1,
                                          name='a',
                                          value=10)
        event_store.append(event5)

        event_player = EventPlayer(event_store=event_store,
                                   id_prefix='Example',
                                   mutate_func=Example.mutate)
        self.assertEqual(10, event_player.replay_events('entity1').a)

        event_player = EventPlayer(
            event_store=event_store,
            id_prefix='Example',
            mutate_func=Example.mutate,
            is_short=True,
        )
        self.assertEqual(10, event_player.replay_events('entity1').a)
    def test_snapshots(self):
        stored_event_repo = PythonObjectsStoredEventRepository()
        event_store = EventStore(stored_event_repo)
        self.ps = PersistenceSubscriber(event_store)
        event_player = EventPlayer(event_store=event_store, id_prefix='Example', mutate_func=Example.mutate)

        # Create a new entity.
        registered_example = register_new_example(a=123, b=234)

        # Take a snapshot.
        snapshot = take_snapshot(registered_example, uuid1().hex)

        # Replay from this snapshot.
        after = snapshot.at_event_id
        initial_state = entity_from_snapshot(snapshot)
        retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state, after=after)

        # Check the attributes are correct.
        self.assertEqual(retrieved_example.a, 123)

        # Remember the time now.
        timecheck1 = uuid1().hex

        # Change attribute value.
        retrieved_example.a = 999

        # Check the initial state doesn't move.
        self.assertEqual(initial_state.a, 123)

        # Remember the time now.
        timecheck2 = uuid1().hex

        # Change attribute value.
        retrieved_example.a = 9999

        # Remember the time now.
        timecheck3 = uuid1().hex

        # Check the event sourced entities are correct.
        assert initial_state.a == 123
        retrieved_example = event_player.replay_events(registered_example.id)
        self.assertEqual(retrieved_example.a, 9999)

        # Take another snapshot.
        snapshot2 = take_snapshot(retrieved_example, uuid1().hex)

        # Check we can replay from this snapshot.
        initial_state2 = entity_from_snapshot(snapshot2)
        after2 = snapshot2.domain_event_id
        retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state2, after=after2)
        # Check the attributes are correct.
        self.assertEqual(retrieved_example.a, 9999)

        # Check we can get historical state at timecheck1.
        retrieved_example = event_player.replay_events(registered_example.id, until=timecheck1)
        self.assertEqual(retrieved_example.a, 123)

        # Check we can get historical state at timecheck2.
        retrieved_example = event_player.replay_events(registered_example.id, until=timecheck2)
        self.assertEqual(retrieved_example.a, 999)

        # Check we can get historical state at timecheck3.
        retrieved_example = event_player.replay_events(registered_example.id, until=timecheck3)
        self.assertEqual(retrieved_example.a, 9999)

        # Similarly, check we can get historical state using a snapshot
        retrieved_example = event_player.replay_events(registered_example.id, initial_state=initial_state, after=after, until=timecheck2)
        self.assertEqual(retrieved_example.a, 999)
class EventSourcedRepository(EntityRepository):

    # If the entity won't have very many events, marking the entity as
    # "short" by setting __is_short__ value equal to True will mean
    # the fastest path for getting all the events is used. If you set
    # a value for page size (see below), this option will have no effect.
    __is_short__ = False

    # The page size by which events are retrieved. If this
    # value is set to a positive integer, the events of
    # the entity will be retrieved in pages, using a series
    # of queries, rather than with one potentially large query.
    __page_size__ = None

    def __init__(self, event_store, use_cache=False, snapshot_strategy=None):
        self._cache = {}
        self._use_cache = use_cache
        self._snapshot_strategy = snapshot_strategy

        # Check we got an event store.
        assert isinstance(event_store, AbstractEventStore)
        self.event_store = event_store

        # Check domain class is a type of event sourced entity.
        assert issubclass(self.domain_class, EventSourcedEntity)

        # Instantiate an event player for this repo, with
        # repo-specific mutate function, page size, etc.
        self.event_player = EventPlayer(
            event_store=self.event_store,
            id_prefix=id_prefix_from_entity_class(self.domain_class),
            mutate_func=self.domain_class.mutate,
            page_size=self.__page_size__ or self.domain_class.__page_size__,
            is_short=self.__is_short__ or self.domain_class.__is_short__,
            snapshot_strategy=self._snapshot_strategy,
        )

    def __contains__(self, entity_id):
        """
        Returns a boolean value according to whether entity with given ID exists.
        """
        return self.get_entity(entity_id) is not None

    def __getitem__(self, entity_id):
        """
        Returns entity with given ID.
        """
        # Get entity from the cache.
        if self._use_cache:
            try:
                return self._cache[entity_id]
            except KeyError:
                pass

        # Reconstitute the entity.
        entity = self.get_entity(entity_id)

        # Never created or already discarded?
        if entity is None:
            raise RepositoryKeyError(entity_id)

        # Put entity in the cache.
        if self._use_cache:
            self.add_cache(entity_id, entity)

        # Return entity.
        return entity

    def add_cache(self, entity_id, entity):
        self._cache[entity_id] = entity

    @abstractproperty
    def domain_class(self):
        """
        Returns the type of entity held by this repository.
        """
        return EventSourcedEntity

    def get_entity(self, entity_id, until=None):
        """
        Returns entity with given ID, optionally as it was until the given domain event ID.
        """

        # Get a snapshot (None if none exist).
        snapshot = self.event_player.get_snapshot(entity_id, until)

        # Decide the initial state, and after when we need to get the events.
        if snapshot is None:
            after = None
            initial_state = None
        else:
            after = snapshot.at_event_id
            initial_state = entity_from_snapshot(snapshot)

        # Replay domain events.
        return self.event_player.replay_events(entity_id, after=after, until=until, initial_state=initial_state)

    def fastforward(self, stale_entity, until=None):
        """
        Mutates an instance of an entity, according to the events that have occurred since its version.
        """
        return self.event_player.fastforward(stale_entity, until=until)
class EventSourcedRepository(EntityRepository):

    # If the entity won't have very many events, marking the entity as
    # "short" by setting __is_short__ value equal to True will mean
    # the fastest path for getting all the events is used. If you set
    # a value for page size (see below), this option will have no effect.
    __is_short__ = False

    # The page size by which events are retrieved. If this
    # value is set to a positive integer, the events of
    # the entity will be retrieved in pages, using a series
    # of queries, rather than with one potentially large query.
    __page_size__ = None

    def __init__(self, event_store, use_cache=False, snapshot_strategy=None):
        self._cache = {}
        self._use_cache = use_cache
        self._snapshot_strategy = snapshot_strategy

        # Check we got an event store.
        assert isinstance(event_store, AbstractEventStore)
        self.event_store = event_store

        # Check domain class is a type of event sourced entity.
        assert issubclass(self.domain_class, EventSourcedEntity)

        # Instantiate an event player for this repo, with
        # repo-specific mutate function, page size, etc.
        self.event_player = EventPlayer(
            event_store=self.event_store,
            id_prefix=id_prefix_from_entity_class(self.domain_class),
            mutate_func=self.domain_class.mutate,
            page_size=self.__page_size__ or self.domain_class.__page_size__,
            is_short=self.__is_short__ or self.domain_class.__is_short__,
            snapshot_strategy=self._snapshot_strategy,
        )

    def __contains__(self, entity_id):
        """
        Returns a boolean value according to whether entity with given ID exists.
        """
        return self.get_entity(entity_id) is not None

    def __getitem__(self, entity_id):
        """
        Returns entity with given ID.
        """
        # Get entity from the cache.
        if self._use_cache:
            try:
                return self._cache[entity_id]
            except KeyError:
                pass

        # Reconstitute the entity.
        entity = self.get_entity(entity_id)

        # Never created or already discarded?
        if entity is None:
            raise RepositoryKeyError(entity_id)

        # Put entity in the cache.
        if self._use_cache:
            self.add_cache(entity_id, entity)

        # Return entity.
        return entity

    def add_cache(self, entity_id, entity):
        self._cache[entity_id] = entity

    @abstractproperty
    def domain_class(self):
        """
        Returns the type of entity held by this repository.
        """
        return EventSourcedEntity

    def get_entity(self, entity_id, until=None):
        """
        Returns entity with given ID, optionally as it was until the given domain event ID.
        """

        # Get a snapshot (None if none exist).
        snapshot = self.event_player.get_snapshot(entity_id, until)

        # Decide the initial state, and after when we need to get the events.
        if snapshot is None:
            after = None
            initial_state = None
        else:
            after = snapshot.at_event_id
            initial_state = entity_from_snapshot(snapshot)

        # Replay domain events.
        return self.event_player.replay_events(entity_id,
                                               after=after,
                                               until=until,
                                               initial_state=initial_state)

    def fastforward(self, stale_entity, until=None):
        """
        Mutates an instance of an entity, according to the events that have occurred since its version.
        """
        return self.event_player.fastforward(stale_entity, until=until)