diff --git a/.travis.yml b/.travis.yml index 63ef3ba..ef9e077 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "2.6" - "2.7" before_install: diff --git a/VERSION b/VERSION index 9325c3c..a2268e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.0 \ No newline at end of file +0.3.1 \ No newline at end of file diff --git a/realms/__init__.py b/realms/__init__.py index 4c63701..de30b1b 100644 --- a/realms/__init__.py +++ b/realms/__init__.py @@ -20,12 +20,13 @@ import click from flask import Flask, request, render_template, url_for, redirect, g from flask.ext.cache import Cache from flask.ext.login import LoginManager, current_user -from flask.ext.sqlalchemy import SQLAlchemy +from flask.ext.sqlalchemy import SQLAlchemy, declarative_base, Model, _QueryProperty from flask.ext.assets import Environment, Bundle from werkzeug.routing import BaseConverter from werkzeug.exceptions import HTTPException from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict +from realms.lib.hook import HookModelMeta class Application(Flask): @@ -55,6 +56,7 @@ class Application(Flask): 'commands', 'models', 'views', + 'hooks' ) start_time = time.time() @@ -88,6 +90,16 @@ class Application(Flask): return super(Application, self).make_response(tuple(rv)) +class MySQLAlchemy(SQLAlchemy): + + def make_declarative_base(self): + """Creates the declarative base.""" + base = declarative_base(cls=Model, name='Model', + metaclass=HookModelMeta) + base.query = _QueryProperty(self) + return base + + class Assets(Environment): default_filters = {'js': 'rjsmin', 'css': 'cleancss'} default_output = {'js': 'assets/%(version)s.js', 'css': 'assets/%(version)s.css'} @@ -181,7 +193,7 @@ def cli(): login_manager = LoginManager(app) login_manager.login_view = 'auth.login' -db = SQLAlchemy(app) +db = MySQLAlchemy(app) cache = Cache(app) assets = Assets(app) diff --git a/realms/lib/hook.py b/realms/lib/hook.py new file mode 100644 index 0000000..e4b2795 --- /dev/null +++ b/realms/lib/hook.py @@ -0,0 +1,57 @@ +from flask.ext.sqlalchemy import DeclarativeMeta + +from functools import wraps + + +def hook_func(name, fn): + @wraps(fn) + def wrapper(self, *args, **kwargs): + for hook, a, kw in self.__class__._pre_hooks.get(name) or []: + hook(*a, **kw) + + rv = fn(self, *args, **kwargs) + + for hook, a, kw in self.__class__._post_hooks.get(name) or []: + hook(*a, **kw) + + return rv + return wrapper + + +class HookMixinMeta(type): + def __new__(cls, name, bases, attrs): + super_new = super(HookMixinMeta, cls).__new__ + + for key, value in attrs.items(): + if callable(value): + attrs[key] = hook_func(key, value) + + return super_new(cls, name, bases, attrs) + + +class HookMixin(object): + __metaclass__ = HookMixinMeta + + _pre_hooks = {} + _post_hooks = {} + + @classmethod + def after(cls, method_name): + def outer(f, *args, **kwargs): + cls._post_hooks.setdefault(method_name, []).append((f, args, kwargs)) + return f + return outer + + + @classmethod + def before(cls, method_name): + def outer(f, *args, **kwargs): + cls._pre_hooks.setdefault(method_name, []).append((f, args, kwargs)) + return f + return outer + + +class HookModelMeta(DeclarativeMeta, HookMixinMeta): + pass + + diff --git a/realms/lib/model.py b/realms/lib/model.py index 015ae43..ecdd3a4 100644 --- a/realms/lib/model.py +++ b/realms/lib/model.py @@ -44,7 +44,6 @@ class Model(db.Model): >>> {u'email': u'john@localhost', u'posts': [{u'id': 1, u'title': u'My First Post'}], u'name': u'John', u'id': 1} """ __abstract__ = True - # Stores changes made to this model's attributes. Can be retrieved # with model.changes _changes = {} diff --git a/realms/modules/auth/tests.py b/realms/modules/auth/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/realms/modules/wiki/models.py b/realms/modules/wiki/models.py index 9d2c8e4..3545666 100644 --- a/realms/modules/wiki/models.py +++ b/realms/modules/wiki/models.py @@ -10,9 +10,10 @@ from dulwich.repo import NotGitRepository from werkzeug.utils import escape, unescape from realms.lib.util import to_canonical from realms import cache +from realms.lib.hook import HookMixin -class Wiki(): +class Wiki(HookMixin): path = None base_path = '/' default_ref = 'master'