diff --git a/compressor/utils.py b/compressor/utils.py index 4d8f580..05b769d 100644 --- a/compressor/utils.py +++ b/compressor/utils.py @@ -116,3 +116,112 @@ def import_module(name, package=None): name = _resolve_name(name[level:], package, level) __import__(name) return sys.modules[name] + + +class AppSettings(object): + """ + An app setting object to be used for handling app setting defaults + gracefully and providing a nice API for them. Say you have an app + called ``myapp`` and want to define a few defaults, and refer to the + defaults easily in the apps code. Add a ``settings.py`` to your app:: + + from path.to.utils import AppSettings + + class MyAppSettings(AppSettings): + SETTING_1 = "one" + SETTING_2 = ( + "two", + ) + + Then initialize the setting with the correct prefix in the location of + of your choice, e.g. ``conf.py`` of the app module:: + + settings = MyAppSettings(prefix="MYAPP") + + The ``MyAppSettings`` instance will automatically look at Django's + global setting to determine each of the settings and respect the + provided ``prefix``. E.g. adding this to your site's ``settings.py`` + will set the ``SETTING_1`` setting accordingly:: + + MYAPP_SETTING_1 = "uno" + + Usage + ----- + + Instead of using ``from django.conf import settings`` as you would + usually do, you can switch to using your apps own settings module + to access the app settings:: + + from myapp.conf import settings + + print myapp_settings.MYAPP_SETTING_1 + + ``AppSettings`` instances also work as pass-throughs for other + global settings that aren't related to the app. For example the + following code is perfectly valid:: + + from myapp.conf import settings + + if "myapp" in settings.INSTALLED_APPS: + print "yay, myapp is installed!" + + Custom handling + --------------- + + Each of the settings can be individually configured with callbacks. + For example, in case a value of a setting depends on other settings + or other dependencies. The following example sets one setting to a + different value depending on a global setting:: + + from django.conf import settings + + class MyCustomAppSettings(AppSettings): + ENABLED = True + + def configure_enabled(self, value): + return value and not self.DEBUG + + custom_settings = MyCustomAppSettings("MYAPP") + + The value of ``custom_settings.MYAPP_ENABLED`` will vary depending on the + value of the global ``DEBUG`` setting. + + Each of the app settings can be customized by providing + a method ``configure_`` that takes the default + value as defined in the class attributes as the only parameter. + The method needs to return the value to be use for the setting in + question. + """ + def __dir__(self): + return sorted(list(set(self.__dict__.keys() + dir(settings)))) + + __members__ = lambda self: self.__dir__() + + def __getattr__(self, name): + if name.startswith(self._prefix): + raise AttributeError("%r object has no attribute %r" % + (self.__class__.__name__, name)) + try: + return getattr(settings, name) + except AttributeError: + raise + + def __init__(self, prefix): + defaults = filter(self.issetting, inspect.getmembers(self.__class__)) + self._prefix = prefix + for name, value in defaults: + prefixed_name = "%s_%s" % (self._prefix.upper(), name.upper()) + value = getattr(settings, prefixed_name, value) + try: + callback = getattr(self, "configure_%s" % name.lower()) + if callable(callback): + callback_value = callback(value) + if callback_value is not None: + value = callback_value + except AttributeError: + pass + delattr(self.__class__, name) + setattr(self, prefixed_name, value) + + def issetting(self, setting): + return setting[0] == setting[0].upper()