Содержание сайта
Главная Новичку Цитаты Реализации Статьи Документация
Компании Программы Ссылки Обсуждение Обсуждение 2 Гостевая

Добавляем собственные установки

Андрей Собчук
JabberID: andreis@jabber.ru. ICQ UIN: 46466235.

На этот раз, мы рассмотрим как добавить в среду VisualWorks свой набор установок, изменяемых пользователем. Интересно это двум категориям разработчиков. Во-первых, при разработке своих инструментальных средств неплохо не растыкивать установки куда попало, а иметь возможность задавать их там, где задаются все прочие установки для среды. Во-вторых, прикладным программистам. Система задания установок может быть переиспользована в прикладной программе. Она легко настраивается, профессионально выглядит, имеет интерфейс для сохранения установок в формате XML в поток (файл) или чтения их оттуда.

Идеология системы задания установок

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

В рамках доменов отдельные установки группируются в страницы.

Каждый отдельный параметр и каждая отдельная страница имеют идентификатор. Идентификатор представляет из себя массив символов (объектов класса Symbol). Например: #(tools workspace). Идентификатор можно сравнить с путём к файлу. Последовательность элементов, которые логически соответствуют каталогу в пути называется префикс. В данном примере префикс - #tools. Подразумевается, что у параметров, логически относящихся друг к другу, один и тот же префикс, и у их идентификаторов различаются только последние элементы.

Домены установок

Так. Пора переходить к практике. Та система задания установок, которая использовалась до VW7, требовала явной регистрации страниц с установками. В текущей системе, как для определения отдельных параметров так и для группировки их в страницы используются прагмы. Прагмы позволяют пометить отдельные методы. Аналог прагмам имеется, например, в .Net - это атрибуты [методов].

Каждый домен представлен отдельным подклассом класса SettingsDomain. К примеру, настройки инструментальных средств среды VW определяются в классе VisualWorksSettings. В каждом классе, задающем домен, должен быть определён метод #label, возвращающий строку-имя домена.

Описание отдельных параметров

Для определения каждого отдельного параметра нужно создать отдельный метод со стороны класса в нужном домене. В методе, определяющем параметр, нужно задать прагму #setting: с одним аргументом - идентификатором параметра.

Например, предположим, что мы хотим иметь возможность изменять два параметра относящиеся к GLORP-у. Это платформа по умолчанию, и параметр, задающий режим передачи аргументов в запросах. Для этого, на стороне класса в классе VisualWorksSettings (напомню, что этот класс представляет собой домен установок среды разработки) создадим два метода:

glorpDatabasePlatform
	<setting: #(glorp glorpDatabasePlatform)>
	| choicesSequence keysSequence |
	choicesSequence := Glorp.DatabasePlatform allSubclasses.
	keysSequence := choicesSequence collect: 
				[:each | each fullName asSymbol].
	^((EnumerationSetting keys: keysSequence choices: choicesSequence) 
		onUISetting: #glorpDatabasePlatform)
		label: 'Default database platform';
		helpText: 'Database platform used by Glorp development tools. 
			To set user name, password and environment 
			go to "Tools-Database" page'


glorpUseBindingIfSupported
	<setting: #(glorp useBindingIfSupported)>
	^(BooleanSetting 
		on: Glorp.DatabasePlatform 
		aspect: #useBindingIfSupported)
			label: 'Use binding if supported';
			helpText: 'GLORP can work in both printing and 
				binding mode. Select preffered mode.'
Каждый такой метод должен возвращать описатель типа параметра - один из подклассов класса SettingType. Доступные типы:
  • Класс BooleanSetting - позволяет задать параметр булевого типа;
  • Класс ColorValueSetting - задаёт параметр содержащий экземпляр класса ColorValue;
  • Класс EnumerationSetting - позволяет выбрать одно значение из списка возможных вариантов. Перечень вариантов задаётся параметром choices: и может содержать любые объекты, которые можно сравнить при помощи метода #=. Для каждого объекта из choices: в коллекции keys: должен находится ключ - символ идентифицирующий соответствующий вариант. Этот ключ используется, например, при сериализации установок в файл. Визуально этот тип представлен в виде выпадающего списка. Но, если странице с установками послать сообщение #useRadioButtonsForEnumerations, то перечисления, на данной странице, будут отображены в виде группы радио-кнопок;
  • Класс FilenameSetting и DirectorySetting - ссылаются на параметр типа LogicalFilename либо PortableFilename. Послав сообщение #mustExist: с булевым аргументом, можно установить должен ли существовать путь, задаваемый как значение параметра;
  • Класс NumberSetting и IntegerSetting - позволяют задать числовой параметр. Число должно быть целым для типа IntegerSetting. Существует возможность задать минимально и (или) максимально допустимое значение. Для этого используются сообщения min: и max:. Если странице, на которой есть установки с целыми числами, послать сообщение #useSpinButtonsForIntegers, то, при отображении, у каждого параметра появятся кнопочки для увеличения/уменьшения значения параметра;
  • Класс SequenceSetting - указывает, что значение параметра это коллекция различных значений. Тип отдельных значений задаётся при создании экземпляра класса SequenceSetting и может быть любым из подклассов класса SettingType;
  • Класс StringSetting - указывает, что тип параметра - строка. Если, при создании экземпляра, послать классу StringSetting сообщение #forPassword, то содержимое параметра будет представлено в виде звёздочек (см. пример на изображении);
    Пример строки и пароля

При создании экземпляра класса SettingType следует указать откуда брать (и куда сохранять) значения параметра. Сообщение #onUISetting: устанавливает, что установки сохраняются (получаются) при помощи посылки сообщения #preferenceFor: (#preferenceFor:put:). В нашем примере именно так сохраняется параметр определяемый методом #glorpDatabasePlatform. Обратите внимание на то, что слот, с используемым именем параметра, должен уже существовать. Для инициализации слота можно использовать такой метод:

DatabasePlatform class>>initialize
	UISettings preferenceFor: #glorpDatabasePlatform
		ifNone: 
			[UISettings preferenceFor: #glorpDatabasePlatform
				put: Glorp.PostgreSQLPlatform]
Второй метод из нашего примера (#glorpUseBindingIfSupported) использует другой подход. Это, по сути, аналог AspectAdaptor-а. Значение параметра получается в результате посылки объекту, заданному аргументом on:, сообщения, заданного аргументом aspect:. Если вместо аргумента aspect: использовать аргумент atKey:, то значение будет извлекаться путём посылки объекту on: сообщения #at: с аргументом, заданным параметром atKey:.

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

Группировка параметров в страницы

Следующим шагом, после описания отдельных параметров, является создание страницы, на которой будут выводится логически связанные параметры. Объект, представляющий страницу, создаётся в методе, который должен быть помечен прагмой #settingsPage:. Аргументом прагмы является идентификатор страницы, созданный по правилам, описанным выше.

Метод описания страницы
Страницы, имеющие друг к другу отношение, должны иметь одинаковый префикс. Таким образом формируется дерево страниц. Взглянуть на пример такого дерева можно выбрав пункт меню 'System'-'Settings' в IDE VW или на снимке ниже.

В рамках среды VW, страницы представлены объектами подклассов класса SettingsManagerPage. Для группировки отдельных параметров в страницы используется ModularSettingsPage. Необходимо вначале создать объект, а затем задать ряд [опциональных] значений:

glorpPage
	<settingsPage: #(tools database glorp)>
	^(ModularSettingsPage new)
		label: 'Glorp';
		icon: (ListIconLibrary visualFor: #query);
		settings: (self settingsWithPrefix: #(#glorp))

Для конфигурации объекта ModularSettingsPage используется ряд сообщений:

  • Сообщение #label: позволяет задать отображаемое наименование страницы.
  • Сообщение #icon: устанавливает иконку, соответствующую странице.
  • Сообщение #settings: задаёт набор установок, которые будут отображаться на этой странице. Для отбора установок используется ряд методов. Метод #settingsWithPrefix: отбирает все установки с определённым префиксом, #settingsWithPrefix:except: позволяет исключить параметры с префиксом except: из набора. Метод #settingWithId: выбирает один параметр с конкретным идентификатором. Метод #settingWithId:ifAbsent: выполняет блок ifAbsent: если не обнаружен параметр с данным идентификатором.
  • Сообщения #addAllSettings: и #addSetting: позволяют добавить к набору параметров дополнительные параметры. Используются для сложных страниц.
  • Сообщения #useSpinButtonsForIntegers и #useRadioButtonsForEnumerations позволяют изменить внешний вид некоторых параметров. После сообщения #useSpinButtonsForIntegers в поле ввода чисел появятся кнопки для увеличения/уменьшения числа. После сообщения #useRadioButtonsForEnumerations, при выборе одного значения из перечня значений, будет использоваться не выпадающий список, а набор радио-кнопок;
  • Сообщение #addModule:. Позволяет включить в новую страницу другую страницу. Например:
    module1 := ... .
    module2 := (EnumerationSettingModule on: (self settingWithID: #(some id))).
    	^(ModularSettingsPage new)
    		label: '...'
    		addModule: module1;
    		addModule: module2;
    		when: module1 valueHolder
    		  valueSatisfies: [:v| v = #enable] enable: module2
    
  • Сообщения #when:valueSatisfies:enable: и #when:valueSatisfies:enableAll: позволяют контролировать доступность отдельных модулей для редактирования. Параметр when: это объект поддерживающий value-протокол, например, ValueHolder, AspectAdaptor. Аргумент valueSatisfies: принимает блок с одним параметром - значением хранящимся в объекте when:. Если в результате вычисления блока возвращается true, то модуль, заданный в аргументе enable: становится доступным для редактирования, если результат вычисления - false, то - недоступным. Блок вычисляется при каждом изменении значения хранящегося в объекте when:.

Очерёдность вывода параметров

Существует так же возможность определить очерёдность, в которой выводятся как страницы в целом, так и очерёдность отдельных параметров на странице. Для упорядочивания отдельных параметров нужно использовать вместо прагмы #setting: прагму #setting:position:. Аргумент position: это число. Установки упорядочиваются от меньшего числа к большему. Если аргумент position: не задан, то предполагается, что его позиция - 0. Параметры с одинаковой позицией упорядочиваются по селекторам методов. В нашем примере, параметр с селектором #glorpDatabasePlatform будет стоять перед параметром #glorpUseBindingIfSupported.

По такому же принципу, для упорядочивания страниц, нужно пользоваться прагмой #settingsPage:position: вместо прагмы #settingsPage:.

Сохранение установок в файл

Система установок умеет сохранять текущие значения параметров в файл в формате XML. И, естественно, умеет читать их. Для записи установок в файл нужно послать сохраняемому домену сообщение #writeToFile: с именем файла (объектом класса Filename) в качестве аргумента. Попробуйте выполнить код:

VisualWorksSettings writeToFile: 'vw.xml' asFilename.
Для чтения установок служит сообщение #readFromFile:. Интересной возможностью является то, что система позволяет загружать даже те параметры, которые в домене еще не описаны. Значение сохраняется, и позже, при добавлении описателя параметра в домен, это значение присваивается параметру.

Открываем окно с установками для редактирования

Ну, в общем, почти всё. Осталось научиться открывать окно установок. Это можно сделать несколькими разными способами. Первый способ предполагает открытие одного окна установок на все домены одновременно. Сделать это можно послав классу SettingsManager сообщение #open. Попробуйте:

SettingsManager open.
При этом, в открывшемся окне установок, в левом верхнем углу будет находиться выпадающий список со всеми доступными доменами установок.
Выбор домена установок
Второй способ позволяет открыть окно редактирования одного конкретного домена. Для этого используется сообщение #open: с доменом в качестве аргумента. Например, нижеследующий кусок кода откроет окно редактирования установок среды разработки:
SettingsManager open: VisualWorksSettings.
Окно установок
На этом снимке можно видеть, как отображаются установки для GLORP-а. Листинги методов для их создания приведены выше. И, наконец, сообщение #open:pageWithId: служит для открытия окна установок прямо на странице с определённым идентификатором. Например, чтобы открыть окно установок и перейти на страницу с установками GLORP-а нужно выполнить такой код:
SettingsManager 
	open: VisualWorksSettings 
	pageWithId: #(tools database glorp).

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

Использование систему задания установок в своём приложении

Для использования системы задания установок в своём приложении необходимо загрузить парсел 'Tools-Settings'. После чего можно создать/загрузить домен установок своего приложения - подкласс класса SettingDomain с методами со стороны класса, определяющими отдельные параметры и страницы в целом.

Шифрование сохранённых установок

Для шифрования сохранённых установок нужно загрузить парсел 'EncryptedSettings'. После чего, в домене, установки которого будут шифроваться, нужно определить метод #cipher, который возвращает шифратор. Шифратор это экземпляр подкласса AsymmetricCipher или подкласса SymmetricCipher. Например, для использования шифратора Blowfish нужно подгрузить парсел 'Blowfish'. В случае, если, в ответ на это сообщение, домен возвращает не nil, то возвращаемый шифратор используется для шифровки и дешифровки установок. Подробнее об использовании шифраторов можно почитать в документации - файл SecurityGuide.pdf.

Итоги

Кратко подытожим всё вышесказанное. Для использования системы установок необходимо загрузить парсел 'Tools-Settings'. Для установок каждого отдельного приложения нужно создать отдельный домен - подкласс класса SettingDomain. Установки, относящиеся к инструментам разработчика, нужно добавлять в домен VisualWorksSettings. Методы, которые создают описатели отдельных параметров, помечаются прагмой #setting:. Методы, описывающие страницы, помечаются прагмой settingsPage:. Методы, описывающие и параметры и страницы необходимо создавать со стороны класса в классе-домене. Страницы можно как формировать автоматически, из отдельных параметров, так и создавать самому. Что бы открыть окно с установками определённого домена нужно послать классу SettingsManager сообщение #open: с одним аргументом - классом-доменом. Для сохранения параметров в файл нужно послать соответствующему домену сообщение #writeToFile:, для чтения - #readFromFile:. Для шифрования сохранённых установок нужно загрузить парсел 'EncryptedSettings' и в классе-домене на стороне экземпляра определить метод #cipher. Этот метод должен возвращать шифровальщик.




Есть комментарии? Пишите.