Небольшая коллекция самописных классов и процедур на REXX

Автор: Глеб Беляков

Источник: OS2.ru

Дата: 10.02.2001

Предлагаю вашему вниманию небольшую коллекцию самописных классов и процедур (20 Kb). Не судите сторого -- все это я сначала писал для себя, и только потом мне пришла в голову идея вынести все это на суд общественности.

config.class
.config

класс для чтения конфигурационных файлов.

progress.class
.roller

класс для отображения индикатора активности в виде вращающейся палочки.

.slider

класс для отображения индикатора активности в виде предложеном IBM'ом. :)

.progressbar

класс для чтения конфигурауционных файлов.

sems.class
.event

класс для чтения конфигурауционных файлов.

.mutex

класс для чтения конфигурауционных файлов.

works.procs
подборка процедур.

 

CONFIG.CLASS

В этом файле содержится только один класс:

.config

Представляет собой достаточно умную читалку текстовых конфигов, выполненую в виде класса языка Object REXX. Поддерживает макроопределения, вложения файлов, значения по умолчанию для директив. Класс содержит следующие методы:

~new(filename,keys)

инициализирует класс для чтения конфигурационного файла с заданным именем.

filename имя файла.
keys список директив из файла.
~read

cчитывает файл и возвращает структуру values, содержащую информацию из конфига.

~show

выводит на экран информацию о считаных данных. Представляет ценность только во время отладки.

~getfilename

возвращает имя конфига, подлежащего чтению.

~getvalues

возвращает структуру values, содержащую информацию из конфига.

~requires

предназначен для переопределения. Вызывается в том случае, если в конфиге не найдена хотя бы одна директива, помеченная как requiers

По умолчанию выдает сообщение и вызывает ~abort.

~invalid

предназначен для переопределения. Вызывается в том случае, если в в конфиге найдена директива, не указанная в keys

По умолчанию выдает сообщение и вызывает ~abort.

~abort(message)

выводит на экран сообщение message и выходит из скрипта.

~filenotfound

предназначен для переопределения. Вызывается если не найден файл для чтения.

По умолчанию выводит на экран сообщение message и вызывает ~abort.

~include

не трогайте его. Вызывается при нахождении директивы #include.

~setting

опять же не трогайте его. Вызывается при нахождении правильной директивы.

~setmethod

и его не трогайте. Он простой и позволяет переопределять методы.

Методы ~read и ~getvalues возвращают структуру values, которая является классом .directory индексы которого соответствуют директивам, а значения полей, являются либо строками (для single), либо массивами строк (для multi).

метод ~new принимает структуру keys, которая также является классом .directory, индексами которого являются имена директив, а значениями параметры:

  single multi
optional

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

Кроме того, через ':' можно указать значение по умолчанию.

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

Кроме того, через ':' можно указать несколько значений по умолчанию, разделенных ';'

requied

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

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

Например:
keys = .directory~new
keys~key1 = 'single,required'
keys~key2 = 'single,optional:default for key2'
keys~key3 = 'multi,required'
keys~key4 = 'multi,optional:default1 for key4;default2 for key4'
keys~key5 = 'single,optional'
keys~key6 = 'multi,optional'

Из конфигурационного файла будет прочитано следующее:

key1
   будет прочитано последнее из указанных значений, причем если не попадется ни одного значения, работа скрипта будет остановлена.
key2
   будет прочитано последнее из указанных значений, причем если не попадется ни одного значения, будет установлено значение 'default for key2'
key3
   будут считаны в массив все значения, причем если не попадется ни одного значения, работа скрипта будет остановлена.
key4
   будут считаны в массив все значения, причем если не попадется ни одного значения, будут установлены значения 'default1 for key4' и 'default2 for key4'
   Если попадется хотя бы одно значение, то все значения по умолчанию будут проигнорированы.
key5 и key6
   в принципе, аналогично key2/key4, но параметр по умолчанию отсутствует. Для key5 будет установлена пустая строка, а для key6 -- массив без элементов.

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

#DEFINE name [argument]

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

#UNDEF name

отменяет определение для name

#IFDEF name
...
#END

читает находящееся между #IFDEF и #END только если name определено.

#IFNDEF name
...
#END

читает находящееся между #IFDEF и #END только если name НЕ определено.

#INCLUDE filename

"включает" файл filename в текущий файл. Вложенность не ограничена.


 

PROGRESS.CLASS

В этом файле содержатся сразу 3 класса: .roller, .slider и .progressbar

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

Эти классы представляют собой 3 растпространенных индикатора того, что в программе что-то происходит.

.roller
представляет собой крутящуюся палочку.
.slider
представляет собой индикатор предложеный IBM'ом. В системе он выводится при установке, в момент поиска p'n'p. По-моему неплохая альтернатива для: "Если ваш винчестер длительное время не подает признаков жизни..." (с)
.progressbar
ну тут комментарии излишни. Этот индикатор попадается решительно везде, где только можно.

Теперь подробнее:

.roller

Имеет следующие методы:

~new(symbols,speed) инициализирует класс.
symbols

задает символы для вращения. Собственно не обязательно вращения... По умолчанию используются '-\|/', что как раз и создает эффект вращения.

speed

Задает скорость как количество изменений в секунду. По умолчанию 2.

~show

выводит некую информацию о классе. Полезен только при отладке.

~start

запускает индикатор, после чего тот крутится до вызова метода ~stop.

~stop

останавливает индикатор.

Индикатор не зависит от хода выполнения основной программы. В этом смысле он пригоден скорее для развлечения пользователя, чем для определения, не повис ли скрипт. С другой стороны, покажите мне повисший скрипт на Rexx'е... :)

Индикатор сам определяет текущую (на момент ~start) позицию курсора на экране, и крутится в ней. Поэтому выводить что-либо на экран в процессе вращения индикатора лучше не надо.


.slider

Имеет следующие методы:

~new(len,speed) инициализирует класс.
len задает горизонтальный размер индикатора в символах.
speed Задает скорость как количество изменений в секунду. По умолчанию 2.
~show выводит некую информацию о классе. Полезен только при отладке.
~start запускает индикатор, после чего тот крутится до вызова метода ~stop.
~stop останавливает индикатор.

Как и .roller, .slider не зависит от хода выполнения основной программы.

И так же как и для roller'а, не стоит выводить ничего на экран, пока индикатор рисует себя.

На самом деле, я знаю как это все исправить, и сделать очень удобно, но... в следующий раз. :)


.progressbar

Имеет следующие методы:

~new(start,max,len) инициализирует класс.
start задает начальное значение некоторой величины, т. е. значение для 0%.
max задает максимальное (конечное) значение некоторой величины, т. е. значение для 100%.
len задает горизонтальный размер индикатора в символах.
~step переходит к следующему значению некоторой величины. Заметьте: не к следующему проценту, а именно к следующему значению величины.
~stepto переходит к заданному значению некоторой величины.
~stop останавливает индикатор.

Что такое "некоторая величина"? Например, Вам нужно скопировать 5 файлов. Тогда создание класса будет выглядеть следующим образом:

p = .progressbar~new(0,5,50)

Имеем:

значение 0%:
0 фалов скопировано.
значение 100%:
5 фалов скопировано.
размеры:
на индикатор отведено 50 символов по горизонтали. Учтите, что по вертикали .progressbar занимает 2 строки.

При этом метод ~step будет изменять показания прогрессбара на 20%.

Если Вы хотите вывести показания для момента завершения копирования 3-го файла, вызывайте:
p~stepto(3)

Как Вы, наверное, догадались, при использовании этого индикатора тоже не стоит ничего выводить на экран.


 

SEMS.CLASS

Этот файл содержит два крайне полезных, на мой взгляд, класса: .mutex и .event.

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

.event

Содержит следующие методы:

~new(name) инициализирует класс. name задает имя семафора.
~post постит семафор.
~reset сбрасывает семафор.
~query возвращает .true, если семафор запощен.
~wait ожидает, пока семафор будет запощен.
~defaultname возвращает информацию о семафоре в формате name (handle) state

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


.mutex

Содержит следующие методы:

~new(name) инициализирует класс. name задает имя семафора.
~request захватывает семафор.
~release отпускает семафор.
~defaultname возвращает информацию о семафоре в формате name (handle)

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

s = .mutex~new('\sem32\msgbase.sem')
s~request
...
...
s~release

Класс полностью гарантирует, что фрагмент, заключеный между ~request и ~release, будет выплняться в одиночестве, и все остальные (естественно, кто пытается выполнить ~request для семафора с тем же именем) будут дожидаться вызова ~release в скрипте, выполнившем ~request.

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

Кроме того, класс успешно борется с ситуацией, когда скрипт выполнил ~request и по каким либо причинам не смог выполнить ~release. Такой "зомбированый" семафор автоматически будет пересоздан.


works.procs

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

makepath(path)

создает путь path, опускаясь вниз по дереву.

log(file,string)

записывает строку string в файл file, предваряя ее датой и временем. Удобно для ведения логов.

setenv(var,val)

аналогична стандартной функции value, но с фиксированым третьим параметром: 'OS2ENVIRONMENT'.
Возвращает значение переменной среды, заданной в var. Если указано val, то заодно устанавливает новое значение.

arcmail(filename)

возвращяет .true, если filename является правильным именем arcmail-пакета (это из FTN).

datebase(date,format)

возвращает количество дней между 01.01.0001 и указанной датой. format задает формат, в котором указана дата. По умолчанию 'N'.

datedest(date1,date2,format)

возвращает количество дней между датами. format задает формат, в котором указаны даты. По умолчанию 'N'.

dateago(date,format)

возвращает количество дней, прошедших с указанной даты. format задает формат, в котором указана дата. По умолчанию 'N'.

Стоит заметить, что функции datebase, datedest и dateago в общем-то потеряли смысл при выходе Object REXX'а. Но вот в некоторых моих скриптах они используются, а потому были переписаны под ORexx и оставлены "для удобства".

TimeBase_M(time)

возвращает количество минут, прошедших от полночи. Время принимается в 24-х часовом формате hh:mm:ss.

TimeBase_S(time)

возвращает количество секунд, прошедших от полночи. Время принимается в 24-х часовом формате hh:mm:ss.

Про эти две функции можно сказать то же самое, что и про date*.

arrinsert(arr1,index,arr2)

Возвращает массив, получившийся в результате вставки arr2 в arr1 с позиции index.

expandaddress(address,default)

Преобразует частично заданный адрес в полный, подставляя недостающие части из default. (это из FTN).

YesNo(Q)

возвращяет .true, если Q является утвердительным ответом ('Yes' или 'Да').

matchaddr(address,mask)

возвращает .true, если address попадает в маску mask. Поддерживаются символы '*' -- все что угодно и '!' -- отрицание. (это из FTN).

match(word,mask)

возвращает .true, если word попадает в маску mask. Поддерживаются символы '*' -- все что угодно и '!' -- отрицание.

FileCopy(source,dest)

копирует файл из source в dest. Не мудрствуя лукаво, пользуется командой copy.

FileMove(source,dest)

перемещает файл из source в dest. Не мудрствуя лукаво, пользуется командой mov. mov.exe лежит тут же...

Abort(message)

выводит на экран message и останавливает работу скрипта.

QSort(array)

сортирует массив Array. Название говорит обо всем.

Color(color)

устанавливает текущий цвет символов.

Если у Вас возникнут какие-нибудь глобальные идеи по поводу этих скриптов, или Вы захотите указать мне на мои ошибки, или Вы чего-то не поймете, то пишите мне.