Implemented more robust delayed registry iteration. Modules declaring new searches...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Thu, 19 May 2011 19:18:29 +0000 (15:18 -0400)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Thu, 19 May 2011 19:18:29 +0000 (15:18 -0400)
philo/contrib/sobol/models.py
philo/contrib/sobol/search.py
philo/contrib/sobol/utils.py

index 37bb29e..cd773a5 100644 (file)
@@ -1,4 +1,5 @@
 import datetime
+import itertools
 
 from django.conf import settings
 from django.conf.urls.defaults import patterns, url
@@ -12,7 +13,7 @@ from django.utils.datastructures import SortedDict
 
 from philo.contrib.sobol import registry
 from philo.contrib.sobol.forms import SearchForm
-from philo.contrib.sobol.utils import HASH_REDIRECT_GET_KEY, URL_REDIRECT_GET_KEY, SEARCH_ARG_GET_KEY, check_redirect_hash
+from philo.contrib.sobol.utils import HASH_REDIRECT_GET_KEY, URL_REDIRECT_GET_KEY, SEARCH_ARG_GET_KEY, check_redirect_hash, RegistryIterator
 from philo.exceptions import ViewCanNotProvideSubpath
 from philo.models import MultiView, Page
 from philo.models.fields import SlugMultipleChoiceField
@@ -133,9 +134,29 @@ class Click(models.Model):
                get_latest_by = 'datetime'
 
 
+class RegistryChoiceField(SlugMultipleChoiceField):
+       def _get_choices(self):
+               if isinstance(self._choices, RegistryIterator):
+                       return self._choices.copy()
+               elif hasattr(self._choices, 'next'):
+                       choices, self._choices = itertools.tee(self._choices)
+                       return choices
+               else:
+                       return self._choices
+       choices = property(_get_choices)
+
+
+try:
+       from south.modelsinspector import add_introspection_rules
+except ImportError:
+       pass
+else:
+       add_introspection_rules([], ["^philo\.contrib\.shipherd\.models\.RegistryChoiceField"])
+
+
 class SearchView(MultiView):
        results_page = models.ForeignKey(Page, related_name='search_results_related')
-       searches = SlugMultipleChoiceField(choices=registry.iterchoices())
+       searches = RegistryChoiceField(choices=registry.iterchoices())
        enable_ajax_api = models.BooleanField("Enable AJAX API", default=True, help_text="Search results will be available <i>only</i> by AJAX, not as template variables.")
        placeholder_text = models.CharField(max_length=75, default="Search")
        
index f1a2c6b..4ab5980 100644 (file)
@@ -11,7 +11,7 @@ from django.utils.safestring import mark_safe
 from django.utils.text import capfirst
 from django.template import loader, Context, Template
 
-from philo.contrib.sobol.utils import make_tracking_querydict
+from philo.contrib.sobol.utils import make_tracking_querydict, RegistryIterator
 
 
 if getattr(settings, 'SOBOL_USE_EVENTLET', False):
@@ -24,7 +24,7 @@ else:
 
 
 __all__ = (
-       'Result', 'BaseSearch', 'DatabaseSearch', 'URLSearch', 'JSONSearch', 'GoogleSearch', 'registry'
+       'Result', 'BaseSearch', 'DatabaseSearch', 'URLSearch', 'JSONSearch', 'GoogleSearch', 'SearchRegistry', 'registry'
 )
 
 
@@ -41,7 +41,8 @@ class RegistrationError(Exception):
 
 
 class SearchRegistry(object):
-       # Holds a registry of search types by slug.
+       """Holds a registry of search types by slug."""
+       
        def __init__(self):
                self._registry = {}
        
@@ -68,11 +69,10 @@ class SearchRegistry(object):
                return self._registry.items()
        
        def iteritems(self):
-               return self._registry.iteritems()
+               return RegistryIterator(self._registry, 'iteritems')
        
        def iterchoices(self):
-               for slug, search in self.iteritems():
-                       yield slug, search.verbose_name
+               return RegistryIterator(self._registry, 'iteritems', lambda x: (x[0], x[1].verbose_name))
        
        def __getitem__(self, key):
                return self._registry[key]
@@ -132,7 +132,7 @@ class Result(object):
 class BaseSearchMetaclass(type):
        def __new__(cls, name, bases, attrs):
                if 'verbose_name' not in attrs:
-                       attrs['verbose_name'] = capfirst(convert_camelcase(name))
+                       attrs['verbose_name'] = capfirst(' '.join(convert_camelcase(name).rsplit(' ', 1)[:-1]))
                if 'slug' not in attrs:
                        attrs['slug'] = name.lower()
                return super(BaseSearchMetaclass, cls).__new__(cls, name, bases, attrs)
@@ -252,7 +252,7 @@ class BaseSearch(object):
                return make_tracking_querydict(self.search_arg, self.more_results_url)
        
        def __unicode__(self):
-               return ' '.join(self.__class__.verbose_name.rsplit(' ', 1)[:-1]) + ' results'
+               return self.verbose_name
 
 
 class DatabaseSearch(BaseSearch):
index 5c52c81..50d2113 100644 (file)
@@ -30,4 +30,25 @@ def make_tracking_querydict(search_arg, url):
                SEARCH_ARG_GET_KEY, urlquote_plus(search_arg),
                URL_REDIRECT_GET_KEY, urlquote(url),
                HASH_REDIRECT_GET_KEY, make_redirect_hash(search_arg, url))
-       )
\ No newline at end of file
+       )
+
+
+class RegistryIterator(object):
+       def __init__(self, registry, iterattr='__iter__', transform=lambda x:x):
+               if not hasattr(registry, iterattr):
+                       raise AttributeError("Registry has no attribute %s" % iterattr)
+               self.registry = registry
+               self.iterattr = iterattr
+               self.transform = transform
+       
+       def __iter__(self):
+               return self
+       
+       def next(self):
+               if not hasattr(self, '_iter'):
+                       self._iter = getattr(self.registry, self.iterattr)()
+               
+               return self.transform(self._iter.next())
+       
+       def copy(self):
+               return self.__class__(self.registry, self.iterattr, self.transform)
\ No newline at end of file