File Coverage

lib/Frost/Util.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Frost::Util;
2              
3 53     53   3516624 use strict;
  53         87  
  53         1302  
4 53     53   179 use warnings;
  53         69  
  53         3331  
5              
6             our $VERSION = '0.5'; # see ROOT_VERSION below !!!
7             $VERSION = eval $VERSION;
8             our @ISA;
9             our @EXPORT;
10              
11             our $UUID_OBJ;
12             our $UUID_CLEAR;
13              
14             # INHERITANCE
15             #
16 53     53   190 use Exporter;
  53         63  
  53         2663  
17             @ISA = qw( Exporter );
18              
19             # LIBS
20             #
21 53     53   36026 use Log::Log4perl 1.24 qw(:levels get_logger);
  53         1922664  
  53         285  
22 53     53   61830 use BerkeleyDB 0.43 ();
  0            
  0            
23             # use DB_File 1.820;
24             use Fcntl 1.05 qw( :DEFAULT :flock );
25             use File::Path 2.04 ();
26             use Time::HiRes 1.9719 qw(usleep);
27             use Data::UUID 1.202;
28             use Data::Dumper 1.125 ();
29             use Carp 1.04 qw(confess);
30             use Class::MOP 1.08 ();
31             use Moose 1.14 ();
32              
33             use Moose::Util qw(find_meta);
34              
35             # DIE HANDLER
36             #
37             BEGIN
38             {
39             # ...:~# export Frost_NO_DIE_ON_WARNINGS=1
40              
41             $SIG{__DIE__} = \&Carp::confess;
42              
43             if ( $ENV{Frost_NO_DIE_ON_WARNINGS} ) { $SIG{__WARN__} = \&Carp::cluck; }
44             else { $SIG{__WARN__} = \&Carp::confess; }
45             }
46              
47             # CONSTANTS
48             #
49             @EXPORT =
50             (
51             # qw ( $VERSION ),
52             qw ( IS_DEBUG DEBUG INFO WARN ERROR FATAL ),
53             qw ( Dumper Dump ),
54             qw ( true false ),
55             qw ( TRUE FALSE ),
56             qw ( UUID UUID_NEW_TAG UUID_BAD_TAG TIMESTAMP_ZERO ),
57             qw ( NULL_KEYS_ALLOWED ),
58             qw ( DEFAULT_CACHESIZE ),
59             qw ( SORT_INT SORT_FLOAT SORT_DATE SORT_TEXT ),
60             qw ( STATUS_NOT_INITIALIZED STATUS_MISSING STATUS_LOADED STATUS_EXISTS STATUS_SAVING ),
61             qw ( ROOT_VERSION ROOT_TAG OBJECT_TAG ATTR_TAG VALUE_TAG ),
62             qw ( INDEX_TAG ENTRY_TAG ),
63             qw ( ID_ATTR NAME_ATTR TYPE_ATTR REF_ATTR ),
64             qw ( KEY_ATTR ATTR_ATTR NUM_ATTR VALUE_ATTR ),
65             qw ( VALUE_TYPE ARRAY_TYPE HASH_TYPE CLASS_TYPE ),
66             # qw ( make_cache_key split_cache_key ),
67             qw ( find_attribute_manuel find_type_constraint_manuel check_type_constraint_manuel ),
68             qw ( make_path make_file_path ),
69             qw ( check_or_create_dir filepath_from_class filename_from_class_and_id class_and_id_from_filename ),
70             qw ( touch ),
71             qw ( lock_fh unlock_fh ),
72             qw ( check_type_manuel ),
73             );
74              
75             BEGIN
76             {
77             $Data::Dumper::Bless = "bless";
78             $Data::Dumper::Deepcopy = 0;
79             $Data::Dumper::Deparse = 0; # 1 = show source perl
80             $Data::Dumper::Freezer = "";
81             $Data::Dumper::Indent = 1; # default = 2 # keep it small
82             $Data::Dumper::Maxdepth = 0;
83             $Data::Dumper::Pad = "";
84             $Data::Dumper::Pair = ' => ';
85             $Data::Dumper::Purity = 0;
86             $Data::Dumper::Quotekeys = 1;
87             $Data::Dumper::Sortkeys = 1; # default = 0 # need reproduceable order for testing...
88             $Data::Dumper::Terse = 0;
89             $Data::Dumper::Toaster = "";
90             $Data::Dumper::Useperl = 0;
91             $Data::Dumper::Useqq = 1; # default = 0 # nice output of Storable formatting
92             $Data::Dumper::Varname = "VAR";
93              
94             binmode STDERR, ":utf8"; # no wide character error in Log4Perl !!!!!
95              
96             Log::Log4perl->easy_init
97             (
98             {
99             # level => ( $ENV{Frost_DEBUG} ? $DEBUG : $INFO ),
100             level => $DEBUG,
101             file => 'STDERR',
102             # layout => '[%d{dd/MMM/yyyy:HH:mm:ss}] (%P) [%p] %M-%L: %m%n',
103             layout => '[%d{ISO8601}] (%P) [%p] %M-%L: %m%n',
104             },
105             );
106             }
107              
108             # SUBS
109             #
110             sub IS_DEBUG () { $ENV{Frost_DEBUG}; }
111              
112             sub DEBUG { $Log::Log4perl::caller_depth++; get_logger(__PACKAGE__)->debug ( @_ ); $Log::Log4perl::caller_depth--; }
113             sub INFO { $Log::Log4perl::caller_depth++; get_logger(__PACKAGE__)->info ( @_ ); $Log::Log4perl::caller_depth--; }
114             sub WARN { $Log::Log4perl::caller_depth++; get_logger(__PACKAGE__)->warn ( @_ ); $Log::Log4perl::caller_depth--; }
115             sub ERROR { $Log::Log4perl::caller_depth++; get_logger(__PACKAGE__)->error ( @_ ); $Log::Log4perl::caller_depth--; }
116             sub FATAL { $Log::Log4perl::caller_depth++; get_logger(__PACKAGE__)->fatal ( @_ ); $Log::Log4perl::caller_depth--; }
117              
118             sub Dumper { "\n" . Data::Dumper->Dump ( [ @_ ] ); }
119             sub Dump ($;$) { "\n" . Data::Dumper->Dump ( $_[0], $_[1] ); }
120              
121             sub true () { 1; }
122             sub false () { 0; }
123              
124             sub TRUE () { 'true'; }
125             sub FALSE () { 'false'; }
126              
127             sub UUID ()
128             {
129             unless ( $UUID_CLEAR )
130             {
131             $UUID_OBJ ||= new Data::UUID or die "Cannot create Data::UUID object\n";
132              
133             $UUID_OBJ->create_str();
134             }
135             else
136             {
137             $UUID_OBJ ||= 0;
138              
139             'A-A-A-A-' . ++$UUID_OBJ;
140             }
141             }
142              
143             sub UUID_NEW_TAG () { 'UNEW-UNEW-UNEW-UNEW-UNEW' }
144             sub UUID_BAD_TAG () { 'UBAD-UBAD-UBAD-UBAD-UBAD' }
145              
146             # see DB_File-1.820/t/db-btree.t
147             #
148             # use constant NULL_KEYS_ALLOWED => ( $DB_File::db_ver < 2.004010 || $DB_File::db_ver >= 3.1 );
149             # #use constant NULL_KEYS_ALLOWED => false; # TEST!!!
150             use constant NULL_KEYS_ALLOWED => ( $BerkeleyDB::db_version < 2.004010 || $BerkeleyDB::db_version >= 3.1 );
151              
152             # Be careful in the china store:
153             #
154             # 10 classes, each with 5 attributes and 2 indices = ( 10 * ( 5 + 2 ) DBs ) * 2 MB Cache = 140 MB !!!
155             #
156             sub DEFAULT_CACHESIZE () { 2 * 1024 * 1024 } # 2 MB
157              
158             sub TIMESTAMP_ZERO () { '0000-00-00 00:00:00' }
159              
160             sub SORT_INT () { 'int' }
161             sub SORT_FLOAT () { 'float' }
162             sub SORT_DATE () { 'date' }
163             sub SORT_TEXT () { 'text' }
164              
165             sub STATUS_NOT_INITIALIZED () { 'not_initialized' }
166             sub STATUS_MISSING () { 'missing' }
167             sub STATUS_LOADED () { 'loaded' }
168             sub STATUS_EXISTS () { 'exists' }
169             sub STATUS_SAVING () { 'saving' }
170              
171             sub ROOT_VERSION () { $VERSION }
172             sub ROOT_TAG () { 'frost' }
173             sub OBJECT_TAG () { 'object' }
174             sub ATTR_TAG () { 'attr' }
175             sub VALUE_TAG () { 'value' }
176              
177             sub INDEX_TAG () { 'index' }
178             sub ENTRY_TAG () { 'entry' }
179              
180             sub ID_ATTR () { 'id' }
181             sub NAME_ATTR () { 'name' }
182             sub TYPE_ATTR () { 'type' }
183             sub REF_ATTR () { 'ref' }
184              
185             sub KEY_ATTR () { 'key' }
186             sub ATTR_ATTR () { 'attr' }
187             sub NUM_ATTR () { 'numeric' }
188             sub VALUE_ATTR () { 'value' }
189              
190             sub VALUE_TYPE () { '__VALUE__' }
191             sub ARRAY_TYPE () { '__ARRAY__' }
192             sub HASH_TYPE () { '__HASH__' }
193             sub CLASS_TYPE () { '__CLASS__' }
194              
195             # Inlined! 4 times faster!
196             #
197             # sub make_cache_key ( $$ ) { $_[0] . '|' . $_[1] }
198             # sub split_cache_key ( $ ) { split /\|/, $_[0] }
199              
200             sub find_attribute_manuel ( $$ )
201             {
202             my ( $class_or_obj, $name ) = @_;
203              
204             defined $class_or_obj or die "Param class_or_obj missing";
205             defined $name or die "Param name missing";
206              
207             my $meta = find_meta ( $class_or_obj );
208              
209             return undef unless defined $meta;
210              
211             my $attr = $meta->get_attribute ( $name ); # fast...
212              
213             $attr = $meta->find_attribute_by_name ( $name ) unless defined $attr; # slow...
214              
215             return $attr;
216             }
217              
218             sub find_type_constraint_manuel ( $$ )
219             {
220             my ( $class_or_obj, $name ) = @_;
221              
222             my $attr = find_attribute_manuel ( $class_or_obj, $name );
223              
224             return undef unless defined $attr;
225              
226             return $attr->type_constraint;
227             }
228              
229             sub check_type_constraint_manuel ( $$$ )
230             {
231             my ( $class_or_obj, $name, $value ) = @_;
232              
233             # 6000 28.2ms 5µs
234             #
235             # my $class = blessed ( $class_or_obj ) || $class_or_obj;
236              
237             # 6000 4.12ms 686ns ref is 6,67 times faster...
238             #
239             my $class = ref ( $class_or_obj ) || $class_or_obj;
240             #
241             # use of blessed checked everywhere!
242              
243             my $type_constraint = find_type_constraint_manuel ( $class_or_obj, $name );
244              
245             ( defined $type_constraint )
246             || die "Could not find a type constraint for the attribute by the name of '$name' in '$class'";
247              
248             $type_constraint->check ( $value )
249             || die "$class\::Attribute ("
250             . $name
251             . ") does not pass the type constraint because: "
252             . $type_constraint->get_message ( $value );
253              
254             return 1;
255             }
256              
257             sub make_path ( @ )
258             {
259             my @parts = @_;
260              
261             my $path = '';
262              
263             foreach my $part ( @parts )
264             {
265             next unless $part;
266              
267             $path .= '/' . $part;
268             }
269             $path .= '/';
270              
271             $path =~ s#//#/#g while $path =~ m#//#g;
272              
273             return $path;
274             }
275              
276             sub make_file_path ( @ )
277             {
278             my @parts = @_;
279              
280             my @rest = split '/', ( pop @parts );
281             my $file = pop @rest;
282              
283             my $path = make_path @parts, @rest;
284              
285             $path .= $file || '';
286              
287             return $path;
288             }
289              
290             sub check_or_create_dir ($;$)
291             {
292             my ( $dir, $dont_create ) = @_;
293              
294             defined $dir or die "Param dir missing";
295              
296             $dir = make_path $dir;
297             $dir =~ s#/$##;
298              
299             return $dir if $dont_create;
300              
301             $@ = '';
302              
303             ( -e $dir && -d _ ) or eval { File::Path::mkpath ( $dir, false, 0700 ) }; # $paths, $verbose, $mode
304              
305             die "Cannot access directory '$dir': $@" if $@;
306              
307             return $dir;
308             }
309              
310             sub filepath_from_class ( $$;$ )
311             {
312             my ( $base_dir, $class, $dont_create ) = @_;
313              
314             defined $base_dir or die "Param base_dir missing";
315             defined $class or die "Param class missing";
316              
317             my $dir = $base_dir . '/' . $class;
318              
319             $dir =~ s#::#/#g;
320              
321             check_or_create_dir ( $dir, $dont_create );
322             }
323              
324             sub filename_from_class_and_id ( $$$;$$ )
325             {
326             my ( $base_dir, $class, $id, $dont_create, $suffix ) = @_;
327              
328             defined $id or die "Param id missing";
329              
330             $suffix ||= '.xml';
331              
332             my $filepath = filepath_from_class ( $base_dir, $class, $dont_create );
333              
334             my $filename = make_file_path $filepath, $id . $suffix;
335              
336             return $filename;
337             }
338              
339             sub class_and_id_from_filename ( $$ )
340             {
341             my ( $base_dir, $filename ) = @_;
342              
343             defined $base_dir or die "Param base_dir missing";
344             defined $filename or die "Param filename missing";
345              
346             $filename =~ s#^$base_dir/##;
347              
348             my @path = split '/', $filename;
349              
350             my $id = pop @path;
351              
352             $id =~ s/\..+$//;
353              
354             my $class = join '::', @path;
355              
356             return ( $class, $id );
357             }
358              
359             sub touch ( $ )
360             {
361             my ( $filename ) = @_;
362              
363             my @path = split '/', $filename;
364              
365             pop @path;
366              
367             check_or_create_dir ( make_path ( @path ) );
368              
369             sysopen my $fh, $filename, O_WRONLY | O_CREAT, 0600 or die "Cannot create $filename : $!";
370             close $fh or die "Cannot close $filename : $!";
371             }
372              
373             sub lock_fh ( $;$$$ )
374             {
375             my ( $fh, $how, $wait, $sleep ) = @_;
376              
377             IS_DEBUG and DEBUG "start...";
378              
379             return false unless defined $fh;
380             return false unless $fh->opened;
381              
382             $how ||= O_RDONLY;
383              
384             my $flag = ( $how & O_RDWR )
385             ? ( LOCK_EX | LOCK_NB )
386             : ( LOCK_SH | LOCK_NB );
387              
388             $wait ||= 30; # seconds
389             $sleep ||= 0.2; # seconds
390              
391             my $i = 1;
392             my $max_i = int ( $wait / $sleep );
393              
394             my $success = false;
395              
396             my $usleep = $sleep * 1_000_000; # usec
397              
398             while ( true )
399             {
400             $success = flock ( $fh, $flag );
401              
402             last if $success;
403              
404             $i++;
405              
406             return false if $i > $max_i;
407              
408             usleep ( $usleep );
409             }
410              
411             IS_DEBUG and DEBUG "done";
412              
413             return true;
414             }
415              
416             sub unlock_fh ( $ )
417             {
418             my ( $fh ) = @_;
419              
420             IS_DEBUG and DEBUG "start...";
421              
422             return false unless defined $fh;
423             return false unless $fh->opened;
424              
425             my $flag = LOCK_UN;
426              
427             my $success = flock ( $fh, $flag );
428              
429             IS_DEBUG and DEBUG "done";
430              
431             return $success;
432             }
433              
434             sub check_type_manuel ( $$;$ )
435             {
436             # see Frost::Types !
437             #
438             Frost::Check::check_type_manuel ( @_ );
439             }
440              
441             1;
442              
443             __END__
444              
445              
446             =head1 NAME
447              
448             Frost::Util - The handyman
449              
450             =head1 ABSTRACT
451              
452             No documentation yet...
453              
454             =head1 DESCRIPTION
455              
456             =for comment CLASS VARS
457              
458             =head1 CLASS METHODS
459              
460             =head1 PUBLIC ATTRIBUTES
461              
462             =head1 PRIVATE ATTRIBUTES
463              
464             =head1 CONSTRUCTORS
465              
466             =head1 DESTRUCTORS
467              
468             =head2 DEMOLISH
469              
470             =head1 PUBLIC FUNCTIONS
471              
472             =head2 IS_DEBUG
473              
474             =head2 DEBUG
475              
476             =head2 INFO
477              
478             =head2 WARN
479              
480             =head2 ERROR
481              
482             =head2 FATAL
483              
484             =head2 Dumper
485              
486             =head2 Dump
487              
488             =head2 true
489              
490             =head2 false
491              
492             =head2 TRUE
493              
494             =head2 FALSE
495              
496             =head2 UUID
497              
498             =head2 UUID_NEW_TAG
499              
500             =head2 UUID_BAD_TAG
501              
502             =head2 DEFAULT_CACHESIZE
503              
504             =head2 TIMESTAMP_ZERO
505              
506             =head2 SORT_INT
507              
508             =head2 SORT_FLOAT
509              
510             =head2 SORT_DATE
511              
512             =head2 SORT_TEXT
513              
514             =head2 STATUS_NOT_INITIALIZED
515              
516             =head2 STATUS_MISSING
517              
518             =head2 STATUS_LOADED
519              
520             =head2 STATUS_EXISTS
521              
522             =head2 STATUS_SAVING
523              
524             =head2 ROOT_VERSION
525              
526             =head2 ROOT_TAG
527              
528             =head2 OBJECT_TAG
529              
530             =head2 ATTR_TAG
531              
532             =head2 VALUE_TAG
533              
534             =head2 INDEX_TAG
535              
536             =head2 ENTRY_TAG
537              
538             =head2 ID_ATTR
539              
540             =head2 NAME_ATTR
541              
542             =head2 TYPE_ATTR
543              
544             =head2 REF_ATTR
545              
546             =head2 KEY_ATTR
547              
548             =head2 ATTR_ATTR
549              
550             =head2 NUM_ATTR
551              
552             =head2 VALUE_ATTR
553              
554             =head2 VALUE_TYPE
555              
556             =head2 ARRAY_TYPE
557              
558             =head2 HASH_TYPE
559              
560             =head2 CLASS_TYPE
561              
562             =head2 find_attribute_manuel
563              
564             =head2 find_type_constraint_manuel
565              
566             =head2 check_type_constraint_manuel
567              
568             =head2 make_path
569              
570             =head2 make_file_path
571              
572             =head2 check_or_create_dir
573              
574             =head2 filepath_from_class
575              
576             =head2 filename_from_class_and_id
577              
578             =head2 class_and_id_from_filename
579              
580             =head2 touch
581              
582             =head2 lock_fh
583              
584             =head2 unlock_fh
585              
586             =head2 check_type_manuel
587              
588             =head1 PUBLIC METHODS
589              
590             =head1 PRIVATE METHODS
591              
592             =head1 CALLBACKS
593              
594             =for comment IMMUTABLE
595              
596             =head1 GETTING HELP
597              
598             I'm reading the Moose mailing list frequently, so please ask your
599             questions there.
600              
601             The mailing list is L<moose@perl.org>. You must be subscribed to send
602             a message. To subscribe, send an empty message to
603             L<moose-subscribe@perl.org>
604              
605             =head1 BUGS
606              
607             All complex software has bugs lurking in it, and this module is no
608             exception.
609              
610             Please report any bugs to me or the mailing list.
611              
612             =head1 AUTHOR
613              
614             Ernesto L<ernesto@dienstleistung-kultur.de>
615              
616             =head1 COPYRIGHT AND LICENSE
617              
618             Copyright (C) 2010 by Dienstleistung Kultur Ltd. & Co. KG
619              
620             L<http://dienstleistung-kultur.de/frost/>
621              
622             This library is free software; you can redistribute it and/or modify
623             it under the same terms as Perl itself.
624              
625             =cut