Christopher Grebs avatar Christopher Grebs committed a430982

ported LodgeIt to SQLAlchemy 0.6

Comments (0)

Files changed (7)

lodgeit/application.py

 
     the WSGI application
 
-    :copyright: 2007 by Armin Ronacher, Christopher Grebs.
-                2008 by Christopher Grebs.
+    :copyright: 2007-2009 by Armin Ronacher, Christopher Grebs.
     :license: BSD
 """
 import os
 from werkzeug import SharedDataMiddleware, ClosingIterator
 from werkzeug.exceptions import HTTPException, NotFound
 from sqlalchemy import create_engine
-from lodgeit import i18n, local
+from lodgeit import i18n
+from lodgeit.local import application, ctx, _local_manager
 from lodgeit.urls import urlmap
 from lodgeit.utils import COOKIE_NAME, Request, jinja_environment
-from lodgeit.database import metadata, session
+from lodgeit.database import db
+from lodgeit.models import Paste
 from lodgeit.controllers import get_controller
 
 
 
         #: bind metadata, create engine and create all tables
         self.engine = engine = create_engine(dburi, convert_unicode=True)
-        metadata.bind = engine
-        metadata.create_all(engine)
+        db.metadata.bind = engine
+        db.metadata.create_all(engine, [Paste.__table__])
 
         #: jinja_environment update
-        jinja_environment.globals.update(
-            i18n_languages=i18n.list_languages()
-        )
-        jinja_environment.filters.update(
-            datetimeformat=i18n.format_datetime
-        )
+        jinja_environment.globals.update({
+            'i18n_languages': i18n.list_languages()})
+        jinja_environment.filters.update({
+            'datetimeformat': i18n.format_datetime})
         jinja_environment.install_null_translations()
 
         #: bind the application to the current context local
         self.bind_to_context()
 
+        self.cleanup_callbacks = (db.session.close, _local_manager.cleanup,
+                                  self.bind_to_context())
+
     def bind_to_context(self):
-        local.application = self
+        ctx.application = application = self
 
     def __call__(self, environ, start_response):
         """Minimal WSGI application for request dispatching."""
                                             expires=expires)
 
         return ClosingIterator(resp(environ, start_response),
-                               [local._local_manager.cleanup, session.remove])
+                               self.cleanup_callbacks)
 
 
 def make_app(dburi, secret_key, debug=False, shell=False):
     app = LodgeIt(dburi, secret_key)
     if debug:
         app.engine.echo = True
+    app.bind_to_context()
     if not shell:
         # we don't need access to the shared data middleware in shell mode
         app = SharedDataMiddleware(app, {
-            '/static':      static_path
-        })
+            '/static': static_path})
     return app

lodgeit/controllers/pastes.py

 from lodgeit.lib import antispam
 from lodgeit.i18n import list_languages, _
 from lodgeit.utils import render_to_response
-from lodgeit.database import session, Paste
+from lodgeit.models import Paste
+from lodgeit.database import db
 from lodgeit.lib.highlighting import list_languages, STYLES, get_style
 from lodgeit.lib.pagination import generate_pagination
 from lodgeit.lib.captcha import check_hashed_solution, Captcha
             if code and language and not error:
                 paste = Paste(code, language, parent, req.user_hash,
                               'private' in req.form)
-                session.flush()
+                db.session.add(paste)
+                db.session.commit()
                 local.request.session['language'] = language
                 return redirect(paste.url)
 

lodgeit/database.py

 
     Database fun :)
 
-    :copyright: 2007-2008 by Armin Ronacher, Christopher Grebs.
+    :copyright: 2007-2010 by Armin Ronacher, Christopher Grebs.
     :license: BSD
 """
-import time
-import difflib
-from datetime import datetime
-from werkzeug import cached_property
-from sqlalchemy import MetaData, Integer, Text, DateTime, ForeignKey, \
-     String, Boolean, Table, Column, select, and_, func
-from sqlalchemy.orm import scoped_session, create_session, backref, relation
-from sqlalchemy.orm.scoping import ScopedSession
-from lodgeit import local
-from lodgeit.utils import generate_paste_hash
-from lodgeit.lib.highlighting import highlight, preview_highlight, LANGUAGES
+import sys
+from types import ModuleType
+import sqlalchemy
+from sqlalchemy import MetaData, create_engine
+from sqlalchemy import orm, sql
+from sqlalchemy.orm.session import Session
+from sqlalchemy.ext.declarative import declarative_base
+from lodgeit.local import application, _local_manager
 
 
-session = scoped_session(lambda: create_session(local.application.engine),
-    scopefunc=local._local_manager.get_ident)
-
 metadata = MetaData()
 
-pastes = Table('pastes', metadata,
-    Column('paste_id', Integer, primary_key=True),
-    Column('code', Text),
-    Column('parent_id', Integer, ForeignKey('pastes.paste_id'),
-           nullable=True),
-    Column('pub_date', DateTime),
-    Column('language', String(30)),
-    Column('user_hash', String(40), nullable=True),
-    Column('handled', Boolean, nullable=False),
-    Column('private_id', String(40), unique=True, nullable=True)
-)
 
+def session_factory():
+    opts = {
+        'autoflush': True,
+        'transactional': True}
+    return orm.create_session(application.engine, **opts)
+session = orm.scoped_session(
+    session_factory, scopefunc=_local_manager.get_ident)
 
-class Paste(object):
-    """Represents a paste."""
 
-    def __init__(self, code, language, parent=None, user_hash=None,
-                 private=False):
-        if language not in LANGUAGES:
-            language = 'text'
-        self.code = u'\n'.join(code.splitlines())
-        self.language = language
-        if isinstance(parent, Paste):
-            self.parent = parent
-        elif parent is not None:
-            self.parent_id = parent
-        self.pub_date = datetime.now()
-        self.handled = False
-        self.user_hash = user_hash
-        self.private = private
+class ModelBase(object):
+    """Internal baseclass for all models.  It provides some syntactic
+    sugar and maps the default query property.
 
-    @staticmethod
-    def get(identifier):
-        """Return the paste for an identifier.  Private pastes must be loaded
-        with their unique hash and public with the paste id.
-        """
-        if isinstance(identifier, basestring) and not identifier.isdigit():
-            return Paste.query.filter(Paste.private_id == identifier).first()
-        return Paste.query.filter(
-            (Paste.paste_id == int(identifier)) &
-            (Paste.private_id == None)
-        ).first()
+    We use the declarative model api from sqlalchemy.
+    """
 
-    @staticmethod
-    def find_all():
-        """Return a query for all public pastes ordered by the id in reverse
-        order.
-        """
-        return Paste.query.filter(Paste.private_id == None) \
-                          .order_by(Paste.paste_id.desc())
 
-    @staticmethod
-    def count():
-        """Count all pastes."""
-        s = select([func.count(pastes.c.paste_id)])
-        return session.execute(s).fetchone()[0]
+# configure the declarative base
+Model = declarative_base(name='Model', cls=ModelBase,
+    mapper=orm.mapper, metadata=metadata)
+ModelBase.query = session.query_property()
 
-    @staticmethod
-    def resolve_root(identifier):
-        """Find the root paste for a paste tree."""
-        paste = Paste.get(identifier)
-        if paste is None:
-            return
-        while paste.parent_id is not None:
-            paste = paste.parent
-        return paste
 
-    @staticmethod
-    def fetch_replies():
-        """Get the new replies for the ower of a request and flag them
-        as handled.
-        """
-        s = select([pastes.c.paste_id],
-            Paste.user_hash == local.request.user_hash
-        )
+def _make_module():
+    db = ModuleType('db')
+    for mod in sqlalchemy, orm:
+        for key, value in mod.__dict__.iteritems():
+            if key in mod.__all__:
+                setattr(db, key, value)
 
-        paste_list = Paste.query.filter(and_(
-            Paste.parent_id.in_(s),
-            Paste.handled == False,
-            Paste.user_hash != local.request.user_hash,
-        )).order_by(pastes.c.paste_id.desc()).all()
+    db.session = session
+    db.metadata = metadata
+    db.Model = Model
+    db.NoResultFound = orm.exc.NoResultFound
+    return db
 
-        to_mark = [p.paste_id for p in paste_list]
-        session.execute(pastes.update(pastes.c.paste_id.in_(to_mark),
-                                      values={'handled': True}))
-        return paste_list
-
-    def _get_private(self):
-        return self.private_id is not None
-
-    def _set_private(self, value):
-        if not value:
-            self.private_id = None
-            return
-        if self.private_id is None:
-            while 1:
-                self.private_id = generate_paste_hash()
-                paste = Paste.query.filter(Paste.private_id ==
-                                           self.private_id).first()
-                if paste is None:
-                    break
-    private = property(_get_private, _set_private, doc='''
-        The private status of the paste.  If the paste is private it gets
-        a unique hash as identifier, otherwise an integer.
-    ''')
-    del _get_private, _set_private
-
-    @property
-    def identifier(self):
-        """The paste identifier.  This is a string, the same the `get`
-        method accepts.
-        """
-        if self.private:
-            return self.private_id
-        return str(self.paste_id)
-
-    @property
-    def url(self):
-        """The URL to the paste."""
-        return '/show/%s/' % self.identifier
-
-    def compare_to(self, other, context_lines=4, template=False):
-        """Compare the paste with another paste."""
-        udiff = u'\n'.join(difflib.unified_diff(
-            self.code.splitlines(),
-            other.code.splitlines(),
-            fromfile='Paste #%s' % self.identifier,
-            tofile='Paste #%s' % other.identifier,
-            lineterm='',
-            n=context_lines
-        ))
-        if template:
-            from lodgeit.lib.diff import prepare_udiff
-            diff, info = prepare_udiff(udiff)
-            return diff and diff[0] or None
-        return udiff
-
-    @cached_property
-    def parsed_code(self):
-        """The paste as rendered code."""
-        return highlight(self.code, self.language)
-
-    def to_xmlrpc_dict(self):
-        """Convert the paste into a dict for XMLRCP."""
-        return {
-            'paste_id':         self.paste_id,
-            'code':             self.code,
-            'parsed_code':      self.parsed_code,
-            'pub_date':         int(time.mktime(self.pub_date.timetuple())),
-            'language':         self.language,
-            'parent_id':        self.parent_id,
-            'url':              self.url
-        }
-
-    def render_preview(self, num=5):
-        """Render a preview for this paste."""
-        return preview_highlight(self.code, self.language, num)
-
-
-session.mapper(Paste, pastes, properties={
-    'children': relation(Paste,
-        primaryjoin=pastes.c.parent_id==pastes.c.paste_id,
-        cascade='all',
-        backref=backref('parent', remote_side=[pastes.c.paste_id])
-    )
-})
+sys.modules['lodgeit.database.db'] = db = _make_module()

lodgeit/lib/webapi.py

     :license: BSD.
 """
 import inspect
-from lodgeit.database import session, Paste
+from lodgeit.models import Paste
+from lodgeit.database import db
 from lodgeit.lib.xmlrpc import XMLRPCRequestHandler
 from lodgeit.lib.json import JSONRequestHandler
 from lodgeit.lib.highlighting import STYLES, LANGUAGES, get_style, \
             raise ValueError('parent paste not found')
 
     paste = Paste(code, language, parent, private=private)
-    session.flush()
+    db.session.add(paste)
+    db.session.commit()
     return paste.identifier
 
 
     adds the current request to the context. This is used for the
     welcome message.
     """
-    from lodgeit.database import Paste
+    from lodgeit.models import Paste
     request = local.request
     if request.method == 'GET':
         context['new_replies'] = Paste.fetch_replies()
 import os
 
-from werkzeug import script
-from werkzeug.serving import run_simple
-from werkzeug.utils import create_environ, run_wsgi_app
+from werkzeug import script, run_simple, create_environ, run_wsgi_app
 
 from lodgeit import local
 from lodgeit.application import make_app
-from lodgeit.database import session
+from lodgeit.database import db
 
 dburi = 'sqlite:////tmp/lodgeit.db'
 
     return run_wsgi_app(app, env)
 
 action_runserver = script.make_runserver(
-    lambda: make_app(dburi, SECRET_KEY),
+    lambda: make_app(dburi, SECRET_KEY, debug=True),
     use_reloader=True)
 
 action_shell = script.make_shell(
     lambda: {
         'app': make_app(dburi, SECRET_KEY, False, True),
         'local': local,
-        'session': session,
+        'db': db,
         'run_app': run_app
     },
     ('\nWelcome to the interactive shell environment of LodgeIt!\n'
      '\n'
-     'You can use the following predefined objects: app, local, session.\n'
+     'You can use the following predefined objects: app, local, db.\n'
      'To run the application (creates a request) use *run_app*.')
 )
 

scripts/make-bootstrap.py

     easy_install('Jinja2', home_dir)
     easy_install('Werkzeug', home_dir)
     easy_install('Pygments', home_dir)
-    easy_install('SQLAlchemy', home_dir)
+    easy_install('SQLAlchemy==0.6', home_dir)
     easy_install('simplejson', home_dir)
     easy_install('Babel', home_dir)
     easy_install('PIL', home_dir)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.