Advertisement
m0user

Data::Mini 0.91, moderning stage..

Sep 30th, 2014
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 85.86 KB | None | 0 0
  1. # ai, sts=2, ts=4, sw=2;
  2. package Data::Mini; # FIXME: this module work's isn't correct!
  3. BEGIN {
  4.     $Data::Mini::VERSION         = 0.91;
  5.     $Data::Mini::AT              = '2014-09-30';
  6.     $Data::Mini::AUTHORITY       = 'Михаил Шелковой ( мишъцх )';
  7.     $Data::Mini::TEAMGROUP       = 'ust:m0userZ';
  8.     $Data::Mini::SINCE           = 'at 2013-07-13 from Config::Tiny by ADAMK';
  9.     $Data::Mini::STATUS          = 'moderning';
  10.     $Data::Mini::REQUIRE         = [qw/ Mo Params::Validate JSON /];
  11.     $Data::Mini::JSON::VERSION   = 0.8;
  12.     $Data::Mini::JSON::AT        = '2014.09.25';
  13.     $Data::Mini::JSON::AUTHOR    = 'Miki m0user';
  14.     $Data::Mini::JSON::TEAMGROUP = 'Информационная безопасность Украины';
  15.     $Data::Mini::JSON::SINCE     = '2014.09.24';
  16. };
  17.  
  18. # OS
  19. #
  20. use Mo qw/ default build is required chain /;
  21.  
  22. =pod
  23.  
  24. =encoding utf8
  25.  
  26. =head1 NAME
  27.  
  28. Data::Mini - Модуль для работы с файлами `ini'.
  29.  Смотри "Ini files specification"
  30.  
  31. =head1 TOC, "Table Of Contents"
  32.  
  33. Data::Mini
  34. Data::Mini::JSON
  35.  
  36. =head1 DESCRIPTION, "Описание"
  37.  
  38. TODO
  39.  
  40. =head2 Оссобенности
  41.  
  42.  *  Автоматическое открытие и закрытие файла `path'
  43.  *  Возможно считывать с нескольких файлов в одну структуру
  44.  *  Возможно дополнять в структуру данные через ключ `source'
  45.  *  Возможно вообще не использовать файл, используя только `memory'
  46.  *  Возможность читать с одного файла а писать в другой
  47.  *  Легкий и удобный интерфейс для разного рода задач!
  48.  *  Полный контроль. смотри `new'
  49.  *  Исключение не нужного `exclude'. Осторожно работает 'autowrite'
  50.  *  Может создать дерего дерикторий. `make_directory'
  51.  *  Возможность использовать вложеные структуры, используя `json'
  52.  *  Секции, ключи могут использоваться по умолчанию.
  53.  *  Требуются знания PCRE!
  54.  *  Читайте дальше!
  55.  *  Осторожно работает 'autowrite' !!
  56.  *  Спасиба!
  57.  
  58.  
  59. =head1 TODO, FIXME
  60.  
  61. 2014-09-29: json method
  62. 2014-09-28: multi-types, see: exclude
  63. 2014-09-25: section & key for `_from_ini_string'
  64. 2014-09-22: FIXME: complete the `read' & `write'!
  65. 2014-09-21: delimeter => default_delimeter
  66. 2014-09-19: FIXME: new, read, inc, dec, find ( multi-typing )
  67. 2014-09-18: Один ангумент вместо двоих для `key': json => 1
  68. 2014-09-18: FIXME: Автоматическое в `key', определять и декодировать JSON
  69. 2014-09-18: find %$args modification
  70. 2014-09-15: POD for "new/BUILD" items
  71. 2014-09-15: !! POD !!
  72. 2014-09-15: TODO: exclude for new (BUILD)
  73. 2014-09-15: Params::Validate
  74. 2014-09-14: тип qr// для PCRE вместо обычных string
  75.  
  76. =head1 SYNOPSIS
  77.  
  78.  Module variables
  79.     VERSION, AT, AUTHORITY, TEAMGROUP, CODENAME, SINCE
  80.  
  81.  Main methods
  82.  m  new          ( args: path, source, autoread, autowrite, dump_indentlevel,
  83.                          dump_sortkeys, debug, memory, make_directory,
  84.                          default_section, default_key, default_json_key,
  85.                          delimeter )
  86.  m  read         ( args: path, source, make_directory, delimeter, write_path )
  87.  m  write        ( args: path, source, make_directory, delimeter )
  88.  m  reset        ( args: memory, source, delimeter )
  89.  m  rename       ( args: file, section, key) # TODO
  90.  m  dump         ( args: section, dump_indentlevel, dump_sortkeys )
  91.  F  delete       ( args: path, section, key ) # TODO
  92.  
  93.  Miscellaneous methods
  94.  m  typeof       ( args: section, key )
  95.  m  exists       ( args: section, key )
  96.  m  defined      ( args: section, key )
  97.  m  find         ( args: section, key, value )
  98.  m  SECTIONS     ( args: section, counter  ) # FIXME
  99.  m  KEYS         ( args: section, counter )
  100.  m  VALUES       ( args: section, counter )
  101.  
  102.  Get/Set methods
  103.  m  key          ( args: section, key, value, existonly )
  104.  m  inc          ( args: section, key, by, existonly )
  105.  m  dec          ( args: section, key, by, existonly )
  106.  TE href         ( args: section, href, existonly )
  107.  M  exclude      ( args: section, key, value )
  108.  TO json         ( args: section, key, json_key,  )
  109.  
  110.  Private/Closed methods
  111.  m  _from_ini_string ( args: section, key, source )
  112.  m  _to_ini_string   ( args: section, key, delimeter ) # TODO
  113.  
  114.  Defaults settings
  115.     defaults                ( args: section, key, json_key, delimeter )
  116.     default_section         ( '_' )
  117.     default_key             ( 'default' )
  118.     default_json_key        ( 'default' )
  119.     delimeter               ( '=' )
  120.  
  121. =cut
  122.  
  123. use Carp 'confess';
  124. use feature 'say';
  125. use Data::Dumper;
  126. use JSON qw/ encode_json decode_json /;
  127. use Params::Validate ':all';
  128.  
  129. # FIXME: Re: Data::Mini validate templates for params
  130. #
  131. our $Re = {
  132.   section   => qr/^[\S]+$/,
  133.   key       => qr/^[\S]+$/,
  134.   json_key  => qr/^[\S]+$/,
  135.   value     => qr/.+/,
  136.   path      => qr/.+/,  # FIXME
  137.   source    => qr/.+/,
  138.   by        => qr/^\d+$/,   # inc, dec
  139.   topic     => qr/^.+$/,
  140.   debug     => qr/^(?:0|1|2|3)$/,   # TODO
  141. };
  142.  
  143.  
  144.  
  145.  
  146.  
  147. ##############################################################################
  148. # Code segment
  149. # ----------------------------------------------------------------------------
  150.  
  151. =pod
  152.  
  153. =head1 METHODS
  154.  
  155. Далее подробно рассмотрим каждую функцию.
  156.  
  157. =head1 new %params, ( BUILD/DESTROY )
  158.  
  159. Каждый объект Data::Mini начинает работу из его создания. Именно с функции `new' начинает работать все остальное в этой библиотеке.
  160.  
  161. =head2 ARGS
  162.  
  163.  local $\ = undef;
  164.  my $ini = Data::Mini->new(
  165.     path                    => 'path',
  166.     source                  => <DATA>,
  167.     autoread                => 1,
  168.     autowrite               => 1,
  169.     write_path              => 'path',      # TODO
  170.     dump_indentlevel        => 2,
  171.     dump_sortkeys           => 1,
  172.     debug                   => 0,
  173.     memory                  => 0,
  174.     make_directory          => 0,
  175.     default_section         => '_',
  176.     default_key             => 'default',
  177.     default_json_key        => 'default',
  178.     backup                  => "",          # TODO
  179.     exclude                 => undef,       # TODO
  180.     delimeter               => '=',
  181.     allow_spaces            => 0,           # TODO
  182.  );
  183.  
  184. =cut
  185.  
  186. has timestamp   => ( is => 'ro', default => sub { time }, lazy => 0 );
  187. has path        => ( is => 'rw', default => sub { undef } );
  188. has write_path  => ( is => 'rw', default => sub { undef } );                # TODO
  189. has source      => ( is => 'rw', required => 0, default => sub { undef } );
  190. has autoread    => ( is => 'rw', required => 0, default => 1, chain => 1);
  191. has autowrite   => ( is => 'rw', required => 0, default => 1, chain => 1 );
  192. has debug       => ( is => 'ro', default => 0, chain => 1 );
  193. has memory      => ( is => 'ro', default => 0 );
  194. has make_directory     => ( is => 'rw', default => 0 );
  195. has JSON        => ( is => 'rw', default => sub { undef } );
  196.  
  197. # dump
  198. has dump_indentlevel   => ( is => 'rw', default => 1, chain => 1 );
  199. has dump_sortkeys      => ( is => 'rw', default => 1, chain => 1 );
  200.  
  201. # defaults
  202. has delimeter           => ( is => 'rw', default => '=', chain => 1 );      # FIXME all!
  203. has default_section     => ( is => 'rw', default => '_', chain => 1 );
  204. has default_key         => ( is => 'rw'. default => 'default', chain => 1 );
  205. has default_json_key    => ( is => 'rw', default => 'default', chain => 1 );
  206. has allow_spaces        => ( is => 'rw', default => 0, chain => 1 );        # TODO
  207.  
  208. =pod
  209.  
  210. =head2 %$args - Аргументы метода `new'
  211.  
  212. Следующии аргументы используются для создания объекта
  213.  
  214. =over
  215.  
  216. =item path => Scalar | ArrayRef (TODO)
  217.  
  218. Путь к файлу(ам) конфига
  219.  
  220.  Data::Mini->new( path => 'path' );
  221.  Data::Mini->new( path => [qw/ path1 path2 /], write_path => 'path3' ); # TODO
  222.  
  223. *Конфликт в `write'. он должен писать в один файл, а там `ArrayRef'
  224.  
  225. =item source => SCALAR
  226.  
  227. Дополнить данные конфига из переменной. Оссобено удобно добавлять стартовые настройки прямо из конца модуля, как в примере.
  228.  
  229.  {
  230.     undef $/;
  231.     my $source = <DATA>;
  232.     $ini = Data::Mini->new(
  233.         path            => '/path/to/file.ini',
  234.         source          => <DATA>,
  235.         default_section => 'SECTION',
  236.         default_key     => 'KEY'
  237.     );
  238.     $ini->defaults; # restore defaults: section, key, delimeter
  239.    
  240.     my $ini = Data::Mini->new( source => <DATA>, memory => 1,
  241.         delimeter => '<__NULL__>' );
  242.     $ini->write( path => '/path/to/clone.ini', make_directory => 1,
  243.         delimeter => 1 );
  244.  }
  245.  
  246.  __DATA__
  247.  
  248.  [section]
  249.  key=value
  250.  
  251. =item memory => Bool
  252.  
  253. Не ипользуем файлы.. Хотя можно записать ввиде "клона", через `write'
  254.  
  255.  my $ini = Data::Mini->new( memory => 1 );
  256.  my $ini = Data::Mini->new( memory => 1, source => <DATA> );
  257.  
  258.  $ini->write( path => 'clone.ini' );
  259.  
  260. =item autoread => Bool
  261.  
  262. Прочитать данные при создании объекта
  263.  
  264.  my $ini = Data::Mini->new( path => 'path', autoread => 1 );
  265.  $ini->autoread( 1 );
  266.  say $ini->autoread;
  267.  
  268. =item autowrite => Bool
  269.  
  270. Записать данные в конфиг файл при закрытии объекта
  271.  
  272.  my $ini => Data::Mini->new( path => 'path', autowrite => 0 );
  273.  $ini->autowrite( 1 );
  274.  say $ini->autowrite;
  275.  
  276. =item dump_indentlevel => Num
  277.  
  278. Уровень отступов для метода dump
  279.  
  280.  my $ini => Data::Mini->new( path => 'path', dump_indentlevel => 3 );
  281.  $ini->dump_indentlevel( 1 );
  282.  say $ini->dump_indentlevel;
  283.  
  284. =item dump_sortkeys => Bool
  285.  
  286. Сортировка ключей для метода dump
  287.  my $ini => Data::Mini->new( path => 'path', dump_sortkeys => 1 );
  288.  
  289.  $ini->dump_sortkeys( 0 );
  290.  say $ini->dump_sortkeys;
  291.  
  292. =item make_directory => Bool
  293.  
  294. Создает дерево директорий в файловой системе
  295.  
  296.  my $mini = Data::Mini->new( path => '/tmp/dir1/dir2/dir3/file.ini',
  297.   make_directory => 1 );
  298.  
  299. =item default_section => Str
  300.  
  301.  Задает имя секции по умолчанию
  302.  
  303.  $mini->default_section( 'new_default_section_name' );
  304.  $mini->defaults; # restore default key and section
  305.  
  306.  see also: defaults, default_key
  307.  
  308. =item default_key => Str
  309.  
  310.  Задает имя ключа по умолчанию
  311.  
  312.  $mini->default_key( 'new_default_key_name' );
  313.  $mini->defaults; # restore default key and section
  314.  
  315.  see also: defaults, default_section, delimeter
  316.  
  317. =item default_json_key => Str
  318.  
  319. TODO
  320.  
  321. =item delimeter => Str
  322.  
  323.  Изменить разделитель полей property=value
  324.  
  325.  $mini = Data::Mini->new(memory => 1, autoread => 0)->read( delimeter => 1 );
  326.  $mini->delimeter( 'new' );
  327.  $mini->write( path => '/path/to.ini', delimeter => ':', make_directory => 1 );
  328.  
  329.  delimeter => Str
  330.   Установить следуюший символ, как разделитель. property=value
  331.  
  332.  delimeter => Bool (TODO)
  333.   Установить символ разделитель по умолчанию.
  334.     delimeter => 1
  335.  
  336.  add me to:
  337.   # write
  338.   # value 1 for delimeter is a default delimeter character for a write method
  339.   $mini->write( path => '/path/to.ini', delimeter => 1, make_directory => 1 );
  340.  
  341. =item allow_spaces => Bool, TODO: releaseme
  342.  
  343. Разрешить использовать пробелы в именах секций и ключей. По умолчанию выключено
  344.  
  345.  $mini = Data::Mini->new( memory => 1, autoread => 0 )->read(
  346.    delimeter => '<=>', allow_spaces => 1 );
  347.  
  348. =item debug => Bool
  349.  
  350. Используется для разработчика основном.
  351.  
  352. =back
  353.  
  354. =cut
  355. sub BUILD {
  356.     my $self = shift;
  357.     # autoread
  358.     $self->read if $self->autoread;
  359.  
  360.     # DEBUG
  361.     say __PACKAGE__."::BUILD" if $self->debug;
  362. }
  363. sub DESTROY {
  364.     my $self = shift;
  365.     # autowrite
  366.     $self->write if $self->autowrite;
  367.  
  368.     # DEBUG
  369.     say __PACKAGE__."::DESTROY" if $self->debug;
  370. }
  371.  
  372.  
  373.  
  374. #
  375. # ----------------------------------------------------------------------------
  376. =pod
  377.  
  378. =head1 json
  379.  
  380. Используем вложенные структуры с помощью JSON
  381.  
  382. =head2 synopsis
  383.  
  384.  $mini->json( section => 'name', key => [qw/ name /], json_key => qw/ name /,
  385.    set => { k => v }, inc => [qw/ name /], dec => 'name', by => 2,
  386.    undef => [qw/ name /], delete => [qw/ name /],
  387.    exists => 'name', defined => 'name', reset => 1 );
  388.  
  389. =head2 $args
  390.  
  391. TODO
  392.  
  393. =over
  394.  
  395. =item section => SCALAR |SCALARREF |ARRAYREF
  396.  
  397. TODO
  398.  
  399.  $mini->json( section => 'name' );
  400.  $mini->json( section => qr/pcre/ );
  401.  $mini->json( section => [qw/ name /] );
  402.  
  403. =item key => SCALAR |SCALARREF |ARRAYREF
  404.  
  405. TODO
  406.  
  407.  $mini->json( key => 'name' );
  408.  $mini->json( key => qr/pcre/ );
  409.  $mini->json( key => [qw/ name /] );
  410.  
  411. =item json_key => SCALAR |SCALARREF |ARRAYREF
  412.  
  413. TODO
  414.  
  415.  $mini->json( json_key => 'name' );
  416.  $mini->json( json_key => qr/pcre/ );
  417.  $mini->json( json_key => [qw/ name /] );
  418.  
  419. =item set => HASHREF
  420.  
  421. TODO
  422.  
  423.  $mini->json( set => { k => v } );
  424.  
  425. =item inc => SCALAR |SCALARREF |ARRAYREF
  426.  
  427. TODO
  428.  
  429.  $mini->json( inc => 'name', by => 1 );
  430.  $mini->json( inc => qr/pcre/, by => 3 );
  431.  $mini->json( inc => [qw/ name /], by => 5 );
  432.  
  433. =item dec => SCALAR |SCALARREF |ARRAYREF
  434.  
  435. TODO
  436.  
  437.  $mini->json( dec => 'name', by => 1 );
  438.  $mini->json( dec => qr/pcre/, by => 3 );
  439.  $mini->json( dec => [qw/ name /], by => 5 );
  440.  
  441. =item by => SCALAR
  442.  
  443. TODO
  444.  
  445.  $mini->json( inc => 'name', by => 1 );
  446.  $mini->json( inc => qr/pcre/, by => 3 );
  447.  $mini->json( dec => [qw/ name /], by => 5 );
  448.  
  449. =item undef => SCALAR |SCALARREF |ARRAYREF
  450.  
  451. TODO
  452.  
  453.  $mini->json( undef => 'name' );
  454.  $mini->json( undef => qr/pcre/ );
  455.  $mini->json( undef => [qw/ name /] );
  456.  
  457. =item delete => SCALAR |SCALARREF |ARRAYREF
  458.  
  459. TODO
  460.  
  461.  $mini->json( delete => 'name' );
  462.  $mini->json( delete => qr/pcre/ );
  463.  $mini->json( delete => [qw/ name /] );
  464.  
  465. =item exists => SCALAR
  466.  
  467. TODO
  468.  
  469.  $mini->json( json_key => 'name', exists => 1 );
  470.  
  471. =item defined => SCALAR
  472.  
  473. TODO
  474.  
  475.  $mini->json( json_key => 'name', defined => 1 );
  476.  
  477. =back
  478.  
  479. =head2 See also
  480.  
  481. Data::Mini::JSON
  482.  
  483. =cut
  484. sub json {
  485.     my $self = shift;
  486.     my $args = \%{validate(@_, {
  487.         section     => {
  488.             type        => SCALAR |ARRAYREF,
  489.             regex       => $Re->{'section'},
  490.             default     => $self->default_section,
  491.             optional    => 1,
  492.         },
  493.         key         => {
  494.             type        => SCALAR |ARRAYREF,
  495.             regex       => $Re->{'key'},
  496.             default     => $self->default_key,
  497.             optional    => 1,
  498.         },
  499.         json_key    => {
  500.             type        => SCALAR |ARRAYREF,
  501.             regex       => $Re->{'json_key'},
  502.             default     => $self->default_json_key,
  503.             optional    => 1,
  504.         },
  505.         set         => {
  506.             type        => HASHREF,
  507.             optional    => 1,
  508.         },
  509.         inc         => {
  510.             type        => SCALAR |ARRAYREF,
  511.             regex       => $Re->{'key'},
  512.             optional    => 1,
  513.         },
  514.         dec         => {
  515.             type        => SCALAR |ARRAYREF,
  516.             regex       => $Re->{'key'},
  517.             optional    => 1,
  518.         },
  519.         by          => {
  520.             type        => SCALAR,
  521.             regex       => $Re->{'by'},
  522.             default     => 1,
  523.             optional    => 1,
  524.         },
  525.         undef       => {
  526.             type        => SCALAR |ARRAYREF,
  527.             regex       => $Re->{'key'},
  528.             optional    => 1,
  529.         },
  530.         delete      => {
  531.             type        => SCALAR |ARRAYREF,
  532.             regex       => $Re->{'key'},
  533.             optional    => 1,
  534.         },
  535.     })};
  536.  
  537.     $self->Data( Data::Mini::JSON->new );
  538.  
  539. #   section
  540. #   key
  541. #   json_key
  542. #   set
  543. #   inc
  544. #   dec
  545. #   undef
  546. #   delete
  547.  
  548.     return $self;
  549. }
  550.  
  551.  
  552. # TODO
  553. # ----------------------------------------------------------------------------
  554. =pod
  555.  
  556. =head1 exclude ( keys: section, key, value, reset )
  557.  
  558. Исключить секции или ключи, а также ключи по их значению. Кароче это крутая фича для извлечения всякого не нужного хлама, который не нужный для конкретной сессии. Будьте внимательны, работает autowrite, который на конце сессии автоматически сохраняет все изменения! К счастью это можно отключить.
  559.  
  560.     $ini->autowrite( 0 );
  561.  
  562. Часто бывает полезно при использывании большых ненужных объемов данных, которые не используються. Для минимизации использывания памяти достаточно использывать только нужное, а всё остальное можно исключить. По завершению работы exclude возвращает все секции и ключи, которые были удалены - ввиде хэш-структуры. А в самом объекте 'Data::Mini' скилет данных будет, без исключенных секций и кдючей
  563.  
  564. =head2 %$args properties
  565.  
  566. Ключи метода. Всего _
  567.  
  568. =over
  569.  
  570. =item section => SCALAR | SCALARREF | ARRAYREF
  571.  
  572.  $ini->exclude( section => 'name' );
  573.  $ini->exclude( section => qr/pcre/ );
  574.  $ini->exclude( section => [qw/ name /] );
  575.  
  576. =item key =>  SCALAR | SCALARREF | ARRAYREF
  577.  
  578.  $ini->exclude( key => 'name' );
  579.  $ini->exclude( key => qr/pcre/ );
  580.  $ini->exclude( key => [qw/ name /] );
  581.  
  582. =item value => SCALARREF
  583.  
  584.  $ini->exclude( value => qr/pcre/ );
  585.  
  586. =item reset => BOOLEAN
  587.  
  588. TODO
  589.  
  590. =back
  591.  
  592. =head2 TODO
  593.  
  594.  2014-09-22:    $args::reverse => Bool
  595.  2014-09-15:    $args::reset => Bool
  596.  
  597. =cut
  598. sub exclude {
  599.     my $self = shift;
  600.     my $args = \%{validate(@_, {
  601.         section => {
  602.             type        => SCALAR |SCALARREF | ARRAYREF,
  603.             regex       => $Re->{ 'section' },
  604.             default     => $self->default_section,
  605.             optional    => 1,
  606.         },
  607.         key     => {
  608.             type        => SCALAR |SCALARREF | ARRAYREF,
  609.             regex       => $Re->{ 'key' },
  610.             optional    => 1,
  611.         },
  612.         value   => {
  613.             type        => SCALARREF,
  614.             regex       => $Re->{ 'value' },
  615.             optional    => 1,
  616.         },
  617.         reset   => {
  618.             type        => BOOLEAN,
  619.             optional    => 1,
  620.         },
  621.     })};
  622.     my $excluded = {};
  623.  
  624.     # zero-length args
  625.     return undef unless defined $args;
  626.  
  627.     # Stage_1: preparing %excluded
  628.     #
  629.  
  630.     # matching sections
  631.     if ( ref( $args->{'section'}) eq 'ARRAY' ) {        # by ArrayRef
  632.         %{$excluded} = map { $_ => $self->{'Config'}->{$_} }
  633.             @{ $args->{'section'} };
  634.     } elsif ( ref( $args->{'section'} ) eq 'Regexp' ) { # by PCRE
  635.         %{$excluded} = map {
  636.             unless( ref($_) eq 'HASH' ) {
  637.                 $_ => $self->{'Config'}->{$_};
  638.             } else { } # do nothing
  639.         } $self->find( section => $args->{'section'} );
  640.     }
  641.  
  642.  
  643.     # per section
  644.     foreach my $section ( sort keys %$excluded ) {
  645.         # check for section existion
  646.         if ( ! $self->exists( section => $section ) ) {
  647.             delete $excluded->{$section};
  648.             next;
  649.         }
  650.         # delete sections only
  651.         #
  652.         if ( ! exists $args->{'key'} && ! exists $args->{'value'} ) {
  653.             # new copy
  654.             $excluded->{$section} = $self->{'Config'}->{ $section };
  655.             delete $self->{'Config'}->{ $section };
  656.         }
  657.         # delete by section-keys
  658.         # example: $ini->exclude( section => '\w+', key => '\d+' );
  659.         # example: $ini->exclude( section => ['zero_0'], key => '\d+' );
  660.         # example: $ini->exclude( section => ['zero_0'], key => ['key'] );
  661.         elsif ( exists $args->{'key'} && ! exists $args->{'value'} ) {
  662.             # by ArrayRef
  663.             if ( ref($args->{'key'}) eq 'ARRAY' ) {
  664.                 foreach ( @{$args->{'key'}} ) {
  665.                     $excluded->{$section}->{"$_"}
  666.                         = $self->{'Config'}->{ $section }->{$_};
  667.                     delete $self->{'Config'}->{
  668.                         $section }->{ "$_" };
  669.                     next unless $self->exists( section => $section, # %excluded
  670.                         key => $_ );
  671.                 }
  672.             # by PCRE
  673.             } elsif ( ref($args->{'key'}) eq 'Regexp' ) {
  674.                 my @excluded = grep /$args->{'key'}/,
  675.                     keys %{ $self->{'Config'}->{$section} };
  676.                 foreach ( @excluded ) {
  677.                     $excluded->{$section}->{"$_"}
  678.                         = $self->{'Config'}->{ $section }->{$_}; # %excluded
  679.                     delete $self->{'Config'}->{$section}->{"$_"};
  680.                 }
  681.             }
  682.             # remove section if key-length zero
  683.             delete $self->{'Config'}->{$section}
  684.                 unless keys %{$self->{'Config'}->{$section}};
  685.         }
  686.         # delete by section-key-values (by PCRE only)
  687.         # example: $ini->exclude( section => ['zero_0'], value => '\d+' );
  688.         elsif ( exists $args->{'value'}
  689.                 and ref ($args->{'value'}) eq 'Regexp' ) {
  690.             # matching
  691.             my %exc = grep /$args->{'value'}/,
  692.                     %{ $self->{'Config'}->{$section} };
  693.             # deletion
  694.             $excluded->{ $section } = \%exc;
  695.             foreach ( keys %exc ) {
  696.                 delete $self->{'Config'}->{$section}->{"$_"};
  697.             }
  698.             # remove section if key-length zero
  699.             delete $self->{'Config'}->{$section}
  700.                 unless keys %{$self->{'Config'}->{$section}};
  701.         }
  702.     }
  703.  
  704.     return keys %$excluded ? $excluded : undef;
  705. }
  706.  
  707.  
  708.  
  709.  
  710.  
  711.  
  712.  
  713. # ----------------------------------------------------------------------------
  714. =pod
  715.  
  716. =head1 defaults ( %$args: section, key, delimeter )
  717.  
  718.  Временно на время сесии изменить стандартные настройки по умолчанию
  719.  
  720.  # Restore defaults
  721.  $ini->defaults;
  722.  $ini->defaults( section => 1 );   # restore section only
  723.  $ini->defaults( key => 1 );       # restore key only
  724.  $ini->defaults( delimeter => 1 ); # restore delimeter only
  725.  $ini->defaults( delimeter => 1, key => 1 ); # restore delimeter & key
  726.  
  727.  
  728.  # set defaults
  729.  my $ini = $ini->defaults( section => '_' );
  730.  my $ini = $ini->defaults( key => 'default' );
  731.  
  732. =head2 setters/accessors
  733.  
  734.  default_section, default_key
  735.  
  736. =head2 TODO
  737.  
  738.  2014-09-29: default json_key
  739.  
  740.  $ini->defaults( section => 1 );        # restore original section name
  741.  $ini->defaults( section => 'new' );    # set new default section name
  742.  
  743. =cut
  744. sub defaults {
  745.     my $self = shift;
  746.     my $args = \%{validate(@_, {
  747.         section => {
  748.             type        => SCALAR,
  749.             regex       => $Re->{'section'},
  750.             optional    => 1,
  751.         },
  752.         key     => {
  753.             type        => SCALAR,
  754.             regex       => $Re->{'key'},
  755.             optional    => 1,
  756.         },
  757.         json_key        => {
  758.             type        => SCALAR,
  759.             regex       => $Re->{'json_key'},
  760.             optional    => 1,
  761.         },
  762.         delimeter => {
  763.             type        => SCALAR,
  764.             regex       => $Re->{'delimeter'},
  765.             optional    => 1,
  766.         },
  767.     })};
  768.  
  769.     # restore all (by chain feature)
  770.     unless ( @_ ) {
  771.         $self->default_section( '_' )->default_key( 'default' )
  772.             ->default_json_key( 'default' )->delimeter( '=' );
  773.     }
  774.  
  775.     #=> section
  776.     #
  777.     # set a section name by user given
  778.     if ( ! ref($args->{'section'}) && $args->{'section'} != 1 ) {
  779.         $self->default_section( $args->{'section'} );
  780.     }
  781.     # restore a section
  782.     elsif ( ! ref($args->{'section'}) && $args->{'section'} == 1 ) {
  783.         $self->default_section( '_' );
  784.     }
  785.  
  786.  
  787.     #=> key
  788.     #
  789.     # set a key name by user given
  790.     if ( ! ref($args->{'key'}) && $args->{'key'} != 1 ) {
  791.         $self->default_key( $args->{'key'} );
  792.     }
  793.     # restore a key
  794.     elsif ( ! ref($args->{'key'}) && $args->{'key'} == 1 ) {
  795.         $self->default_key( 'default' );
  796.     }
  797.  
  798.  
  799.     #=> json_key
  800.     #
  801.     # set a json_key character by user given
  802.     if ( ! ref($args->{'json_key'}) && $args->{'json_key'} != 1 ) {
  803.         $self->default_json_key( $args->{'json_key'} );
  804.     }
  805.     # restore a json_key
  806.     elsif ( ! ref($args->{'json_key'}) && $args->{'json_key'} == 1 ) {
  807.         $self->default_json_key( 'default' );
  808.     }
  809.  
  810.  
  811.     #=> delimeter
  812.     #
  813.     # set a delimeter character by user given
  814.     if ( ! ref($args->{'delimeter'}) && $args->{'delimeter'} != 1 ) {
  815.         $self->delimeter( $args->{'delimeter'} );
  816.     }
  817.     # restore a delimeter
  818.     elsif ( ! ref($args->{'delimeter'}) && $args->{'delimeter'} == 1 ) {
  819.         $self->delimeter( '=' );
  820.     }
  821.  
  822.  
  823.     return $self;
  824. }
  825.  
  826.  
  827.  
  828.  
  829.  
  830.  
  831.  
  832. # ----------------------------------------------------------------------------
  833. =pod
  834.  
  835. =head1 read ( args: path, section, key, source, memory, make_directory, write_path )
  836.  
  837. Это главная функция записи данных. Смотри также, `_from_ini_string'
  838.  
  839. Status: testme, optimize me
  840.  
  841. =head3 ARGS
  842.  
  843. TODO
  844.  
  845. =over
  846. =item path => SCALAR |ARRAYREF
  847.  
  848.  $self->read( path => '/path/to/file' );
  849.  $self->read( path => [qw[ path/to/1 path/to/2 ]], write_path => 1 );
  850.  
  851.  TODO:
  852.     $self->read( path => qr/pcre/, dir => 'path', write_path => 1 );
  853.  
  854. =item dir => SCALAR |ARRAYREF
  855.  
  856. Есть два случая использывания `path', SCALAR и ARRAYREF. То есть один файл или список. Иногда мы сами не знаем какой файл нам нужно, но мы знаем где он может находиться.
  857.  
  858. =item source => SCALAR
  859.  
  860. TODO
  861.  
  862. =item memory => BOOLEAN
  863.  
  864.  $ini->read( source => <DATA>, memory => 1 );
  865.  
  866. =item make_directory => BOOLEAN
  867.  
  868.  $ini->read( path => '/1/1/1/1/1/file.ini', make_directory => 1 );
  869.  
  870. =item write_path => SCALAR
  871.  
  872. Когда мы задаем при создании объекта патч или несколько, то мы можем изменить патч записи изменений. Если мы зададим в качестве параметра еденицу, -то по умолчанию сохраняться данные будут в первом файле из списка патчей.
  873.  
  874.  # at 2014-09-22
  875.  $ini->read( path => [qw/ path1 path2 /], write_path => 'path2' );
  876.  
  877.  # write into $path[0] by default
  878.  $ini->read( path => [qw/ path1 path2 /], write_path => 1 );
  879.  
  880. =back
  881.  
  882. =head2 Example
  883.  
  884.  $ini->read;
  885.  $ini->read( source => $source );
  886.  
  887. =head3 TODO
  888.  
  889.  # at 2014-09-24
  890.  $ini->read( section => 'name', source => $src  );
  891.  $ini->read( section => [qw/ name /], source => $src  );
  892.  $ini->read( section => qr/pcre/i, source => $src  );
  893.  
  894.  # at 2014-09-24
  895.  $ini->read( key => 'name', source => $src  );
  896.  $ini->read( key => [qw/ name /], source => $src  );
  897.  $ini->read( key => qr/pcre/, source => $src  );
  898.  
  899.  
  900.  
  901.  # at 2014-09-21
  902.  $ini->read( spaces => '_', source => $source, memory => 1 );
  903.  $ini->read( allow_spaces => 1, source => $source, memory => 1 );
  904.  
  905.  # at 2014-09-18
  906.  $ini->read( source => <DATA>, memory => 1 );
  907.  $ini->read( path => '/path/from/read', make_directory => 1 );
  908.  $ini->read( path => [qw/ path1 path2 /] );
  909.  
  910. =cut
  911. sub read {  # FIXME
  912.     my $self = shift;
  913.     my $args = \%{validate(@_, {
  914.         section     => {                                # TODO
  915.             type        => SCALAR |SCALARREF |ARRAYREF,
  916.             regex       => $Re->{ 'section' },
  917.             optional    => 1,
  918.         },
  919.         key         => {                                # TODO
  920.             type        => SCALAR |SCALARREF |ARRAYREF,
  921.             regex       => $Re->{ 'key' },
  922.             optional    => 1,
  923.         },
  924.         source  => {
  925.             type        => SCALAR,
  926.             regex       => $Re->{'source'},
  927.             optional    => 1,
  928.         },
  929.         path    => {                                    # TODO
  930.             type        => SCALAR | ARRAYREF,
  931.             regex       => $Re->{'path'},
  932.             optional    => 1,
  933.         },
  934.         make_directory  => {
  935.             type        => BOOLEAN,
  936.             optional    => 1,
  937.         },
  938.         memory          => {                            # FIXME: testme
  939.             type        => BOOLEAN.
  940.             optional    => 1,
  941.         },
  942.         write_path          => {                            # TODO
  943.             type        => SCALAR,
  944.             optional    => 1,
  945.             default     => 1,
  946.         },
  947.         allow_spaces    => {                            # TODO
  948.             type        =>  BOOLEAN,
  949.             optional    => 1,
  950.             default     => 0,
  951.         },
  952.     })};
  953.  
  954.     # args:source
  955.     if ( exists $args->{'source'} ) {
  956.         $self->source( $args->{'source'} );
  957.     }
  958.  
  959.     # args:write_path
  960.     if ( exists $args->{'write_path'} ) {
  961.         $self->write_path( $args->{'write_path'} );
  962.     }
  963.  
  964.     # args:path
  965.     $self->path( $args->{'path'} ) if ( $args->{'path'} );
  966.  
  967.     # make new directories tree
  968.     # args:make_directory
  969.     $self->make_directory( 1 ) if exists $args->{'make_directory'};
  970.  
  971.     # FIXME: dejavu, смотри ниже
  972.     # data variable for data-storing
  973.     my $data  = undef;
  974.     my @files = ();
  975.  
  976.  
  977.     # FIXME: path
  978.     # Example:
  979.     #   $self->read( path => '/path/to/file' );
  980.     #   $self->read( path => [qw[ path/to/1 path/to/2 ]] );
  981.     if ( defined $self->path ) {
  982.         # data variable for data-storing
  983.         #
  984.         my $data  = undef;
  985.         my @files = ();
  986.         # FIXME: testme
  987.         # preparing @files
  988.         #
  989.         if ( ! ref( $args->{'path'} ) ) {               # SCALAR
  990.             $files[0] = $args->{'path'};
  991.         } elsif ( ref( $args->{'path'} ) eq 'ARRAY' ) { # ARRAY
  992.             @files = @{ $args->{'path'} };
  993.         }
  994.         #
  995.         # getting $data, processing @files
  996.         { local $/ = undef;
  997.         foreach ( sort @files ) {
  998.             $self->path( $_ );
  999.             # make directory & file if not exists
  1000.             #
  1001.             $self->_make_directory if ! -e $self->path && ! $self->memory;
  1002.             open(INI, $self->path) or confess $!;
  1003.             $data .= <INI>;
  1004.             close INI;
  1005.         }}
  1006.  
  1007.         # TODO: write_path
  1008.         # restoring path. after all, writing into $files[0]!
  1009.         $self->path( $files[0] );
  1010.  
  1011.         # writing within source
  1012.         if ( defined $self->source ) { # TODO: testme
  1013.             $self->_from_ini_string( source => $data ."\n".$self->source );
  1014.         }
  1015.         # writing without source
  1016.         else {
  1017.             $self->_from_ini_string( source => $data );
  1018.         }
  1019.     }
  1020.  
  1021.     # memory
  1022.     elsif ( defined $self->memory ) { # FIXME: testme
  1023.         # DEBUG
  1024.         say "memory storing" if $self->debug;
  1025.         if ( ! $self->source ) {   
  1026.             # new empty Data::Mini object
  1027.             $self->{'Config'} = +{};
  1028.         } else {
  1029.             # Data::Mini object source only
  1030.             $self->_from_ini_string( source => $self->source );
  1031.         }
  1032.     }
  1033.  
  1034.     # DEBUG
  1035.     say "read complete" if $self->debug;
  1036.     return $self;
  1037. }
  1038.  
  1039.  
  1040.  
  1041.  
  1042.  
  1043.  
  1044. # ----------------------------------------------------------------------------
  1045. =pod
  1046.  
  1047. =head1 write ( args: path, section, key, source, make_directory, delimeter )
  1048.  
  1049. Это главная функция записи данных в файл. смотри также, `_to_ini_string'
  1050.  
  1051. Status: all works fine, optimize-me, modern-me
  1052.  
  1053. =head3 args
  1054.  
  1055. TODO
  1056.  
  1057. =over
  1058. =item path => SCALAR
  1059.  
  1060. TODO
  1061.  
  1062. =item source => SCALAR
  1063.  
  1064. TODO
  1065.  
  1066. =item make_directory => BOOLEAN
  1067.  
  1068. TODO
  1069.  
  1070. =item delimeter => SCALAR |BOOLEAN
  1071.  
  1072. TODO
  1073.  
  1074. =back
  1075.  
  1076. =head2 Example
  1077.  
  1078.  $content = $ini->write;
  1079.  
  1080.  $ini->write( source => <DATA> );
  1081.  $ini->write( path => '/path/to/write', make_directory => 1 );
  1082.  $ini->write( source => <DATA>, path => '/path/to/clone.ini' );
  1083.  
  1084. =head2 TODO, FIXME
  1085.  
  1086.  # at 2014-09-24
  1087.  $ini->read( section => 'name', source => $src );
  1088.  $ini->read( section => [qw/ name /], source => $src );
  1089.  $ini->read( section => qr/pcre/i, source => $src );
  1090.  
  1091.  # at 2014-09-24
  1092.  $ini->read( key => 'name', source => $src  );
  1093.  $ini->read( key => [qw/ name /], source => $src  );
  1094.  $ini->read( key => qr/pcre/, source => $src  );
  1095.  
  1096.  # at 2014-09-19
  1097.  # value 1 for delimeter is a default delimeter character for a write method
  1098.  $mini->write( path => '/path/to.ini', delimeter => 1, make_directory => 1 );
  1099.  
  1100. =cut
  1101. sub write {
  1102.     my $self = shift;
  1103.     my $args = \%{validate(@_, {
  1104.         source  => {
  1105.             type        => SCALAR,
  1106.             regex       => qr/$Re->{'source'}/,
  1107.             optional    => 1,
  1108.         },
  1109.         path    => {
  1110.             type        => SCALAR,
  1111.             regex       => qr/$Re->{'path'}/,
  1112.             optional    => 1,
  1113.         },
  1114.         make_directory => {
  1115.             type        => BOOLEAN,
  1116.             optional    => 1,
  1117.         },
  1118.     })};
  1119.  
  1120.     # source data
  1121.     if ( exists $args->{'source'} ) {
  1122.         $self->source( $args->{'source'} );
  1123.     }
  1124.  
  1125.     # make new directories
  1126.     if ( exists $args->{'make_directory'} ) {
  1127.         $self->make_directory(1);
  1128.     }
  1129.  
  1130.     # storing path for clone if requested
  1131.     my $store = $self->path;
  1132.  
  1133.     # write/clone to another path
  1134.     $self->path( $args->{'path'} ) if ( $args->{'path'} );
  1135.  
  1136.     # get source content
  1137.     my $content = $self->_to_ini_string;
  1138.  
  1139.     # memory only or write to new path if requested
  1140.     return $content
  1141.         if $self->memory && ! exists $args->{'path'};
  1142.  
  1143.     # make new directory flag. setting up
  1144.     $self->make_directory(1) if $args->{'make_directory'};
  1145.  
  1146.     # make directory & file if not exists
  1147.     $self->_make_directory if ! -e $self->path;
  1148.  
  1149.     # writing
  1150.     return undef unless defined $content;
  1151.     open(INI, '>' .$self->path) or confess $!;
  1152.     print INI $content;
  1153.     close INI;
  1154.  
  1155.     # restore path
  1156.     $self->path( $store );
  1157.  
  1158.     say "write complete" if $self->debug;
  1159.     return $content;
  1160. }
  1161.  
  1162.  
  1163. # ----------------------------------------------------------------------------
  1164. =pod
  1165.  
  1166. =head1 _make_directory
  1167.  
  1168. Строит дерево категорий и файл.
  1169.  
  1170. =cut
  1171. sub _make_directory {
  1172.     use File::Path 'mkpath';
  1173.     use File::Basename 'dirname';
  1174.     my $self = shift;
  1175.  
  1176.     if ( $self->make_directory && ! -e $self->path ) {
  1177.         # making new directory
  1178.         # make_directory( 1 );
  1179.         mkpath( dirname( $self->path ) );
  1180.         $self->make_directory( 0 );
  1181.         system "touch ".$self->path;
  1182.         return $self;
  1183.     } elsif ( ! -e $self->path ) {
  1184.         # making new file without directory creating
  1185.         # make_directory( 0 );
  1186.         system "touch ".$self->path;
  1187.         return $self;
  1188.     }
  1189.  
  1190.     # Otherwise undef
  1191.     return undef;
  1192. }
  1193.  
  1194.  
  1195.  
  1196.  
  1197.  
  1198. # ----------------------------------------------------------------------------
  1199. =pod
  1200.  
  1201. =head1 _from_ini_string
  1202.  
  1203. Парсит на секции, ключи и их значения.
  1204.  
  1205. =head3 args
  1206.  
  1207. TODO
  1208.  
  1209. =over
  1210. =item source => SCALAR
  1211.  
  1212. TODO
  1213.  
  1214. =item section => SCALAR |SCALARREF |ARRAYREF
  1215.  
  1216. TODO
  1217.  
  1218. =item delimeter => BOOLEAN
  1219.  
  1220. TODO
  1221.  
  1222. =back
  1223.  
  1224. =head2 Example
  1225.  
  1226.  $ini->_from_ini_string( source => $source, delimeter => 1 );
  1227.  
  1228. Status: optimize me
  1229.  
  1230. =head2 TODO, FIXME
  1231.  
  1232.  2014-09-29: $ini->_from_ini_string( source => $source, generate_hash => 1);
  1233.  
  1234.  $ini->_from_ini_string( source => $source, delimeter => ':' );
  1235.  
  1236.   $ini->_from_ini_string( section => 'name', source => $source );
  1237.   $ini->_from_ini_string( section => qr/pcre/, source => $source );
  1238.   $ini->_from_ini_string( section => [qw/ name /], source => $source );
  1239.  
  1240.   $ini->_from_ini_string( key => 'name', source => $source );
  1241.   $ini->_from_ini_string( key => qr/pcre/, source => $source );
  1242.   $ini->_from_ini_string( key => [qw/ name /], source => $source );
  1243.  
  1244. =cut
  1245. sub _from_ini_string { # TODO
  1246.     my $self = shift;
  1247.     my $args = \%{validate(@_, {
  1248.         # TODO
  1249.         section     => {
  1250.             type        => SCALAR |SCALARREF |ARRAYREF,
  1251.             regex       => $Re->{'section'},
  1252.             optional    => 1,
  1253.         },
  1254.         # TODO
  1255.         key     => {
  1256.             type        => SCALAR |SCALARREF |ARRAYREF,
  1257.             regex       => $Re->{'key'},
  1258.             optional    => 1,
  1259.         },
  1260.         source      => {
  1261.             type        => SCALAR,
  1262.             regex       => $Re->{'source'},
  1263.             optional    => 0,   # required
  1264.             default     => '',  # empty by default
  1265.         },
  1266.     })};
  1267.  
  1268.     my $section = $self->default_section;
  1269.     my $line = 0;
  1270.     foreach ( split /(?:\015{1,2}\012|\015|\012)/, $args->{'source'} ) {
  1271.         # line counter
  1272.         $line++;
  1273.  
  1274.         # skip comments & empty lines
  1275.         # TODO: надобы сохранять комментарии :\
  1276.         next if /^\s*(?:\#|\;|$)/;
  1277.        
  1278.         # remove inline comments
  1279.         s/\s\;\s.+$//g;
  1280.  
  1281.         # section parsing ('_' is a root section)
  1282.         if (/^\s*\[\s*(.+?)\s*]\s*$/) {
  1283.             # section checking
  1284. #           next unless grep /^$1$/, @sections );   # FIXME:=> @sections
  1285.             # preparing section
  1286.             $self->{'Config'}->{$section = $1} ||=+{};
  1287.             next;
  1288.         }
  1289.  
  1290.         # TODO: test me
  1291.         # getting delimeter
  1292.         my $d = $self->delimeter;
  1293.         # preparing pcre
  1294.         my $pcre = '^\s*([^'.$d.']+?)\s*$d\s*(.*?)\s*$';
  1295.  
  1296.         # key checking
  1297. #       next unless grep /^$1$/, @keys;             # FIXME:=> @keys
  1298.  
  1299.         # section properties
  1300.         if (/$pcre/) {
  1301.             $self->{'Config'}->{$section}->{$1} = $2;
  1302.             next;
  1303.         }
  1304.  
  1305.         # не должно сюда дойти, ошибка парсинга
  1306.         confess "error at $line";
  1307.     }
  1308.  
  1309.     say "_from_ini_string: parsing ini string complete" if $self->debug;
  1310.     return $self;
  1311. }
  1312.  
  1313.  
  1314.  
  1315. # ----------------------------------------------------------------------------
  1316. =pod
  1317.  
  1318. =head1 _to_ini_string
  1319.  
  1320. write into ini file
  1321.  
  1322. =head2 args
  1323.  
  1324. TODO
  1325.  
  1326. =over
  1327. =item section => SCALAR |SCALARREF |ARRAYREF
  1328.  
  1329. TODO
  1330.  
  1331. =item key => SCALAR |SCALARREF |ARRAYREF
  1332.  
  1333. TODO
  1334.  
  1335. =item value => SCALARREF
  1336.  
  1337. TODO
  1338.  
  1339. =item delimeter => SCALAR
  1340.  
  1341. TODO
  1342.  
  1343.  
  1344. =back
  1345.  
  1346. =head2 Example
  1347.  
  1348.  my $ini_source = $ini->_to_ini_string;
  1349.  
  1350. =head2 TODO
  1351.  
  1352.  # at 2014-09-19
  1353.  $source = $ini->_to_ini_string( delimeter => '=' ); # TODO
  1354.  
  1355.  
  1356.  $source = $ini->_to_ini_string( section => 'name' );
  1357.  $source = $ini->_to_ini_string( section => qr/pcre/ );
  1358.  $source = $ini->_to_ini_string( section => qw/ name1 name2 /] );
  1359.  
  1360.  $source = $ini->_to_ini_string( key => 'name' );
  1361.  $source = $ini->_to_ini_string( key => qr/pcre/ );
  1362.  $source = $ini->_to_ini_string( key => qw/ name1 name2 /] );
  1363.  
  1364.  $source = $ini->_to_ini_string( value => qr/pcre/ );
  1365.  
  1366. Status: optimize me
  1367.  
  1368. =cut
  1369. sub _to_ini_string {
  1370.     my $self = shift;
  1371.     my $content = '';
  1372.     my $delimeter = $self->delimeter;
  1373.     my $args = \%{validate(@_, {
  1374.         section     => {
  1375.        
  1376.         },
  1377.         key         => {
  1378.        
  1379.         },
  1380.         value       => {
  1381.        
  1382.         },
  1383.         delimeter   => {
  1384.        
  1385.         },
  1386.     })};
  1387.  
  1388.  
  1389.     # add source to hash-struct, if given
  1390.     if ( defined $self->source ) {
  1391.         $self->_from_ini_string( source => $self->source );
  1392. #       $content .= "\n".$self->source;
  1393.         $self->source(undef); # fixed double sections writing at 2014-08-16
  1394.     }
  1395.  
  1396.     # write via root section `_' sorted first
  1397.     foreach my $section (sort { (($b eq '_') <=> ($a eq '_'))
  1398.             || ($a cmp $b)} keys %{$self->{'Config'}}) {    # root section first
  1399.         # disable whitespace characters
  1400.         confess __PACKAGE__.": Whitespace character in section name"
  1401.             if $section =~ /(?:^\s|\n|\s$)/s;
  1402.         my $block = $self->{'Config'}->{$section};          # getting block
  1403.         $content .= "\n" if length $content;                # skip first line
  1404.         $content .= "[$section]\n" unless $section eq '_';
  1405.         foreach my $property (sort keys %$block) {
  1406.             confess "Whitespace into property: $section\: $property\n"
  1407.                 if $block->{"$property"} =~ /(?:\012|\015)/s;
  1408.             $content .= "$property$delimeter".$block->{"$property"}."\n"; # gen
  1409.         }
  1410.     }
  1411.  
  1412.     say "_to_ini_string: parsing Config-hash structure into ini string complete"
  1413.         if $self->debug;
  1414.     return $content;
  1415. }
  1416.  
  1417.  
  1418. # ----------------------------------------------------------------------------
  1419. =pod
  1420.  
  1421. =head1 reset ( %$args: source, delimeter, memory )
  1422.  
  1423. Запись и чтение одновременно.
  1424.  
  1425.  $ini->reset;
  1426.  
  1427. Запись и чтение одновременно. Сначала мы ставим переменную разделителя строк в режим поглощения, записываем данные и читаем наного с аргументами:
  1428.  
  1429.  undef $/;
  1430.  $ini->reset( delimeter => ':', source => <DATA> );
  1431.  
  1432.  
  1433.  
  1434. =head2 ARGS
  1435.  
  1436. Аргументы метода `reset'
  1437.  
  1438. =over
  1439. =item source => Scalar
  1440.  
  1441. Строка сырых данных
  1442.  
  1443. =item delimeter => Scalar |Bool
  1444.  
  1445. Разделитель полей ключа и данных. например, key=value
  1446.  
  1447. =item memory => BOOLEAN
  1448.  
  1449. удоляет старую структуру и заполняет новую в режиме `memory'
  1450.  
  1451. =back
  1452.  
  1453. =head3 Example
  1454.  
  1455.  package Sample::Role;
  1456.  use Moo::Role;
  1457.  
  1458.  # write and read at once
  1459.  
  1460.  sub get_sample_config {
  1461.    my $self = shift;
  1462.    local $/ = undef;
  1463.    $self->DataMini->reset( source => <DATA> );
  1464.  }
  1465.  __DATA__
  1466.  [section]
  1467.  key=value
  1468.  
  1469. =head3 reset, TODO - FIXME
  1470.  
  1471.  # memory
  1472.  # удоляет старую структуру и заполняет новую в режиме `memory'
  1473.  
  1474.  # записывает старые данные с новым разделителем и читает заного.
  1475.  $mini->reset( delimeter => '<__DELIMETER__>' );
  1476.  
  1477. =cut
  1478. sub reset {
  1479.     my $self = shift;
  1480.     my $args = \%{validate(@_, {
  1481.         source      => {
  1482.             type        => SCALAR,
  1483.             regex       => $Re->{'source'},
  1484.             optional    => 1,
  1485.         },
  1486.     })};
  1487.  
  1488.     if ( exists $args->{'source'} ) {
  1489.         $self->source( $args->{'source'} );
  1490.     }
  1491.  
  1492.     $self->write;
  1493.     $self->read;
  1494.  
  1495.     say "reset: reset complete" if $self->debug;
  1496.     return $self;
  1497. }
  1498.  
  1499.  
  1500.  
  1501.  
  1502.  
  1503.  
  1504.  
  1505.  
  1506. # ----------------------------------------------------------------------------
  1507. =pod
  1508.  
  1509. =head1 exists( keys: section, key )
  1510.  
  1511. Checking for section or key existion
  1512.  
  1513. =head2 ARGS
  1514.  
  1515. =over
  1516. =item section => SCALAR
  1517.  
  1518. TODO
  1519.  
  1520. =item key => SCALAR
  1521.  
  1522. TODO
  1523.  
  1524. =back
  1525.  
  1526. =head2 Example
  1527.  
  1528.  $bool = $ini->exists;
  1529.  $bool = $ini->exists( key => 'default' );
  1530.  $bool = $ini->exists( section => '_', key => 'default' );
  1531.  
  1532. =cut
  1533. sub exists {
  1534.     my $self = shift;
  1535.     my $args = \%{validate(@_, {
  1536.         section     => {
  1537.             type        => SCALAR,
  1538.             regex       => $Re->{'section'},
  1539.             optional    => 1,
  1540.             default     => $self->default_section,
  1541.         },
  1542.         key         => {
  1543.             type        => SCALAR,
  1544.             regex       => $Re->{'key'},
  1545.             optional    => 1,
  1546.         },
  1547.     })};
  1548.  
  1549.     # key
  1550.     if ( exists $args->{'key'} ) {
  1551.         say "exists: key" if $self->debug;
  1552.         return exists $self->{'Config'}->{
  1553.             $args->{'section'} }->{ $args->{'key'} } ? 1 : 0;
  1554.     }
  1555.  
  1556.     # section
  1557.     say "exists: section" if $self->debug;
  1558.     return exists $self->{'Config'}->{ $args->{'section'} } ? 1 : 0;
  1559. }
  1560.  
  1561.  
  1562.  
  1563.  
  1564.  
  1565.  
  1566.  
  1567. # ----------------------------------------------------------------------------
  1568. =pod
  1569.  
  1570. =head1 defined (keys: section, key)
  1571.  
  1572. Определение установлено ли значение для section или key
  1573.  
  1574. =head2 ARGS
  1575.  
  1576. TODO
  1577.  
  1578. =over
  1579. =item section => SCALAR
  1580.  
  1581. TODO
  1582.  
  1583. =item key => SCALAR
  1584.  
  1585. TODO
  1586.  
  1587. =back
  1588.  
  1589. =head2 Example
  1590.  
  1591.  $ini->defined; # defaults: section => `_', key => `default'
  1592.  $ini->defined( key => 'default' ); # defaults: section `_' by default
  1593.  $ini->defined( section => '_', key => 'default' );
  1594.  
  1595.  Status: OK, testme
  1596.  
  1597. =cut
  1598. sub defined {
  1599.     my $self = shift;
  1600.     my $args = \%{validate(@_, {
  1601.         section     => {
  1602.             type        => SCALAR,
  1603.             regex       => $Re->{'section'},
  1604.             optional    => 1,
  1605.             default     => $self->default_section,
  1606.         },
  1607.         key         => {
  1608.             type        => SCALAR,
  1609.             regex       => $Re->{'key'},
  1610.             optional    => 1,
  1611.         },
  1612.     })};
  1613.  
  1614.     # key
  1615.     if ( exists $args->{'key'} ) {
  1616.         say "defined: key" if $self->debug;
  1617.         return defined $self->{'Config'}->{
  1618.             $args->{'section'} }->{ $args->{'key'} } ? 1 : 0;
  1619.     }
  1620.  
  1621.     # section
  1622.     say "defined: section" if $self->debug;
  1623.     return defined $self->{'Config'}->{ $args->{'section'} } ? 1 : 0;
  1624. }
  1625.  
  1626.  
  1627.  
  1628.  
  1629.  
  1630.  
  1631.  
  1632.  
  1633. # ----------------------------------------------------------------------------
  1634. =pod
  1635.  
  1636. =head1 find( keys: section, key, value )
  1637.  
  1638. TODO
  1639.  
  1640. =head2 ARGS
  1641.  
  1642. TODO
  1643.  
  1644. =over
  1645. =item section => SCALAR |ARRAYREF |SCALARREF
  1646.  
  1647. TODO
  1648.  
  1649. =item key => SCALAR |SCALARREF |ARRAYREF
  1650.  
  1651. TODO
  1652.  
  1653. =item value => SCALARREF
  1654.  
  1655. TODO
  1656.  
  1657. =item dumper => BOOLEAN
  1658.  
  1659. TODO
  1660.  
  1661. =back
  1662.  
  1663. =head2 Example
  1664.  
  1665.  $ini->find; # defaults: section => '_', key => 'default', value => '.*'
  1666.  $ini->find( section => '^pcre$' ); # key => 'default', value => '.*'
  1667.  $ini->find( key => '^pcre$' );     # section => '_', value => '.*'
  1668.  $ini->find( value => '^pcre$' );   # section => '_', key => 'default'
  1669.  
  1670.  $ini->find( section => qr/^pcre$/, dumper => 1 );
  1671.  
  1672. =head2 TODO, FIXME
  1673.  
  1674.  $ini->find( section => 'name' );
  1675.  $ini->find( section => qr/pcre/ );
  1676.  $ini->find( section => qw[/ name1 name2 /] );
  1677.  
  1678.  $ini->find( key => 'name' );
  1679.  $ini->find( key => qr/pcre/ );
  1680.  $ini->find( key => qw[/ name1 name2 /] );
  1681.  
  1682.  $ini->find( value => qr/pcre/ );
  1683.  
  1684. =cut
  1685. sub find {
  1686.     my $self = shift;
  1687.     my $args = \%{validate(@_, {
  1688.         section     => {
  1689.             type        => SCALAR |SCALARREF |ARRAYREF,
  1690.             regex       => $Re->{ 'section' },
  1691.             default     => $self->default_section,
  1692.             optional    => 1,
  1693.         },
  1694.         key         => {
  1695.             type        => SCALAR |SCALARREF |ARRAYREF,
  1696.             regex       => $Re->{ 'key' },
  1697.             optional    => 1,
  1698.         },
  1699.         value       => {
  1700.             type        => SCALARREF,
  1701.             regex       => $Re->{ 'value' },
  1702.             optional    => 1,
  1703.         },
  1704.         dumper      => {
  1705.             type        => BOOLEAN,
  1706.             optional    => 1,
  1707.             default     => 0,
  1708.         },
  1709.     })};
  1710.  
  1711.     #                            *** getting section list
  1712.     #
  1713.     #
  1714.     my %result = (); # store, return value
  1715.     # TODO: test-me
  1716.     #
  1717.     my @sections = ();
  1718.     # scalarref, qr/pcre/
  1719.     if ( ref $args->{'section'} && ref( $args->{'section'} ) ne 'ARRAY' ) {
  1720.         @sections = $self->find( section => $args->{'section'} );
  1721.     }
  1722.     # arrayref
  1723.     elsif ( ref $args->{'section'} && ref( $args->{'section'} ) eq 'ARRAY' ) {
  1724.         @sections = @{$args->{'section'}};
  1725.     }
  1726.     # scalar
  1727.     else {
  1728.         $sections[0] = $args->{'section'};
  1729.     }
  1730.  
  1731.     # section
  1732.     #
  1733.     foreach my $section ( sort @sections ) {
  1734.         # DEBUG
  1735.         say "match by section `$section'" if $self->debug; # FIXME
  1736.         # by key given
  1737.         # Example:
  1738.         #   $ini->find( key => qr/pcre/ );
  1739.         #
  1740.         if ( exists $args->{'key'} ) {
  1741.             #
  1742.             # TODO:                   *** getting keys
  1743.             #
  1744.             #
  1745.             my @keys = ();
  1746.             # scalarref, qr/pcre/
  1747.             if ( ref $args->{'key'}
  1748.                     && ref( $args->{'key'} ) ne 'ARRAY' ) {
  1749.                 @keys = grep /$args->{'key'}/,
  1750.                     $self->KEYS( section => $section );
  1751.             }
  1752.             # arrayref
  1753.             elsif ( ref $args->{'key'}
  1754.                     && ref( $args->{'key'} ) eq 'ARRAY' ) {
  1755.                 @keys = @{$args->{'key'}};
  1756.             }
  1757.             # scalar
  1758.             else {
  1759.                 $keys[0] = $args->{'key'};
  1760.             }
  1761.  
  1762.             # matching value..
  1763.             # Example:
  1764.             #   $ini->find( key => qr/pcre/, value => qr/pcre/ );
  1765.             #
  1766.             if ( exists $args->{ 'value' } ) {
  1767.                 # matching value per key
  1768.                 #
  1769.                 foreach my $key ( sort @keys ) {
  1770.                     my $value =
  1771.                         $self->key( section => $section, key => $key );
  1772.                     # value by default if not given
  1773.                     $args->{ 'value' } = '.*' if( ! exists $args->{ 'value' } );
  1774.                     if ( $value =~ /$args->{ 'value' }/ ) {
  1775.                         $result{ $section }{ $key } = $value;
  1776.                     }
  1777.                 }
  1778.             }
  1779.            
  1780.             else {
  1781.                 # otherwise store key names (if not zero length)
  1782.                 # Example:
  1783.                 #   $ini->find( section => 'name', key => qr/pcre/ );
  1784.                 if ( @keys ) {
  1785.                     $result{ $section } = [ @keys ];
  1786.                 }
  1787.             }
  1788.         }
  1789.         # no key given
  1790.         # Example:
  1791.         #   $ini->find( section => 'name', value => qr/pcre/ );
  1792.         #
  1793.         else { # if ( exists $args->{'key'} )
  1794.             # DEBUG
  1795.             say "no key given, returning sections" if $self->debug; # FIXME
  1796.             # if not defined value to match, then returning a sections names
  1797.             if ( ! exists $args->{'value'} ) {
  1798.                 # Return section list
  1799.                 # Example:
  1800.                 #   @sections = $ini->find( section => qr/pcre/ );
  1801.                 #
  1802.                 return @sections;
  1803.             } else {
  1804.                 # match section-key names by given value
  1805.                 # Example:
  1806.                 #   $ini->find( section => 'pcre', value => 'pcre' );
  1807.                 #
  1808.                 # TODO
  1809.                 foreach my $section ( @sections ) {
  1810.                     # DEBUG
  1811.                     say "match section-key names by given value"
  1812.                         if $self->debug; # FIXME
  1813.                     # match value per key
  1814.                     foreach my $key ( $self->KEYS( section => $section )) {
  1815.                         # getting value
  1816.                         my $value = $self->key( section => $section,
  1817.                                 key => $key );
  1818.                         # checking for value's matches
  1819.                         if ( $value =~ m/$args->{ 'value' }/ ) {
  1820.                             $result{ $section }{ $key } = $value;
  1821.                         }
  1822.                     }
  1823.                 }
  1824.             }
  1825.         }
  1826.     }
  1827.  
  1828.     # dumping all matches
  1829.     if ( $args->{'dumper'} ) {
  1830.         say Dumper \%result;
  1831.     }
  1832.  
  1833.     # returning matches (hash reference)
  1834.     return \%result;
  1835. }
  1836.  
  1837.  
  1838.  
  1839.  
  1840. # ----------------------------------------------------------------------------
  1841. =pod
  1842.  
  1843. =head1 rename ( args: file, section, key, as )
  1844.  
  1845.  $ini->rename( as => 'new_key_name' );          # FIXME
  1846.  $ini->rename( section => '_', as => 'new_section_name' );
  1847.  $ini->rename( section => '_', key => 'key', as => 'new_key_name' );
  1848.  
  1849. =head2 ARGS
  1850.  
  1851. TODO
  1852.  
  1853. =over
  1854. =item section => SCALAR |SCALARREF |ARRAYREF
  1855.  
  1856. TODO
  1857.  
  1858. =item key => SCALAR
  1859.  
  1860. TODO
  1861.  
  1862. =item as => SCALAR
  1863.  
  1864. TODO
  1865.  
  1866. =item file => SCALAR
  1867.  
  1868. TODO
  1869.  
  1870. =back
  1871.  
  1872. =head3 TODO
  1873.  
  1874.   # rename file-name & section-name
  1875.   $ini->rename( file => 'new_filename' );
  1876.   $ini->rename( section => 'name', as => 'new_name' );
  1877.   # multi-section key renaming
  1878.   $ini->rename( section => 'name', key => 'name', as => new );
  1879.   $ini->rename( section => qr/pcre/, key => 'name', as => new );
  1880.   $ini->rename( section => [qw/ name /], key => 'name', as => new );
  1881.  
  1882.  
  1883.  Status: OK
  1884.  
  1885. =cut
  1886. sub rename {
  1887.     my $self = shift;
  1888.     my $args = \%{validate(@_, {
  1889.         section     => {
  1890.             type        => SCALAR |SCALARREF |ARRAYREF,
  1891.             regex       => qr/$Re->{'section'}/,
  1892.             optional    => 1,
  1893.         },
  1894.         key         => {
  1895.             type        => SCALAR,
  1896.             regex       => qr/$Re->{'key'}/,
  1897.             optional    => 1,
  1898.         },
  1899. #       name        => {
  1900. #           optional    => 1,
  1901. #       },
  1902.         as          => {
  1903.             type        => SCALAR,
  1904.             regex       => qr/$Re->{'key'}/,
  1905.             optional    => 0, # required
  1906.         },
  1907.     })};
  1908.  
  1909.     # section
  1910.     if ( exists $args->{'section'} && ! exists $args->{'key'}) {
  1911.         say "renaming `".$args->{'section'}."' as `".$args->{'key'}."'"
  1912.             if $self->debug;
  1913.         # return undef unless exists section
  1914.         return undef
  1915.             unless $self->exists( section => $args->{'section'} );
  1916.         # getting section
  1917.         my $href = $self->{'Config'}->{ $args->{'section'} };
  1918.         # setting section as new section name
  1919.         $self->{'Config'}->{ $args->{'as'} } = $href;   # %{} замена
  1920.         # delete old section
  1921.         delete $self->{'Config'}->{ $args->{'section'} };
  1922.         return $self;
  1923.     }
  1924.    
  1925.     # key
  1926.     elsif ( exists $args->{'section'} &&  exists $args->{'key'} ) {
  1927.         # return undef unless exists key
  1928.         return undef
  1929.             unless $self->exists( section => $args->{'section'},
  1930.                 key => $args->{'key'} );
  1931.         # getting key value
  1932.         my $value = $self->{'Config'}->{ $args->{'section'} }->{
  1933.             $args->{'key'} };
  1934.         # setting key as new key
  1935.         $self->{'Config'}->{ $args->{'section'} }->{ $args->{'as'} }
  1936.             = $value;
  1937.         # delete old key
  1938.         delete $self->{'Config'}->{ $args->{'section'} }->{$args->{'key'}};
  1939.         return $self;
  1940.     }
  1941.    
  1942.     # section & key by default
  1943.     elsif ( ! exists $args->{'section'} && ! exists $args->{'key'} ) {
  1944.         # defaults
  1945.         $args->{'section'} = '_';
  1946.         $args->{'key'} = 'default';
  1947.         # return undef unless exists key
  1948.         return undef
  1949.             unless $self->exists( section => $args->{'section'},
  1950.                 key => $args->{'key'} );
  1951.         # getting key value
  1952.         my $value = $self->{'Config'}->{ $args->{'section'} }->{
  1953.             $args->{'key'} };
  1954.         # setting key as new
  1955.         $self->{'Config'}->{ $args->{'section'} }->{ $args->{'as'} }
  1956.             = $value;
  1957.         # delete old key
  1958.         delete $self->{'Config'}->{ $args->{'section'} }->{$args->{'key'}};
  1959.         return $self;
  1960.     }
  1961.  
  1962.     # Otherwise undef
  1963.     return undef;
  1964. }
  1965.  
  1966.  
  1967.  
  1968.  
  1969.  
  1970. # ----------------------------------------------------------------------------
  1971. =pod
  1972.  
  1973. =head1 dump( args: section, dump_indentlevel, dump_sortkeys )
  1974.  
  1975. Метод позволяет выворачивать структуру по запросу
  1976.  
  1977. =head2 ARGS
  1978.  
  1979. TODO
  1980.  
  1981. =over
  1982. =item section => SCALAR |SCALARREF |ARRAYREF
  1983.  
  1984. TODO
  1985.  
  1986. =item dump_indentlevel => SCALAR
  1987.  
  1988. TODO
  1989.  
  1990. =item dump_sortkeys => SCALAR
  1991.  
  1992. TODO
  1993.  
  1994. =back
  1995.  
  1996. =head2 Example(s)
  1997.  
  1998.  $ini->dump;
  1999.  
  2000.  $ini->dump_indentlevel( 5 );
  2001.  $ini->dump_sortkeys( 0 );
  2002.  
  2003. =head2 TODO, FIXME
  2004.  
  2005.  $ini->dump( section => 'name', dump_indentlevel => 7, dump_sortkeys => 1 );
  2006.  
  2007.  $ini->dump( section => 'name' );
  2008.  $ini->dump( section => [qw/ name1 name2 /] );
  2009.  $ini->dump( section => qr/pcre/ );
  2010.  
  2011.  Status: testme
  2012. =cut
  2013. sub dump {
  2014.     my $self = shift;
  2015.     my $args = \%{validate(@_, {
  2016.         section     => {
  2017.             type        => SCALAR |SCALARREF |ARRAYREF,
  2018.             regex       => qr/$Re->{'section'}/,
  2019.             optional    => 1,
  2020.         },
  2021.     })};
  2022.     use Data::Dumper;
  2023.     $Data::Dumper::Indent = $self->dump_indentlevel;
  2024.     $Data::Dumper::Sortkeys = $self->dump_sortkeys;
  2025.  
  2026.     # ARRAYREF
  2027.     if ( ref($args->{'section'}) eq 'ARRAY' ) {
  2028.         foreach my $section ( sort @{ $args->{'section'} } ) {
  2029.             $Data::Dumper::Varname = $section;
  2030.             print Dumper $self->{'Config'}->{ $section };
  2031.         }
  2032.         return $self;
  2033.     }
  2034.  
  2035.     # SCALARREF
  2036.     elsif ( exists $args->{'section'}) {
  2037.         my @sections = $self->find( section => $args->{'section'} );
  2038.         foreach my $section ( sort @sections ) {
  2039.             $Data::Dumper::Varname = $section;
  2040.             print Dumper $self->{'Config'}->{ $section };
  2041.         }
  2042.         return $self;
  2043.     }
  2044.  
  2045.     # SCALAR
  2046.     elsif ( 1 ) {
  2047.         # TODO
  2048.     }
  2049.  
  2050.     # Otherwise all-dump
  2051.     print Dumper $self->{'Config'};
  2052.     return $self;
  2053. }
  2054.  
  2055.  
  2056.  
  2057.  
  2058. # ----------------------------------------------------------------------------
  2059. =pod
  2060.  
  2061. =head1 delete ( Undef|keys: section, key )
  2062.  
  2063. Удоляет секции, ключи и файл
  2064.  
  2065. =head2 ARGS
  2066.  
  2067. TODO
  2068.  
  2069. =over
  2070. =item section =>  SCALAR |SCALARREF |ARRAYREF
  2071.  
  2072. TODO
  2073.  
  2074. =item key => SCALAR |SCALARREF |ARRAYREF
  2075.  
  2076. TODO
  2077.  
  2078. =item value => SCALAR
  2079.  
  2080. TODO
  2081.  
  2082. =item file => BOOLEAN
  2083.  
  2084. TODO
  2085.  
  2086. =back
  2087.  
  2088. =head3 Example(s)
  2089.  
  2090.  $ini->delete;
  2091.  $ini->delete( key => 'default' );
  2092.  $ini->delete( section => '_', key => 'default' );
  2093.  
  2094. =head3 TODO, FIXME
  2095.  
  2096.  $ini->delete( section => 'name' )
  2097.  $ini->delete( section => qr/pcre/ )
  2098.  $ini->delete( section => [qw/ name1 name2 /] )
  2099.  
  2100.  $ini->delete( key => 'name' );
  2101.  $ini->delete( key => qr/pcre/ );
  2102.  $ini->delete( key => [qw/ name1 name2 /] );
  2103.  
  2104.  $ini->delete( value => qr/pcre/ );
  2105.  
  2106.  FIXME: создаються пустые секции при их отсутствии
  2107.  TODO:
  2108.   delete via pcre
  2109.  
  2110.   $ini->delete( file => 1 );
  2111.  
  2112. =cut
  2113. sub delete {
  2114.     my $self = shift;
  2115.     my $args = \%{validate(@_, {
  2116.         section     => {
  2117.             type        => SCALAR,
  2118.             regex       => qr/$Re->{'section'}/,
  2119. #           default     => $self->default_section,
  2120.             optional    => 1,
  2121.         },
  2122.         key         => {
  2123.             type        => SCALAR,
  2124.             regex       => qr/$Re->{'key'}/,
  2125. #           default     => $self->default_key,
  2126.             optional    => 1,
  2127.         },
  2128.         file        => {
  2129.             type        => BOOLEAN,
  2130.             optional    => 1,
  2131.         },
  2132.     })};
  2133.  
  2134.     # section
  2135.     if ( exists $args->{'section'} && ! exists $args->{'key'} ) {
  2136.         # return undef unless exists section
  2137.         return undef
  2138.             unless exists $self->{'Config'}->{$args->{'section'}};
  2139.         say "deletion section `".$args->{'section'}."'"
  2140.             if $self->debug;
  2141.         delete $self->{'Config'}->{ $args->{'section'} };
  2142.         return $self;
  2143.     }
  2144.  
  2145.     # key
  2146.     # FIXME: создаються пустые секции при их отсутствии
  2147.     #
  2148.     $args->{'key'} = $self->default_key
  2149.         unless exists $args->{'key'};
  2150.     # return undef unless exists section-key
  2151.     return undef
  2152.         unless $self->exists( section => $args->{'section'},
  2153.             key => $args->{'key'} );
  2154.  
  2155.     # DEBUG
  2156.     say "deletion key `".$args->{'key'}."'"
  2157.         if $self->debug;
  2158.    
  2159.     # processing
  2160.     delete $self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} }
  2161.         if exists $self->{'Config'}->{
  2162.             $args->{'section'} }->{ $args->{'key'} };
  2163.  
  2164.     #
  2165.     # delete zero length section (empty)
  2166.     if ( ! $self->KEYS( section => $args->{'section'}, counter => 1 ) ) {
  2167.         delete $self->{'Config'}->{ $args->{'section'} };
  2168.     }
  2169.    
  2170.     return $self;
  2171. }
  2172.  
  2173. ##############################################################################
  2174. # "Get/Set methods"
  2175. # ----------------------------------------------------------------------------
  2176. =pod
  2177.  
  2178. =head2 SECTIONS( Undef | section, counter )
  2179.  
  2180. Получить список имен секций. Для более мощного поиска можно использовать PCRE
  2181.  
  2182. =head2 ARGS
  2183.  
  2184. TODO
  2185.  
  2186. =over
  2187. =item section => SCALARREF
  2188.  
  2189. TODO
  2190.  
  2191. =item counter => BOOLEAN
  2192.  
  2193. TODO
  2194.  
  2195. =back
  2196.  
  2197. =head3 Example(s)
  2198.  
  2199.  my @section_names = $ini->SECTIONS;
  2200.  my @section_names = $ini->SECTIONS( section => qr/^pcre$/ );
  2201.  my $section_counter = $ini->SECTIONS( counter => 1 );
  2202.  
  2203. =head3 See also
  2204.  
  2205.  my @secs = $ini->find( section => qr/^pcre$/ );
  2206.  
  2207. Status: all works fine
  2208.  
  2209. =cut
  2210. sub SECTIONS {
  2211.     my $self = shift;
  2212.     my $args = \%{validate(@_, {
  2213.         section   => {
  2214.             type        => SCALARREF,
  2215.             regex       => $Re->{'section'},
  2216.             optional    => 1,
  2217.         },
  2218.         counter => {    # TODO
  2219.             type        => BOOLEAN,
  2220.             optional    => 1,
  2221.         },
  2222.     })};
  2223.  
  2224.     # get by PCRE
  2225.     #
  2226.     if ( exists $args->{'section'}
  2227.             and ref($args->{'section'}) eq 'Regexp' ) {
  2228.         my @s = grep /$args->{'section'}/, keys %{ $self->{'Config'} };
  2229.         return exists $args->{'counter'} ? scalar @s : sort @s;
  2230.     }
  2231.  
  2232.     # get all section names
  2233.     #
  2234.     my @s = ( keys %{ $self->{'Config'} } );
  2235.     return exists $args->{'counter'} ? scalar @s : sort @s;
  2236. }
  2237.  
  2238.  
  2239.  
  2240.  
  2241.  
  2242.  
  2243.  
  2244. # ----------------------------------------------------------------------------
  2245. =pod
  2246.  
  2247. =head2 KEYS (keys: section, counter)
  2248.  
  2249. Получить ключи заданой секции, если она задана, а если нет то используеться секция по умолчанию
  2250.  
  2251. =head2 ARGS
  2252.  
  2253. TODO
  2254.  
  2255. =over
  2256. =item section => SCALARREF
  2257.  
  2258. TODO
  2259.  
  2260. =item counter => BOOLEAN
  2261.  
  2262. TODO
  2263.  
  2264. =back
  2265.  
  2266. =head3 Example(s)
  2267.  
  2268.  # get all key-names of default section `_'
  2269.  $ini->KEYS;
  2270.  
  2271.  # get all key-names of section `an'
  2272.  $ini->KEYS( section => 'an' );
  2273.  
  2274.  # get all key-counter of section `an'
  2275.  $ini->KEYS( section => 'an', counter => 1 );
  2276.  
  2277. =cut
  2278. sub KEYS {
  2279.     my $self = shift;
  2280.     my $args = \%{validate(@_, {
  2281.         section     => {
  2282.             type        => SCALAR,
  2283.             regex       => qr/$Re->{'section'}/,
  2284.             optional    => 1,
  2285.             default     => $self->default_section,
  2286.         },
  2287.         counter     => {
  2288.             type        => BOOLEAN,
  2289.             optional    => 1,
  2290.         },
  2291.     })};
  2292.  
  2293.     return exists $args->{'counter'}
  2294.     # counter
  2295.     ?   scalar keys %{ $self->{'Config'}->{ $args->{'section'} } }
  2296.     # names
  2297.     :   keys %{ $self->{'Config'}->{ $args->{'section'} } };
  2298.  
  2299.     # Otherwise undef
  2300.     return undef;
  2301. }
  2302.  
  2303. # ----------------------------------------------------------------------------
  2304. =pod
  2305.  
  2306. =head1 VALUES (keys: section, counter)
  2307.  
  2308. Получить значения-ключей заданой секции, если она задана, а если нет то используеться секция по умолчанию
  2309.  
  2310.  # all values of section `_'
  2311.  $ini->VALUES;
  2312.  
  2313.  # all values of `an' section
  2314.  $ini->VALUES( section => 'an' );
  2315.  
  2316.  # counter of `an' section values
  2317.  $ini->VALUES( section => 'an', counter => 1 );
  2318.  
  2319. =head2 ARGS
  2320.  
  2321. TODO
  2322.  
  2323. =over
  2324. =item section => SCALARREF
  2325.  
  2326. TODO
  2327.  
  2328. =item counter => BOOLEAN
  2329.  
  2330. TODO
  2331.  
  2332. =back
  2333.  
  2334.  
  2335. =cut
  2336. sub VALUES {
  2337.     my $self = shift;
  2338.     my $args = \%{validate(@_, {
  2339.         section     => {
  2340.             type        => SCALAR,
  2341.             regex       => qr/$Re->{'section'}/,
  2342.             optional    => 1,
  2343.             default     => $self->default_section,
  2344.         },
  2345.         counter     => {
  2346.             type        => BOOLEAN,
  2347.             optional    => 1,
  2348.         },
  2349.     })};
  2350.    
  2351.     return exists $args->{'counter'}
  2352.     # counter
  2353.     ?   scalar values %{ $self->{'Config'}->{ $args->{'section'} } }
  2354.     # names
  2355.     :   values %{ $self->{'Config'}->{ $args->{'section'} } };
  2356.  
  2357.     # Otherwise undef
  2358.     return undef;
  2359. }
  2360.  
  2361.  
  2362.  
  2363. # ----------------------------------------------------------------------------
  2364. =pod
  2365.  
  2366. =head1 typeof( %$args: section, key )
  2367.  
  2368. Get type of a key or json_key
  2369.  
  2370. =head2 ARGS - Аргументы метода
  2371.  
  2372. TODO
  2373.  
  2374. =over
  2375.  
  2376. =item section => SCALAR
  2377.  
  2378. TODO
  2379.  
  2380. =item key => SCALAR
  2381.  
  2382. TODO
  2383.  
  2384. =back
  2385.  
  2386. =head2 Examples
  2387.  
  2388.  $type = $ini->typeof;
  2389.  $type = $ini->typeof( section => '_', key => 'default' );
  2390.  $type = $ini->typeof( key => 'default' );
  2391.  
  2392. =head2 Method history
  2393.  
  2394.  2014-09-16: Params::Validate - argument's validate tool
  2395.  
  2396. =cut
  2397. sub typeof {
  2398.     my $self = shift;
  2399.     my $args = \%{validate(@_, {
  2400.         section     => {
  2401.             type        => SCALAR,
  2402.             regex       => $Re->{'section'},
  2403.             optional    => 1,
  2404.             default     => $self->default_section,
  2405.         },
  2406.         key         => {
  2407.             type        => SCALAR,
  2408.             regex       => $Re->{'key'},
  2409.             optional    => 1,
  2410.             default     => $self->default_key,
  2411.         },
  2412.     })};
  2413.  
  2414.     # undef type if key not exists
  2415.     return undef
  2416.         unless $self->exists(section => $args->{'section'},
  2417.             key => $args->{'key'} );
  2418.     # getting value
  2419.     my $value = $self->key(section => $args->{'section'},
  2420.         key => $args->{'key'});
  2421.  
  2422.     # undef type if value not defined
  2423.     return undef unless $value; # fixed bug at 2014-08-12
  2424.  
  2425.     # returning real data type name
  2426.     $value =~ m/^{.*}|\[.*\]$/              ?   return 'json'       :
  2427.     $value =~ m/^-?\d+$/                    ?   return 'integer'    :
  2428.     $value =~ m/^(?:yes|no|true|false)$/i   ?   return 'boolean'    :
  2429.     $value =~ m/^-?\d*?\.\d+$/              ?   return 'float'      :
  2430.                                                 return 'string'     ;
  2431.     # сюда не дойдет
  2432. }
  2433.  
  2434.  
  2435.  
  2436. ##############################################################################
  2437. # "Miscellaneous methods"
  2438. # ----------------------------------------------------------------------------
  2439. =pod
  2440.  
  2441. =head2 key (keys: section, key, value)
  2442.  
  2443. Метод доступа к ключам.
  2444.  
  2445. =head2 ARGS
  2446.  
  2447. TODO
  2448.  
  2449. =over
  2450. =item section => SCALAR |SCALARREF |ARRAYREF
  2451.  
  2452. TODO
  2453.  
  2454. =item key => SCALAR |SCALARREF |ARRAYREF
  2455.  
  2456. TODO
  2457.  
  2458. =item value => SCALAR |SCALARREF |ARRAYREF |HASHREF
  2459.  
  2460. TODO
  2461.  
  2462. =item existonly => BOOLEAN
  2463.  
  2464. TODO
  2465.  
  2466. =item json => BOOLEAN
  2467.  
  2468. TODO
  2469.  
  2470. =back
  2471.  
  2472. =head3 Example(s)
  2473.  
  2474.  # get
  2475.  $value = $ini->key;
  2476.  $value = $ini->key(section => '_', key => 'default' );
  2477.  
  2478.  # set
  2479.  $self = $ini->key( value => 'value' );
  2480.  $self = $ini->key( value => 'value', existonly => 1 );  # TODO
  2481.  $self = $ini->key( section => '_', key => 'default', value => 'value' );
  2482.  
  2483.  
  2484.  # get JSON
  2485.  # decode_json => Bool
  2486.  HashRef = $ini->key( key => 'json', decode_json => Bool );
  2487.  
  2488.  # set HashRef ( json_key by default is a `default' )
  2489.  # encode_json => Bool
  2490.  # value => HashRef
  2491.  $ini = $ini->key( value => { key => Str }, encode_json => 1, key => 'json' );
  2492.  
  2493.  # set ArrayRef
  2494.  # value => ArrayRef
  2495.  $ini->key( value => [], encode_json => 1 );
  2496.  
  2497. =head3 TODO, FIXME
  2498.  
  2499.  $ini->key( value => \%,  json => 1 );
  2500.  $ini->key( value => \[], json => 1 );
  2501.  $ini->key( value => \$,  json => 1 );
  2502.  
  2503.  args: existonly key
  2504.  
  2505. =cut
  2506. sub key {
  2507.     my $self = shift;
  2508.     my $args = \%{validate(@_, {
  2509.         section     => {
  2510.             type        => SCALAR,
  2511.             regex       => $Re->{'section'},
  2512.             optional    => 1,
  2513.             default     => $self->default_section,
  2514.         },
  2515.         key         => {
  2516.             regex       => $Re->{'key'},
  2517.             optional    => 1,
  2518.             default     => $self->default_key,
  2519.         },
  2520.         value       => {
  2521.             type        => SCALAR | SCALARREF | ARRAYREF | HASHREF,
  2522.             regex       => $Re->{'value'},
  2523.             optional    => 1,
  2524.         },
  2525.         existonly   => {
  2526.             type        => BOOLEAN,
  2527.             optional    => 1,
  2528.         },
  2529.         json        => {
  2530.             type        => BOOLEAN,
  2531.             optional    => 1,
  2532.         },
  2533.         encode_json => {
  2534.             type        => BOOLEAN,
  2535.             optional    => 1,
  2536.         },
  2537.         decode_json => {
  2538.             type        => BOOLEAN,
  2539.             optional    => 1,
  2540.         },
  2541.     })};
  2542.  
  2543.     # get regular key
  2544.     #
  2545.     if ( ! exists $args->{'value'} ) {
  2546.         print "get regular key\n" if $self->debug;
  2547.         # root by default, `_'
  2548.         #
  2549.         if (! exists $args->{'section'}) {
  2550.             return $self->{'Config'}->{ '_' }->{ $args->{'key'} };
  2551.         }
  2552.  
  2553.         # TODO: JSON auto detect & decoding   #Test-me
  2554.         # get decoded data by JSON
  2555.         if ( $self->typeof( section => $args->{'section'},
  2556.               key => $args->{'key'} ) eq 'json' ) {
  2557.             return decode_json $self->{'Config'}->{
  2558.                 $args->{'section'} }->{ $args->{'key'} };
  2559.         }
  2560.  
  2561.         return $self->{'Config'}->{
  2562.             $args->{'section'} }->{ $args->{'key'} };
  2563.     }
  2564.  
  2565.     # set regular key
  2566.     #
  2567.     elsif ( exists $args->{'value'} ) {
  2568.         print "set regular key\n" if $self->debug;
  2569.         # root by default, `_'
  2570.         #
  2571.         $args->{'section'} = $self->default_section
  2572.             unless exists $args->{'section'};
  2573.  
  2574.         # JSON auto detect & encoding
  2575.         if ( ref $args->{'value'} ) {
  2576.             # setting up encoded data by JSON
  2577.             $self->{'Config'}->{$args->{'section'} }->{ $args->{'key'} }
  2578.                 = encode_json $args->{'value'};
  2579.             return $self;
  2580.         }
  2581.  
  2582.         # existonly
  2583.         #
  2584. # FIXME
  2585. #       return undef
  2586. #           if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
  2587. #               && $args->{'existonly'} == 1 );
  2588.         # setting up without JSON
  2589.         $self->{'Config'}->{
  2590.             $args->{'section'} }->{ $args->{'key'} } = $args->{'value'};
  2591.         return $self;
  2592.     }
  2593.  
  2594.  
  2595.     # Otherwise undef
  2596.     #
  2597.     return undef;
  2598. }
  2599.  
  2600.  
  2601.  
  2602. # ----------------------------------------------------------------------------
  2603. =pod
  2604.  
  2605. =head1 inc (keys: section, key, by, existonly)
  2606.  
  2607. Инкрементирование на 1 или более. Возможное инкрементирование для key.
  2608.  
  2609. =head2 ARGS
  2610.  
  2611. TODO
  2612.  
  2613. =over
  2614. =item section => SCALAR |SCALARREF |ARRAYREF
  2615.  
  2616. TODO
  2617.  
  2618. =item key => SCALAR |SCALARREF |ARRAYREF
  2619.  
  2620. TODO
  2621.  
  2622. =item by => SCALAR
  2623.  
  2624. TODO
  2625.  
  2626. =item existonly => BOOLEAN
  2627.  
  2628. TODO
  2629.  
  2630. =back
  2631.  
  2632. =head3 Example(s)
  2633.  
  2634.  $ini->inc;                                        # `_' -> `default'   : +1
  2635.  $ini->inc( existonly => 1 );
  2636.  $ini->inc( by => N );                             # `_' -> `default'   : +N
  2637.  $ini->inc( by => N, existonly => 1 )
  2638.  $ini->inc( key => 'key' )                         # `_' -> `key'       : +1
  2639.  $ini->inc( key => 'key', by => N )                # `_' -> `key'       : +N
  2640.  $ini->inc( section => 'section', key => 'key' )   # `section' -> `key' : +1
  2641.  $ini->inc( section => '_', key => '_', by => N )  # `_' -> `_'         : +N
  2642.  
  2643.  Status: OK
  2644.  Defaults: section => `_', key => `default'
  2645.  
  2646. =head3 TODO
  2647.  
  2648.  $ini->inc( section => 'name' )
  2649.  $ini->inc( section => [qw/ name1 name2 /] );
  2650.  $ini->inc( section => qr/pcre/ );
  2651.  
  2652.  $ini->inc( key => 'name' );
  2653.  $ini->inc( key => qr/pcre/ );
  2654.  $ini->inc( key => [qw/ name1 name2 /] );
  2655.  
  2656.  %$args: existonly
  2657.  
  2658. =cut
  2659. sub inc {
  2660.     my $self = shift;
  2661.     my $args = \%{validate(@_, {
  2662.         section     => {
  2663.             type        => SCALAR |SCALARREF |ARRAYREF,
  2664.             regex       => $Re->{'section'},
  2665.             default     => $self->default_section,
  2666.             optional    => 1,
  2667.         },
  2668.         key         => {
  2669.             type        => SCALAR |SCALARREF |ARRAYREF,
  2670.             regex       => $Re->{'key'},
  2671.             default     => $self->default_key,
  2672.             optional    => 1,
  2673.         },
  2674.         by          => {
  2675.             type        => SCALAR,
  2676.             regex       => $Re->{'by'},
  2677.             optional    => 1,
  2678.         },
  2679.         existonly   => {
  2680.             type        => BOOLEAN,
  2681.             optional    => 1,
  2682.         },
  2683.     })};
  2684.  
  2685.     # TODO: test-me
  2686.     # getting section list
  2687.     #
  2688.     my @sections = ();
  2689.     # scalarref, qr/pcre/
  2690.     if ( ref $args->{'section'} && ref( $args->{'section'} ) ne 'ARRAY' ) {
  2691.         @sections = $self->find( section => $args->{'section'} );
  2692.     }
  2693.     # arrayref
  2694.     elsif ( ref $args->{'section'} && ref( $args->{'section'} ) eq 'ARRAY' ) {
  2695.         @sections = @{$args->{'section'}};
  2696.     }
  2697.     # scalar
  2698.     else {
  2699.         $sections[0] = $args->{'section'};
  2700.     }
  2701.  
  2702.     # processing sections
  2703.     foreach my $section ( @sections ) {
  2704.         # TODO: test-me
  2705.         # existonly
  2706.         next
  2707.             if ( ! $self->exists( section => $section,
  2708.                     key  => $args->{'key'} )
  2709.                 && $args->{'existonly'} == 1 );
  2710.  
  2711.  
  2712.         # TODO: test-me
  2713.         # if key not a integer setting it to zero
  2714.         if ( $self->typeof( section => $section,
  2715.                 key => $args->{'key'} ) ne 'integer' )
  2716.         {
  2717.             $self->key( section => $args->{'section'}, key => $args->{'key'},
  2718.                 value => 0 );
  2719.         }
  2720.  
  2721.         # TODO: test-me
  2722.         # incrementation by N
  2723.         if ( exists $args->{'by'} ) {
  2724.             $self->{'Config'}->{ $section }->{ $args->{'key'} }
  2725.                 += $args->{'by'};
  2726.             next;
  2727.         }
  2728.  
  2729.  
  2730.         # TODO: test-me
  2731.         # incrementation by 1
  2732.         ++$self->{'Config'}->{ "$args->{'section'}" }->{ "$args->{'key'}" };
  2733.         next;
  2734.     }
  2735.  
  2736.     return $self;
  2737. }
  2738.  
  2739.  
  2740.  
  2741.  
  2742.  
  2743.  
  2744. # ----------------------------------------------------------------------------
  2745. =pod
  2746. =head2 dec (keys: section, key, by, existonly)
  2747.  
  2748. Декрементирование на 1 или более. Возможное декрементирование несколько секций и ключей.
  2749.  
  2750. =head2 ARGS
  2751.  
  2752. =over
  2753. =item section => SCALAR |SCALARREF |ARRAYREF
  2754.  
  2755. TODO
  2756.  
  2757. =item key => SCALAR |SCALARREF |ARRAYREF
  2758.  
  2759. TODO
  2760.  
  2761. =item by => SCALAR
  2762.  
  2763. TODO
  2764.  
  2765. =item existonly => BOOLEAN
  2766.  
  2767. TODO
  2768.  
  2769. =back
  2770.  
  2771.  
  2772. =head3 Example(s)
  2773.  
  2774.  $ini->dec;                                        # `_' -> `default'   : -1
  2775.  $ini->dec( existonly => 1 )
  2776.  $ini->dec( by => N )                              # `_' -> `default'   : -N
  2777.  $ini->dec( by => N, existonly => 1 )
  2778.  $ini->dec( key => 'key' )                         # `_' -> `key'       : -1
  2779.  $ini->dec( key => 'key', by => N )                # `_' -> `key'       : -N
  2780.  $ini->dec( section => 'section', key => 'key' )   # `section' -> `key' : -1
  2781.  $ini->dec( section => '_', key => '_', by => N )  # `_' -> `_'         : -N
  2782.  
  2783.  Status: OK
  2784.  
  2785. =head3 TODO, FIXME
  2786.  
  2787.  $ini->dec( section => 'name' );
  2788.  $ini->dec( section => qr/pcre/ );
  2789.  $ini->dec( section => [qw/ name1 name2 /] );
  2790.  
  2791.  $ini->dec( key => 'name' );
  2792.  $ini->dec( key => qr/pcre/ );
  2793.  $ini->dec( key => [qw/ name1 name2 /] );
  2794.  
  2795.  # мульти-вычитание
  2796.  2014-09-18: $ini->dec( section => 'sec1', key => [qw/ key1 key2 /] );
  2797.  2014-09-18: $ini->dec( section => [qw/ sec1 sec2 /], key => 'key1' );
  2798.  
  2799.  unknown: existonly key
  2800.  
  2801. =cut
  2802. sub dec {
  2803.     my $self = shift;
  2804.     my $args = \%{validate(@_, {
  2805.         section     => {
  2806.             type        => SCALAR |SCALARREF |ARRAYREF,
  2807.             regex       => $Re->{'section'},
  2808.             default     => $self->default_section,
  2809.             optional    => 1,
  2810.         },
  2811.         key         => {
  2812.             type        => SCALAR |SCALARREF |ARRAYREF,
  2813.             regex       => $Re->{'key'},
  2814.             default     => $self->default_key,
  2815.             optional    => 1,
  2816.         },
  2817.         by          => {
  2818.             regex       => $Re->{'by'},
  2819.             optional    => 1,
  2820.         },
  2821.         existonly   => {
  2822.             type        => BOOLEAN,
  2823.             optional    => 1,
  2824.         },
  2825.     })};
  2826.  
  2827.     # defaults
  2828.     $args->{'section'} = $self->default_section
  2829.         unless exists $args->{'section'};
  2830.     $args->{'key'} = $self->default_key unless exists $args->{'key'};
  2831.  
  2832.     # FIXME
  2833.     # if key not a integer setting it to zero
  2834. #   if ( $self->typeof(
  2835. #           section => $args->{'section'},
  2836. #           key => $args->{'key'} ) ne 'integer' ) {
  2837. #       $self->key( section => $args->{'section'}, key => $args->{'key'},
  2838. #           value => 0 );
  2839. #   }
  2840.    
  2841.     # decrementation by N
  2842.     if ( exists $args->{'by'} ) {
  2843.         # existonly
  2844.         #
  2845. #       return undef
  2846. #           if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
  2847. #               && $args->{'existonly'} == 1 );
  2848.         $self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} }
  2849.             -= $args->{'by'};
  2850.         return $self;
  2851.     }
  2852.  
  2853.     # decrementation by 1
  2854.     # existonly
  2855.     # FIXME
  2856. #   return undef
  2857. #       if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
  2858. #           && $args->{'existonly'} == 1 );
  2859.     --$self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} };
  2860.  
  2861.     return $self;
  2862. }
  2863.  
  2864.  
  2865.  
  2866.  
  2867.  
  2868.  
  2869.  
  2870.  
  2871.  
  2872.  
  2873. # ----------------------------------------------------------------------------
  2874. =pod
  2875. =head1 href ( keys: section, href, existonly )
  2876.  
  2877. Добавить, заменить или получить "дерево секций и ключей", ввиде хеш-структуры. Может быть два варианта вложения ключей.
  2878.  
  2879. Каждый ключ первого уровня вложения, -это имя секции.
  2880.   $ini->href( section => 'baz', href => { foo => bar } ); # setter
  2881.   $ini->href( section => 'baz' );                         # accessor
  2882.  
  2883. Каждый ключ второго уровня вложения, -это имя ключа секции.
  2884.  
  2885.  $SPEC => {
  2886.     section_0   => { k1=>v, k2=>v, ... },
  2887.     section_1   => { k1=>v, k2=>v, ... },
  2888.     ....
  2889.  };
  2890.  
  2891.  Status: FIXME, testme
  2892.  
  2893. =head2 ARGS
  2894.  
  2895. TODO
  2896.  
  2897. =over
  2898. =item section => SCALAR |SCALARREF |ARRAYREF
  2899.  
  2900. TODO
  2901.  
  2902. Set
  2903.  
  2904.  $ini->href( href => \% );
  2905.  $ini->href( href => \%, existonly => 1 );
  2906.  
  2907.  $ini->href( section => 'sec1',
  2908.     href => { sec1 => {}, sec2 => {} } );
  2909.  $ini->href( section => [qw/ sec1 /],
  2910.     href => { sec1 => {}, sec2 => {} } );
  2911.  $ini->href( section => qr/pcre/,
  2912.     href => { sec1 => {}, sec2 => {} } );
  2913.  
  2914. Get
  2915.  
  2916.  $href = $ini->href
  2917.  $href = $ini->href( section => 'name' )
  2918.  $href = $ini->href( section => '^pcre$' )
  2919.  $href = $ini->href( section = [qw/ section section2 ... /]);
  2920.  
  2921. =item key => SCALAR |SCALARREF |ARRAYREF
  2922.  
  2923. Set
  2924.  
  2925.  # Scalar
  2926.  $ini->href( section => 's1', key => 'k1',
  2927.     href => { s1 => {k1 => 1} } );
  2928.  # ArrayRef
  2929.  $ini->href( section => 's1', key => [qw/ k1 /],
  2930.     href => { s1 => {k1 => 1} } );
  2931.  # PCRE, qr//
  2932.  $ini->href( section => 's1', key => qr/pcre/,
  2933.     href => { s1 => {k1 => 1} } );
  2934.  
  2935. Get
  2936.  
  2937. TODO
  2938.  
  2939. =item href => HASHREF
  2940.  
  2941. TODO
  2942.  
  2943. =item existonly => BOOLEAN
  2944.  
  2945. TODO
  2946.  
  2947. =back
  2948.  
  2949. =head2 Example(s)
  2950.  
  2951.  # get
  2952.  $href = $ini->href
  2953.  $href = $ini->href( section => 'name' )
  2954.  $href = $ini->href( section => '^pcre$' )
  2955.  $href = $ini->href( section = [qw/ section section2 ... /]);
  2956.  
  2957.  # set
  2958.  $self = $ini->href( href => \% );
  2959.  $self = $ini->href( href => \%, existonly => 1 );
  2960.  $self = $ini->href( section => '_', href => \%, existonly => 1 );  # TODO
  2961.  
  2962.  SINOPSYS:
  2963.     # get all sections
  2964.     $ini->href;
  2965.  
  2966.     # get sections by ArrayRef list
  2967.     $ini->href( section => [qw/ test test_2 /] );
  2968.  
  2969.     # get sections by PCRE matching
  2970.     $ini->href( section => '^pcre$' );
  2971.  
  2972.     # set into root section
  2973.     $ini->href( href => {key11 => 'Value11', key2 => 'Value2'} );
  2974.  
  2975.     # set other section
  2976.     $ini->href( section => 'other', href => { key => 'Value', key2 => 'Value2'} );
  2977.  
  2978. =head2 TODO, FIXME
  2979.  
  2980.  $ini->href( section => 'name' );
  2981.  $ini->href( section => qr/pcre/ );
  2982.  $ini->href( section => [qw/ name1 name2 /] );
  2983.  
  2984.   2014-08-27: existonly key, must to be released!
  2985.  
  2986. =cut
  2987. sub href {
  2988.     my $self = shift;
  2989.     my $args = \%{validate(@_, {
  2990.         section     => {
  2991.             type        => SCALAR |SCALARREF |ARRAYREF,
  2992.             regex       => $Re->{'section'},
  2993.             optional    => 1,
  2994.         },
  2995.         key         => {
  2996.             type        => SCALAR |SCALARREF |ARRAYREF,
  2997.             regex       => $Re->{'key'},
  2998.             optional    => 1,
  2999.         },
  3000.         href        => {
  3001.             type        => SCALAR |SCALARREF |ARRAYREF,
  3002.             optional    => 1,
  3003.         },
  3004.         existonly   => {
  3005.             type        => BOOLEAN,
  3006.             optional    => 1,
  3007.         },
  3008.     })};
  3009.  
  3010.     # store
  3011.     my $href = undef;
  3012.  
  3013.     # get
  3014.     if ( ! exists $args->{'href'} ) {
  3015.         # defaults
  3016. #       $args->{'section'} = '_' unless exists $args->{'section'};
  3017.  
  3018.         # FIXME: test-me
  3019.         # match sections by ArrayRef list TODO
  3020.         # example: $ini->href( section => [qw/ one two /] );
  3021.         if ( exists $args->{'section'} && ref($args->{'section'}) ) {
  3022.             say "match sections by ArrayRef" if $self->debug;
  3023.             foreach my $section ( @{$args->{'section'}} ) {
  3024.                 $href->{$section} = $self->{'Config'}->{$section};
  3025.             }
  3026.             return $href;
  3027.         }
  3028.  
  3029.         # FIXME: test-me
  3030.         # match sections by PCRE
  3031.         # example: $ini->href( section => '^PCRE$' );
  3032.         if ( exists $args->{'section'} &&  ref($args->{'section'}) eq 'Regexp' ) {
  3033.             return $self->{'Config'}->{$args->{'section'}};
  3034.         }
  3035.  
  3036.         # get all sections
  3037.         # example: $ini->href();
  3038.         say "get all sections href" if $self->debug;
  3039.         return $self->{'Config'};
  3040.     }
  3041.  
  3042.     # set
  3043.     elsif ( exists $args->{'href'} ) {
  3044.         # default section
  3045.         $args->{'section'} = $self->default_section
  3046.             unless defined $args->{'section'};
  3047.         # args->href
  3048.         if ( ref( $args->{'href'} ) eq 'HASH' ) {
  3049.             $self->{'Config'}->{$args->{'section'}} = $args->{'href'};
  3050.         }
  3051.         return $self;
  3052.     }
  3053.  
  3054.     # Otherwise undef
  3055.     return undef;
  3056. }
  3057.  
  3058.  
  3059.  
  3060. # ----------------------------------------------------------------------------
  3061. =pod
  3062.  
  3063. =head1 help Undef
  3064.  
  3065. TODO
  3066.  
  3067. =head2 Example
  3068.  
  3069.  $ini->help;
  3070.  $ini->help( topic => 'name' );
  3071.  $ini->help( topic => [qw/ name /] );
  3072.  $ini->help( topic => qr/pcre/ );
  3073.  
  3074. =head2 Topics
  3075.  
  3076.  TOC - Table Of Contents
  3077.  
  3078. =cut
  3079. sub help {
  3080.     my $self = shift;
  3081.     my $args = \%{validate(@_, {
  3082.         topic   => {
  3083.             type        => SCALAR |SCALARREF |ARRAYREF,
  3084.             optional    => 1,
  3085.             default     => 'TOC',
  3086.         },
  3087.     })};
  3088.  
  3089. return <<'EOF';
  3090. develop stage, sorry!
  3091. EOF
  3092. }
  3093.  
  3094.  
  3095.  
  3096.  
  3097. # End of Data::Mini module
  3098. #
  3099.  
  3100.  
  3101. =pod
  3102.  
  3103. =head1 OVERVIEW
  3104.  
  3105.  # Regexp skills required
  3106.  #
  3107.  
  3108.  use feature 'say';
  3109.  use Data::Mini;
  3110.  
  3111.  my $ini = Data::Mini->new(
  3112.     path                   => '/tmp/1/1/1/1/1/1/Config.ini',
  3113.     make_directory         => 1, # make tree of new directories
  3114.     debug                  => 1,
  3115.     default_section        => '_',
  3116.     default_key            => 'default',
  3117.     delimeter              => '=',
  3118.  );
  3119.  
  3120.  
  3121.  # set defaults
  3122.  #
  3123.  $ini->default_section( 'new' );      #=> set default section to `new'
  3124.  $ini->default_key( 'new' );          #=> set default key to `new'
  3125.  $ini->delimeter( 'new' );            #=> set default delimeter to `new'
  3126.  
  3127.  # restore defaults
  3128.  #
  3129.  $ini->defaults;                      #=> restore defaults
  3130.  
  3131.  # get defaults
  3132.  #
  3133.  say $ini->default_section;           #=> `_'
  3134.  say $ini->default_key;               #=> `default'
  3135.  
  3136.  
  3137.  # load/add new ini source string from package DATA segment
  3138.  #
  3139.  {
  3140.     local $/ = undef;
  3141.     $ini->reset( source => <DATA> );  # loading from __DATA__
  3142.     say $ini->key;                    #=> undef
  3143.  }
  3144.  __DATA__
  3145.  default=
  3146.  
  3147.  
  3148.  # key setter/accessor ( +JSON encode/decode )
  3149.  $ini->key( section => 'sec1', key => 'key1', value => 'val1' );
  3150.  say $ini->key( section => 'sec1', key => 'key1' ); #=> val1
  3151.  
  3152.  # encode HashRef
  3153.  $ini->key( section => 'sec1', key => 'key1', encode_json = 1,
  3154.    value => { key0 => 0, key1 => 1, key2 => 2, key3 => 3 } );
  3155.  
  3156.  # TODO: encode ArrayRef
  3157.  $ini->key( section => 'sec1', key => 'key1', encode_json = 1,
  3158.    value => [qw/ 1 2 3 4 5 6 7 8 9 0 /] );
  3159.  
  3160.  # decode
  3161.  $ini->key( section => 'sec1', key => 'key1', decode_json = 1 );
  3162.  
  3163.  # incrementation
  3164.  $ini->inc( section => 'sec2', key => 'inc' );              #=> 1
  3165.  $ini->inc( section => 'sec2', key => 'incby', by => 7 );   #=> 7
  3166.  
  3167.  # decrementation
  3168.  $ini->dec( section => 'sec3', key => 'dec' );              #=> -1
  3169.  $ini->dec( section => 'sec3', key => 'decby', by => 7 );   #=> -7
  3170.  
  3171.  # typeof
  3172.  $ini->typeof( section => 'sec1', key => 'key1' );          #=> string
  3173.  $ini->typeof( section => 'sec2', key => 'incby' );         #=> integer
  3174.  
  3175.  # exists/defined
  3176.  $ini->exists( section => 'sec1', key => 'key1' );          #=> true
  3177.  $ini->defined( section => 'sec1', key => 'key1' );         #=> true
  3178.  
  3179.  pcre: be specific!
  3180.  my @sections = $ini->find( section => '.+' );              # Array
  3181.  my $href = $ini->find( section => '.+', key => '.+' );     # HashRef
  3182.  $ini->find( section => '.+', key => '.+', value => '.+' ); # HashRef
  3183.  
  3184.  # get section(s)
  3185.  $href = $ini->href                                            # get all
  3186.  $href = $ini->href( section => '^pcre$' )                     # get by pcre
  3187.  $href = $ini->href( section => [qw/ section section2 ... /]); # get by spec
  3188.  
  3189.  # set section(s)
  3190.  $self = $ini->href( href => \% );
  3191.  $self = $ini->href( href => \%, existonly => 1 ); # TODO
  3192.  $self = $ini->href( section => '_', href => \% ); # TODO
  3193.  
  3194.  $ini->dump_sortkeys( 0 );     # disable sorting
  3195.  $ini->dump_indentlevel( 10 ); # set indent to 10
  3196.  $ini->dump( 'sec1', 'sec2' ); # dump by section(s) list
  3197.  $ini->dump;                   # dumping all
  3198.  
  3199.  exit;
  3200.  
  3201.  __DATA__
  3202.  
  3203.  [section]
  3204.  key=value
  3205.  
  3206. =cut
  3207.  
  3208.  
  3209. =head1 INI Specification
  3210.  
  3211. The INI file format is an informal standard for configuration files for some platforms or software. INI files are simple text files with a basic structure composed of "sections" and "properties".
  3212.  
  3213. In MS-DOS and 16-bit Windows platforms up through Windows ME and Windows 2000, the INI file served as the primary mechanism to configure operating system and installed applications features, such as device drivers, fonts, startup launchers, and things that needed to be initialized in booting Windows. INI files were also generally used by applications to store their individual settings
  3214.  
  3215. Linux and Unix systems also use a similar file format for system configuration. In addition, platform-agnostic software may use this file format for configuration. It is human-readable and simple to parse, so it is a usable format for configuration files that do not require much greater complexity.
  3216.  
  3217. =head2 Keys ( properties )
  3218.  
  3219. The basic element contained in an INI file is the key or property. Every key has a name and a value, delimited by an equals sign (=). The name appears to the left of the equals sign.
  3220.  
  3221.  key=value
  3222.  
  3223. =head2 Sections
  3224.  
  3225. Keys may (but need not) be grouped into arbitrarily named sections. The section name appears on a line by itself, in square brackets ([ and ]). All keys after the section declaration are associated with that section. There is no explicit "end of section" delimiter; sections end at the next section declaration, or the end of the file. Sections may not be nested.
  3226.  
  3227.  [section]
  3228.  a=a
  3229.  b=b
  3230.  
  3231. =head2 Comments
  3232.  
  3233. Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
  3234.  
  3235.  ; comment text
  3236.  
  3237. =head2 Blank lines
  3238.  
  3239. Some rudimentary programs do not allow blank lines. Every line must therefore be a section head, a property, or a comment.
  3240.  
  3241. =head2 Duplicate names
  3242.  
  3243. Most implementations only support having one property with a given name in a section. The second occurrence of a property name may cause an abort; the second occurrence may be ignored (and the value discarded); the second occurrence may override the first occurrence (discard the first value). Some programs use duplicate property names to implement multi-valued properties.
  3244.  
  3245. Interpretation of multiple section declarations with the same name also varies. In some implementations, duplicate sections simply merge their properties together, as if they occurred contiguously. Others may abort, or ignore some aspect of the INI file.
  3246.  
  3247. =head2 Escape characters
  3248.  
  3249. Some implementations also offer varying support for an escape character, typically with the backslash (\). Some support "line continuation", where a backslash followed immediately by EOL (end-of-line) causes the line break to be ignored, and the "logical line" to be continued on the next actual line from the INI file. Implementation of various "special characters" with sequences escapes is also seen.
  3250.  
  3251.  Common escape sequences
  3252.  Sequence   Meaning
  3253.  \\         \ (a single backslash, escaping the escape character)
  3254.  \0         Null character
  3255.  \a         Bell/Alert/Audible
  3256.  \b         Backspace, Bell character for some applications
  3257.  \t         Tab character
  3258.  \r         Carriage return
  3259.  \n         Line feed
  3260.  \;         Semicolon
  3261.  \#         Number sign
  3262.  \=         Equals sign
  3263.  \:         Colon
  3264.  \x????     Unicode character with hexadecimal code point corresponding to ????
  3265.  
  3266. =head2 Global properties
  3267.  
  3268. Optional "global" properties may also be allowed, that are declared before any section is declared
  3269.  
  3270. =head2 Hierarchy
  3271.  
  3272. Most commonly, INI files have no hierarchy of sections within sections. Some files appear to have a hierarchical naming convention, however. For section A, subsection B, sub-subsection C, property P and value V, they may accept entries such as [A.B.C] and P=V (Windows' xstart.ini), [A\B\C] and P=V (the IBM Windows driver file devlist.ini), or [A] and B,C,P = V (Microsoft Visual Studio file AEMANAGR.INI).
  3273.  
  3274. It is unclear whether these are simply naming conventions that an application happens to use in order to give the appearance of a hierarchy, or whether the file is being read by a module that actually presents this hierarchy to the application programmer.
  3275.  
  3276. =head2 Name/value delimiter
  3277.  
  3278. Some implementations allow a colon (:) as the name/value delimiter (instead of the equals sign)
  3279.  
  3280. =head2 Quoted values
  3281.  
  3282. Some implementations allow values to be quoted, typically using double quotes and/or apostrophes. This allows for explicit declaration of whitespace, and/or for quoting of special characters (equals, semicolon, etc.). The standard Windows function GetPrivateProfileString supports this, and will remove quotation marks that surround the values.
  3283.  
  3284. =head2 Whitespace
  3285.  
  3286. Interpretation of whitespace varies. Most implementations ignore leading and trailing whitespace around the outside of the property name. Some even ignore whitespace within values (for example, making "host name" and "hostname" equivalent). Some implementations also ignore leading and trailing whitespace around the property value; others consider all characters following the equals sign (including whitespace) to be part of the value.
  3287.  
  3288. =head2 Order of sections and properties
  3289.  
  3290. In most cases the order of properties in a section and the order of sections in a file is irrelevant, but implementations may vary.
  3291.  
  3292.  
  3293.  
  3294. =head1 DEFAULTS
  3295.  
  3296.  section:         `_'
  3297.  key:             `default'
  3298.  
  3299.  *note: use `$ini-defaults' to restore original defaults
  3300.  
  3301. =head2 LINKS
  3302.  
  3303.  wiki: http://ru.wikipedia.org/wiki/.ini
  3304.  msdn: http://msdn.microsoft.com/en-us/library/windows/desktop/ms717987%28v=vs.85%29.aspx
  3305.  
  3306.  
  3307. =head1 Data::Mini::JSON
  3308.  
  3309. TODO
  3310. =cut
  3311.  
  3312. # __END__ of Data::Mini
  3313. 1;
  3314.  
  3315.  
  3316.  
  3317.  
  3318. package Data::Mini::JSON;
  3319. use Params::Validate  qw/ :all /;
  3320. use feature           qw/ say /;
  3321. use Mo                qw/ default build is required /;
  3322. use JSON              qw/ encode_json decode_json /;
  3323. use Data::Dumper;
  3324. use POSIX             qw/ strftime /;
  3325.  
  3326. has data => ( is => 'rw', default => sub { undef } );
  3327. has default_key => ( is => 'rw', default => 'default' );
  3328.  
  3329. =pod
  3330.  
  3331. =head1 DESCRIPTION
  3332.  
  3333. Нужен такой модуль, чтоб брал данные и автоматически, с помощъю JSON управлял ими.
  3334.  
  3335. =head1 OVERVIEW
  3336.  
  3337.  use JSON;
  3338.  use Data::Mini::JSON;
  3339.  
  3340.  my $obj = Data::Mini::JSON->new( default_key => 'default', debug => 1 );
  3341.  
  3342.  key( key => value, key2 => value )
  3343.  delete( key => 'default' )
  3344.  inc( key => 'name', by => 1 );
  3345.  dec( key => [qw/ name /], by => 1000 );
  3346.  dump( key => 'key' )
  3347.  finish
  3348.  
  3349.  
  3350. =cut
  3351.  
  3352. sub BUILD {
  3353.   my $self = shift;
  3354.  
  3355.   # data exists, decoding
  3356.   #
  3357.   if ( defined $self->data ) {
  3358.     $self->data( decode_json $self->data );
  3359.   }
  3360.  
  3361.   # No data, making new!
  3362.   #
  3363.   else {
  3364.     $self->data( +{ ctime => strftime("%F %T", localtime),
  3365.       maker => __PACKAGE__ } );
  3366.   }
  3367. }
  3368.  
  3369.  
  3370.  
  3371.  
  3372. sub DESTROY {
  3373.   my $self = shift;
  3374. }
  3375.  
  3376.  
  3377.  
  3378.  
  3379.  
  3380.  
  3381.  
  3382. =pod
  3383.  
  3384. =head1 finish
  3385.  
  3386. TODO
  3387.  
  3388. =cut
  3389. sub finish {
  3390.   my $self = shift;
  3391.   return encode_json $self->data;
  3392. }
  3393.  
  3394.  
  3395.  
  3396.  
  3397.  
  3398.  
  3399.  
  3400. =pod
  3401.  
  3402. =head1 delete key => Scalar | ScalarRef
  3403.  
  3404. TODO
  3405.  
  3406. =cut
  3407. sub delete {
  3408.   my $self = shift;
  3409.   my $args = \%{validate(@_, {
  3410.     key   => {
  3411.       type      => SCALAR |ARRAYREF,
  3412.       default   => $self->default_key,
  3413.       optional  => 1,
  3414.     },
  3415.   })};
  3416.  
  3417.   if ( ! ref($args->{'key'}) ) {
  3418.     delete $self->{'data'}->{ $args->{'key'} };
  3419.   } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
  3420.     foreach ( @{$args->{'key'}} ) {
  3421.       delete $self->{'data'}->{ $_ };
  3422.     }
  3423.   }
  3424.  
  3425.   return $self;
  3426. }
  3427.  
  3428.  
  3429.  
  3430.  
  3431. =pod
  3432.  
  3433. =head1 dump key => Scalar |Arrayref, sort => Bool, indent => Bool
  3434.  
  3435. TODO
  3436.  
  3437. =cut
  3438. sub dump {
  3439.   my $self = shift;
  3440.   my $args = \%{validate(@_, {
  3441.     key   => {
  3442.       type      => SCALAR |ARRAYREF,
  3443.       optional  => 1,
  3444.     },
  3445.     sort  => {
  3446.       type      => BOOLEAN,
  3447.       default   => 0,
  3448.       optional  => 1,
  3449.     },
  3450.     indent  => {
  3451.       type      => SCALAR,
  3452.       regex     => qr/^\d+$/,
  3453.       default   => 1,
  3454.       optional  => 1,
  3455.     },
  3456.   })};
  3457.  
  3458.   # args->sort
  3459.   if ( exists $args->{'sort'} ) {
  3460.     $Data::Dumper::Sortkeys = 1,
  3461.   }
  3462.  
  3463.   # args->indent
  3464.   if ( exists $args->{'indent'} ) {
  3465.     $Data::Dumper::Indent = $args->{'indent'};
  3466.   }
  3467.  
  3468.   # dumping all
  3469.   #
  3470.   unless ( exists $args->{'key'} ) {
  3471.     say Dumper $self->{'data'};
  3472.     return $self;
  3473.   }
  3474.  
  3475.   # args->key
  3476.   #
  3477.   my $heap = {};
  3478.   if ( ! ref($args->{'key'}) ) {
  3479.     $heap->{ $args->{'key'} } = $self->{'data'}->{ $args->{'key'} };
  3480.   } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
  3481.     foreach ( @{$args->{'key'}} ) {
  3482.       $heap->{ $_ } = $self->{'data'}->{ $_ };
  3483.     }
  3484.   }
  3485.  
  3486.   # dumping heap
  3487.   say Dumper $heap;
  3488.   return $self;
  3489. }
  3490.  
  3491.  
  3492.  
  3493.  
  3494.  
  3495. =pod
  3496.  
  3497. =head1 inc key => Scalar |ScalarRef, by => Num
  3498.  
  3499. TODO
  3500.  
  3501.  $foo->inc( key => 'name' );
  3502.  $foo->inc( key => [qw/ name /], by => 3 );
  3503.  
  3504. =cut
  3505. sub inc {
  3506.   my $self = shift;
  3507.   my $args = \%{validate(@_, {
  3508.     key   => {
  3509.       type      => SCALAR |ARRAYREF,
  3510.       default   => $self->default_key,
  3511.       optional  => 1,
  3512.     },
  3513.     by    => {
  3514.       type      => SCALAR,
  3515.       regex     => qr/^\d+$/,
  3516.       default   => 1,
  3517.       optional  => 1,
  3518.     },
  3519.   })};
  3520.  
  3521.   if ( ! ref($args->{'key'}) ) {
  3522.     $self->{'data'}->{ $args->{'key'} } += $args->{'by'};
  3523.   } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
  3524.     foreach ( @{$args->{'key'}} ) {
  3525.       $self->{'data'}->{ $_ } += $args->{'by'};
  3526.     }
  3527.   }
  3528.  
  3529.   return $self;
  3530. }
  3531.  
  3532.  
  3533.  
  3534.  
  3535.  
  3536.  
  3537.  
  3538. =pod
  3539.  
  3540. =head1 dec key => Scalar |ScalarRef, by => Num
  3541.  
  3542. TODO
  3543.  
  3544. =cut
  3545. sub dec {
  3546.   my $self = shift;
  3547.   my $args = \%{validate(@_, {
  3548.     key   => {
  3549.       type      => SCALAR |ARRAYREF,
  3550.       default   => $self->default_key,
  3551.       optional  => 1,
  3552.     },
  3553.     by    => {
  3554.       type      => SCALAR,
  3555.       regex     => qr/^\d+$/,
  3556.       default   => 1,
  3557.       optional  => 1,
  3558.     },
  3559.   })};
  3560.  
  3561.   if ( ! ref($args->{'key'}) ) {
  3562.     $self->{'data'}->{ $args->{'key'} } -= $args->{'by'};
  3563.   } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
  3564.     foreach ( @{$args->{'key'}} ) {
  3565.       $self->{'data'}->{ $_ } -= $args->{'by'};
  3566.     }
  3567.   }
  3568.  
  3569.   return $self;
  3570. }
  3571.  
  3572.  
  3573.  
  3574.  
  3575. =pod
  3576.  
  3577. =head1 key key => Scalar |ArrayRef, value => HashRef
  3578.  
  3579. TODO
  3580.  
  3581. =cut
  3582. sub key {
  3583.   my $self = shift;
  3584.   my $args = \%{validate(@_, {
  3585.     key   => {
  3586.       type      => SCALAR |ARRAYREF,
  3587.       optional  => 1,
  3588.     },
  3589.     value => {
  3590.       type      => HASHREF,
  3591.       optional  => 1,
  3592.     },
  3593. #   inc     => {},
  3594. #   dec     => {},
  3595. #   undef   => {},
  3596. #   length  => {},
  3597.   })};
  3598.  
  3599.  
  3600.   # set
  3601.   #
  3602.   if ( exists $args->{'value'} && ref($args->{'value'}) eq 'HASH' ) {
  3603.     $self->data( { %{$self->data}, %{$args->{'value'}} } );
  3604.   }
  3605.  
  3606.   # get
  3607.   #
  3608.   elsif ( exists $args->{'key'} ) {
  3609.     return $self->data->{$args->{'key'}};
  3610.   }
  3611.  
  3612.   return $self;
  3613. }
  3614.  
  3615.  
  3616.  
  3617.  
  3618. =head1 COPYRIGHT
  3619.  
  3620. Copyright (c) 2013-2014. by Michael Shelkovoy ( мишъцх, `i/ust:m0user:sts=2' )
  3621.  
  3622. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
  3623.  
  3624. =head1 THANKS
  3625.  
  3626. Thanks to ADAMK. _to_ini_string & _from_ini_string modified parts of Config::Tiny
  3627.  
  3628. =cut
  3629.  
  3630. 1;
  3631. __END__
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement