File Coverage

blib/lib/Meta/Builder/Base.pm
Criterion Covered Total %
statement 141 143 98.6
branch 28 44 63.6
condition 4 12 33.3
subroutine 34 34 100.0
pod 22 25 88.0
total 229 258 88.7


line stmt bran cond sub pod time code
1             package Meta::Builder::Base;
2 2     2   12 use strict;
  2         4  
  2         53  
3 2     2   10 use warnings;
  2         13  
  2         50  
4              
5 2     2   12 use Meta::Builder::Util;
  2         10  
  2         12  
6 2     2   11 use Carp qw/croak carp/;
  2         4  
  2         3916  
7              
8             sub new {
9 5     5 1 2019 my $class = shift;
10 5         11 my ( $package, %metrics ) = @_;
11 5         12 my $meta = $class->meta_meta;
12 5         15 my $self = bless( [ $package ], $class );
13              
14 5         7 for my $metric ( keys %{ $meta->{metrics} }) {
  5         17  
15 11         24 my $idx = $meta->{metrics}->{$metric};
16             $self->[$idx] = $metrics{$metric}
17 11   33     54 || $meta->{generators}->[$idx]->();
18             }
19              
20             inject(
21             $package,
22             ($meta->{accessor} || croak "$class does not have an accessor set."),
23 1     1   722 sub { $self }
24 5   33     37 );
25              
26 5 50       31 $self->init( %metrics ) if $self->can( 'init' );
27              
28 5         15 return $self;
29             }
30              
31             sub meta_meta {
32 58     58 1 83 my $class = shift;
33 58 100       211 return $class->_meta_meta
34             if $class->can( '_meta_meta' );
35              
36 2         8 my $meta = { index => 1 };
37 2     56   13 inject( $class, "_meta_meta", sub { $meta });
  56         135  
38 2         8 return $meta;
39             }
40              
41 1     1 1 5 sub package { shift->[0] }
42              
43             sub set_accessor {
44 2     2 1 6 my $class = shift;
45 2         11 ($class->meta_meta->{accessor}) = @_;
46             }
47              
48             sub add_hash_metric {
49 3     3 1 41 my $class = shift;
50 3         7 my ( $metric, %actions ) = @_;
51 3         26 $class->add_metric(
52             $metric,
53             \&gen_hash,
54             add => \&default_hash_add,
55             get => \&default_hash_get,
56             has => \&default_hash_has,
57             clear => \&default_hash_clear,
58             pull => \&default_hash_pull,
59             merge => \&default_hash_merge,
60             %actions,
61             );
62             }
63              
64             sub add_lists_metric {
65 3     3 1 54 my $class = shift;
66 3         17 my ( $metric, %actions ) = @_;
67 3         24 $class->add_metric(
68             $metric,
69             \&gen_hash,
70             push => \&default_list_push,
71             get => \&default_list_get,
72             has => \&default_list_has,
73             clear => \&default_list_clear,
74             pull => \&default_list_pull,
75             merge => \&default_list_merge,
76             %actions,
77             );
78             }
79              
80             sub add_metric {
81 8     8 1 587 my $class = shift;
82 8         32 my ( $metric, $generator, %actions ) = @_;
83 8         20 my $meta = $class->meta_meta;
84 8         17 my $index = $meta->{index}++;
85              
86             croak "Already tracking metric '$metric'"
87 8 50       20 if $meta->{metrics}->{$metric};
88              
89 8         16 $meta->{metrics}->{$metric} = $index;
90 8         15 $meta->{generators}->[$index] = $generator;
91 8         26 $meta->{indexes}->{$index} = $metric;
92              
93 8     58   49 inject( $class, $metric, sub { shift->[$index] });
  58         1718  
94 8         35 $class->add_action( $metric, %actions );
95             }
96              
97             sub add_action {
98 10     10 1 39 my $class = shift;
99 10         35 my ( $metric, %actions ) = @_;
100             $class->_add_action( $metric, $_, $actions{ $_ })
101 10         43 for keys %actions;
102             }
103              
104             sub _add_action {
105 39     39   72 my $class = shift;
106 39         69 my ( $metric, $action, $code ) = @_;
107 39 50 33     159 croak "You must specify a metric, an action name, and a coderef"
      33        
108             unless $metric && $action && $code;
109              
110 39         65 my $meta = $class->meta_meta;
111 39         80 my $name = $class->action_method_name( $metric, $action );
112              
113             inject( $class, $name, sub {
114 36     36   168 my $self = shift;
115 36         62 my $args = \@_;
116             $_->( $self, $self->$metric, $metric, $action, @$args )
117 36 100       56 for @{ $meta->{before}->{$name} || [] };
  36         162  
118 35         97 my @out = $code->( $self, $self->$metric, $metric, $action, @$args );
119             $_->( $self, $self->$metric, $metric, $action, @$args )
120 34 100       69 for @{ $meta->{after}->{$name} || [] };
  34         127  
121 33 100       153 return @out ? (@out > 1 ? @out : $out[0]) : ();
    50          
122 39         240 });
123             }
124              
125             sub action_method_name {
126 75     75 1 101 my $class = shift;
127 75         126 my ( $metric, $action ) = @_;
128 75         191 return "$metric\_$action";
129             }
130              
131             sub hook_before {
132 1     1 1 710 my $class = shift;
133 1         3 my ( $metric, $action, $code ) = @_;
134 1         3 my $name = $class->action_method_name( $metric, $action );
135 1         2 push @{ $class->meta_meta->{before}->{$name} } => $code;
  1         3  
136             }
137              
138             sub hook_after {
139 1     1 1 7 my $class = shift;
140 1         4 my ( $metric, $action, $code ) = @_;
141 1         2 my $name = $class->action_method_name( $metric, $action );
142 1         2 push @{ $class->meta_meta->{after}->{$name} } => $code;
  1         3  
143             }
144              
145 10     10 1 42 sub gen_hash { {} }
146              
147             sub default_hash_add {
148 9     9 1 39 my $self = shift;
149 9         30 my ( $data, $metric, $action, $item, @value ) = @_;
150 9         22 my $name = $self->action_method_name( $metric, $action );
151 9 50       23 croak "$name() called without anything to add"
152             unless $item;
153              
154 9 50       20 croak "$name('$item') called without a value to add"
155             unless @value;
156              
157             croak "'$item' already added for metric $metric"
158 9 50       23 if $data->{$item};
159              
160 9         30 ($data->{$item}) = @value;
161             }
162              
163             sub default_hash_get {
164 3     3 1 4 my $self = shift;
165 3         9 my ( $data, $metric, $action, $item ) = @_;
166 3         7 my $name = $self->action_method_name( $metric, $action );
167 3 50       10 croak "$name() called without anything to get"
168             unless $item;
169              
170             # Prevent autovivication
171             return exists $data->{$item}
172 3 50       25 ? $data->{$item}
173             : undef;
174             }
175              
176             sub default_hash_has {
177 8     8 1 12 my $self = shift;
178 8         19 my ( $data, $metric, $action, $item ) = @_;
179 8         18 my $name = $self->action_method_name( $metric, $action );
180 8 50       21 croak "$name() called without anything to find"
181             unless $item;
182 8 100       25 return exists $data->{$item} ? 1 : 0;
183             }
184              
185             sub default_hash_clear {
186 3     3 1 4 my $self = shift;
187 3         7 my ( $data, $metric, $action, $item ) = @_;
188 3         16 my $name = $self->action_method_name( $metric, $action );
189 3 50       8 croak "$name() called without anything to clear"
190             unless $item;
191 3         9 delete $data->{$item};
192 3         5 return 1;
193             }
194              
195             sub default_hash_pull {
196 1     1 1 2 my $self = shift;
197 1         3 my ( $data, $metric, $action, $item ) = @_;
198 1         3 my $name = $self->action_method_name( $metric, $action );
199 1 50       5 croak "$name() called without anything to pull"
200             unless $item;
201 1         3 return delete $data->{$item};
202             }
203              
204             sub default_hash_merge {
205 2     2 0 4 my $self = shift;
206 2         4 my ( $data, $metric, $action, $merge ) = @_;
207 2         13 for my $key ( keys %$merge ) {
208             croak "$key is defined for $metric in both meta-objects"
209 3 100       31 if $data->{$key};
210 2         6 $data->{$key} = $merge->{$key};
211             }
212             }
213              
214             sub default_list_push {
215 6     6 1 10 my $self = shift;
216 6         19 my ( $data, $metric, $action, $item, @values ) = @_;
217 6         21 my $name = $self->action_method_name( $metric, $action );
218 6 50       16 croak "$name() called without an item to which data should be pushed"
219             unless $item;
220              
221 6 50       14 croak "$name('$item') called without values to push"
222             unless @values;
223              
224 6         13 push @{$data->{$item}} => @values;
  6         34  
225             }
226              
227             sub default_list_get {
228 2     2 1 5 my $data = default_hash_get(@_);
229 2 50       9 return $data ? @$data : ();
230             }
231              
232             sub default_list_has {
233 6     6 1 14 default_hash_has( @_ );
234             }
235              
236             sub default_list_clear {
237 2     2 1 6 default_hash_clear( @_ );
238             }
239              
240             sub default_list_pull {
241 1     1 1 4 my @out = default_list_get( @_ );
242 1         3 default_list_clear( @_ );
243 1         4 return @out;
244             }
245              
246             sub default_list_merge {
247 2     2 0 3 my $self = shift;
248 2         5 my ( $data, $metric, $action, $merge ) = @_;
249 2         8 for my $key ( keys %$merge ) {
250 1         45 push @{ $data->{$key} } => @{ $merge->{$key} };
  1         4  
  1         6  
251             }
252             }
253              
254             sub merge {
255 2     2 0 59 my $self = shift;
256 2         6 my ( $merge ) = @_;
257 2         5 for my $metric ( keys %{ $self->meta_meta->{ metrics }}) {
  2         5  
258 4         11 my $mergesub = $self->action_method_name( $metric, 'merge' );
259 4 50       16 unless( $self->can( $mergesub )) {
260 0         0 carp "Cannot merge metric '$metric', define a 'merge' action for it.";
261 0         0 next;
262             }
263 4         10 $self->$mergesub( $merge->$metric );
264             }
265             }
266              
267             1;
268              
269             __END__