Здесь будет сделана попытка дать краткую характеристику штатных тегов ClsRuby для того, чтобы показать для каких конкретно ситуаций предназначен тот или иной тег. Для получения подробного описания классов тегов и их методов следует обратиться к соответствующим разделам документации.
Класс ClsRuby::Tag является базовым классом для всех тегов. Он не предназначен для непосредственного использования, т.к. в своих методах tag_on_tok_nonspace и tag_on_tok_string порождает исключение ClsRuby::UnexpectedTokenEx.
Однако, класс ClsRuby::Tag может использоваться в качестве базового класса для создания собственных тегов с нетривиальной логикой, когда ни один из штатных тегов ClsRuby не подходит по каким-либо причинам.
Класс ClsRuby::TagAny предназначен для случая, когда нужно разобрать входной поток, структура которого заранее неизвестна. Использование ClsRuby::TagAny в чем-то похожа на разбор XML с помощью DOM-парсинга – сначала содержимое входного потока преобразуется в дерево значений в памяти, а затем осуществляется обход и обработка этого DOM-дерева.
Класс ClsRuby::TagAny является одним из наиболее редко используемых тегов ClsRuby. Это объясняется тем, что в большинстве случаев структура входного потока известна. Поэтому можно, а часто и нужно, создать иерархию тегов, которая будет отвечать за парсинг входного потока, проверку извлекаемых значений, формирование нужных прикладных структур данных. В этих случаях ClsRuby::TagAny оказывается невостребованным.
Необходимость в ClsRuby::TagAny возникает, например, когда требуется поддерживать неизвестные заранее расширения входного потока. Скажем, ClsRuby используется для обмена данными между двумя приложениями. Приложение-клиент обращается к приложению-серверу с запросами вида:
{request {name <str> } {params ... } {extension ... } }
где тег {params} строго определен, а вот тег {extension} может содержать произвольные структуры. Для того, чтобы приложение сервер могло разобрать такой запрос, для тега {extension} можно воспользоваться ClsRuby::TagAny:
class TagRequest < ClsRuby::TagNoValue mandatory_child_tag :name, ClsRuby::TagStringScalar mandatory_child_tag :params, TagRequestParams child_tag :extension, ClsRuby::TagAny end
Класс ClsRuby::TagNoValue является одним из самых широкоиспользуемых тегов ClsRuby. Основная его задача – быть контейнером для подчиненных тегов. Например, в такой ситуации:
{project {name <str> } {location <str> } {files <str>* } {resources <str>* } }
Здесь тег {project} не имеет собственных значений :nonspace и :string, зато владеет подчиненными тегами {name}, {location}, {files} и {resources}. Для представления тега {project} в программе следует воспользоваться тегом ClsRuby::TagNoValue:
class TagProject < ClsRuby::TagNoValue mandatory_child_tag :name, ClsRuby::TagStringScalar ... end
Еще одна роль ClsRuby::TagNoValue – это переключатель. Т.е. сам факт наличия или отсутствия тега является логическим переключателем. Например:
{project ... {use-absolute-path} ... }
Тег {use-absolute-path} не имеет собственных значений, но если он задан во входном потоке, то следует считать, что включен логический переключатель “использовать абсолютные пути”. В программе это может быть выражено так:
class TagProject < ClsRuby::TagNoValue ... child_tag :use_absolute_path, ClsRuby::TagNoValue, :name => 'use-absolute-path' ... def value r = Project.new ... ... # Если тег 'use-absolute-path' определен, значит логический # переключатель включен. r.use_absolute_path = @use_absolute_path.tag_defined? ... end end
Класс ClsRuby::TagScalar, так же как и ClsRuby::TagNoValue, является одним из самых широко используемых классов тегов. Его задача – разбирать и сохранять единственное скалярное значение внутри тега. Т.е. ClsRuby::TagScalar предназначен для разбора тегов вида:
{name <str> } {location <str> } {min <int> } {max <int> }
Тип скаляра определяется с помощью аргумента :format, передаваемого в конструктор тега. В ClsRuby реализованы следующие штатные форматы:
ClsRuby::SCALAR_INT (знаковые целые);
ClsRuby::SCALAR_UINT (беззнаковые целые);
ClsRuby::SCALAR_FLOAT (вещественные);
ClsRuby::SCALAR_STRING (строка заключенная в двойные кавычки, токен :string);
ClsRuby::SCALAR_NONSPACE_STRING (строка без пробелов, токен :nonspace);
ClsRuby::TimeXmlSchemaScalarParser (дата/время в формате XML Schema).
Если готовых форматов недостаточно, то можно реализовать собственный класс форматера, удовлетворяющий интерфейсу ClsRuby::ScalarFormat.
Класс ClsRuby::TagScalar способен проверять так же корректность разобранных скаляров. Если в конструкторе задан аргумент :constraint, то этот объект будет использоваться для проверки разобранного значения. Объект для аргумента :constraint должен удовлетворять интерфейсу ClsRuby::ScalarChecker (например, таковым является стандартный класс Range).
Например, для того, чтобы разобрать теги вида:
[{hours <unit:1..24>}] [{minutes <uint:1..59>}] [{seconds <uint:1..59>}]
можно воспользоваться следующей конструкцией:
class TagTimePeriod < ClsRuby::TagNoValue child_tag :hours, ClsRuby::TagScalar, :type => ClsRuby::SCALAR_UINT, :constraint => 1..24 child_tag :minutes, ClsRuby::TagScalar, :type => ClsRuby::SCALAR_UINT, :constraint => 1...60 child_tag :seconds, ClsRuby::TagScalar, :type => ClsRuby::SCALAR_UINT, :constraint => 1...60 ... end
либо, более коротко, если использовать синоним ClsRuby::TagUintScalar:
class TagTimePeriod < ClsRuby::TagNoValue child_tag :hours, ClsRuby::TagUintScalar, :constraint => 1..24 child_tag :minutes, ClsRuby::TagUintScalar, :constraint => 1...60 child_tag :seconds, ClsRuby::TagUintScalar, :constraint => 1...60 ... end
Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_FLOAT. Т.е. для разбора тега:
{min <float> }
достаточно использовать конструкцию:
child_tag :min, ClsRuby::TagFloatScalar
Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_INT. Т.е. для разбора тега:
{min <int> }
достаточно использовать конструкцию:
child_tag :min, ClsRuby::TagIntScalar
Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_UINT. Т.е. для разбора тега:
{min <uint> }
достаточно использовать конструкцию:
child_tag :min, ClsRuby::TagUintScalar
Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_STRING. Т.е. для разбора тега:
{include <str> }
достаточно использовать конструкцию:
child_tag :include, ClsRuby::TagStringScalar
Класс ClsRuby::TagScalarVector отличается от ClsRuby::TagScalar только тем, что позволяет теги с несколькими скалярами в качестве значений. Например:
{include-paths <str> [<str> [<str>...]] } {init-bytes <uint> [<uint> [<uint>...]] }
Класс ClsRuby::TagScalarVector использует те же самые ключи :format и :constraint, что и класс ClsRuby::TagScalar.
Например, для того, чтобы разобрать следующие теги:
{device-init-info {devices <nonspace>+ } {init-bytes <uint:0..255>+ } {shutdown-bytes <uint:0..255>+ } }
можно воспользоваться тегами:
class TagDeviceInitInfo < ClsRuby::TagNoValue default_tag_params :name => 'device-init-info' mandatory_child_tag :devices, ClsRuby::TagScalarVector, :format => ClsRuby::SCALAR_NONSPACE_STRING mandatory_child_tag :init_bytes, ClsRuby::TagScalarVector, :format => ClsRuby::SCALAR_UINT, :constraint => 0..255, :name => 'init-bytes' mandatory_child_tag :shutdown_bytes, ClsRuby::TagScalarVector, :format => ClsRuby::SCALAR_UINT, :constraint => 0..255, :name => 'shutdown-bytes' ... end
Класс ClsRuby::TagVectorOfDifferentTags предназначен для случая, когда из входного потока нужно извлечь последовательность тегов с разными именами, но при этом необходимо сохранить их порядок следования. При этом теги могут иметь разные типы. Например, если нужно разбирать такой входной поток:
{book {part <str>}* {chapter <str>}* {section <str>}* {subsection <str>}* {subsubsection <str>}* {par <TagParagraph>}* {include <TagIncludeFragment>}* }
где теги {part}, {chapter}, {section} и др. могут чередоваться, но нужно сохранять их порядок следования относительно друг друга. Для этого можно воспользоваться конструкцией:
child_tag :book, ClsRuby::TagVectorOfDifferentTags, :nested_tags => [ { :name => 'part', :type => ClsRuby::TagStringScalar }, { :name => 'chapter', :type => ClsRuby::TagStringScalar }, { :name => 'section', :type => ClsRuby::TagStringScalar }, { :name => 'subsection', :type => ClsRuby::TagStringScalar }, { :name => 'subsubsection', :type => ClsRuby::TagStringScalar }, { :name => 'par', :type => TagParagraph }, { :name => 'include', :type => TagIncludeFragment } ]
Класс ClsRuby::TagVectorOfTags является упрощенным аналогом тега TagVectorOfDifferentTags – он позволяет разбирать последовательность одинаковых тегов во входном потоке (т.е. теги должны иметь одинаковые имена и один и тот же тип). Например:
{message {name <nonspace> } {field {name <str> } {type <str>}}* }
реализуется с помощью конструкций:
class TagField < ClsRuby::TagNoValue mandatory_child_tag :name, ClsRuby::TagStringScalar mandatory_child_tag :type, ClsRuby::TagStringScalar ... end class TagMessage < ClsRuby::TagNoValue mandatory_child_tag :name, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_NONSPACE_STRING child_tag :field, ClsRuby::TagVectorOfTags, :type => TagField ... end
# vim:ts=2:sts=2:sw=2:expandtab:ft=txt:tw=78
Generated with the Darkfish Rdoc Generator 2.