Class Index [+]

Quicksearch

Принципы работы с Cls. Краткое описание штаных тегов ClsRuby

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

ClsRuby::Tag

Класс ClsRuby::Tag является базовым классом для всех тегов. Он не предназначен для непосредственного использования, т.к. в своих методах tag_on_tok_nonspace и tag_on_tok_string порождает исключение ClsRuby::UnexpectedTokenEx.

Однако, класс ClsRuby::Tag может использоваться в качестве базового класса для создания собственных тегов с нетривиальной логикой, когда ни один из штатных тегов ClsRuby не подходит по каким-либо причинам.

ClsRuby::TagAny

Класс 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::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::TagScalar, так же как и ClsRuby::TagNoValue, является одним из самых широко используемых классов тегов. Его задача – разбирать и сохранять единственное скалярное значение внутри тега. Т.е. ClsRuby::TagScalar предназначен для разбора тегов вида:

{name <str> }
{location <str> }
{min <int> }
{max <int> }

Тип скаляра определяется с помощью аргумента :format, передаваемого в конструктор тега. В ClsRuby реализованы следующие штатные форматы:

Если готовых форматов недостаточно, то можно реализовать собственный класс форматера, удовлетворяющий интерфейсу 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::TagFloatScalar

Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_FLOAT. Т.е. для разбора тега:

{min <float> }

достаточно использовать конструкцию:

child_tag :min, ClsRuby::TagFloatScalar

ClsRuby::TagIntScalar

Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_INT. Т.е. для разбора тега:

{min <int> }

достаточно использовать конструкцию:

child_tag :min, ClsRuby::TagIntScalar

ClsRuby::TagUintScalar

Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_UINT. Т.е. для разбора тега:

{min <uint> }

достаточно использовать конструкцию:

child_tag :min, ClsRuby::TagUintScalar

ClsRuby::TagStringScalar

Является синонимом для ClsRuby::TagScalar с типом форматирования ClsRuby::SCALAR_STRING. Т.е. для разбора тега:

{include <str> }

достаточно использовать конструкцию:

child_tag :include, ClsRuby::TagStringScalar

ClsRuby::TagScalarVector

Класс 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

Класс 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

Класс 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

[Validate]

Generated with the Darkfish Rdoc Generator 2.