2008-03-17

Архитектура реестров.txt

  2008-03-17 23:05

По назначению в Zope3 выделяются реестр адаптеров и реестр утилит. Глобальный реестр : Глобальный реестр определяется для всего Zope3 сервера, а его заполнение осуществляется в момент старта, посредством использования директив ZCML, наиболее важные из которых перечислены ниже: adapter -- регистрирует адаптер, subscriber -- регистрирует подписной адаптер или обработчик событий, utility -- регистрирует утилиту, class/factory -- регистрирует фабрику класса, interface -- регистрирует интерфейс. Сайт-менеджеры и локальные реестры позволяют вести в одном экземпляре zope несколько сайтов с независимыми служебными утилитами и их настройками. Поиск : Zope имеет встроенную утилиту индексации, с интерфейсом ICatalog. ...

Что такое реестры:

Реестры - это такие компоненты, которые позволяют одним компонентам находить другие компоненты, удовлетворяюще условиям, которые определяются необходимостью взаимодействия с ними.

Локальные и глобальные реестры :

Реестры можно классифицировать по их назначению и по способу реализации. По назначению в Zope3 выделяются реестр адаптеров и реестр утилит. Оба типа реестров имеют две реализации как глобальный реестр и локальный реестр.

Глобальный реестр :

Глобальный реестр определяется для всего Zope3 сервера, а его заполнение осуществляется в момент старта, посредством использования директив ZCML, наиболее важные из которых перечислены ниже:

adapter
регистрирует адаптер,
subscriber
регистрирует подписной адаптер или обработчик событий,
utility
регистрирует утилиту,
class/factory
регистрирует фабрику класса,
interface
регистрирует интерфейс.

Многие другие директивы корректируют глобальные реестры в результате своей работы, но именно перечисленные выше директивы нацелены на непосредственное взаимодействие с реестрами.

Локальные реестры:

Локальные реестры (или, как их еще называют, persistent реестры), создаются и заполняются в процессе работы Zope3 сервера, и действительны не для всего сервера, а только для той части иерархии, которая находится в поддереве объекта, предоставляющего эти реестры. Обычно, локальные реестры сохраняются в ZODB, хотя название "persistent" обуславлено, скорее, особенностями жизненного цикла, а не местом хранения.

Конструкция локальных реестров предполагает, что если искомый элемент не удается найти в локальном реестре, то он ищется в более близком к корню дерева объектов и так далее, вплоть до глобального реестра (на самом деле, из локальных реестров можно собрать любую цепочку, но реально используется именно эта).

Для доступа к локальным реестрам предусмотрен интерфейс IComponents, который реализуется, например, компонентом zope.app.component.site.LocalSiteManager (далее - сайт-менеджер). Zope3 предлагает следующий сценарий использования локальных реестров:

  • Создается контейнер, способный предоставлять сайт-менеджер (такой контейнер должен наследовать от zope.app.component.site.SiteManagerContainer, примером такого контейнера является папка);
  • В контейнере вызовом setSiteManager интерфейса zope.app.component.interfaces.IPossibleSite созадется сайт-менеджер (для этого достаточно выбрать в меню пункт "Make a site");
  • Сайт-менеджер доступен через веб в пространстве имен "++etc++", как "++etc++site";
  • В сайт-менеджере есть страница "registrations" позволяющая посмотреть список утилит, зарегистрированных в его реестре;
  • Любые компоненты, как лежащие в сайт-менеджере и его папках, так и находящиеся в контейнере, предоставляющем сайт-менеджер, могут быть зарегистрированы в его реестре утилит (нужно войти на специальную вкладку, заполнить форму и нажать клавишу "зарегистрировать");

Регистрация локальных адаптеров TTW не поддерживается, хотя локальный реестр адаптеров существует и куски кода, предназначенного для их регистрации, есть в коде сайт-менеджера, его состояние и судьба в ближайшее время не ясны.

Сайт-менеджеры и локальные реестры позволяют вести в одном экземпляре zope несколько сайтов с независимыми служебными утилитами и их настройками.

АПИ реестров :

Подробное описание можно посмотреть в API работы с реестрами.txt. Здесь дадим краткое описание самих функций доступа в реестры (запись осуществляется через ZCML или TTW), большая часть которых расположена в модуле zope.app.zapi:

getSiteManager(context=None)
вернуть локальный сайт-менеджер. Если контекст не указан, то функция возвращает последний сайт-менеджер, пройденный при траверсе (а не глобальный сайт менеджер, как многие думают). Подробности см. SiteManager.txt. Все нижеприведенные функции используют getSiteManager и полностью разделяют это замечание;
getUtility(interface, name='', context=None)
вернуть утилиту с интерфейсом interface и именем name, или породить исключение ComponentLookupError;
queryUtility(interface, name='', default=None, context=None)
аналогично getUtility, но вместо исключения вернуть значение default;
getUtilitiesFor(interface,context=None)
вернуть список пар (имя, утилита) для данного интерфейса;
getAdapter(ob, interface=Interface, name=u'', context=None)
вернуть адаптер объекта ob к интерфейсу interface и именем name. Если адаптер не найден, породить исключение ComponentLookupError;
getAdapters(obs, provide, context = None)
вернуть список пар (имя, адаптер) для всех именованных адаптеров, адаптирующих объекты obs к интерфейсу provide;
queryAdapter(ob, interface=Interface, name=u'', default=None, context=None)
аналогично getAdapter, но если адаптер не найден, вернуть значение *default;
getMultiAdapter(objects, interface=Interface, name=u'', context=None)
вернуть адаптер объектов objects к интерфейсу interface и именем name. Если адаптер не найден, породить исключение ComponentLookupError;
queryMultiAdapter(objects, interface=Interface, name=u'', default=None, context=None)
аналогично getMultiAdapter, но если адаптер не найден, вернуть значение *default;
subscribers(objects, interface, context=None)
вернуть подписные адаптеры к интерфейсу interface для объектов objects;
handle(*objects)
обрабатать объекты objects зарегистрированными обработчиками;
createObject(factory_name, args, *kwargs)
вернуть объект фабрикой factory_name с использованием аргументов args и kwargs;
getFactoryInterfaces(name, context=None)
вернуть интерфейсы фабрики name;
getFactoriesFor(interface, context=None)
вернуть последовательность пар (имя, фабрика) для интерфейса interface;

К вышеизложенному остается добавить, что функции getAdapter и queryAdapter полностью вытеснены синтаксисом IA(ob) - т.е. вызовом интерфейса, в который передается объект как параметр.

Использование реестров :

Основная идея использования реестров была дана в статье Введение в компоненты.txt: "реестр это элемент порядка в хаосе компонент". Реестры обеспечивают подбор компонентов, подходящих для данного типа взаимодействия. В этой главе будут приведены примеры кода, использующие реестры, в основном - локальный реестр утилит.

Поиск :

Zope имеет встроенную утилиту индексации, с интерфейсом ICatalog. Её можно создать, настроить и зарегистрировать как утилиту, предоставляющую такой интерфейс. Пример вызова:

                from zope.app.catalog.interfaces import ICatalog
                from zope.app.zapi import getUtility

                class Search(object) :

                    @property
                    def results(self) :
                        return getUtility(ICatalog,context=self.context) \
                            .searchResults(**self.request.form)

В этом примере приводится mix-in класс (задаётся атрибутом class) для директивы page, темплейт которой (задаётся атрибутом template) получает результаты поиска из атрибута results вида. Подробности использования директивы page можно посмотреть в статье Директивы для создания видов и форм.txt.

Скрытая папка с контентом:

Существует продукт ng.app.smartbanner, который позволяет создать в сайт-менеджере папку, содержащую баннеры, и потом получать эти баннеры в любом месте сайта. Приведем, как пример, код адаптера вида, использующего папку с баннерами:

                class SmartBannerView(object) :
                    """ SmartbannerView
                    """

                    @property
                    def banners(self):
                        """ Get images from ISmartBannerContainer utility
                            and set banners property from them
                        """

                        return [x for x in zapi.getUtility(ISmartBannerContainer).values() ]_

темплейт :

                <tal:block repeat="banner view/banners"
                   tal:content="structure banner/@@smartbannerview"
                   />

регистрация вида :

                <browser:page
                  class=".smartbannerview.SmartBannerView"
                  for="*"
                  name="smartbannerview"
                  template="smartbannerview.pt"
                  permission="zope.View"
                  />

вид @@smartbannerview будет для каждого баннера генерировать запрос вида:

                 "++smartbanner++" <ИМЯ БАННЕРА>

обслуживание которого осуществляется специальной формой адаптера, адаптера пространства имен, пример которого следует:

                from zope.traversing.interfaces import ITraverser, ITraversable
                from zope.app.catalog.interfaces import ICatalog
                from interfaces import ISmartBannerContainer

                from zope.app.zapi import getUtility

                class SmartBannerNamespace(object) :
                    implements(ITraversable)

                    def __init__(self,context,request) :
                        self.context = context
                        self.request = request

                        def traverse(self,name,ignored) :
                            return getUtility(ISmartBannerContainer)[name]

Пользователю продукта не нужно знать все эти нонкости, и достаточно просто вписать в шаблон страницы что-то, такое же простое как:

                 <div tal:content="structure context/@@smartbannerview"/>

Пример захватывает несколько специфичных областей, зато хорошо иллюстрирует то, что утилиты могут применяться для того, чтобы хранить контент, недоступный напрямую, но получаемый при помощи адаптеров вида или адаптера пространства имен.

Такой контент может быть преобразованной версией контента, доступного на сайте или же генерироваться под запрос. В этих случаях утилита чаще всего используется для хранения настроечных параметров, как в следующем примере.

Перечень настроек:

Самые разные компоненты могут требовать для своей работы какие-то настроечные параметры. Такие параметры удобно хранить в перечнях настроек, оформляемых как утилиты. Например, пусть задан интерфейс, описывающий настройки адаптера, преобразующего контент-компонент к интерфейсу абстракта:

                class IAbstractProperty(Interface) :

                    prefix = TextLine(title=u"Префикс заголовка")

                    headlen = Int(title=u"Длина аннотации")

                    keywords = Tuple(title=u"Ключевые слова аннотации",value_type=TextLine())

                    title_attribute = TextLine(title=u"Атрибут title контент-компонента")                                

                    body_attribute = TextLine(title=u"Атрибут title контент-компонента")

                class IAbstract(Interface) :
                    title = TextLine(title=u"Заголовок")
                    abstract = TextLine(title=u"Абстракт")

                class Adapter(object) :
                    adapts(IContent)
                    implements(IAbstract)                    

                    def __init__(self,context) :
                        prop = getUtility(IAbstractProperty,context=context)
                        self.title = prop.prefix % getattr(context,prop.title_attribute,"")
                        keywords = set(prop.keywords)
                        s = ""

                        for phrase in re.findall(
                            "[^.!?]+[.!?]",getattr(context,prop.body_attribute,"") :
                            if keywords.intersection(set(phrase.split(" ")) :
                                s += phrase   
                                if len(s) > prop.headlen :
                                    break                           

                        self.abstract = s

Очевидно, что создание и регистрация в локальном реестре утилиты с интерфейсом IAbstractProperty даст возможность настройки адаптера Adapter под нужды конкретного сайта. Примерно такая идея лежит в основе продукта ng.convertor, который позволяет легко настроить преобразование любого контента в формат, приемлемый для сайта.

Заключение :

Основная функция реестров - подбор компонентов по интерфейсам. Текущая реализация реестров имеет в этом отношении ряд особенностей и ограничений, о которых можно прочитать в статье Особенности работы реестров.txt.

Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml