File Coverage

blib/lib/DBIx/Class/EasyFixture/Definition.pm
Criterion Covered Total %
statement 104 104 100.0
branch 43 44 97.7
condition 3 4 75.0
subroutine 19 19 100.0
pod 0 7 0.0
total 169 178 94.9


line stmt bran cond sub pod time code
1             package DBIx::Class::EasyFixture::Definition;
2             $DBIx::Class::EasyFixture::Definition::VERSION = '0.13';
3             # ABSTRACT: Validate fixture definitions
4              
5 10     10   5644 use Moo;
  10         27  
  10         92  
6 10     10   4205 use MooX::HandlesVia;
  10         23  
  10         140  
7 10     10   1387 use Types::Standard qw(Str HashRef ArrayRef);
  10         34  
  10         68  
8 10     10   7503 use Carp;
  10         25  
  10         636  
9 10     10   708 use Storable 'dclone';
  10         3233  
  10         502  
10 10     10   70 use Scalar::Util 'blessed';
  10         32  
  10         455  
11 10     10   533 use namespace::autoclean;
  10         12859  
  10         112  
12              
13             has 'name' => (
14             is => 'ro',
15             isa => Str,
16             required => 1,
17             );
18              
19             has 'definition' => (
20             is => 'ro',
21             isa => HashRef,
22             required => 1,
23             );
24              
25             has 'fixtures' => (
26             is => 'ro',
27             isa => HashRef,
28             required => 1,
29             handles_via => 'Hash',
30             handles => {
31             fixture_exists => 'exists',
32             },
33             );
34              
35             has 'group' => (
36             is => 'ro',
37             isa => ArrayRef[Str],
38             );
39              
40             around 'BUILDARGS' => sub {
41             my $orig = shift;
42             my $self = shift;
43             my $args = $_[0];
44             if ( 'HASH' ne ref $_[0] ) {
45             $args = {@_};
46             }
47             if ( 'ARRAY' eq ref $args->{definition} ) {
48             $args->{group} = $args->{definition};
49             $args->{definition} = {};
50             }
51             my $definition = $args->{definition};
52             if ( my $using = $definition->{using} ) {
53             foreach my $attribute ( keys %$using ) {
54             my $value = $using->{$attribute};
55             my $ref = ref $value;
56             next if not $ref or blessed($value);
57              
58             my @requires
59             = 'ARRAY' eq $ref ? @$value
60             : 'HASH' eq $ref ? %$value
61             : 'SCALAR' eq $ref ? ( $$value => $attribute )
62             : croak(
63             "Unhandled reference type passed for $args->{name}.$attribute: $value"
64             );
65             unless ( 2 == @requires ) {
66             croak("$args->{name}.$attribute malformed: @requires");
67             }
68             delete $using->{$attribute};
69             $definition->{requires} ||= {};
70             $definition->{requires}{ $requires[0] } = $requires[1];
71             }
72             }
73             $self->$orig( dclone($args) );
74             };
75              
76             sub BUILD {
77 462     462 0 335314 my $self = shift;
78              
79 462 100       1509 if ( $self->group ) {
80 18         80 $self->_validate_group;
81             }
82             else {
83 444         1267 $self->_validate_keys;
84 442         1316 $self->_validate_class_and_data;
85 440         1222 $self->_validate_next;
86 437         9914 $self->_validate_required_objects;
87             }
88             }
89              
90 950     950 0 3936 sub resultset_class { shift->definition->{new} }
91 583     583 0 32617 sub constructor_data { shift->definition->{using} }
92 519     519 0 1409 sub next { shift->definition->{next} }
93             # used only internally to validate the fixture definitions
94 438     438 0 917 sub requires { shift->definition->{requires} }
95             # returns all requires configs which are not marked as `deferred`
96             sub requires_pre {
97 148     148 0 270 my $self = shift;
98 148         282 my $requires = {};
99 148         259 REQUIRES: while( my ($parent, $methods) = each( %{ $self->definition->{requires} } ) ) {
  236         942  
100 88 100       247 next REQUIRES if($methods->{deferred});
101 80         217 $requires->{$parent} = $methods;
102             }
103 148         506 return $requires;
104             }
105             # returns all requires configs which are marked as `deferred`
106             sub requires_defer {
107 78     78 0 160 my $self = shift;
108 78         185 my $deferred = {};
109 78         160 DEFERRED: while( my ($parent, $methods) = each( %{ $self->definition->{requires} } ) ) {
  127         536  
110 49 100       187 next DEFERRED unless($methods->{deferred});
111 4         15 $deferred->{$parent} = $methods;
112             }
113 78         266 return $deferred;
114             }
115              
116             sub _validate_group {
117 18     18   44 my $self = shift;
118 18         61 my $name = $self->name;
119 18         40 my @group = @{ $self->group }; # shallow copy currently ok
  18         72  
120 18 100       72 unless ( @group ) {
121 1         21 croak("Fixture '$name' defines an empty group");
122             }
123 17 100       49 if ( my @unknown = sort grep { ! $self->fixture_exists($_) } @group ) {
  34         1443  
124 1         67 croak("Fixture '$name'.group had unknown fixtures: @unknown");
125             }
126              
127             }
128             sub _validate_keys {
129 444     444   702 my $self = shift;
130 444         987 my $name = $self->name;
131 444         675 my %definition = %{ $self->definition }; # shallow copy currently ok
  444         2036  
132 444 100       1409 unless ( keys %definition ) {
133 1         17 croak("Fixture '$name' had no keys");
134             }
135 443         1418 delete @definition{qw/group new using next requires/};
136 443 100       1717 if ( my @unknown = sort keys %definition ) {
137 1         16 croak("Fixture '$name' had unknown keys: @unknown");
138             }
139             }
140              
141             sub _validate_class_and_data {
142 442     442   743 my $self = shift;
143              
144 442         895 my $class = $self->resultset_class;
145 442         963 my $data = $self->constructor_data;
146              
147 442 100 75     2132 if ( $class xor $data ) {
148 2 100       8 my $found = $class ? 'new' : 'using';
149 2 100       6 my $missing = $class ? 'using' : 'new';
150 2         6 my $name = $self->name;
151 2         30 croak("Fixture '$name' had a '$found' without a '$missing'");
152             }
153             }
154              
155             sub _validate_next {
156 440     440   687 my $self = shift;
157 440         890 my $next = $self->next;
158 440 100       1038 return if not $next;
159              
160 184 50       522 $next = [$next] unless 'ARRAY' eq ref $next;
161 184         411 my $name = $self->name;
162 184         417 foreach my $child (@$next) {
163 208 100       1628 if ( !defined $child ) {
164 1         18 croak("Fixture '$name' had an undefined element in 'next'");
165             }
166 207 100       443 if ( ref $child ) {
167 1         14 croak("Fixture '$name' had non-string elements in 'next'");
168             }
169 206 100       4361 unless ( $self->fixture_exists($child) ) {
170 1         69 croak(
171             "Fixture '$name' lists a non-existent fixture in 'next': '$child'"
172             );
173             }
174             }
175             }
176              
177             sub _validate_required_objects {
178 437     437   720 my $self = shift;
179              
180 437         1003 my $name = join '.' => $self->name, $self->resultset_class, 'requires';
181              
182 437         1047 my $requires = $self->requires;
183 437 100       4793 return if not $requires;
184 220 100       585 unless ( 'HASH' eq ref $requires ) {
185 1         16 croak("$name does not appear to be a hashref");
186             }
187              
188             # XXX don't use a while loop here because we might rewrite requires() and
189             # that would break the iterator
190 219         742 foreach my $parent ( keys %$requires ) {
191 256         494 my $methods = $requires->{$parent};
192 256 100       5624 unless ( $self->fixture_exists($parent) ) {
193 1         66 croak(
194             "Fixture '$name' requires a non-existent fixture '$parent'");
195             }
196 255 100       12712 if ( !ref $methods ) {
197              
198             # they used a single key and it matched
199 177         690 $self->definition->{requires}{$parent}
200             = { our => $methods, their => $methods };
201 177         3511 next;
202             }
203 78 100       274 if ( my @bad_keys = grep { !/^(?:our|their|deferred)$/ } keys %$methods ) {
  190         920  
204 1         16 croak("'$name' had bad keys: @bad_keys");
205             }
206 77 100       234 unless ( exists $methods->{our} ) {
207 1         13 croak("'$name' requires 'our'");
208             }
209 76 100       1981 unless ( exists $methods->{their} ) {
210 1         13 croak("'$name' requires 'their'");
211             }
212             }
213             }
214              
215             1;
216              
217             __END__
218              
219             =pod
220              
221             =encoding UTF-8
222              
223             =head1 NAME
224              
225             DBIx::Class::EasyFixture::Definition - Validate fixture definitions
226              
227             =head1 VERSION
228              
229             version 0.13
230              
231             =head2 DESCRIPTION
232              
233             For internal use only. Maybe I'll document it some day.
234              
235             =head1 AUTHOR
236              
237             Curtis "Ovid" Poe <ovid@cpan.org>
238              
239             =head1 COPYRIGHT AND LICENSE
240              
241             This software is copyright (c) 2014 by Curtis "Ovid" Poe.
242              
243             This is free software; you can redistribute it and/or modify it under
244             the same terms as the Perl 5 programming language system itself.
245              
246             =cut