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(a)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(a)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(a)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(a)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(a)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(a)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(a)redhat.com>
+# Copyright 2008-2009, Red Hat, Inc.
+"""
+:mod:`moksha.apps.knowledge.model` -- The Moksha Polymorphic Vertical Adjacency Tree
Model
+==========================================================================================
+
+.. moduleauthor:: Luke Macken <lmacken(a)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')