def test_deliver_delta_without_dealer(self): dm = DealerManager() # Send a delta of a model class without dealers delta = InsertionDelta('test_model', {'id': 1}) with self.assertRaises(UnknownModelClass): dm.deliver_delta(delta)
def test_deliver_delta_other_model_class(self): dm = DealerManager() dm.register_dealer(self.dealer1) # Send a delta of a model class without dealers delta = InsertionDelta('unknown_model', {'id': 1}) with self.assertRaises(UnknownModelClass): dm.deliver_delta(delta)
def test_deliver_delta(self): dm = DealerManager() dm.register_dealer(self.dealer1) # Send a delta of the same model class of the registered dealer delta = InsertionDelta('test_model', {'id': 1}) dm.deliver_delta(delta) self.dealer1.deliver_delta.assert_called_once_with(delta)
def test_unrelated_creation(self): dealer = PlayersWithColorDealer() dealer.add_subscription_item(self.subscription_item) # Alice record is created with color blue original_delta = InsertionDelta('player', {'id': 1, 'name': 'Alice', 'color': 'blue'}) dealer.deliver_delta(original_delta) # No delta is received self.assertFalse(self.deliver_delta.called)
def test_update_creation(self): dealer = PlayersWithColorDealer() dealer.add_subscription_item(self.subscription_item) # Alice changes her color original_delta = UpdateDelta('player', old_data={'id': 1, 'name': 'Alice', 'color': 'blue'}, new_data={'id': 1, 'name': 'Alice', 'color': 'red'}) dealer.deliver_delta(original_delta) # Subscription item should receive a creation self.deliver_delta.assert_called_once_with( InsertionDelta("player", { 'id': 1, 'name': 'Alice', 'color': 'red' }))
def publishDeltas(self, req, deltas): """Distributes one or more deltas to the appropriate dealers which, in turn, will distribute them to browser clients. :param list deltas: A list of deltas represented as dictionaries. """ # deltas = [{ # "type": "insert", # "model": "player", # "data": { "color": "blue" } # }] obj_deltas = [] try: for delta in deltas: snorky_log.info(delta) delta_type = delta["type"] model = delta["model"] if not is_string(model): raise RPCError("model must be string") if delta_type == "insert": obj_delta = InsertionDelta(model, delta["data"]) elif delta_type == "delete": obj_delta = DeletionDelta(model, delta["data"]) elif delta_type == "update": obj_delta = UpdateDelta(model, delta["newData"], delta["oldData"]) else: raise RPCError("Invalid delta type") obj_deltas.append(obj_delta) except KeyError: raise RPCError("Missing field") for delta_obj in obj_deltas: self.frontend.dm.deliver_delta(delta_obj)
def test_insertion(self): insertion = InsertionDelta('foo', {'id': 1, 'name': 'Alice'}) self.dealer.deliver_delta(insertion) self.deliver_delta.assert_called_once_with(insertion)
def deliver_delta(self, delta): """Receives a delta, computes a set of destination subscriptions items and forwards the delta to them. Please note that altough InsertionDelta and DeletionDelta will always trigger deltas of the same time on the client, UpdateDelta may not. To understand this fact, see the next example: Suppose our model class composes a player name and a color, i.e. 'id: 1, name: Alice, color: blue'. We may also have a dealer named 'Players with color' that uses the `color` attribute to filter players. A client maybe subscribed to players with color red, showing them in a list. Now, if Alice changes its color to red, this client will receive a insertion delta instead of an update one because they had no previous knowledge of Alice's model and will have to render it as a new row. If then Alice decides to change her player name, the client will receive a delta update because they already knew Alice's model. And lastly, if Alice changes its color again to other than red the new model will no longer satisfy the filter and thus a deletion delta will be sent to the client. """ if isinstance(delta, InsertionDelta) or \ isinstance(delta, DeletionDelta): deliver_to = self.get_subscription_items_for_model(delta.data) for subscription_item in deliver_to: subscription_item.subscription.deliver_delta(delta) else: # If origin delta is an update model = delta.model new_data = delta.new_data old_data = delta.old_data set_old = self.get_subscription_items_for_model(old_data) set_new = self.get_subscription_items_for_model(new_data) # Prepare every type of destination delta insertion_delta = InsertionDelta(model, new_data) update_delta = delta deletion_delta = DeletionDelta(model, old_data) # For the subscription items that do not match the filter with the # old data but do with the new data, send a insertion delta. for item in set_new - set_old: item.subscription.deliver_delta(insertion_delta) # For the subscription items that match the filter with both the # new and old data, send an update delta. for item in set_old.intersection(set_new): item.subscription.deliver_delta(update_delta) # For the subscription items that match the filter with the old # data but do not match with the new data, send a deletion delta. for item in set_old - set_new: item.subscription.deliver_delta(deletion_delta)