package CHI::Cascade::Value;
use strict;
use warnings;
my %states = (
# value = undef -> no in cache
CASCADE_NO_CACHE => 1 << 0,
# value = undef | old_value -> other process is computing this target or its any dependencies
CASCADE_COMPUTING => 1 << 1,
# value = undef | old_value -> recomputing is deferred
CASCADE_DEFERRED => 1 << 2,
# value = old_value | actual_value -> the value from cache (not computed now)
CASCADE_FROM_CACHE => 1 << 3,
# value = actual_value -> this value is actual
CASCADE_ACTUAL_VALUE => 1 << 4,
# value = actual_value & recomuted now -> this value is recomputed right now
CASCADE_RECOMPUTED => 1 << 5,
# value = undef | old_value | value passed by exception -> code of target or code of any dependencies has raised an exception
CASCADE_CODE_EXCEPTION => 1 << 6,
# value = old_value | actual_value - value may be actual or not but actual term isn valid (only if 'run' is run with 'actual_term' option)
CASCADE_ACTUAL_TERM => 1 << 7,
# Some dependencies are affected for recomputing, but no recomputing now - only TTL period and value from cache
CASCADE_TTL_INVOLVED => 1 << 8
);
for ( keys %states ) {
no strict 'refs';
no warnings 'redefine';
my $bit = $states{$_};
*{ $_ } = sub () { $bit }
}
use parent 'Exporter';
{
no strict 'refs';
our %EXPORT_TAGS = (
state => [ map { "$_" } grep { /^CASCADE_/ && *{$_}{CODE} } keys %{ __PACKAGE__ . "::" } ]
);
Exporter::export_ok_tags( keys %EXPORT_TAGS );
}
sub new {
my ($class, %opts) = @_;
my $self = bless { %opts }, ref($class) || $class;
$self->{state} ||= 0;
$self;
}
sub is_value {
shift->{is_value};
}
sub state {
my $self = shift;
if (@_) {
$self->{state} |= $_[0];
return $self;
}
$self->{state};
}
sub state_as_str {
my $state = $_[1];
return '' if ! $state;
my @names;
for ( keys %states ) {
push @names, $_
if ( $state & $states{$_} );
}
join( " | ", sort @names );
}
sub value {
my $self = shift;
if (@_) {
$self->{is_value} = 1;
$self->{value} = $_[0];
return $self;
}
$self->{value};
}
sub thrown_from_code {
my $self = shift;
if (@_) {
$self->{thrown_from_code} = $_[0];
return $self;
}
$self->{thrown_from_code};
}
1;
__END__
=head1 NAME
CHI::Cascade::Value - a class for valid values
=head1 SYNOPSIS
You can use it class for a returning of values by exceptions. For example:
die CHI::Cascade::Value->new
This throws an exception with nothing value. If you do it from your recompute
code your L<CHI::Cascade/run> method will return an old value from cache or if
it's not in cache it will return an C<undef> value.
Or
die CHI::Cascade::Value->new->value( $any_value );
die CHI::Cascade::Value->new->value( undef );
This throws an exception with valid value. Please note that C<undef> is valid
value too! But bacause the L<CHI::Cascade/run> method returns only a value (not
instance of L<CHI::Cascade::Value> object) there is not recommended to use
C<undef> values (C<run> method returns C<undef> when it cannot get a value right
now).
Please use it class only in special cases - when you need to break recopmuting,
want to return an specific value only for once execution of L<CHI::Cascade/run>
method and don't want to save value in cache.
=head1 CONSTRUCTOR
$value = CHI::Cascade::Value->new;
It will create instance $value with nothing value
=head1 METHODS
=over
=item value
Examples:
$value->value
$value->value( $new_value )
You can use it to get/set a value of $value. An C<undef> value is valid too!
First version returns a value, second sets a value and returns C<$value>.
=item is_value
$value->is_value
returns C<true> if value was set by L</value> method or C<false> else.
=item state
use CHI::Cascade::Value ':state';
$state_bits = $value->state;
$value = $value->state( CASCADE_* );
A getting or setting of state bits of value object.
=item state_as_str
my $value = $cascade->run( 'my_target', state => \$state );
my $str = CHI::Cascade::Value->state_as_str( $state );
Returns a string presentation of state bits (see below L</"STATE BITS">).
Strings of bits are ordered by alphabetical before concatenation. Here some
examples:
# It means you get actual value and this was recomputed right now
CASCADE_ACTUAL_VALUE | CASCADE_RECOMPUTED
# It happens when returned value of CHI::Cascade::run is undef and here is reason why:
# value right now is being computed in other process and no old value in cache
CASCADE_COMPUTING | CASCADE_NO_CACHE
This method is useful for debugging or logging processes.
=back
=head1 STATE BITS
Since version 0.26 the CHI::Cascade introduces the concept of state bits. An
every value object (even which has not valid value) has a history is described
by these state bits. To use this bit mask we can know how this value was gotten.
These bits are returned by L<CHI::Cascade/run> in L<CHI::Cascade/state>
variable.
=over
=item CASCADE_NO_CACHE
A value of target was missed in cache. Only as information as value was fetched
=item CASCADE_COMPUTING
A value of target to be computing in other process. So L<CHI::Cascade/run> will
return to you a B<undef> (if it misses in cache) or B<old value from cache>.
=item CASCADE_DEFERRED
A value of target should be recomputed but was not recomputed because
L<CHI::Cascade/run> was executed with L<CHI::Cascade/defer> option as B<true>.
This useful when you want to control an excution of codes of targets yourself.
=item CASCADE_FROM_CACHE
A value of target is B<old> or B<actual> value and was fetched from cache.
=item CASCADE_ACTUAL_VALUE
A value of target is B<actual> value (should not be recomputed)
=item CASCADE_RECOMPUTED
A value of target was recomputed by your request right now (was called
L<CHI::Cascade/code> in your process)
=item CASCADE_CODE_EXCEPTION
This state bit occurs only if exception was thrown from code or any dependencies
and it has the type L<CHI::Cascade::Value> (the expression C<<
$@->isa('CHI::Cascade::Value') >> is C<true>). If there to be thrown an other
type expression it will be rethrown from L<CHI::Cascade/run>. A value of
target returned by L<CHI::Cascade/run> can be:
=over
=item undef
A cache doesn't have any value of target
=item old value from cache
If L<CHI::Cascade/code> if a code or any code of dependencies threw exception as
L<CHI::Cascade::Value> object without value and a cache has any value for target
(i.e. C<< die CHI::Cascade::Value->new >>)
=item value was thrown by exception
If value was thrown by C<< die CHI::Cascade::Value->new->value(123) >> and even same: C<< die
CHI::Cascade::Value->new->value(undef) >>) for example.
=back
=item CASCADE_ACTUAL_TERM
The method L<CHI::Cascade/run> was run with
L<actual_term|CHI::Cascade/actual_term> option and C<actual term> is actual for
this value (a value can be old - the CASCADE_ACTUAL_VALUE bit will not be set).
=item CASCADE_TTL_INVOLVED
A returned value is not actual value and already is old because some dependence
is newly than value which depends from this. But you describes an option C<ttl>
in L<CHI::Cascade/rule>. If you had passed the option C<ttl> like C<\$ttl> to
L<CHI::Cascade/run> method there in $ttl will be fractal number of "time to
live" - how many seconds are left before the computation (of course, if you will
call C<run> again for that target). This feature is useful for global reset
mechanism (one I<reset> target as global dependence and other rules from its
have a C<ttl> parameter in I<rules>).
=back
=head1 AUTHOR
This module has been written by Perlover <perlover@perlover.com>
=head1 LICENSE
This module is free software and is published under the same terms as Perl
itself.
=head1 SEE ALSO
L<CHI::Cascade>
=cut