File Coverage

blib/lib/KiokuDB/Set.pm
Criterion Covered Total %
statement 12 57 21.0
branch 0 20 0.0
condition 0 9 0.0
subroutine 4 19 21.0
pod 0 12 0.0
total 16 117 13.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package KiokuDB::Set;
4 12     12   6029 use Moose::Role 'requires', 'has' => { -as => "attr" }; # need a 'has' method
  12         23  
  12         105  
5              
6 12     12   37879 use Moose::Util::TypeConstraints;
  12         20  
  12         102  
7              
8 12     12   21311 use Set::Object;
  12         5746  
  12         479  
9              
10 12     12   54 use namespace::clean -except => "meta";
  12         24  
  12         86  
11              
12             coerce( __PACKAGE__,
13             from ArrayRef => via {
14             require KiokuDB::Set::Transient;
15             KiokuDB::Set::Transient->new( set => Set::Object->new( @$_ ) ),
16             },
17             );
18              
19             requires qw(
20             includes
21             members
22             insert
23             remove
24             );
25              
26             attr _objects => (
27             isa => "Set::Object",
28             is => "ro",
29             init_arg => "set",
30             writer => "_set_objects",
31             handles => [qw(clear size is_weak weaken strengthen is_null)],
32             default => sub { Set::Object->new },
33             );
34              
35             sub clone {
36 0     0 0   my ( $self, @args ) = @_;
37 0           $self->_clone(@args);
38             }
39              
40             sub _clone {
41 0     0     my ( $self, %args ) = @_;
42 0   0       $args{set} ||= $self->_clone_object_set;
43 0           $self->meta->clone_object( $self, %args );
44             }
45              
46             sub _clone_object_set {
47 0     0     my $self = shift;
48 0           my $set = $self->_objects;
49 0           ( ref $set )->new( $set->members );
50             }
51              
52 0     0 0   sub delete { shift->remove(@_) }
53              
54 0     0 0   sub elements { shift->members }
55              
56 0     0 0   sub has { (shift)->includes(@_) }
57 0     0 0   sub contains { (shift)->includes(@_) }
58 0     0 0   sub element { (shift)->member(@_) }
59             sub member {
60 0     0 0   my $self = shift;
61 0           my $item = shift;
62 0 0         return ( $self->includes($item) ?
63             $item : undef );
64             }
65              
66             sub _apply {
67 0     0     my ( $self, $method, @sets ) = @_;
68              
69 0           my @real_sets;
70              
71 0           foreach my $set ( @sets ) {
72 0 0         if ( my $meth = $set->can("_load_all") ) {
73 0           $set->$meth;
74             }
75              
76 0 0         if ( my $inner = $set->can("_objects") ) {
    0          
77 0           push @real_sets, $set->$inner;
78             } elsif ( $set->isa("Set::Object") ) {
79 0           push @real_sets, $set;
80             } else {
81 0           die "Bad set interaction: $self with $set";
82             }
83             }
84              
85 0           $self->_clone( set => $self->_objects->$method( @real_sets ) );
86             }
87              
88             # we weed out empty sets so that they don't trigger loading of deferred sets
89              
90             sub union {
91             if ( my @sets = grep { $_->size } @_ ) {
92             my $self = shift @sets;
93             return $self->_apply( union => @sets );
94             } else {
95             my $self = shift;
96             return $self->_clone
97             }
98             }
99              
100             sub intersection {
101 0     0 0   my ( $self, @sets ) = @_;
102              
103 0 0         if ( grep { $_->size == 0 } $self, @sets ) {
  0            
104 0           return $self->_clone;
105             } else {
106 0           $self->_apply( intersection => @sets );
107             }
108             }
109              
110             sub subset {
111 0     0 0   my ( $self, $other ) = @_;
112              
113 0 0         return if $other->size < $self->size;
114 0 0         return 1 if $self->size == 0;
115              
116 0           $self->_apply( subset => $other )
117             }
118              
119             sub difference {
120 0     0 0   my ( $self, $other ) = @_;
121              
122 0 0         if ( $other->size == 0 ) {
123 0           return $self->_clone;
124             } else {
125 0           $self->_apply( difference => $other );
126             }
127             }
128              
129             sub equal {
130 0     0 0   my ( $self, $other ) = @_;
131              
132 0 0 0       return 1 if $self->size == 0 and $other->size == 0;
133 0 0 0       return if $self->size != 0 and $other->size != 0;
134              
135 0           $self->_apply( equal => $other )
136             }
137              
138             sub not_equal {
139 0     0 0   my ( $self, $other ) = @_;
140 0           not $self->equal($other);
141             }
142              
143             __PACKAGE__
144              
145             __END__
146              
147             =pod
148              
149             =head1 NAME
150              
151             KiokuDB::Set - L<Set::Object> wrapper for KiokuDB with lazy loading.
152              
153             =head1 SYNOPSIS
154              
155             use KiokuDB::Util qw(set);
156              
157             my $set = set(); # KiokuDB::Set::Transient
158              
159             $set->insert($object);
160              
161             warn $set->size;
162              
163             my $id = $dir->store( $set );
164              
165             =head1 DESCRIPTION
166              
167             This role defines the API implemented by L<KiokuDB::Set::Transient>,
168             L<KiokuDB::Set::Deferred>, and L<KiokuDB::Set::Loaded>.
169              
170             These three classes are modeled after L<Set::Object>, but have implementation
171             details specific to L<KiokuDB>.
172              
173             =head2 Transient Sets
174              
175             Transient sets are in memory, they are sets that have been constructed by the
176             user for subsequent insertion into storage.
177              
178             When you create a new set, this is what you should use.
179              
180             L<KiokuDB::Util> provides convenience functions (L<KiokuDB::Util/set> and
181             L<KiokuDB::Util/weak_set>) to construct transient sets concisely.
182              
183             =head2 Deferred Sets
184              
185             When a set is loaded from the backend, it is deferred by default. This means
186             that the objects inside the set are not yet loaded, and will be fetched only as
187             needed.
188              
189             When set members are needed, the set is upgraded in place into a
190             L<KiokuDB::Set::Loaded> object.
191              
192             =head2 Loaded Sets
193              
194             This is the result of vivifying the members of a deferred set, and is similar
195             to transient sets in implemenation.
196              
197             =cut
198