File Coverage

blib/lib/RapidApp/Util/MetaKeys.pm
Criterion Covered Total %
statement 79 89 88.7
branch 26 50 52.0
condition 9 23 39.1
subroutine 16 18 88.8
pod 0 5 0.0
total 130 185 70.2


line stmt bran cond sub pod time code
1             package RapidApp::Util::MetaKeys;
2 1     1   1735 use strict;
  1         3  
  1         31  
3 1     1   5 use warnings;
  1         3  
  1         71  
4              
5             # ABSTRACT: External FK declarations (EXPERIMENTAL)
6              
7 1     1   6 use Moo;
  1         2  
  1         6  
8 1     1   319 use Types::Standard qw(:all);
  1         2  
  1         9  
9 1     1   48601 use Scalar::Util qw(blessed);
  1         27  
  1         66  
10              
11 1     1   14 use RapidApp::Util::MetaKeys::FK;
  1         2  
  1         40  
12 1     1   703 use JSON qw( from_json -support_by_pp );
  1         8135  
  1         6  
13 1     1   15168 use Path::Class qw( file dir );
  1         34141  
  1         61  
14 1     1   491 use Try::Tiny;
  1         1284  
  1         1192  
15              
16             sub load {
17 7     7 0 16379 my ($self, $data) = @_;
18            
19             # Transparent passthrough when supplied an already
20             # constructed MetaKeys object
21 7 50 33     30 return $data if (
22             blessed($data) &&
23             $data->isa(__PACKAGE__)
24             );
25            
26 7   33     56 my $can_be_file = ! (
27             ref($data) ||
28             length($data) > 1024 ||
29             $data =~ /\n/
30             );
31              
32 7 50       19 unless (ref $data) {
33 7 50 33     28 $data = $can_be_file && -f file($data)
34             ? $self->data_from_file($data)
35             : $self->data_from_string($data)
36             }
37            
38 7         147 $self->new({ data => $data });
39             }
40              
41              
42             sub data_from_file {
43 0     0 0 0 my $self = shift;
44 0         0 my $File = file(shift)->resolve;
45            
46             # Common-sense size check/limit
47 0 0       0 die "File '$File' too big - probably not the right file" if ($File->stat->size > 65536);
48            
49 0         0 $self->data_from_string( scalar $File->slurp )
50             }
51              
52             sub data_from_string {
53 7     7 0 15 my ($self, $string) = @_;
54            
55             my $data = scalar(
56             # Assume JSON as the first format
57 7     7   216 try{ from_json($string, {allow_singlequote => 1, allow_barekey => 1}) } ||
58            
59             # free-form key/value text fallback
60 5     5   5335 try{ $self->parse_key_vals($string) }
61            
62             # Parse from other possible formats
63             # ...
64 7   66     37 );
65              
66              
67 7 50       4340 die "Failed to parse data from string using any support formats" unless ($data);
68              
69 7         16 $data
70             }
71              
72             sub parse_key_vals {
73 5     5 0 10 my ($self, $string) = @_;
74            
75 5         9 my @data = ();
76 5         43 for my $line (split(/\r?\n/,$string)) {
77            
78             # Handle/strip comments:
79 23 100       48 if($line =~ /\#/) {
80 4         18 my ($active,$comment) = split(/\s*\#/,$line,2);
81 4         10 $line = $active;
82             }
83              
84             # strip leading/trailing whitespace
85 23         39 $line =~ s/^\s+//; $line =~ s/\s+$//;
  23         46  
86            
87             # Ignore commas at the end of the line:
88 23         33 $line =~ s/\s*\,\s*$//;
89            
90             # Ignore blank/empty lines:
91 23 100 66     70 next if (!$line || $line eq '');
92            
93             # Split on a variety of delim chars/sequences:
94 15         96 my @parts = split(/\s*[\s\=\:\,\>\/\|]+\s*/,$line);
95            
96 15 50       30 unless (scalar(@parts) == 2) {
97 0         0 warn "Bad key/val format - expected exactly one key and one value - got: (".join('|',@parts);
98 0         0 return undef;
99             }
100            
101 15         34 push @data, \@parts;
102             }
103            
104             return \@data
105 5         17 }
106              
107              
108             has 'data', is => 'ro', isa => ArrayRef[
109             InstanceOf['RapidApp::Util::MetaKeys::FK']
110             ], required => 1, coerce => \&_coerce_data;
111              
112             has '_table_ndx', is => 'ro', lazy => 1, default => sub {
113             my $self = shift;
114            
115             my $ndx = {};
116             for my $FK (@{ $self->data }) {
117             push @{ $ndx
118             ->{ $FK->schema || '' }
119             ->{ $FK->table }
120             }, $FK
121             }
122            
123             $ndx
124              
125             }, init_arg => undef, isa => HashRef;
126              
127             sub table_fks {
128 0     0 0 0 my ($self, $table, $schema) = @_;
129 0   0     0 $schema //= '';
130            
131 0 0       0 $self->_table_ndx->{$schema}{$table} || undef
132             }
133              
134              
135             sub _coerce_data {
136 7     7   2257 my $data = $_[0];
137            
138 7 50       23 return $data if blessed($data);
139            
140 7 50       17 if(my $ref_type = ref($data)) {
141 7 50       31 if($ref_type eq 'ARRAY') {
    0          
142            
143             $data = [ map {
144 7         13 my $itm = $_;
  21         34  
145            
146 21 50 33     93 if(ref($itm) && ! blessed($itm)) {
147 21 100       47 if(ref($itm) eq 'ARRAY') {
    50          
148 20 50       40 die join(' ',
149             "Bad fk definition - must be ArrayRef with 2 elements:",
150             Dumper($_)
151             ) unless (scalar(@$_) == 2);
152            
153 20         44 $itm = RapidApp::Util::MetaKeys::FK->new({
154             lhs => &_coerce_element($_->[0]),
155             rhs => &_coerce_element($_->[1])
156             })
157             }
158             elsif(ref($itm) eq 'HASH') {
159 1         18 $itm = RapidApp::Util::MetaKeys::FK->new($itm)
160             }
161             }
162            
163             $itm
164 21         1404 } @$data ]
165             }
166             elsif ($ref_type eq 'HASH') {
167 0         0 die "coerce HashRef TODO...";
168            
169             }
170             }
171              
172             $data
173 7         119 }
174              
175             sub _coerce_element {
176 40     40   62 my $el = $_[0];
177            
178 40 100       72 unless (ref $el) {
179 38         52 my $new = {};
180 38         87 my @parts = split(/\./,$el);
181 38 50 33     80 die "Failed to parse/coerce element '$el'" unless (
182             scalar(@parts) == 2 ||
183             scalar(@parts) == 3
184             );
185            
186 38 50       102 $new->{column} = pop(@parts) or die "Failed to parse/coerce element '$el'";
187 38 50       75 $new->{table} = pop(@parts) or die "Failed to parse/coerce element '$el'";
188 38 50       80 $new->{schema} = $parts[0] if (scalar(@parts) > 0);
189            
190 38         423 return $new;
191             }
192            
193 2 50       5 die "Bad element - must be a dot(.) delimited string or a HashRef" unless (ref($el) eq 'HASH');
194            
195 2 50       7 $el->{column} or die "Bad element - 'column' key missing: " . Dumper($el);
196 2 50       5 $el->{table} or die "Bad element - 'table' key missing: " . Dumper($el);
197            
198 2         24 $el
199             }
200              
201              
202             1;
203              
204              
205             __END__
206              
207             =head1 NAME
208              
209             RapidApp::Util::MetaKeys - External FK declarations (EXPERIMENTAL)
210              
211             =head1 SYNOPSIS
212              
213             use RapidApp::Util::MetaKeys;
214              
215              
216             =head1 DESCRIPTION
217              
218             Experimental external definitions of foreign keys
219              
220              
221             =head1 METHODS
222              
223             =head2 new
224              
225             Create a new RapidApp::Util::MetaKeys instance. The following build options are supported:
226              
227             =over 4
228              
229             =item file
230              
231             Path to ...
232              
233             =back
234              
235              
236             =head1 SEE ALSO
237              
238             =over
239              
240             =item *
241              
242             L<DBIx::Class>
243              
244             =item *
245              
246             L<RapidApp>
247              
248             =back
249              
250             =head1 AUTHOR
251              
252             Henry Van Styn <vanstyn@cpan.org>
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             This software is copyright (c) 2015 by IntelliTree Solutions llc.
257              
258             This is free software; you can redistribute it and/or modify it under
259             the same terms as the Perl 5 programming language system itself.
260              
261             =cut