File Coverage

blib/lib/Class/Easy.pm
Criterion Covered Total %
statement 129 149 86.5
branch 49 60 81.6
condition 15 27 55.5
subroutine 19 23 82.6
pod 12 12 100.0
total 224 271 82.6


line stmt bran cond sub pod time code
1             package Class::Easy;
2              
3             # PORTIONS FROM Sub::Identify and common::sense
4              
5             BEGIN {
6 6     6   14 our $VERSION = '0.18';
7 6         9 our @ISA;
8              
9 6     6   7139 use Class::Easy::Import;
  6         15  
  6         33  
10            
11 6         11 my $loaded;
12 6 50       28 unless ($ENV{PERL_SUB_IDENTIFY_PP}) {
13 6         10 local $@;
14 6         9 eval {
15 6         32 require XSLoader;
16 6         4097 XSLoader::load(__PACKAGE__, $VERSION);
17             };
18            
19 6 50 33     62 die $@ if $@ && $@ !~ /object version|loadable object/;
20            
21 6 50       25 $loaded = 1 unless $@;
22             }
23            
24 6         14 our $is_pure_perl = !$loaded;
25            
26 6 50       11442 if ($is_pure_perl) {
27 0         0 require Class::Easy::PP;
28             }
29              
30             }
31              
32             require Class::Easy::Timer;
33              
34 0     0 1 0 sub stash_name ($) { (get_coderef_info($_[0]))[0] }
35 0     0 1 0 sub sub_name ($) { (get_coderef_info($_[0]))[1] }
36 0     0 1 0 sub sub_fullname ($) { join '::', get_coderef_info($_[0]) }
37              
38              
39             our @EXPORT = qw(has try_to_use try_to_use_quiet try_to_use_inc try_to_use_inc_quiet make_accessor timer);
40             our @EXPORT_OK = qw(sub_name stash_name sub_fullname get_coderef_info);
41              
42             our %EXPORT_FOREIGN = (
43             'Class::Easy::Log' => [qw(debug critical debug_depth logger catch_stderr release_stderr)],
44             # 'Class::Easy::Timer' => [qw(timer)],
45             );
46              
47             our $LOG = '';
48              
49             sub timer {
50 3     3 1 1301 return Class::Easy::Timer->new (@_);
51             }
52              
53             sub import {
54 5     5   216 my $mypkg = shift;
55 5         11 my $callpkg = caller;
56            
57 5         16 my %params = @_;
58            
59             # use warnings
60 5         19 ${^WARNING_BITS} = $Class::Easy::Import::WARN;
61            
62             # use strict, use utf8;
63 5         15 $^H |= $Class::Easy::Import::H;
64            
65             # use feature
66 5         70 $^H{feature_switch} = $^H{feature_say} = $^H{feature_state} = 1;
67            
68             # probably check for try_to_use is enough
69             return
70 5         55 if defined *{"$callpkg\::try_to_use"}{CODE}
  0         0  
71 5 50 33     11 and sub_fullname (*{"$callpkg\::try_to_use"}{CODE}) eq __PACKAGE__.'::__ANON__';
72            
73             # export subs
74 5         19 *{"$callpkg\::$_"} = \&{"$mypkg\::$_"} foreach @EXPORT;
  35         132  
  35         164  
75 5         21 foreach my $p (keys %EXPORT_FOREIGN) {
76 5         10 *{"$callpkg\::$_"} = \&{"$p\::$_"} foreach @{$EXPORT_FOREIGN{$p}};
  5         23  
  30         310  
  30         118  
77             }
78             }
79              
80             sub has ($;%) {
81            
82 14     14 1 708 my ($caller) = caller;
83 14         20 my $accessor = shift;
84            
85 14         33 return make_accessor ($caller, $accessor, _unless_exists => 1, @_);
86             }
87              
88             sub make_accessor ($;$;$;%) {
89 51     51 1 963 my $caller = shift;
90 51         65 my $name = shift;
91              
92 51         250 my $full_ref = "${caller}::$name";
93            
94 51         54 my $default;
95 51 100 66     271 $default = pop
96             if @_ == 1 or @_ == 3; # _from_has support
97            
98 51 50       195 die 'bad call from: ' . join (', ', caller)
99             if scalar @_ % 2;
100 51         137 my %config = @_;
101            
102 51         73 my $isa = $config{isa};
103 51   100     174 my $is = $config{is} || 'ro';
104 51 100       113 $default = $config{default}
105             if exists $config{default};
106            
107 51 100 100     229 $config{global} = 1
108             if defined $default and $is eq 'ro';
109            
110             # when make_accessor called from has, we must check for already created
111             # accessor and redefine only if redefined flag supplied
112 51 50 66     138 if (delete $config{_unless_exists} and defined *{$full_ref}{CODE}) {
  14         87  
113 0         0 return;
114             }
115            
116 51         56 my $mode;
117 51 100       109 $mode = 1 if $is eq 'ro';
118 51 100       101 $mode = 2 if $is eq 'rw';
119            
120 51 50       200 die "unknown accessor type: $is"
121             unless $is =~ /^r[ow]$/;
122            
123 51 100       141 if (ref $default eq 'CODE') {
    100          
124            
125 13         15 *{$full_ref} = $default;
  13         146  
126            
127             } elsif ($config{global}) {
128            
129 14         150 *{$full_ref} = sub {
130            
131 18     18   39 my $c = @_;
132            
133             # return &$default if $c == 1 and ref $default eq 'CODE';
134 18 100       79 return $default if $c == 1;
135 6 100       19 _has_error ($caller, $name, $c - 1) if $c ^ $mode;
136            
137 5   33     28 make_accessor (ref $_[0] || $_[0], $name, %config, default => $_[1]);
138 14         48 };
139            
140             } else {
141 24         154 *{$full_ref} = sub {
142            
143 21     21   461 my $c = @_;
144            
145 21 100       103 return $_[0]->{$name} if $c == 1;
146 9 100       26 _has_error ($caller, $name, $c - 1) if $c ^ $mode;
147            
148 7         44 $_[0]->{$name} = $_[1];
149              
150 24         109 };
151            
152             }
153             }
154              
155             sub _has_error {
156 3     3   5 my $caller = shift;
157 3         6 my $name = shift;
158 3         5 my $argc = shift;
159            
160 3         21 my ($acc_caller, $line) = (caller(1))[0, 2];
161 3         33 die "too many parameters ($argc) for accessor $caller\->$name at $acc_caller line $line.\n";
162             }
163              
164             sub _try_to_use {
165 17     17   24 my $use_lib = shift;
166 17         20 my $quiet = shift;
167 17         34 my @chunks = @_;
168              
169 17         43 my $package = join '::', @chunks;
170 17         68 @chunks = split '::', $package;
171 17         45 my $path = join ('/', @chunks) . '.pm';
172            
173 17         27 $@ = '';
174            
175 17 100       69 if ($use_lib) {
176 1 50       4 return "exists in \%INC"
177             if exists $INC{$path};
178             } else {
179             # OLD: we removed "or ! exists $INC{$path}" statement because
180             # "used" package always available via symbol table
181 16 100       1560 if (eval ("scalar grep {!/\\w+\:\:/} keys \%$package\::;") > 0) {
182 2         12 return "exists in symbol table";
183             }
184             }
185            
186 6     6   5781 eval "use $package";
  6     6   7723  
  6     2   256  
  6     1   2552  
  0         0  
  0         0  
  2         842  
  0         0  
  0         0  
  1         362  
  0         0  
  0         0  
  15         889  
187            
188 15 100       65 if ($@) {
189 9 100       72 Class::Easy::Log::debug ("i can't load module ($path): $@")
190             unless $quiet;
191 9         52 return;
192             }
193            
194 6         35 return 1;
195             }
196              
197             sub try_to_use {
198 13     13 1 19849 return _try_to_use (0, 0, @_);
199             }
200              
201             sub try_to_use_quiet {
202 3     3 1 11 return _try_to_use (0, 1, @_);
203             }
204              
205             sub try_to_use_inc {
206 0     0 1 0 return _try_to_use (1, 0, @_);
207             }
208              
209             sub try_to_use_inc_quiet {
210 1     1 1 3 return _try_to_use (1, 1, @_);
211             }
212              
213             sub list_local_subs_for {
214 7     7 1 11 my $module = shift;
215 7   50     30 my $enum_imported = shift || 0;
216            
217 7         7 my $namespace = \%{$module . '::'};
  7         23  
218            
219 119         359 my @sub_list = grep {
220 119         96 defined *{"$module\::$_"}{CODE}
  7         47  
221 7         10 } keys %{$namespace};
222            
223 7         49 my $sub_by_type = {
224             method => {},
225             imported => {},
226             runtime => {}
227             };
228            
229 7         15 foreach my $sub (@sub_list) {
230 94         87 my ($real_package, $real_sub) = (get_coderef_info (*{"$module\::$sub"}{CODE}));
  94         386  
231              
232 94 100       215 if ($real_package eq $module) {
    100          
233 19         48 $sub_by_type->{method}->{$sub} = 1;
234             } elsif ($real_sub eq '__ANON__') {
235 23         47 $sub_by_type->{runtime}->{$sub} = 1;
236             } else {
237 52         123 $sub_by_type->{imported}->{$real_package}->{$real_sub} = $sub; # who needs $real_sub ?
238             }
239             }
240            
241             wantarray
242 7 100       27 ? (keys %{$sub_by_type->{method}}, keys %{$sub_by_type->{runtime}})
  4         13  
  4         42  
243             : $sub_by_type;
244             }
245              
246             sub list_all_subs_for {
247 3   33 3 1 551 my $module = shift || (caller)[0];
248 3   50     19 my $filter = shift || '';
249            
250 3 100       11 $module = ref $module
251             if ref $module;
252            
253 3         4 my $namespace = \%{$module . '::'};
  3         10  
254            
255 3         5 my $linear_isa;
256            
257 3 50       14 if ($] < 5.009_005) {
258 0         0 require Class::Easy::MRO;
259 0         0 $linear_isa = __get_linear_isa ($module);
260             } else {
261 3         2580 require mro;
262 3         1520 $linear_isa = mro::get_linear_isa ($module);
263             }
264            
265 3         18 my $sub_by_type = list_local_subs_for ($module);
266 7         31 $sub_by_type->{inherited}->{$_} = [list_local_subs_for ($_)]
267 3         7 foreach grep {$_ ne $module} @$linear_isa;
268            
269             wantarray
270             ? (
271 0           keys %{$sub_by_type->{method}},
  0            
272 0           keys %{$sub_by_type->{runtime}},
273 3 50       15 map {@{$sub_by_type->{inherited}->{$_}}} keys %{$sub_by_type->{inherited}})
  0            
  0            
274             : $sub_by_type;
275             }
276              
277             1;
278              
279             =head1 NAME
280              
281             Class::Easy - make class routine easy
282              
283             =head1 ABSTRACT
284              
285             This module is a functionality compilation of some good modules from CPAN.
286             Ideas are taken from Class::Data::Inheritable, Class::Accessor, Modern::Perl
287             and Moose at least.
288              
289             Instead of building monstrous alternatives to Moose or making thousand modules
290             for every function I need, I decide to write small and efficient libraries for
291             everyday use. Class::Easy::Base is a base component for classes.
292              
293             =head1 SYNOPSIS
294              
295             SYNOPSIS
296              
297             # automatic loading of strict, warnings and utf8, like common::sense
298             use Class::Easy::Import;
299             # or same as above + functions like 'has', 'try_to_use', 'timer' and 'logger'
300             use Class::Easy;
301            
302             # try to load package IO::Easy, return 1 when success
303             try_to_use ('IO::Easy');
304            
305             # try to load package IO::Easy, but search for package existence
306             # within %INC instead of symbolic table
307             try_to_use_inc ('IO::Easy');
308            
309             # for current package
310             has "property_ro"; # make readonly object accessor
311             has "property_rw", is => 'rw'; # make readwrite object accessor
312            
313             has global25 => 25; # make readonly static accessor with value 25
314             has "global", global => 1, is => 'rw'; # make readwrite static accessor
315              
316             # make subroutine in package main
317             make_accessor ('main', 'initialize', default => sub {
318             $::initialized = 1;
319             return "initialized!";
320             });
321            
322             # see documentation for Class::Easy::Log
323            
324             # string "[PID] [PACKAGE(STRING)] [DBG] something" logged
325             debug "something";
326              
327             # see documentation for Class::Easy::Timer
328              
329             my $t = timer ('long operation');
330             # … long operation
331              
332             my $time = $t->lap ('another long op');
333             # …
334              
335             $time = $t->end;
336             # $time contains time between last 'lap' or 'timer'
337             # and 'end' call
338              
339             $time = $t->total;
340             # now $time contains total time between timer init
341             # and end call
342              
343             =head1 FUNCTIONS
344              
345             =head2 has ($name [, is => 'ro' | 'rw'] [, default => $default], [, global => 1])
346              
347             create accessor named $name in current scope
348              
349             =cut
350              
351             =head2 make_accessor ($scope, $name)
352              
353             create accessor in selected scope
354              
355             =cut
356              
357             =head2 try_to_use, try_to_use_quiet
358              
359             tries to use specified package with printing error message to STDERR
360             or "_quiet" version.
361              
362             return true value in case of successful operation or existing non-package
363             references in symbol table. correctly works with virtual packages.
364              
365             takes package name or package name chunks, for example:
366              
367             try_to_use ('IO::Easy');
368             # or equivalent
369             try_to_use (qw(IO Easy));
370              
371             if you want to separate io errors from syntax errors you may want to
372             check $! variable;
373              
374             for example:
375              
376             use Errno qw(:POSIX);
377            
378             if (!try_to_use ('IO::Easy')) {
379             die 'file not found for package IO::Easy'
380             if $!{ENOENT};
381             }
382              
383             =cut
384              
385             =head2 try_to_use_inc, try_to_use_inc_quiet
386              
387             similar to the try_to_use, but check for module presence in %INC
388             instead of symbol table lookup.
389              
390             =cut
391              
392             =head2 timer
393              
394             create new L object
395              
396             =cut
397              
398             =head2 get_coderef_info, stash_name, sub_name, sub_fullname
399              
400             retrieve real name for coderef. useful for anonymous or imported functions
401              
402             get_coderef_info (*{Class::Easy::timer}{CODE}); # ('Class::Easy', 'timer')
403             stash_name (*{Class::Easy::timer}{CODE}); # 'Class::Easy'
404             sub_name (*{Class::Easy::timer}{CODE}); # 'timer'
405             sub_fullname (*{Class::Easy::timer}{CODE}); # 'Class::Easy::timer'
406              
407             =cut
408              
409             =head2 list_all_subs_for, list_local_subs_for
410              
411             in scalar context return hashref with complete coderef info for class.
412             - key 'inherited' contains all inherited methods, separated by class name,
413             - key 'runtime' contains all code references in current package which point
414             to anonymous method,
415             - key 'method' contains all local methods,
416             - key 'imported' contains all imported subs, separated by class name
417              
418             {
419             'inherited' => {
420             'My::Circle' => [
421             'new',
422             'global_hash',
423             'global_hash_rw',
424             'new_default',
425             'global_hash_rw_default',
426             'dim_x',
427             'id',
428             'dim_y'
429             ]
430             },
431             'runtime' => {
432             'global_ro' => 1,
433             'global_one' => 1,
434             'global_one_defined' => 1,
435             'dim_z' => 1,
436             'accessor' => 1
437             },
438             'method' => {
439             'sub_z' => 1
440             },
441             'imported' => {
442             'Class::Easy' => {
443             'make_accessor' => 'make_accessor',
444             'try_to_use' => 'try_to_use',
445             'try_to_use_inc' => 'try_to_use_inc',
446             'try_to_use_quiet' => 'try_to_use_quiet',
447             'has' => 'has',
448             'timer' => 'timer',
449             'try_to_use_inc_quiet' => 'try_to_use_inc_quiet'
450             },
451             'Class::Easy::Log' => {
452             'critical' => 'critical',
453             'release_stderr' => 'release_stderr',
454             'catch_stderr' => 'catch_stderr',
455             'debug' => 'debug',
456             'debug_depth' => 'debug_depth',
457             'logger' => 'logger'
458             }
459             }
460             };
461              
462             'local' version of subroutine doesn't contains any inherited methods
463              
464              
465             =cut
466              
467              
468              
469             =head1 AUTHOR
470              
471             Ivan Baktsheev, C<< >>
472              
473             =head1 BUGS
474              
475             Please report any bugs or feature requests to my email address,
476             or through the web interface at L.
477             I will be notified, and then you'll automatically be notified
478             of progress on your bug as I make changes.
479              
480             =head1 SUPPORT
481              
482              
483              
484             =head1 ACKNOWLEDGEMENTS
485              
486              
487              
488             =head1 COPYRIGHT & LICENSE
489              
490             Copyright 2008-2009 Ivan Baktsheev
491              
492             This program is free software; you can redistribute it and/or modify it
493             under the same terms as Perl itself.
494              
495             =cut