Fix generation of meteor ID's to match meteor client implementation.

This commit is contained in:
Tyson Clugg 2015-04-14 11:08:08 +10:00
parent 7dea154d0d
commit 41c544dadb
6 changed files with 78 additions and 12 deletions

View file

@ -52,16 +52,44 @@ class ThreadLocal(local):
def get(self, name, factory, *factory_args, **factory_kwargs):
"""Get attribute, creating if required using specified factory."""
if not hasattr(self, name):
return setattr(self, name, factory(*factory_args, **factory_kwargs))
obj = factory(*factory_args, **factory_kwargs)
setattr(self, name, obj)
return obj
return getattr(self, name)
THREAD_LOCAL = ThreadLocal(alea_random=alea.Alea)
class RandomStreams(object):
def __init__(self):
self._streams = {}
self._seed = THREAD_LOCAL.alea_random.hex_string(20)
def get_seed(self):
return self._seed
def set_seed(self, val):
self._streams = {}
self._seed = val
random_seed = property(get_seed, set_seed)
def __getitem__(self, key):
if key not in self._streams:
return self._streams.setdefault(key, alea.Alea(self._seed, key))
return self._streams[key]
THREAD_LOCAL = ThreadLocal(
alea_random=alea.Alea,
random_streams=RandomStreams,
)
METEOR_ID_CHARS = u'23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz'
def meteor_random_id():
return THREAD_LOCAL.alea_random.random_string(17, METEOR_ID_CHARS)
def meteor_random_id(name=None):
if name is None:
stream = THREAD_LOCAL.alea_random
else:
stream = THREAD_LOCAL.random_streams[name]
return stream.random_string(17, METEOR_ID_CHARS)
def autodiscover():

View file

@ -155,6 +155,11 @@ class Alea(object):
self.choice(alphabet) for n in range(length)
)
def hex_string(self, digits):
"""Return a hex string of `digits` length."""
return self.random_string(digits, '0123456789abcdef')
if __name__ == '__main__':
import doctest
doctest.testmod()

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('dddp', '0002_auto_20150408_0321'),
]
operations = [
migrations.AlterField(
model_name='objectmapping',
name='object_id',
field=models.CharField(max_length=255),
preserve_default=True,
),
]

View file

@ -3,6 +3,7 @@
from django.db import models, transaction
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.utils.encoding import python_2_unicode_compatible
import ejson
from dddp import meteor_random_id
@ -12,12 +13,22 @@ from dddp import meteor_random_id
def get_meteor_id(obj):
"""Return an Alea ID for the given object."""
# Django model._meta is now public API -> pylint: disable=W0212
content_type = ContentType.objects.get_for_model(obj._meta.model)
mapping, _ = ObjectMapping.objects.get_or_create(
content_type=content_type,
object_id=obj.pk,
)
return mapping.meteor_id
meta = obj._meta
obj_pk = str(obj.pk)
content_type = ContentType.objects.get_for_model(meta.model)
try:
return ObjectMapping.objects.values_list(
'meteor_id', flat=True,
).get(
content_type=content_type,
object_id=obj_pk,
)
except ObjectDoesNotExist:
return ObjectMapping.objects.create(
content_type=content_type,
object_id=obj_pk,
meteor_id=meteor_random_id('/collection/%s' % meta),
).meteor_id
@transaction.atomic
@ -51,7 +62,7 @@ class ObjectMapping(models.Model):
meteor_id = AleaIdField()
content_type = models.ForeignKey(ContentType, db_index=True)
object_id = models.PositiveIntegerField()
object_id = models.CharField(max_length=255)
# content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):

View file

@ -8,7 +8,7 @@ from django.db import connections
from django.utils.module_loading import import_string
from dddp.api import collection_name
from dddp.models import Subscription
from dddp.models import get_meteor_id, Subscription
from dddp.msg import obj_change_as_msg
@ -49,6 +49,7 @@ def send_notify(model, obj, msg, using):
sub_ids.add(sub.sub_id)
if not sub_ids:
get_meteor_id(obj) # force creation of meteor ID using randomSeed
return # no subscribers for this object, nothing more to do.
name, payload = obj_change_as_msg(obj, msg)

View file

@ -306,6 +306,7 @@ class DDPWebSocketApplication(geventwebsocket.WebSocketApplication):
def recv_method(self, method, params, id_, randomSeed=None):
"""DDP method handler."""
if randomSeed is not None:
this.random_streams.random_seed = randomSeed
this.alea_random = alea.Alea(randomSeed)
API.method(method, params, id_)
self.reply('updated', methods=[id_])