package Data::MultiValued::Ranges; use Moose; use namespace::autoclean; use MooseX::Params::Validate; use Moose::Util::TypeConstraints; use MooseX::Types::Moose qw(Num Str Undef Any); use Data::MultiValued::Exceptions; use Data::MultiValued::RangeContainer; # ABSTRACT: Handle values with validity ranges =head1 SYNOPSIS use Data::MultiValued::Ranges; my $obj = Data::MultiValued::Ranges->new(); $obj->set({ from => 10, to => 20, value => 'foo', }); say $obj->get({at => 15}); # prints 'foo' say $obj->get({at => 35}); # dies =cut has _storage => ( is => 'rw', isa => class_type('Data::MultiValued::RangeContainer'), init_arg => undef, lazy_build => 1, ); sub _build__storage { Data::MultiValued::RangeContainer->new(); } =method C $obj->set({ from => $min, to => $max, value => $the_value }); Stores the given value for the given range. Throws L if C<< $min > $max >>. The range is defined as C<< Num $x : $min <= $x < $max >>. A C<< from => undef >> means "from -Inf", and a C<< to => undef >> means "to +Inf". Not passing in C or C is equivalent to passing C. If the given range intersects existing ranges, these are spliced to avoid overlaps. In other words: $obj->set({ from => 10, to => 20, value => 'foo', }); $obj->set({ from => 15, to => 25, value => 'bar', }); say $obj->get({at => 12}); # prints 'foo' say $obj->get({at => 15}); # prints 'bar' say $obj->get({at => 25}); # dies No cloning is done: if you pass in a reference, the reference is just stored. =cut sub set { my ($self,%args) = validated_hash( \@_, from => { isa => Num|Undef, optional => 1, }, to => { isa => Num|Undef, optional => 1, }, value => { isa => Any, }, ); $self->_storage->get_or_create(\%args) ->{value} = $args{value}; } =method C my $value = $obj->get({ at => $point }); Retrieves the value for the given point. Throws a L exception if no ranges exist in this object that include the point (remember that a range does not include its C point). A C<< at => undef >> means "at -Inf". Not passing in C is equivalent to passing C. No cloning is done: if a reference was stored, you get it back untouched. =cut sub get { my ($self,%args) = validated_hash( \@_, at => { isa => Num|Undef, optional => 1, }, ); $self->_storage->get(\%args) ->{value}; } =method C $obj->clear({ from => $min, to => $max }); Deletes all values for the given range. Throws L if C<< $min > $max >>. A C<< from => undef >> means "from -Inf", and a C<< to => undef >> means "to +Inf". Not passing in C or C is equivalent to passing C. Thus, C<< $obj->clear() >> clears everything. If the given range intersects existing ranges, these are spliced. In other words: $obj->set({ from => 10, to => 20, value => 'foo', }); $obj->clear({ from => 15, to => 25, }); say $obj->get({at => 12}); # prints 'foo' say $obj->get({at => 15}); # dies =cut sub clear { my ($self,%args) = validated_hash( \@_, from => { isa => Num|Undef, optional => 1, }, to => { isa => Num|Undef, optional => 1, }, ); $self->_storage->clear(\%args); } =head1 Serialisation helpers These are used through L. =head2 C<_rebless_storage> Blesses the storage into L. =cut sub _rebless_storage { my ($self) = @_; bless $self->{_storage},'Data::MultiValued::RangeContainer'; } =head2 C<_as_hash> Returns the internal representation with no blessed hashes, with as few copies as possible. =cut sub _as_hash { my ($self) = @_; my %ret = %{$self->_storage}; return {_storage=>\%ret}; } =head1 SEE ALSO L, L =cut __PACKAGE__->meta->make_immutable(); 1;