File Coverage

blib/lib/Set/Associate.pm
Criterion Covered Total %
statement 31 31 100.0
branch 4 4 100.0
condition n/a
subroutine 11 11 100.0
pod 4 4 100.0
total 50 50 100.0


line stmt bran cond sub pod time code
1 16     16   284629 use 5.006;
  16         53  
2 16     16   75 use strict;
  16         22  
  16         424  
3 16     16   68 use warnings;
  16         22  
  16         1191  
4              
5             package Set::Associate;
6              
7             # ABSTRACT: Pick items from a data set associatively
8              
9             our $VERSION = '0.004001';
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 16     16   79 use Carp qw( croak );
  16         27  
  16         1086  
14 16     16   8612 use Moose qw( around has );
  16         6037469  
  16         117  
15 16     16   82467 use MooseX::AttributeShortcuts;
  16         4997224  
  16         83  
16              
17             around BUILDARGS => sub {
18             my ( $orig, $self, @args ) = @_;
19             my ($result) = $self->$orig(@args);
20             if ( exists $result->{items} ) {
21             croak('->new( items => ) was deprecated in v0.2.0');
22             }
23             return $result;
24             };
25              
26             has _items_cache => (
27             isa => 'ArrayRef',
28             is => rwp =>,
29             traits => ['Array'],
30             lazy => 1,
31             default => sub { [] },
32             handles => {
33             _items_cache_empty => is_empty =>,
34             _items_cache_shift => shift =>,
35             _items_cache_push => push =>,
36             _items_cache_count => count =>,
37             _items_cache_get => get =>,
38             },
39             );
40              
41             has _association_cache => (
42             isa => 'HashRef',
43             is => rwp =>,
44             traits => ['Hash'],
45             default => sub { {} },
46             handles => {
47             _association_cache_has => exists =>,
48             _association_cache_get => get =>,
49             _association_cache_set => set =>,
50             },
51             );
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65             has on_items_empty => (
66             does => 'Set::Associate::Role::RefillItems',
67             is => rwp =>,
68             required => 1,
69             );
70              
71              
72              
73              
74              
75              
76              
77              
78              
79 14     14 1 376 sub run_on_items_empty { $_[0]->on_items_empty->get_all }
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97              
98              
99             has on_new_key => (
100             does => 'Set::Associate::Role::NewKey',
101             is => rwp =>,
102             lazy => 1,
103             default => sub {
104             require Set::Associate::NewKey;
105             Set::Associate::NewKey->linear_wrap;
106             },
107             );
108              
109             __PACKAGE__->meta->make_immutable;
110 16     16   487571 no Moose;
  16         32  
  16         102  
111              
112 70     70 1 1736 sub run_on_new_key { $_[0]->on_new_key->get_assoc(@_) }
113              
114              
115              
116              
117              
118              
119              
120              
121              
122              
123              
124             sub associate {
125 140     140 1 143 my ( $self, $key ) = @_;
126 140 100       6066 return if $self->_association_cache_has($key);
127 70 100       2390 if ( $self->_items_cache_empty ) {
128 14         54 $self->_items_cache_push( $self->run_on_items_empty );
129             }
130 70         151 $self->_association_cache_set( $key, $self->run_on_new_key($key) );
131 70         81 return 1;
132             }
133              
134              
135              
136              
137              
138              
139              
140              
141              
142             sub get_associated {
143 140     140 1 55111 my ( $self, $key ) = @_;
144 140         292 $self->associate($key);
145 140         5169 return $self->_association_cache_get($key);
146             }
147              
148             1;
149              
150             __END__
151              
152             =pod
153              
154             =encoding UTF-8
155              
156             =head1 NAME
157              
158             Set::Associate - Pick items from a data set associatively
159              
160             =head1 VERSION
161              
162             version 0.004001
163              
164             =head1 DESCRIPTION
165              
166             Essentially, this is a simple toolkit to map an infinite-many items to a
167             corresponding finite-many values, i.e: A nick coloring algorithm.
168              
169             The most simple usage of this code gives out values from C<items>
170             sequentially, and remembers seen values and persists them within the scope of
171             the program, i.e:
172              
173             my $set = Set::Associate->new(
174             on_items_empty => Set::Associate::RefillItems->linear(
175             items => [qw( red blue yellow )],
176             ),
177             );
178             sub color_nick {
179             my $nick = shift;
180             return colorize( $nick, $set->get_associated( $nick );
181             }
182             ...
183             printf '<< %s >> %s', color_nick( $nick ), $message;
184              
185             And this is extensible to use some sort of persisting allocation method such
186             as a hash
187              
188             my $set = Set::Associate->new(
189             on_items_empty => Set::Associate::RefillItems->linear(
190             items => [qw( red blue yellow )],
191             ),
192             on_new_key => Set::Associate::NewKey->hash_sha1,
193             );
194             sub color_nick {
195             my $nick = shift;
196             return colorize( $nick, $set->get_associated( $nick );
197             }
198             ...
199             printf '<< %s >> %s', color_nick( $nick ), $message;
200              
201             Alternatively, you could use 1 of 2 random forms:
202              
203             # Can produce colour runs if you're unlucky
204              
205             my $set = Set::Associate->new(
206             on_items_empty => Set::Associate::RefillItems->linear(
207             items => [qw( red blue yellow )],
208             ),
209             on_new_key => Set::Associate::NewKey->random_pick,
210             );
211              
212             # Will exhaust the colour variation before giving out the same colour
213             # twice
214             my $set = Set::Associate->new(
215             on_items_empty => Set::Associate::RefillItems->shuffle(
216             items => [qw( red blue yellow )],
217             ),
218             );
219              
220             =head1 IMPLEMENTATION DETAILS
221              
222             There are 2 Main phases that occur within this code
223              
224             =over 4
225              
226             =item * pool population
227              
228             =item * pool selection
229              
230             =back
231              
232             =head2 Pool Population
233              
234             The pool of available options ( C<_items_cache> ) is initialized as an empty
235             list, and every time the pool is being detected as empty
236             ( C<_items_cache_empty> ), the C<on_items_empty> method is called
237             ( C<run_on_items_empty> ) and the results are pushed into the pool.
238              
239             =head2 Pool Selection
240              
241             Pool selection can either be cherry-pick based, where the pool doesn't
242             shrink, or can be destructive, so that the pool population phase is
243             triggered to replenish the supply of items only when all values have been
244             exhausted.
245              
246             The L<< default implementation|Set::Associate::NewKey/linear_wrap >>
247             C<shift>'s the first item off the queue, allowing the queue to be exhausted
248             and requiring pool population to occur periodically to regenerate the source
249             list.
250              
251             =head1 CONSTRUCTOR ARGUMENTS
252              
253             =head2 on_items_empty
254              
255             required Set::Associate::RefillItems
256              
257             =head2 on_new_key
258              
259             lazy Set::Associate::NewKey = Set::Associate::NewKey::linear_wrap
260              
261             =head1 METHODS
262              
263             =head2 run_on_items_empty
264              
265             if( not @items ){
266             push @items, $sa->run_on_items_empty();
267             }
268              
269             =head2 run_on_new_key
270              
271             if ( not exists $cache{$key} ){
272             $cache{$key} = $sa->run_on_new_key( $key );
273             }
274              
275             =head2 associate
276              
277             if( $object->associate( $key ) ) {
278             say "already cached";
279             } else {
280             say "new value"
281             }
282              
283             =head2 get_associated
284              
285             Generates an association automatically.
286              
287             my $result = $object->get_associated( $key );
288              
289             =head1 ATTRIBUTES
290              
291             =head2 on_items_empty
292              
293             my $object = $sa->on_items_empty();
294             say "Running empty items mechanism " . $object->name;
295             push @items, $object->run( $sa );
296              
297             =head2 on_new_key
298              
299             my $object = $sa->on_new_key();
300             say "Running new key mechanism " . $object->name;
301             my $value = $object->run( $sa, $key );
302              
303             =head1 AUTHOR
304              
305             Kent Fredric <kentnl@cpan.org>
306              
307             =head1 COPYRIGHT AND LICENSE
308              
309             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
310              
311             This is free software; you can redistribute it and/or modify it under
312             the same terms as the Perl 5 programming language system itself.
313              
314             =cut