diff --git a/tiramisu/storage/sqlalchemy/setting.py b/tiramisu/storage/sqlalchemy/setting.py
new file mode 100644
index 0000000..c9ad3da
--- /dev/null
+++ b/tiramisu/storage/sqlalchemy/setting.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+"default plugin for setting: set it in a simple dictionary"
+# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+# ____________________________________________________________
+from ..util import Cache
+from .util import SqlAlchemyBase
+import util
+from sqlalchemy import Column, Integer, String, PickleType, ForeignKey
+from sqlalchemy.orm import relationship
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.orm.collections import attribute_mapped_collection
+
+
+#____________________________________________________________
+#
+# properties|permissives
+class _Property(SqlAlchemyBase):
+ __tablename__ = 'property'
+ id = Column(Integer, primary_key=True)
+ setting = Column(Integer, ForeignKey('settings.id'), nullable=False)
+ path = Column(String)
+ properties = Column(PickleType)
+
+ def __init__(self, path, properties):
+ self.path = path
+ self.properties = properties
+
+
+class _Permissive (SqlAlchemyBase):
+ __tablename__ = 'permissive'
+ id = Column(Integer, primary_key=True)
+ setting = Column(Integer, ForeignKey('settings.id'), nullable=False)
+ path = Column(String)
+ permissives = Column(PickleType)
+
+ def __init__(self, path, permissives):
+ self.path = path
+ self.permissives = permissives
+
+
+#____________________________________________________________
+#FIXME marche pas le cache ... de toute facon je vais faire un storage separe !
+class Settings(Cache, SqlAlchemyBase):
+ __tablename__ = 'settings'
+ id = Column(Integer, primary_key=True)
+ _props = relationship("_Property",
+ collection_class=attribute_mapped_collection('path'),
+ cascade="all, delete-orphan")
+ _properties = association_proxy("_props", "properties")
+ _perms = relationship("_Permissive",
+ collection_class=attribute_mapped_collection('path'),
+ cascade="all, delete-orphan")
+ _permissives = association_proxy("_perms", "permissives")
+
+ def __init__(self, storage):
+ super(Settings, self).__init__(storage)
+
+ # properties
+ def setproperties(self, path, properties):
+ self._properties[path] = properties
+
+ def getproperties(self, path, default_properties):
+ return self._properties.get(path, set(default_properties))
+
+ def hasproperties(self, path):
+ return path in self._properties
+
+ def reset_all_properties(self):
+ self._properties.clear()
+
+ def delproperties(self, path):
+ try:
+ del(self._properties[path])
+ except KeyError:
+ pass
+
+ # permissive
+ def setpermissive(self, path, permissive):
+ self._permissives[path] = frozenset(permissive)
+ util.session.commit()
+
+ def getpermissive(self, path=None):
+ ret = self._permissives.get(path, frozenset())
+ #replace None by a frozenset()
+ return {None: frozenset()}.get(ret, ret)
+
+ def get_modified_properties(self):
+ """return all modified settings in a dictionary
+ example: {'path1': set(['prop1', 'prop2'])}
+ """
+ return self._properties
+
+ def get_modified_permissives(self):
+ """return all modified permissives in a dictionary
+ example: {'path1': set(['perm1', 'perm2'])}
+ """
+ return self._permissives
diff --git a/tiramisu/storage/sqlalchemy/storage.py b/tiramisu/storage/sqlalchemy/storage.py
new file mode 100644
index 0000000..252d29e
--- /dev/null
+++ b/tiramisu/storage/sqlalchemy/storage.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+# ____________________________________________________________
+from tiramisu.i18n import _
+from tiramisu.error import ConfigError
+from ..util import SerializeObject
+
+
+class Setting(SerializeObject):
+ """Dictionary storage has no particular setting.
+ """
+ pass
+
+
+setting = Setting()
+_list_sessions = []
+
+
+def list_sessions(): # pragma: optional cover
+ return _list_sessions
+
+
+def delete_session(session_id): # pragma: optional cover
+ raise ConfigError(_('dictionary storage cannot delete session'))
+
+
+class Storage(object):
+ __slots__ = ('session_id', 'persistent')
+ storage = 'dictionary'
+ #if object could be serializable
+ serializable = True
+
+ def __init__(self, session_id, persistent, test=False):
+ if not test and session_id in _list_sessions: # pragma: optional cover
+ raise ValueError(_('session already used'))
+ if persistent: # pragma: optional cover
+ raise ValueError(_('a dictionary cannot be persistent'))
+ self.session_id = session_id
+ self.persistent = persistent
+ _list_sessions.append(self.session_id)
+
+ def __del__(self):
+ try:
+ _list_sessions.remove(self.session_id)
+ except AttributeError: # pragma: optional cover
+ pass
diff --git a/tiramisu/storage/sqlalchemy/util.py b/tiramisu/storage/sqlalchemy/util.py
new file mode 100644
index 0000000..7af84a1
--- /dev/null
+++ b/tiramisu/storage/sqlalchemy/util.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+""
+# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
+#
+# This program 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 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# ____________________________________________________________
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import create_engine
+
+
+engine = create_engine('sqlite:///:memory:')
+SqlAlchemyBase = declarative_base()
+
+global session
+session = None
+
+
+def load():
+ global session
+ if session is None:
+ #engine.echo = True
+ #print SqlAlchemyBase.metadata.tables.keys()
+ SqlAlchemyBase.metadata.create_all(engine)
+ Session = sessionmaker(bind=engine)
+ session = Session()
diff --git a/tiramisu/storage/sqlalchemy/value.py b/tiramisu/storage/sqlalchemy/value.py
new file mode 100644
index 0000000..30447eb
--- /dev/null
+++ b/tiramisu/storage/sqlalchemy/value.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"plugin for value: set it in sqlalchemy"
+# Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors)
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+# ____________________________________________________________
+
+#FIXME : il me faut une classe pour le owner !
+#FIXME : pas si simple que ca ... parce que on lit un owner pour une config ...
+#FIXME : mais ca serait peut etre logique
+#FIXME : c'est en fait dans le Setting qu'il faut faire ca ... a voir après
+
+
+from ..util import Cache
+from .util import SqlAlchemyBase
+from sqlalchemy import Column, Integer, String, PickleType, ForeignKey
+from sqlalchemy.orm import relationship
+from sqlalchemy.orm.collections import attribute_mapped_collection
+from sqlalchemy.ext.associationproxy import association_proxy
+
+
+#____________________________________________________________
+#
+# information
+class _Vinformation(SqlAlchemyBase):
+ __tablename__ = 'vinformation'
+ id = Column(Integer, primary_key=True)
+ values = Column(Integer, ForeignKey('values.id'))
+ key = Column(String)
+ value = Column(PickleType)
+
+ def __init__(self, key, value):
+ self.key = key
+ self.value = value
+
+
+class _Value(SqlAlchemyBase):
+ __tablename__ = 'value'
+ id = Column(Integer, primary_key=True)
+ values = Column(Integer, ForeignKey('values.id'), nullable=False)
+ path = Column(String, nullable=True, unique=True, index=True)
+ #FIXME a revoir avec le owner dans le setting
+ owner = Column(String, nullable=False)
+ value = Column(PickleType, nullable=False)
+
+ def __init__(self, key, value):
+ self.path = key
+ self.value = value[0]
+ self.owner = value[1]
+
+
+class Values(Cache, SqlAlchemyBase):
+ __tablename__ = 'values'
+ id = Column(Integer, primary_key=True)
+ _vals = relationship("_Value",
+ collection_class=attribute_mapped_collection('key'),
+ cascade="all, delete-orphan")
+ _informations = association_proxy("_vals", "value")
+ _infos = relationship("_Vinformation",
+ collection_class=attribute_mapped_collection('key'),
+ cascade="all, delete-orphan")
+ _informations = association_proxy("_infos", "value")
+
+ def __init__(self, storage):
+ """init plugin means create values storage
+ """
+ self._values = {}
+ self._informations = {}
+ super(Values, self).__init__(storage)
+
+ # value
+ def setvalue(self, path, value, owner):
+ """set value for a path
+ a specified value must be associated to an owner
+ """
+ self._values[path] = (owner, value)
+
+ def getvalue(self, path):
+ """get value for a path
+ return: only value, not the owner
+ """
+ return self._values[path][1]
+
+ def hasvalue(self, path):
+ """if path has a value
+ return: boolean
+ """
+ return path in self._values
+
+ def resetvalue(self, path):
+ """remove value means delete value in storage
+ """
+ del(self._values[path])
+
+ def get_modified_values(self):
+ """return all values in a dictionary
+ example: {'path1': (owner, 'value1'), 'path2': (owner, 'value2')}
+ """
+ return self._values
+
+ # owner
+ def setowner(self, path, owner):
+ """change owner for a path
+ """
+ self._values[path] = (owner, self._values[path][1])
+
+ def getowner(self, path, default):
+ """get owner for a path
+ return: owner object
+ """
+ return self._values.get(path, (default, None))[0]
+
+ def set_information(self, key, value):
+ """updates the information's attribute
+ (which is a dictionary)
+
+ :param key: information's key (ex: "help", "doc"
+ :param value: information's value (ex: "the help string")
+ """
+ self._informations[key] = value
+
+ def get_information(self, key):
+ """retrieves one information's item
+
+ :param key: the item string (ex: "help")
+ """
+ if key in self._informations:
+ return self._informations[key]
+ else: # pragma: optional cover
+ raise ValueError("not found")