File Coverage

blib/lib/Device/Firmata/Base.pm
Criterion Covered Total %
statement 27 136 19.8
branch 9 78 11.5
condition 1 11 9.0
subroutine 5 21 23.8
pod 15 15 100.0
total 57 261 21.8


line stmt bran cond sub pod time code
1             package Device::Firmata::Base;
2            
3             =head1 NAME
4            
5             Device::Firmata::Base - abstract baseclass for Device::Firmata modules
6            
7             =head1 DESCRIPTION
8            
9             Provides various convenience methods for module internal use.
10            
11             =cut
12            
13 1     1   7 use strict 'vars', 'subs';
  1         3  
  1         50  
14 1     1   6 use warnings;
  1         2  
  1         122  
15 1         2612 use vars qw/
16             $AUTOLOAD
17             $FIRMATA_DEBUG_LEVEL
18             $FIRMATA_ERROR_CLASS
19             $FIRMATA_ERROR
20             $FIRMATA_ATTRIBS
21             $FIRMATA_DEBUGGING
22             $FIRMATA_LOCALE
23             $FIRMATA_LOCALE_PATH
24             $FIRMATA_LOCALE_MESSAGES
25 1     1   8 /;
  1         3  
26            
27             $FIRMATA_DEBUGGING = 1;
28             $FIRMATA_ATTRIBS = {};
29             $FIRMATA_LOCALE = 'en';
30             $FIRMATA_LOCALE_PATH = '.';
31             $FIRMATA_DEBUG_LEVEL = 0;
32             $FIRMATA_ERROR_CLASS = 'Device::Firmata::Error';
33            
34             =head1 METHODS
35            
36             =head2 import
37            
38             Ease setting of configuration options
39            
40             =cut
41            
42             sub import {
43 2     2   18 my $self = shift;
44 2         6 my $pkg = caller;
45 2         6 my $config_opts = {
46             debugging => $FIRMATA_DEBUGGING,
47             };
48            
49 2 100       24 if ( @_ ) {
50 1         4 my $opts = $self->parameters( @_ );
51 1 50       5 if ( my $attrs = $opts->{FIRMATA_ATTRIBS} ) {
52 1         2 *{$pkg.'::FIRMATA_ATTRIBS'} = \$attrs;
  1         6  
53             }
54            
55 1 50 33     2 unless ( ref *{$pkg.'::ISA'} eq 'ARRAY' and @${$pkg.'::ISA'}) {
  1         10  
  0         0  
56 0         0 my @ISA = ref $opts->{ISA} ? @{$opts->{ISA}} :
57             $opts->{ISA} ? $opts->{ISA} :
58 1 50       5 __PACKAGE__;
    50          
59 1         4 *{$pkg.'::ISA'} = \@ISA;
  1         347  
60             }
61             }
62             }
63            
64             =head2 new
65            
66             =cut
67            
68             sub new {
69 0     0 1 0 my $pkg = shift;
70 0         0 my $basis = copy_struct( $pkg->init_class_attribs );
71 0         0 my $self = bless $basis, $pkg;
72            
73 0 0       0 @_ = $self->pre_init( @_ ) if $self->{_biofunc_pre_init};
74            
75 0 0       0 if ( $self->{_biofunc_init} ) {
76 0         0 $self->init( @_ );
77             }
78             else {
79 0         0 $self->init_instance_attribs( @_ );
80             }
81            
82 0 0       0 return $self->post_init if $self->{_biofunc_post_init};
83 0         0 return $self;
84             }
85            
86             =head2 create
87            
88             A soft new as some objects will override new and
89             we don't want to cause problems but still want
90             to invoice our creation code
91            
92             =cut
93            
94             sub create {
95 0     0 1 0 my $self = shift;
96 0         0 my $basis = copy_struct( $self->init_class_attribs );
97            
98 0         0 @$self{ keys %$basis } = values %$basis;
99            
100 0 0       0 @_ = $self->pre_init( @_ ) if $self->{_biofunc_pre_init};
101            
102 0 0       0 if ( $self->{_biofunc_init} ) {
103 0         0 $self->init( @_ );
104             }
105             else {
106 0         0 $self->init_instance_attribs( @_ );
107             }
108            
109 0 0       0 return $self->post_init if $self->{_biofunc_post_init};
110 0         0 return $self;
111             }
112            
113             =head2 init_instance_attribs
114            
115             =cut
116            
117             sub init_instance_attribs {
118             # --------------------------------------------------
119 0     0 1 0 my $self = shift;
120 0         0 my $opts = $self->parameters( @_ );
121            
122 0         0 foreach my $k ( keys %$self ) {
123 0 0       0 next unless exists $opts->{$k};
124 0 0       0 next if $k =~ /^_biofunc/;
125 0         0 $self->{$k} = $opts->{$k};
126             }
127            
128 0         0 return $self;
129             }
130            
131             =head2 init_class_attribs
132            
133             =cut
134            
135             sub init_class_attribs {
136             # --------------------------------------------------
137 0   0 0 1 0 my $class = ref $_[0] || shift;
138 0 0       0 my $track = { $class => 1, @_ ? %{$_[0]} : () };
  0         0  
139            
140 0 0       0 return ${"${class}::ABSOLUTE_ATTRIBS"} if ${"${class}::ABSOLUTE_ATTRIBS"};
  0         0  
  0         0  
141            
142 0   0     0 my $u = ${"${class}::FIRMATA_ATTRIBS"} || {};
143            
144 0         0 for my $c ( @{"${class}::ISA"} ) {
  0         0  
145 0 0       0 next unless ${"${c}::FIRMATA_ATTRIBS"};
  0         0  
146            
147 0         0 my $h;
148 0 0       0 if ( ${"${c}::ABSOLUTE_ATTRIBS"} ) {
  0         0  
149 0         0 $h = ${"${c}::ABSOLUTE_ATTRIBS"};
  0         0  
150             }
151             else {
152 0 0       0 $c->fatal( "Cyclic dependancy!" ) if $track->{$c};
153 0         0 $h = $c->init_class_attribs( $c, $track );
154             }
155            
156 0         0 foreach my $k ( keys %$h ) {
157 0 0       0 next if exists $u->{$k};
158 0         0 $u->{$k} = copy_struct( $h->{$k} );
159             }
160             }
161            
162 0         0 foreach my $f ( qw( pre_init init post_init ) ) {
163 0 0       0 $u->{"_biofunc_" . $f} = $class->can( $f ) ? 1 : 0;
164             }
165            
166 0         0 ${"${class}::ABSOLUTE_ATTRIBS"} = $u;
  0         0  
167            
168 0         0 return $u;
169             }
170            
171             # logging/exception functions
172            
173            
174            
175             # Utilty functions
176            
177             =head2 parameters
178            
179             =cut
180            
181             sub parameters {
182             # --------------------------------------------------
183 1 50   1 1 3 return {} unless @_ > 1;
184            
185 1 50       4 if ( @_ == 2 ) {
186 0 0       0 return $_[1] if ref $_[1];
187 0         0 return; # something wierd happened
188             }
189            
190 1 50       4 @_ % 2 or $_[0]->warn( "Even number of elements were not passed to call.", join( " ", caller() ) );
191            
192 1         2 shift;
193            
194 1         4 return {@_};
195             }
196            
197             =head2 copy_struct
198            
199             =cut
200            
201             sub copy_struct {
202             # --------------------------------------------------
203 0     0 1   my $s = shift;
204            
205 0 0         if ( ref $s ) {
206 0 0         if ( UNIVERSAL::isa( $s, 'HASH' ) ) {
    0          
207             return {
208 0           map { my $v = $s->{$_}; (
  0            
209 0 0         $_ => ref $v ? copy_struct( $v ) : $v
210             )} keys %$s
211             };
212             }
213             elsif ( UNIVERSAL::isa( $s, 'ARRAY' ) ) {
214             return [
215 0 0         map { ref $_ ? copy_struct($_) : $_ } @$s
  0            
216             ];
217             }
218 0           die "Cannot copy struct! : ".ref($s);
219             }
220            
221 0           return $s;
222             }
223            
224             =head2 locale
225            
226             =cut
227            
228             sub locale {
229             # --------------------------------------------------
230 0 0   0 1   @_ >= 2 and shift;
231 0           $FIRMATA_LOCALE = shift;
232             }
233            
234             =head2 locale_path
235            
236             =cut
237            
238             sub locale_path {
239             # --------------------------------------------------
240 0 0   0 1   @_ >= 2 and shift;
241 0           $FIRMATA_LOCALE_PATH = shift;
242             }
243            
244             =head2 language
245            
246             =cut
247            
248             sub language {
249             # --------------------------------------------------
250 0     0 1   my $self = shift;
251 0           require Device::Firmata::Language;
252 0           return Device::Firmata::Language->language(@_);
253             }
254            
255             =head2 error
256            
257             =cut
258            
259             sub error {
260             # --------------------------------------------------
261             # Handle any error messages
262             #
263 0     0 1   my $self = shift;
264 0 0         if ( @_ ) {
265 0           my $err_msg = $self->init_error->error(@_);
266 0           $self->{error} = $err_msg;
267 0           return;
268             }
269            
270 0           my $err_msg = $self->{error};
271 0           $self->{error} = '';
272 0           return $err_msg;
273             }
274            
275             =head2 init_error
276            
277             Creates the global error object that will collect
278             all error messages generated on the system. This
279             function can be called as many times as desired.
280            
281             =cut
282            
283             sub init_error {
284             # --------------------------------------------------
285             #
286 0 0   0 1   $FIRMATA_ERROR and return $FIRMATA_ERROR;
287            
288 0 0         if ( $FIRMATA_ERROR_CLASS eq 'Device::Firmata::Error' ) {
289 0           require Device::Firmata::Error;
290 0           return $FIRMATA_ERROR = $FIRMATA_ERROR_CLASS;
291             }
292            
293             # Try and load the file. Use default if fails
294 0           eval "require $FIRMATA_ERROR_CLASS";
295 0 0         $@ and return $FIRMATA_ERROR = $FIRMATA_ERROR_CLASS;
296            
297             # Try and init the error object. Use default if fails
298 0           eval { $FIRMATA_ERROR = $FIRMATA_ERROR_CLASS->new(); };
  0            
299 0 0         $@ and return $FIRMATA_ERROR = $FIRMATA_ERROR_CLASS;
300 0           return $FIRMATA_ERROR;
301             }
302            
303             =head2 fatal
304            
305             Handle tragic and unrecoverable messages
306            
307             =cut
308            
309             sub fatal {
310             # --------------------------------------------------
311             #
312 0     0 1   my $self = shift;
313 0           return $self->error( -1, @_ );
314             }
315            
316             =head2 warn
317            
318             Handle tragic and unrecoverable messages
319            
320             =cut
321            
322             sub warn {
323             # --------------------------------------------------
324             #
325 0     0 1   my $self = shift;
326 0           return $self->error( 0, @_ );
327             }
328            
329             =head2 debug
330            
331             =cut
332            
333             sub debug {
334             # --------------------------------------------------
335 0     0 1   my ( $self, $debug ) = @_;
336 0           $FIRMATA_DEBUG_LEVEL = $debug;
337             }
338            
339             =head2 DESTROY
340            
341             =cut
342            
343             sub DESTROY {
344             # --------------------------------------------------
345 0     0     my $self = shift;
346             }
347            
348             =head2 AUTOLOAD
349            
350             =cut
351            
352             sub AUTOLOAD {
353             # --------------------------------------------------
354 0     0     my $self = shift;
355 0           my ($attrib) = $AUTOLOAD =~ /::([^:]+)$/;
356            
357 0 0 0       if ( $self and UNIVERSAL::isa( $self, 'Device::Firmata::Base' ) ) {
358 0           $self->error( FIRMATA__unhandled => $attrib, join( " ", caller() ) );
359 0           die $self->error;
360             }
361             else {
362 0           die "Tried to call function '$attrib' via object '$self' @ ", join( " ", caller(1) ), "\n";
363             }
364            
365             }
366            
367             ####################################################
368             # Object instantiation code
369             ####################################################
370            
371             =head2 object_load
372            
373             Load the appropriate package and attempt to initialize
374             the object as well
375            
376             =cut
377            
378             sub object_load {
379             # --------------------------------------------------
380 0     0 1   my $self = shift;
381 0           my $object_class = shift;
382 0 0         return unless $object_class =~ /^\w+(?:::\w+)*$/; # TODO ERROR MESSAGE
383 0 0         eval "require $object_class; 1" or die $@;
384 0           my $object = $object_class->new(@_);
385 0           return $object;
386             }
387            
388            
389             1;