Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # ai, sts=2, ts=4, sw=2;
- package Data::Mini; # FIXME: this module work's isn't correct!
- BEGIN {
- $Data::Mini::VERSION = 0.91;
- $Data::Mini::AT = '2014-09-30';
- $Data::Mini::AUTHORITY = 'Михаил Шелковой ( мишъцх )';
- $Data::Mini::TEAMGROUP = 'ust:m0userZ';
- $Data::Mini::SINCE = 'at 2013-07-13 from Config::Tiny by ADAMK';
- $Data::Mini::STATUS = 'moderning';
- $Data::Mini::REQUIRE = [qw/ Mo Params::Validate JSON /];
- $Data::Mini::JSON::VERSION = 0.8;
- $Data::Mini::JSON::AT = '2014.09.25';
- $Data::Mini::JSON::AUTHOR = 'Miki m0user';
- $Data::Mini::JSON::TEAMGROUP = 'Информационная безопасность Украины';
- $Data::Mini::JSON::SINCE = '2014.09.24';
- };
- # OS
- #
- use Mo qw/ default build is required chain /;
- =pod
- =encoding utf8
- =head1 NAME
- Data::Mini - Модуль для работы с файлами `ini'.
- Смотри "Ini files specification"
- =head1 TOC, "Table Of Contents"
- Data::Mini
- Data::Mini::JSON
- =head1 DESCRIPTION, "Описание"
- TODO
- =head2 Оссобенности
- * Автоматическое открытие и закрытие файла `path'
- * Возможно считывать с нескольких файлов в одну структуру
- * Возможно дополнять в структуру данные через ключ `source'
- * Возможно вообще не использовать файл, используя только `memory'
- * Возможность читать с одного файла а писать в другой
- * Легкий и удобный интерфейс для разного рода задач!
- * Полный контроль. смотри `new'
- * Исключение не нужного `exclude'. Осторожно работает 'autowrite'
- * Может создать дерего дерикторий. `make_directory'
- * Возможность использовать вложеные структуры, используя `json'
- * Секции, ключи могут использоваться по умолчанию.
- * Требуются знания PCRE!
- * Читайте дальше!
- * Осторожно работает 'autowrite' !!
- * Спасиба!
- =head1 TODO, FIXME
- 2014-09-29: json method
- 2014-09-28: multi-types, see: exclude
- 2014-09-25: section & key for `_from_ini_string'
- 2014-09-22: FIXME: complete the `read' & `write'!
- 2014-09-21: delimeter => default_delimeter
- 2014-09-19: FIXME: new, read, inc, dec, find ( multi-typing )
- 2014-09-18: Один ангумент вместо двоих для `key': json => 1
- 2014-09-18: FIXME: Автоматическое в `key', определять и декодировать JSON
- 2014-09-18: find %$args modification
- 2014-09-15: POD for "new/BUILD" items
- 2014-09-15: !! POD !!
- 2014-09-15: TODO: exclude for new (BUILD)
- 2014-09-15: Params::Validate
- 2014-09-14: тип qr// для PCRE вместо обычных string
- =head1 SYNOPSIS
- Module variables
- VERSION, AT, AUTHORITY, TEAMGROUP, CODENAME, SINCE
- Main methods
- m new ( args: path, source, autoread, autowrite, dump_indentlevel,
- dump_sortkeys, debug, memory, make_directory,
- default_section, default_key, default_json_key,
- delimeter )
- m read ( args: path, source, make_directory, delimeter, write_path )
- m write ( args: path, source, make_directory, delimeter )
- m reset ( args: memory, source, delimeter )
- m rename ( args: file, section, key) # TODO
- m dump ( args: section, dump_indentlevel, dump_sortkeys )
- F delete ( args: path, section, key ) # TODO
- Miscellaneous methods
- m typeof ( args: section, key )
- m exists ( args: section, key )
- m defined ( args: section, key )
- m find ( args: section, key, value )
- m SECTIONS ( args: section, counter ) # FIXME
- m KEYS ( args: section, counter )
- m VALUES ( args: section, counter )
- Get/Set methods
- m key ( args: section, key, value, existonly )
- m inc ( args: section, key, by, existonly )
- m dec ( args: section, key, by, existonly )
- TE href ( args: section, href, existonly )
- M exclude ( args: section, key, value )
- TO json ( args: section, key, json_key, )
- Private/Closed methods
- m _from_ini_string ( args: section, key, source )
- m _to_ini_string ( args: section, key, delimeter ) # TODO
- Defaults settings
- defaults ( args: section, key, json_key, delimeter )
- default_section ( '_' )
- default_key ( 'default' )
- default_json_key ( 'default' )
- delimeter ( '=' )
- =cut
- use Carp 'confess';
- use feature 'say';
- use Data::Dumper;
- use JSON qw/ encode_json decode_json /;
- use Params::Validate ':all';
- # FIXME: Re: Data::Mini validate templates for params
- #
- our $Re = {
- section => qr/^[\S]+$/,
- key => qr/^[\S]+$/,
- json_key => qr/^[\S]+$/,
- value => qr/.+/,
- path => qr/.+/, # FIXME
- source => qr/.+/,
- by => qr/^\d+$/, # inc, dec
- topic => qr/^.+$/,
- debug => qr/^(?:0|1|2|3)$/, # TODO
- };
- ##############################################################################
- # Code segment
- # ----------------------------------------------------------------------------
- =pod
- =head1 METHODS
- Далее подробно рассмотрим каждую функцию.
- =head1 new %params, ( BUILD/DESTROY )
- Каждый объект Data::Mini начинает работу из его создания. Именно с функции `new' начинает работать все остальное в этой библиотеке.
- =head2 ARGS
- local $\ = undef;
- my $ini = Data::Mini->new(
- path => 'path',
- source => <DATA>,
- autoread => 1,
- autowrite => 1,
- write_path => 'path', # TODO
- dump_indentlevel => 2,
- dump_sortkeys => 1,
- debug => 0,
- memory => 0,
- make_directory => 0,
- default_section => '_',
- default_key => 'default',
- default_json_key => 'default',
- backup => "", # TODO
- exclude => undef, # TODO
- delimeter => '=',
- allow_spaces => 0, # TODO
- );
- =cut
- has timestamp => ( is => 'ro', default => sub { time }, lazy => 0 );
- has path => ( is => 'rw', default => sub { undef } );
- has write_path => ( is => 'rw', default => sub { undef } ); # TODO
- has source => ( is => 'rw', required => 0, default => sub { undef } );
- has autoread => ( is => 'rw', required => 0, default => 1, chain => 1);
- has autowrite => ( is => 'rw', required => 0, default => 1, chain => 1 );
- has debug => ( is => 'ro', default => 0, chain => 1 );
- has memory => ( is => 'ro', default => 0 );
- has make_directory => ( is => 'rw', default => 0 );
- has JSON => ( is => 'rw', default => sub { undef } );
- # dump
- has dump_indentlevel => ( is => 'rw', default => 1, chain => 1 );
- has dump_sortkeys => ( is => 'rw', default => 1, chain => 1 );
- # defaults
- has delimeter => ( is => 'rw', default => '=', chain => 1 ); # FIXME all!
- has default_section => ( is => 'rw', default => '_', chain => 1 );
- has default_key => ( is => 'rw'. default => 'default', chain => 1 );
- has default_json_key => ( is => 'rw', default => 'default', chain => 1 );
- has allow_spaces => ( is => 'rw', default => 0, chain => 1 ); # TODO
- =pod
- =head2 %$args - Аргументы метода `new'
- Следующии аргументы используются для создания объекта
- =over
- =item path => Scalar | ArrayRef (TODO)
- Путь к файлу(ам) конфига
- Data::Mini->new( path => 'path' );
- Data::Mini->new( path => [qw/ path1 path2 /], write_path => 'path3' ); # TODO
- *Конфликт в `write'. он должен писать в один файл, а там `ArrayRef'
- =item source => SCALAR
- Дополнить данные конфига из переменной. Оссобено удобно добавлять стартовые настройки прямо из конца модуля, как в примере.
- {
- undef $/;
- my $source = <DATA>;
- $ini = Data::Mini->new(
- path => '/path/to/file.ini',
- source => <DATA>,
- default_section => 'SECTION',
- default_key => 'KEY'
- );
- $ini->defaults; # restore defaults: section, key, delimeter
- my $ini = Data::Mini->new( source => <DATA>, memory => 1,
- delimeter => '<__NULL__>' );
- $ini->write( path => '/path/to/clone.ini', make_directory => 1,
- delimeter => 1 );
- }
- __DATA__
- [section]
- key=value
- =item memory => Bool
- Не ипользуем файлы.. Хотя можно записать ввиде "клона", через `write'
- my $ini = Data::Mini->new( memory => 1 );
- my $ini = Data::Mini->new( memory => 1, source => <DATA> );
- $ini->write( path => 'clone.ini' );
- =item autoread => Bool
- Прочитать данные при создании объекта
- my $ini = Data::Mini->new( path => 'path', autoread => 1 );
- $ini->autoread( 1 );
- say $ini->autoread;
- =item autowrite => Bool
- Записать данные в конфиг файл при закрытии объекта
- my $ini => Data::Mini->new( path => 'path', autowrite => 0 );
- $ini->autowrite( 1 );
- say $ini->autowrite;
- =item dump_indentlevel => Num
- Уровень отступов для метода dump
- my $ini => Data::Mini->new( path => 'path', dump_indentlevel => 3 );
- $ini->dump_indentlevel( 1 );
- say $ini->dump_indentlevel;
- =item dump_sortkeys => Bool
- Сортировка ключей для метода dump
- my $ini => Data::Mini->new( path => 'path', dump_sortkeys => 1 );
- $ini->dump_sortkeys( 0 );
- say $ini->dump_sortkeys;
- =item make_directory => Bool
- Создает дерево директорий в файловой системе
- my $mini = Data::Mini->new( path => '/tmp/dir1/dir2/dir3/file.ini',
- make_directory => 1 );
- =item default_section => Str
- Задает имя секции по умолчанию
- $mini->default_section( 'new_default_section_name' );
- $mini->defaults; # restore default key and section
- see also: defaults, default_key
- =item default_key => Str
- Задает имя ключа по умолчанию
- $mini->default_key( 'new_default_key_name' );
- $mini->defaults; # restore default key and section
- see also: defaults, default_section, delimeter
- =item default_json_key => Str
- TODO
- =item delimeter => Str
- Изменить разделитель полей property=value
- $mini = Data::Mini->new(memory => 1, autoread => 0)->read( delimeter => 1 );
- $mini->delimeter( 'new' );
- $mini->write( path => '/path/to.ini', delimeter => ':', make_directory => 1 );
- delimeter => Str
- Установить следуюший символ, как разделитель. property=value
- delimeter => Bool (TODO)
- Установить символ разделитель по умолчанию.
- delimeter => 1
- add me to:
- # write
- # value 1 for delimeter is a default delimeter character for a write method
- $mini->write( path => '/path/to.ini', delimeter => 1, make_directory => 1 );
- =item allow_spaces => Bool, TODO: releaseme
- Разрешить использовать пробелы в именах секций и ключей. По умолчанию выключено
- $mini = Data::Mini->new( memory => 1, autoread => 0 )->read(
- delimeter => '<=>', allow_spaces => 1 );
- =item debug => Bool
- Используется для разработчика основном.
- =back
- =cut
- sub BUILD {
- my $self = shift;
- # autoread
- $self->read if $self->autoread;
- # DEBUG
- say __PACKAGE__."::BUILD" if $self->debug;
- }
- sub DESTROY {
- my $self = shift;
- # autowrite
- $self->write if $self->autowrite;
- # DEBUG
- say __PACKAGE__."::DESTROY" if $self->debug;
- }
- #
- # ----------------------------------------------------------------------------
- =pod
- =head1 json
- Используем вложенные структуры с помощью JSON
- =head2 synopsis
- $mini->json( section => 'name', key => [qw/ name /], json_key => qw/ name /,
- set => { k => v }, inc => [qw/ name /], dec => 'name', by => 2,
- undef => [qw/ name /], delete => [qw/ name /],
- exists => 'name', defined => 'name', reset => 1 );
- =head2 $args
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( section => 'name' );
- $mini->json( section => qr/pcre/ );
- $mini->json( section => [qw/ name /] );
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( key => 'name' );
- $mini->json( key => qr/pcre/ );
- $mini->json( key => [qw/ name /] );
- =item json_key => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( json_key => 'name' );
- $mini->json( json_key => qr/pcre/ );
- $mini->json( json_key => [qw/ name /] );
- =item set => HASHREF
- TODO
- $mini->json( set => { k => v } );
- =item inc => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( inc => 'name', by => 1 );
- $mini->json( inc => qr/pcre/, by => 3 );
- $mini->json( inc => [qw/ name /], by => 5 );
- =item dec => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( dec => 'name', by => 1 );
- $mini->json( dec => qr/pcre/, by => 3 );
- $mini->json( dec => [qw/ name /], by => 5 );
- =item by => SCALAR
- TODO
- $mini->json( inc => 'name', by => 1 );
- $mini->json( inc => qr/pcre/, by => 3 );
- $mini->json( dec => [qw/ name /], by => 5 );
- =item undef => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( undef => 'name' );
- $mini->json( undef => qr/pcre/ );
- $mini->json( undef => [qw/ name /] );
- =item delete => SCALAR |SCALARREF |ARRAYREF
- TODO
- $mini->json( delete => 'name' );
- $mini->json( delete => qr/pcre/ );
- $mini->json( delete => [qw/ name /] );
- =item exists => SCALAR
- TODO
- $mini->json( json_key => 'name', exists => 1 );
- =item defined => SCALAR
- TODO
- $mini->json( json_key => 'name', defined => 1 );
- =back
- =head2 See also
- Data::Mini::JSON
- =cut
- sub json {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'section'},
- default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'key'},
- default => $self->default_key,
- optional => 1,
- },
- json_key => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'json_key'},
- default => $self->default_json_key,
- optional => 1,
- },
- set => {
- type => HASHREF,
- optional => 1,
- },
- inc => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- dec => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- by => {
- type => SCALAR,
- regex => $Re->{'by'},
- default => 1,
- optional => 1,
- },
- undef => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- delete => {
- type => SCALAR |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- })};
- $self->Data( Data::Mini::JSON->new );
- # section
- # key
- # json_key
- # set
- # inc
- # dec
- # undef
- # delete
- return $self;
- }
- # TODO
- # ----------------------------------------------------------------------------
- =pod
- =head1 exclude ( keys: section, key, value, reset )
- Исключить секции или ключи, а также ключи по их значению. Кароче это крутая фича для извлечения всякого не нужного хлама, который не нужный для конкретной сессии. Будьте внимательны, работает autowrite, который на конце сессии автоматически сохраняет все изменения! К счастью это можно отключить.
- $ini->autowrite( 0 );
- Часто бывает полезно при использывании большых ненужных объемов данных, которые не используються. Для минимизации использывания памяти достаточно использывать только нужное, а всё остальное можно исключить. По завершению работы exclude возвращает все секции и ключи, которые были удалены - ввиде хэш-структуры. А в самом объекте 'Data::Mini' скилет данных будет, без исключенных секций и кдючей
- =head2 %$args properties
- Ключи метода. Всего _
- =over
- =item section => SCALAR | SCALARREF | ARRAYREF
- $ini->exclude( section => 'name' );
- $ini->exclude( section => qr/pcre/ );
- $ini->exclude( section => [qw/ name /] );
- =item key => SCALAR | SCALARREF | ARRAYREF
- $ini->exclude( key => 'name' );
- $ini->exclude( key => qr/pcre/ );
- $ini->exclude( key => [qw/ name /] );
- =item value => SCALARREF
- $ini->exclude( value => qr/pcre/ );
- =item reset => BOOLEAN
- TODO
- =back
- =head2 TODO
- 2014-09-22: $args::reverse => Bool
- 2014-09-15: $args::reset => Bool
- =cut
- sub exclude {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF | ARRAYREF,
- regex => $Re->{ 'section' },
- default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR |SCALARREF | ARRAYREF,
- regex => $Re->{ 'key' },
- optional => 1,
- },
- value => {
- type => SCALARREF,
- regex => $Re->{ 'value' },
- optional => 1,
- },
- reset => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- my $excluded = {};
- # zero-length args
- return undef unless defined $args;
- # Stage_1: preparing %excluded
- #
- # matching sections
- if ( ref( $args->{'section'}) eq 'ARRAY' ) { # by ArrayRef
- %{$excluded} = map { $_ => $self->{'Config'}->{$_} }
- @{ $args->{'section'} };
- } elsif ( ref( $args->{'section'} ) eq 'Regexp' ) { # by PCRE
- %{$excluded} = map {
- unless( ref($_) eq 'HASH' ) {
- $_ => $self->{'Config'}->{$_};
- } else { } # do nothing
- } $self->find( section => $args->{'section'} );
- }
- # per section
- foreach my $section ( sort keys %$excluded ) {
- # check for section existion
- if ( ! $self->exists( section => $section ) ) {
- delete $excluded->{$section};
- next;
- }
- # delete sections only
- #
- if ( ! exists $args->{'key'} && ! exists $args->{'value'} ) {
- # new copy
- $excluded->{$section} = $self->{'Config'}->{ $section };
- delete $self->{'Config'}->{ $section };
- }
- # delete by section-keys
- # example: $ini->exclude( section => '\w+', key => '\d+' );
- # example: $ini->exclude( section => ['zero_0'], key => '\d+' );
- # example: $ini->exclude( section => ['zero_0'], key => ['key'] );
- elsif ( exists $args->{'key'} && ! exists $args->{'value'} ) {
- # by ArrayRef
- if ( ref($args->{'key'}) eq 'ARRAY' ) {
- foreach ( @{$args->{'key'}} ) {
- $excluded->{$section}->{"$_"}
- = $self->{'Config'}->{ $section }->{$_};
- delete $self->{'Config'}->{
- $section }->{ "$_" };
- next unless $self->exists( section => $section, # %excluded
- key => $_ );
- }
- # by PCRE
- } elsif ( ref($args->{'key'}) eq 'Regexp' ) {
- my @excluded = grep /$args->{'key'}/,
- keys %{ $self->{'Config'}->{$section} };
- foreach ( @excluded ) {
- $excluded->{$section}->{"$_"}
- = $self->{'Config'}->{ $section }->{$_}; # %excluded
- delete $self->{'Config'}->{$section}->{"$_"};
- }
- }
- # remove section if key-length zero
- delete $self->{'Config'}->{$section}
- unless keys %{$self->{'Config'}->{$section}};
- }
- # delete by section-key-values (by PCRE only)
- # example: $ini->exclude( section => ['zero_0'], value => '\d+' );
- elsif ( exists $args->{'value'}
- and ref ($args->{'value'}) eq 'Regexp' ) {
- # matching
- my %exc = grep /$args->{'value'}/,
- %{ $self->{'Config'}->{$section} };
- # deletion
- $excluded->{ $section } = \%exc;
- foreach ( keys %exc ) {
- delete $self->{'Config'}->{$section}->{"$_"};
- }
- # remove section if key-length zero
- delete $self->{'Config'}->{$section}
- unless keys %{$self->{'Config'}->{$section}};
- }
- }
- return keys %$excluded ? $excluded : undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 defaults ( %$args: section, key, delimeter )
- Временно на время сесии изменить стандартные настройки по умолчанию
- # Restore defaults
- $ini->defaults;
- $ini->defaults( section => 1 ); # restore section only
- $ini->defaults( key => 1 ); # restore key only
- $ini->defaults( delimeter => 1 ); # restore delimeter only
- $ini->defaults( delimeter => 1, key => 1 ); # restore delimeter & key
- # set defaults
- my $ini = $ini->defaults( section => '_' );
- my $ini = $ini->defaults( key => 'default' );
- =head2 setters/accessors
- default_section, default_key
- =head2 TODO
- 2014-09-29: default json_key
- $ini->defaults( section => 1 ); # restore original section name
- $ini->defaults( section => 'new' ); # set new default section name
- =cut
- sub defaults {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => $Re->{'section'},
- optional => 1,
- },
- key => {
- type => SCALAR,
- regex => $Re->{'key'},
- optional => 1,
- },
- json_key => {
- type => SCALAR,
- regex => $Re->{'json_key'},
- optional => 1,
- },
- delimeter => {
- type => SCALAR,
- regex => $Re->{'delimeter'},
- optional => 1,
- },
- })};
- # restore all (by chain feature)
- unless ( @_ ) {
- $self->default_section( '_' )->default_key( 'default' )
- ->default_json_key( 'default' )->delimeter( '=' );
- }
- #=> section
- #
- # set a section name by user given
- if ( ! ref($args->{'section'}) && $args->{'section'} != 1 ) {
- $self->default_section( $args->{'section'} );
- }
- # restore a section
- elsif ( ! ref($args->{'section'}) && $args->{'section'} == 1 ) {
- $self->default_section( '_' );
- }
- #=> key
- #
- # set a key name by user given
- if ( ! ref($args->{'key'}) && $args->{'key'} != 1 ) {
- $self->default_key( $args->{'key'} );
- }
- # restore a key
- elsif ( ! ref($args->{'key'}) && $args->{'key'} == 1 ) {
- $self->default_key( 'default' );
- }
- #=> json_key
- #
- # set a json_key character by user given
- if ( ! ref($args->{'json_key'}) && $args->{'json_key'} != 1 ) {
- $self->default_json_key( $args->{'json_key'} );
- }
- # restore a json_key
- elsif ( ! ref($args->{'json_key'}) && $args->{'json_key'} == 1 ) {
- $self->default_json_key( 'default' );
- }
- #=> delimeter
- #
- # set a delimeter character by user given
- if ( ! ref($args->{'delimeter'}) && $args->{'delimeter'} != 1 ) {
- $self->delimeter( $args->{'delimeter'} );
- }
- # restore a delimeter
- elsif ( ! ref($args->{'delimeter'}) && $args->{'delimeter'} == 1 ) {
- $self->delimeter( '=' );
- }
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 read ( args: path, section, key, source, memory, make_directory, write_path )
- Это главная функция записи данных. Смотри также, `_from_ini_string'
- Status: testme, optimize me
- =head3 ARGS
- TODO
- =over
- =item path => SCALAR |ARRAYREF
- $self->read( path => '/path/to/file' );
- $self->read( path => [qw[ path/to/1 path/to/2 ]], write_path => 1 );
- TODO:
- $self->read( path => qr/pcre/, dir => 'path', write_path => 1 );
- =item dir => SCALAR |ARRAYREF
- Есть два случая использывания `path', SCALAR и ARRAYREF. То есть один файл или список. Иногда мы сами не знаем какой файл нам нужно, но мы знаем где он может находиться.
- =item source => SCALAR
- TODO
- =item memory => BOOLEAN
- $ini->read( source => <DATA>, memory => 1 );
- =item make_directory => BOOLEAN
- $ini->read( path => '/1/1/1/1/1/file.ini', make_directory => 1 );
- =item write_path => SCALAR
- Когда мы задаем при создании объекта патч или несколько, то мы можем изменить патч записи изменений. Если мы зададим в качестве параметра еденицу, -то по умолчанию сохраняться данные будут в первом файле из списка патчей.
- # at 2014-09-22
- $ini->read( path => [qw/ path1 path2 /], write_path => 'path2' );
- # write into $path[0] by default
- $ini->read( path => [qw/ path1 path2 /], write_path => 1 );
- =back
- =head2 Example
- $ini->read;
- $ini->read( source => $source );
- =head3 TODO
- # at 2014-09-24
- $ini->read( section => 'name', source => $src );
- $ini->read( section => [qw/ name /], source => $src );
- $ini->read( section => qr/pcre/i, source => $src );
- # at 2014-09-24
- $ini->read( key => 'name', source => $src );
- $ini->read( key => [qw/ name /], source => $src );
- $ini->read( key => qr/pcre/, source => $src );
- # at 2014-09-21
- $ini->read( spaces => '_', source => $source, memory => 1 );
- $ini->read( allow_spaces => 1, source => $source, memory => 1 );
- # at 2014-09-18
- $ini->read( source => <DATA>, memory => 1 );
- $ini->read( path => '/path/from/read', make_directory => 1 );
- $ini->read( path => [qw/ path1 path2 /] );
- =cut
- sub read { # FIXME
- my $self = shift;
- my $args = \%{validate(@_, {
- section => { # TODO
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{ 'section' },
- optional => 1,
- },
- key => { # TODO
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{ 'key' },
- optional => 1,
- },
- source => {
- type => SCALAR,
- regex => $Re->{'source'},
- optional => 1,
- },
- path => { # TODO
- type => SCALAR | ARRAYREF,
- regex => $Re->{'path'},
- optional => 1,
- },
- make_directory => {
- type => BOOLEAN,
- optional => 1,
- },
- memory => { # FIXME: testme
- type => BOOLEAN.
- optional => 1,
- },
- write_path => { # TODO
- type => SCALAR,
- optional => 1,
- default => 1,
- },
- allow_spaces => { # TODO
- type => BOOLEAN,
- optional => 1,
- default => 0,
- },
- })};
- # args:source
- if ( exists $args->{'source'} ) {
- $self->source( $args->{'source'} );
- }
- # args:write_path
- if ( exists $args->{'write_path'} ) {
- $self->write_path( $args->{'write_path'} );
- }
- # args:path
- $self->path( $args->{'path'} ) if ( $args->{'path'} );
- # make new directories tree
- # args:make_directory
- $self->make_directory( 1 ) if exists $args->{'make_directory'};
- # FIXME: dejavu, смотри ниже
- # data variable for data-storing
- my $data = undef;
- my @files = ();
- # FIXME: path
- # Example:
- # $self->read( path => '/path/to/file' );
- # $self->read( path => [qw[ path/to/1 path/to/2 ]] );
- if ( defined $self->path ) {
- # data variable for data-storing
- #
- my $data = undef;
- my @files = ();
- # FIXME: testme
- # preparing @files
- #
- if ( ! ref( $args->{'path'} ) ) { # SCALAR
- $files[0] = $args->{'path'};
- } elsif ( ref( $args->{'path'} ) eq 'ARRAY' ) { # ARRAY
- @files = @{ $args->{'path'} };
- }
- #
- # getting $data, processing @files
- { local $/ = undef;
- foreach ( sort @files ) {
- $self->path( $_ );
- # make directory & file if not exists
- #
- $self->_make_directory if ! -e $self->path && ! $self->memory;
- open(INI, $self->path) or confess $!;
- $data .= <INI>;
- close INI;
- }}
- # TODO: write_path
- # restoring path. after all, writing into $files[0]!
- $self->path( $files[0] );
- # writing within source
- if ( defined $self->source ) { # TODO: testme
- $self->_from_ini_string( source => $data ."\n".$self->source );
- }
- # writing without source
- else {
- $self->_from_ini_string( source => $data );
- }
- }
- # memory
- elsif ( defined $self->memory ) { # FIXME: testme
- # DEBUG
- say "memory storing" if $self->debug;
- if ( ! $self->source ) {
- # new empty Data::Mini object
- $self->{'Config'} = +{};
- } else {
- # Data::Mini object source only
- $self->_from_ini_string( source => $self->source );
- }
- }
- # DEBUG
- say "read complete" if $self->debug;
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 write ( args: path, section, key, source, make_directory, delimeter )
- Это главная функция записи данных в файл. смотри также, `_to_ini_string'
- Status: all works fine, optimize-me, modern-me
- =head3 args
- TODO
- =over
- =item path => SCALAR
- TODO
- =item source => SCALAR
- TODO
- =item make_directory => BOOLEAN
- TODO
- =item delimeter => SCALAR |BOOLEAN
- TODO
- =back
- =head2 Example
- $content = $ini->write;
- $ini->write( source => <DATA> );
- $ini->write( path => '/path/to/write', make_directory => 1 );
- $ini->write( source => <DATA>, path => '/path/to/clone.ini' );
- =head2 TODO, FIXME
- # at 2014-09-24
- $ini->read( section => 'name', source => $src );
- $ini->read( section => [qw/ name /], source => $src );
- $ini->read( section => qr/pcre/i, source => $src );
- # at 2014-09-24
- $ini->read( key => 'name', source => $src );
- $ini->read( key => [qw/ name /], source => $src );
- $ini->read( key => qr/pcre/, source => $src );
- # at 2014-09-19
- # value 1 for delimeter is a default delimeter character for a write method
- $mini->write( path => '/path/to.ini', delimeter => 1, make_directory => 1 );
- =cut
- sub write {
- my $self = shift;
- my $args = \%{validate(@_, {
- source => {
- type => SCALAR,
- regex => qr/$Re->{'source'}/,
- optional => 1,
- },
- path => {
- type => SCALAR,
- regex => qr/$Re->{'path'}/,
- optional => 1,
- },
- make_directory => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # source data
- if ( exists $args->{'source'} ) {
- $self->source( $args->{'source'} );
- }
- # make new directories
- if ( exists $args->{'make_directory'} ) {
- $self->make_directory(1);
- }
- # storing path for clone if requested
- my $store = $self->path;
- # write/clone to another path
- $self->path( $args->{'path'} ) if ( $args->{'path'} );
- # get source content
- my $content = $self->_to_ini_string;
- # memory only or write to new path if requested
- return $content
- if $self->memory && ! exists $args->{'path'};
- # make new directory flag. setting up
- $self->make_directory(1) if $args->{'make_directory'};
- # make directory & file if not exists
- $self->_make_directory if ! -e $self->path;
- # writing
- return undef unless defined $content;
- open(INI, '>' .$self->path) or confess $!;
- print INI $content;
- close INI;
- # restore path
- $self->path( $store );
- say "write complete" if $self->debug;
- return $content;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 _make_directory
- Строит дерево категорий и файл.
- =cut
- sub _make_directory {
- use File::Path 'mkpath';
- use File::Basename 'dirname';
- my $self = shift;
- if ( $self->make_directory && ! -e $self->path ) {
- # making new directory
- # make_directory( 1 );
- mkpath( dirname( $self->path ) );
- $self->make_directory( 0 );
- system "touch ".$self->path;
- return $self;
- } elsif ( ! -e $self->path ) {
- # making new file without directory creating
- # make_directory( 0 );
- system "touch ".$self->path;
- return $self;
- }
- # Otherwise undef
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 _from_ini_string
- Парсит на секции, ключи и их значения.
- =head3 args
- TODO
- =over
- =item source => SCALAR
- TODO
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item delimeter => BOOLEAN
- TODO
- =back
- =head2 Example
- $ini->_from_ini_string( source => $source, delimeter => 1 );
- Status: optimize me
- =head2 TODO, FIXME
- 2014-09-29: $ini->_from_ini_string( source => $source, generate_hash => 1);
- $ini->_from_ini_string( source => $source, delimeter => ':' );
- $ini->_from_ini_string( section => 'name', source => $source );
- $ini->_from_ini_string( section => qr/pcre/, source => $source );
- $ini->_from_ini_string( section => [qw/ name /], source => $source );
- $ini->_from_ini_string( key => 'name', source => $source );
- $ini->_from_ini_string( key => qr/pcre/, source => $source );
- $ini->_from_ini_string( key => [qw/ name /], source => $source );
- =cut
- sub _from_ini_string { # TODO
- my $self = shift;
- my $args = \%{validate(@_, {
- # TODO
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'section'},
- optional => 1,
- },
- # TODO
- key => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- source => {
- type => SCALAR,
- regex => $Re->{'source'},
- optional => 0, # required
- default => '', # empty by default
- },
- })};
- my $section = $self->default_section;
- my $line = 0;
- foreach ( split /(?:\015{1,2}\012|\015|\012)/, $args->{'source'} ) {
- # line counter
- $line++;
- # skip comments & empty lines
- # TODO: надобы сохранять комментарии :\
- next if /^\s*(?:\#|\;|$)/;
- # remove inline comments
- s/\s\;\s.+$//g;
- # section parsing ('_' is a root section)
- if (/^\s*\[\s*(.+?)\s*]\s*$/) {
- # section checking
- # next unless grep /^$1$/, @sections ); # FIXME:=> @sections
- # preparing section
- $self->{'Config'}->{$section = $1} ||=+{};
- next;
- }
- # TODO: test me
- # getting delimeter
- my $d = $self->delimeter;
- # preparing pcre
- my $pcre = '^\s*([^'.$d.']+?)\s*$d\s*(.*?)\s*$';
- # key checking
- # next unless grep /^$1$/, @keys; # FIXME:=> @keys
- # section properties
- if (/$pcre/) {
- $self->{'Config'}->{$section}->{$1} = $2;
- next;
- }
- # не должно сюда дойти, ошибка парсинга
- confess "error at $line";
- }
- say "_from_ini_string: parsing ini string complete" if $self->debug;
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 _to_ini_string
- write into ini file
- =head2 args
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item value => SCALARREF
- TODO
- =item delimeter => SCALAR
- TODO
- =back
- =head2 Example
- my $ini_source = $ini->_to_ini_string;
- =head2 TODO
- # at 2014-09-19
- $source = $ini->_to_ini_string( delimeter => '=' ); # TODO
- $source = $ini->_to_ini_string( section => 'name' );
- $source = $ini->_to_ini_string( section => qr/pcre/ );
- $source = $ini->_to_ini_string( section => qw/ name1 name2 /] );
- $source = $ini->_to_ini_string( key => 'name' );
- $source = $ini->_to_ini_string( key => qr/pcre/ );
- $source = $ini->_to_ini_string( key => qw/ name1 name2 /] );
- $source = $ini->_to_ini_string( value => qr/pcre/ );
- Status: optimize me
- =cut
- sub _to_ini_string {
- my $self = shift;
- my $content = '';
- my $delimeter = $self->delimeter;
- my $args = \%{validate(@_, {
- section => {
- },
- key => {
- },
- value => {
- },
- delimeter => {
- },
- })};
- # add source to hash-struct, if given
- if ( defined $self->source ) {
- $self->_from_ini_string( source => $self->source );
- # $content .= "\n".$self->source;
- $self->source(undef); # fixed double sections writing at 2014-08-16
- }
- # write via root section `_' sorted first
- foreach my $section (sort { (($b eq '_') <=> ($a eq '_'))
- || ($a cmp $b)} keys %{$self->{'Config'}}) { # root section first
- # disable whitespace characters
- confess __PACKAGE__.": Whitespace character in section name"
- if $section =~ /(?:^\s|\n|\s$)/s;
- my $block = $self->{'Config'}->{$section}; # getting block
- $content .= "\n" if length $content; # skip first line
- $content .= "[$section]\n" unless $section eq '_';
- foreach my $property (sort keys %$block) {
- confess "Whitespace into property: $section\: $property\n"
- if $block->{"$property"} =~ /(?:\012|\015)/s;
- $content .= "$property$delimeter".$block->{"$property"}."\n"; # gen
- }
- }
- say "_to_ini_string: parsing Config-hash structure into ini string complete"
- if $self->debug;
- return $content;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 reset ( %$args: source, delimeter, memory )
- Запись и чтение одновременно.
- $ini->reset;
- Запись и чтение одновременно. Сначала мы ставим переменную разделителя строк в режим поглощения, записываем данные и читаем наного с аргументами:
- undef $/;
- $ini->reset( delimeter => ':', source => <DATA> );
- =head2 ARGS
- Аргументы метода `reset'
- =over
- =item source => Scalar
- Строка сырых данных
- =item delimeter => Scalar |Bool
- Разделитель полей ключа и данных. например, key=value
- =item memory => BOOLEAN
- удоляет старую структуру и заполняет новую в режиме `memory'
- =back
- =head3 Example
- package Sample::Role;
- use Moo::Role;
- # write and read at once
- sub get_sample_config {
- my $self = shift;
- local $/ = undef;
- $self->DataMini->reset( source => <DATA> );
- }
- __DATA__
- [section]
- key=value
- =head3 reset, TODO - FIXME
- # memory
- # удоляет старую структуру и заполняет новую в режиме `memory'
- # записывает старые данные с новым разделителем и читает заного.
- $mini->reset( delimeter => '<__DELIMETER__>' );
- =cut
- sub reset {
- my $self = shift;
- my $args = \%{validate(@_, {
- source => {
- type => SCALAR,
- regex => $Re->{'source'},
- optional => 1,
- },
- })};
- if ( exists $args->{'source'} ) {
- $self->source( $args->{'source'} );
- }
- $self->write;
- $self->read;
- say "reset: reset complete" if $self->debug;
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 exists( keys: section, key )
- Checking for section or key existion
- =head2 ARGS
- =over
- =item section => SCALAR
- TODO
- =item key => SCALAR
- TODO
- =back
- =head2 Example
- $bool = $ini->exists;
- $bool = $ini->exists( key => 'default' );
- $bool = $ini->exists( section => '_', key => 'default' );
- =cut
- sub exists {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => $Re->{'section'},
- optional => 1,
- default => $self->default_section,
- },
- key => {
- type => SCALAR,
- regex => $Re->{'key'},
- optional => 1,
- },
- })};
- # key
- if ( exists $args->{'key'} ) {
- say "exists: key" if $self->debug;
- return exists $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} } ? 1 : 0;
- }
- # section
- say "exists: section" if $self->debug;
- return exists $self->{'Config'}->{ $args->{'section'} } ? 1 : 0;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 defined (keys: section, key)
- Определение установлено ли значение для section или key
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR
- TODO
- =item key => SCALAR
- TODO
- =back
- =head2 Example
- $ini->defined; # defaults: section => `_', key => `default'
- $ini->defined( key => 'default' ); # defaults: section `_' by default
- $ini->defined( section => '_', key => 'default' );
- Status: OK, testme
- =cut
- sub defined {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => $Re->{'section'},
- optional => 1,
- default => $self->default_section,
- },
- key => {
- type => SCALAR,
- regex => $Re->{'key'},
- optional => 1,
- },
- })};
- # key
- if ( exists $args->{'key'} ) {
- say "defined: key" if $self->debug;
- return defined $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} } ? 1 : 0;
- }
- # section
- say "defined: section" if $self->debug;
- return defined $self->{'Config'}->{ $args->{'section'} } ? 1 : 0;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 find( keys: section, key, value )
- TODO
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |ARRAYREF |SCALARREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item value => SCALARREF
- TODO
- =item dumper => BOOLEAN
- TODO
- =back
- =head2 Example
- $ini->find; # defaults: section => '_', key => 'default', value => '.*'
- $ini->find( section => '^pcre$' ); # key => 'default', value => '.*'
- $ini->find( key => '^pcre$' ); # section => '_', value => '.*'
- $ini->find( value => '^pcre$' ); # section => '_', key => 'default'
- $ini->find( section => qr/^pcre$/, dumper => 1 );
- =head2 TODO, FIXME
- $ini->find( section => 'name' );
- $ini->find( section => qr/pcre/ );
- $ini->find( section => qw[/ name1 name2 /] );
- $ini->find( key => 'name' );
- $ini->find( key => qr/pcre/ );
- $ini->find( key => qw[/ name1 name2 /] );
- $ini->find( value => qr/pcre/ );
- =cut
- sub find {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{ 'section' },
- default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{ 'key' },
- optional => 1,
- },
- value => {
- type => SCALARREF,
- regex => $Re->{ 'value' },
- optional => 1,
- },
- dumper => {
- type => BOOLEAN,
- optional => 1,
- default => 0,
- },
- })};
- # *** getting section list
- #
- #
- my %result = (); # store, return value
- # TODO: test-me
- #
- my @sections = ();
- # scalarref, qr/pcre/
- if ( ref $args->{'section'} && ref( $args->{'section'} ) ne 'ARRAY' ) {
- @sections = $self->find( section => $args->{'section'} );
- }
- # arrayref
- elsif ( ref $args->{'section'} && ref( $args->{'section'} ) eq 'ARRAY' ) {
- @sections = @{$args->{'section'}};
- }
- # scalar
- else {
- $sections[0] = $args->{'section'};
- }
- # section
- #
- foreach my $section ( sort @sections ) {
- # DEBUG
- say "match by section `$section'" if $self->debug; # FIXME
- # by key given
- # Example:
- # $ini->find( key => qr/pcre/ );
- #
- if ( exists $args->{'key'} ) {
- #
- # TODO: *** getting keys
- #
- #
- my @keys = ();
- # scalarref, qr/pcre/
- if ( ref $args->{'key'}
- && ref( $args->{'key'} ) ne 'ARRAY' ) {
- @keys = grep /$args->{'key'}/,
- $self->KEYS( section => $section );
- }
- # arrayref
- elsif ( ref $args->{'key'}
- && ref( $args->{'key'} ) eq 'ARRAY' ) {
- @keys = @{$args->{'key'}};
- }
- # scalar
- else {
- $keys[0] = $args->{'key'};
- }
- # matching value..
- # Example:
- # $ini->find( key => qr/pcre/, value => qr/pcre/ );
- #
- if ( exists $args->{ 'value' } ) {
- # matching value per key
- #
- foreach my $key ( sort @keys ) {
- my $value =
- $self->key( section => $section, key => $key );
- # value by default if not given
- $args->{ 'value' } = '.*' if( ! exists $args->{ 'value' } );
- if ( $value =~ /$args->{ 'value' }/ ) {
- $result{ $section }{ $key } = $value;
- }
- }
- }
- else {
- # otherwise store key names (if not zero length)
- # Example:
- # $ini->find( section => 'name', key => qr/pcre/ );
- if ( @keys ) {
- $result{ $section } = [ @keys ];
- }
- }
- }
- # no key given
- # Example:
- # $ini->find( section => 'name', value => qr/pcre/ );
- #
- else { # if ( exists $args->{'key'} )
- # DEBUG
- say "no key given, returning sections" if $self->debug; # FIXME
- # if not defined value to match, then returning a sections names
- if ( ! exists $args->{'value'} ) {
- # Return section list
- # Example:
- # @sections = $ini->find( section => qr/pcre/ );
- #
- return @sections;
- } else {
- # match section-key names by given value
- # Example:
- # $ini->find( section => 'pcre', value => 'pcre' );
- #
- # TODO
- foreach my $section ( @sections ) {
- # DEBUG
- say "match section-key names by given value"
- if $self->debug; # FIXME
- # match value per key
- foreach my $key ( $self->KEYS( section => $section )) {
- # getting value
- my $value = $self->key( section => $section,
- key => $key );
- # checking for value's matches
- if ( $value =~ m/$args->{ 'value' }/ ) {
- $result{ $section }{ $key } = $value;
- }
- }
- }
- }
- }
- }
- # dumping all matches
- if ( $args->{'dumper'} ) {
- say Dumper \%result;
- }
- # returning matches (hash reference)
- return \%result;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 rename ( args: file, section, key, as )
- $ini->rename( as => 'new_key_name' ); # FIXME
- $ini->rename( section => '_', as => 'new_section_name' );
- $ini->rename( section => '_', key => 'key', as => 'new_key_name' );
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR
- TODO
- =item as => SCALAR
- TODO
- =item file => SCALAR
- TODO
- =back
- =head3 TODO
- # rename file-name & section-name
- $ini->rename( file => 'new_filename' );
- $ini->rename( section => 'name', as => 'new_name' );
- # multi-section key renaming
- $ini->rename( section => 'name', key => 'name', as => new );
- $ini->rename( section => qr/pcre/, key => 'name', as => new );
- $ini->rename( section => [qw/ name /], key => 'name', as => new );
- Status: OK
- =cut
- sub rename {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => qr/$Re->{'section'}/,
- optional => 1,
- },
- key => {
- type => SCALAR,
- regex => qr/$Re->{'key'}/,
- optional => 1,
- },
- # name => {
- # optional => 1,
- # },
- as => {
- type => SCALAR,
- regex => qr/$Re->{'key'}/,
- optional => 0, # required
- },
- })};
- # section
- if ( exists $args->{'section'} && ! exists $args->{'key'}) {
- say "renaming `".$args->{'section'}."' as `".$args->{'key'}."'"
- if $self->debug;
- # return undef unless exists section
- return undef
- unless $self->exists( section => $args->{'section'} );
- # getting section
- my $href = $self->{'Config'}->{ $args->{'section'} };
- # setting section as new section name
- $self->{'Config'}->{ $args->{'as'} } = $href; # %{} замена
- # delete old section
- delete $self->{'Config'}->{ $args->{'section'} };
- return $self;
- }
- # key
- elsif ( exists $args->{'section'} && exists $args->{'key'} ) {
- # return undef unless exists key
- return undef
- unless $self->exists( section => $args->{'section'},
- key => $args->{'key'} );
- # getting key value
- my $value = $self->{'Config'}->{ $args->{'section'} }->{
- $args->{'key'} };
- # setting key as new key
- $self->{'Config'}->{ $args->{'section'} }->{ $args->{'as'} }
- = $value;
- # delete old key
- delete $self->{'Config'}->{ $args->{'section'} }->{$args->{'key'}};
- return $self;
- }
- # section & key by default
- elsif ( ! exists $args->{'section'} && ! exists $args->{'key'} ) {
- # defaults
- $args->{'section'} = '_';
- $args->{'key'} = 'default';
- # return undef unless exists key
- return undef
- unless $self->exists( section => $args->{'section'},
- key => $args->{'key'} );
- # getting key value
- my $value = $self->{'Config'}->{ $args->{'section'} }->{
- $args->{'key'} };
- # setting key as new
- $self->{'Config'}->{ $args->{'section'} }->{ $args->{'as'} }
- = $value;
- # delete old key
- delete $self->{'Config'}->{ $args->{'section'} }->{$args->{'key'}};
- return $self;
- }
- # Otherwise undef
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 dump( args: section, dump_indentlevel, dump_sortkeys )
- Метод позволяет выворачивать структуру по запросу
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item dump_indentlevel => SCALAR
- TODO
- =item dump_sortkeys => SCALAR
- TODO
- =back
- =head2 Example(s)
- $ini->dump;
- $ini->dump_indentlevel( 5 );
- $ini->dump_sortkeys( 0 );
- =head2 TODO, FIXME
- $ini->dump( section => 'name', dump_indentlevel => 7, dump_sortkeys => 1 );
- $ini->dump( section => 'name' );
- $ini->dump( section => [qw/ name1 name2 /] );
- $ini->dump( section => qr/pcre/ );
- Status: testme
- =cut
- sub dump {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => qr/$Re->{'section'}/,
- optional => 1,
- },
- })};
- use Data::Dumper;
- $Data::Dumper::Indent = $self->dump_indentlevel;
- $Data::Dumper::Sortkeys = $self->dump_sortkeys;
- # ARRAYREF
- if ( ref($args->{'section'}) eq 'ARRAY' ) {
- foreach my $section ( sort @{ $args->{'section'} } ) {
- $Data::Dumper::Varname = $section;
- print Dumper $self->{'Config'}->{ $section };
- }
- return $self;
- }
- # SCALARREF
- elsif ( exists $args->{'section'}) {
- my @sections = $self->find( section => $args->{'section'} );
- foreach my $section ( sort @sections ) {
- $Data::Dumper::Varname = $section;
- print Dumper $self->{'Config'}->{ $section };
- }
- return $self;
- }
- # SCALAR
- elsif ( 1 ) {
- # TODO
- }
- # Otherwise all-dump
- print Dumper $self->{'Config'};
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 delete ( Undef|keys: section, key )
- Удоляет секции, ключи и файл
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item value => SCALAR
- TODO
- =item file => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- $ini->delete;
- $ini->delete( key => 'default' );
- $ini->delete( section => '_', key => 'default' );
- =head3 TODO, FIXME
- $ini->delete( section => 'name' )
- $ini->delete( section => qr/pcre/ )
- $ini->delete( section => [qw/ name1 name2 /] )
- $ini->delete( key => 'name' );
- $ini->delete( key => qr/pcre/ );
- $ini->delete( key => [qw/ name1 name2 /] );
- $ini->delete( value => qr/pcre/ );
- FIXME: создаються пустые секции при их отсутствии
- TODO:
- delete via pcre
- $ini->delete( file => 1 );
- =cut
- sub delete {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => qr/$Re->{'section'}/,
- # default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR,
- regex => qr/$Re->{'key'}/,
- # default => $self->default_key,
- optional => 1,
- },
- file => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # section
- if ( exists $args->{'section'} && ! exists $args->{'key'} ) {
- # return undef unless exists section
- return undef
- unless exists $self->{'Config'}->{$args->{'section'}};
- say "deletion section `".$args->{'section'}."'"
- if $self->debug;
- delete $self->{'Config'}->{ $args->{'section'} };
- return $self;
- }
- # key
- # FIXME: создаються пустые секции при их отсутствии
- #
- $args->{'key'} = $self->default_key
- unless exists $args->{'key'};
- # return undef unless exists section-key
- return undef
- unless $self->exists( section => $args->{'section'},
- key => $args->{'key'} );
- # DEBUG
- say "deletion key `".$args->{'key'}."'"
- if $self->debug;
- # processing
- delete $self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} }
- if exists $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} };
- #
- # delete zero length section (empty)
- if ( ! $self->KEYS( section => $args->{'section'}, counter => 1 ) ) {
- delete $self->{'Config'}->{ $args->{'section'} };
- }
- return $self;
- }
- ##############################################################################
- # "Get/Set methods"
- # ----------------------------------------------------------------------------
- =pod
- =head2 SECTIONS( Undef | section, counter )
- Получить список имен секций. Для более мощного поиска можно использовать PCRE
- =head2 ARGS
- TODO
- =over
- =item section => SCALARREF
- TODO
- =item counter => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- my @section_names = $ini->SECTIONS;
- my @section_names = $ini->SECTIONS( section => qr/^pcre$/ );
- my $section_counter = $ini->SECTIONS( counter => 1 );
- =head3 See also
- my @secs = $ini->find( section => qr/^pcre$/ );
- Status: all works fine
- =cut
- sub SECTIONS {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALARREF,
- regex => $Re->{'section'},
- optional => 1,
- },
- counter => { # TODO
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # get by PCRE
- #
- if ( exists $args->{'section'}
- and ref($args->{'section'}) eq 'Regexp' ) {
- my @s = grep /$args->{'section'}/, keys %{ $self->{'Config'} };
- return exists $args->{'counter'} ? scalar @s : sort @s;
- }
- # get all section names
- #
- my @s = ( keys %{ $self->{'Config'} } );
- return exists $args->{'counter'} ? scalar @s : sort @s;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head2 KEYS (keys: section, counter)
- Получить ключи заданой секции, если она задана, а если нет то используеться секция по умолчанию
- =head2 ARGS
- TODO
- =over
- =item section => SCALARREF
- TODO
- =item counter => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- # get all key-names of default section `_'
- $ini->KEYS;
- # get all key-names of section `an'
- $ini->KEYS( section => 'an' );
- # get all key-counter of section `an'
- $ini->KEYS( section => 'an', counter => 1 );
- =cut
- sub KEYS {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => qr/$Re->{'section'}/,
- optional => 1,
- default => $self->default_section,
- },
- counter => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- return exists $args->{'counter'}
- # counter
- ? scalar keys %{ $self->{'Config'}->{ $args->{'section'} } }
- # names
- : keys %{ $self->{'Config'}->{ $args->{'section'} } };
- # Otherwise undef
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 VALUES (keys: section, counter)
- Получить значения-ключей заданой секции, если она задана, а если нет то используеться секция по умолчанию
- # all values of section `_'
- $ini->VALUES;
- # all values of `an' section
- $ini->VALUES( section => 'an' );
- # counter of `an' section values
- $ini->VALUES( section => 'an', counter => 1 );
- =head2 ARGS
- TODO
- =over
- =item section => SCALARREF
- TODO
- =item counter => BOOLEAN
- TODO
- =back
- =cut
- sub VALUES {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => qr/$Re->{'section'}/,
- optional => 1,
- default => $self->default_section,
- },
- counter => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- return exists $args->{'counter'}
- # counter
- ? scalar values %{ $self->{'Config'}->{ $args->{'section'} } }
- # names
- : values %{ $self->{'Config'}->{ $args->{'section'} } };
- # Otherwise undef
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 typeof( %$args: section, key )
- Get type of a key or json_key
- =head2 ARGS - Аргументы метода
- TODO
- =over
- =item section => SCALAR
- TODO
- =item key => SCALAR
- TODO
- =back
- =head2 Examples
- $type = $ini->typeof;
- $type = $ini->typeof( section => '_', key => 'default' );
- $type = $ini->typeof( key => 'default' );
- =head2 Method history
- 2014-09-16: Params::Validate - argument's validate tool
- =cut
- sub typeof {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => $Re->{'section'},
- optional => 1,
- default => $self->default_section,
- },
- key => {
- type => SCALAR,
- regex => $Re->{'key'},
- optional => 1,
- default => $self->default_key,
- },
- })};
- # undef type if key not exists
- return undef
- unless $self->exists(section => $args->{'section'},
- key => $args->{'key'} );
- # getting value
- my $value = $self->key(section => $args->{'section'},
- key => $args->{'key'});
- # undef type if value not defined
- return undef unless $value; # fixed bug at 2014-08-12
- # returning real data type name
- $value =~ m/^{.*}|\[.*\]$/ ? return 'json' :
- $value =~ m/^-?\d+$/ ? return 'integer' :
- $value =~ m/^(?:yes|no|true|false)$/i ? return 'boolean' :
- $value =~ m/^-?\d*?\.\d+$/ ? return 'float' :
- return 'string' ;
- # сюда не дойдет
- }
- ##############################################################################
- # "Miscellaneous methods"
- # ----------------------------------------------------------------------------
- =pod
- =head2 key (keys: section, key, value)
- Метод доступа к ключам.
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item value => SCALAR |SCALARREF |ARRAYREF |HASHREF
- TODO
- =item existonly => BOOLEAN
- TODO
- =item json => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- # get
- $value = $ini->key;
- $value = $ini->key(section => '_', key => 'default' );
- # set
- $self = $ini->key( value => 'value' );
- $self = $ini->key( value => 'value', existonly => 1 ); # TODO
- $self = $ini->key( section => '_', key => 'default', value => 'value' );
- # get JSON
- # decode_json => Bool
- HashRef = $ini->key( key => 'json', decode_json => Bool );
- # set HashRef ( json_key by default is a `default' )
- # encode_json => Bool
- # value => HashRef
- $ini = $ini->key( value => { key => Str }, encode_json => 1, key => 'json' );
- # set ArrayRef
- # value => ArrayRef
- $ini->key( value => [], encode_json => 1 );
- =head3 TODO, FIXME
- $ini->key( value => \%, json => 1 );
- $ini->key( value => \[], json => 1 );
- $ini->key( value => \$, json => 1 );
- args: existonly key
- =cut
- sub key {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR,
- regex => $Re->{'section'},
- optional => 1,
- default => $self->default_section,
- },
- key => {
- regex => $Re->{'key'},
- optional => 1,
- default => $self->default_key,
- },
- value => {
- type => SCALAR | SCALARREF | ARRAYREF | HASHREF,
- regex => $Re->{'value'},
- optional => 1,
- },
- existonly => {
- type => BOOLEAN,
- optional => 1,
- },
- json => {
- type => BOOLEAN,
- optional => 1,
- },
- encode_json => {
- type => BOOLEAN,
- optional => 1,
- },
- decode_json => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # get regular key
- #
- if ( ! exists $args->{'value'} ) {
- print "get regular key\n" if $self->debug;
- # root by default, `_'
- #
- if (! exists $args->{'section'}) {
- return $self->{'Config'}->{ '_' }->{ $args->{'key'} };
- }
- # TODO: JSON auto detect & decoding #Test-me
- # get decoded data by JSON
- if ( $self->typeof( section => $args->{'section'},
- key => $args->{'key'} ) eq 'json' ) {
- return decode_json $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} };
- }
- return $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} };
- }
- # set regular key
- #
- elsif ( exists $args->{'value'} ) {
- print "set regular key\n" if $self->debug;
- # root by default, `_'
- #
- $args->{'section'} = $self->default_section
- unless exists $args->{'section'};
- # JSON auto detect & encoding
- if ( ref $args->{'value'} ) {
- # setting up encoded data by JSON
- $self->{'Config'}->{$args->{'section'} }->{ $args->{'key'} }
- = encode_json $args->{'value'};
- return $self;
- }
- # existonly
- #
- # FIXME
- # return undef
- # if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
- # && $args->{'existonly'} == 1 );
- # setting up without JSON
- $self->{'Config'}->{
- $args->{'section'} }->{ $args->{'key'} } = $args->{'value'};
- return $self;
- }
- # Otherwise undef
- #
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 inc (keys: section, key, by, existonly)
- Инкрементирование на 1 или более. Возможное инкрементирование для key.
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item by => SCALAR
- TODO
- =item existonly => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- $ini->inc; # `_' -> `default' : +1
- $ini->inc( existonly => 1 );
- $ini->inc( by => N ); # `_' -> `default' : +N
- $ini->inc( by => N, existonly => 1 )
- $ini->inc( key => 'key' ) # `_' -> `key' : +1
- $ini->inc( key => 'key', by => N ) # `_' -> `key' : +N
- $ini->inc( section => 'section', key => 'key' ) # `section' -> `key' : +1
- $ini->inc( section => '_', key => '_', by => N ) # `_' -> `_' : +N
- Status: OK
- Defaults: section => `_', key => `default'
- =head3 TODO
- $ini->inc( section => 'name' )
- $ini->inc( section => [qw/ name1 name2 /] );
- $ini->inc( section => qr/pcre/ );
- $ini->inc( key => 'name' );
- $ini->inc( key => qr/pcre/ );
- $ini->inc( key => [qw/ name1 name2 /] );
- %$args: existonly
- =cut
- sub inc {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'section'},
- default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'key'},
- default => $self->default_key,
- optional => 1,
- },
- by => {
- type => SCALAR,
- regex => $Re->{'by'},
- optional => 1,
- },
- existonly => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # TODO: test-me
- # getting section list
- #
- my @sections = ();
- # scalarref, qr/pcre/
- if ( ref $args->{'section'} && ref( $args->{'section'} ) ne 'ARRAY' ) {
- @sections = $self->find( section => $args->{'section'} );
- }
- # arrayref
- elsif ( ref $args->{'section'} && ref( $args->{'section'} ) eq 'ARRAY' ) {
- @sections = @{$args->{'section'}};
- }
- # scalar
- else {
- $sections[0] = $args->{'section'};
- }
- # processing sections
- foreach my $section ( @sections ) {
- # TODO: test-me
- # existonly
- next
- if ( ! $self->exists( section => $section,
- key => $args->{'key'} )
- && $args->{'existonly'} == 1 );
- # TODO: test-me
- # if key not a integer setting it to zero
- if ( $self->typeof( section => $section,
- key => $args->{'key'} ) ne 'integer' )
- {
- $self->key( section => $args->{'section'}, key => $args->{'key'},
- value => 0 );
- }
- # TODO: test-me
- # incrementation by N
- if ( exists $args->{'by'} ) {
- $self->{'Config'}->{ $section }->{ $args->{'key'} }
- += $args->{'by'};
- next;
- }
- # TODO: test-me
- # incrementation by 1
- ++$self->{'Config'}->{ "$args->{'section'}" }->{ "$args->{'key'}" };
- next;
- }
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head2 dec (keys: section, key, by, existonly)
- Декрементирование на 1 или более. Возможное декрементирование несколько секций и ключей.
- =head2 ARGS
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item key => SCALAR |SCALARREF |ARRAYREF
- TODO
- =item by => SCALAR
- TODO
- =item existonly => BOOLEAN
- TODO
- =back
- =head3 Example(s)
- $ini->dec; # `_' -> `default' : -1
- $ini->dec( existonly => 1 )
- $ini->dec( by => N ) # `_' -> `default' : -N
- $ini->dec( by => N, existonly => 1 )
- $ini->dec( key => 'key' ) # `_' -> `key' : -1
- $ini->dec( key => 'key', by => N ) # `_' -> `key' : -N
- $ini->dec( section => 'section', key => 'key' ) # `section' -> `key' : -1
- $ini->dec( section => '_', key => '_', by => N ) # `_' -> `_' : -N
- Status: OK
- =head3 TODO, FIXME
- $ini->dec( section => 'name' );
- $ini->dec( section => qr/pcre/ );
- $ini->dec( section => [qw/ name1 name2 /] );
- $ini->dec( key => 'name' );
- $ini->dec( key => qr/pcre/ );
- $ini->dec( key => [qw/ name1 name2 /] );
- # мульти-вычитание
- 2014-09-18: $ini->dec( section => 'sec1', key => [qw/ key1 key2 /] );
- 2014-09-18: $ini->dec( section => [qw/ sec1 sec2 /], key => 'key1' );
- unknown: existonly key
- =cut
- sub dec {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'section'},
- default => $self->default_section,
- optional => 1,
- },
- key => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'key'},
- default => $self->default_key,
- optional => 1,
- },
- by => {
- regex => $Re->{'by'},
- optional => 1,
- },
- existonly => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # defaults
- $args->{'section'} = $self->default_section
- unless exists $args->{'section'};
- $args->{'key'} = $self->default_key unless exists $args->{'key'};
- # FIXME
- # if key not a integer setting it to zero
- # if ( $self->typeof(
- # section => $args->{'section'},
- # key => $args->{'key'} ) ne 'integer' ) {
- # $self->key( section => $args->{'section'}, key => $args->{'key'},
- # value => 0 );
- # }
- # decrementation by N
- if ( exists $args->{'by'} ) {
- # existonly
- #
- # return undef
- # if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
- # && $args->{'existonly'} == 1 );
- $self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} }
- -= $args->{'by'};
- return $self;
- }
- # decrementation by 1
- # existonly
- # FIXME
- # return undef
- # if ( ! $self->exists( $args->{'section'}, $args->{'key'} )
- # && $args->{'existonly'} == 1 );
- --$self->{'Config'}->{ $args->{'section'} }->{ $args->{'key'} };
- return $self;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 href ( keys: section, href, existonly )
- Добавить, заменить или получить "дерево секций и ключей", ввиде хеш-структуры. Может быть два варианта вложения ключей.
- Каждый ключ первого уровня вложения, -это имя секции.
- $ini->href( section => 'baz', href => { foo => bar } ); # setter
- $ini->href( section => 'baz' ); # accessor
- Каждый ключ второго уровня вложения, -это имя ключа секции.
- $SPEC => {
- section_0 => { k1=>v, k2=>v, ... },
- section_1 => { k1=>v, k2=>v, ... },
- ....
- };
- Status: FIXME, testme
- =head2 ARGS
- TODO
- =over
- =item section => SCALAR |SCALARREF |ARRAYREF
- TODO
- Set
- $ini->href( href => \% );
- $ini->href( href => \%, existonly => 1 );
- $ini->href( section => 'sec1',
- href => { sec1 => {}, sec2 => {} } );
- $ini->href( section => [qw/ sec1 /],
- href => { sec1 => {}, sec2 => {} } );
- $ini->href( section => qr/pcre/,
- href => { sec1 => {}, sec2 => {} } );
- Get
- $href = $ini->href
- $href = $ini->href( section => 'name' )
- $href = $ini->href( section => '^pcre$' )
- $href = $ini->href( section = [qw/ section section2 ... /]);
- =item key => SCALAR |SCALARREF |ARRAYREF
- Set
- # Scalar
- $ini->href( section => 's1', key => 'k1',
- href => { s1 => {k1 => 1} } );
- # ArrayRef
- $ini->href( section => 's1', key => [qw/ k1 /],
- href => { s1 => {k1 => 1} } );
- # PCRE, qr//
- $ini->href( section => 's1', key => qr/pcre/,
- href => { s1 => {k1 => 1} } );
- Get
- TODO
- =item href => HASHREF
- TODO
- =item existonly => BOOLEAN
- TODO
- =back
- =head2 Example(s)
- # get
- $href = $ini->href
- $href = $ini->href( section => 'name' )
- $href = $ini->href( section => '^pcre$' )
- $href = $ini->href( section = [qw/ section section2 ... /]);
- # set
- $self = $ini->href( href => \% );
- $self = $ini->href( href => \%, existonly => 1 );
- $self = $ini->href( section => '_', href => \%, existonly => 1 ); # TODO
- SINOPSYS:
- # get all sections
- $ini->href;
- # get sections by ArrayRef list
- $ini->href( section => [qw/ test test_2 /] );
- # get sections by PCRE matching
- $ini->href( section => '^pcre$' );
- # set into root section
- $ini->href( href => {key11 => 'Value11', key2 => 'Value2'} );
- # set other section
- $ini->href( section => 'other', href => { key => 'Value', key2 => 'Value2'} );
- =head2 TODO, FIXME
- $ini->href( section => 'name' );
- $ini->href( section => qr/pcre/ );
- $ini->href( section => [qw/ name1 name2 /] );
- 2014-08-27: existonly key, must to be released!
- =cut
- sub href {
- my $self = shift;
- my $args = \%{validate(@_, {
- section => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'section'},
- optional => 1,
- },
- key => {
- type => SCALAR |SCALARREF |ARRAYREF,
- regex => $Re->{'key'},
- optional => 1,
- },
- href => {
- type => SCALAR |SCALARREF |ARRAYREF,
- optional => 1,
- },
- existonly => {
- type => BOOLEAN,
- optional => 1,
- },
- })};
- # store
- my $href = undef;
- # get
- if ( ! exists $args->{'href'} ) {
- # defaults
- # $args->{'section'} = '_' unless exists $args->{'section'};
- # FIXME: test-me
- # match sections by ArrayRef list TODO
- # example: $ini->href( section => [qw/ one two /] );
- if ( exists $args->{'section'} && ref($args->{'section'}) ) {
- say "match sections by ArrayRef" if $self->debug;
- foreach my $section ( @{$args->{'section'}} ) {
- $href->{$section} = $self->{'Config'}->{$section};
- }
- return $href;
- }
- # FIXME: test-me
- # match sections by PCRE
- # example: $ini->href( section => '^PCRE$' );
- if ( exists $args->{'section'} && ref($args->{'section'}) eq 'Regexp' ) {
- return $self->{'Config'}->{$args->{'section'}};
- }
- # get all sections
- # example: $ini->href();
- say "get all sections href" if $self->debug;
- return $self->{'Config'};
- }
- # set
- elsif ( exists $args->{'href'} ) {
- # default section
- $args->{'section'} = $self->default_section
- unless defined $args->{'section'};
- # args->href
- if ( ref( $args->{'href'} ) eq 'HASH' ) {
- $self->{'Config'}->{$args->{'section'}} = $args->{'href'};
- }
- return $self;
- }
- # Otherwise undef
- return undef;
- }
- # ----------------------------------------------------------------------------
- =pod
- =head1 help Undef
- TODO
- =head2 Example
- $ini->help;
- $ini->help( topic => 'name' );
- $ini->help( topic => [qw/ name /] );
- $ini->help( topic => qr/pcre/ );
- =head2 Topics
- TOC - Table Of Contents
- =cut
- sub help {
- my $self = shift;
- my $args = \%{validate(@_, {
- topic => {
- type => SCALAR |SCALARREF |ARRAYREF,
- optional => 1,
- default => 'TOC',
- },
- })};
- return <<'EOF';
- develop stage, sorry!
- EOF
- }
- # End of Data::Mini module
- #
- =pod
- =head1 OVERVIEW
- # Regexp skills required
- #
- use feature 'say';
- use Data::Mini;
- my $ini = Data::Mini->new(
- path => '/tmp/1/1/1/1/1/1/Config.ini',
- make_directory => 1, # make tree of new directories
- debug => 1,
- default_section => '_',
- default_key => 'default',
- delimeter => '=',
- );
- # set defaults
- #
- $ini->default_section( 'new' ); #=> set default section to `new'
- $ini->default_key( 'new' ); #=> set default key to `new'
- $ini->delimeter( 'new' ); #=> set default delimeter to `new'
- # restore defaults
- #
- $ini->defaults; #=> restore defaults
- # get defaults
- #
- say $ini->default_section; #=> `_'
- say $ini->default_key; #=> `default'
- # load/add new ini source string from package DATA segment
- #
- {
- local $/ = undef;
- $ini->reset( source => <DATA> ); # loading from __DATA__
- say $ini->key; #=> undef
- }
- __DATA__
- default=
- # key setter/accessor ( +JSON encode/decode )
- $ini->key( section => 'sec1', key => 'key1', value => 'val1' );
- say $ini->key( section => 'sec1', key => 'key1' ); #=> val1
- # encode HashRef
- $ini->key( section => 'sec1', key => 'key1', encode_json = 1,
- value => { key0 => 0, key1 => 1, key2 => 2, key3 => 3 } );
- # TODO: encode ArrayRef
- $ini->key( section => 'sec1', key => 'key1', encode_json = 1,
- value => [qw/ 1 2 3 4 5 6 7 8 9 0 /] );
- # decode
- $ini->key( section => 'sec1', key => 'key1', decode_json = 1 );
- # incrementation
- $ini->inc( section => 'sec2', key => 'inc' ); #=> 1
- $ini->inc( section => 'sec2', key => 'incby', by => 7 ); #=> 7
- # decrementation
- $ini->dec( section => 'sec3', key => 'dec' ); #=> -1
- $ini->dec( section => 'sec3', key => 'decby', by => 7 ); #=> -7
- # typeof
- $ini->typeof( section => 'sec1', key => 'key1' ); #=> string
- $ini->typeof( section => 'sec2', key => 'incby' ); #=> integer
- # exists/defined
- $ini->exists( section => 'sec1', key => 'key1' ); #=> true
- $ini->defined( section => 'sec1', key => 'key1' ); #=> true
- pcre: be specific!
- my @sections = $ini->find( section => '.+' ); # Array
- my $href = $ini->find( section => '.+', key => '.+' ); # HashRef
- $ini->find( section => '.+', key => '.+', value => '.+' ); # HashRef
- # get section(s)
- $href = $ini->href # get all
- $href = $ini->href( section => '^pcre$' ) # get by pcre
- $href = $ini->href( section => [qw/ section section2 ... /]); # get by spec
- # set section(s)
- $self = $ini->href( href => \% );
- $self = $ini->href( href => \%, existonly => 1 ); # TODO
- $self = $ini->href( section => '_', href => \% ); # TODO
- $ini->dump_sortkeys( 0 ); # disable sorting
- $ini->dump_indentlevel( 10 ); # set indent to 10
- $ini->dump( 'sec1', 'sec2' ); # dump by section(s) list
- $ini->dump; # dumping all
- exit;
- __DATA__
- [section]
- key=value
- =cut
- =head1 INI Specification
- 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".
- 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
- 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.
- =head2 Keys ( properties )
- 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.
- key=value
- =head2 Sections
- 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.
- [section]
- a=a
- b=b
- =head2 Comments
- Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
- ; comment text
- =head2 Blank lines
- Some rudimentary programs do not allow blank lines. Every line must therefore be a section head, a property, or a comment.
- =head2 Duplicate names
- 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.
- 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.
- =head2 Escape characters
- 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.
- Common escape sequences
- Sequence Meaning
- \\ \ (a single backslash, escaping the escape character)
- \0 Null character
- \a Bell/Alert/Audible
- \b Backspace, Bell character for some applications
- \t Tab character
- \r Carriage return
- \n Line feed
- \; Semicolon
- \# Number sign
- \= Equals sign
- \: Colon
- \x???? Unicode character with hexadecimal code point corresponding to ????
- =head2 Global properties
- Optional "global" properties may also be allowed, that are declared before any section is declared
- =head2 Hierarchy
- 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).
- 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.
- =head2 Name/value delimiter
- Some implementations allow a colon (:) as the name/value delimiter (instead of the equals sign)
- =head2 Quoted values
- 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.
- =head2 Whitespace
- 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.
- =head2 Order of sections and properties
- In most cases the order of properties in a section and the order of sections in a file is irrelevant, but implementations may vary.
- =head1 DEFAULTS
- section: `_'
- key: `default'
- *note: use `$ini-defaults' to restore original defaults
- =head2 LINKS
- wiki: http://ru.wikipedia.org/wiki/.ini
- msdn: http://msdn.microsoft.com/en-us/library/windows/desktop/ms717987%28v=vs.85%29.aspx
- =head1 Data::Mini::JSON
- TODO
- =cut
- # __END__ of Data::Mini
- 1;
- package Data::Mini::JSON;
- use Params::Validate qw/ :all /;
- use feature qw/ say /;
- use Mo qw/ default build is required /;
- use JSON qw/ encode_json decode_json /;
- use Data::Dumper;
- use POSIX qw/ strftime /;
- has data => ( is => 'rw', default => sub { undef } );
- has default_key => ( is => 'rw', default => 'default' );
- =pod
- =head1 DESCRIPTION
- Нужен такой модуль, чтоб брал данные и автоматически, с помощъю JSON управлял ими.
- =head1 OVERVIEW
- use JSON;
- use Data::Mini::JSON;
- my $obj = Data::Mini::JSON->new( default_key => 'default', debug => 1 );
- key( key => value, key2 => value )
- delete( key => 'default' )
- inc( key => 'name', by => 1 );
- dec( key => [qw/ name /], by => 1000 );
- dump( key => 'key' )
- finish
- =cut
- sub BUILD {
- my $self = shift;
- # data exists, decoding
- #
- if ( defined $self->data ) {
- $self->data( decode_json $self->data );
- }
- # No data, making new!
- #
- else {
- $self->data( +{ ctime => strftime("%F %T", localtime),
- maker => __PACKAGE__ } );
- }
- }
- sub DESTROY {
- my $self = shift;
- }
- =pod
- =head1 finish
- TODO
- =cut
- sub finish {
- my $self = shift;
- return encode_json $self->data;
- }
- =pod
- =head1 delete key => Scalar | ScalarRef
- TODO
- =cut
- sub delete {
- my $self = shift;
- my $args = \%{validate(@_, {
- key => {
- type => SCALAR |ARRAYREF,
- default => $self->default_key,
- optional => 1,
- },
- })};
- if ( ! ref($args->{'key'}) ) {
- delete $self->{'data'}->{ $args->{'key'} };
- } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
- foreach ( @{$args->{'key'}} ) {
- delete $self->{'data'}->{ $_ };
- }
- }
- return $self;
- }
- =pod
- =head1 dump key => Scalar |Arrayref, sort => Bool, indent => Bool
- TODO
- =cut
- sub dump {
- my $self = shift;
- my $args = \%{validate(@_, {
- key => {
- type => SCALAR |ARRAYREF,
- optional => 1,
- },
- sort => {
- type => BOOLEAN,
- default => 0,
- optional => 1,
- },
- indent => {
- type => SCALAR,
- regex => qr/^\d+$/,
- default => 1,
- optional => 1,
- },
- })};
- # args->sort
- if ( exists $args->{'sort'} ) {
- $Data::Dumper::Sortkeys = 1,
- }
- # args->indent
- if ( exists $args->{'indent'} ) {
- $Data::Dumper::Indent = $args->{'indent'};
- }
- # dumping all
- #
- unless ( exists $args->{'key'} ) {
- say Dumper $self->{'data'};
- return $self;
- }
- # args->key
- #
- my $heap = {};
- if ( ! ref($args->{'key'}) ) {
- $heap->{ $args->{'key'} } = $self->{'data'}->{ $args->{'key'} };
- } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
- foreach ( @{$args->{'key'}} ) {
- $heap->{ $_ } = $self->{'data'}->{ $_ };
- }
- }
- # dumping heap
- say Dumper $heap;
- return $self;
- }
- =pod
- =head1 inc key => Scalar |ScalarRef, by => Num
- TODO
- $foo->inc( key => 'name' );
- $foo->inc( key => [qw/ name /], by => 3 );
- =cut
- sub inc {
- my $self = shift;
- my $args = \%{validate(@_, {
- key => {
- type => SCALAR |ARRAYREF,
- default => $self->default_key,
- optional => 1,
- },
- by => {
- type => SCALAR,
- regex => qr/^\d+$/,
- default => 1,
- optional => 1,
- },
- })};
- if ( ! ref($args->{'key'}) ) {
- $self->{'data'}->{ $args->{'key'} } += $args->{'by'};
- } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
- foreach ( @{$args->{'key'}} ) {
- $self->{'data'}->{ $_ } += $args->{'by'};
- }
- }
- return $self;
- }
- =pod
- =head1 dec key => Scalar |ScalarRef, by => Num
- TODO
- =cut
- sub dec {
- my $self = shift;
- my $args = \%{validate(@_, {
- key => {
- type => SCALAR |ARRAYREF,
- default => $self->default_key,
- optional => 1,
- },
- by => {
- type => SCALAR,
- regex => qr/^\d+$/,
- default => 1,
- optional => 1,
- },
- })};
- if ( ! ref($args->{'key'}) ) {
- $self->{'data'}->{ $args->{'key'} } -= $args->{'by'};
- } elsif ( ref($args->{'key'}) eq "ARRAY" ) {
- foreach ( @{$args->{'key'}} ) {
- $self->{'data'}->{ $_ } -= $args->{'by'};
- }
- }
- return $self;
- }
- =pod
- =head1 key key => Scalar |ArrayRef, value => HashRef
- TODO
- =cut
- sub key {
- my $self = shift;
- my $args = \%{validate(@_, {
- key => {
- type => SCALAR |ARRAYREF,
- optional => 1,
- },
- value => {
- type => HASHREF,
- optional => 1,
- },
- # inc => {},
- # dec => {},
- # undef => {},
- # length => {},
- })};
- # set
- #
- if ( exists $args->{'value'} && ref($args->{'value'}) eq 'HASH' ) {
- $self->data( { %{$self->data}, %{$args->{'value'}} } );
- }
- # get
- #
- elsif ( exists $args->{'key'} ) {
- return $self->data->{$args->{'key'}};
- }
- return $self;
- }
- =head1 COPYRIGHT
- Copyright (c) 2013-2014. by Michael Shelkovoy ( мишъцх, `i/ust:m0user:sts=2' )
- This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
- =head1 THANKS
- Thanks to ADAMK. _to_ini_string & _from_ini_string modified parts of Config::Tiny
- =cut
- 1;
- __END__
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement