development.ini | 5 +- moksha/controllers/root.py | 10 +++-- moksha/exc.py | 3 + moksha/lib/app_globals.py | 18 +++++++++ moksha/lib/cache.py | 20 +++++++++++ moksha/lib/helpers.py | 47 ++++++++++++++++++++++---- moksha/widgetbrowser/widgetbrowser/wsgiapp.py | 7 +++ moksha/wsgiapp.py | 2 - setup.py | 2 - start-moksha | 7 +++ stop-moksha | 3 + 11 files changed, 109 insertions(+), 15 deletions(-)
New commits: commit c3a1d0f69176efd73192566bc37cac04444d36ff Author: Luke Macken lmacken@redhat.com Date: Mon Feb 16 15:16:02 2009 -0500
Add a memcached-specific cache object for our cache_rendered{,_data} decorators
diff --git a/moksha/lib/app_globals.py b/moksha/lib/app_globals.py index 63dcbce..230c024 100644 --- a/moksha/lib/app_globals.py +++ b/moksha/lib/app_globals.py @@ -24,7 +24,6 @@ class Globals(object): timeout = int(config.get('beaker.cache.timeout', '0')) try: self.cache = Cache(config['beaker.cache.url'], timeout) - #self.cache = Shove('memcache://%s' % config['beaker.cache.url']) except CacheBackendException, e: log.warning(str(e)) self.cache = None diff --git a/moksha/lib/cache.py b/moksha/lib/cache.py new file mode 100644 index 0000000..20536c1 --- /dev/null +++ b/moksha/lib/cache.py @@ -0,0 +1,20 @@ +import memcache +from moksha.exc import CacheBackendException + +class Cache(object): + """ A memcached-specific caching interface """ + + def __init__(self, url, timeout=None, prefix=None): + self.mc = memcache.Client([url]) + self.timeout = timeout or 300 + self.prefix = prefix + if not self.mc.set('x', 'x', 1): + raise CacheBackendException("Cannot connect to Memcached") + + def get(self, key): + return self.mc.get(key) + + def set(self, key, value, timeout=None): + if self.prefix: + key = self.prefix + key + self.mc.set(key, value, timeout or self.timeout)
commit df367d6183408ca3fee3dbeaf03200b6dfccee7f Author: Luke Macken lmacken@redhat.com Date: Mon Feb 16 13:21:10 2009 -0500
Add a cache_rendered and cache_rendered_data methods to the moksha helpers module
diff --git a/development.ini b/development.ini index 4fa8376..21eba0d 100644 --- a/development.ini +++ b/development.ini @@ -75,8 +75,9 @@ cache_dir = %(here)s/data beaker.session.key = moksha beaker.session.secret = somesecret
-beaker.cache.type = memory -#beaker.cache.type = memcached +#beaker.cache.type = memory +beaker.cache.type = ext:memcached +beaker.cache.url = localhost:11211
# If you'd like to fine-tune the individual locations of the cache data dirs # for the Cache data, or the Session saves, un-comment the desired settings diff --git a/moksha/controllers/root.py b/moksha/controllers/root.py index 90dc1a9..1bcffde 100644 --- a/moksha/controllers/root.py +++ b/moksha/controllers/root.py @@ -17,10 +17,14 @@ # Authors: Luke Macken lmacken@redhat.com
import moksha +import pylons +import os
from tg import config from tg import expose, flash, require, tmpl_context, redirect, validate from tg.controllers import WSGIAppController +from tg.decorators import after_render + from repoze.what import predicates from pkg_resources import resource_filename from widgetbrowser import WidgetBrowser @@ -28,22 +32,21 @@ from widgetbrowser import WidgetBrowser from moksha import _ from moksha.model import DBSession from moksha.lib.base import BaseController +from moksha.lib.helpers import cache_rendered_data from moksha.exc import ApplicationNotFound from moksha.controllers.error import ErrorController from moksha.controllers.apps import AppController from moksha.controllers.widgets import WidgetController -#from moksha.controllers.secc import AdminController
# So we can mount the WidgetBrowser as /docs -import os os.environ['TW_BROWSER_PREFIX'] = '/docs'
+ class RootController(BaseController):
appz = AppController() widgets = WidgetController() error = ErrorController() - #admin = AdminController()
# ToscaWidgets WidgetBrowser integration docs = WSGIAppController( @@ -53,6 +56,7 @@ class RootController(BaseController): docs_dir=config.get('docs_dir', 'docs'), full_stack=False))
+ @after_render(cache_rendered_data) @expose('mako:moksha.templates.index') def index(self): tmpl_context.menu_widget = moksha.menus['default_menu'] diff --git a/moksha/exc.py b/moksha/exc.py index 152d9d2..442f131 100644 --- a/moksha/exc.py +++ b/moksha/exc.py @@ -25,3 +25,6 @@ class ApplicationNotFound(MokshaException):
class WidgetNotFound(MokshaException): pass + +class CacheBackendException(MokshaException): + pass diff --git a/moksha/lib/app_globals.py b/moksha/lib/app_globals.py index 342c8cb..63dcbce 100644 --- a/moksha/lib/app_globals.py +++ b/moksha/lib/app_globals.py @@ -1,5 +1,16 @@ """The application's Globals object"""
+import logging + +from tg import config +from shove import Shove + +from moksha.exc import CacheBackendException +from moksha.lib.cache import Cache + + +log = logging.getLogger(__name__) + class Globals(object): """Globals acts as a container for objects available throughout the life of the application @@ -10,4 +21,10 @@ class Globals(object): initialization and is available during requests via the 'g' variable """ - pass + timeout = int(config.get('beaker.cache.timeout', '0')) + try: + self.cache = Cache(config['beaker.cache.url'], timeout) + #self.cache = Shove('memcache://%s' % config['beaker.cache.url']) + except CacheBackendException, e: + log.warning(str(e)) + self.cache = None diff --git a/moksha/lib/helpers.py b/moksha/lib/helpers.py index 4dc88b3..fefaf6f 100644 --- a/moksha/lib/helpers.py +++ b/moksha/lib/helpers.py @@ -1,3 +1,9 @@ +import moksha +import pylons +import uuid +import simplejson as json +import re + from webhelpers import date, feedgenerator, html, number, misc, text
from repoze.what.predicates import (Not, Predicate, All, Any, has_all_permissions, @@ -9,13 +15,8 @@ from repoze.what.authorize import check_authorization, NotAuthorizedError
from webob import Request
-import urllib -import uuid -import simplejson as json -import re - from decorator import decorator -import moksha +
scrub_filter = re.compile('[^_a-zA-Z0-9-]')
@@ -485,3 +486,37 @@ except: def __repr__(self): return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) + + +# TODO: document this +def cache_rendered_data(data): + """ A method to cache ``data`` with the current request path as the key. + + This method can be used within TurboGears2 hooks to cache rendered data + from a given method for a specific URL. For example, to cache the + index.html, you could do something like this:: + + from tg.decorators import after_render + + @after_render(cache_rendered_data) + @expose('mako:moksha.templates.index') + def index(self): + return dict() + + This is best utilized in conjunction with Nginx and Memcached. + """ + if pylons.g.cache and pylons.request.environ.get('HTTP_X_FORWARDED_PROTO'): + pylons.g.cache.set(pylons.request.path_qs, str(data)) + +@decorator +def cache_rendered(func, *args, **kwargs): + print "cache_rendered" + raise Exception + content = func(*args, **kwargs) + print "wrapper. content = %r" % content + if pylons.g.cache and \ + pylons.request.environ.get('HTTP_X_FORWARDED_PROTO'): + pylons.g.cache.set(pylons.request.path_qs, str(content)) + print "cache_rendered(%r)" % str(content) + else: print "Not caching rendered!" + return content diff --git a/moksha/widgetbrowser/widgetbrowser/wsgiapp.py b/moksha/widgetbrowser/widgetbrowser/wsgiapp.py index 600d8b2..ec12764 100644 --- a/moksha/widgetbrowser/widgetbrowser/wsgiapp.py +++ b/moksha/widgetbrowser/widgetbrowser/wsgiapp.py @@ -17,6 +17,8 @@ try: except ImportError: have_formencode = False
+from moksha.lib.helpers import cache_rendered + __all__ = ['WidgetBrowser']
log = logging.getLogger(__name__) @@ -119,6 +121,7 @@ class WidgetBrowser(object): def __call__(self, environ, start_response): return self.app(environ, start_response)
+ @cache_rendered def app(self, environ, start_response): req = Request(environ) resp = Response(request=req, content_type='text/html; charset=utf-8') @@ -198,6 +201,7 @@ class WidgetBrowser(object): widget = req.widget return self.render('widget_index.html', locals())
+ @cache_rendered def show_demo(self, req, resp): widget = req.widget widget_name = widget.__class__.__name__ @@ -215,7 +219,10 @@ class WidgetBrowser(object): _disp = req.GET.get('_disp', '') else: _disp = '' + widget_output = util.display_widget(widget, _disp, self.context) + + # Wrap non-genshi things, like Mako based widgets, in a Genshi Markup object if not callable(widget_output): widget_output = Markup(widget_output)
diff --git a/moksha/wsgiapp.py b/moksha/wsgiapp.py index 5003877..2fee60c 100644 --- a/moksha/wsgiapp.py +++ b/moksha/wsgiapp.py @@ -18,8 +18,6 @@
from pylons.wsgiapp import PylonsApp
-import moksha - class MokshaAppDispatcher(PylonsApp): """ Moksha WSGI Application Dispatcher.
diff --git a/setup.py b/setup.py index 7aa603d..0d08282 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,7 @@ setup( [moksha.stream] demo = moksha.streams.demo:MokshaDemoDataStream #livegraph = moksha.examples.livegraph:LiveGraphDataStream - #feed = moksha.streams.feed:FeedStream + feed = moksha.streams.feed:FeedStream
#[moksha.consumer] #moksha = moksha.hub.hub:MokshaConsumer diff --git a/start-moksha b/start-moksha index 33c0461..269bf9d 100755 --- a/start-moksha +++ b/start-moksha @@ -23,11 +23,18 @@ python setup.py develop
echo "Starting Moksha..."
+echo "Starting the WSGI stack..." paster serve development.ini & echo $! > paster.pid
+echo "Starting Orbited..." orbited & echo $! > orbited.pid
+echo "Starting the Moksha Hub..." python moksha/hub/hub.py -v & echo $! > hub.pid + +#echo "Starting memcached..." +#memcached & +#echo $! > memcached.pid diff --git a/stop-moksha b/stop-moksha index 3e32a25..9c0ce08 100755 --- a/stop-moksha +++ b/stop-moksha @@ -8,4 +8,7 @@ fi if [ -f hub.pid ]; then kill $(cat hub.pid) fi +if [ -f memcached.pid ]; then + kill $(cat memcached.pid) +fi rm -f *.pid
moksha-commits@lists.fedorahosted.org