Commit c89c6533 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Add erp5.component.extension as a dynamic module where extensions are lazily loaded.

parent 610b2abe
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
# Arnaud Fontaine <arnaud.fontaine@nexedi.com>
# Jean-Paul Smets <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
......@@ -26,6 +28,9 @@
#
##############################################################################
import imp
import os
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Base import Base
......@@ -53,15 +58,20 @@ class DocumentComponent(Base):
'Reference',
'TextDocument')
def loadComponent(self):
"""
"""
from Products.ERP5Type.Utils import importLocalDocument
def load(self):
# XXX-arnau: There should be a load_source() taking a string rather than
# creating a temporary file
from App.config import getConfiguration
instance_home = getConfiguration().instancehome
path = '%s/component/document' % instance_home
component_path = '%s/%s.py' % (path, self.getReference()) # using ID would be better... with some extensions in importLocalDocument
component_file = open(component_path, 'w')
component_file.write(self.getTextContent())
component_file.close()
importLocalDocument(self.getReference(), path=path)
path = '%s/Component' % instance_home
if not os.path.isdir(path):
os.mkdir(path)
component_path = '%s/%s.py' % (path, self.getId())
with open(component_path, 'w') as component_file:
component_file.write(self.getTextContent())
try:
return imp.load_source(self.getReference(), component_path)
finally:
os.remove(component_path)
......@@ -27,88 +27,19 @@
#
##############################################################################
""" Component Tool module for ERP5 """
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions
class ComponentLoader:
"""
A callable class which delegates component load to ComponentTool
and which contains attributes which can either be called or
implement default component access for different types of components
"""
def __init__(self):
self.component = self
# This could be automated by introspecting
# portal_types and listing all component types
self.document = TypedComponentLoader('Document Component')
self.interface = TypedComponentLoader('Interface Component')
self.mixin = TypedComponentLoader('Mixin Component')
self.accessor = TypedComponentLoader('Accessor Component')
self.test = TypedComponentLoader('Test Component')
self.extension = TypedComponentLoader('Extension Component')
def __call__(self, portal_type, reference, version=None):
site = getSite()
return site.portal_components.loadComponent(portal_type, reference, version=version)
def TypedComponentLoader(ComponentLoader):
"""
A callable class which delegates component load to
ComponentLoader and provides default component access through
attributes.
"""
def __init__(self, portal_type):
self._portal_type = portal_type
def __call__(self, reference, version=None):
return ComponentLoader.__call__(self._portal_type, reference, version=version)
def __getattr__(self, key):
if key.startswith('_'):
return self.__dict__[key]
return self(key)
component = ComponentLoader()
component_revision = None
component_dict = None
class ComponentTool(BaseTool):
"""
This tool provides methods to load the the different types
of components of the ERP5 framework: Document classes, interfaces,
mixin classes, fields, accessors, etc.
"""
id = "portal_components"
meta_type = "ERP5 Component Tool"
portal_type = "Component Tool"
security = ClassSecurityInfo()
manage_options = BaseTool.manage_options
def loadComponent(self, portal_type, reference, version=None):
"""
This first version compiles all components once. A lazy
version could be more efficient.
"""
global component_dict
# Reset cache if modified
#if component_revision is None:
# component_dict = None
if component_dict is None:
component_dict = {}
for document in contentValues():
portal_type = document.getPortalType()
version = document.getVersion()
reference = document.getReference()
component = document.loadComponent()
typed_component_dict = component_dict.setdefault(portal_type, {})
component_version_dict = typed_component_dict.setdefault(reference, {})
# How to handle default ?
component_version_dict[version] = component
component_version_dict[None] = component
return component_dict[portal_type][reference][version]
security.declareObjectProtected(Permissions.AccessContentsInformation)
##############################################################################
#
# Copyright (c) 2012 Nexedi SARL and Contributors. All Rights Reserved.
# Arnaud Fontaine <arnaud.fontaine@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from zLOG import LOG, INFO
class ComponentProxyClass(object):
"""
XXX-arnau: should maybe use Ghost class?
"""
def __init__(self, component, module):
self._component = component
self._module = module
self.__isghost__ = False
# XXX-arnau: metaclass!
self.__class__.__name__ = component.getReference()
self.__class__.__module__ = component.getId().rsplit('.', 1)[0]
description = component.getDescription()
if description:
self.__doc__ = description
def restoreGhostState(self):
self.__isghost__ = True
def __getattr__(self, name):
if self.__isghost__:
self._module = self._component.load()
self.__isghost__ = False
LOG("ERP5Type.dynamic", INFO, "Reloaded %s" % self._component.getId())
return getattr(self._module, name)
def generateComponentClassWrapper(namespace):
def generateComponentClass(component_name):
from Products.ERP5.ERP5Site import getSite
site = getSite()
component_name = '%s.%s' % (namespace, component_name)
try:
component = getattr(site.portal_components, component_name)
except AttributeError:
LOG("ERP5Type.dynamic", INFO,
"Could not find %s, perhaps it has not been migrated yet?" % component_name)
raise
else:
if component.getValidationState() == 'validated':
klass = ComponentProxyClass(component, component.load())
LOG("ERP5Type.dynamic", INFO, "Loaded successfully %s" % component_name)
return klass
else:
raise AttributeError("Component %s not validated" % component_name)
return generateComponentClass
......@@ -87,6 +87,8 @@ def initializeDynamicModules():
holds accessors holders of Portal Types
erp5.component:
holds component modules
erp5.component.extension:
holds extension classes previously found in bt5 in instancehome/Extensions
"""
erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5
......@@ -122,3 +124,8 @@ def initializeDynamicModules():
# Components
erp5.component = ModuleType("erp5.component")
sys.modules["erp5.component"] = erp5.component
from component_class import generateComponentClassWrapper
erp5.component.extension = registerDynamicModule(
'erp5.component.extension',
generateComponentClassWrapper('erp5.component.extension'))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment