File Coverage

blib/lib/Device/Firmata/Base.pm
Criterion Covered Total %
statement 31 140 22.1
branch 9 78 11.5
condition 1 11 9.0
subroutine 6 22 27.2
pod 15 15 100.0
total 62 266 23.3


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