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