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   1089 use strict;
  1         2  
  1         28  
3 1     1   4 use warnings;
  1         2  
  1         26  
4              
5             # ABSTRACT: External FK declarations (EXPERIMENTAL)
6              
7 1     1   4 use Moo;
  1         1  
  1         5  
8 1     1   292 use Types::Standard qw(:all);
  1         2  
  1         9  
9 1     1   40075 use Scalar::Util qw(blessed);
  1         28  
  1         69  
10              
11 1     1   13 use RapidApp::Util::MetaKeys::FK;
  1         2  
  1         25  
12 1     1   711 use JSON qw( from_json -support_by_pp );
  1         6917  
  1         5  
13 1     1   13019 use Path::Class qw( file dir );
  1         29259  
  1         61  
14 1     1   536 use Try::Tiny;
  1         1101  
  1         1055  
15              
16             sub load {
17 7     7 0 13669 my ($self, $data) = @_;
18            
19             # Transparent passthrough when supplied an already
20             # constructed MetaKeys object
21 7 50 33     28 return $data if (
22             blessed($data) &&
23             $data->isa(__PACKAGE__)
24             );
25            
26 7   33     53 my $can_be_file = ! (
27             ref($data) ||
28             length($data) > 1024 ||
29             $data =~ /\n/
30             );
31              
32 7 50       15 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         117 $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 11 my ($self, $string) = @_;
54            
55             my $data = scalar(
56             # Assume JSON as the first format
57 7     7   177 try{ from_json($string, {allow_singlequote => 1, allow_barekey => 1}) } ||
58            
59             # free-form key/value text fallback
60 5     5   4495 try{ $self->parse_key_vals($string) }
61            
62             # Parse from other possible formats
63             # ...
64 7   66     38 );
65              
66              
67 7 50       3501 die "Failed to parse data from string using any support formats" unless ($data);
68              
69 7         14 $data
70             }
71              
72             sub parse_key_vals {
73 5     5 0 11 my ($self, $string) = @_;
74            
75 5         10 my @data = ();
76 5         37 for my $line (split(/\r?\n/,$string)) {
77            
78             # Handle/strip comments:
79 23 100       48 if($line =~ /\#/) {
80 4         15 my ($active,$comment) = split(/\s*\#/,$line,2);
81 4         8 $line = $active;
82             }
83              
84             # strip leading/trailing whitespace
85 23         33 $line =~ s/^\s+//; $line =~ s/\s+$//;
  23         39  
86            
87             # Ignore commas at the end of the line:
88 23         30 $line =~ s/\s*\,\s*$//;
89            
90             # Ignore blank/empty lines:
91 23 100 66     52 next if (!$line || $line eq '');
92            
93             # Split on a variety of delim chars/sequences:
94 15         84 my @parts = split(/\s*[\s\=\:\,\>\/\|]+\s*/,$line);
95            
96 15 50       25 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         29 push @data, \@parts;
102             }
103            
104             return \@data
105 5         15 }
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   1941 my $data = $_[0];
137            
138 7 50       20 return $data if blessed($data);
139            
140 7 50       16 if(my $ref_type = ref($data)) {
141 7 50       16 if($ref_type eq 'ARRAY') {
    0          
142            
143             $data = [ map {
144 7         11 my $itm = $_;
  21         30  
145            
146 21 50 33     75 if(ref($itm) && ! blessed($itm)) {
147 21 100       39 if(ref($itm) eq 'ARRAY') {
    50          
148 20 50       35 die join(' ',
149             "Bad fk definition - must be ArrayRef with 2 elements:",
150             Dumper($_)
151             ) unless (scalar(@$_) == 2);
152            
153 20         37 $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         14 $itm = RapidApp::Util::MetaKeys::FK->new($itm)
160             }
161             }
162            
163             $itm
164 21         1143 } @$data ]
165             }
166             elsif ($ref_type eq 'HASH') {
167 0         0 die "coerce HashRef TODO...";
168            
169             }
170             }
171              
172             $data
173 7         96 }
174              
175             sub _coerce_element {
176 40     40   47 my $el = $_[0];
177            
178 40 100       60 unless (ref $el) {
179 38         45 my $new = {};
180 38         72 my @parts = split(/\./,$el);
181 38 50 33     71 die "Failed to parse/coerce element '$el'" unless (
182             scalar(@parts) == 2 ||
183             scalar(@parts) == 3
184             );
185            
186 38 50       75 $new->{column} = pop(@parts) or die "Failed to parse/coerce element '$el'";
187 38 50       73 $new->{table} = pop(@parts) or die "Failed to parse/coerce element '$el'";
188 38 50       60 $new->{schema} = $parts[0] if (scalar(@parts) > 0);
189            
190 38         327 return $new;
191             }
192            
193 2 50       6 die "Bad element - must be a dot(.) delimited string or a HashRef" unless (ref($el) eq 'HASH');
194            
195 2 50       6 $el->{column} or die "Bad element - 'column' key missing: " . Dumper($el);
196 2 50       4 $el->{table} or die "Bad element - 'table' key missing: " . Dumper($el);
197            
198 2         20 $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