File Coverage

blib/lib/Inline/Wrapper/Module.pm
Criterion Covered Total %
statement 111 125 88.8
branch 21 42 50.0
condition 0 2 0.0
subroutine 26 29 89.6
pod 2 2 100.0
total 160 200 80.0


line stmt bran cond sub pod time code
1             package Inline::Wrapper::Module;
2             #
3             # Inline::* module dynamic loader and useful wrapper routines
4             #
5             # Individual module handler object
6             #
7             # $Id: Module.pm 14 2010-03-10 09:08:18Z infidel $
8             #
9             # POD documentation after __END__
10             #
11              
12 6     6   36 use strict;
  6         12  
  6         377  
13 6     6   58 use warnings;
  6         34  
  6         525  
14 6     6   33 use Carp qw( carp croak );
  6         12  
  6         352  
15 6     6   32 use Data::Dumper;
  6         65  
  6         243  
16 6     6   40 use base qw( Inline::Wrapper );
  6         14  
  6         732  
17 6     6   7094 use Inline;
  6         176567  
  6         53  
18 6     6   377 use vars qw( $TRUE $FALSE $VERSION );
  6         12  
  6         6269  
19              
20             ###
21             ### VARS
22             ###
23              
24             ($VERSION) = q$Revision: 14 $ =~ /(\d+)/;
25             *TRUE = \1;
26             *FALSE = \0;
27              
28             my $PARAMS = {
29             module_name => sub { $_[0] },
30             lang_ext => sub { $_[0] },
31             };
32              
33             ###
34             ### INITIALIZER
35             ###
36              
37             sub new
38             {
39 1     1 1 5 my( $class, @args ) = @_;
40              
41 1 50       6 croak "Do not use this class directly; used internally by Inline::Wrapper"
42             unless( caller eq 'Inline::Wrapper' );
43              
44 1         10 return( $class->SUPER::new( @args ) );
45             }
46              
47             sub initialize
48             {
49 1     1 1 3 my( $self, @args ) = @_;
50              
51             # Check parameters
52 1 50       6 @args = %{ $args[0] } if( ref( $args[0] ) eq 'HASH' );
  0         0  
53 1 50       3 croak "initialize(): \%args must be a hash; read the docs"
54             if( @args & 1 );
55 1         4 my %args = @args;
56              
57 1         3 for( keys %args )
58             {
59 2 50       8 next unless( exists( $PARAMS->{lc $_} ) ); # ignore unwanted args
60 2         8 $self->{lc $_} = $PARAMS->{lc $_}->( $args{$_} );
61 2         5 delete( $args{$_} );
62             }
63              
64 1         5 $self->_set_function_list( [] );
65 1         4 $self->_set_last_load_time( 0 );
66              
67 1         4 return;
68             }
69              
70             ###
71             ### DESTRUCTOMATIC!
72             ###
73              
74             sub DESTROY
75             {
76 1     1   3 my( $self ) = @_;
77              
78 1         5 $self->_delete_namespace();
79              
80 1         9 return;
81             }
82              
83             ###
84             ### PRIVATE METHODS
85             ###
86              
87             # Load the self-corresponding sub-language code module.
88             # At this point in time, we should be a complete object.
89             sub _load
90             # "He who fights with monsters should be careful lest he thereby become a
91             # monster..."
92             {
93 1     1   2 my( $self ) = @_;
94              
95 1         5 my $module_src = $self->_read_module_source();
96 1         6 my $namespace = $self->_namespace();
97              
98             # Try to bind via Inline::$language
99 1         5 $self->_delete_namespace();
100             # BugFix: For some reason, the package stash changed between
101             # 5.11.1 and >= 5.11.1 commit-id 81693ff90925b7d196d1f339fa6f85555e38cab7
102             # Needed to add own module name into the grep -v list.
103 1         7 my $code = sprintf(q#package %s::%s;
104             use Inline;
105             Inline->bind( %s => $module_src );
106             package %s;
107             return( grep { !/^(?:BEGIN|ISA|Inline)$/ }
108             keys %%%s::%s:: )#,
109             __PACKAGE__, $namespace,
110             $self->language(),
111             __PACKAGE__,
112             __PACKAGE__, $namespace );
113              
114             # DEAR LORD, STRING EVAL! RUN AWAY!
115             # http://perlmonks.org/index.pl?node_id=732598
116 1     1   88 my @symbols = eval $code;
  1         10  
  1         2  
  1         8  
117 1 50       7 if( $@ )
118             {
119 0         0 chomp $@;
120 0         0 carp "Error compiling " . $self->_module_path() . ": '$@'";
121 0         0 return();
122             }
123              
124             # Update our state
125 1         6 $self->_set_function_list( @symbols );
126 1         17 $self->_set_last_load_time( time );
127              
128             # return loaded symbol list
129 1         10 return( @symbols );
130             }
131              
132             # Actually run the associated function and return its @retvals
133             sub _run
134             # ".. And if thou gaze long into an abyss, the abyss will also gaze into thee."
135             {
136 1     1   4 my( $self, $funcname, @args ) = @_;
137 1 50       6 croak "run(): $funcname is a required param; read the docs"
138             unless( $funcname );
139              
140 1 50       6 $self->_load() if( $self->_issue_reload() );
141              
142 1 50       6 croak "run(): $funcname not found"
143             unless( $self->_func_exists( $funcname ) );
144              
145             # Attempt to pull coderef out of package namespace
146 1         6 my $namespace = $self->_namespace();
147 1         4 my $sub = \&{__PACKAGE__ . "::${namespace}::${funcname}"};
  1         9  
148 1 50       7 carp "No such module or function: '$namespace'::'$funcname'", return
149             unless( ref( $sub ) eq 'CODE' );
150              
151             # Attempt to execute coderef
152 1         4 my @retvals = eval { $sub->( @args ) }; # Ahhh, block eval.
  1         34  
153 1 50       10 chomp $@ if( $@ );
154 1 50       6 carp "Error executing ${namespace}::${funcname}: $@", return
155             if( $@ );
156              
157 1         7 return( @retvals );
158             }
159              
160             # Fairly self-explanatory.
161             sub _read_module_source
162             {
163 1     1   3 my( $self ) = @_;
164              
165 1         3 my $path = $self->_module_path();
166              
167 1 50       50 open( my $fd, '<', $path )
168             or carp "$path is inaccessible: $!", return( undef );
169 1         2 my $module_src = do { local $/; <$fd> };
  1         6  
  1         35  
170 1         11 close( $fd );
171              
172 1         6 return( $module_src );
173             }
174              
175             sub _delete_namespace
176             {
177 2     2   5 my( $self ) = @_;
178              
179 2         5 my $namespace = $self->_namespace();
180 6     6   62 no strict 'refs';
  6         27  
  6         6068  
181 2         4 my $wiped = delete( ${__PACKAGE__.'::'}{$namespace.'::'} );
  2         21  
182              
183 2 100       26 return( $wiped ? $TRUE : $FALSE );
184             }
185              
186             ###
187             ### ACCESSORS
188             ###
189              
190             sub _module_name
191             {
192 5     5   8 my( $self ) = @_;
193              
194 5         13 return( $self->{module_name} );
195             }
196              
197             sub _set_module_name
198             {
199 0     0   0 my( $self, $modname ) = @_;
200              
201             # Validate
202 0         0 $modname = $PARAMS->{module_name}->( $modname );
203              
204 0 0       0 return( $modname
205             ? $self->{module_name} = $modname
206             : $self->{module_name} );
207             }
208              
209             sub _function_list
210             {
211 1     1   4 my( $self ) = @_;
212              
213 1         2 return( keys %{ $self->{functions} } );
  1         9  
214             }
215              
216             sub _set_function_list
217             {
218 2     2   7 my( $self, @funcs ) = @_;
219              
220 2 100       12 @funcs = @{ $funcs[0] } if( ref( $funcs[0] ) );
  1         3  
221              
222 2         8 return( $self->{functions} = { map { $_ => $TRUE } @funcs } );
  1         9  
223             }
224              
225             sub _func_exists
226             {
227 1     1   4 my( $self, $funcname ) = @_;
228              
229 1         6 return( exists( $self->{functions}->{$funcname} ) );
230             }
231              
232             sub _last_load_time
233             {
234 0     0   0 my( $self ) = @_;
235              
236 0         0 return( $self->{last_load_time} );
237             }
238              
239             sub _set_last_load_time
240             {
241 2     2   5 my( $self, $load_time ) = @_;
242              
243 2 50       21 return( $load_time =~ /^\d+$/
244             ? $self->{last_load_time} = $load_time
245             : $self->{last_load_time} );
246             }
247              
248             ###
249             ### UTILITY ROUTINES
250             ###
251              
252             # Overload the parent classes' _lang_ext, as we've stored this as an attr
253             sub _lang_ext
254             {
255 1     1   2 my( $self ) = @_;
256              
257 1         2 return( $self->{lang_ext} );
258             }
259              
260             # Return boolean if source file has been updated
261             sub _issue_reload
262             {
263 1     1   3 my( $self ) = @_;
264 1 50       13 return( $FALSE ) unless( $self->auto_reload() );
265              
266 0         0 my $file_mod_time = $self->_module_mtime();
267              
268 0 0       0 return( $self->_last_load_time < $self->_module_mtime ? $TRUE : $FALSE );
269             }
270              
271             # Return file modificiation time
272             sub _module_mtime
273             {
274 0     0   0 my( $self ) = @_;
275              
276 0         0 my $path = $self->_module_path();
277              
278 0   0     0 return( (stat $path)[9] || 0 );
279             }
280              
281             # What is our namespace, based on our object state?
282             # XXX: I don't think this is unique.
283             sub _namespace
284             {
285 4     4   7 my( $self ) = @_;
286              
287 4         12 my $modname = $self->_module_name();
288 4         11 $modname =~ s#[/\\]#_#;
289              
290 4         8 return( $modname );
291             }
292              
293             # What is our path, based on our object state?
294             sub _module_path
295             {
296 1     1   2 my( $self ) = @_;
297              
298 1         4 my $modname = $self->_module_name();
299 1         5 my $file_ext = $self->_lang_ext();
300 1 50       35 my $src_file = ( $modname =~ m/.*\Q$file_ext\E$/ )
301             ? $modname
302             : $modname . $file_ext;
303 1         8 my $path = _path_join( $self->base_dir(), $src_file );
304              
305 1         18 return( $path );
306             }
307              
308             # Generate a joined path from @_
309             sub _path_join
310             {
311 1 50   1   4 ref( $_[0] ) and shift; # scrap instance ref, if passed
312              
313 1 50       4 my $pathchar = ( $^O eq 'MSWin32' ) ? "\\" : '/';
314 1         4 return( join( $pathchar, @_ ) );
315             }
316              
317             1;
318              
319             __END__