File Coverage

blib/lib/Mock/Data/Util.pm
Criterion Covered Total %
statement 56 61 91.8
branch 31 38 81.5
condition 5 6 83.3
subroutine 11 14 78.5
pod 7 7 100.0
total 110 126 87.3


line stmt bran cond sub pod time code
1             package Mock::Data::Util;
2 9     9   479961 use strict;
  9         34  
  9         288  
3 9     9   45 use warnings;
  9         17  
  9         6168  
4             require Carp;
5             our @CARP_NOT= qw( Mock::Data Mock::Data::Generator );
6             require Exporter;
7             our @ISA= qw( Exporter );
8             our @EXPORT_OK= qw( uniform_set weighted_set inflate_template coerce_generator mock_data_subclass
9             charset template _parse_context _escape_str
10             );
11              
12             # ABSTRACT: Exportable functions to assist with declaring mock data
13             our $VERSION = '0.02'; # VERSION
14              
15              
16             sub uniform_set {
17 0     0 1 0 return Mock::Data::Set->new(items => [@_]);
18             }
19              
20             sub weighted_set {
21 0     0 1 0 my $i= 0;
22 0         0 return Mock::Data::Set->new_weighted(@_);
23             }
24              
25             sub charset {
26 14     14 1 69 return Mock::Data::Charset->new(@_);
27             }
28              
29              
30             sub template {
31 0     0 1 0 Mock::Data::Template->new(@_);
32             }
33              
34             sub inflate_template {
35 30     30 1 14881 my ($tpl)= @_;
36             # If it does not contain '{', return as-is. Else parse.
37 30 100       164 return $tpl if index($tpl, '{') == -1;
38 17         28 local $@;
39 17         26 my $cmp= eval { Mock::Data::Template->new($tpl) };
  17         60  
40             # If the template "compiled" to a simple scalar, return the scalar. Else return the generator.
41 17 100       95 return !$cmp? $tpl : ref $cmp->{_compiled}? $cmp : $cmp->{_compiled};
    100          
42             }
43              
44              
45             sub coerce_generator {
46 72     72 1 13271 my ($spec)= @_;
47 72 0       473 !ref $spec? Mock::Data::Template->new($spec)
    50          
    100          
    50          
    100          
    100          
48             : ref $spec eq 'ARRAY'? Mock::Data::Set->new(items => [map &_maybe_coerce_set_item, @$spec])
49             : ref $spec eq 'HASH'? weighted_set(%$spec)
50             : ref $spec eq 'CODE'? Mock::Data::GeneratorSub->new($spec)
51             : ref($spec)->can('generate')? $spec
52             : ref $spec eq 'Regexp'? Mock::Data::Regex->new($spec)
53             : Carp::croak("Don't know how to make '$spec' into a generator");
54             }
55             sub _maybe_coerce_set_item {
56 14 100   14   57 !ref($_)? inflate_template($_)
    100          
57             : ref($_) eq 'ARRAY'? Mock::Data::Set->new(items => [map &_maybe_coerce_set_item, @$_])
58             : coerce_generator($_);
59             }
60              
61              
62             sub mock_data_subclass {
63 13     13 1 17179 my $self= shift;
64 13   66     50 my $class= ref $self || $self;
65 13         117 my @to_add= grep !$class->isa($_), @_;
66             # Nothing to do if already part of this class/object
67 13 50       34 return $self unless @to_add;
68             # Determine what the new @ISA will be
69             my @new_isa= defined $Mock::Data::auto_subclasses{$class}
70 13 50       43 ? @{$Mock::Data::auto_subclasses{$class}}
  0         0  
71             : ($class);
72             # Remove redundant classes
73 13         25 for my $next_class (@to_add) {
74 26 100       91 next if grep $_->isa($next_class), @new_isa;
75 24         110 @new_isa= grep !$next_class->isa($_), @new_isa;
76 24         47 push @new_isa, $next_class;
77             }
78             # If only one class remains, then this one class already defined an inheritance for all
79             # the others. Use it directly.
80 13         17 my $new_class;
81 13 100       29 if (@new_isa == 1) {
82 3         5 $new_class= $new_isa[0];
83             } else {
84             # Now find if this combination was already composed, else create it.
85 10         17 $new_class= _name_for_combined_isa(@new_isa);
86 10 100       24 if (!$Mock::Data::auto_subclasses{$new_class}) {
87 9     9   79 no strict 'refs';
  9         19  
  9         5312  
88 8         14 @{"${new_class}::ISA"}= @new_isa;
  8         188  
89 8         40 $Mock::Data::auto_subclasses{$new_class}= \@new_isa;
90             }
91             }
92 13 100       67 return ref $self? bless($self, $new_class) : $new_class;
93             }
94              
95             # When choosing a name for a new @ISA list, the name could be something as simple as ::AUTO$n
96             # with an incrementing number, but that wouldn't be helpful in a stack dump. But, a package
97             # name fully containing the ISA package names could get really long and also be unhelpful.
98             # Compromise by shortening the names by removing Mock::Data prefix and removing '::' and '_'.
99             # If this results in a name collision (seems unlikely), add an incrementing number on the end.
100             sub _name_for_combined_isa {
101 10     10   20 my @parts= grep { $_ ne 'Mock::Data' } @_;
  24         85  
102 10         28 my $isa_key= join "\0", @parts;
103 10         33 for (@parts) {
104 18         68 $_ =~ s/^Mock::Data:://;
105 18         59 $_ =~ s/::|_//g;
106             }
107 10         24 my $class= join '_', 'Mock::Data::_AUTO', @parts;
108 10         13 my $iter= 0;
109 10         13 my $suffix= '';
110             # While iterating, check to see if that package uses the same ISA list as this new request.
111 10   100     38 while (defined $Mock::Data::auto_subclasses{$class . $suffix}
112             && $isa_key ne join("\0",
113 17         44 grep { $_ ne 'Mock::Data' } @{$Mock::Data::auto_subclasses{$class . $suffix}}
  6         14  
114             )
115             ) {
116 4         14 $suffix= '_' . ++$iter;
117             }
118 10         28 $class . $suffix;
119             }
120              
121             my %_escape_common= ( "\n" => '\n', "\t" => '\t', "\0" => '\0' );
122             sub _escape_str {
123 201     201   2145 my $str= shift;
124 201 100       705 $str =~ s/([^\x20-\x7E])/ $_escape_common{$1} || sprintf("\\x{%02X}",ord $1) /ge;
  98         575  
125 201         1260 return $str;
126             }
127             sub _parse_context {
128 2 50   2   29 return '"' . _escape_str(substr($_, defined $_[0]? $_[0] : pos, 10)) .'"';
129             }
130              
131             # included last, because they depend on this module.
132             require Mock::Data::Set;
133             require Mock::Data::Charset;
134             require Mock::Data::Regex;
135             require Mock::Data::Template;
136             require Mock::Data::GeneratorSub;
137              
138             __END__