Config-Model

 view release on metacpan or  search on metacpan

t/warped_value.t  view on Meta::CPAN

# -*- cperl -*-

use Test::More;
use Test::Differences;
use Test::Memory::Cycle;
use Test::Exception;
use Config::Model;
use Config::Model::ValueComputer;
use Config::Model::Tester::Setup qw/init_test/;

use strict;
use warnings;

my ($model, $trace) = init_test();

$model->create_config_class(
    name    => "RSlave",
    element => [
        recursive_slave => {
            type       => 'hash',
            index_type => 'string',
            cargo      => {
                type              => 'node',
                config_class_name => 'RSlave'
            },
        },
        big_compute => {
            type       => 'hash',
            index_type => 'string',
            cargo      => {
                type       => 'leaf',
                value_type => 'string',
                compute    => {
                    variables => {
                        'm' => '!  macro',
                    },
                    formula => 'macro is $m, my idx: &index, '
                        . 'my element &element, '
                        . 'upper element &element( - ), '
                        . 'up idx &index( - )',
                }
            },
        },
        big_replace => {
            type       => 'leaf',
            value_type => 'string',
            compute    => {
                formula => 'trad idx $replace{&index(-)}',
                replace => {
                    l1 => 'level1',
                    l2 => 'level2'
                } }
        },
        [qw/bar foo foo2/] => {
            type              => 'node',
            config_class_name => 'Slave'
        },
        macro_replace => {
            type       => 'hash',
            index_type => 'string',
            cargo      => {
                type       => 'leaf',
                value_type => 'string',
                compute    => {
                    formula   => 'trad macro is $replace{$m}',
                    variables => { 'm' => '!  macro', },
                    replace   => {
                        A => 'macroA',
                        B => 'macroB',
                        C => 'macroC'
                    },
                }
            },
        }
    ],
);

$model->create_config_class(
    name => "Slave",

    'element' => [
        [qw/X Y Z/] => {
            type       => 'leaf',
            value_type => 'enum',
            choice     => [qw/Av Bv Cv/],
            warp       => {
                follow => { m => '- - macro' },
                rules  => [
                    { when => '$m eq "A"', apply => { default => 'Av' }},
                    { when => '$m eq "B"', apply => { default => 'Bv' }},
                ]
            }
        },
        'recursive_slave' => {
            type       => 'hash',
            index_type => 'string',
            cargo      => {
                type              => 'node',
                config_class_name => 'RSlave',
            },
        },
        W => {
            type       => 'leaf',
            value_type => 'enum',
            level      => 'hidden',
            warp       => {
                follow  => '- - macro',
                'rules' => {
                    A => {
                        default    => 'Av',
                        level      => 'normal',
                        choice     => [qw/Av Bv Cv/],
                    },
                    B => {
                        default    => 'Bv',
                        level      => 'normal',
                        choice     => [qw/Av Bv Cv/] } }
            },
        },
        Comp => {
            type       => 'leaf',
            value_type => 'string',
            compute    => {
                formula   => 'macro is $m',
                variables => { 'm' => '- - macro' },
            },
        },
        warped_by_location => {
            type       => 'leaf',
            value_type => 'uniline',
            default    => 'slaved',
            warp       => {
                rules => [ '&location =~ /recursive/', { 'default' => 'rslaved' } ]
            },
        },
    ] );

$model->create_config_class(
    name    => "Master",
    element => [
        get_element => {
            type       => 'leaf',
            value_type => 'enum',
            choice     => [qw/m_value_element compute_element/]
        },
        where_is_element => {
            type       => 'leaf',
            value_type => 'enum',
            choice     => [qw/get_element/]
        },
        macro => {
            type       => 'leaf',
            value_type => 'enum',
            mandatory  => 1,
            choice     => [qw/A B C D/]
        },
        m_value_out => {
            type       => 'leaf',
            value_type => 'uniline',
            warp       => {
                follow  => '- macro',
                'rules' => [
                    "B" => {
                        level => 'hidden',
                    },
                ] }
        },
        m2_value_out => {
            type       => 'leaf',
            value_type => 'uniline',
            warp       => {
                follow => { m => '- macro', m2 => '- macro2' },
                rules => [ '$m eq "A" or $m2 eq "A"' => { level => 'hidden', }, ] }
        },
        macro2 => {
            type       => 'leaf',
            value_type => 'enum',
            level      => 'hidden',
            warp       => {
                follow  => '- macro',
                'rules' => [
                    "B" => {
                        level  => 'normal',
                        choice => [qw/A B C D/]
                    },
                ] }
        },
        'm_value' => {
            type       => 'leaf',
            value_type => 'enum',
            level      => 'hidden',
            'warp'     => {
                follow  => { m => '- macro' },
                'rules' => [
                    '$m eq "A" or $m eq "D"' => {
                        choice => [qw/Av Bv/],
                        level  => 'normal',
                        help   => { Av => 'Av help' },
                    },
                    '$m eq "B"' => {
                        choice => [qw/Bv Cv/],
                        level  => 'normal',
                        help   => { Bv => 'Bv help' },
                    },
                    '$m eq "C"' => {
                        choice => [qw/Cv/],
                        level  => 'normal',
                        help   => { Cv => 'Cv help' },
                    } ] }
        },
        'm_value_old' => {
            type       => 'leaf',
            value_type => 'enum',
            level      => 'hidden',
            'warp'     => {
                follow  => '- macro',
                'rules' => [
                    [qw/A D/] => {
                        choice => [qw/Av Bv/],
                        level  => 'normal',
                        help   => { Av => 'Av help' },
                    },
                    B => {
                        choice => [qw/Bv Cv/],
                        level  => 'normal',
                        help   => { Bv => 'Bv help' },
                    },
                    C => {
                        choice => [qw/Cv/],
                        level  => 'normal',
                        help   => { Cv => 'Cv help' },
                    } ] }
        },
        'compute' => {
            type       => 'leaf',
            value_type => 'string',
            compute    => {
                formula   => 'macro is $m, my element is &element',
                variables => { 'm' => '!  macro' },
            },
        },

        'var_path' => {
            type       => 'leaf',
            value_type => 'string',
            mandatory  => 1,          # will croak if value cannot be computed
            compute    => {
                formula   => 'get_element is $replace{$s}, indirect value is \'$v\'',
                variables => {
                    's'   => '! $where',
                    where => '! where_is_element',
                    v     => '! $replace{$s}',
                },
                replace => {qw/m_value_element m_value compute_element compute/} }
        },

        'class' => {
            type       => 'hash',
            index_type => 'string',
            cargo      => {
                type       => 'leaf',
                value_type => 'string'
            },
        },
        'warped_out_ref' => {
            type       => 'leaf',
            refer_to   => '! class',
            value_type => 'reference',
            level      => 'hidden',
            warp       => {
                follow => { m => '- macro', m2 => '- macro2' },
                rules  => [ '$m eq "A" or $m2 eq "A"' => { level => 'normal', }, ]
            }
        },

        [qw/bar foo foo2/] => {
            type              => 'node',
            config_class_name => 'Slave'
        },
        'ClientAliveCheck',
        {
            'value_type'       => 'boolean',
            'upstream_default' => '0',
            'type'             => 'leaf',
        },
        'ClientAliveInterval',
        {
            'value_type' => 'integer',
            'level'      => 'hidden',
            'min'        => '1',
            'warp'       => {
                'follow' => { 'c_a_check' => '- ClientAliveCheck' },
                'rules' => [ '$c_a_check == 1', { 'level' => 'normal' } ]
            },
            'type' => 'leaf'
        },
        # a bit dumb, but required to test warp from computed value
        'compute_simple' => {
            type       => 'leaf',
            value_type => 'string',
            compute    => {
                formula   => 'my element is &element',
            },
        },
        warped_from_computed_value => {
            type       => 'leaf',
            value_type => 'string',
            level      => 'hidden',
            default    => 'hello',
            warp       => {
                follow => { c => '- compute_simple' },
                rules  => [ '$c =~ /simple/' => { level => 'normal', }, ]
            }
        }
    ] );

my $inst = $model->instance(
    root_class_name => 'Master',
    instance_name   => 'test1'
);
ok( $inst, "created dummy instance" );

my $root = $inst->config_root;

my $mvo = $root->fetch_element('m_value_out');
isa_ok( $mvo->{warper}, 'Config::Model::Warper', "check warper object" );

my $macro = $root->fetch_element('macro');

my @macro_slaves = ('Warper of Master m_value_out');

eq_or_diff( [ map { $_->name } $macro->get_depend_slave ],
    \@macro_slaves, "check m_value_out warper" );

my $mvo2 = $root->fetch_element('m2_value_out');
isa_ok( $mvo2->{warper}, 'Config::Model::Warper', "check warper object" );

push @macro_slaves, 'Warper of Master m2_value_out', 'Warper of Master macro2';

eq_or_diff(
    [ sort map { $_->name } $macro->get_depend_slave ],
    [ sort @macro_slaves ],
    "check m_value_out and m2_value_out warper"
);

eq_or_diff(
    [ $root->get_element_name() ],
    [
        qw'get_element where_is_element macro m_value_out m2_value_out
            compute var_path class bar foo foo2 ClientAliveCheck
            compute_simple warped_from_computed_value'
    ],
    "Elements of Master"
);

# query the model instead of the instance
eq_or_diff( [
        $model->get_element_name(
            class => 'Slave',
        )
    ],
    [qw'X Y Z recursive_slave Comp warped_by_location'],

t/warped_value.t  view on Meta::CPAN

is(
    $rslave2->fetch_element('big_replace')->fetch(),
    'trad idx level2',
    'reading rslave2->big_replace(br1)'
);

is(
    $rslave1->fetch_element('macro_replace')->fetch_with_id('br1')->fetch,
    'trad macro is macroC',
    'reading rslave1->macro_replace(br1)'
);

is(
    $rslave2->fetch_element('macro_replace')->fetch_with_id('br1')->fetch,
    'trad macro is macroC',
    'reading rslave2->macro_replace(br1)'
);

is(
    $root->fetch_element('compute')->fetch(),
    'macro is C, my element is compute',
    'reading root->compute'
);

my @masters = $root->fetch_element('macro')->get_depend_slave();
my @names = sort map { $_->name } @masters;
print "macro controls:\n\t", join( "\n\t", @names ), "\n"
    if $trace;

is( scalar @masters, 16, 'reading macro slaves' );

eq_or_diff(
    \@names,
    [
        'Master compute',
        'Warper of Master m2_value_out',
        'Warper of Master m_value',
        'Warper of Master m_value_old',
        'Warper of Master m_value_out',
        'Warper of Master macro2',
        'Warper of Master warped_out_ref',
        'Warper of bar W',
        'Warper of bar X',
        'Warper of bar Y',
        'Warper of bar Z',
        'bar Comp',
        'bar recursive_slave:l1 macro_replace:br1',
        'bar recursive_slave:l1 recursive_slave:l2 big_compute:b1',
        'bar recursive_slave:l1 recursive_slave:l2 big_compute:test_1',
        'bar recursive_slave:l1 recursive_slave:l2 macro_replace:br1',
    ],
    "check names of values using 'macro' element"
);

Config::Model::Exception::Any->Trace(1);

throws_ok { $root->fetch_element('var_path')->fetch; }
    qr/'! where_is_element' is undef/,
    'reading var_path while where_is_element variable is undef';

# set one variable of the formula
$root->fetch_element('where_is_element')->store('get_element');

is( $root->fetch_element('var_path')->fetch(check => 'no'), undef, 'reading var_path while where_is_element is defined' );
throws_ok { $root->fetch_element('var_path')->fetch; }
    qr/Undefined mandatory value/, 'reading var_path while get_element variable is undef';

# set the other variable of the formula
$root->fetch_element('get_element')->store('m_value_element');

is(
    $root->fetch_element('var_path')->fetch(),
    'get_element is m_value, indirect value is \'Cv\'',
    "reading var_path through m_value element"
);

# modify the other variable of the formula
$root->fetch_element('get_element')->store('compute_element');

is(
    $root->fetch_element('var_path')->fetch(),
    'get_element is compute, indirect value is \'macro is C, my element is compute\'',
    "reading var_path through compute element"
);

$root->fetch_element('ClientAliveCheck')->store(0);

throws_ok { $root->fetch_element('ClientAliveInterval')->fetch; }
    qr/unavailable element/, 'reading ClientAliveInterval when ClientAliveCheck is 0';

$root->fetch_element('ClientAliveCheck')->store(1);
$root->fetch_element('ClientAliveInterval')->store(10);
is( $root->fetch_element('ClientAliveInterval')->fetch, 10, "check ClientAliveInterval" );

my %loc_h = (
    qw/bar slaved foo2 slaved/,
    'bar recursive_slave:l1 foo2'                    => 'rslaved',
    'bar recursive_slave:l1 recursive_slave:l2 foo2' => 'rslaved'
);

foreach my $k ( sort keys %loc_h ) {
    my $path = "$k warped_by_location";
    is( $root->grab_value($path), $loc_h{$k}, "check &location with $path" );
}

# test warp in layered mode
my $layered_i = $model->instance(
    root_class_name => 'Master',
    instance_name   => 'test_layered'
);
ok( $layered_i, "created layered instance" );

my $l_root = $layered_i->config_root;
$layered_i->layered_start;

my $l_macro = $l_root->fetch_element('macro');

$l_macro->store('D');

my $l_mv = $l_root->fetch_element('m_value');
$layered_i->layered_stop;

$l_mv->store('Av');
is( $l_mv->fetch, 'Av', "test warp in layered mode" );

memory_cycle_ok( $model, "test memory cycle" );

done_testing ;



( run in 0.556 second using v1.01-cache-2.11-cpan-d7f47b0818f )