Merge branch 'source-signal-cleanup' into develop

This commit is contained in:
Matthew Tretter 2013-05-29 21:41:44 -04:00
commit 33b8913031
9 changed files with 74 additions and 64 deletions

View file

@ -122,7 +122,7 @@ The answer is that, when you define an ImageSpecField, ImageKit automatically
creates and registers an object called a *source group*. Source groups are
responsible for two things:
1. They dispatch signals when a source is created, changed, or deleted, and
1. They dispatch signals when a source is saved, and
2. They expose a generator method that enumerates source files.
When these objects are registered (using ``imagekit.register.source_group()``),
@ -177,7 +177,6 @@ Running the "generateimages" management command would now cause thumbnails to be
generated (using the "myapp:profile:avatar_thumbnail" spec) for each of the
JPEGs in `/path/to/some/pics`.
Note that, since this source group doesnt send the `source_created` or
`source_changed` signals, the corresponding cache file strategy callbacks
would not be called for them.
Note that, since this source group doesnt send the `source_saved` signal, the
corresponding cache file strategy callbacks would not be called for them.

View file

@ -33,7 +33,7 @@ Cache File Strategy
-------------------
Each ``ImageCacheFile`` has a cache file strategy, which abstracts away when
image is actually generated. It can implement the following four methods:
image is actually generated. It can implement the following three methods:
* ``on_content_required`` - called by ``ImageCacheFile`` when it requires the
contents of the generated image. For example, when you call ``read()`` or
@ -41,9 +41,7 @@ image is actually generated. It can implement the following four methods:
* ``on_existence_required`` - called by ``ImageCacheFile`` when it requires the
generated image to exist but may not be concerned with its contents. For
example, when you access its ``url`` or ``path`` attribute.
* ``on_source_created`` - called when the source of a spec is created
* ``on_source_changed`` - called when the source of a spec is changed
* ``on_source_deleted`` - called when the source of a spec is deleted
* ``on_source_saved`` - called when the source of a spec is saved
The default strategy only defines the first two of these, as follows:

View file

@ -23,10 +23,7 @@ class Optimistic(object):
"""
def on_source_created(self, file):
file.generate()
def on_source_changed(self, file):
def on_source_saved(self, file):
file.generate()

View file

@ -1,6 +1,5 @@
from .exceptions import AlreadyRegistered, NotRegistered
from .signals import (content_required, existence_required, source_created,
source_changed, source_deleted)
from .signals import content_required, existence_required, source_saved
from .utils import call_strategy_method
@ -70,9 +69,7 @@ class SourceGroupRegistry(object):
"""
_signals = {
source_created: 'on_source_created',
source_changed: 'on_source_changed',
source_deleted: 'on_source_deleted',
source_saved: 'on_source_saved',
}
def __init__(self):

View file

@ -6,6 +6,4 @@ content_required = Signal()
existence_required = Signal()
# Source group signals
source_created = Signal()
source_changed = Signal()
source_deleted = Signal()
source_saved = Signal()

View file

@ -2,20 +2,19 @@
Source groups are the means by which image spec sources are identified. They
have two responsibilities:
1. To dispatch ``source_created``, ``source_changed``, and ``source_deleted``
signals. (These will be relayed to the corresponding specs' cache file
strategies.)
1. To dispatch ``source_saved`` signals. (These will be relayed to the
corresponding specs' cache file strategies.)
2. To provide the source files that they represent, via a generator method named
``files()``. (This is used by the generateimages management command for
"pre-caching" image files.)
"""
from django.db.models.signals import post_init, post_save, post_delete
from django.db.models.signals import post_init, post_save
from django.utils.functional import wraps
import inspect
from ..cachefiles import LazyImageCacheFile
from ..signals import source_created, source_changed, source_deleted
from ..signals import source_saved
from ..utils import get_nonabstract_descendants
@ -58,7 +57,6 @@ class ModelSignalRouter(object):
uid = 'ik_spec_field_receivers'
post_init.connect(self.post_init_receiver, dispatch_uid=uid)
post_save.connect(self.post_save_receiver, dispatch_uid=uid)
post_delete.connect(self.post_delete_receiver, dispatch_uid=uid)
def add(self, source_group):
self._source_groups.append(source_group)
@ -94,17 +92,9 @@ class ModelSignalRouter(object):
old_hashes = instance._ik.get('source_hashes', {}).copy()
new_hashes = self.update_source_hashes(instance)
for attname, file in self.get_field_dict(instance).items():
if created:
self.dispatch_signal(source_created, file, sender, instance,
if file and old_hashes[attname] != new_hashes[attname]:
self.dispatch_signal(source_saved, file, sender, instance,
attname)
elif old_hashes[attname] != new_hashes[attname]:
self.dispatch_signal(source_changed, file, sender, instance,
attname)
@ik_model_receiver
def post_delete_receiver(self, sender, instance=None, **kwargs):
for attname, file in self.get_field_dict(instance).items():
self.dispatch_signal(source_deleted, file, sender, instance, attname)
@ik_model_receiver
def post_init_receiver(self, sender, instance=None, **kwargs):

View file

@ -31,8 +31,7 @@ class CountingCacheFileStrategy(object):
def __init__(self):
self.on_existence_required_count = 0
self.on_content_required_count = 0
self.on_source_changed_count = 0
self.on_source_created_count = 0
self.on_source_saved_count = 0
def on_existence_required(self, file):
self.on_existence_required_count += 1
@ -40,11 +39,8 @@ class CountingCacheFileStrategy(object):
def on_content_required(self, file):
self.on_content_required_count += 1
def on_source_changed(self, file):
self.on_source_changed_count += 1
def on_source_created(self, file):
self.on_source_created_count += 1
def on_source_saved(self, file):
self.on_source_saved_count += 1
class AbstractImageModel(models.Model):

View file

@ -1,27 +1,7 @@
from django.core.files import File
from imagekit.signals import source_created
from imagekit.specs.sourcegroups import ImageFieldSourceGroup
from imagekit.utils import get_nonabstract_descendants
from nose.tools import eq_
from . models import (AbstractImageModel, ConcreteImageModel,
ConcreteImageModelSubclass)
from .utils import get_image_file
def test_source_created_signal():
source_group = ImageFieldSourceGroup(AbstractImageModel, 'original_image')
count = [0]
def receiver(sender, *args, **kwargs):
if sender is source_group:
count[0] += 1
source_created.connect(receiver, dispatch_uid='test_source_created')
instance = ConcreteImageModel()
img = File(get_image_file())
instance.original_image.save('test_source_created_signal.jpg', img)
eq_(count[0], 1)
ConcreteImageModelSubclass)
def test_nonabstract_descendants_generator():

View file

@ -0,0 +1,55 @@
from django.core.files import File
from imagekit.signals import source_saved
from imagekit.specs.sourcegroups import ImageFieldSourceGroup
from nose.tools import eq_
from . models import AbstractImageModel, ImageModel, ConcreteImageModel
from .utils import get_image_file
def make_counting_receiver(source_group):
def receiver(sender, *args, **kwargs):
if sender is source_group:
receiver.count += 1
receiver.count = 0
return receiver
def test_source_saved_signal():
"""
Creating a new instance with an image causes the source_saved signal to be
dispatched.
"""
source_group = ImageFieldSourceGroup(ImageModel, 'image')
receiver = make_counting_receiver(source_group)
source_saved.connect(receiver)
ImageModel.objects.create(image=File(get_image_file()))
eq_(receiver.count, 1)
def test_no_source_saved_signal():
"""
Creating a new instance without an image shouldn't cause the source_saved
signal to be dispatched.
https://github.com/jdriscoll/django-imagekit/issues/214
"""
source_group = ImageFieldSourceGroup(ImageModel, 'image')
receiver = make_counting_receiver(source_group)
source_saved.connect(receiver)
ImageModel.objects.create()
eq_(receiver.count, 0)
def test_abstract_model_signals():
"""
Source groups created for abstract models must cause signals to be
dispatched on their concrete subclasses.
"""
source_group = ImageFieldSourceGroup(AbstractImageModel, 'original_image')
receiver = make_counting_receiver(source_group)
source_saved.connect(receiver)
ConcreteImageModel.objects.create(original_image=File(get_image_file()))
eq_(receiver.count, 1)