Object
Базовый класс CLS тегов.
Структура для хранения описания дочернего тега, полученного при помощи методов child_tag и mandatory_child_tag.
Вспомогательный метод для декларирования дочерних тегов во время описания собственного класса тега.
Пример использования.
Пусть необходимо парсить следующую структуру: {params {min <int:1..100>} {max <int:100..1000>} {password <str>} } Для этого можно применить тег: class TagParams < ClsRuby::TagNoValue child_tag :min, ClsRuby::TagScalar, :mandatory => true, :format => ClsRuby::SCALAR_INT, :constraint => 1..100 child_tag :max, ClsRuby::TagScalar, :mandatory => true, :format => ClsRuby::SCALAR_INT, :constraint => 100..1000 child_tag :password, ClsRuby::TagScalar, :mandatory => true, :format => ClsRuby::SCALAR_STRING default_tag_params :name => 'params' def query_params Params.new( @min.value, @max.value, @password.value ) end end
Если требуется, чтобы instance variables создавалось с одним именем (например, op_id), а тег во входном потоке имел иное название, то в списке параметров child_tag нужно указать значение с ключем :name
child_tag :op_id, ClsRuby::TagScalar, :name => 'op-id', :mandatory => true, :format => ClsRuby::SCALAR_STRING
Принцип использования см. в описании tag_metaclass.
# File lib/cls-ruby/tag.rb, line 206 def Tag.child_tag( name, type, params = {} ) tag_metaclass.tag_add_child_tag_info( name, type, params ) end
Вспомогательный метод, который позволяет задать параметры тега по умолчанию.
Если разрабатывается какой-то тег, который в большинстве случаев будет иметь одинаковое имя, то задать это имя можно двумя способами:
во-первых, можно сделать собственный конструктор:
class TagMy < ClsRuby::TagNoValue def initialize( params = {} ) super( { :name => 'my-tag' }.merge( params ) ) end ... end ... t = TagMy.new # Используется имя my-tag. c = TagMy.new( :name => 'custom-tag' ) # Имя custom-tag вместо my-tag.
во-вторых, точно такого же эффекта можно достичь посредством метаметода default_tag_params:
class TagMy < ClsRuby::TagNoValue default_tag_params :name => 'my-tag' ... end ... t = TagMy.new # Используется имя my-tag. c = TagMy.new( :name => 'custom-tag' ) # Имя custom-tag вместо my-tag.
Примечание. Если в цепочке базовых классов несколько раз используется default_tag_params, то все заданные параметры по умолчанию объединяются. Причем так, что значения из базовых классов затираются значениями из производных классов.
# File lib/cls-ruby/tag.rb, line 258 def Tag.default_tag_params( params ) self.send( :define_method, :tag_default_params ) do super().merge( params ) end end
Вспомогательный метод в дополнение к методу child_tag, который позволяет упростить объявление обязательных тегов.
Его использование:
mandatory_child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_INT
полностью эквивалентно более многословному использованию child_tag:
child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_INT, :mandatory => true
# File lib/cls-ruby/tag.rb, line 221 def Tag.mandatory_child_tag( name, type, params = {} ) child_tag( name, type, params.merge( :mandatory => true ) ) end
Конструктор.
Принимает аргументы в виде Hash. Распознаются ключи:
:name => имя тега. :owner => родительский тег. Автоматически добавляет себя в список дочерних тегов к родителю. :mandatory => true или false. Признак того, что тег должен быть обязательным. По умолчанию тег считается опциональным.
Остальные ключи игнорируются.
# File lib/cls-ruby/tag.rb, line 20 def initialize( args = {} ) @tag_params = tag_default_params.merge( args ) @tag_name = tag_params.fetch( :name, nil ) owner = tag_params.fetch( :owner, nil ) owner.tag_add( self ) if owner @tag_mandatory = tag_params.fetch( :mandatory, false ) @tag_defined = false @tag_children = [] tag_init_children_tags end
Добавление описания очередного дочернего тега.
Используется в реализации методов child_tag и mandatory_child_tag.
Подробнее см. tag_metaclass.
# File lib/cls-ruby/tag.rb, line 316 def tag_add_child_tag_info( name, type, params ) @tag_child_tags ||= [] @tag_child_tags << TagChildTagInfo.new( name, type, params ) end
Вспомогательный метод для реализации child_tag и mandatory_child_tag.
Принцип работы состоит в том, чтобы для текущего класса создать экземпляр класса-singleton в котором будут instance methods с именами tag_add_child_tag_info и tag_child_tags.
Фокус в том, что когда tag_metaclass вызывается на контексте класса Tag, то класс Tag расширяется методами tag_add_child_tag_info и tag_child_tags. Если же tag_metaclass вызывается на контексте производного от Tag класса, то производится расширение производного класса (т.к. self будет указывать на производный класс).
Таким образом, если есть цепочка наследования Tag <- First <- Second и в классах First, Second вызываются методы child_tag, то в классе First будет свой объект класса-singleton с описаниями присущих только классу First дочерних тегов. А в классе Second будет свой объект.
Следовательно, задача сводится к тому, чтобы в конструкторе Tag собрать описания из всех классов-singleton и сформировать полный список дочерних тегов. Для этого используется тот факт, что self в конструкторе Tag указывает на конкретный класс (т.е. на First или Second). Следовательно, можно проверить, есть ли в этом классе instance method с именем tag_child_tags и вызывать его. После чего перейти к базовому классу и т.д.:
child_tags = [] c = self.class while c if c.respond_to?( :tag_child_tags ) child_tags = c.tag_child_tags + child_tags end c = c.superclass end
Такой сложный механизм хранения описаний дочерних тегов потребовался для того, чтобы сохранить строгий порядок следования тегов – в каком порядке вызывались child_tag, в таком порядке дочерние теги должны добавляться к родительскому тегу.
# File lib/cls-ruby/tag.rb, line 309 def Tag.tag_metaclass class <<self # Добавление описания очередного дочернего тега. # # Используется в реализации методов child_tag и mandatory_child_tag. # # Подробнее см. tag_metaclass. def tag_add_child_tag_info( name, type, params ) @tag_child_tags ||= [] @tag_child_tags << TagChildTagInfo.new( name, type, params ) end # Возвращает список описаний всех дочерних тегов, полученных # с помощью методов child_tag и mandatory_child_tag. # # Подробнее см. tag_metaclass. def tag_child_tags @tag_child_tags || [] end end self end
Добавление дочернего тега в список дочерних тегов.
# File lib/cls-ruby/tag.rb, line 49 def tag_add( child ) @tag_children << child end
Сравнение имени тега с указанным именем.
Предназначен для перекрытия в производных классах для случая, когда один тег может иметь несколько имен.
# File lib/cls-ruby/tag.rb, line 44 def tag_compare_name( name ) @tag_name == name end
Является ли тег полностью определенным в процессе разбора?
По умолчанию возвращает значение атрибута @tag_defined (он принимает значение true либо в методе tag_on_finish, либо в методе tag_make_defined).
# File lib/cls-ruby/tag.rb, line 65 def tag_defined?; @tag_defined; end
Выполнить форматирование тега.
Объект formatter поддерживает интерфейс TagFormatter.
Реализация в базовом классе выполняет следующие действия:
вызывает у formatter метод start;
вызывает собственный метод tag_on_format;
вызывает метод tag_format у всех дочерних тегов;
вызывает у formatter метод finish.
Таким образом, если производному классу нужно отобразить собственное содержимое, то он должен переопределить у себя метод tag_on_format.
# File lib/cls-ruby/tag.rb, line 95 def tag_format( formatter ) if tag_defined? formatter.start( tag_name ) tag_on_format( formatter ) tag_tags.each do |child| child.tag_format( formatter ) end formatter.finish end self end
Установить признак, что тег полностью определен.
# File lib/cls-ruby/tag.rb, line 68 def tag_make_defined; @tag_defined = true; end
Является ли тег обязательным?
По умолчанию возвращает значение атрибута @tag_mandatory.
# File lib/cls-ruby/tag.rb, line 59 def tag_mandatory?; @tag_mandatory; end
Имя тега.
Возвращает nil, если в конструкторе имя тега не было задано.
# File lib/cls-ruby/tag.rb, line 38 def tag_name; @tag_name; end
Реакция на завершение парсинга тега во входном потоке.
Просматривает все дочерние теги и если находит неопределенный обязательный тег, то порождает исключение UndefinedMandatoryTagEx.
После чего делает самого себя полностью определенным.
# File lib/cls-ruby/tag.rb, line 120 def tag_on_finish @tag_children.each do |tag| raise UndefinedMandatoryTagEx.new( tag.tag_name ) if tag.tag_mandatory? && !tag.tag_defined? end tag_make_defined end
Вызывается для форматирования собственного содержимого.
В базовом классе ничего не делает. Предназначен для переопределения в производных классах.
# File lib/cls-ruby/tag.rb, line 161 def tag_on_format( formatter ) end
Реакция на начало парсинга тега во входном потоке.
В базовом классе ничего не делает. Предназначен для предоставления возможности перекрытия в производных классах.
# File lib/cls-ruby/tag.rb, line 112 def tag_on_start( name ); end
Реакция на завершение разбора дочернего тега во входном потоке.
Ничего не делает. Метод предназначен для перекрытия в производных классах для предоставления возможности сделать что-нибудь полезное.
# File lib/cls-ruby/tag.rb, line 133 def tag_on_tag( tag ) end
Реакция на токен :tok_nonspace.
Порождает исключение UnexpectedTokenEx.
# File lib/cls-ruby/tag.rb, line 144 def tag_on_tok_nonspace( token ) raise UnexpectedTokenEx.new( "unexpected token :tok_nonspace ('#{token}')" ) end
Реакция на токен :tok_space.
Ничего не делает.
# File lib/cls-ruby/tag.rb, line 139 def tag_on_tok_space( token ); end
Реакция на токен :tok_string.
Порождает исключение UnexpectedTokenEx.
# File lib/cls-ruby/tag.rb, line 152 def tag_on_tok_string( token ) raise UnexpectedTokenEx.new( "unexpected token :tok_string ('#{token}')" ) end
Сбросить содержимое тега.
Переводит тег в состояние, предшествующее парсингу.
По умолчанию вызывает метод tag_reset у всех дочерних тегов, после чего устанавливает @tag_defined в false.
# File lib/cls-ruby/tag.rb, line 76 def tag_reset @tag_children.each do |tag| tag.tag_reset end @tag_defined = false end
Возвращает список параметров тега по умолчанию.
Этот список затем в конструкторе дополняется явно указанными параметрами и уже из результирующего списка конструктор берет значения для инициализации тега.
Производный класс может переопределить данный метод, чтобы назначить собственные параметры по умолчанию.
См. так же default_tag_params
В базовом классе возвращает пустой Hash.
# File lib/cls-ruby/tag.rb, line 351 def tag_default_params {} end
Вспомогательный метод, который находит в параметрах тега указанный ключ и запускает указанный блок со значением данного ключа.
Данный метод позволяет упростить обработку, например, ключей :value, когда тег нужно сразу же в конструкторе инициализировать. Простое решение этой задачи выглядит как:
def initialize( params ) super( params ) v = tag_params.fetch( :value, nil ) if v ... tag_make_defined end end
Основная проблема здесь в том, что можно забыть обратиться к tag_params, чтобы получить доступ ко всем параметрам, включая параметры по-умолчанию. С использованием метода tag_handle_opt_param этот код будет выглядеть так:
def initialize( params ) super( params ) tag_handle_opt_param( :value ) do |v| ... tag_make_defined end end
Возвращает значение блока, если ключ был найден и блок вызывался или nil, если ключ найден не был.
# File lib/cls-ruby/tag.rb, line 416 def tag_handle_opt_param( key, &block ) v = tag_params.fetch( key, nil ) if v block.call( v ) else nil end end
Generated with the Darkfish Rdoc Generator 2.