docs/_static/moksha-messaging-amqp.png |binary docs/_static/moksha-messaging-stomp-amqp.png |binary docs/_static/moksha-messaging-stomp.png |binary docs/_static/moksha-middleware.png |binary docs/main/Messaging.rst | 1 docs/main/MessagingScenarios.rst | 29 + docs/main/Middleware.rst | 25 - moksha/apps/knowledge/moksha/apps/knowledge/controllers/root.py | 99 ++++ moksha/apps/knowledge/moksha/apps/knowledge/model/model.py | 87 +++- moksha/apps/knowledge/pavement.py | 32 + moksha/apps/knowledge/pavement.py.hide | 34 - moksha/tests/models/test_entity.py | 205 +++++----- pavement.py | 2 13 files changed, 338 insertions(+), 176 deletions(-)
New commits: commit 4374a4ec573f543efca6f2106b7111e5cdebb840 Author: Luke Macken lmacken@redhat.com Date: Wed Dec 16 21:16:37 2009 -0500
Update our middleware documentation
diff --git a/docs/main/Middleware.rst b/docs/main/Middleware.rst index be9c7da..65cce68 100644 --- a/docs/main/Middleware.rst +++ b/docs/main/Middleware.rst @@ -12,10 +12,10 @@ It currently provides the following functionality * Handles dispatching moksha applications, which can be any WSGI app or Controller. * Handles dispatching to individual widgets, which are simply `ToscaWidgets http://toscawidgets.org`_ * Sets up `SQLAlchemy` database engines for each application - * Initializes applications data models * Loads all application configuration + * Initializes the :doc:`LiveWidget` callback registry + * Initializes applications data models * Sets up the moksha resource connectors - * Sets up the stomp callback registry
Using the MokshaMiddleware -------------------------- @@ -53,23 +53,4 @@ Not only can Moksha be inserted into any existing `WSGI http://wsgi.org`_-comp but on it's own offers a comprehensive top-to-bottom middleware stack that provides a vast plethora of additional functionality.
-=============================== =============== -Middleware Function Module -=============================== =============== -Profiling repoze.profile -Resource compression repoze.squeeze - **TG2 Middleware** ---------------------------------------------------- -Application routing/dispatching Routes -Session management Beaker -Caching layer Beaker -Widget resource injection ToscaWidgets -Authentication repoze.who -Authorization repoze.what -Transaction management repoze.tm2 -Error handling & Debugging WebError -Registry manager Paste - **Moksha Middleware** ---------------------------------------------------- -Your application -=============================== =============== +.. image:: ../_static/moksha-middleware.png
commit 4a0e49c4b7c3da8ee1caea41e2c3c5ec3740fe2c Author: Luke Macken lmacken@redhat.com Date: Wed Dec 16 21:15:43 2009 -0500
Add documentation on different messaging scenarios
diff --git a/docs/_static/moksha-messaging-amqp.png b/docs/_static/moksha-messaging-amqp.png new file mode 100644 index 0000000..e54275f Binary files /dev/null and b/docs/_static/moksha-messaging-amqp.png differ diff --git a/docs/_static/moksha-messaging-stomp-amqp.png b/docs/_static/moksha-messaging-stomp-amqp.png new file mode 100644 index 0000000..3070d63 Binary files /dev/null and b/docs/_static/moksha-messaging-stomp-amqp.png differ diff --git a/docs/_static/moksha-messaging-stomp.png b/docs/_static/moksha-messaging-stomp.png new file mode 100644 index 0000000..bba1d70 Binary files /dev/null and b/docs/_static/moksha-messaging-stomp.png differ diff --git a/docs/_static/moksha-middleware.png b/docs/_static/moksha-middleware.png new file mode 100644 index 0000000..704add4 Binary files /dev/null and b/docs/_static/moksha-middleware.png differ diff --git a/docs/main/Messaging.rst b/docs/main/Messaging.rst index f2b29a1..2eefda8 100644 --- a/docs/main/Messaging.rst +++ b/docs/main/Messaging.rst @@ -10,3 +10,4 @@ Messaging LiveWidget MokshaHub MessageBrokers + MessagingScenarios diff --git a/docs/main/MessagingScenarios.rst b/docs/main/MessagingScenarios.rst new file mode 100644 index 0000000..6719906 --- /dev/null +++ b/docs/main/MessagingScenarios.rst @@ -0,0 +1,29 @@ +Messaging Scenarios +=================== + +.. image:: ../_static/moksha-messaging-stomp.png + +Pros: + - No configuration needed + - Works out-of-the-box +Cons: + - Not designed for scalability + - No wildcard topic names + +.. image:: ../_static/moksha-messaging-stomp-amqp.png + + +Pros + - STOMP/AMQP broker briding +Cons + - Not very well tested + - May or may not work as expected + +.. image:: ../_static/moksha-messaging-amqp.png + +Pros: + - Trivial to configure + - 100% AMQP, from the browser to the broker + - Flexibility and power of AMQP queues, exchanges, routing keys, etc. +Cons: + - Relatively new and not 100% complete JavaScript bindings
commit 08b7bc55d709f35d1642437d72824ec2a70a8bb0 Author: Luke Macken lmacken@redhat.com Date: Tue Dec 15 21:03:17 2009 -0500
Add a couple more unit tests for our knowledge app
diff --git a/moksha/tests/models/test_entity.py b/moksha/tests/models/test_entity.py index b37eb21..ed1a898 100644 --- a/moksha/tests/models/test_entity.py +++ b/moksha/tests/models/test_entity.py @@ -37,6 +37,7 @@ class TestEntity(ModelTest): self.obj[u'age'] = 24 self.obj[u'dob'] = datetime(1984, 11, 02) self.obj[u'l33t'] = True + self.obj[u'json'] = {'foo':'bar'}
def test_entity_creation_name(self): eq_(self.obj.name, u'lmacken') @@ -84,6 +85,19 @@ class TestEntity(ModelTest): assert (DBSession.query(Fact). filter(with_characteristic(u'l33t', True))).one()
+ def test_child_entities(self): + child = Entity(u'child1') + DBSession.add(child) + self.obj.append(child) + DBSession.flush() + me = Entity.by_name(u'lmacken') + assert_true('child1' in me.children) + eq_(self.obj.children['child1'].parent, me) + + def test_entity_json_fact(self): + me = Entity.by_name(u'lmacken') + eq_(me[u'json'], {'foo': 'bar'}) + """ TODO: add hooks that send AMQP messages? Or, do this i
commit ea59a3ad9b50bea9d2d0a305c9a7b269de76d18c Author: Luke Macken lmacken@redhat.com Date: Tue Dec 15 21:02:06 2009 -0500
Entity/fact model tweaks, and allow us to create Entities w/o any args for sprox
diff --git a/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py b/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py index 1a7d0ee..d2ed245 100644 --- a/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py +++ b/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py @@ -84,14 +84,14 @@ class Entity(VerticalPropertyDictMixin, DeclarativeBase): _property_type = Fact _property_mapping = 'facts'
- #id = Column(Integer, autoincrement=True, primary_key=True) - name = Column(UnicodeText) + id = Column(Integer, autoincrement=True, primary_key=True) + name = Column(UnicodeText, nullable=False)
facts = relation(Fact, backref='entity', collection_class=attribute_mapped_collection('key'))
# Adjacency tree properties - id = Column(Integer, Sequence('moksha_tree_id_seq', optional=True), primary_key=True) + #id = Column(Integer, Sequence('moksha_tree_id_seq', optional=True), primary_key=True) parent_id = Column(Integer, ForeignKey('moksha_entities.id'), nullable=True)
children = relation('Entity', cascade="all", @@ -99,7 +99,7 @@ class Entity(VerticalPropertyDictMixin, DeclarativeBase): collection_class=attribute_mapped_collection('name'), lazy=False, join_depth=3)
- def __init__(self, name): + def __init__(self, name=None): self.name = name
@classmethod diff --git a/moksha/apps/knowledge/pavement.py b/moksha/apps/knowledge/pavement.py index e4b20dc..8bb2d45 100644 --- a/moksha/apps/knowledge/pavement.py +++ b/moksha/apps/knowledge/pavement.py @@ -22,13 +22,11 @@ options( namespace_packages=[ 'moksha', 'moksha.apps', - ], install_requires=["Moksha"], - entry_points={ - 'moksha.application': ( - 'moksha.apps.knowledge = moksha.apps.knowledge.controllers.root:RootController' - ), - } + entry_points=""" + [moksha.application] + moksha.apps.knowledge = moksha.apps.knowledge.controllers.root:RootController + """, ), )
commit e08ed309917f6facefc0c5443c6ed6c053f59117 Author: Luke Macken lmacken@redhat.com Date: Tue Dec 15 21:01:13 2009 -0500
Initial attempt at using the CrudRestController & sprox for our entity/fact model
diff --git a/moksha/apps/knowledge/moksha/apps/knowledge/controllers/root.py b/moksha/apps/knowledge/moksha/apps/knowledge/controllers/root.py index 1fd2644..22924ad 100644 --- a/moksha/apps/knowledge/moksha/apps/knowledge/controllers/root.py +++ b/moksha/apps/knowledge/moksha/apps/knowledge/controllers/root.py @@ -4,14 +4,109 @@ from tg import expose, flash, require, url, request, redirect from pylons.i18n import ugettext as _, lazy_ugettext as l_ from sqlalchemy import or_ +from tgext.crud import CrudRestController +from sprox.tablebase import TableBase +from sprox.fillerbase import TableFiller, EditFormFiller +from sprox.formbase import AddRecordForm, EditableForm +from tw.core import WidgetsList +from tw.forms import TableForm, TextField, CalendarDatePicker, SingleSelectField, TextArea +from formencode.validators import Int, NotEmpty, DateConverter, DateValidator +from repoze.what.predicates import has_permission
+from moksha import model +#from moksha.model import DBSession, metadata from moksha.lib.base import Controller from moksha.lib.helpers import defaultdict - -#from moksha.model import DBSession from moksha.apps.knowledge.model import Fact, Entity, with_characteristic, DBSession
+#class EntityForm(TableForm): +# class fields(WidgetsList): +# name = TextField(validator=NotEmpty) + #description = TextArea(attrs=dict(rows=3, cols=25)) + #release_date = CalendarDatePicker(validator=DateConverter()) + #genrechoices = ((1,"action"), + # (2,"animation"), + # (3,"comedy"), + # (4,"documentary"), + # (5,"drama"), + # (6,"sci-fi")) + #genre = SingleSelectField(options=genrechoices) + +#then, we create an instance of this form +#movie_add_form = MovieForm("create_movie_form") +# +# + +class EntityTable(TableBase): + __model__ = Entity +entity_table = EntityTable(DBSession) + +class EntityTableFiller(TableFiller): + __model__ = Entity +entity_table_filler = EntityTableFiller(DBSession) + +class EntityAddForm(AddRecordForm): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id', 'facts', 'children'] +entity_add_form = EntityAddForm(DBSession) + +class EntityController(CrudRestController): + requires = has_permission('manager') + + model = Entity + #table = entity_table + #table_filler = entity_table_filler + #new_form = entity_add_form + + @expose('mako:moksha.apps.knowledge.templates.get_all') + def get_all(self, *args, **kw): + return super(EntityController, self).get_all(*args, **kw) + + @expose('mako:moksha.apps.knowledge.templates.new') + def new(self, *args, **kw): + return super(EntityController, self).new(*args, **kw) + + @expose('mako:moksha.apps.knowledge.templates.edit') + def edit(self, *args, **kw): + return super(EntityController, self).edit(*args, **kw) + + #@expose() + #def post(self, **kw): + # print "EntityController.post(%s)" % locals() + # return super(EntityController, self).post(**kw) + + class new_form_type(AddRecordForm): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id'] + + class edit_form_type(EditableForm): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id'] + + class edit_filler_type(EditFormFiller): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id'] + + class table_type(TableBase): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id'] + + class table_filler_type(TableFiller): + __model__ = Entity + __omit_fields__ = ['id', 'parent_id'] + __xml_fields__ = ['children', 'facts'] + + def children(self, entity): + return ', '.join([child.name for child in entity.children]) + + def facts(self, entity, *args, **kw): + #print "facts(%s)"% locals() + for fact in entity.facts: + print fact + return entity.name + class RootController(Controller): + entities = EntityController(DBSession)
@expose('json') def index(self):
commit 19bcb72156ea8b3d4d9ffd717f149b0f3bc93525 Author: Luke Macken lmacken@redhat.com Date: Tue Dec 15 00:56:12 2009 -0500
Make our polymoprhic vertical model also an adjacency tree.
Also, re-enable the Fact/Entity test cases
diff --git a/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py b/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py index bf84685..1a7d0ee 100644 --- a/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py +++ b/moksha/apps/knowledge/moksha/apps/knowledge/model/model.py @@ -1,37 +1,45 @@ +# -*- coding: utf-8 -*- # This file is part of Moksha. -# Copyright (C) 2008-2009 Red Hat, Inc. # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. +# Moksha is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, +# Moksha is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. +# GNU General Public License for more details. # -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. +# You should have received a copy of the GNU General Public License +# along with Moksha. If not, see http://www.gnu.org/licenses/. # -# Authors: Luke Macken lmacken@redhat.com +# Copyright 2008-2009, Red Hat, Inc. +""" +:mod:`moksha.apps.knowledge.model` -- The Moksha Polymorphic Vertical Adjacency Tree Model +========================================================================================== + +.. moduleauthor:: Luke Macken lmacken@redhat.com +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.orm.collections import attribute_mapped_collection
from datetime import datetime from sqlalchemy import * from sqlalchemy.orm import relation, comparable_property from sqlalchemy.orm.collections import attribute_mapped_collection -from moksha.model.hook import MokshaHookMapperExtension -from moksha.model import metadata, DBSession, DeclarativeBase from vertical import PolymorphicVerticalProperty, VerticalPropertyDictMixin
-moksha_mapper_extension = MokshaHookMapperExtension() +from moksha.model import metadata, DeclarativeBase, DBSession +
class Fact(PolymorphicVerticalProperty, DeclarativeBase): """ A polymorphic-valued vertical table property """ - __tablename__ = 'facts' - #__mapper_args__ = {'extension': moksha_mapper_extension} + __tablename__ = 'moksha_facts'
- id = Column(Integer, ForeignKey('entities.id'), primary_key=True) + id = Column(Integer, ForeignKey('moksha_entities.id'), primary_key=True) key = Column(Unicode(64), primary_key=True) type_ = Column(Unicode(16), default=None) int_value = Column(Integer, default=None) @@ -59,7 +67,7 @@ with_characteristic = lambda key, value: and_(Fact.key==key, Fact.value==value)
class Entity(VerticalPropertyDictMixin, DeclarativeBase): - """ An entity. + """ An polymorphic entity.
Entity facts are available via the 'facts' property or by using dict-like accessors on an Entity instance:: @@ -68,24 +76,32 @@ class Entity(VerticalPropertyDictMixin, DeclarativeBase): apple['color'] = 'red' # or, equivalently: apple.facts['color'] = Fact('color', 'red') + + This Entity is also an adjacency tree. Meaning, every entity + can have a `parent` and zero or more `children`. """ - __tablename__ = 'entities' - #__mapper_args__ = {'extension': moksha_mapper_extension} + __tablename__ = 'moksha_entities' _property_type = Fact _property_mapping = 'facts'
- id = Column(Integer, autoincrement=True, primary_key=True) - name = Column(Unicode(100)) + #id = Column(Integer, autoincrement=True, primary_key=True) + name = Column(UnicodeText)
facts = relation(Fact, backref='entity', collection_class=attribute_mapped_collection('key'))
+ # Adjacency tree properties + id = Column(Integer, Sequence('moksha_tree_id_seq', optional=True), primary_key=True) + parent_id = Column(Integer, ForeignKey('moksha_entities.id'), nullable=True) + + children = relation('Entity', cascade="all", + backref=backref("parent", remote_side="Entity.id"), + collection_class=attribute_mapped_collection('name'), + lazy=False, join_depth=3) + def __init__(self, name): self.name = name
- def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, self.name) - @classmethod def by_name(cls, name): """A class method that permits to search entities @@ -93,3 +109,26 @@ class Entity(VerticalPropertyDictMixin, DeclarativeBase): """ return DBSession.query(cls).filter(cls.name==name).first()
+ # Adjacency-tree methods + def append(self, node): + if isinstance(node, basestring): + node = Entity(node) + node.parent = self + self.children[node.name] = node + + def __repr__(self): + return self._getstring(0, False) + + def __str__(self): + return self._getstring(0, False) + + def _getstring(self, level, expand = False): + s = (' ' * level) + "%s (%s,%s, %d)" % ( + self.name, self.id,self.parent_id,id(self)) + '\n' + if expand: + s += ''.join([n._getstring(level+1, True) + for n in self.children.values()]) + return s + + def print_nodes(self): + return self._getstring(0, True) diff --git a/moksha/apps/knowledge/pavement.py b/moksha/apps/knowledge/pavement.py new file mode 100644 index 0000000..e4b20dc --- /dev/null +++ b/moksha/apps/knowledge/pavement.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from paver.easy import * +from paver.setuputils import (setup, find_package_data, find_packages, + install_distutils_tasks) +install_distutils_tasks() +from moksha.lib.paver_tasks import * + +options( + setup=Bunch( + name="moksha.knowledge", + version="0.1", + release="1", + url="http://moksha.fedorahosted.org", + description="Describe your package here", + license="AGPLv3", + long_description="", + author="", + author_email="", + rpm_name='moksha-apps-knowledge', + packages=find_packages(), + package_data=find_package_data(), + namespace_packages=[ + 'moksha', + 'moksha.apps', + + ], + install_requires=["Moksha"], + entry_points={ + 'moksha.application': ( + 'moksha.apps.knowledge = moksha.apps.knowledge.controllers.root:RootController' + ), + } + ), +) diff --git a/moksha/apps/knowledge/pavement.py.hide b/moksha/apps/knowledge/pavement.py.hide deleted file mode 100644 index e4b20dc..0000000 --- a/moksha/apps/knowledge/pavement.py.hide +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -from paver.easy import * -from paver.setuputils import (setup, find_package_data, find_packages, - install_distutils_tasks) -install_distutils_tasks() -from moksha.lib.paver_tasks import * - -options( - setup=Bunch( - name="moksha.knowledge", - version="0.1", - release="1", - url="http://moksha.fedorahosted.org", - description="Describe your package here", - license="AGPLv3", - long_description="", - author="", - author_email="", - rpm_name='moksha-apps-knowledge', - packages=find_packages(), - package_data=find_package_data(), - namespace_packages=[ - 'moksha', - 'moksha.apps', - - ], - install_requires=["Moksha"], - entry_points={ - 'moksha.application': ( - 'moksha.apps.knowledge = moksha.apps.knowledge.controllers.root:RootController' - ), - } - ), -) diff --git a/moksha/tests/models/test_entity.py b/moksha/tests/models/test_entity.py index 751be65..b37eb21 100644 --- a/moksha/tests/models/test_entity.py +++ b/moksha/tests/models/test_entity.py @@ -1,107 +1,110 @@ +# -*- coding: utf-8 -*- # This file is part of Moksha. # Copyright (C) 2008-2009 Red Hat, Inc. -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. -# +# # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
-# -*- coding: utf-8 -*- -#"""Test Moksha's Entity/Fact model""" -# -#from datetime import datetime -#from nose.tools import eq_, assert_true -#from sqlalchemy import * -#from moksha.model import DBSession, Entity, Fact, with_characteristic -#from moksha.tests.models import ModelTest - -#class TestEntity(ModelTest): -# """Test case for the Entity model.""" -# -# klass = Entity -# attrs = dict(name=u'lmacken') -# -# def setup(self): -# super(TestEntity, self).setup() -# self.obj[u'firstname'] = u'Luke' -# self.obj[u'lastname'] = u'Macken' -# self.obj[u'age'] = 24 -# self.obj[u'dob'] = datetime(1984, 11, 02) -# self.obj[u'l33t'] = True -# -# def test_entity_creation_name(self): -# eq_(self.obj.name, u'lmacken') -# -# def test_fact_types(self): -# DBSession.add(self.obj) -# DBSession.flush() -# me = DBSession.query(Entity).filter_by(name=u'lmacken').one() -# eq_(me[u'lastname'], u'Macken') -# eq_(me[u'age'], 24) -# eq_(me[u'dob'], datetime(1984, 11, 2)) -# eq_(me[u'l33t'], True) -# -# def test_getting_by_name(self): -# """ Entities should be fetchable by their name """ -# DBSession.add(self.obj) -# lmacken = Entity.by_name(u'lmacken') -# eq_(lmacken, self.obj) -# -# def test_filter_by_name(self): -# DBSession.add(self.obj) -# me = DBSession.query(Entity).filter_by(name=u'lmacken').one() -# eq_(me.name, u'lmacken') -# eq_(me[u'firstname'], u'Luke') -# -# def test_query_by_fact(self): -# """ Query entities by facts """ -# DBSession.add(self.obj) -# assert DBSession.query(Entity).filter( -# Entity.facts.any( -# and_(Fact.key == u'l33t', -# Fact.value == True))).first() -# -# def test_query_with_characteristic(self): -# """ Query entities based on facts using with_characteristic """ -# DBSession.add(self.obj) -# assert (DBSession.query(Entity). -# filter(or_(Entity.facts.any( -# with_characteristic(u'dob', datetime(1984, 11, 02))), -# not_(Entity.facts.any(Fact.key == u'l33t'))))).first() -# -# def test_query_facts_by_characteristic(self): -# """ Query facts by certain characteristics """ -# DBSession.add(self.obj) -# assert (DBSession.query(Fact). -# filter(with_characteristic(u'l33t', True))).one() -# -# """ -# def test_hooks(self): -# from moksha.hook import Hook, MokshaHookMapperExtension -# from moksha.model import model -# insert = update = delete = False -# -# class MyHook(Hook): -# def after_insert(self, instance): -# insert = True -# def after_update(self, instance): -# update = True -# def after_delete(self, instance): -# delete = True -# -# model.moksha_mapper_extension.hooks['after_insert'].append(MyHook()) -# -# # create a new entity -# something = Entity(u'something') -# DBSession.add(something) -# DBSession.flush() -# assert insert -# """ +"""Test Moksha's Entity/Fact model""" + +from datetime import datetime +from nose.tools import eq_, assert_true +from sqlalchemy import * +from moksha.tests.models import ModelTest + +from moksha.apps.knowledge.model import DBSession, Entity, Fact, with_characteristic + +class TestEntity(ModelTest): + """Test case for the Entity model.""" + + klass = Entity + attrs = dict(name=u'lmacken') + + def setup(self): + super(TestEntity, self).setup() + self.obj[u'firstname'] = u'Luke' + self.obj[u'lastname'] = u'Macken' + self.obj[u'age'] = 24 + self.obj[u'dob'] = datetime(1984, 11, 02) + self.obj[u'l33t'] = True + + def test_entity_creation_name(self): + eq_(self.obj.name, u'lmacken') + + def test_fact_types(self): + DBSession.add(self.obj) + DBSession.flush() + me = DBSession.query(Entity).filter_by(name=u'lmacken').one() + eq_(me[u'lastname'], u'Macken') + eq_(me[u'age'], 24) + eq_(me[u'dob'], datetime(1984, 11, 2)) + eq_(me[u'l33t'], True) + + def test_getting_by_name(self): + """ Entities should be fetchable by their name """ + DBSession.add(self.obj) + lmacken = Entity.by_name(u'lmacken') + eq_(lmacken, self.obj) + + def test_filter_by_name(self): + DBSession.add(self.obj) + me = DBSession.query(Entity).filter_by(name=u'lmacken').one() + eq_(me.name, u'lmacken') + eq_(me[u'firstname'], u'Luke') + + def test_query_by_fact(self): + """ Query entities by facts """ + DBSession.add(self.obj) + assert DBSession.query(Entity).filter( + Entity.facts.any( + and_(Fact.key == u'l33t', + Fact.value == True))).first() + + def test_query_with_characteristic(self): + """ Query entities based on facts using with_characteristic """ + DBSession.add(self.obj) + assert (DBSession.query(Entity). + filter(or_(Entity.facts.any( + with_characteristic(u'dob', datetime(1984, 11, 02))), + not_(Entity.facts.any(Fact.key == u'l33t'))))).first() + + def test_query_facts_by_characteristic(self): + """ Query facts by certain characteristics """ + DBSession.add(self.obj) + assert (DBSession.query(Fact). + filter(with_characteristic(u'l33t', True))).one() + + """ + TODO: add hooks that send AMQP messages? + Or, do this i + def test_hooks(self): + from moksha.hook import Hook, MokshaHookMapperExtension + from moksha.model import model + insert = update = delete = False + + class MyHook(Hook): + def after_insert(self, instance): + insert = True + def after_update(self, instance): + update = True + def after_delete(self, instance): + delete = True + + model.moksha_mapper_extension.hooks['after_insert'].append(MyHook()) + + # create a new entity + something = Entity(u'something') + DBSession.add(something) + DBSession.flush() + assert insert + """ diff --git a/pavement.py b/pavement.py index 1b64fc6..eb1e3ea 100644 --- a/pavement.py +++ b/pavement.py @@ -175,8 +175,10 @@ def license():
@task def test(): + print "Running Moksha test suite..." sh("nosetests") cwd = os.getcwd() + print "Running MokshaJQPlotDemo test suite..." os.chdir(path('moksha') / 'apps' / 'demo' / 'MokshaJQPlotDemo') sh('nosetests')