Технология ZEN / PROTOTYPE...

148
Юрий Якунин, Руслан Квитунов Учебное пособие «Технология ZEN / PROTOTYPE 6» Красноярск, 2008

Transcript of Технология ZEN / PROTOTYPE...

Юрий Якунин, Руслан Квитунов

Учебное пособие

«Технология ZEN / PROTOTYPE 6»

Красноярск, 2008

Оглавление

Введение ............................................................................................................................. 4 1 Быстрый старт................................................................................................................. 5

1.1 Создание области и базы данных...................................................................... 6 1.2 Создание проекта ................................................................................................ 7 1.3 Работа с классами в Caché ................................................................................. 9

1.3.1 Создание свойств класса ..................................................................... 11 1.3.2 Наследование ........................................................................................ 15

1.4 Создание Zen-страницы ................................................................................... 16 1.4.1 Создание таблицы ................................................................................ 18 1.4.2 Создание блока редактирования......................................................... 19 1.4.3 Вызов клиентских методов на странице ............................................ 21

2 Введение в СУБД Caché............................................................................................... 23 2.1 Структура и принципы работы Caché ............................................................ 25 2.2 Сервер баз данных ............................................................................................ 26

2.2.1 Хранилище многомерных данных ..................................................... 26 2.2.2 Глобалы ................................................................................................. 27 2.2.3 Объекты................................................................................................. 31 2.2.4 Определение классов ........................................................................... 32 2.2.5 SQL ........................................................................................................ 34 2.2.6 Связка объектной и реляционной модели данных ........................... 34 2.2.7 Расширения SQL .................................................................................. 35 2.2.8 Хранение объектов............................................................................... 36

2.3 Интеграция с другими объектными технологиями ....................................... 39 2.3.1 C++......................................................................................................... 39 2.3.2 JAVA...................................................................................................... 40

3 Технология Zen ............................................................................................................. 47 3.1 Архитектура Zen ............................................................................................... 47

3.1.1 Уровень модели данных ...................................................................... 48 3.1.2 Уровень управления............................................................................. 49 3.1.3 Уровень представления данных ......................................................... 50

3.2 Приложение Zen................................................................................................ 50 3.2.1 Жизненный цикл запроса .................................................................... 51 3.2.2 Классы Zen-приложения...................................................................... 54 3.2.3 Новое Zen-приложение........................................................................ 55

3.3 Работа с компонентами. Компоновка страницы............................................ 56 3.3.1 Концепция Zen-компонентов .............................................................. 56 3.3.2 Структура Zen-страницы ..................................................................... 57 3.3.3 Компоновка компонентов страницы .................................................. 59 3.3.4 Навигационные компоненты .............................................................. 64 3.3.5 Разработка пользовательских компонентов ...................................... 65

3.4 Шаблонные страницы ...................................................................................... 67 3.5 Каскадные таблицы стилей.............................................................................. 68

3.5.1 Порядок определения стилей.............................................................. 68

2

3.5.2 Определение стилей для нашего приложения .................................. 69 3.6 Привязка данных к элементам страницы ....................................................... 70

3.6.1 Модели данных .................................................................................... 70 3.6.2 Связывание формы с объектной моделью данных ........................... 74

3.7 Zen-таблицы ...................................................................................................... 77 3.8 Реализация поведения страницы..................................................................... 79

3.8.1 Zen-методы ........................................................................................... 79 3.8.2 Пример реализации Zen-методов ....................................................... 81 3.8.3 Передача параметров на страницы..................................................... 85

3.9 Безопасность в Zen ........................................................................................... 86 3.9.1 Авторизация.......................................................................................... 87 3.9.2 Регистрация........................................................................................... 89 3.9.3 Работа с авторизированным пользователем на страницах .............. 91

3.10 SVG-компоненты ............................................................................................ 92 3.10.1 Виды SVG-компонентов.................................................................... 92 3.10.2 Работа с SVG-компонентами ............................................................ 93

3.11 Работа с отчетами ........................................................................................... 99 3.11.1 Порядок построения отчетов в Zen ................................................ 100

4 Prototype 6.................................................................................................................... 105 4.1 Архитектура Prototype 6................................................................................. 105 4.2 Установка и настройка Prototype 6 ............................................................... 106 4.3 Блоки Prototype 6 для быстрой разработки приложений............................ 108

4.3.1 Главное меню ..................................................................................... 109 4.3.2 Справочники ....................................................................................... 111 4.3.3 Документ Prototype 6 ......................................................................... 115 4.3.4 Поиск в документе Prototype 6.......................................................... 119

4.4 Компоненты Prototype 6 ................................................................................. 122 4.5 Специальные возможности Prototype 6 ........................................................ 126

4.5.1 Обеспечение ссылочной целостности.............................................. 126 4.5.2 Протоколирование ............................................................................. 127

4.6 Разработка отчётов ......................................................................................... 128 4.6.1 Архитектура механизма формирования отчётов ............................ 128 4.6.2 Создание отчётов ............................................................................... 129

5 Организация совместной работы .............................................................................. 134 5.1 Контроль версий в Caché ............................................................................... 134

5.1.1 Настройка Caché для работы с SVN в Prototype 6 .......................... 135 5.2 Установка и настройка SVN.......................................................................... 136

5.2.1 Модель работы ................................................................................... 137 5.2.2 Типы репозиториев ............................................................................ 138 5.2.3 Доступ к репозиторию ....................................................................... 138 5.2.4 Установка сервера SVN..................................................................... 138 5.2.5 Клиент SVN ........................................................................................ 142

5.3 Работа в SVN................................................................................................... 145 5.4 Интеграция Caché и IBM Rational Rose ........................................................ 146

Библиографический список.......................................................................................... 148

3

ВВЕДЕНИЕ Современные тенденции в области построения и развития корпоратив-

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

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

Принципы разработки WEB-приложений и WEB-страниц в Intersystems Caché до недавнего времени были аналогичными. Однако в новой техноло-гии Zen удалось ускорить разработку за счёт реализации компонентного под-хода, аналогичного RAD1, и скрещивания её с очевидными преимуществами объектной СУБД. Кроме того, в Zen реализована архитектура аналогичная MDA2, которая позволяет вести разработку через моделирование архитекту-ры независимо от структуры хранения данных. Таким образом, технология Zen для разработки WEB-приложений вобрала в себя самые новые подходы, заимствованные из разных дисциплин технологий разработки программного обеспечения.

1 Rapid application development (быстрая разработка приложений) − концепция создания средств раз-

работки программных продуктов, уделяющая особое внимание быстроте и удобству программирования. 2 Model Driven Architecture − концепция модельно ориентированного подхода к разработке про-

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

4

В этом учебном пособии на простых примерах описывается процесс создания WEB-приложений на технологии Zen.

В главе 1 поверхностно описываются элементы технологии Zen и Caché, достаточные для создания простой WEB-страницы. Подробно описы-вается процесс создания WEB-страницы в инструментарии Caché.

В главе 2 описываются основные принципы устройства и функциони-рования СУБД Caché, которые дают неоспоримые преимущества при реше-нии проблем скорости обработки хранимых данных. Приводятся примеры интеграции с другими технологиями и инструментами объектного подхода к разработке автоматизированных систем.

В главе 3 подробно описывается архитектура технологии Zen и меха-низмы ее функционирования. На сквозном примере разбираются основные подходы к разработке Zen-приложений и основные компоненты, используе-мые при построении Zen-страниц. Отдельно рассмотрены темы безопасности и графические компоненты.

В главе 4 рассмотрена новая свободно распространяемая технология «Prototype 6», выполненная на базе технологии Zen. Prototype 6 за счёт мо-дернизации компонентов еще больше увеличивает скорость разработки WEB-приложений относительно технологии Zen. Кроме того, Prototype 6 восполняет недостающий функционал по обеспечению целостности данных на уровне бизнес логики, ведению журнала изменения данных пользователя-ми приложения и интеграции с системой версионного контроля.

В главе 5 описан процесс командной разработки WEB-приложений с использованием системы версионного контроля на примере SVN.

1 БЫСТРЫЙ СТАРТ В данной главе рассматриваются возможности технологии Zen при

разработке динамических web-страниц на простом примере. Более подробно данная технология представлена в следующей главе (см. главу 2).

Технология Zen позволяет создавать web-приложения, включающие в себя современные технологические конструкции, такие как HTML1, XML2, AJAX3 и JavaScript4, которые позволяют создавать более гибкий интерфейс

1 Oт англ. Hypertext Markup Language — «язык разметки гипертекста» 2 От англ. eXtensible Markup Language — расширяемый язык разметки, рекомендованный Консор-

циумом Всемирной паутины, фактически представляющий собой свод общих синтаксических правил 3 От англ. Asynchronous Javascript and XML (асинхронный JavaScript и XML) — это подход к по-

строению интерактивных пользовательских интерфейсов веб-приложений, заключающийся в «фоновом» обмене данными браузера с веб-сервером.

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

5

пользователя (web-страницы) в части взаимодействия с базой данных и об-новления данных на странице.

В этой главе будет рассмотрен пример создания простой web-страницы, на которой будут отображаться данные о человеке с возможно-стью их редактирования. Перед началом работы над примером выполним шаги по настройке области и базы данных (см. п. 1.1).

1.1 Создание области и базы данных Создадим в Caché область (namespace) и базу данных. Для этого запус-

тим портал управления системой и в разделе «Администрирование системы» выберем ссылку «Конфигурация», затем пройдём по ссылке «Локальные ба-зы данных». Для создания базы данных запустим мастер по ссылке «Создать новую базу данных», в поле «имя базы данных» введём «ZEN_DB» и укажем каталог, в котором она будет размещена (см. Рис. 1.1).

На следующих шагах мастера оставим все параметры по умолчанию. Далее создадим область (namespace). Для этого в том же разделе выбе-

рем «Создать новую область» и укажем имя области «ZEN_SPACE». Затем выберем базу данных, которую создали на предыдущем шаге (см. Рис. 1.2).

Рис. 1.1 – Мастер создания новой базы данных

6

Рис. 1.2 – Создание новой области

1.2 Создание проекта Когда база данных и область готовы создадим проект, в котором будем

размещать все классы, касающиеся нашего проекта. Для этого запустим Caché Studio и авторизуемся. Если во время установки Caché была выбрана минимальная конфигурация безопасности, то авторизация не потребуется, в противном случае введём имя пользователя(_system) и пароль(SYS)1.

При авторизации нужно проследить, чтобы была выбрана, созданная нами область на предыдущем шаге (см. Рис. 1.2). Если в момент авторизации область не была выбрана или была выбрана другая область, то её можно из-менить через меню «Файл → Изменить область» (см. Рис. 1.3).

При успешном завершении процедуры авторизации становятся актив-ными инструменты среды разработки. Ниже приведен внешний вид Caché Studio и пояснения к основным её инструментам (см. Рис. 1.4).

Для создания нового проекта, выберем пункт меню «Файл → Новый проект» и сохраним его «Файл → Сохранить (Ctrl+S)».

1 Пользователь _system создаётся в Caché при установке

7

Рис. 1.3 – Окно смены области

Рис. 1.4 – Внешний вид Caché Studio

Область проекта

Область редакти-рования

Область ин-формации

Область инспектора

Главное меню Панель отладки Стандартная панель Панель мастеров

8

1.3 Работа с классами в Caché В соответствии с описанием нашего примера (см. начало гл. 1) созда-

дим класс Personal , в котором будет храниться информация о человеке, и добавим его в проект. Класс в Caché Studio можно создать либо вручную, ис-пользуя конструкции UDL, либо автоматически, используя мастер классов.

Для создания класса посредствам мастера нужно выбрать пункт меню «Файл → Создать (Ctrl+N)», либо выбрать на панели инструментов следую-щую пиктограмму . Затем, в появившемся окне (см. Рис. 1.5) нужно вы-брать элемент проекта, в нашем случае это − «Класс Caché» и нажать кнопку «ОК». На экране появится первый шаг мастера классов (см. Рис. 1.6), где нужно указать общие сведения о создаваемом классе – имя пакета, имя клас-са и комментарии. Если имя пакета не определено в БД, тогда Caché создаст новый пакет автоматически. Укажем имя пакета – tutorial, имя класса – Per-sonal, комментарии – Информация о человеке и перейдём к следующему ша-гу мастера.

Рис. 1.5 – Окно создания нового элемента

На втором шаге (см. Рис. 1.7) укажем тип создаваемого класса. Типы

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

9

Рис. 1.6 – Мастер создания классов

Рис. 1.7 – Выбор типа класса в мастере классов

10

Остальные параметры мастера оставим без изменений и нажмём кноп-ку «Готово». По завершению создания класса сохраним его (Ctrl+S) и ском-пилируем (Ctrl+F7).

1.3.1 СОЗДАНИЕ СВОЙСТВ КЛАССА Для создания новых свойств класса можно воспользоваться мастером,

запустив его через пункт меню «Класс → Добавить → Новое свойство», либо пиктограммой на панели инструментов .

На первом шаге мастера свойств указываются общие сведения о созда-ваемом свойстве – имя свойства и его описание. Введём Surname в поле име-ни и «Фамилия» в поле описания (см. Рис. 1.8).

На следующем шаге мастера указывается тип свойства (см. Рис. 1.9), в нашем случае для свойства Surname − это %String.

В Caché поддерживается несколько типов свойств: – Константы. Простые типы данных (%String, %Integer, %Date и т.д.),

которые наследуют свое поведение от класса типов данных %Li-brary.DataType.

Рис. 1.8 – Мастер создания свойств класса

11

Рис. 1.9 – Выбор типа свойства

– Ссылки на объекты. Каждый класс объектов может рассматриваться как сложный тип данных. Caché автоматически осуществляет подкачку объ-ектов в память и доступ к элементам объектов осуществляется через точеч-ный синтаксис.

– Коллекции. Достоинством объектно-ориентированных БД является возможность хранить множество значений в одном свойстве. В Caché под-держиваются два типа коллекций – массив и список. Каждый элемент масси-ва обладает индексом, который уникально характеризует элемент в массиве, в то время как в списке элемент уникально определяется по номеру позиции в списке.

– Отношения. Отношения представляют собой двунаправленные зави-симости между хранимыми объектами. В Caché реализовано 2 типа отноше-ний – Parent-Child (зависимая связь, т.е. при удалении предка, автоматически удаляются наследники) и One-Many (независимая связь – удаление предка при существовании наследников приводит к ошибке).

На следующем шаге мастера свойств (см. Рис. 1.10) определяются ха-рактеристики свойства.

12

Рис. 1.10 – Характеристики свойства

Характеристика «Обязательно» определяет ограничение на то, что

значение свойства не может быть пустым. Характеристика «Индексируе-мое» – определяет создание индекса на основе этого свойства. Характери-стика «Уникальное» накладывает ограничение на значения свойства, которые должны быть уникальны. Характеристика «Вычисляемое» определяет свой-ство как вычисляемое (производное) и его значение не хранится перманентно в БД, а рассчитывается из значений других свойств. Имя поля для SQL явля-ется не обязательным и служит для определения имени свойства в запросе SQL (по умолчанию принимается имя свойства). Свойство Surname должно быть обязательным к заполнению, поэтому отметим эту характеристику в мастере (см. Рис. 1.10) и перейдём к следующему шагу.

На следующем шаге определяются параметры свойства (см. Рис. 1.11), с помощью которых можно контролировать допустимые значения. К приме-ру, MAXLEN или MINLEN задают соответственно максимально допустимую или минимально допустимую длину значения свойства. Зададим для свойст-ва Surname параметр MAXLEN равный 200.

13

Рис. 1.11 – Параметры свойства

На следующем шаге мастера свойств можно переопределить методы работы со значением свойства, соответственно метод Get и метод Set. В на-шем случае нет необходимости переопределять методы для свойства Surname, воспользуемся стандартными методами, предоставляемыми Caché. Завершаем работу мастера свойств нажатием кнопки «Готово».

Далее по аналогии создадим свойства класса Personal, приведенные в таблице (см. Табл. 1.1). Табл. 1.1 – Свойства класса Personal Наименование Комментарий Тип Параметры

Name Имя %String MAXLEN = 200 DOB Дата рождения %Date FORMAT = 3 Sex Пол человека %String Education Образование %String FPassport Имеется заграничный пас-

порт? %Boolean

FavoriteColor Любимый цвет %String AdditionalInfo Дополнительная информа-

ция %String MAXLEN = 500

14

1.3.2 НАСЛЕДОВАНИЕ Особое место в Caché отводится для отношения наследования, которое

может быть множественным. Так, при создании класса Personal, он уже был унаследован от класса %Persistent, что позволило ему приобрести возмож-ность сохранять свои объекты в БД и загружать их оттуда.

В технологии Zen для того чтобы объекты класса Personal можно было связать с элементами управления на странице, нужно унаследовать этот класс ещё и от %ZEN.DataModel.Adaptor. Для этого изменим список насле-дуемых классов у Personal. Это можно сделать непосредственно в коде, как показано ниже. Class tutorial.Personal Extends (%Persistent, %ZEN.DataModel.Adaptor)

Или посредствам инспектора, в котором к списку параметра Super до-

бавим класс %ZEN.DataModel.Adaptor (см. Рис. 1.12).

Рис. 1.12 – Установка наследников для класса Personal

В результате выполнения действий по добавлению всех свойств для класса Personal в соответствии с таблицей (см. Табл. 1.1) должен получиться следующий код. /// Человек Class tutorial.Personal Extends (%Persistent, %ZEN.DataModel.Adaptor) { /// Фамилия Property Surname As %String(MAXLEN = 200) [ Required ]; /// Имя Property Name As %String(MAXLEN = 200); /// Дата рождения Property DOB As %Date(FORMAT = 3); /// Пол человека

15

Property Sex As %String; /// Образование Property Education As %String; /// Имеется заграничный паспорт? Property FPassport As %Boolean; /// Любимый цвет Property FavoriteColor As %String; /// Дополнительная информация Property AdditionalInfo As %String(MAXLEN = 500); }

1.4 Создание Zen-страницы В данном разделе рассматривается последовательность действий по по-

строению простого Zen-приложения, состоящего из одной страницы, на ко-торой находится таблица людей с возможностью фильтрации по определён-ным полям и возможностью редактирования каждой записи таблицы (см. Рис. 1.13). В приложении при редактировании записи таблицы (данных о че-ловеке), страница полностью не перегружается, а изменяются только редак-тируемые элементы.

Рис. 1.13 – Страница редактирования объектов класса Personal

16

Создадим новую страницу Zen, для чего запустим соответствующий мастер(Ctrl+N) (см. Рис. 1.14, Рис. 1.15).

В поле «Имя пакета» введём имя пакета, в котором будут размещаться классы Zen-страниц, в нашем случае пакет называется zui.tutorial. В следую-щее поле введём имя класса DisplayPersonal, а имя приложения оставим пус-тым. Подробно о приложениях рассказывается в разделе 3.2.

Рис. 1.14 – Окно создания нового элемента

Рис. 1.15 – Мастер создания Zen-страницы

17

В поле «Имя страницы» введём «Информация о персонах» и в поле «Описание» введём описание этой страницы (см. Рис. 1.15). На следующем шаге мастера оставим все параметры по умолчанию и завершим его работу. Мастер сгенерирует код класса, который уже готов к запуску в браузере. Скомпилируем этот класс и откроем его в браузере посредствам меню «Вид → Web-страница». Браузер должен отобразить абсолютно пустую страницу.

1.4.1 СОЗДАНИЕ ТАБЛИЦЫ Теперь добавим на эту страницу таблицу персон. Для этого в блок

XData внутри тега <page> поместим тег <tablePane> и зададим параметр tableName="tutorial.Personal". Таким образом мы привязываем класс Personal к таблице. Ниже представлен код страницы, на которой помещён элемент управления <tablePane>. /// Список людей с возможностью их поиска и редактирования Class zui.tutorial.DisplayPersonal Extends %ZEN.Component.page { /// Имя приложения, которому принадлежит эта страница. Parameter APPLICATION; /// Отображаемое имя для нового приложения. Parameter PAGENAME = "Человек"; /// Домен, используемый для локализации. Parameter DOMAIN; /// Этот блок Style содержит определение CSS стиля страницы. XData Style { <style type="text/css"> </style> } /// Этот XML блок описывает содержимое этой страницы. XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen" title=""> <tablePane tableName="tutorial.Personal"> </tablePane> </page> } }

После компиляции класса и отображения его в браузере можно увидеть

пустую таблицу с одним столбцом (ID). Теперь добавим столбцы, которые должны отображаться в таблице.

Внутри тега <tablePane> для каждого столбца создадим тег <column> и за-дадим параметры colName и header (см. код ниже). Значение параметра col-

18

Name должно соответствовать имени одного из свойств в классе Personal, а значение параметра header будет отображаться в шапке таблицы в названии столбца. <tablePane tableName="tutorial.Personal"> <column header="Фамилия" colName="Surname"/> <column header="Имя" colName="Name"/> <column header="Дата рождения" colName="DOB"/> </tablePane>

Для реализации возможности фильтрации записей в таблице, в любом

из полей в элементе <column> специальным параметром filterType можно ус-танавливать фильтры разного типа.

В нашем примере (см. код ниже) используется три типа фильтрации: 1) по набранному тексту (filterType="text"); 2) по диапазону дат (filterType="date"); 3) по значению элемента списка (filterOp="BETWEEN"). Кроме того для правильной работы фильтров "date" и "enum" дополнительно задаются параметры filterOp и filterEnum (только для списка). <column header="Фамилия" colName="Surname" filterType="text"/> <column header="Имя" colName="Name"/> <column header="Дата рождения" colName="DOB" filterType="date" fil-terOp="BETWEEN"/> <column header="Пол" colName="Sex" width="9%" filterType="enum" filterEnum="Мужской, Женский" filterOp="="/> <column header="Любимый цвет" colName="FavoriteColor"/> <column header="Образование" colName="Education" width="15%" filter-Type="enum" filterEnum="нет, среднее,специальное, незаконченное в/о, высшее" filterOp="="/>

Иногда требуется отображать данные в таблице отличные от данных,

хранящихся в базе. Так, например, в классе Personal имеется свойство FPassport с типом данных %Boolean, данные которого в базе имеют значе-ния 0 или 1. Для корректного представления данных в самой таблице в эле-менте <column> используется параметр colExpression (см. код ниже). <column header="Загран.паспорт" colName="FPassport" colExpression="CASE WHEN FPassport=1 THEN 'Имеется' ELSE 'Нет' END"/>

1.4.2 СОЗДАНИЕ БЛОКА РЕДАКТИРОВАНИЯ Для реализации возможности редактирования данных в таблице ис-

пользуется блок XData editPane, который создаётся по аналогии с предыду-щим блоком и размещается сразу за этим блоком (см. код ниже). XData editPane [ XMLNamespace = "http://www.intersystems.com/zen" ] { <pane xmlns="http://www.intersystems.com/zen" title=""> </pane> }

19

Принцип работы с объектами базы данных и элементами страницы реализуется через элемент управления <dataController>, который подключа-ет объекты (через модель данных) к элементам управления на странице. В нашем примере через <dataController> подключается класс Personal. <dataController id="controller" modelClass="tutorial.Personal" />

Визуальные элементы управления могут размещаться в специальных

контейнерах, организующих размещение групп компонентов на странице. В нашем примере для этого создаётся форма <form>, в которой и размещаются элементы управления (см. код ниже). Более подробно работа с визуальными элементами управления рассматривается в разделе 3.3. <form id="MyForm" align="center" labelPosition="top" control-lerId="controller"> <!—Этот элемент позволяет разместить горизонтально текстовые боксы для фамилии имени и даты рождения --> <hgroup> <!--К текстовому элементу управления привязываем свойство "Surname" --><text label="Фамилия:" id="Surname" name="Surname" size="35" re-quired="true" dataBinding="Surname"/> <!--Делаем отступ--> <spacer width="20"/> <text label="Имя:" id="Name" name="Name" size="20" dataBinding="Name"/><spacer width="20"/> <dateText label="Дата рождения:" id="DOB" name="DOB" dataBind-ing="DOB"/> </hgroup> <spacer height="10"/> <hgroup labelPosition="left"> <radioSet label="Пол:" id="Sex" name="Sex" displayList="Мужской,Женский" valueList="Мужской,Женский" dataBinding="Sex"/> <spacer width="40"/> <checkbox id="FPassport" name="FPassport" label="Наличие загран.паспорта:" value="да,нет" dataBinding="FPassport"/> </hgroup> <spacer height="10"/> <hgroup> <combobox label="Образование:" id="Education"

20

name="Education" displayList="нет,среднее,специальное,незаконченное в/о,высшее" valueList="нет,среднее,специальное,незаконченное в/о,высшее" dataBinding="Education"/> <spacer width="40"/> <colorPicker label="Любимый цвет:" id="FavoriteColor" name="FavoriteColor" colorList="white,red,green,blue,black,yellow,orange,purple" dataBinding="FavoriteColor"/> <spacer height="10"/> </hgroup> <textarea label="Доп.информация" id="AdditionalInfo" name="AdditionalInfo" cols="50" rows="5" dataBinding="AdditionalInfo"/> </form>

1.4.3 ВЫЗОВ КЛИЕНТСКИХ МЕТОДОВ НА СТРАНИЦЕ Привязать элементы управления страницы к модели данных не доста-

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

В нашем примере на форме используются кнопки “Создать”, “Сохра-нить”, “Удалить”, вызывающие соответствующие методы страницы. Выбор строки в таблице также приводит к вызову соответствующего метода. Все методы в примере являются методами экземпляра, запускающимися на сто-роне клиента. Такие методы пишутся на JavaScript.

Ниже представлен код описания кнопок на странице и вызова соответ-ствующих методов. <hgroup id="btnGroup" cellAlign="left"> <button id="btnNew" caption="Создать" onclick="zenPage.NewItem();"/> <spacer width="10"/> <button id="btnSave" caption="Сохранить" onclick="zenPage. SaveItem();"/> <spacer width="10"/> <button id="btnDelete" caption="Удалить" onclick="zenPage. DeleteItem();"/> <spacer width="10"/> </hgroup>

21

Напишем метод, вызываемый по нажатию на кнопку «Создать». Этот

метод должен создавать новый объект класса Person и связывать его с ком-понентами страницы. Поскольку за связь данных с компонентами страницы отвечает объект класса %ZEN.Auxiliary.dataController (на странице он пред-ставлен тегом <dataController>), то создание нового объекта Person выполня-ется через этот класс.

Обратиться к любому компоненту страницы (в том числе и не визуаль-ному, в нашем случае к <dataController>), можно через метод самой страни-цы zenPage.getComponentById(). Очевидно, что для использования этого ме-тода у компонентов страницы должно быть определено свойство «id» (у на-шего контроллера оно принимает значение "controller"). Код метода создания нового объекта представлен ниже. Method NewItem() [ Language = javascript ] { var controller = zenPage.getComponentById('controller'); controller.createNewObject(); }

Руководствуясь тем же принципом, напишем методы zenPage.SaveItem() и zenPage.DeleteItem(): Method SaveItem() [ Language = javascript ] { //Получаем ссылку на объект контроллера var controller = zenPage.getComponentById('controller'); //Сохраняем данные из формы через модель данных в базу controller.save(); //Получаем ссылку на таблицу var table = zenPage.getComponentById('table'); //Обновляем данные в таблице table.executeQuery(); } Method DeleteItem() [ Language = javascript ] { var controller = zenPage.getComponentById('controller'); //Получаем ссылку на объект модели данных var id = controller.getModelId(); //Проверяем выбрана ли запись в таблице (есть ли ссылка на объ-ект) if (id == '') { alert('Для начала выберите запись'); } else if (confirm('Вы действительно хотите удалить эту запись?')) {

22

//Удаляем объект controller.deleteId(id); var table = zenPage.getComponentById('table'); table.executeQuery(); } }

Теперь реализуем функцию заполнения полей формы данными из вы-

бранной строки таблицы. Для этого в элементе <tablePane> установим пара-метр onselectrow равным zenPage.RowSelected(zenThis) и опишем функцию RowSelected. ///Обновление данных на форме при выборе строки таблицы Method RowSelected(table) [ Language = javascript ] { var id = table.getValue(); zenPage.ShowObject(id); } ///Обновление данных на форме по идентификатору объекта модели данных Method ShowObject(id) [ Language = javascript ] { var controller = zenPage.getComponentById('controller'); controller.setModelId(id); }

2 ВВЕДЕНИЕ В СУБД CACHÉ Caché является многослойной технологией (см. Рис. 2.1), включающей

такие компоненты как:

– многомерная транзакционная СУБД, с возможностью создания рас-пределенных баз данных;

– унифицированная архитектура данных, объединяющая преимущества объектного подхода и языка запросов SQL;

– набор инструментария для быстрой разработки баз данных и web-приложений;

– встроенная поддержка языка XML и web-сервисов;

– встроенная поддержка Java, EJB, JDBC, ActiveX, .NET, C++, ODBC, XML, SOAP, Perl, Python, и т.д.

23

Рис. 2.1 – Архитектура Caché

Связующим звеном всех слоев и технологий Caché является ее уни-кальная архитектура, которая обеспечивает управление данными, включая их хранение, управление конкурентным (многопользовательским) доступом к данным, управление транзакциями и процессами.

По сути, Caché есть система управления многомерными базами данных (СУБД) с мощным дополнительным набором инструментов, используя кото-рые становится возможным быстрое и эффективное создание объектно-реляционных систем, баз данных и приложений различной архитектуры.

Производитель использует для Caché термин «постреляционная СУБД». Это означает, с одной стороны, что Caché является полнофункцио-нальной реляционной СУБД, при этом, все данные внутри БД доступны в ви-де реляционных таблиц и, соответственно, данные из них могут быть извле-чены и модифицированы посредством стандартного SQL. С другой стороны, слово «пост» в термине «постреляционная» означает, что помимо реляцион-ной парадигмы, Caché предлагает ряд дополнительных базовых возможно-стей, которые выводят ее за рамки просто реляционной СУБД, а именно:

– Возможность моделировать схемы данных в объектно-ориентированной парадигме, нивелируя различия в среде разработки клиент-ского приложения (как правило, это объектно-ориентированная среда) и дан-ных хранящихся в БД (как правило, реляционных). Cachè автоматически ве-дет для каждого объекта в системе кроме объектного представления еще и

24

реляционное.

– Более простая, основанная на объектах, модель управления конку-рентным доступом процессов к данным.

– Типы данных, определяемые пользователем (разработчиком), в том числе и сложные.

– Преимущества объектного подхода, такие, как наследование и поли-морфизм.

– Объектные расширения SQL для работы с объектными идентификато-рами и связями (relationships).

– Возможность смешивания объектного и встроенного SQL-кода внутри одного приложения или модуля, там, где это наиболее удобно. В то время как большинство СУБД с двумя типами доступа к данным (объектным и реляци-онным) реализуют один вид доступа к данным через второй, Caché использу-ет независимые реализации способов доступа путем низкоуровневого обра-щения к данным.

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

Особое внимание в Caché уделяется встроенным возможностям для профессионального создания клиент-серверных и web-приложений. Разра-ботчики на платформе Caché получают выбор средств разработки, выбор языка программирования, выбор методов доступа к данным, транзакционную обработку данных, высокую производительность, масштабируемость прило-жений и данных, и, безусловно, надежность. Все эти возможности связаны воедино в единую среду разработки.

2.1 Структура и принципы работы Caché Структурно Caché состоит из трех тесно взаимосвязанных крупных

частей: – сервер баз данных (database server); Языки и интерфейсы

– сервер приложений (application server); Сервер приложений

– языки и интерфейсы. Более детально структура Caché пред-

ставлена ниже (см. Рис. 2.2).

Сервер баз данных

25

Рис. 2.2 − Структура Caché

2.2 Сервер баз данных Сервер баз данных – высокопроизводительная транзакционная СУБД,

управляющая многомерными разреженными данными. Сервер баз данных обладает всеми атрибутами современной промышленной СУБД, а именно, поддержкой конкурентного доступа пользователей, обработкой транзакций, журналированием, online-резервным копированием, инструментами админи-стрирования, и т.п.

2.2.1 ХРАНИЛИЩЕ МНОГОМЕРНЫХ ДАННЫХ Все данные внутри Caché хранятся в виде разреженных многомерных

массивов. В отличие от многомерных массивов, используемых OLAP-инструментами, Caché поддерживает операции вставки (insert), обновления (update), блокировки (locking) непосредственно для многомерных структур данных, причем размер этих многомерных структур не ограничен свободной оперативной памятью сервера. Для оптимизации времени доступа к много-мерным данным используется специальный алгоритм кэширования.

26

С учетом того, что Caché хранит данные переменной длины в виде раз-реженных массивов, часто размер базы данных составляет половину от раз-мера аналогичных данных в реляционных СУБД. К тому же большая ком-пактность данных на жестком диске влечет меньшее количество операций ввода/вывода, а это, в свою очередь, к увеличению общей производительно-сти системы.

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

2.2.2 ГЛОБАЛЫ Глобалы – долговременно хранимые многомерные разреженные масси-

вы данных. Наиболее удобным визуальным представлением глобала является граф типа «дерево». Данные в глобале могут храниться в двух местах – в уз-лах графа (nodes) и в ветвях графа (keys). Максимальный размер данных, хранящихся в ключе (ветви графа) не может превосходить 255 байт. Как пра-вило, в ключах хранят идентификаторы записей, т.е. уникальные характери-стики данных для каждого уровня глубины графа-дерева.

Пусть, для примера, мы хотим сохранить в виде глобала некоторые ха-рактеристики нескольких автомобилей (гос. номер, цвет, модель, владелец). Для этого используем глобал с именем ^Auto. Среди предложенных атрибу-тов необходимо выделить те, которые можно хранить в ключе – в нашем случае удобно взять характеристику автомобиля «государственный номер», т.к. он является уникальным для страны. Остальные атрибуты можно размес-тить в узлах. ^Auto(“a416сх”,”Характеристики”,”Цвет”) = “Серый ” ^Auto(“a416сх”,”Характеристики”,”Модель”) = “Honda Avancier” ^Auto(“с035ом”,”Владелец”,”Фамилия”) = “Иванов” ^Auto(“с035ом”,”Владелец”,”Имя”) = “Александр” ^Auto(“с035ом”,”Владелец”,”Отчество”) = “Владимирович” ^Auto(“с035ом”,”Характеристики”,”Цвет”) = “Красный”

Отметим, что в качестве ключей второго и третьего уровней многомер-

ного массива мы выбрали именованные значения «Характеристики», «Цвет», «Модель», «Фамилия», «Имя», «Отчество».

Дерево, отображающее данный глобал будет выглядеть следую-щим образом (см. Рис. 2.3).

27

Рис. 2.3 – Дерево глобала ^Auto

На рисунке ключи (ветви графа) поименованы значениями в фигурных скобках, значения в узлах помечены жирным шрифтом, а все узлы пронуме-рованы числами от 1 до 12 в порядке левого обхода дерева.

Из приведенного выше рисунка видно, что для каждого из двух авто-мобилей хранятся разное количество подузлов, т.е. записи, хранимые в гло-балах, могут быть неоднородными. Далее, узлы 1, 2, 3, 7, 9 и 10 реально дан-ных не хранят, соответственно места хранения для них системой не отводит-ся. Таким образом, хранение глобала будет еще более компактным на диске. Остальные атрибуты преимущественно хранятся в ключах (ветвях графа), т.к. они уникально адресуют каждый атрибут автомобиля. Узлы-листья 4, 5, 6, 8, 11 и 12 хранят строковые данные переменной длины.

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

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

Данные в глобале хранятся в одном или более узлах, идентифицируе-мых уникальными для каждого узла ключами. Каждый узел может содержать до 3 Mb символов или текста. В узлах, как правило, содержится строка или

{ a416сх }^Auto

1{ с035ом}

3

6

5

4

7

8

92

{Характеристики}

Александр Владимирович

{Фамилия}

{Владелец} {Характеристики} 10

Красный

12

Серый

{Цвет} {Модель}

11

Honda Avancier

{Отчество} {Цвет} {Имя}

Иванов

28

числовое значение. Для размещения в одном узле нескольких логических по-лей их принято разделять некоторым символом-разделителем. Например: ^Data(10) = “Иванов^Александр^Владимирович^Красноярск”

Другой вариант размещения в одном узле нескольких полей – исполь-

зование структуры списка построенного с помощью встроенной функции $LISTBUILD. Такой список содержит закодированные значения полей и для него не требуется символа разделителя. Например: ^Data(10) = $LISTBUILD(“Иванов”,”Александр”,1982,”Красноярск”)

В случае если данные хранятся только в значении ключа, в физическом

узле глобала не хранится ничего. В случае если глобал используется для хра-нения битовых индексов, то в узлах глобала хранятся строки логических бит (0 или 1).

Объекты могут хранить бинарные потоки (streams), а записи в SQL-таблицах могут хранить блоки данных (BLOBs) как последовательность из узлов размеров в 32Kb внутри глобала. Каждый узел в глобале идентифици-руется нулем или другим значением ключей (subscripts). Для корневого узла глобала ключ не задается. Например: ^Data ^Data(1) ^Data(1,2,3) ^Data(“Клиенты”,”453-543”,56,”Баланс”)

Узлы хранятся упорядоченно в порядке сортирующей последователь-

ности для ключей. Например, клиенты Петров, Сидоров, Иванов будут хра-ниться в алфавитном порядке их фамилий, если в качестве ключа избрать по-ле фамилии: ^Clients(“Иванов” ) ^Clients(“Петров”) ^Clients(“Сидоров”)

И в другом порядке, если в качестве ключа избрать, например, табель-

ный номер: ^Clients(89)=“Сидоров” ^Clients(102)=“Петров” ^Clients(103)=“Иванов”

29

Поскольку любая компьютерная система с целью оптимизации обмена производит обмен между памятью и диском в виде блоков, то атомарным элементом, хранящим данным на диске, является блок. Физически все глоба-лы в БД Caché хранятся в виде B-деревьев1.

Глобалы обладают следующими характеристиками: – Проста в использовании. При программировании на Caché Object

Script (COS) – глобалы используются как еще один тип переменных. – Многомерность. Возможность точно адресовать любой узел глобала

посредством указания необходимого количества ключей. Например, в при-мере выше ^Auto(“с035ом”,”Характеристики”,”Цвет”) тремя ключа-ми адресован узел дерева третьего уровня с номером 8, в котором находится цвет автомобиля.

– Разреженность. Ключи глобала эффективно сжимаются системой, которая не хранит повторяющихся подстрок в составе ключа.

– Эффективность. Операции вставки, изменения, удаления значений в/из глобала, а также операции обхода дерева глобала оптимизированы для многопользовательской работы с подобными структурами данных. Также есть специальные операции для поточной вставки данных в глобал.

– Надежность. Сервер баз данных Caché предлагает набор механизмов для обеспечения надежного хранения данных в глобалах, включая журнали-рование как на логическом, так и на физическом уровнях организации дан-ных в глобалах. Глобалы, содержащие данные, естественно, включаются в резервную копию БД при выполнении такой операции.

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

– Многопользовательский доступ. Глобалы поддерживают конкурент-ный доступ от множества процессов Caché. Извлечение или присвоение зна-чения узлу глобала (элементу многомерного массива) всегда есть атомарная операция, гарантирующая ее непротиворечивость.

Глобалы используются внутри приложений Caché следующим образом: – Как механизм хранения нижнего уровня для объектов и реляционных

таблиц.

1 http://ru.wikipedia.org/wiki/B-дерево

30

– Как механизм для хранения разнообразных индексов данных, включая битовые индексы для объектов и SQL-данных.

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

– Для разработки специфичной, более оптимальной, для приложения стратегии хранения данных. Используя глобалы можно определить необхо-димую древовидную структуру данных и сделать ее доступной для процессов Caché и внешних приложений через объектные методы.

– Для всего разнообразия системных и конфигурационных данных, ре-позитория классов и исполняемого кода.

2.2.3 ОБЪЕКТЫ Объектная модель данных Caché разработана на основе стандарта

ODMG (Object Database Management Group − Группа по объектному управ-лению базами данных). Caché поддерживает полную гамму концепций объ-ектного программирования, включая инкапсуляцию, встраиваемые объекты, множественное наследование, полиморфизм и коллекции.

Объектная модель Caché включает в себя следующее: – Классы. Можно определять классы, отображающие состояние (дан-

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

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

– Связи (relationships).В описании классов можно определить, каким образом объекты двух разных классов связаны друг с другом. Система авто-матически обеспечивает такие классы методами для навигации по связям и поддержки ссылочной целостности объектов в БД.

– Методы. Классы определяют поведение объектов посредством мето-дов (исполняемого кода). Методы объектов исполняются в контексте процес-са и могут быть описаны на нескольких поддерживаемых языках (Caché Ob-jectScript, Caché Basic, Embedded SQL), либо код методов может быть сгене-рирован специальными методами-генераторами, которые автоматически соз-дают настроенные посредством параметров, методы, что дает большую гиб-

31

кость в описании поведения объектов. – Хранимые объекты. Хранимые объекты способны автоматически со-

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

– Наследование. Путем наследования одного класса от другого сущест-вующего класса Вы можете повторно использовать ранее написанный и от-лаженный код.

– Полиморфизм. Caché поддерживает полиморфизм объектов, это озна-чает, что приложение может использовать методы определенные в супер-классе, а система будет автоматически вызывать корректные методы, опре-деленные индивидуально для каждого класса методов.

– Подкачка (Swizzling). Caché автоматически подкачивает с диска в опе-ративную память любые объекты, связанные с данным, в момент, когда тре-буется использовать свойства связанного объекта.

Объектная функциональность Caché является центральной, базовой технологией для разработки приложений на этой системе.

2.2.4 ОПРЕДЕЛЕНИЕ КЛАССОВ Самый простой и наиболее распространенный путь для определения

класса в Caché это использования среды разработки Caché Studio. Студия по-зволяет описать класс как в текстовом редакторе с подсветкой синтаксиса, так и, используя разнообразные мастера (wizards). Оба способа описания класса взаимно эквивалентны и синхронны друг с другом.

На примере ниже приведено описание достаточно простого хранимого класса, Component, так как он отображается в среде Studio: Class MyApp.Component Extends %Persistent [ClassType = persistent] {

Property TheName As %String;

Property TheValue As %Integer;

}

Этот класс определен как хранимый, точнее класс хранимых объектов,

то есть объекты могут долговременно храниться системой в БД. За поддерж-ку долговременного хранения объектов в БД отвечает библиотечный класс %Persistent, который обеспечивает прикладной класс всеми необходимыми для работы с хранимыми объектами методами через механизм наследования.

32

В примере выше класс принадлежит к пакету “MyApp”. Пакет – это механизм группировки логически связанных классов в Cachè, который суще-ственно упрощает разработку крупных приложений с десятками и сотнями классов. В классе также определены два свойства: строковое TheName, и це-лочисленное TheValue.

Посредством одного из встроенных в Caché языков, можно манипули-ровать хранимым объектом класса Component. Так, например, с помощью Caché Basic можно включить в один из методов метод следующий код: ‘ Создать новый экземпляр

component = New Component()

component.TheName = “Библиотека”

component.TheValue = 22

‘ Сохранить объект в БД

component.%Save()

Здесь новый экземпляр класса Component сохраняется в БД с уникаль-

ным объектным идентификатором (object id). После сохранения, объект мож-но извлечь из БД, открыв его методом OpenId() по заданному идентификато-ру: ‘ Открыть экземпляр объекта и удвоить значение свойства TheValue

component = OpenId Component(id)

component.TheValue = component.TheValue * 2

component.%Save()

Аналогичные операции с объектами можно выполнять, используя Java,

C++, или любую другую технологию имеющую связку с Caché на уровне объектов. Компилятор классов может сгенерировать и синхронизировать лю-бой дополнительный код, который требуется для доступа к объекту извне Caché .

Например, если используется Java, компилятор автоматически генери-рует и поддерживает класса Java-проекции, которые обеспечивают удален-ный доступ к хранимым классам Caché.

В Java программе код будет выглядеть так: // Извлечь экземпляр класса Comonent из БД component = (MyApp.Component)MyApp.Component._open(database, new Id(id)); // Распечатать некоторые свойства объекта System.out.println(“Name: “ + component.getTheName()); System.out.println(“Value: “ + component.getTheValue());

33

2.2.5 SQL Caché SQL обеспечивает полнофункциональную поддержку стандарта

SQL-92, и полностью интегрирован с объектным механизмом Cachè. Cachè в дополнение к стандарту SQL-92 поддерживает следующие возможности:

– Потоки (известные в SQL под аббревиатурой BLOB - Binary Large Ob-jects).

– Хранимые процедуры (реализованные в виде объектных методов). – Типы данных, определяемых разработчиком. – Битовые индексы. Битовые индексы, обычно используемые OLAP-

системами и хранилищами данных, в Caché SQL дают возможность выпол-нять поисковые запросы со сложными комбинациями условий с очень высо-кой скоростью. Это, в свою очередь, дает транзакционным приложениям возможность запрашивать данные в стиле OLAP-систем, при этом сохраняет способность к высокоскоростной вставке или обновлению данных.

2.2.6 СВЯЗКА ОБЪЕКТНОЙ И РЕЛЯЦИОННОЙ МОДЕЛИ ДАННЫХ Все компоненты внутри репозитория Caché определены как классы.

Компилятор классов автоматически проецирует хранимые классы в их реля-ционные представления в виде таблиц. Основные параметры соответствия объектной и реляционной модели Caché приведены ниже (см. Табл. 2.1). Табл. 2.1 − Параметры объектов и их реляционная проекция

Параметры объектов Параметры реляционной модели Пакет Схема Класс Таблица Экземпляр Строка Идентификатор объекта (OID) ID-столбец в виде первичного ключа Свойство Столбец Ссылка на хранимый объект Внешний ключ Встраиваемый объект Несколько атрибутов Коллекция-список Столбец с полем-списком Коллекция-массив ПодтаблицаПоток данных BLOB Индекс Индекс Запрос (Query) Хранимая процедура или представление Метод класса Хранимая процедура

34

Рассмотрим простой пример, демонстрирующий объектно-реляционную проекцию в действии.

Пусть мы имеем определение класса Person, содержащего два свойства – имя и адрес проживания: Class MyApp.Person Extends %Persistent [ClassType = persistent] {

Property Name As %String (MAXLEN=100); Property Addr As Address; }

Класс Person получает поведения для хранимых классов от родитель-

ского класса %Persistent, который является базовым для Caché. Свойство Name определено как строка длиной не более 100 символов.

Для свойства Addr указан сложный тип, определяемый встроенным (не хра-нимым) классом Address: Class MyApp.Address Extends %SerialObject [ClassType = serial] {

Property City As %String; Property PostIndex As %String (MAXLEN=6); }

Класс Address унаследован от системного %SerialObject. Этот класс

умеет сериализоваться (преобразовывать свое представление в строку) и встраиваться в другой класс-контейнер (в нашем случае это хранимый класс Person).

При просмотре данных Person через запрос SQL (SELECT * FROM Per-son) видится следующая структура таблицы: ID Name Addr_City Addr_PostIndex 1 Иванов Андрей Красноярск 600133 2 Ольшанская Наталья Москва 124000

Отметим, что объектный идентификатор виден в таблице как отдельная

колонка ID, а свойства встроенного класса Address проецируются как отдель-ные атрибуты Addr_City и Addr_PostIndex.

2.2.7 РАСШИРЕНИЯ SQL Для облегчения применения SQL в приложениях использующих объ-

ектный подход, Caché включает несколько объектных расширений для SQL.

35

Наиболее интересна возможность использовать в запросе переход по объект-ной ссылке (object reference) посредством операции «→».

Например, для класса Vendor, свойства которого ссылаются на классы Contact и Region можно использовать конструкцию следующего вида. SELECT ID,Name,ContactInfo→Name FROM Vendor WHERE Vendor→Region→Name = ‘Россия’

Естественно, не запрещается использовать в запросах классический

SQL JOIN, однако конструкция запроса будет более громоздкой и менее по-нятной.

2.2.8 ХРАНЕНИЕ ОБЪЕКТОВ Хранение объектов в Caché организовано на базе глобалов. Объект, ра-

бота с которым во время процесса приложения происходит преимущественно в оперативной памяти, при операциях сохранения obj.%Save() сохраняется в структуры долговременного хранения в БД – в глобалы. Равно как при опе-рациях инстанцирования объекта obj.%OpenId(), значения его свойств подка-чиваются системой из долговременной памяти (глобала) в оперативную.

Для оптимизации работы SQL-запросов к реляционным проекциям массивов объектов, в языке описания классов (Class Definition Language) объ-ектов предусмотрена декларация индексов. Соответствующие индексы под-держиваются Caché автоматически как при работе непосредственно с объек-тами, так и с их SQL-проекциями. Различные индексы также хранятся в гло-балах.

Рассмотрим устройство класса, описывающего сущность автомобиль, реквизиты которого описаны в разделе выше (гос. номер, модель, цвет, вла-делец).

Создадим класс User.Auto. /// Автомобили

Class User.Auto Extends %Persistent { //Индекс по гос.номерам авто (уникальный, идентификатор)

Index GosNumberIndex On GosNumber [IdKey];

//Индекс по моделям авто

Index ModelIndex On Model;

//Индекс по владельцам авто

Index OwnerIndex On FIO;

//Госномер в виде РРбЦЦЦбб (Р-цифра региона, б-буква, Ц- цифра номера)

Property GosNumber As %String(MAXLEN = 8);

//Цвет авто

36

Property Color As %String;

//Модель авто

Property Model As %String;

//Фамилия владельца

Property FIO As %String;

}

После компиляции класса, Caché автоматически конструирует страте-

гию хранения свойств класса и индексов в глобалах. Для просмотра страте-гии хранения класса можно воспользоваться меню «Вид → Просмотр спосо-ба хранения» в Studio.

Рис. 2.4 − Стратегия хранения

Стратегия хранения отображается в виде XML-документа, как пред-ставлено в следующем коде. <Storage name="Default">

<Data name="AutoDefaultData">

<Value name="1">

<Value>%%CLASSNAME</Value> </Value>

37

<Value name="2">

<Value>GosNumber</Value> </Value> <Value name="3">

<Value>Color</Value> </Value> <Value name="4">

<Value>Model</Value> </Value> <Value name="5">

<Value>FIO</Value> </Value> </Data> <DataLocation>^User.AutoD</DataLocation> <DefaultData>AutoDefaultData</DefaultData> <ExtentSize>100000</ExtentSize> <IdLocation>^User.AutoD</IdLocation> <IndexLocation>^User.AutoI</IndexLocation> <StreamLocation>^User.AutoS</StreamLocation> <Type>%Library.CacheStorage</Type> </Storage> }

При этом глобал, в котором будут храниться объекты (class extent) ука-

зан в элементе <DataLocation/> и равен, в нашем случае, значению ^User.AutoD. Индексы будут храниться в глобале, указанном в элементе <In-dexLocation/>, в нашем случае ^User.AutoI.

Структуру самого глобала ^User.AutoD можно описать одноуровневым деревом. ^User.AutoD({ID})=$LISTBUILD(%%CLASSNAME,GosNumer,Color,Model,FIO)

Здесь {ID} – автоинкрементное поле идентификатора объектов (авто-

мобилей), %%CLASSNAME – служебное поле, содержащее имя класса для каждого экземпляра, остальные значения – поля класса в порядке их пере-числения в описании класса. Стратегия хранения по умолчанию хранит упа-кованный список ($LISTBUILD) значений свойств в одном узле глобала для каждого экземпляра.

Детали структуры глобала для хранения объектов можно просмотреть, используя инспектор классов Studio в разделе «Storage → Default → DataN-odes» (см. Рис. 2.5).

38

Рис. 2.5 − Структура глобала, содержащего объекты

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

2.3 Интеграция с другими объектными технологиями Встроенные в Caché языки напрямую манипулируют объектами. Caché

также предоставляет доступ к этим объектам из классов приложений, разра-батываемых на технологии Java, EJB, COM, .NET, и C++.

Классы Caché автоматически обеспечивают поддержку XML и SOAP. Как результат, объекты Caché оказываются доступными для большинства существующих объектных технологий.

2.3.1 C++

Из каждого класса Caché может быть создана проекция класса в C++, чьи методы соответствуют свойствам и методам класса Caché. Для программ на C++ эти классы выглядят как любые другие классы C++ и Caché автома-тически обеспечивает взаимодействие между сервером и клиентом. Свойства класса кэшируются клиентом, и вызовы методов C++ вызывают соответст-

39

вующие им методы на стороне сервера, включая методы для сохранения объ-екта в базу данных и открытия объекта (см. Рис. 2.6).

Рис. 2.6 – Архитектура взаимодействия Caché и C++

2.3.2 JAVA

Java является популярным языком программирования, но связь прило-жений Java с большинством баз данных − непростая задача. Подключение к реляционной базе данных требует использование SQL, что занимает много времени и лишает преимуществ объектной технологии Java. Возможность работать с объектами Caché напрямую избавляет разработчика от забот по представлению описания данных для хранения, а использование объектного для работы с базой данных гораздо проще и предпочтительней (см. Рис. 2.7).

Некоторые разработчики предпочитают работать только с POJO1 объ-ектами на сервере (plain old Java objects), в то время как другие предпочитают EJB2 (Enterprise Java Beans) или же сначала описывают схему данных, а после генерируют соответствующие классам базы данных Java-классы, либо наобо-рот сначала создают классы Java и по ним генерируют схему данных в Caché.

1 http://ru.wikipedia.org/wiki/POJO 2 http://ru.wikipedia.org/wiki/EJB

40

Рис. 2.7 − Архитектура взаимодействия Caché и Java

Caché обеспечивает все эти способы: – Для любого класса Caché может быть создана его проекция как класс

Java, так что свойства и методы этого класса доступны как будто это объект Java.

– Классы Caché также проецируются как Enterprise Java Beans. – Высокопроизводительный доступ для SQL полностью обеспечивается

"родным" драйвером JDBC на Java (тип 4). – Технология Intersystems Jalapeño создает классы Caché из описаний

классов POJO. Для каждого класса Caché может быть создана соответствующая про-

екция − класс Java (или EJB) с методами, соответствующими каждому свой-ству и методу класса Caché. Для программ на Java эти классы ничем не отли-чаются от других классов Java приложения. Сгенерированные классы Java используют библиотеку Java из поставки Intersystems для связи клиента с сервером.

Сервер приложений Caché хранит состояние каждого объекта Caché, в то время как свойства класса также кэшируются клиентом для обеспечения оптимальной производительности. Вызовы методов Java вызывают соответ-ствующие методы на сервере приложений Caché, включая методы сохране-ния и открытия объекта.

Методы классов Caché могут быть написаны на Java в IDE Caché Studio. Но в отличие от Caché ObjectScript и Basic, методы Java не выполня-ются в виртуальной машине Caché. Они, вместо этого, включаются в генери-

41

руемый класс Java и выполняются в любой виртуальной машине Java. Код этих методов не доступен из методов не на Java.

2.3.2.1 EJB Разработчики приложений J2EE, использующие EJB (Enterprise Java

Beans), работают с объектами в основном до момента доступа к базе данных. После этого они вынуждены использовать SQL. Caché может предоставить быстрый ответ на SQL запросы через интерфейс JDBC для таких приложе-ний. В то же время доступ с использованием SQL не является предпочти-тельным в большинстве случаев.

Объектные базы данных представляют более естественную технику доступа для программистов EJB (см. Рис. 2.8). Caché проецирует классы Caché как EJB, автоматически генерируя высокопроизводительные методы хранения для BMP (Bean-Managed Persistence). Это позволяет избежать лиш-него кодирования при использовании SQL и объектно-реляционного отобра-жения, а в результате такие приложения лучше масштабируются.

Рис. 2.8 − EJB-звенья

42

Технология InterSystems Jalapeño также может использоваться в при-ложениях J2EE с теми же преимуществами.

2.3.2.2 Jalapeño Вместо описания классов в Caché и создания проекции их как компо-

ненты Java, технология InterSystems Jalapeño (JAva LAnguage PErsistence with NO mapping) предлагает обратный подход. Он позволяет разработчикам на Java описывать классы в привычной среде разработки Java и автоматиче-ски создавать описания для хранения этих классов в Caché. При этом классы, разработанные в Java, остаются без изменений – Caché предоставляет биб-лиотеку с API для обеспечения механизмов хранения и запросов по этим классам.

Используя Jalapeño, разработчик создает классы базы данных тем же способом, как и другие классы POJO, используя привычную для него среду IDE для Java. Дальше разработчик указывает Jalapeño, какие классы являются классами базы данных – обычно используя подключаемый к IDE модуль, по-ставляемый с библиотекой Jalapeño (Jalapeño Persistence Library). Jalapeño анализирует классы, автоматически создает соответствующие классы схемы данных (и код SQL) и генерирует необходимый код для обеспечения сохра-нения и чтения объектов во время выполнения приложения (см. Рис. 2.9). При этом Jalapeño не модифицирует разработанные классы POJO и програм-мист может продолжать редактировать их.

Рис. 2.9 − Архитектура Jalapeno

43

Jalapeño также поддерживает «эволюцию схемы», когда разработчик может продолжать изменять класс, добавляя новые свойства или изменяя их описания, и описание схемы «подхватывает» эти изменения так, что уже вве-денные раньше данные в новой редакции класса не становятся неправильны-ми.

2.3.2.3 .NET Caché, благодаря открытому и гибкому механизму доступа к данным,

обеспечивает целостное взаимодействие с платформой .NET1. Можно ис-пользовать различные интерфейсы: объектный, SQL, XML, SOAP2. Разработ-чик может выбирать предпочтительную технологию − с любой из них будет достигнута высокая производительность и масштабируемость Caché.

2.3.2.4 ADO .NET ADO.NET3 — новый вариант технологии ADO, оптимизированный для

платформы .NET. Назначение ADO − сделать приложения .NET «независи-мыми» от производителя базы данных. Для операций с базами данных в ос-новном применяют SQL. Используя реляционный доступ к данным, Caché обеспечивает поддержку ADO.NET. Также поддерживается соединение с ODBC.NET от Microsoft и подключение только для чтения через SOAP, встроенного в ADO.NET.

2.3.2.5 Web-службы Web-службы позволяют разделять функциональность между приложе-

ниями, как через Интернет, так и между разными системами в одной органи-зации. Интерфейс web-служб описывается на языке WSDL4 (Web Service Definition Language − язык описания web-службы). Web-служба возвращает документ XML, оформленный согласно протоколу SOAP.

Любой метод класса Caché, хранимая процедура или запрос может быть автоматически представлен как Web-служба. Caché генерирует описа-ние службы в файл WSDL и при вызове службы отправляет соответствую-щий документ XML. Caché облегчает быструю разработку, автоматически создавая web-страницу для тестирования службы без необходимости созда-ния тестового клиентского приложения.

1 http://ru.wikipedia.org/wiki/.NET 2 http://ru.wikipedia.org/wiki/SOAP 3 http://ru.wikipedia.org/wiki/ADO.NET 4 http://ru.wikipedia.org/wiki/WSDL

44

2.3.2.6 Perl Связка Caché со скриптовым языком для разработки web-приложений

Perl1 обеспечивает простой и прямой путь к управлению объектами Caché из Perl-приложения, что позволяет таким программам устанавливать соедине-ние к БД, создавать и открывать объекты, манипулировать свойствами этих объектов, сохранять их, запускать методы и запросы.

Perl-связывание для Caché предоставляет полную поддержку таких свойств как долговременное хранение объектов (persistence), многопользова-тельская обработка, транзакционный контроль.

Ниже приведен пример использования некоторых операций над объек-тами Caché из Perl-приложения:

– Создать соединение. $url = "localhost[1972]:Samples" $conn = Intersys:: PERLBIND:: Connection-> new ($url, "_SYSTEM", "SYS",0); $database = Intersys::PERLBIND::Database->new($conn);

– Открытие существующего объекта.

$person = $database->openid("Sample.Person","1", -1, -1);

– Создание нового объекта.

$person = $database->create_new("Sample.Person",undef);

– Чтение/запись свойства.

$person->set("Name","Иванов, Петр"); $name = $person->get("Name");

– Запуск метода.

$answer = $person->Addition(17,20);

– Сохранение объекта

$person->run_obj_method("%Save")

– Запуск запроса

$query = $database->alloc_query(); $query->prepare("SELECT * FROM SAMPLE.PERSON WHERE ID=?", $sqlcode);

1 http://ru.wikipedia.org/wiki/Perl

45

$query->set_par(1,2); $query->execute($sqlcode); while (@data_row = $query->fetch($sqlcode)) { $colnum = 1; foreach $col (@data_row) { $col_name = $query->col_name($colnum); print "column name = $col_name, data=$col\n"; $colnum++; } }

2.3.2.7 Python Все сказанное выше относительно языка Perl справедливо и для друго-

го распространенного скриптового языка Python1. Ниже приведен пример использования некоторых операций над объек-

тами Caché из python-приложения: – Создать соединение.

conn = intersys.pythonbind.connection() conn.connect_now(url,user,password, None) database = intersys.pythonbind.database(conn)

– Открытие существующего объекта

person = database.openid("Sample.Person",str(id),-1,-1)

– Создание нового объекта.

person = database.create_new("Sample.Person", None)

– Чтение/запись свойства.

person.set("Name","Doe, Joe A") name = person.get("Name")

– Запуск метода.

answer = person.run_obj_method("Addition",[17,20])

– Сохранение объекта.

person.run_obj_method("%Save",[])

– Запуск запроса.

sqlstring ="SELECT ID, Name, DOB, SSN \ FROM SAMPLE.PERSON \

1 http://ru.wikipedia.org/wiki/Python

46

WHERE Name %STARTSWITH ?" query = intersys.pythonbind.query(database) query.prepare(sqlstring) query.set_par(1,"A") query.execute(); while 1: cols = query.fetch([None]) if len(cols) == 0: break print cols

3 ТЕХНОЛОГИЯ ZEN

3.1 Архитектура Zen Архитектура Zen представляет собой трёхслойную конструкцию, так

называемую MVC (model, view, controller) архитектуру (см. Рис. 3.1). MVC архитектура бала разработана для решения задачи синхронизации и взаимо-действия хранимых объектов с элементами управления на интерфейсе без лишнего написания кода. Такое взаимодействие позволяет значительно со-кращать время разработки за счёт сокращения написания кода отображения данных на странице и их редактирования.

Zen-компоненты

Видимые

Представ-ление

Скрытый

Модель данных

Рис. 3.1 – MVC архитектура

Как видно из рисунка (Рис. 3.1) архитектура MVC имеет уровень моде-ли данных (Model), уровень управления данными (Controller) и уровень пред-ставления данных на странице (View).

Контроллер Представ-ление

Представ-ление

На клиенте и на сервере

Только на сервере

Только на клиенте

47

3.1.1 УРОВЕНЬ МОДЕЛИ ДАННЫХ Моделью данных является любой класс, унаследованный от

%ZEN.DataModel.DataModel. Объекты этого класса обеспечивают своего ро-да представление данных из разных источников для уровня управления (Con-troller). Источниками данных для модели данных могут быть: хранимые объ-екты Caché, внешние базы данных (ODBC или JDBC), глобалы Caché, биз-нес-службы Ensemble. У класса модели данных есть производные классы (см. Рис. 3.2).

Рис. 3.2 – Производные классы модели данных

Класс %ZEN.DataModel.ObjectDataModel (см. Рис. 3.3) позволяет скры-вать некоторые свойства из источников данных от класса контроллера. Такое сокрытие может использоваться при разработке приложений, когда имеются конфиденциальные данные, запрещенные к просмотру.

%ZEN.DataModel.ObjectDataModel

Источник данных

Модель данных

Контроллер

Свойство 1 Свойство 1

Свойство 2

Свойство 3 Свойство 3. . .

Свойство N

Рис. 3.3 – Класс %ZEN.DataModel.ObjectDataModel

48

Очень часто бывает удобно использовать хранимые объекты как объек-ты моделей данных. Для этого в Zen был разработан интерфейс в виде класса %ZEN.DataModel.Adaptor. Любой класс, который наследуется от %ZEN.DataModel.Adaptor становится моделью данных и одновременно хра-нимым классом.

%ZEN.DataModel.Adaptor

Источник данных

Контроллер

Свойство 1

Свойство 2

Свойство 3...

Свойство N

Рис. 3.4 – Класс %ZEN.DataModel.Adaptor

3.1.2 УРОВЕНЬ УПРАВЛЕНИЯ За реализацию уровня управления в Zen отвечают классы контроллеры,

которые наследуют от %ZEN.Auxiliary.dataController. В иерархии классов (см. Рис. 3.5) контроллер является также компонентом (не визуальным), ко-торый можно размещать на Zen-странице. Контроллер управляет коммуни-кациями между моделью данных и представлением данных.

Рис. 3.5 – Классы уровня контроллера и представления

49

3.1.3 УРОВЕНЬ ПРЕДСТАВЛЕНИЯ ДАННЫХ Любой Zen-компонент, реализующий интерфейс

%ZEN.Component.dataView относится к уровню представления данных. Спи-сок компонентов уровня представления данных (см. Рис. 3.5) включает в се-бя: таблицы, формы, графики, индикаторы.

3.2 Приложение Zen Приложение Zen состоит из трёх частей (см. Рис. 3.6): 1) из класса при-

ложения (%ZEN.application), определяющего поведение и стиль; 2) из клас-сов страниц (%ZEN.Component.page); 3) из классов компонентов (%ZEN.Component.component), размещённых на страницах.

Класс при-ложения

Класс стра-ницы

Класс стра-ницы

Класс стра-ницы

Класс стра-ницы

Дерево объектов

Класс ком-понента

Класс ком-понента

Класс ком-понента

Класс ком-понента

Класс пользо-вательского компонента

Рис. 3.6 – Zen-приложение со страницами и компонентами

Приложение Zen содержит классы страниц. Каждая Zen-страница со-держит объект страницы и множество объектов компонентов. Множество компонентов для страницы определяется встроенным блоком XML в класс страницы.

Класс страницы может также определять множество методов описы-вающих дополнительное поведение, выполняющихся по действиям пользо-

50

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

Когда клиент отправляет запрос на страницу, объект страницы и все содержащиеся в нём объекты компонентов создаются сначала на сервере, об-разуется дерево объектов (см. Рис. 3.7). Для этого дерева объектов генериру-ются все CSS (таблицы стилей), JavaScript и HTML необходимые для ото-бражения страницы в браузере. Как альтернатива может быть сгенерирован XML или SVG. Таким образом, в браузере клиента появляется копия дерева объектов в виде множества JavaScript-объектов.

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

Рис. 3.7 – Синхронизация Zen-страницы на клиенте и сервере

3.2.1 ЖИЗНЕННЫЙ ЦИКЛ ЗАПРОСА Zen-приложения имеют такой же принцип работы, что и csp-

приложения, поскольку используют те же самые механизмы (см. Рис. 3.8). На приведенном рисунке отображена последовательность основных

потоков событий на клиенте и сервере пока CSP обрабатывает HTTP-запрос. Когда браузер посылает запрос на сервер, web-сервер определяет – это CSP-запрос или http-запрос. В случае csp-запроса web-сервер перенаправляет его на CSP-шлюз, который в свою очередь направляет его на сервер Caché. Caché

51

сервер декодирует сообщение, определяет какое событие управляет классом и вызывает метод OnPage этого класса. Caché сервер возвращает результат метода OnPage CSP-шлюзу в виде HTTP-ответа. CSP-шлюз направляет HTTP-ответ web-серверу, который в свою очередь направляет его браузеру.

Web-клиенты Web-сервер Сервер приложений, СУБД

Браузер Web-сервер CSP-шлюз Caché-сервер CSP-класс

HTTP запрос

Запуск фильтра Перенаправ-

ление сессии Перенаправ-ление классу

Вызов метода OnPage

Ответ

HTTP ответ

Статическое содержание

Отображение страницы

Рис. 3.8 – Принцип работы Caché Server Pages

Zen-приложения используют CSP технологию в части: фильтрации и ответа на HTTP-запросы; управления сессиями и их жизненным циклом; управления идентификацией пользователей, авторизацией и контролем дос-тупа; обеспечения механизма гиперсобытий, использующего вызов сервер-ных методов из клиента.

52

Zen-приложения в отличии от CSP-приложений могут обрабатывать разные типы запросов, такие как XML или SVG. Далее (см. Рис. 3.9) рас-смотрим последовательность действий для отображения Zen-страницы в браузере.

Браузер Web-сервер CSP-шлюз Caché-сервер CSP-класс

HTTP запрос

Запуск фильтра Перенаправ-

ление сессии Перенаправ-ление классу

%OnBeforeCreatePage

Ответ

HTTP ответ

Статическое содержание

Отображение страницы

%DrowHTML

%OnAfterCreatePage

%CreatePage

Рис. 3.9 – Принцип работы Zen-приложений

Web-браузер отправляет HTTP-запрос на получение Zen-страницы на Web-сервер. Запрос, адресованный Caché, перенаправляется шлюзом(CSP Gateway) на сервер Caché. Caché-сервер, получив запрос, автоматически на-правляет его классу Zen-страницы. Класс страницы вызывает метод %OnBeforeCreatePage. Этот метод позволяет приложению выполнить любую работу до создания страницы. Далее на сервере создается экземпляр класса Zen-страницы. Класс страницы вызывает метод %CreatePage, который созда-ёт множество объектов компонентов и добавляет эти объекты к объекту страницы в качестве «детей». Если класс Zen-страницы содержит описание в XML-формате, Zen автоматически генерирует метод %CreatePage каждый раз, когда класс компилируется. Затем класс страницы вызывает метод %OnAfterCreatePage, который позволяет изменить содержание сгенериро-ванное методом %CreatePage если это необходимо.

53

После выполнения этого метода объект страницы представляется в ви-де объектной модели документа (DOM), которая будет использоваться для прорисовки HTML, необходимого для отображения в браузере. Далее стра-ница вызывает метод %DrawHTML. Каждая страница и объект компонента имеет метод %DrawHTML, который знает, как представить объект в виде HTML. В отношении Zen-страницы метод %DrawHTML создаёт: заголовок страницы; meta-HTML теги требуемые странице; набор конструкций, обес-печивающих поддержку гиперсобытий; операторы подключения JavaScript или CSS-файлов; JavaScript, необходимый для определения компонентов страницы на клиенте; JavaScript, необходимый для создания и инициализа-ции компонентов страницы на клиенте; установление стилей CSS из опреде-лений классов компонентов; определение стилей CSS из определений класса приложения; определение стилей CSS из определений класса страницы; HTML-представление для каждого компонента на странице (выполняется по-средствам вызова метода %DrawHTML у каждого компонента страницы). После генерации HTML сервер Caché отправляет HTML-документ клиенту, который отображает его в браузере.

3.2.2 КЛАССЫ ZEN-ПРИЛОЖЕНИЯ Zen-приложение описывает полное множество действий, возможных

при взаимодействии сервера и клиента. Эти действия могут содержать ото-бражение web-страницы, выполнение запросов к базе данных, запись данных на диск и многое другое. Когда создается Zen-приложение, создается набор классов Zen реализующие эти действия. Языком, используемым для описа-ния этих классов, является Caché ObjectScript со встроенными XML, HTML, SVG и JavaScript.

Базовый класс Zen-приложения (%ZEN.application) и базовый класс Zen-страницы (%ZEN.Component.page) наследуются от %CSP.page. Это гово-рит о том, что и Zen-приложение и Zen-страница являются страницами CSP. Таким образом, все свойства, методы, параметры и переменные (например, %session) доступны и Zen-странице.

Zen-приложение может потребовать конфигурации своих системных установок Caché. Код Zen-приложения находится в области Caché. Каждая область Caché имеет как минимум одно CSP-приложение отображённое на ней. По умолчанию, когда вы создаёте новую область, создаётся и CSP-приложение, соответствующее этой области. Это CSP-приложение по умол-чанию уже имеет определённые установки:

– CSP Application Name – имя приложения (значение по умолчанию: /csp/myNamespace).

– CSP Files Physical Path – место на сервере, где приложение хранит свои файлы (значение по умолчанию: install-dir/CSP/myNamespace). Обычно

54

Zen-приложение использует все установки по умолчанию своего пространст-ва Caché и ассоциируется с CSP-приложением этого пространства. Во время компиляции класса Zen (в том числе и класса Zen-приложения) генерируются JavaScript (.js) и Cascading Style Sheet (.css) файлы, которые располагаются по тому же пути, что и файл CSP-приложения. При необходимости можно изме-нить файлы (.js) и (.css).

– Default Timeout – время (в секундах), в течение которого приложение может бездействовать.

– Custom Error Page – страница, отображаемая когда в приложении воз-никает ошибка отсутствия страницы в приложении.

– Login Page – URL страницы, используемой при входе в приложение. Этот URL должен начинаться с CSP Application Name, затем должно быть указано имя пакета и затем имя класса отображаемой страницы. Например: /csp/myNamespace/MyDemo.Login.csp

Во время компиляции Zen-приложение устанавливает связь с CSP-приложением и использует его конфигурационные установки.

Обычно Zen-приложение создается для описания основных парамет-ров, свойственных для всех (или большей части) Zen-страниц разрабатывае-мого WEB-приложения. В основном это касается создания описания к стилям как внутри самого приложения, так и в виде CSS-файла.

3.2.3 НОВОЕ ZEN-ПРИЛОЖЕНИЕ Для создания нового приложения выберем пункт меню «Файл → Соз-

дать (Ctrl+N)». На закладке пользователь выберем «Новое Zen-приложение» (см. Рис. 3.10).

Рис. 3.10 – Новое Zen-приложение

55

Запустится мастер создания приложения (см. Рис. 3.11). Значение па-раметра «Имя пакета» зададим «zui.zen». По аналогии с примером в главе «Быстрый старт» пакеты, в которых будут располагаться классы страниц (в том числе и их приложение) будем размещать в пакете «zui». Имя класса за-дадим «MyApplication», остальные параметры могут остаться без значений. Сохраним и скомпилируем новое Zen-приложение.

Рис. 3.11 –Мастер создания нового приложения

3.3 Работа с компонентами. Компоновка страницы

3.3.1 КОНЦЕПЦИЯ ZEN-КОМПОНЕНТОВ Компоненты обеспечивают расположение, стиль и поведение для стра-

ницы. Следующая таблица демонстрирует эти базовые понятия (см. Табл. 3.1). Табл. 3.1 – Концепция компонентов Понятие Иллюстрация Комментарий Страница

Прямоугольное отображение в окне браузе-ра

56

Понятие Иллюстрация Комментарий Расположение

Позиционирование каждого компонента и группы компонентов на странице

Стиль

Определяет внешний вид компонентов

Поведение

Действия приложения по событию пользо-вателя, таймера или других событий

Вновь созданная страница − пустая и не содержит компонентов. Она

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

Компоненты заключаются внутрь групп – это специальный тип компо-нентов, который способен содержать от нуля до нескольких компонентов. Группа отвечает за размещение этих компонентов на странице. Страница са-ма является группой. Zen генерирует стандартную HTML-таблицу базовых элементов на основе определениях групп. Вы может выстраивать компонен-ты вертикально и горизонтально, заключённые внутри соответствующих групп. Как правило, наибольшие по ширине или высоте компоненты в группе определяют высоту и ширину группы на странице. Существуют разделяю-щие компоненты, которые могут быть использованы для установления про-межутков между другими компонентами в группах.

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

3.3.2 СТРУКТУРА ZEN-СТРАНИЦЫ Класс Zen-страницы (см. Рис. 3.12) состоит из:

57

– Описания содержимого в виде XML-блока Contents, встроенного в класс страницы. Он определяет множество компонентов, являющихся частью страницы.

-

Рис. 3.12 –

– Перегруженного стилстраницы. Он определяет пстраницы.

– Методов, вызываемынабор методов, выполняемынапример, возникающим прстраницы.

Следующая диаграммнентов внешнего вида странпонентов, имеющих связь с

Большая часть классоментами управления. Все кося в пакете %ZEN.Componeможно посмотреть в докуме

Имя приложения, частью которого является эта страница

Стиль страницы

Какие компоненты при-сутствуют на странице, где и как отображаются и какие действия могут выполнять

ObjectScript или Caché Basic код, выполняемыена сервере JavaScript, выполняемый в браузере

Класс страницы

Описание содержимого стра

я в виде XML-блока Styерегруженный CSS-стиль

х по событию. Класс страх по событиям, ассоциири взаимодействии пользо

а (см. Рис. 3.13) демонстрицы и элементов управлемоделью данных. в, представленных на димпоненты, представленныnt. Более детальную инфонтации Caché.

58

Объект страницы

ницы

le, встроенного в класс для компонентов этой

ницы обычно содержит ованным со страницей, вателя с компонентами

ирует перечень компо-ния − визуальных ком-

аграмме, являются эле-е на рисунке, находят-рмацию о компонентах

Рис. 3.13 – Иерархия классов компонентов внешнего вида страницы

3.3.3 КОМПОНОВКА КОМПОНЕНТОВ СТРАНИЦЫ Все компоненты Zen наследуют от класса %ZEN.Component.component.

Тем не менее, когда Zen-компонент размещается на странице, программиро-вание ведётся, прежде всего, на XML, а не на ObjectScript.

В данном разделе будет описано, как компонуется Zen-страница с ис-пользованием XML.

59

3.3.3.1 XData Contents При подготовке класса Zen для приложения, компоненты размещаются

на странице, используя блок XData Contents, в котором они описываются на языке XML. Каждый элемент XML соответствует классу компонента с тем же именем. Следующий пример блока XData Contents включает XML-элементы <page>, <html>, <hgroup>, <vgroup>, <menu>, <menuItem> и <pane>. Этот XML-блок представляет Zen-классы %ZEN.Component.page, %ZEN.Component.html, %ZEN.Component.hgroup, и т.д. <page xmlns="http://www.intersystems.com/zen"> <html enclosingClass="title">Макет социальной сети</html> <hgroup id="hgroup" width="50%" align="center"> <vgroup id="groupLeft" valign="top"> <menu layout="vertical"> <menuItem caption="Моя страница"/> <menuItem caption="Редактировать"/> <menuItem caption="Друзья"/> <menuItem caption="Люди"/> <menuItem caption="Настройки"/> <menuItem caption="Графики"/> <menuItem caption="Выход"/> </menu> </vgroup> <vgroup align="center" id="groupRight" width="100%" valign="top"> <pane align="center" paneName="mainPane" width="100%"/> </vgroup> </hgroup> </page>

В браузере эта страница будет выглядеть так (см. Рис. 3.14).

Рис. 3.14 – Шаблон для страниц системы «Макет социальной сети»

60

Представленный пример является частью класса «Template.cls» систе-мы «Макета социальной сети»1, модель данных которого представлена на следующем рисунке (см. Рис. 3.15).

Faculty(from zen)

University(from zen)

0..1+University 0..1

City(from zen)

0..1+City 0..1

Region(from zen)

0..1 +Region0..1

TypeGroup(from zen)

PersonalInGroup(from zen)

0..1+TypeGroup 0..1

Status(from zen)

Occupation(from zen)

Speciality(from zen)

0..1

+Faculty

0..1

Personal(from zen)

0..1

+PerIn

0..10..1+Personal

0..1

0..1 +Current0..10..1+History 0..1

0..1

+Occupation

0..1

0..1

+Speciality

0..1

School(from zen)

0..1

+City

0..1 0..1

+School

0..1

Рис. 3.15 – Модель данных системы «Макет социальной сети»

Когда компилируется класс, содержащий блок XData Contents, Zen ге-нерирует код для отображения страницы в браузере. Во время выполнения этот код представляет указанные классы компонентов как потомки объекта страницы. Zen управляет этой иерархией автоматически.

Программист не работает с классами компонентов напрямую, он рабо-тает с XML-проекциями этих классов. Для каждого компонента существует проекция, которая состоит из:

– XML-элемента с тем же названием, как и у класса (<page>, <html>, <hgroup>, и т.д.);

– XML-атрибутов, соответствующих свойствам класса (width, height, и

1 В данной главе все примеры будут разбираться на базе одного приложения которое представляет

собой упрощенный макет социальной сети (“ВКонтакте”, “Одноклассники”, FaceBook). Данный макет по-зволяет: регистрировать пользователя в системе; редактировать профили пользователей; устанавливать вза-имные связи различного типа между пользователями (друг, родственник, коллега); просматривать списки контактов с возможностью фильтрации по типам связей; просматривать списки друзей (2-й круг), в том чис-ле в графическом виде; изменять текущий статус пользователя; просматривать ленты изменений статусов друзей; просматривать графики данных пользователей (возраст, пол, род занятий).

61

т.д.) Такое соглашение облегчает работу программиста, которому не требу-

ется вникать в детали реализации этого механизма и ускоряет разработку за счёт автоматизации типовых операций.

3.3.3.2 Страницы Блок XData Contents содержит один элемент <page>, который выступа-

ет в качестве контейнера верхнего уровня для всех XML-элементов в этом блоке.

3.3.3.3 Заголовки Описать заголовок можно тремя способами: элементом <titleBox>, эле-

ментом <html> или произвольным пользовательским компонентом. Ниже приведены примеры для представленных способов. <page xmlns="http://www.intersystems.com/zen"> <titleBox id="sampleTitle" title="Макет социальной сети" subtitle="Общая информация"/> <pane paneName="" /> </page>

<page xmlns="http://www.intersystems.com/zen"> <pane paneName="" /> <html id="sampleTitle"> <h1>Макет социальной сети</h1> <h2>Общая информация</h2> </html> <pane paneName="" /> </page>

3.3.3.4 Группы Компонент группы наследует от %ZEN.Component.group. Главным на-

значением компонента-группы является исполнение функции контейнера для других компонент. Примерами групп являются компоненты page, pane, menu, form и composite, все они унаследованы от %ZEN.Component.group.

Каждая группа предназначена для размещения компонент, которые она содержит, на странице. Группа может иметь горизонтальное или вертикаль-ное расположение. Каждая группа по умолчанию имеет вертикальное распо-ложение за исключением <hmenu> и <hgroup>.

62

Приведённый ниже пример демонстрирует использование групп при компоновке страницы «Моя страница» приложения «Макет социальной се-ти», результат которого приведён на рисунке (см. Рис. 3.16). <page xmlns="http://www.intersystems.com/zen">

<dataController id="controller" modelClass= "zen.PersonalDataModel" modelId="#(%page.objId)#"/>

<html enclosingClass="subtitle" id="subtitle"></html>

<form controllerId="controller" width="500px" align="center">

<fieldSet legend="Статус" align="center" labelPosition="left">

<spacer height="10"/>

<group id="group" align="center">

<hgroup align="left">

<label id="date" dataBinding="CurrentDate"/>

<spacer width="10"/>

<link id="currStatus" disabled="false" align="center" href="#" onclick="zenPage.StatusEdit();"/>

</hgroup>

<link id="history" align="center" href="#" caption="история" on-click="zenPage.History();"/>

<hgroup id="btGroup" align="left">

<button id="btDel" disabled="false" align="center" caption= "очистить" hidden="true" onclick="zenPage.ClearHistory();"/>

<button id="btClose" align="center" caption="закрыть" hidden= "true" onclick="zenPage.CloseHistory();"/>

</hgroup> </group> <hgroup id="groupEdit" align="center" hidden="true">

<text id="editStatus" align="center" dataBinding="CurrentName"/>

<button caption="OK" onclick="zenPage.StatusChange();"/>

</hgroup> <spacer height="10"/>

</fieldSet> <link id="linkEdit1" hidden="false" href="zui.zen.EditProfile.cls? ref=1" align="right" caption="редактировать"/> <fieldSet legend="Основное" align="center" labelPosition="left">

<label label="Дата рождения:" dataBinding="DOB"/>

<label label="Пол:" dataBinding="Sex"/>

<label label="Род занятий:" dataBinding="OccupationName"/>

<spacer height="10"/>

</fieldSet> <link id="linkEdit2" hidden="false" href="zui.zen.EditProfile.cls? ref=2" align="right" caption="редактировать"/> <fieldSet legend="Школа" align="center" labelPosition="left">

<label label="Школа:" id="School" dataBinding="SchoolName"/>

63

<label label="Дата окончания:" dataBinding="EndSchDate"/>

<spacer height="10"/>

</fieldSet> <link id="linkEdit3" hidden="false" href="zui.zen.EditProfile.cls? ref=3" align="right" caption="редактировать"/> <fieldSet legend="Университет" align="center" labelPosition="left">

<label id="University" label="Вуз:" dataBinding="UniversityName"/>

<label id="Faculty" label="Факультет:" dataBinding="FacultyName"/>

<label id="Speciality" label="Специальность:" dataBinding= "SpecialityName"/>

<label label="Дата окончания:" dataBinding="EndUnivDate"/>

<spacer height="10"/>

</fieldSet> </form> </pane>

Рис. 3.16 – «Моя страница» приложения «Макет социальной сети»

3.3.4 НАВИГАЦИОННЫЕ КОМПОНЕНТЫ К навигационным компонентам относятся: вертикальное и горизон-

тальное меню, ссылки, закладки и комбинированные ссылки (например, де-ревья) (см. Рис. 3.17).

64

Рис. 3.17 – Навигационные компоненты Zen

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

3.3.5 РАЗРАБОТКА ПОЛЬЗОВАТЕЛЬСКИХ КОМПОНЕНТОВ Одним из наиболее существенных свойств архитектуры Zen является

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

Пользовательские компоненты встраиваются в существующую плат-форму Zen с минимальными затратами. Они обеспечивают стиль и поведе-ние, которые необходимы разработчику. В следующей таблице (см. Табл. 3.2) описываются варианты создания пользовательских компонентов. Табл. 3.2 – Варианты создания пользовательских компонентов Задача Описание Построение составно-го компонента

Составные пользовательские компоненты группируют существующие, встраиваемые Zen-компоненты. Такая группа может размещаться на странице как простой ком-понент.

Модификация стиля компонента

Можно сделать простые изменения стилей встраиваемых Zen-компонентов путём создания наследника и перегруз-ки блока XData Style.

65

Задача Описание Создание пользова-тельского компонента

Создание пользовательских компонентов позволяет рас-ширить перечень Zen-компонентов. При использовании Zen-соглашения, вновь создаваемые пользовательские компоненты автоматически получают XML-проекцию для использования на любой Zen-странице в блоке XData Contents.

Создание пользова-тельской метрики

Пользовательская метрика создаётся путём наследования от класса базовой метрики и добавлением метода, кото-рый требуется для визуализации SVG-контента с новой метрикой.

Рассмотрим в качестве примера создание композитного компонента.

Следующий код демонстрирует создание композитного компонента с двумя кнопками. Class MyApp.myComposite Extends %ZEN.Component.composite { /// Define XML namespace for this component Parameter NAMESPACE = "http://www.intersystems.com/myApp"; /// Contents of this composite component: XData Contents [XMLNamespace="http://www.intersystems.com/zen"] { <composite> <button caption="OK" onclick="zenThis.composite.okBtn();"/> <button caption="Cancel" /> </composite> } Method okBtn() [Language = JavaScript] { alert('ok'); } }

В новом пользовательском компоненте требуется определение пара-

метра NAMESPACE. В приведённом выше примере этому параметру при-сваивается значение "http://www.intersystems.com/myApp". Далее рассмотрим код, использующий новый компонент. XData Contents [XMLNamespace="http://www.intersystems.com/zen"] { <page xmlns="http://www.intersystems.com/zen" xmlns:myApp="http://www.intersystems.com/myApp"> <myApp:myComposite /> </page> }

66

В приведённом выше примере в элементе <page> определяется ссылка на пространство имён композитного компонента, и её алиас xmlns:myApp="http://www.intersystems.com/myApp". Обращение к новому композитному элементу имеет следующую форму: <alias:component>. В со-ответствии с этой формой в примере обращение записано так: <myApp:myComposite />

3.4 Шаблонные страницы Шаблонная страница является абстрактным классом страницы, кото-

рый реализует свойства и поведение одинаковые для всех страниц приложе-ния. Все страницы приложения наследуют от этого абстрактного класса.

В нашем примере существует шаблонная страница, в которой реализо-вано общее меню и структура страницы, код которой приведён ниже. Class zui.zen.Template Extends %ZEN.Component.page [ Abstract ] { … XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen"> <html enclosingClass="title">Макет социальной сети</html> <hgroup id="hgroup" width="50%" align="center"> <vgroup id="groupLeft" valign="top"> <menu layout="vertical"> <menuItem caption="Моя страница" onclick="zenPage.goMain();"/> <menuItem caption="Редактировать" onclick= "window.location= 'zui.zen.EditProfile.cls';"/> <menuItem caption="Друзья" onclick="window.location= 'zui.zen.Friend.cls';"/> <menuItem caption="Люди" onclick="window.location= 'zui.zen.People.cls';"/> <menuItem caption="Настройки" onclick="window.location= 'zui.zen.Setting.cls';"/> <menuItem caption="Графики" onclick="window.location= 'zui.zen.SvgChart.cls';"/> <menuItem caption="Выход" onclick="zenPage.FinishSession();"/> </menu> </vgroup> <vgroup align="center" id="groupRight" width="100%" valign="top"> <pane align="center" paneName="mainPane" width="100%"/> </vgroup> </hgroup> </page> }

67

Шаблонная страница позволяет удобно организовать Zen-приложение. В описанном примере шаблон описывает вертикальное меню и панель, в ко-торой будет размещено содержание, определённое в классах наследниках. Так, например, код класса наследника, отображающего основную информа-цию о человеке, будет выглядеть примерно так: Class zui.zen.Main Extends zui.zen.Template { … XData mainPane [ XMLNamespace = "http://www.intersystems.com/zen" ] { <pane xmlns="http://www.intersystems.com/zen"> … </pane> }

3.5 Каскадные таблицы стилей

3.5.1 ПОРЯДОК ОПРЕДЕЛЕНИЯ СТИЛЕЙ Когда Zen-страница конструирует себя, происходит сборка всей ин-

формации CSS-стилей, которые были установлены для каждого компонента страницы и применение их к компонентам. Существует несколько источни-ков для этой информации: классы страниц, классы приложений и классы компонентов. Zen применяет эту информацию в следующем порядке.

1) Стили определяютcя CSS-файлом для библиотеки компонентов Zen (ZEN_Component.css).

2) Стили определяются классами базовых компонентов. По умолчанию стили для встроенных компонентов обеспечиваются через XData Style-блок в соответствующем классе %ZEN.Component. Тем не менее, если вы хотите изменить стиль встроенного Zen-компонента не редактируя указанный класс, можно воспользоваться одним из двух способов: 1) создать свой собствен-ный компонент на основе существующего; 2) перегрузить соответствующее определение стилей в классе страницы или приложения.

3) Стили определяются в подклассах базовых классов компонентов (пользовательские компоненты). В любом таком подклассе стили может обеспечить блок XData Style. Если такой блок в подклассе отсутствует, то стили обеспечивает такой же блок из родительского класса.

4) Стили определяются внутри класса приложения. Существует два ис-точника для определения стилей в приложении: 1) в блоке XData Style; 2) че-рез ссылку на внешний CSS-файл, описанную в параметре CSSINCLUDES.

5) Стили определяются через класс шаблона страницы. Для такого опре-деления стилей также существует несколько источников: 1) ссылка на внеш-

68

ний CSS-файл, описанная в параметре CSSINCLUDES; 2) в блоке XData Style самого класса; 3) в стилях атрибутов блока XData Contents.

6) Стиля определяются внутри класса страницы (аналогично шаблону страницы).

Таким образом, стили страницы перегружают стили приложения, кото-рые в свою очередь перегружают стили базовых компонентов (см. Рис. 3.18). Внутри каждой категории потомок перегружает стили родителя.

й Порядок предшествования

Класс ком-понента

-

Рис. 3.18 – Пой

й

Порядок определения

3.5.2 ОПРЕДЕЛЕНИЕ СОпределим стили для

(p6SkinFB1.css) и подключимфайла к приложению, нужного воспользуемся пунктом м«Параметры» и выберем пар

Подкласс класса компонента

Класс при-ложения

Класс стра-ницы

рядок определения каскадных стилей

к -

ТИЛЕЙ ДЛЯ НАШЕГО ПРИЛО нашего приложения во внешнем его к Zen-приложению. Для подкл перегрузить параметр CSSINCLUDеню «Класс → переопределить…»аметр CSSINCLUDES (см. Рис. 3.19)

69

Подкласс ласса страницы

Низши

Высший

Предыдущи

Следующи

ЖЕНИЯ CSS-файле ючения CSS-ES. Для это-

, на закладке .

Рис. 3.19 – Мастер переопределения

В приложение добавится строка с параметром CSSINCLUDES, значе-ние которого должно содержать путь до CSS-файла: /// Поключение отдельного файла, содержащего набор стилей CSS Parameter CSSINCLUDES As STRING = "p6SkinFB1.css";

3.6 Привязка данных к элементам страницы Связь данных с элементами страницы в Zen реализуется посредствам

архитектуры MVC (см. п. 3.1), которая подразумевает наличие объектной модели данных, контроллера и компонентов страницы. Таким образом, для реализации страницы Zen необходимо создать соответствующие классы на всех трёх уровнях архитектуры. Начнём с построения модели данных (см. п. 3.6.1).

3.6.1 МОДЕЛИ ДАННЫХ Существует два способа создания модели данных (см. п. 3.1.1) – через

класс %ZEN.DataModel.ObjectDataModel или класс %ZEN.DataModel.Adaptor. Создание модели данных на базе класса ZEN.DataModel.ObjectDataModel увеличивает количество написанного кода, но позволяет создавать более гибкие модели независящие от структуры данных. Кроме того, такую модель данных удобно использовать, если разрабатываемое WEB-приложение ни имеет собственных хранимых классов или источниками данных являются другие СУБД.

70

В свою очередь модель данных на базе класса %ZEN.DataModel.Adaptor позволяет быстро без лишнего написания кода соз-давать модели данных, классы которых одновременно являются хранимыми классами, т.е. описывают структуру базы данных Caché.

3.6.1.1 Модель данных на базе класса %ZEN.DataModel.Adaptor Класс, унаследованный от %ZEN.DataModel.Adaptor и одновременно от

%Persistent является и хранимым классом и моделью данных. Таким обра-зом, все необходимые методы, требуемые для связывания модели данных с компонентами Zen-страницы, в таком классе уже реализованы.

3.6.1.2 Модель данных на базе класса %ZEN.DataModel.ObjectDataModel Создадим модель данных для хранимого класса Personal (со структу-

рой базы данных, используемой в приложении «Макет социальной сети» можно ознакомиться в приложении Б). Для этого создадим новый класс, на-следуемый от %ZEN.DataModel.ObjectDataModel и создадим свойства анало-гичные классу Personal. /// DataModel для класса Personal Class zen.PersonalDataModel Extends %ZEN.DataModel.ObjectDataModel { /// Логин Property Login As %String [ Required ]; /// Пароль Property NewPassword As %String [ Required ]; /// Фамилия Property Surname As %String(MAXLEN = 200) [ Required ]; /// Имя Property Name As %String(MAXLEN = 200) [ Required ]; /// Дата рождения Property DOB As %Date(FORMAT = 3); /// Пол человека Property Sex As %String; /// Род занятий Property Occupation As %String; /// Свойства *Name используются на странице Main /// Род занятий(название) Property OccupationName As %String; /// Текущий статус Property CurrentName As %String; Property CurrentDate As %String; /// Школа Property RegionSchool As %String; Property CitySchool As %String; Property School As %String; Property SchoolName As %String;

71

Property EndSchDate As %Date(FORMAT = 3); /// Универститет Property RegionU As %String; Property CityU As %String; Property University As %String; Property UniversityName As %String; Property Speciality As %String; Property SpecialityName As %String; Property Faculty As %String; Property FacultyName As %String; Property EndUnivDate As %Date(FORMAT = 3); }

Для того чтобы эта модель данных стала работоспособной нужно реа-

лизовать ряд методов, описанных этой моделью. Для удобства перегрузки методов можно воспользоваться мастером. Для этого выберем пункт меню «Класс → переопределить…», перейдём на закладку «Методы» и выберем требуемый метод (см. Рис. 3.20).

Рис. 3.20 – Мастер переопределения

Поочерёдно нужно переопределить следующие методы: %OnNewSource(), %OnOpenSource(), %OnSaveSource(), %OnDeleteSource(), %OnLoadModel(), %OnStoreModel(). Ниже приведён пример перегруженных методов для модели данных PersonalDataModel. Class zen.PersonalDataModel Extends %ZEN.DataModel.ObjectDataModel { …

72

/// Метод вызывается при создании нового экземпляра класса Method %OnNewSource(Output pSC As %Status = {$$$OK}) As %RegisteredObject { Quit ##class(zen.Personal).%New() } /// Этот метод вызывается когда DataModel загружается из памяти Method %OnOpenSource(pID As %String, pConcurrency As %Integer = -1, Output pSC As %Status = {$$$OK}) As %RegisteredObject { Quit ##class(zen.Personal).%OpenId(pID,pConcurrency,.pStatus) } /// Метод, вызывается при сохранении Method %OnSaveSource(pSource As zen.Personal) As %Status { set tSC = pSource.%Save() set ..%id = pSource.%Id() quit tSC } /// Метод, вызывается при удалении ClassMethod %OnDeleteSource(pID As %String) As %Status { Quit ##class(zen.Personal).%DeleteId(pID) } /// Загрузка модели Method %OnLoadModel(pSource As zen.Personal) As %Status { set ..Login = pSource.Login set ..Surname = pSource.Surname set ..Name = pSource.Name set ..DOB = pSource.DOB set ..Sex = pSource.Sex set ..OccupationName = pSource.Occupation.Name set ..Occupation = pSource.OccupationGetObjectId() set ..CurrentName = pSource.Current.Name set ..CurrentDate = pSource.Current.DateTime set ..RegionSchool = pSource.School.City.RegionGetObjectId() set ..CitySchool = pSource.School.CityGetObjectId() set ..EndSchDate = pSource.EndSchDate set ..School = pSource.SchoolGetObjectId() set ..SchoolName = pSource.School.Name set ..RegionU = pSource.Speciality.Faculty.University.City.RegionGetObjectId() set ..CityU = pSource.Speciality.Faculty.University.CityGetObjectId() set ..University = pSource.Speciality.Faculty.UniversityGetObjectId()

73

set ..UniversityName = pSource.Speciality.Faculty.University.Name set ..Faculty = pSource.Speciality.FacultyGetObjectId() set ..FacultyName = pSource.Speciality.Faculty.Name set ..Speciality = pSource.SpecialityGetObjectId() set ..SpecialityName = pSource.Speciality.Name set ..EndUnivDate = pSource.EndUnivDate Quit $$$OK } /// Сохранение модели Method %OnStoreModel(pSource As zen.Personal) As %Status { set pSource.Login = ..Login set pSource.NewPassword = ..NewPassword set pSource.Surname = ..Surname set pSource.Name = ..Name set pSource.DOB = ..DOB set pSource.Sex = ..Sex do pSource.OccupationSetObjectId(..Occupation) do pSource.SchoolSetObjectId(..School) set pSource.EndSchDate = ..EndSchDate do pSource.SpecialitySetObjectId(..Speciality) set pSource.EndUnivDate = ..EndUnivDate Quit $$$OK }}

3.6.2 СВЯЗЫВАНИЕ ФОРМЫ С ОБЪЕКТНОЙ МОДЕЛЬЮ ДАННЫХ Для связывания элементов формы с моделью данных на странице

нужно создать компонент контроллер <dataController> на этой странице. Рас-смотрим в качестве примера страницу «Main.cls». XData mainPane [ XMLNamespace = "http://www.intersystems.com/zen" ] { <pane xmlns="http://www.intersystems.com/zen"> <dataController id="basic" modelClass = "zen.PersonalDataModel" modelId = "#(%session.pId)#"/> … </pane> }

В приведённом выше примере описывается контроллер для страницы

«Моя страница». Здесь у контроллера устанавливается значение идентифика-тора (id="basic"), присоединяется класс модели данных "zen.PersonalDataModel" и устанавливается идентификатор объекта модели данных modelId = "#(%session.pId)#".

74

Размещённый на странице контроллер (<dataController>) позволяет оп-ределить следующие атрибуты (см. Табл. 3.3). Табл. 3.3 – Атрибуты контроллера Атрибут Описание alertOnError Если значение true, отображается окно с предупреждением, когда

возникает ошибка в момент выполнения серверного метода, на-пример сохранения или удаления.

autoRefresh Установленный атрибут autoRefresh в ненулевое значение обеспе-чивает автоматическое обновление данных контроллера. В этом режиме контроллер перезагружает данные с сервера каждые X миллисекунд, где autoRefresh = X.

defaultSeries Если модель данных имеет множество наборов данных, то defaultSeries специфицирует каждый набор данных и обеспечивает отображение данных в представлении (например, на форме) только одного набора.

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

modelId Этот параметр идентифицирует экземпляр класса модели данных. Формат и возможные значения параметра modelId определяются разработчиком в классе модели данных. При изменении значения modelId контроллер загружает новую запись и обновляет соответ-ствующие связи в представлении.

readOnly Если значение true, то контролер не позволяет изменять данные в модели данных.

И т.д. Следующим шагом создадим уровень представления, где разместим

элементы управления (визуальные компоненты страницы) и соединим их с контроллером.

В качестве примера рассмотри страницу EditProfile, на которой редак-тируются персональные данные человека. На этой странице элементы управ-ления размещены на трёх закладках, в каждой из которых помещена форма, связывающаяся с контроллером. Ниже приведён код, демонстрирующий три закладки, в каждой из которых размещена форма, связанная с контроллером посредствам параметра controllerId. <tabGroup id="editTabGroup" showTabBar="true" align="center" label-Position="left"> <tab id="tab1" caption="Основное"> <form id="formBasic" align="center" labelPosition="left" control-lerId="basic" >

75

… </form> </tab> <tab id="tab2" caption="Школа"> <form id="formSchool" align="center" labelPosition="left" controllerId="basic"> … </form> </tab> <tab id="tab3" caption="Университет"> <form id="formUniv" align="center" labelPosition="left" controllerId="basic"> … </form> </tab> </tabGroup>

На примере формы "formBasic", размещённой на первой закладке рас-

смотрим связывание элементов управления с данными. <form id="formBasic" align="center" labelPosition="left" controllerId="basic"> <spacer height="10"/> <radioSet label="Пол:" id="Sex" name="Sex" displayList="мужской,женский" valueList="мужской,женский" dataBinding="Sex"/> <dateText label = "Дата рождения:" id = "DOB" name = "DOB" dataBinding = "DOB" size="22"/> <dataCombo label="Род занятий:" id="Occupation" dropdownHeight="auto" dropdownWidth="150px" sql="select id,Name from zen.Occupation ORDER BY Name" sqlLookup="select Name from zen.Occupation WHERE id=?" dataBinding="Occupation" size="22"/> <spacer height="30"/> <button caption="Сохранить" onclick="zenPage.save();"/> </form>

В приведённом выше коде на форме «formBasic» размещены три эле-

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

76

троллер посредствам параметра dataBinding, которому присваивается имя поля модели данных.

Так элемент управления <radioSet> привязан к полю "Sex". В таких элементах управления главными параметрами являются displayList и valueList, которые позволяют задавать множество отображаемых и хранимых значений соответственно. Параметр id используется для доступа к какому-либо элементу управления через клиентский метод.

В элементе управления <dataCombo> для формирования множества отображаемых и хранимых значений используется параметр SQL. Обратите внимание на перечень полей в операторе select. Значения первого поля ис-пользуется для хранения, а значения второго для отображения. Параметр sqlLookup делает текущее значение активным в выпадающем списке. В пред-ложении WHERE параметра sqlLookup задаётся условие, по которому и уста-навливается текущее значение. На место вопроса во время загрузки Zen под-ставляет значение, полученное через параметр dataBinding.

Кнопка «Сохранить» вызывает стандартный серверный метод контрол-лера save(), который сохраняет данные формы в базу данных. В течение про-цесса сохранения форма спрашивает контроллер о корректности введённых в неё данных. Контроллер выполняет проверку в соответствии с описанием свойств в модели данных и их ограничений. Если данные корректны, форма вызывает метод контроллера save().

3.7 Zen-таблицы Zen-таблицы позволяют просматривать данные в удобном для пользо-

вателя виде. Таблица берёт результат возвращаемый SQL-запросом и ото-бражает его в виде таблицы HTML. Существует несколько возможностей ге-нерации результирующего множества для Zen-таблицы. Так, можно опреде-лить SQL-скрипт в самой таблице, указать ссылку на предопределённый за-прос или определить необходимые входные параметры для генерации SQL-скрипта «налету». Zen-таблицы позволяют осуществлять фильтрацию дан-ных по каждому из столбцов, сортировку и разбивать всё множество данных по страницам.

В качестве примера рассмотрим таблицу на странице Friend (см. Рис. 3.21). Если в таблице выбирается какая-либо строка, то появляются дополни-тельные кнопки для выполнения соответствующих действий (см. Рис. 3.22). Код для отображения таблицы приведён ниже. <tablePane id="table" align="center" tableName="zen.PersonalInGroup" useSnapshot="true" valueColumn="ID" width="400px"

77

onselectrow="zenPage.RowSelected();" ondblclick="zenPage.Rowdbclick();" whereClause="Personal=#(%session.pId)#"> <column colName="ID" hidden="true"/> <column header="Фамилия" colName="PerIn->Surname"/> <column header="Имя" colName="PerIn->Name"/> <column id="colGroup" header="Группа" colName="TypeGroup->GroupName" filterType="enum" filterEnum="Друг,Родственник,Коллега" filterOp="="/> </tablePane>

Рис. 3.21 – Список контактов текущего пользователя (сделан при помощи компонента

<tablePane>)

В приведённом выше коде таблица <tablePane> через параметр tableName привязывается к таблице базы данных zen.PersonalInGroup, что обеспечивает получение данных из этой таблицы и связанных с ней таблиц, для отображения их на странице. Параметр whereClause задаёт условие отбо-ра данных из таблицы. В нашем случае внешнему ключу Personal присваива-ется значение идентификатора текущего пользователя, взятого из свойства сессии.

Параметр useSnapshot, установленный в "true", обеспечивает выполне-ние SQL-запроса один раз. На стороне севера создаётся копия результирую-щего множества, с которой и работает компонент <tablePane>. Это позволя-ет не делать повторных запросов при выполнении операций фильтрации, сортировки и перемещения по страницам таблицы.

В столбцах таблицы осуществляется привязка к полям таблицы базы данных через атрибут colName. Из примера видно, что привязать можно не только поле таблицы, обозначенного в параметре tableName, но и поля смеж-ных таблиц. В последнем столбце таблицы добавлен фильтр типа выпадаю-щего списка с заданным набором значений (Друг,Родственник,Коллега).

78

Реализация реакции компонента на выбранную строку таблицы обес-печивается параметром onselectrow, которому присвоено имя метода zenPage.RoSelected(). Метод RowSelected() делает видимым группу кнопок, которые становятся доступными пользователю (см. Рис. 3.22).

Рис. 3.22 – Реакция на выбор записи в таблице

В другой таблице на странице People (см. Рис. 3.23) установлен пара-метр pageSize, который позволяет отображать в таблице определённое число строк и перемещаться по страницам таблицы.

Рис. 3.23 – Таблица всех зарегистрированных пользователей системы

3.8 Реализация поведения страницы

3.8.1 ZEN-МЕТОДЫ Существует три типа методов, реализуемых в классе Zen-страницы (см.

Табл. 3.4).

79

Табл. 3.4 – Типы методов класса Zen-страницы Место вызова

Место выпол-нения

Принад-лежность

Тип вызова

Язык Клю-чевое слово

Согла-шение по на-званию

Клиент Клиент Экземпляр класса

− JavaScript Lan-guage = Java-Script

my-Method

Асинхр.Экземпляр класса Синхр.

Асинхр.

Клиент Сервер (может возвращать код, выполняемый на клиенте)

Метод класса Синхр.

Ob-jectScript, Caché Ba-sic, Caché MVBasic

Zen-Method

My-Method

Экземпляр класса

Сервер Сервер

Метод класса

− Ob-jectScript, Caché Ba-sic, Caché MVBasic

− %My-Method

Каждый метод, определяемый внутри Zen-станицы, имя которого не

начинается со знака %, является клиентским методом объекта страницы. Лю-бой метод, начинающийся со знака %, является только серверным методом.

Каждый метод экземпляра класса страницы определённый как [Language = javascript], является клиентским методом экземпляра страницы, выполняющимся на стороне клиента в браузере.

Каждый метод определённый с ключевым словом [ZenMethod], являет-ся клиентским методом экземпляра страницы, выполняющимся на стороне сервера. Метод [ZenMethod] может быть также методом класса, выполняю-щимся на сервере, но вызывающимся как метод экземпляра на клиенте.

Методы, выполняемые на стороне сервера, могут быть синхронными и асинхронными. Если такой метод имеет возвращаемое значение, он является синхронным, иначе асинхронным (в этом случае клиент не ждёт завершения выполнения метода).

Основными преимуществами асинхронных методов являются: – Браузер может продолжать работу, пока сервер обрабатывает запрос. – Можно вызвать серверный метод, внести изменения на клиенте (на-

пример, отобразить сообщение), а когда выполнение метода завершится, данные в сообщении могут измениться.

80

Некоторые Zen-компоненты используют возможности асинхронных методов. Например, <dataCombo> использует асинхронные методы для вы-полнения запроса на стороне сервера. Это позволяет <dataCombo> отобра-жать сообщение «loading...» пока запрос выполняется.

3.8.2 ПРИМЕР РЕАЛИЗАЦИИ ZEN-МЕТОДОВ

3.8.2.1 Методы экземпляра, выполняемые на стороне клиента Рассмотрим в качестве примера страницу EditProfile, на которой реали-

зуется сохранение данных на формах на каждой из закладок editTabGroup. В приведённом ниже фрагменте показан принцип вызова методов по событию (в данном случае по нажатию на кнопку). <tab-Group id="editTabGroup" showTabBar="true" align="center" labelPosition="left"> <tab id="tab1" caption="Основное"> <form id="formBasic" align="center" controllerId="basic" labelPosition="left" > … <button caption="Сохранить" onclick="zenPage.SaveBasic();"/> </form> </tab> <tab id="tab2" caption="Школа" onshow="zenPage.LoadSchool();"> <form id="formSchool" align="center" labelPosition="left" controllerId="basic"> … <button caption="Сохранить" onclick="zenPage.SaveSchool();"/> </form> </tab> <tab id="tab3" caption="Университет" onshow="zenPage.LoadUniv();"> <form id="formUniv" align="center" labelPosition="left" controllerId="basic"> … <button caption="Сохранить" onclick="zenPage.SaveUniversity();"/> </form> </tab> </tabGroup>

В приведённом примере в форме "formBasic" по событию onclick вызы-

вается метод "zenPage.SaveBasic();". В отличие от примера, приведённого в п. 3.6.2, где вызывался метод save() контроллера, здесь вызывается метод фор-мы. Это позволяет сохранить только данные формы, а не всего контроллера на всех закладках (см. пример кода ниже).

81

/// Сохранение изменений (основные данные) Method SaveBasic() [ Language = javascript ] { var form = zenPage.getComponentById('formBasic'); form.save(); }

В приведённом примере в методе SaveBasic() в переменную form запи-

сывается ссылка на объект формы formBasic, которая возвращается стандарт-ным методом объекта страницы getComponentById()1. Обращение к объекту страницы осуществляется через имя zenPage. И, наконец, вызывается метод save() объекта формы.

Рассмотрим другой пример использования методов экземпляра страни-цы, выполняемых на стороне клиента. На закладке tab2 размещено несколько взаимосвязанных элементов управления <dataCombo> (см. Рис. 3.24). Так, от выбранного региона зависит список городов, от которых в свою очередь − список школ.

Рис. 3.24 – Редактирование данных закладки «Школа»

Для выполнения правильного заполнения данными соответствующих элементов управления в нашем примере используется следующий приём. Данные в зависимом элементе управления обновляются при выборе значения в родительском. Пример кода для закладки tab2 (см. Рис. 3.24) приведён ни-же. <tab id="tab2" caption="Школа" onshow="zenPage.LoadSchool();"> <form id="formSchool" align="center" labelPosition="left" control-lerId="basic"> <html id="errSchool" hidden="true" enclosingStyle="color:red;"></html>

1 Дополнительные сведения о методах страницы можно найти в документации по Zen

82

<spacer height="10"/> <dataCombo id="region" label="Регион:" dropdownHeight="auto" sql="select id, Name from zen.Region ORDER BY Name" sqlLookup="select Name from zen.Region WHERE id=?" dataBinding="RegionSchool" size="27" showEmpty="false" onchange="zenPage.RegionChange();" /> <dataCombo id="city" label="Город:" sqlLookup="select Name from zen.City WHERE id=?" dropdownHeight="auto" dataBinding="CitySchool" size="27" showEmpty="false" onchange="zenPage.CityChange();" /> <dataCombo id="School" label="Школа:" sqlLookup="select Name from zen.School WHERE id=?" dropdownHeight="auto" dropdownWidth="170px" dataBinding="School" showEmpty="false" size="27"/> <dateText label="Дата окончания:" dataBinding="EndSchDate"/> <spacer height="30"/> <button caption="Сохранить" onclick="zenPage.SaveSchool();"/> </form> </tab>

Из примера видно, что по событию onchange элемента управления

"region" вызывается метод RegionChange(), который задаёт множество дан-ных для элемента управления "city" (см. код ниже). Аналогично заполняется элемент управления «School». /// запрос городов для выбранного региона Method RegionChange() [ Language = javascript ] { var region=zenPage.getComponentById('region'); var city=zenPage.getComponentById('city'); city.clearCache(); var sql= "select id, Name from zen.City where Region= " + re-gion.getProperty ('value') + " ORDER BY Name"; zenPage.SetSql(city,sql); }

83

В приведённом выше примере стандартным методом страницы получа-ем ссылки на элементы управления региона и города, очищаем содержимое элемента управления с городами, создаём строку SQL с условием выбора го-родов определённого региона и вызываем метод SetSql(), суть которого будет рассмотрена в следующем пункте.

3.8.2.2 Методы экземпляра, выполняемые на стороне сервера В продолжение разбора примера на странице EditProfile рассмотрим

случай, где требуется применение клиентского метода, вызываемого на сер-вере. Так, для установки нового значения свойства SQL для элемента управ-ления <dataCombo> воспользуемся методом, исполняемым на сервере. Ниже приведён код метода SetSql(), который выполняется на сервере, а вызывается в клиентском методе, исполняющемся на стороне клиента. /// Этот метод меняет своство SQL у заданного компонента dataCombo Method SetSql(DCombo As %ZEN.Component.dataCombo, sql As %String) [ZenMethod ] { set DCombo.sql=sql }

Рассмотрим ещё один метод, изменяющий статус на главной странице

(см. Рис. 3.16). Метод вызывается по нажатию на кнопку «Ок» (см. Рис. 3.25), его код приведён ниже.

Рис. 3.25 – Изменение статуса

/// Сохраняет новый статус из поля для редактирования Method StatusChange() [ ZenMethod ] { set newStatus=..%GetValueById("editStatus") do ##class(zen.Status).ChangeStatus(newStatus) set DateTime=$ZDATETIME($H,1,1) &js<var edit=zenPage.getComponentById("editStatus"); var current=zenPage.getComponentById("currStatus"); var groupEdit=zenPage.getComponentById("groupEdit"); var group=zenPage.getComponentById("group"); zenPage.getComponentById("date").setProperty ('value','#(DateTime)#'); current.setProperty('caption',edit.getProperty('value')); groupEdit.setProperty('hidden',1); group.setProperty('hidden',0);> }

84

В описанном выше методе получаем значение нового статуса из поля редактирования. Вызываем метод класса ChangeStatus(), изменяющего теку-щее значение статуса и записывающего его в историю (см. код ниже). Запи-сываем в переменную DateTime текущее значение даты и времени. Дескрип-тором &js<…> изменяем текущее значение на странице, скрываем поле ре-дактирования и кнопку и возвращаем страницу к исходному состоянию (см. Рис. 3.26) ClassMethod ChangeStatus(name As %String) As %Status { set pID = $Get(%session.Data("pId")) set per=##class(zen.Personal).%OpenId(pID) set per.Current=##class(zen.Status).%New() set per.Current.Name=name set per.Current.DateTime=$ZDATETIME($H,1,1) do per.Current.HistorySetObjectId(pID) set sc=per.Current.%Save() if $$$ISERR(sc) { &js<alert('Не сохранено!');> quit } do per.%Save() quit }

Рис. 3.26 – Новый текущий статус

3.8.3 ПЕРЕДАЧА ПАРАМЕТРОВ НА СТРАНИЦЫ Так же как и в технологии CSP в Zen существует два способа передачи

параметров на страницы: 1) передача через строку URL; 2) через свойство объекта %session (см. п. 3.9). В первом случае параметр может меняться каж-дый раз при переходе на страницу. Во втором, параметр действителен для всех страниц текущей сессии, но может меняться в рамках этой сессии.

Рассмотрим пример работы с параметром через строку URL. На стра-нице Friend при выборе записи в таблице (см. Рис. 3.22) можно по нажатию на кнопку «Профиль» переместиться на страницу Main для просмотра дан-ных выбранного в таблице человека. Для выполнения этого действия в кноп-

85

ке установлен параметр onclick (см. код ниже) равный имени функции, обес-печивающий переход на страницу Main. <button align="center" caption="Профайл" onclick="zenPage.Rowdbclick();"/>

Код функции приведён ниже. В этой функции получаем идентификатор

записи таблицы PersonalInGroup. Затем запросом получаем идентификатор человека, выбранного в таблице, и перемещаемся на страницу Main, указывая через знак «?» параметр и его значение. /// Переход на главную страницу, с выбранным ID пользователя Method Rowdbclick() [ ZenMethod ] { set id = ..%GetValueById("table") &sql(select PerIn into :pId from zen.PersonalInGroup where ID=:id) &js<window.location='zui.zen.Main.cls?ID=#(pId)#';> }

Для приёма входного параметра на странице Main создаётся свойство

objId, и указывается значение параметра ZENURL (см. код ниже). Когда про-исходит загрузка данной страницы, входящее значение ID записывается в свойство objId, которое может использоваться на странице. Property objId As %ZEN.Datatype.integer(ZENURL = "ID");

3.9 Безопасность в Zen В Zen существует три уровня безопасности: уровень приложения, уро-

вень страницы и уровень компонентов. На уровне приложения безопасность в Zen обеспечивается комбинаци-

ей Zen-форм и настроек конфигурации системы. По умолчанию, когда поль-зователь запускает Zen-приложение, то для него отображается стандартное окно Caché с запросом имени пользователя и пароля.

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

На уровне компонент свойством %resource класса %ZEN.Component.component определены разрешение или запрет на добавле-ние данного компонента (наследника %ZEN.Component.component) во мно-жество компонентов страницы для текущего пользователя. На Zen-странице в блоке XData Contents разрешения для компонента могут быть определены следующим образом.

86

<button id="myButton" caption="Press Me" resource="ADMIN" />

Это означает, что если пользователь обладает привилегиями на ресурс

«ADMIN», то кнопка «myButton» будет отображаться для этого пользователя на странице.

3.9.1 АВТОРИЗАЦИЯ В нашем примере была создана собственная форма авторизации (см.

Рис. 3.27). Её код приведён ниже. <vgroup id="vgroup" width="40%" valign="middle" align="center"> <html enclosingClass="subtitle">Вход</html> <form id="loginForm" align="center" labelPosition="top"> <spacer height="1em" /> <text id="Login" name="Login" label="Логин:" size="25"/> <password id="Password" name="Password" label="Пароль:" size="25"/> <spacer height="1em" /> <hgroup align="center"> <submit id="btLogin" caption="Вход"/> <button id="btLogging" caption="Регистрация" onclick= "win-dow.location= 'zui.zen.Registration.cls';"/> </hgroup> </form> </vgroup>

Для ввода пароля используется специальный элемент управления

<password>, который скрывает вводимые значения на интерфейсе.

Рис. 3.27 – Форма авторизации

При нажатии на кнопку «Вход» вызывается метод класса страницы (см. код ниже), который проверяет правильность логина и пароля, и в случае ус-пеха, выполняет авторизацию. Данные о пользователе (его id, имя и фами-лия) записываются в сессию. /// Этот метод вызывается по событию нажатия на кнопку «Вход»

87

ClassMethod %OnSubmit(ASubmit As %ZEN.Submit) As %Status { set id=##class(zen.Personal).GetUser(ASubmit.%GetValue("Login"),ASubmit.%GetValue("Password")) if (id=$$$NULLOREF) { do ASubmit.%SetError("Login","Неверно задан логин или пароль") } else { set user = ##class(zen.Personal).%OpenId(id) set %session.Data("pId")=id set %session.Data("Surname")=user.Surname set %session.Data("Name")=user.Name set ASubmit.%NextPage="zui.zen.Main.cls?ID="_id } quit $$$OK }

Код проверки правильности введённого имени и пароля приведён ниже

– это метод класса zen.Personal. /// проверка логина и пароля, открытие сессии ClassMethod GetUser(login As %String, password As %String) As %String { set id = ..FindPersonal(login) if id=$$$NULLOREF quit $$$NULLOREF set user=..%OpenId(id) if 'user.IsPasswordCorrect(password) quit $$$NULLOREF quit id } /// поиск пользователя ClassMethod FindPersonal(login As %String) As %String { set id="" &sql(SELECT ID INTO :id FROM zen.Personal WHERE (Login = :login)) quit id } /// проверка пароля Method IsPasswordCorrect(password As %String) As %Boolean { if $extract(..Password,1,4)="MD5:" { set code=$extract(..Password,5,$l(..Password)) if code=$SYSTEM.Encryption.MD5Encode(password) quit 1 else quit 0 } else { if ..Password=password quit 1

88

else quit 0 } quit 0 }

Для защиты хранения паролей и их идентификации применяется алго-

ритм MD51.

3.9.2 РЕГИСТРАЦИЯ Для работы с макетом социальной сети (для выполнения авторизации)

требуется предварительная регистрация в системе (см. Рис. 3.28). Здесь мы рассмотрим только часть кода, касающегося создания объекта zen.Personal, его логина и пароля (см. код ниже). <vgroup id="groupRight" width="100%" valign="top"> <html id="subtitle" enclosingClass="subtitle">Регистрация</html> <spacer height="10"/> <html id="error" align="center" enclosingStyle="color:red;"></html> <form id="formBasic" align="center" width="500px"> <group align="center"> <spacer height="40"/> <text label="Логин:" id="Login" name="Login" size="30" required="true" /> <hgroup> <password label="Пароль:" id="Password1" name="Password1" size="11" required="true" /> <spacer width="20"/> <password label="еще раз:" id="Password2" name="Password2" size="11" required= "true"/> </hgroup> <spacer height="10"/> <text label="Фамилия:" id="Surname" name="Surname" size="30" required="true" /> <text label="Имя:" id="Name" name="Name" size="30" required="true" /> <spacer height="40"/> </group>

… (пропущен код по вводу дополнительных данных при регистрации) <button align="center" caption="Регистрация" onclick= "zenPage.Registration();"/> </form>

При нажатии кнопки «Регистрация» выполняется следующий метод.

/// Регистрация

1 Message Digest 5 − 128-битный алгоритм хеширования, разработанный профессором Рональдом Л.

Ривестом из Массачусетского Технологического Института (MIT, Massachusetts Institute of Technlogy) в 1991 году. Предназначен для создания «отпечатков» или «дайджестов» сообщений произвольной длины.

89

Method Registration() [ ZenMethod ]

{ set p1 = ..%GetValueById("Password1")

set p2 = ..%GetValueById("Password2")

if p1'=p2 { &js<zenPage.getComponentById('error').setContent("Вы ввели два разных пароля");> quit } set login = ..%GetValueById("Login")

#; проверка на наличие пустых полей

if '(..CheckValue()) {

&js<zenPage.getComponentById('error').setContent("Заполните все поля!");> quit } #; если введенный Login существует – ошибка

set pId = ##class(zen.Personal).FindPersonal(login)

if (pId'="") {

&js<zenPage.getComponentById('error').setContent("Пользователь под таким логином уже существует! Введите другой.");>

quit } #; новый класс zen.Personal

set user=##class(zen.Personal).%New() set user.Login = login

set user.NewPassword = p1

set user.Surname = ..%GetValueById("Surname")

set user.Name = ..%GetValueById("Name")

&js<var formB = zenPage.getComponentById('formBasic');

var error = zenPage.getComponentById('error');

zenPage.getComponentById('subtitle').setContent("Регистрация завершена"); error.setProperty('enclosingStyle',"color:green;") error.setContent("Регистрация прошла успешно! Выберите в ме-ню 'вход' для входа");

formB.setHidden(1);> quit }

90

/// Проверка всех полей ввода

Method CheckValue() As %ZEN.Datatype.boolean [ ZenMethod ]

{ set p1 = ..%GetValueById("Password1")

set login = ..%GetValueById("Login")

set surname = ..%GetValueById("Surname")

set name = ..%GetValueById("Name")

if (login="")||(surname="")||(name="")||(p1="")||(sch="")||(univ="")||(fac="")||(spec="") {

quit 0 } quit 1 }

Рис. 3.28 – Регистрация пользователя

3.9.3 РАБОТА С АВТОРИЗИРОВАННЫМ ПОЛЬЗОВАТЕЛЕМ НА СТРАНИЦАХ

Информация об однажды авторизованном пользователе доступна на каждой странице. Так можно управлять доступам к данным на странице. Ес-ли, например данные доступны всем для просмотра, то идентификация поль-зователя не выполняется.

91

В нашем примере так сделано на странице zui.zen.People, на которой список людей отображается для всех пользователей (см. Рис. 3.23). Но, на этой же странице при выполнении операций «Добавить в друзья» или «Уда-лить из друзей» выполняется идентификация пользователей по данным сес-сии. Так, при выполнении операции удаления (см. код ниже) в первую оче-редь из объекта сессии запрашивается идентификатор пользователя. Если идентификатор в сессии отсутствует, то удаления не происходит. /// Удаление из друзей

Method DelFriend() [ ZenMethod ]

{ set id = ..%GetValueById("table")

set pId = $Get(%session.Data("pId"))

&sql(delete from zen.PersonalInGroup where (PerIn=:id and Per-sonal=:pId))

&sql(delete from zen.PersonalInGroup where (PerIn=:pId and Per-sonal=:id))

if SQLCODE { &js<alert('Не удалено!');>

} quit }

3.10 SVG-компоненты Масштабируемая векторная графика (SVG)1 − это язык, который по-

зволяет описывать двухмерную векторную графику в XML формате. Zen использует язык SVG для отображения графиков и индикаторов.

SVG-компоненты могут использоваться для отображения на страницах на-глядных индикаторных панелей, где показатели изменяются в реальном вре-мени. На основе стандартных SVG-компонентов можно создавать пользова-тельские компоненты. Все SVG-компоненты наследуют от %ZEN.SVGComponent.svgComponent.

3.10.1

ВИДЫ SVG-КОМПОНЕНТОВ В Zen существует пять групп SVG-компонентов:

1) Контейнеры размещения SVG-компонентов на странице (<svgFrame>, <svgGroup>, <svgSpacer>, <rect>).

2) Индикаторы (см. Табл. 3.5). Позволяют графически отображать зна-чения одного показателя (метрики). Каждая метрика является классом произ-

1 Спецификацию языка SVG можно посмотреть на сайте www.w3.org/TR/SVG/

92

водным от %ZEN.SVGComponent.meter, который генерирует требуемый SVG-код для отображения.

3) Графики и диаграммы (<lineChart>, <barChart>, <diffChart>, <hilowChart>, <xyChart>, <pieChart>) − представляют графически наборы данных. В Zen существует несколько видов таких компонентов, позволяю-щих представлять данные в виде линий, столбцов, приращений, отрезков, разброса точек и в виде долей целого.

4) Графы <radialNavigator>. Реализуются специальным SVG-компонентом, отображающим отношения между множеством элементов дан-ных. На этом графе центральный элемент окружён множеством равномерно отстоящих дуг от друга узлов. При клике мышью на одном из узлов он ста-новится центровым и вокруг него уже отображаются связанные с ним эле-менты.

5) <ownerDraw> − пустой SVG-компонент, который заполняется дина-мически. Табл. 3.5 – Индикаторы

<fue

lGau

ge>

<ind

icat

orLa

mp>

<lig

htB

ar>

<slid

er>

<sm

iley>

<spe

edom

eter

>

<tra

ffic

Ligh

t>

3.10.2

РАБОТА С SVG-КОМПОНЕНТАМИ Рассмотрим представление связей между людьми в виде графа. В на-

шем примере на странице Friend при нажатии на кнопку «Граф» осуществля-ется переход на страницу SVGBrowser, на которой отображается граф, в цен-тре которого расположен узел авторизованного в данный момент пользовате-ля (см. Рис. 3.29).

93

Рис. 3.29 – Граф связей между людьми

Код отображения такого графа приведён ниже. <vgroup valign="top" align="center"> <svgFrame id="svgFrame" layout="vertical" height="500" width="600" zoomWithWheel="true"> <radialNavigator id="navigator" height="500" width="600" backgroundStyle="fill:white;" onselectNode="zenPage.selectItem();"> </radialNavigator> </svgFrame> </vgroup>

Прорисовка графа происходит каждый раз при загрузке страницы. Для

этого переопределяется метод страницы onloadHandler(), в котором вызыва-ется метод заполнения данными графа (см. код ниже). /// Этот метод выполняется при каждой загрузке страницы Method onloadHandler() [ Language = javascript ] { zenPage.GetServer(); }

94

Method GetServer() [ ZenMethod ] { set personalId=%page.currItemId set you=$G(%session.Data("pId")) #; Определяем центральный узел графа set nav = ..%GetComponentById("navigator") &sql(select Name,Surname into :name,:surname from zen.Personal where id=:personalId) if personalId=you { set nav.mainLabel = name_" "_surname_" (это Вы)" } else { set nav.mainLabel = name_" "_surname } #; Определяем связанные с главным узлы графа #; этот запрос определяется в классе zen.PersonalInGroup set resSet = ##class(zen.PersonalInGroup).GetLinkNode(personalId) if (resSet){ while resSet.Next(){ set id = resSet.Get("ID") if id=you { set caption = resSet.Get("Name")_" "_resSet.Get("Surname")_" (это Вы)" } else { set caption = resSet.Get("Name")_" "_resSet.Get("Surname") } #; добавление узла radialNode в навигатор с определен-ным id и caption do %page.AddNode(id,caption) } } } /// Этот метод добавляет узел radialNode в навигатор Method AddNode(id As %ZEN.Datatype.string, cap-tion As %ZEN.Datatype.string) [ ZenMethod ] { &js<node = zenPage.createComponent('radialNode'); node.setProperty('value','#(id)#') node.setProperty('caption','#(caption)#') zenPage.getComponentById('navigator').appendNode(node);> }

Рассмотрим ещё один пример создания графиков. На странице Svg-

Chart отображаются графики возрастов пользователей системы, их полов и родов занятий (см. Рис. 3.30 − Рис. 3.32). Ниже приведён код для построения диаграммы возрастов (см. Рис. 3.30).

95

<barChart id="barChart" hidden="true" ongetData="return zenPage.getBarData(series);"

backgroundStyle="fill: url(#glow-blue);"

plotAreaStyle="fill:url(#glow-silverDiag);" title="Род занятий"

seriesColors="url(#glow-blueHz),url(#glow-redHz),url(#glow-greenHz)"

ongetLabelX="return zenPage.getBarLabels(value);"

seriesCount="1" marginLeft="5" chartStacked="false" plotStyle="stroke:black; stroke-width:0.1;"

width="600" height="400"> <xAxis/> </barChart>

/// запрос к серверу

Method getBarData(series) [ Language = javascript ]

{ return zenPage.GetBarServer(series).split("/"); }

/// Возвращает строку, по которой строится диаграмма

Method GetBarServer(aSeries As %Integer) As %String [ ZenMethod ]

{ &sql(select count(id) into :count from zen.Occupation)

set bar = ..%GetComponentById("barChart")

set bar.seriesSize=count &sql(select count(id) in-to :first from zen.Personal where Occupation=1)

set series=first for i=2:1:count { &sql(select count(id) in-to :occ from zen.Personal where Occupation=:i) set series=series_"/"_occ } q:aSeries=0 series

}

/// Labels для компонента barChart

96

Method getBarLabels(value) [ Language = javascript ]

{ return zenPage.GetOccupation(value); }

/// Возвращает строку, с labels для компонента barChart

Method GetOccupation(aValue As %Integer) As %String [ ZenMethod ]

{ &sql(select count(id) into :count from zen.Occupation)

set n=0 set resSet=##class(%ResultSet).%New() do resSet.Prepare("select Name from zen.Occupation")

do resSet.Execute() while resSet.Next(){ Set name(n) = resSet.Get("Name")

set n=n+1 } q name(aValue) }

Рис. 3.30 – Диаграмма возрастов

97

Рис. 3.31 – Диаграмма полов

Рис. 3.32 – Диаграмма родов занятий

98

3.11 Работа с отчетами Zen включает в себя набор средств, обеспечивающий процесс форми-

рования отчетов в формате XHTML и PDF. Процесс формирования отчетов в Zen состоит из трех шагов.

1) Создается класс отчета Zen, который включает описание двух XML документов: XData ReportDefinition (описывает данные) XData ReportDisplay (описывает формат отображения данных).

2) Генерируется результат путем включения URI класса отчета в браузер или путем вызова метода GenerateReport из командной строки или из прило-жения.

3) Класс отчета управляет данными и отображает информацию посред-ством одного из двух вариантов (см. Рис. 3.33).

Рис. 3.33 – Формирование отчета классом отчетов Zen

99

3.11.1 ПОРЯДОК ПОСТРОЕНИЯ ОТЧЕТОВ В ZEN В качестве примера рассмотрим приложение поставляемое Intersystems

вместе с Caché (http://localhost:57772/csp/samples/ZENDemo.Home.cls). Запус-тим Caché Studio, перейдем в область SAMPLES и создадим новый Zen-отчет (см. Рис. 3.34).

Рис. 3.34 – Создание нового Zen-отчета

В появившемся мастере отчетов заполним его параметры: «Имя паке-та» = «MyApp»; «Имя класса» = «ReportDemo»; «Имя отчета» = «MyReport». На следующем шаге мастера напишем SQL-запрос (см. код ниже), результат которого будет использован для отчета. SELECT ID,Customer,Num,SalesRep,SaleDate

FROM ZENApp_Report.Invoice ORDER BY SalesRep,Customer

После завершения работы мастера создастся Zen-страница отчета с предопределенными значениями параметров и XML-блоками XData ReportDefinition и XData ReportDisplay.

В блоке XData ReportDefinition в позиции <!--

add definition of the report here. --> нужно вставить свой код, оп-ределяющий структуру отчета. Отчет может содержать одну или более вло-

100

женных групп. Определим первую группы посредством тега <group> (см. код ниже). <report xmlns="http://www.intersystems.com/zen/report/definition"

name="MyReport"

sql="SELECT ID,Customer,Num,SalesRep,SaleDate

FROM ZENApp_Report.Invoice ORDER BY SalesRep,Customer" >

<group name="SalesRep" breakOnField="SalesRep">

</group>

</report>

После компиляции страницы и отображении ее в браузере можно уви-

деть следующий XML-код, представляющий структуру отчета, но без содер-жания данных. <?xml version="1.0" ?>

<MyReport>

<SalesRep />

<SalesRep />

<SalesRep />

<SalesRep />

<SalesRep />

<SalesRep />

</MyReport>

Добавим элемент <attribute> в группу.

<group name="SalesRep" breakOnField="SalesRep">

<attribute name="name" field="SalesRep" />

</group>

Результат такого отчет имеет следующий вид.

<?xml version="1.0" ?>

<MyReport>

<SalesRep name="Jack" />

<SalesRep name="Jen" />

<SalesRep name="Jill" />

<SalesRep name="Jim" />

<SalesRep name="Joanne" />

<SalesRep name="John" />

101

</MyReport>

Добавим элемент агрегации в группу.

<group name="SalesRep" breakOnField="SalesRep">

<attribute name="name" field="SalesRep" />

<aggregate name="total" type="SUM" field="Num" />

</group>

Отчет с наличием агрегирующего элемента будет выглядеть так. <?xml version="1.0" ?>

<MyReport>

<SalesRep name="Jack">

<total>742</total>

</SalesRep>

<SalesRep name="Jen">

<total>868</total>

</SalesRep>

<SalesRep name="Jill">

<total>809</total>

</SalesRep>

<SalesRep name="Jim">

<total>801</total>

</SalesRep>

<SalesRep name="Joanne">

<total>857</total>

</SalesRep>

<SalesRep name="John">

<total>979</total>

</SalesRep>

</MyReport>

Расширим структуру отчета следующим образом. <group name="SalesRep" breakOnField="SalesRep">

<attribute name="name" field="SalesRep" />

<aggregate name="total" type="SUM" field="Num" />

<aggregate name="average" type="AVG" field="Num" />

<aggregate name="clients" type="COUNT" field="Customer" />

102

<group name="SalesTo" breakOnField="Customer" >

<element name="customer" field="Customer" />

<attribute name="date" field="SaleDate" />

</group>

</group>

Теперь опишем спецификацию представления отчета. В блоке XData ReportDisplay добавим следующий код. <report xmlns="http://www.intersystems.com/zen/report/display"

name="MyReport" title="Tutorial Sales Report">

<body>

<p>Tutorial Sales Report</p>

</body>

</report>

Изменим значение параметра DEFAULTMODE на "html" и добавим таблицу в блок XData ReportDisplay (см. код ниже). В результате отчет будет выглядеть следующим образом (см. Рис. 3.35). <body>

<p>Tutorial Sales Report</p>

<group name="SalesRep" line="1px">

<table orient="row" width="4in">

<item field="@name" width="2in">

<caption value="Sales Rep:" width="2in"/>

</item>

<item field="total" formatNumber="##0.00">

<caption value="Total Value of Sales:"/>

</item>

<item field="clients">

<caption value="Number of Clients:"/>

</item>

</table>

</group>

</body>

103

Рис. 3.35 – Отчет в формате XHTML

Здесь <group name="SalesRep"> есть ссылка на элемент <SalesRep> в генерируемом XML, <item field="@name"> определяет синтаксис для ото-бражения данных атрибута name, <item field="total"> определяет син-таксис отображения элемента <total>.

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

1 http://localhost:57772/csp/docbook/DocBook.UI.Page.cls?KEY=GZEN_reports

104

4 PROTOTYPE 6 Prototype 6 является надстройкой над технологией Zen с открытым ис-

ходным кодом1, позволяющей увеличить скорость разработки Zen-приложений, в которой реализованы следующие возможности:

– Созданы новые элементы управления и расширены возможности ком-понентов Zen;

– Обеспечена ссылочная целостность между связанными объектами; – Ведётся протоколирование действий пользователей с данными; – Существует механизм разработки отчётов в форматах OpenOffice2, MS

Office, PDF, XML, HTML; – Реализована интеграция Caché с системой версионного контроля

SVN3.

4.1 Архитектура Prototype 6 Prototype 6 занимает место в единой архитектуре Caché, как расшире-

ние технологии Zen путём увеличения компонент и расширения возможно-стей существующих компонент (см. Рис. 4.1).

Если рассматривать архитектуру Prototype 6 более детально относи-тельно архитектуры Zen (см. раз. 3.1), то мы увидим надстройки по несколь-ким позициям (см. Рис. 4.2).

В Prototype 6 был создан собственный класс модели данных, унаследо-ванный от стандартного класса %ZEN.DataModel.Adaptor и адаптированный для работы с контроллером и компонентами Prototype 6. Соответственно был создан собственный контролер, обеспечивающий связь новой модели данных с новыми элементами Prototype 6. Визуальные элементы управления, вклю-чая саму страницу, а также классы типов данных были разработаны на базе классов Zen. На базе хранимых классов CSP были разработаны функции обеспечения протоколирования и контроля целостности.

1 http://groups.google.com/group/prototype6-ru?hl=ru 2 (OOo, OO.o) Это свободный пакет офисных приложений, разработанный с целью предоставить

альтернативу Microsoft Office как на уровне форматов, так и на уровне интерфейса пользователя 3 Subversion — свободная централизованная система управления версиями

105

Рис. 4.1 – Место Prototype 6 в единой архитектуре Caché

Рис. 4.2 – Архитектура Prototype 6

4.2 Установка и настройка Prototype 6 Чтобы установить Prototype 6 сначала отредактируем файл

installRunInstaller.scr из поставки p6package, где во второй строке укажем полный путь месту расположения установочных файлов (см. Рис. 4.3).

106

Рис. 4.3 – Установка пути для инсталляционного пакета

Затем в Caché Terminal выполним из файла installRunInstaller.scr сцена-рий, для чего в меню терминала выберем пункт меню «Файл → Сценарий» (см. Рис. 4.4).

Рис. 4.4 – Выполнение сценария в терминале при установке Prototype 6

После запуска сценария нужно указать значения некоторых параметров и ответить на несколько вопросов. Перечень шагов по выполнению сценария представлен в Табл. 4.1. Табл. 4.1 – Перечень действий при выполнении сценария № Действие Описание 1 Namespace[P6]: Вводится наименование области Caché для развёрты-

вания Prototype 6. Если не указать, по умолчанию соз-даётся область с наименованием «P6».

107

№ Действие Описание 2 Routine DB

name[P6]: Вводится наименование базы данных для программ. Если не указать, по умолчанию создаётся база с на-именованием «P6».

3 Routine DB directory:P6

Вводится наименование папки, в которой будет хра-ниться база.

4 Global DB name[P6USER]:

Вводится наименование базы данных для глобалов. Если не указать, по умолчанию создаётся база с на-именованием «P6USER».

5 Source directory(they must be empty) (emp-ty-don't copy)

Вводится путь, по которому будут размещены исход-ные файлы Prototype 6.

6 Create CSP applica-tion? (if not copy source, copy "csp" dir from "sources" )[y]:

Создавать или нет приложение («y» - создавать).

7 CSP application name[/csp/p6]:

Вводим наименование приложения.

8 Press <Enter> to start or <Q> to exit:

После нажатия Enter происходит сборка всех классов.

Последнюю версию Prototype 6 и инструкции по установке можно за-

грузить по следующему адресу: http://code.google.com/p/prototype6/wiki/ HomePage.

4.3 Блоки Prototype 6 для быстрой разработки приложений Для быстрой разработки типичных функций приложений, автоматизи-

рующих организационные и управляющие процессы, в Prototype 6 существу-ет ряд специальных блоков, таких как – главное меню, справочник, документ, таблица.

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

– Работа со справочниками (города, организации, транспортные средст-ва);

– Работа с накладной на транспортировку груза; – Операции над документами (отметка об отправки, отметка о доставке,

печать накладной, поиск накладных); – Формирование отчетов (объем перевезенного груза транспортным

средством за период времени, график объема перевозок по городам за период времени).

108

Структура хранимых классов для логистической системы приведена на рисунке (см. Рис. 4.5).

Articles(from examp)

ArticlesInInvoice(from rep)

0..1 +Articles0..1

Transport(from examp)

Invoice(from examp)

0..1

+Invoice

0..1

0..1+Transport

0..1

Товар

Организация

Товары в накладной

Город

Транспортное средство

Накладная

Org(from examp)

0..1+Consignee

0..1

0..1+Consignor

0..1

City(from examp)

0..1+Destination 0..10..1+City0..1

Рис. 4.5 – Диаграмма хранимых классов логистической системы

4.3.1 ГЛАВНОЕ МЕНЮ В Prototype 6 страница главного меню наследуется от страницы

Prototype 6 (p6.zen.Page), в которой реализован механизм авторизации. Возь-мём за основу уже реализованное в Prototype 6 меню и адаптируем к нашему примеру (см. Рис. 4.6). Код блока XData Contents класса zui.Menu для приме-ра приведён ниже. XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen" title="Prototype6" height="58%"> <hgroup width="100%" valign="top"> <vgroup width="25%"> </vgroup> <vgroup width="50%"> <html align="center"><h2>Логистическая информационная система</h2></html> </vgroup>

109

<vgroup> <html OnDrawContent="DrawGreeting" align="right" /> </vgroup> </hgroup> <hgroup width="100%" valign="top"> <hmenu width="100%" id="menu"> <menuItem caption="Накладная" onclick="zenLaunchPopupWindow ('p6.zen.RefBook.cls?ClassName=examp.Invoice','Invoice', GetWindowFea-tures());" /> <menuItem caption="Поиск накладных" onclick= "zenLaunchPopupWindow ('zui.examp.rep.InvoiceRegister.cls','InvoiceRegister', GetWindowFea-tures());" /> <vmenu align="center" caption="Отчеты"> <menuItem caption="Объем перевезенного груза ТС" onclick= "zenLaun-chPopupWindow ('zui.examp.rep.PeriodDate.cls', 'PeriodDate', GetWindow-Features());" /> <menuItem caption="График" onclick="zenLaunchPopupWindow('zui. examp.rep. CityTraffic.cls','CityTraffic',GetWindowFeatures());" /> </vmenu> <vmenu align="center" caption="Справочники"> <menuItem caption="Города" onclick="zenLaunchPopupWindow('p6.zen. RefBook.cls?ClassName=examp.City','City',GetWindowFeatures());" /> <menuItem caption="Организации" onclick="zenLaunchPopupWindow('p6. zen.RefBook.cls?ClassName=examp.Org','Org',GetWindowFeatures());" /> <menuItem caption="Товар" onclick="zenLaunchPopupWindow('p6.zen.RefBook.cls?ClassName=examp.Articles','Articles',GetWindowFeatures());" /> <menuItem caption="Транспорт" onclick="zenLaunchPopupWindow('p6. zen.RefBook.cls?ClassName=examp.Transport','Transport',GetWindowFeatures());" /> </vmenu> <vmenu align="center" caption="Режимы"> <menuItem caption="Настройки приложения" onclick="zenLaunchPopup Window('zui.admin.AppSet.cls');" /> </vmenu> <menuItem caption="Завершить работу" align="right" onclick="zenPage.finishSession()" containerStyle="color:black; width:100%;" enclosingStyle="background: yellow;" /> </hmenu> </hgroup> <html OnDrawContent="DrawVersion" align="right" /> </page> }

110

В момент запуска страницы главного меню, Prototype 6 потребует авто-ризацию (см. Рис. 4.7), где нужно ввести логин: demo и пароль: demo. Это пользователь по умолчанию с правами администратора, который устанавли-вается вместе с Prototype 6.

Рис. 4.6 – Главное меню логистической системы

Рис. 4.7 – Авторизация

4.3.2 СПРАВОЧНИКИ Справочник в Prototype 6 реализован специальным классом страницы

p6.zen.RefBook, на которой используется компонент p6.zen.component.refBook.

111

Справочник Prototype 6 позволяет одной строкой кода создать набор функ-ций, позволяющих отображать список объектов, удалять объекты и выгру-жать список в документы MS Office.

В нашем примере реализовано несколько справочников, и весь код на-писан только на странице главного меню. Например, справочник городов (см. Рис. 4.8) реализуется так (см. код ниже). <menuItem caption= "Города" onclick= "zenLaunchPopupWindow ('p6.zen.RefBook.cls?ClassName=examp.City','City',GetWindowFeatures());" />

Из приведённого выше кода видно, что по клику на меню «Города»

происходит вызов окна посредствам функции zenLaunchPopupWindow, в ко-торую передаются следующие параметры:

– Строка, определяющая содержимое для отображения в окне. В нашем случае это URL класса p6.zen.RefBook.cls с параметром ClassName, которому присвоено значение класса городов examp.City.

– Строка, которая идентифицирует окно. – Строка со списком атрибутов окна функции JavaScript window_open(),

разделёнными запятыми. В нашем примере этот список возвращается функ-цией GetWindowFeatures().

Рис. 4.8 – Справочник городов

По умолчанию наименования полей справочника соответствуют на-именованиям описаний свойств класса. Если описания свойств класса отсут-ствуют, то в наименования полей таблицы справочника подставляются на-именования свойств класса.

Для того чтобы заработали функции редактирования объектов ( ) и создания новых ( ) нужно создать соответствующую страницу. Воспользу-емся мастером создания страниц (Crtl+N см. Рис. 4.9, Рис. 4.10) для справоч-ника городов.

112

Рис. 4.9 – Создание страницы Prototype 6

Рис. 4.10 – Мастер создания страницы Prototype 6

113

Скомпилируем полученный код (см. ниже) и убедимся, что функции добавления и редактирования объектов заработали (см. Рис. 4.11). /// Редактирование города Class zui.examp.City Extends p6.zen.EditPage { Parameter PAGENAME; XData editPane [ XMLNamespace = "http://www.intersystems.com/zen" ] { <pane xmlns="http://www.intersystems.com/zen" xmlns:p6= "http://www.dimas.ru/p6" id="editPane"> <p6:dataController id="source" modelClass= "examp.City" modelId= "#(%page.objectId)#"/> <p6:dynaForm id="myForm" layout="vertical" labelPosition="left" controllerId="source"> </p6:dynaForm> </pane> } }

Рис. 4.11 – Страница редактирования объекта справочника городов

Если не требуется реализации дополнительных функций и возможно-стей на странице редактирования, то данный код (см. выше) может оставать-ся без изменения.

Создание сложных справочников аналогично созданию простым, толь-ко в форме редактирования кроме простых полей будут присутствовать и выпадающие списки, как например, в справочнике организаций (см. Рис. 1.12). А в списке объектов в таких столбцах будут отображаться идентифика-торы (Id) связанных объектов (см. Рис. 4.13). Настройка таблиц для отобра-жения нужных данных вместо Id будет рассмотрено в следующем разделе (см. раз. 4.3.3).

114

Рис. 4.12 – Страница редактирования справочника организаций

Рис. 4.13 – Справочник организаций

4.3.3 ДОКУМЕНТ PROTOTYPE 6 Документом в Prototype 6 называется страница, на которой размещает-

ся отображение (интерфейс) реального документа, т.е. набор элементов управления, таблиц и разделов, соответствующих элементам реального до-кумента. В качестве шаблона для такой страницы в Prototype 6 используется класс p6.zen.EditPage.

В нашем примере документом является накладная на транспортировку груза, на которой будет описываться работа с документами Prototype 6. Вы-зов из меню списка документов выполняется аналогично вызову справочни-ков (см. код ниже) через класс p6.zen.RefBook.cls. <menuItem caption= "Накладная" onclick= "zenLaunchPopupWindow ('p6.zen.RefBook.cls?ClassName=examp.Invoice','Invoice',GetWindowFeatures());" />

В отличие от справочника страницу редактирования документа прихо-

дится разрабатывать вручную, т.к. её структура отличается от типичных справочников, для которых можно использовать шаблон.

115

В нашем примере на странице редактирования накладной есть два бло-ка XData: XData editPane, в котором расположена редактируемая часть доку-мента; и XData refBook, в котором настраивается таблица отображения спи-ска документов. Второй блок (XData refBook) создаётся в случае, если требу-ется настраивать таблицу для отображения списка объектов на странице, вы-зываемой через класс p6.zen.RefBook.cls (см. код выше). Аналогичный подход применим и для настройки таблиц справочников.

В нашем примере таблица накладных настраивается следующим обра-зом (см. Рис. 4.14). XData refBook [ XMLNamespace = "http://www.intersystems.com/zen" ] { <composite xmlns="http://www.intersystems.com/zen" xmlns:p6= "http://www.dimas.ru/p6" id="editPane"> <p6:refBook className="examp.Invoice"> <p6:tablePane> <p6:column field="Aka"/> <p6:column field="DateSend"/> <p6:column field="Destination->Aka"/> <p6:column field="Consignor->Aka"/> <p6:column field="Consignee->Aka"/> <p6:column field="Transport->Aka"/> <p6:column field="Send"/> <p6:column field="Delivered" /> </p6:tablePane> </p6:refBook> </composite> }

Рис. 4.14 – Список накладных

Поскольку страница редактирования документа создаётся вручную, а в нашем случае требуется кроме полей документа создать список отгружаемо-го товара, то для создания такой страницы будем использовать компоненты <form> (для редактирования полей документа) и <p6:refBook> (для редакти-рования списка товаров). Связь списка товаров с объектом накладной осуще-ствляется путём задания условия where и параметра urlParam (см. код ниже). Сама страница редактирования накладной представлена на рисунке (см. Рис. 4.15).

116

XData editPane [ XMLNamespace = "http://www.intersystems.com/zen" ] { <pane xmlns="http://www.intersystems.com/zen" xmlns:p6= "http://www.dimas.ru/p6" id="editPane"> <p6:dataController id="source" modelClass= "examp.Invoice" modelId= "#(%page.objectId)#"/> <form id="myForm" layout="vertical" labelPosition="left" controllerId="source" align= "center" width="50%"> … Поля документа (накладной) … <p6:refBook className="examp.rep.ArticlesInInvoice" where="Invoice= #(%page.objectId)#" urlParam="'aInvoice='+zenPage.objectId"> <p6:tablePane height="100" align="left"> <p6:column field="Articles->Aka"/> <p6:column field="Quantity"/> <p6:column field="Articles->Price" hidden="true"/> <p6:column header="Стоимость" colName="Articles->Price*Quantity as Стоимость"/> </p6:tablePane> </p6:refBook> <p6:buttonsEdit /> </form> </pane> }

Рис. 4.15 – Страница редактирования накладной

117

Любое поле с выпадающим списком на странице редактирования мо-жет быть настроено для отображения и поиска. По умолчанию отображается поле с наименованием, а поиск выполняется по идентификатору и наимено-ванию (ищет любое совпадение набранного текста (см. Рис. 4.16)).

Рис. 4.16 – Стандартный поиск в выпадающем списке

Для настройки отображения выпадающего списка и поиска по этому списку нужно в исходном классе (хранимый класс) переопределить методы GetDisplayValue и GetLookupColumns соответственно.

Предположим, что на странице редактирования накладной в полях «Грузополучатель» и «Грузоотправитель» кроме наименования организации нужно отображать и наименование города, в котором расположена организа-ция, и осуществлять поиск по обоим полям (см. Рис. 4.17). Для этого, как бы-ло отмечено выше, переопределим указанные методы (см. код ниже). /// This method return value which is displayed in textRefBook fields ClassMethod GetDisplayValue(aId As %String) As %String { set obj=..%OpenId(aId) quit obj.Aka_"("_obj.City.Aka_")" } /// This method return columns, which are used to find objects

118

while user type in p6:textRefBook /// Format: Property1[:Collation1],Property2[:Collation2],... /// Where Collation is one of Exact,StartsWith,Like. Default is Like. ClassMethod GetLookupColumns() As %String { quit "Aka:Должность:Like,City->Aka:Город:Like" //Exact - точное совпадение, StartsWith - совпадение в начале, Like - любое совпадение }

Рис. 4.17 – Настроенные выпадающие списки в накладной

4.3.4 ПОИСК В ДОКУМЕНТЕ PROTOTYPE 6 Для реализации поиска в Prototype 6 используется страница редактиро-

вания, класс которой наследует от p6.zen.EditPage. Но в отличие от стандарт-ной страницы, на этой контроллер использует заранее подготовленную мо-дель данных, на базе которой формируется сектор поиска на странице. В на-шем примере реализована страница поиска для накладных, на которой сектор поиска выглядит так (см. Рис. 4.18), а его код приведён ниже. <pane xmlns="http://www.intersystems.com/zen" xmlns:p6="http://www.dimas.ru/p6" id="editPane"> <p6:dataController id="source" modelClass= "ex-amp.rep.InvoiceRegister" modelId= "#(%page.objectId)#"/> <fieldSet legend="Поиск" width="100%"> <form id="myForm" layout="vertical" labelPosition="left" controllerId="source" width="100%"> <p6:vgroup> <p6:hgroup align="left" width="30%"> <p6:calendar dataBinding="BeginDate" /> <p6:calendar dataBinding="EndDate" />

119

</p6:hgroup> <p6:hgroup width="100%"> <p6:textRefBook dataBinding="Consignor" /> <p6:textRefBook dataBinding="Consignee" /> <p6:textRefBook dataBinding="Destination" /> </p6:hgroup > </p6:vgroup> <hgroup align="center" cellStyle="padding:0.5em;" > <button caption="Запрос" onclick="zenPage.btRegister()"/> <button caption="Выход" onclick="CloseWindow()"/> </hgroup> </form> </fieldSet> … </pane>

Рис. 4.18 – Условия поиска накладных

Ниже приведён код модели данных для сектора поиска накладных. Кроме свойств модели данных, которые используются для задания условий поиска (см. Рис. 4.18), в модели определяется метод GetFilter(), который воз-вращает строку условий поиска. Class examp.rep.InvoiceRegister Extends p6.zen.DataModel { /// Грузоотправитель Property Consignor As examp.Org; /// Грузополучатель Property Consignee As examp.Org; /// Пункт назначения Property Destination As examp.City; /// Дата отправки с Property BeginDate As p6.dt.Date; /// по Property EndDate As p6.dt.Date;

120

Method GetFilter() As %String { s f=##class(p6.FilterUtils).%New() s result="" s result=f.FormPeriodConditionSql("DateSend", ..BeginDate,..EndDate) if ..Consignor]"" s result=f.Add(result,"Consignor-> ID="_..Consignor.%Id()_"") if ..Consignee]"" s result=f.Add(result,"Consignee-> ID="_..Consignee.%Id()_"") if ..Destination]"" s result=f.Add(result,"Destination-> ID="_..Destination.%Id()_"") quit result } }

Далее для того чтобы результат поиска мы смогли увидеть в виде таб-

лицы, на этой же странице компонентом p6:refBook создаётся такая таблица. Для отображения списка объектов, соответствующих заданным условиям, у таблицы устанавливается параметр whereClause, которому присваивается значение строки, формируемой методом GetFilter() класса модели данных examp.rep.InvoiceRegister (см. код ниже). Результат поиска представлен на рисунке (см. Рис. 4.19). … <p6:refBook id="refbook" className="examp.Invoice" hidden="true" width="100%"> <p6:tablePane id="table" initialExecute="false" orderByClause="Aka"> <p6:column field="Aka"/> <p6:column field="DateSend"/> <p6:column field="Destination->Aka"/> <p6:column field="Consignor->Aka"/> <p6:column field="Consignee->Aka"/> <p6:column field="Transport->Aka"/> <p6:column field="Send"/> <p6:column field="Delivered" /> </p6:tablePane> </p6:refBook> … Method btRegister() [ Language = javascript ] { var refbook=zenPage.getComponentById("refbook"); var table=zenPage.getComponentById("refbook").getChildById("table"); table.whereClause=zenPage.GetWhere(); table.executeQuery(); refbook.setHidden(0); }

121

Method GetWhere() As %String [ ZenMethod ] { set Controller=..%GetComponentById("source") set Obj=Controller.GetObject() quit Obj.GetFilter() }

Рис. 4.19 – Результат поиска списка накладных

4.4 Компоненты Prototype 6 Все компоненты Prototype 6 являются аналогами компонентов Zen, ко-

торые берут данные из словаря классов и подставляют нужные значения по умолчанию.

p6:addDynaGrid − редактируемая таблица.

p6:buttonsEdit – стандартный набор копок сохранить, удалить, редак-

тировать и выйти.

122

Элемент управления p6:buttonsEdit должен размещаться внутри компо-

нента <form>.

p6:calendar – форматированный ввод даты.

p6:checkbox – компонент логического выбора.

В элементе управления логического выбора автоматически заполняет-

ся свойство caption в соответствии с описанием класса.

p6:combobox – выпадающий список.

В выпадающем списке автоматически заполняются поля displaylist и

valuelist в соответствии с описанием класса.

p6:dataController – компонент управления моделью данных и отобра-жением данных на интерфейсе (аналог компонента Zen dataController). До-бавлены удобные методы GetObject() и SetObject, исправлены ошибки Zen.

p6:dynaForm − динамическая форма на базе компонентов Prototype 6.

p6:hgroup − вертикальная группа, позволяющая размещать элементы управления более эргономичней чем в Zen. Все элементы управления зани-мают одинаковое количество места на странице. Длина элемента управления регулируется параметром slice.

p6:refBook – компонент, обеспечивающий отображение таблицы, кно-пок управления и разбивку данных по страницам. p6:refBook получает объек-ты из базы данных и управляет их созданием, редактированием и удалением.

123

Каждая кнопка может принимать следующие значения:

– пусто (кнопка отображается и выполняет действие по умолчанию); – минус (кнопка не отображается); – другое значение (кнопка отображается и выполняет указанный код на

JavaScript). В p6:refBook существует следующий набор кнопок, элементов управ-

ления и параметров: – Закрыть; btClose as %ZEN.Datatype.string [ InitialExpression = "-" ];

– Удалить; btDelete as %ZEN.Datatype.string;

– Редактировать; btEdit as %ZEN.Datatype.string;

– Создать; btNew as %ZEN.Datatype.string;

– Печать в Excel ; btPrintExcel as %ZEN.Datatype.string;

– Печать в Word; btPrintWord as %ZEN.Datatype.string;

– Выбрать; btReturn as %ZEN.Datatype.string;

– Навигатор; showNavigator as %ZEN.Datatype.boolean [ InitialExpression = 1 ];

– Дополнительные параметры, передаваемые в Url страниц создания и редактирования;

urlParam as %ZEN.Datatype.string;

124

p6:tabGroup – компонент группировки элементов управления на за-кладках. Отличается от аналогичного компонента Zen наличием проверки на существование объекта. Если форма еще не сохранена, вместо закладок пока-зывается кнопка «сохранить и показать кнопки».

p6:tablePane – таблица, аналогичная tablePane с установленными зна-

чениями по умолчанию.

p6:tablePane позволяет отображать итоговые суммы по столбцам таб-

лицы. Поля таблицы создаются посредствам компонента p6:column, который обладает следующим набором свойств:

– field as %ZEN.Datatype.string (используется вместо colName и colEx-pression и поддерживает синтаксис с «->»);

– isAggregated as %ZEN.Datatype.boolean [ InitialExpression = 0 ] (ото-бражать итоговую сумму).

p6:text – текстовое поле, аналогичное text в Zen. p6:textRefBook – поле с возможностью поиска и выбора данных из

справочников Prototype 6. В момент выборки данные в справочнике могут редактироваться и добавляться.

p6:vgroup – композитный компонент вертикальной компоновки эле-ментов управления, аналогичный vgroup в Zen.

125

p6:view – компонент отображения значения без возможности редакти-рования.

4.5 Специальные возможности Prototype 6

4.5.1 ОБЕСПЕЧЕНИЕ ССЫЛОЧНОЙ ЦЕЛОСТНОСТИ В Prototype 6 ссылочная целостность обеспечивается классом

p6.Integrity, от которого наследуют хранимые классы приложения. Информа-ция о ссылках хранится в глобале вида: ^admin.Integrity("S", className, ID, refClassName, refID) ^admin.Integrity("D", refClassName, refID, ClassName, ID)

Здесь "S" − ветвь, работа с которой ведется через метод Save(), а "D" − ветвь, работа с которой ведется через метод DeleteId(). В объекте класса className с Id=ID есть ссылка на объект класса refClassName c Id=refID.

Рассмотрим в качестве примера отношение между классами examp.Org (организация) и examp.Invoice (счет-фактура), которые наследуются от p6.Integrity. В классе examp.Invoice есть свойство Consignee (получатель) ти-па examp.Org. Запустим терминал и выполним действия по созданию новой накладной и привязке к ней организации получателя (см. код ниже). > set org = ##class(examp.Org).%OpenId(2) > set inv = ##class(examp.Invoice).%New() > set inv.Aka = “230” > set inv.Consignee = org > w inv.Save() 1 > w sf.%Id() 9

После этих действий в глобале ^admin.Integrity добавятся следующие

узлы: ^admin.Integrity("S", “examp.Invoice”, 9, “examp.Org”, 2) ^admin.Integrity("D", “examp.Org”, 2, “examp.Invoice”, 9)

При попытке удаления организации с Id=2 – программа проверит ветку “D” – и выдаст ошибку о том, что данный объект нельзя удалить (см. Рис. 4.20), т.к. на него есть ссылка в объекте класса examp.Invoice c Id=9. После удаления счет-фактуры с Id=9 оба этих узла глобала будут удалены, после чего можно будет удалить и организацию.

Таким образом, для обеспечения поддержки ссылочной целостности, хранимые классы должны наследоваться от p6.Integrity. При этом работа с объектами должна вестись не через стандартные методы %Save() и

126

%DeleteId(), а через методы Save() и DeleteId() соответственно, которые оп-ределены в данном классе.

Иногда требуется отслеживать ссылочную целостность не для всех объектных свойств класса. Для этого в классе существует параметр NotIntegrityProperties, который позволяет задать все свойства, не требующие обеспечения ссылочной целостности посредствам перечисления их наимено-ваний в строке (см. код ниже). Parameter NotIntegrityProperties = "FieldName1,FieldName2,…";

Рис. 4.20 – Обеспечение ссылочной целостности при удалении организации

4.5.2 ПРОТОКОЛИРОВАНИЕ По умолчанию в Prototype 6 все классы, унаследованные от p6.Integrity

или p6.Persistent, имеют функционал ведения хронология изменения объек-тов этих классов. Параметр LOGDETALISATION, определяющий вести хро-нологию или нет, и параметр ведения детализации LOGDETALISATIONWITHSTATE, находятся в классе p6.Persistent. В классах наследниках при необходимости эти параметры могут быть переопределены, а их значения заданы вновь.

Результаты изменения данных (хронологию) можно просмотреть через страницу редактирования справочника или документа, посредствам кнопки «История» с пиктограммой . Ниже приведён пример истории изменения накладной с номером 226 (см. Рис. 4.21).

127

Рис. 4.21 – История изменения накладной с Id=7

4.6 Разработка отчётов

4.6.1 АРХИТЕКТУРА МЕХАНИЗМА ФОРМИРОВАНИЯ ОТЧЁТОВ Как было отмечено в начале этой главы, Prototype 6 предоставляет про-

граммисту механизм, позволяющий разрабатывать отчёты в форматах OpenOffice, MS Office, PDF, XML и HTML. Сгенерированный компонентами Prototype 6 объём данных с определённой структурой посредствам JOOReports помещается в заранее подготовленный шаблон в одном из форматов (см. Рис. 4.22).

Рис. 4.22 – Архитектура механизма отчётов

Последовательность формирования отчёта в Prototype 6 приведена ни-же (см. Рис. 4.23)

Классы Caché

CSP

ZEN

Prototype 6 Генерация отчётов JOOReports

Java

Создание шаблоновформ

OpenOffice

128

Рис. 4.23 – Формирование отчётов в Prototype 6

4.6.2 СОЗДАНИЕ ОТЧЁТОВ Формирование отчёта начинается с создания страницы, на которой

описывается структура отчёта в блоке XData ReportDefinition. Для описания структуры используются компоненты <report>, <parameter>, <element> и <aggregate>. В Prototype 6 на странице, предназначенной для создания отчё-та, кроме его структуры нужно задать параметры DEFAULTMODE (тип отчё-та) и TEMPLATE (путь до шаблона документа OO).

Рассмотрим в качестве примера формирование печатной формы на-кладной, которая может быть напечатана по кнопке «Печать» на странице редактирования этой накладной (см. Рис. 4.15). Сначала получим отчёт в формате XML, для чего укажем параметру DEFAULTMODE значение “xml”. Код страницы формируемого отчёта приведён ниже. /// Печать накладной Class zui.examp.rep.Invoice Extends p6.zen.ReportPage { /// Имя приложения, которому принадлежит этот отчет. Parameter APPLICATION = "zui.Application"; /// Это способ отображения по умолчанию для этого отчета. Parameter DEFAULTMODE = "xml"; /// Это опциональная XML область, используемая для отчета. Parameter REPORTXMLNAMESPACE; Property InvoiceID As %ZEN.Datatype.integer(ZENURL = "aInv"); Parameter TEMPLATE As String = "C:\InterSystems\Cache2\CSP\P6\invoiceRep.odt"; /// Этот XML блок описывает логическое содержание этого отчета. XData ReportDefinition [ XMLNamespace = "http://www.intersystems.com/zen/report/definition" ] {

129

<report xmlns="http://www.intersystems.com/zen/report/definition" name="rep" sql="SELECT Invoice->Aka as Number,Invoice->Consignee->Aka as Consignee,Invoice->Consignor->Aka as Consignor,Invoice->Consignor->Address as Address,Invoice->DateSend,Invoice->Destination->Aka as Destination,Invoice->Transport->Aka as Transport, Articles->Aka as Name, Quantity,Quantity*Articles->Price as Cost FROM ex-amp.ArticlesInInvoice where Invoice->ID=?"> <parameter expression="..InvoiceID"/> <element name="aNum" field="Number"/> <element name="aConsignee" field="Consignee"/> <element name="aConsignor" field="Consignor"/> <element name="aDateSend" field="DateSend"/> <element name="aAddress" field="Address"/> <element name="aDestination" field="Destination"/> <group name="gArticles" breakOnField="Name"> <element name="aName" field="Name"/> <element name="aLW" field="Quantity"/> <element name="aPrice" field="Cost"/> </group> <aggregate field="Quantity" type="SUM" name="aMass"/> <aggregate field="Cost" type="SUM" name="allPrice"/> </report> } }

Результатом выполнения приведённого выше кода после нажатия на

кнопку «Печать» будет следующий XML-блок. <?xml version="1.0" encoding="UTF-8" ?>

<rep>

<aNum>225</aNum>

<aConsignee>Магазин "Продукты"</aConsignee>

<aConsignor>Хлебозавод</aConsignor>

<aDateSend>24.08.08</aDateSend>

<aAddress>Куйбышева 34а</aAddress>

<aDestination>Красноярск</aDestination>

<gArticles>

<aName>Мука 1сорт</aName>

<aLW>12</aLW>

<aPrice>2400</aPrice>

<aNum>225</aNum>

<aConsignee>Магазин "Продукты"</aConsignee>

<aConsignor>Хлебозавод</aConsignor>

<aDateSend>24.08.08</aDateSend>

130

<aAddress>Куйбышева 34а</aAddress>

<aDestination>Красноярск</aDestination>

</gArticles>

<gArticles>

<aName>Хлеб</aName>

<aLW>543</aLW>

<aPrice>8145</aPrice>

</gArticles>

<aMass>555</aMass>

<allPrice>10545</allPrice>

</rep>

Из приведённого выше результата формирования отчёта можно заме-

тить особенность представления данных в XML. Так, в группу gArticles кро-ме элементов этой группы попадают элементы родительского узла. Хоть та-кое представление данных и не очень удобно с точки зрения пользователя, но на формирование отчёта в формате OO это никак не влияет.

Для того чтобы получить отчёт в формате OO нужно подготовить шаб-лон, в который будут выгружаться данные и поменять значение параметра DEFAULTMODE на "odt".

Создадим шаблон в OpenOffice Writer и поместим его в область Caché, где установлен наш пример (расположение шаблона может быть любым). В случае с накладной шаблон отчёта будет выглядеть, как показано на рисунке (см. Рис. 4.25).

Для пометки мест, куда будут вставляться данные, в шаблоне исполь-зуется символ «$». После этого символа в фигурных скобках пишется имя отчёта, соответствующее имени в компоненте <report>, и через точку имя элемента, соответствующее имени компонента <element>, расположенного внутри <report>.

Для вывода данных табличного типа в шаблоне используется JOOScript1, в котором задаётся структура выводимых данных, а на странице данные берутся из групп. В нашей накладной скрипт помещается в левую верхнюю ячейку таблицы и выглядит так (см. Рис. 4.24). Поскольку в скрипте мы задали сокращение для имени группы gArticles, то в ячейках таблицы к полям этой группы мы уже обращаемся через сокращение gA.

1 JOOScript специально разработан для описания структуры шаблона отчёта для приложений

Prototype 6. Со спецификацией языка можно ознакомиться на сайте http://code.google.com/p/prototype6/wiki/HomePage

131

132

Рис. 4.24 – Скрипт JOOScript для таблицы накладной

Рис. 4.25 – Шаблон отчёта накладной

133

5 ОРГАНИЗАЦИЯ СОВМЕСТНОЙ РАБОТЫ Процесс современной разработки программного обеспечения ориенти-

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

Очевидно, что чем больше автоматизировано видов деятельности в процессе разработки программного обеспечения, тем выше качество полу-чаемого продукта и управляемость самого процесса. Как правило, в первую очередь производители программных систем автоматизируют следующие виды деятельности: конфигурационное управление (SCM1); тестирование; управлением изменениями; управление требованиями; и др.

В данной главе будут продемонстрированы возможности Caché по ин-теграции со сторонней системой конфигурационного управления (SVN2) и её использования при разработке Zen-приложений.

5.1 Контроль версий в Caché Разработчикам Zen-приложений для контроля версий Caché предостав-

ляет типичное решение, состоящее в следующем:

– Zen-страница представляется в формате XML;

– XML-файлы помещаются под версионный контроль стороннего сред-ства конфигурационного управления;

– обеспечивается синхронизация этих XML-файлов с Caché-документами3;

– В Caché Studio можно получать информацию о состоянии объектов в версионном хранилище, какой файл взят на изменение и кем.

1 Software Configuration Management или конфигурационное управление подразумевает под со-

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

2 Subversion − свободная централизованная система управления версиями. 3 Caché-документ представляет собой определение класса, программу или CSP-файл.

134

Caché записывает информацию в формате XML-файлов о каждом до-кументе после последней компиляции. Используемое версионное хранилище будет соответственно обрабатывать каждый документ как отдельный эле-мент.

Caché предоставляет следующие инструменты для управления Caché-документами и внешними файлами:

– Классы %Studio.Extension.Base и %Studio.SourceControl.Base предос-тавляют методы для базового управления документами. Эти классы могут расширены для добавления пункта меню в Caché Studio с дополнительными действиями над документами Caché;

– Функция $system.OBJ.Export экспортирует Caché-документы в XML-файлы в версионное хранилище;

– Функция $system.OBJ.Load загружает внешние XML-файлы и перепи-сывает соответствующие Caché-документы;

– Метод %RoutineMgr.TS возвращает время и дату изменения Caché-документа. Этот метод также возвращает по ссылке время компиляции.

5.1.1 НАСТРОЙКА CACHÉ ДЛЯ РАБОТЫ С SVN В PROTOTYPE 6 Сначала создаётся директория, в которой будут размещаться XML-

файлы Caché-документов, по умолчанию это − c:\vssdb\. Путь размещения хранилища прописан в глобале ^p6("SourceDirWin"), при необходимости этот путь можно изменить.

Теперь откроем портал управления Caché и пройдём по ссылкам «Кон-фигурация → Настройки управления версиями Studio», выберем область P6 и установим переключатель в позиции p6.SourceControl (см. Рис. 5.1). Класс p6.SourceControl предназначен для осуществления экспорта Caché-документов в XML-файлы и обновления этих документов из хранилища. Те-перь после каждого запуска Caché Studio новые и изменённые Caché доку-менты будут обновляться, и при каждой компиляции в хранилище будут из-меняться соответствующие XML-файлы. Скомпилируем весь проект (F7) для того чтобы все XML-файлы проекта для документов Caché попали в указан-ное место (c:\vssdb\).

135

Рис. 5.1 – Настройка управления версиями

5.2 Установка и настройка SVN SVN (Subversion) используется многими сообществами разработчиков

открытого программного обеспечения. В их числе такие известные проекты как Apache, KDE, GNOME, GCC, Free Pascal, Python, Ruby, Mono. SourceForge.net и Tigris.org предоставляют хостинг Subversion для проектов с открытым кодом. В системах Google Code и BountySource используется ис-ключительно Subversion. Subversion также широко используется в корпора-тивной сфере.

В 2007 году независимая компания Forrester Research, сравнивая пре-имущества и недостатки различных систем, оценила Subversion как «едино-личного лидера в категории Standalone SCM и сильного участника в катего-рии Software Configuration and Change Management (SCCM)».

SVN обладает следующими возможностями: – Отслеживается история файлов, директорий и метаданных файлов, в

том числе при переименовании и копировании. – Публикации изменений атомарны. – Возможность организации доступа к хранилищу Subversion через

Apache по протоколу WebDAV/DeltaV. – Возможность установки автономного сервера Subversion с доступом

по собственному протоколу. – Для операций создания ветвей и меток требуется небольшое фиксиро-

ванное количество временных и дисковых ресурсов. – Многоуровневая архитектура библиотек, изначально рассчитанная на

клиент-серверную модель.

136

– Клиент-серверный протокол пересылает по сети только разницу меж-ду объектами, когда это возможно.

– Затраты ресурсов пропорциональны размеру изменений, а не размеру данных, которые затронуты изменениями.

– Два возможных внутренних формата репозитория: база данных или простой файл.

– Версионированные символьные ссылки (только в рабочих копиях под UNIX-системами).

– Одинаково эффективная работа и с текстовыми, и с двоичными фай-лами.

– Вывод клиента командной строки одинаково удобен и для чтения, и для разбора программами.

– Частичная локализация сообщений (используются настройки локали). – Библиотеки для языков PHP, Python, Perl, Java. – Возможность зеркалирования репозитория.

5.2.1 МОДЕЛЬ РАБОТЫ Subversion − централизованная система (в отличие от распределённых

систем, таких, как Git или Mercurial), то есть данные хранятся в едином хра-нилище. Хранилище может располагаться на локальном диске или на сетевом сервере.

Работа в Subversion мало отличается от работы в других централизо-ванных системах управления версиями. Клиенты копируют файлы из храни-лища, создавая локальные рабочие копии, затем вносят изменения в рабочие копии и публикуют эти изменения в хранилище. Несколько клиентов могут одновременно обращаться к хранилищу. Для совместной работы над файла-ми в Subversion преимущественно используется модель Копирование-Изменение-Слияние. Кроме того, для файлов, не допускающих слияние (раз-личные бинарные форматы файлов), можно использовать модель Блокирова-ние-Изменение-Разблокирование.

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

При использовании доступа с помощью WebDAV также поддерживает-ся прозрачное управление версиями — если любой клиент WebDAV откры-вает для записи и затем сохраняет файл, хранящийся на сетевом ресурсе, то автоматически создаётся новая версия.

137

5.2.2 ТИПЫ РЕПОЗИТОРИЕВ Subversion предлагает два варианта организации репозиториев. Репози-

тории первого типа используют для хранения базу данных на основе Berkeley DB, репозитории второго типа − в обычных файлах специального формата (доступ к данным организуется с помощью собственных библиотек, без ис-пользования сторонних баз данных). Разработчики Subversion часто называ-ют хранилище «файловой системой», поэтому второй тип получил название FSFS, то есть (версионированная) файловая система, использующая обычную файловую систему.

Оба типа репозиториев обеспечивают достаточную надёжность при правильной организации (Berkeley DB использует блокировки файлов, по-этому её нельзя использовать на некоторых сетевых файловых системах, не поддерживающих блокировок), каждая из них обладает своими преимущест-вами и недостатками. Считается, что FSFS легче правильно настроить, она требует меньшего внимания от администратора.

5.2.3 ДОСТУП К РЕПОЗИТОРИЮ Subversion предоставляет следующие способы доступа к репозиториям:

– Локальная или сетевая файловая система используется напрямую кли-ентской программой Subversion.

– WebDAV/DeltaV (поверх http или https) с использованием модуля mod_dav_svn для Apache 2.

– Собственный протокол «svn» (порт по умолчанию 3690), используя простой текст или через SSH.

Все эти способы могут быть использованы для работы с репозиториями обоих типов (FSFS и Berkeley DB). Для доступа к одному и тому же репози-торию могут одновременно использоваться разные способы.

5.2.4 УСТАНОВКА СЕРВЕРА SVN Установка сервера SVN выполняется на компьютере, к которому суще-

ствует сетевой доступ от всех компьютеров-клиентов, на которых ведётся разработка. При установке сервера нужно задать ряд параметров (см. Рис. 5.2): указать место установки и путь к хранилищу; выключить опцию Use secure connection (использовать безопасное соединение) и задать номер пор-та; выбрать тип аутентификации − встроенную (Use Subversion authentica-tion), либо аутентификацию Windows (Use Windows authentication).

После установки сервера посредствам VisualSVN Server Manager (см. Рис. 5.3) создадим хранилище данных и пользователей системы.

138

Рис. 5.2 – Параметры установки сервера SVN

Рис. 5.3 − Server Manager SVN

139

Хранилище создаётся по вызову контекстного меню у элемента «Re-positories» и выбору пункта меню «Create New Repository» (см. Рис. 5.4). Пользователи создаются аналогично (см. Рис. 5.5).

Рис. 5.4 – Создание нового репозитория

Рис. 5.5 – Создание нового пользователя

140

Для того чтобы поместить уже существующий проект в версионное хранилище, а в данном случае мы будем помещать под контроль проект вы-полненный на технологии Prototype 6, воспользуемся инструментом команд-ной строки Server Manager (см. Рис. 5.6) и импортируем данные проекта в хранилище. Для этого в Server command prompt выполним следующую ко-манду (см. Рис. 5.7). svn import C:\vssdb http://yakunin/svn/tutorial/ -m "initial import"

Рис. 5.6 – Запуск Server command prompt

Рис. 5.7 – Инициализация нового проекта через Server command prompt

141

5.2.5 КЛИЕНТ SVN Когда сервер настроен и проект размещён в хранилище, можно начи-

нать работать через клиента SVN. При первом старте клиента SVN требуется подключение к проекту, существующему в хранилище на сервере (см. Рис. 5.8). При подключении указываются имя сервера и путь к проекту в храни-лище (см. Рис. 5.9), имя и пароль пользователя (см. Рис. 5.10), выбирается директория проекта (см. Рис. 5.11), место расположения локальной копии проекта (см. Рис. 5.12).

Рис. 5.8 – Подключение к проекту на сервере

Рис. 5.9 –Настройка параметров подключения

142

Рис. 5.10 – Авторизация при подключении к проекту

Рис. 5.11 – Выбор директории из хранилища для локальной копии

143

Рис. 5.12 – Место хранения копии проекта на компьютере клиента SVN

После завершения, SmartSVN импортирует все данные из хранилища в локальную копию проекта (см. Рис. 5.13).

Рис. 5.13 – Локальная копия проекта в SamrtSVN

144

5.3 Работа в SVN Перед началом работы настроим путь к локальному хранилищу дан-

ных, которое у нас располагается в «e:\svndb\». Для этого установим значение глобала ^p6("SourceDirWin") равное «e:\svndb\» через терминал Caché (см. Рис. 5.14).

Рис. 5.14 – Изменение пути к хранилищу в глобале

Любые действия, выполняемые над документами Caché в Studio, отра-жаются в виде новых XML-файлов помещаемых в локальную копию проекта по пути «e:\svndb\». Например, изменим один из документов Caché − zui.Menu и скомпилируем его. Тогда в SamrtSVN сразу появится информация об изменении этого файла (см. Рис. 5.15).

Рис. 5.15 – SamrtSVN помечает изменённые файлы

145

Все изменения, появившиеся в локальной копии проекта, могут быть отправлены в хранилище на сервере командой «Commit local changes to re-pository (Ctrl+O)» (см. Рис. 5.16). Также изменения выполненные другими участниками проекта, сдавшими свои данные на сервере, можно командой «Update local working copy from repository (Ctrl+U)».

Рис. 5.16 – Отправка изменений проекта на сервер

5.4 Интеграция Caché и IBM Rational Rose Для представления созданных в Caché классов в виде диаграммы клас-

сов на UML используется специальный мост между двумя приложениями. Caché поставляет в стандартной комплектации модуль Rose Caché Link, ко-торый позволяет осуществлять прямое и обратной проектирование посредст-вам IBM Rational Rose.

Запустим IBM Rational Rose и выберем пункт меню Add-Ins → Add-In Manager. Запустится менеджер модулей (см. Рис. 5.17), в котором нужно га-лочкой отметить модуль Caché. Для создания диаграммы классов посредст-вам моста Rose Caché Link выберем пункт меню Tools → Caché → Import Classes from Caché. В появившемся менеджере соединений (см. Рис. 5.17) выберем область, к которой хотим подключиться и нажмём кнопку ОК.

146

Рис. 5.17 − Менеджер подключаемых модулей Rational Rose

Рисунок 5.1. Менеджер соединений

После запуска модуля импорта классов (см. Рис. 5.18), выберем те

классы, которые хотим увидеть на диаграмме классов и синей стрелкой поместим их в правый список (Selected Classes). После этого выполним им-порт кнопкой «Import».

147

Рис. 5.18 − Модуль импорта классов из Caché в Rational Rose

После этого Rational Rose создаст диаграмму классов. Для нашего при-мера после небольшой визуальной корректировки эта диаграмма примет вид, как представлено на рисунке (см. Рис. 3.15).

БИБЛИОГРАФИЧЕСКИЙ СПИСОК 1 Иванчева Н.А., Иваньчева Т.А., Постреляционная СУБД Caché. Мето-

дическое пособие. Новосибирск: НГУ, 2004– 120 с. 2 Кирстен В., Ирингер М., Кюн М., Рериг Б. Постреляционная СУБД

Caché 5. Объектно-ориентированная разработка приложений. 2-е изд. – М.: ООО «Бином-Пресс», 2005 г. – 416 с.

3 Кречетов Н.Е., Петухова Е.А., Скворцов В.И., Умников А.В., Щукин Б.А. Постреляционная технология Caché для реализации объектных прило-жений. Учебное пособие. М.:МИФИ, 2001. – 152 с.

4 Технологический справочник Caché : [электронный ресурс]. – Режим доступа: http://www.intersystems.ru/cache/technology/techguide/index.html.

5 Документация по Zen : [Электронный ресурс]. Режим доступа: http://www.intersystems.com/cache/documentation/index.html.

148