File Coverage

blib/lib/Mock/Data.pm
Criterion Covered Total %
statement 63 88 71.5
branch 29 50 58.0
condition 7 18 38.8
subroutine 12 16 75.0
pod 9 9 100.0
total 120 181 66.3


line stmt bran cond sub pod time code
1             package Mock::Data;
2              
3             # ABSTRACT: Extensible toolkit for generating mock data
4             our $VERSION = '0.03'; # VERSION
5              
6              
7 9     9   888160 use strict;
  9         42  
  9         264  
8 9     9   45 use warnings;
  9         21  
  9         372  
9             BEGIN {
10             # require MRO::Compat if "$]" < '5.009005'; # now requiring v5.10 for dist, anyway
11 9     9   48 require mro;
12 9         12174 mro::set_mro(__PACKAGE__, 'c3');
13             }
14             require Storable;
15             require Module::Runtime;
16              
17              
18             sub new {
19 17     17 1 58128 my $class= shift;
20 17 50       149 my $self= ref $class? $class->clone
21             : bless {
22             generators => {}, # can't initialize, plugins go first
23             generator_state => {},
24             _generator_cache => {},
25             _loaded_plugins => {},
26             }, $class;
27 17 100       64 if (@_) {
28 13 50 66     119 my $args
    100 33        
29             = (@_ == 1 && ref $_[0] eq 'ARRAY')? { plugins => $_[0] }
30             : (@_ == 1 && ref $_[0] eq 'HASH')? $_[0]
31             : { @_ };
32 13 100       48 if (my $plugins= $args->{plugins}) {
33 11 50       45 $self= $self->load_plugin(ref $plugins? @$plugins : ( $plugins ));
34             }
35             $self->add_generators($args->{generators})
36 13 100       53 if $args->{generators};
37             }
38 17         69 return $self;
39             }
40              
41              
42             sub clone {
43 0     0 1 0 my $self= shift;
44             my $new= {
45             %$self,
46             # Shallow clone generators and _loaded_plugins
47 0         0 _loaded_plugins => { %{ $self->{_loaded_plugins} } },
48 0         0 generators => { %{ $self->{generators} } },
49             # deep clone generator_state
50 0         0 generator_state => Storable::dclone($self->{generator_state}),
51             # clear cache
52             _generator_cache => {},
53             };
54             # Allow generators to handle cloned state
55 0         0 for (values %{ $new->{generators} }) {
  0         0  
56 0 0       0 $_= $_->clone if ref->can('clone');
57             }
58 0         0 bless $new, ref $self;
59             }
60              
61              
62             sub generators {
63 209 50   209 1 9398 return $_[0]{generators} if @_ == 1;
64             # Coerce generators
65 0         0 my %new= %{ $_[1] };
  0         0  
66 0         0 $_= Mock::Data::Util::coerce_generator($_) for values %new;
67             # clear cache first
68 0         0 %{$_[0]{_generator_cache}}= ();
  0         0  
69 0         0 return $_[0]{generators}= \%new;
70             }
71              
72             sub generator_state {
73 35 50   35 1 74 $_[0]{generator_state}= $_[1] if @_ > 1;
74 35         148 $_[0]{generator_state};
75             }
76              
77              
78             sub load_plugin {
79 12     12 1 35 my ($self, @names)= @_;
80 12         28 for my $name (@names) {
81 14 50       98 next if $self->{_loaded_plugins}{$name};
82 14         40 my $class= "Mock::Data::Plugin::$name";
83 14 50       137 unless ($class->can('apply_mockdata_plugin')) {
84 0         0 Module::Runtime::require_module($class);
85 0 0       0 $class->can('apply_mockdata_plugin')
86             or Carp::croak("No such method ${class}->apply_mockdata_plugin");
87             }
88 14         51 $self= $class->apply_mockdata_plugin($self);
89 14 50 33     133 ref($self) && ref($self)->isa(__PACKAGE__)
90             or Carp::croak("$class->apply_mockdata_plugin did not return a Mock::Data");
91 14         51 ++$self->{_loaded_plugins}{$name};
92             }
93 12         123 return $self;
94             }
95              
96              
97             sub add_generators {
98 13     13 1 40 my $self= shift;
99 13 100       54 my @args= @_ == 1? %{ $_[0] } : @_;
  3         13  
100 13         54 while (@args) {
101 82         194 my ($name, $gen)= splice @args, 0, 2;
102 82         177 $gen= Mock::Data::Util::coerce_generator($gen);
103 82         168 $self->generators->{$name}= $gen;
104 82         159 delete $self->{_generator_cache}{$name};
105 82 100 33     356 $self->generators->{$1} //= $gen
106             if $name =~ /::([^:]+)$/
107             }
108 13         67 $self;
109             }
110              
111             sub combine_generators {
112 4     4 1 24 my $self= shift;
113 4 50       11 my @args= @_ == 1? %{ $_[0] } : @_;
  0         0  
114 4         10 while (@args) {
115 4         10 my ($name, $gen)= splice @args, 0, 2;
116 4         11 $gen= Mock::Data::Util::coerce_generator($gen);
117 4         7 my $merged= $gen;
118 4 50       8 if (defined (my $cur= $self->generators->{$name})) {
119 0         0 $merged= $cur->combine_generator($gen);
120 0         0 delete $self->{_generator_cache}{$name};
121             }
122 4         9 $self->generators->{$name}= $merged;
123            
124             # If given a namespace-qualified name, also install as the 'leaf' of that name
125 4 50       30 if ($name =~ /::([^:]+)$/) {
126 4         12 ($name, $merged)= ($1, $gen);
127 4 100       9 if (defined (my $cur= $self->generators->{$name})) {
128 1         5 $merged= $cur->combine_generator($gen);
129 1         3 delete $self->{_generator_cache}{$name};
130             }
131 4         19 $self->generators->{$name}= $merged;
132             }
133             }
134 4         10 $self;
135             }
136              
137              
138             sub call {
139 367     367 1 17076 my ($self, $name)= (shift, shift);
140 367 50       894 defined $self->{generators}{$name}
141             or Carp::croak("No such generator '$name'");
142 367 100       1149 return $self->{generators}{$name}->generate($self, @_) if @_;
143             # If no params, call the cached compiled version
144 174   66     722 ($self->{_generator_cache}{$name} ||= $self->{generators}{$name}->compile)
145             ->($self, @_);
146             }
147              
148              
149             sub wrap {
150 0     0 1 0 my ($self, $name)= (shift, shift);
151 0         0 my $gen= $self->{generators}{$name};
152 0 0       0 defined $gen or Carp::croak("No such generator '$name'");
153             my $code= @_? $gen->compile(@_)
154 0 0 0     0 : ($self->{_generator_cache}{$name} ||= $gen->compile);
155 0     0   0 return sub { $code->($self) }
156 0         0 }
157              
158             our $AUTOLOAD;
159             sub AUTOLOAD {
160 357     357   222705 my $self= shift;
161 357 50       879 Carp::croak("No method $AUTOLOAD in package $self") unless ref $self;
162 357         894 my $name= substr($AUTOLOAD, rindex($AUTOLOAD,':')+1);
163 357         817 $self->call($name, @_);
164             # don't install, because generators are defined per-instance not per-package
165             }
166              
167       0     sub DESTROY {} # prevent AUTOLOAD from triggering on ->DESTROY
168              
169              
170             sub import {
171 9     9   81 shift;
172 9         122 Mock::Data::Util->import_into(scalar caller, @_);
173             }
174              
175             require Mock::Data::Util;
176              
177             __END__