File Coverage

blib/lib/App/GenModEmbedder.pm
Criterion Covered Total %
statement 45 48 93.7
branch 4 8 50.0
condition 1 3 33.3
subroutine 6 6 100.0
pod 1 1 100.0
total 57 66 86.3


line stmt bran cond sub pod time code
1             package App::GenModEmbedder;
2              
3             our $DATE = '2016-12-26'; # DATE
4             our $VERSION = '0.003'; # VERSION
5              
6 1     1   57530 use 5.010001;
  1         3  
7 1     1   6 use strict;
  1         1  
  1         29  
8 1     1   4 use warnings;
  1         2  
  1         179  
9              
10             our %SPEC;
11              
12             $SPEC{gen_mod_embedder} = {
13             v => 1.1,
14             summary => 'Generate a piece of Perl code that embeds a module',
15             description => <<'_',
16              
17             Suppose your code depends on a (trivial, single file, stable) module and wants
18             to eliminate dependency on that module by embedding it into your code. To do
19             that, just put the output of this tool (the embedding code) somewhere in your
20             source code. The structure of the embedding code is as follows:
21              
22             unless (eval { require Foo::Bar; 1 }) {
23             my $source = <<'END_OF_SOURCE';
24             ...
25             ...
26             END_OF_SOURCE
27             eval $source; die if $@;
28             $INC{'Foo/Bar.pm'} = '(set by ' . __FILE__ . ')';
29             }
30              
31             Compared to fatpacking, this technique tries to load the original module first,
32             does not use require hook, and is suitable for use inside .pm file as well as
33             script.
34              
35             Compared to datapacking, this technique tries to load the original module first,
36             does not use require hook nor DATA section, and is suitable for use inside .pm
37             file as well as script.
38              
39             _
40             args => {
41             module => {
42             schema => 'perl::modname',
43             req => 1,
44             pos => 0,
45             completion => sub {
46             require Complete::Module;
47             my %args = @_;
48             Complete::Module::complete_module(word=>$args{word});
49             },
50             },
51             as => {
52             summary => 'Rename the module',
53             schema => 'perl::modname',
54             },
55             strip_pod => {
56             schema => ['bool*', is=>1],
57             default => 1,
58             },
59             indent_level => {
60             schema => ['int*', min=>0],
61             default => 0,
62             },
63             },
64             links => [
65             {url => 'Module::FatPack'},
66             {url => 'Module::DataPack'},
67             {url => 'App::FatPacker'},
68             {url => 'App::depak'},
69             ],
70             };
71             sub gen_mod_embedder {
72 1     1   12 no strict 'refs';
  1         2  
  1         29  
73 1     1   4 no warnings 'once';
  1         1  
  1         525  
74 1     1 1 801 require ExtUtils::MakeMaker;
75 1         80046 require File::Slurper;
76 1         2932 require Module::Path::More;
77              
78 1         713 my %args = @_;
79 1         3 my $mod = $args{module};
80 1         6 (my $mod_pm = "$mod.pm") =~ s!::!/!g;
81              
82 1   33     4 my $as = $args{as} // $mod;
83 1         4 (my $as_pm = "$as.pm") =~ s!::!/!g;
84              
85 1 50       4 my $path = Module::Path::More::module_path(module => $mod)
86             or return [400, "Can't find module $mod on filesystem"];
87              
88 1         203 my $version = MM->parse_version($path);
89 1 50       297 defined $version or return [400, "Can't extract VERSION for $mod from $path"];
90              
91 1         5 my $source = File::Slurper::read_text($path);
92              
93 1 50       96 if ($args{strip_pod}) {
94 0         0 require Perl::Stripper;
95 0         0 my $stripper = Perl::Stripper->new(
96             # strip_pod => 1, # the default
97             );
98 0         0 $source = $stripper->strip($source);
99             }
100              
101             # XXX this is not perfect/proper
102 1 50       3 if ($mod ne $as) {
103 1         43 $source =~ s/\b(package\s+)\Q$mod\E\b/$1 . $as/es;
  1         5  
104             }
105              
106 1         84 $source =~ s/\s+\z//s;
107 1         3 $source .= "\n";
108 1         57 $source =~ s/^/#/mg;
109              
110 1         47 my $i0 = " " x $args{indent_level};
111              
112 1         5 my $preamble = "${i0}# BEGIN EMBEDDING MODULE: mod=$mod ver=$version generator=\"".__PACKAGE__." ".(${__PACKAGE__."::VERSION"})."\" generated-at=\"".(scalar localtime)."\"\n";
  1         103  
113 1         4 $preamble .= "${i0}unless (eval { require $as; 1 }) {\n";
114 1         2 $preamble .= "${i0} my \$source = '##line ' . (__LINE__+1) . ' \"' . __FILE__ . qq(\"\\n) . <<'EOS';\n";
115 1         2 my $postamble = "EOS\n";
116 1         2 $postamble .= "${i0} \$source =~ s/^#//gm;\n";
117 1         2 $postamble .= "${i0} eval \$source; die if \$@;\n";
118 1         3 $postamble .= "${i0} \$INC{'$as_pm'} = '(set by embedding code in '.__FILE__.')';\n";
119 1         2 $postamble .= "${i0}}\n";
120 1         2 $postamble .= "${i0}# END EMBEDDING MODULE\n";
121              
122 1         14 return [200, "OK", $preamble . $source . $postamble,
123             {"cmdline.skip_format" => 1}];
124             }
125              
126             1;
127             # ABSTRACT: Generate a piece of Perl code that embeds a module
128              
129             __END__
130              
131             =pod
132              
133             =encoding UTF-8
134              
135             =head1 NAME
136              
137             App::GenModEmbedder - Generate a piece of Perl code that embeds a module
138              
139             =head1 VERSION
140              
141             This document describes version 0.003 of App::GenModEmbedder (from Perl distribution App-GenModEmbedder), released on 2016-12-26.
142              
143             =head1 FUNCTIONS
144              
145              
146             =head2 gen_mod_embedder(%args) -> [status, msg, result, meta]
147              
148             Generate a piece of Perl code that embeds a module.
149              
150             Suppose your code depends on a (trivial, single file, stable) module and wants
151             to eliminate dependency on that module by embedding it into your code. To do
152             that, just put the output of this tool (the embedding code) somewhere in your
153             source code. The structure of the embedding code is as follows:
154              
155             unless (eval { require Foo::Bar; 1 }) {
156             my $source = <<'END_OF_SOURCE';
157             ...
158             ...
159             END_OF_SOURCE
160             eval $source; die if $@;
161             $INC{'Foo/Bar.pm'} = '(set by ' . __FILE__ . ')';
162             }
163              
164             Compared to fatpacking, this technique tries to load the original module first,
165             does not use require hook, and is suitable for use inside .pm file as well as
166             script.
167              
168             Compared to datapacking, this technique tries to load the original module first,
169             does not use require hook nor DATA section, and is suitable for use inside .pm
170             file as well as script.
171              
172             This function is not exported.
173              
174             Arguments ('*' denotes required arguments):
175              
176             =over 4
177              
178             =item * B<as> => I<perl::modname>
179              
180             Rename the module.
181              
182             =item * B<indent_level> => I<int> (default: 0)
183              
184             =item * B<module>* => I<perl::modname>
185              
186             =item * B<strip_pod> => I<bool> (default: 1)
187              
188             =back
189              
190             Returns an enveloped result (an array).
191              
192             First element (status) is an integer containing HTTP status code
193             (200 means OK, 4xx caller error, 5xx function error). Second element
194             (msg) is a string containing error message, or 'OK' if status is
195             200. Third element (result) is optional, the actual result. Fourth
196             element (meta) is called result metadata and is optional, a hash
197             that contains extra information.
198              
199             Return value: (any)
200              
201             =head1 HOMEPAGE
202              
203             Please visit the project's homepage at L<https://metacpan.org/release/App-GenModEmbedder>.
204              
205             =head1 SOURCE
206              
207             Source repository is at L<https://github.com/perlancar/perl-App-GenModEmbedder>.
208              
209             =head1 BUGS
210              
211             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-GenModEmbedder>
212              
213             When submitting a bug or request, please include a test-file or a
214             patch to an existing test-file that illustrates the bug or desired
215             feature.
216              
217             =head1 SEE ALSO
218              
219              
220             L<Module::FatPack>.
221              
222             L<Module::DataPack>.
223              
224             L<App::FatPacker>.
225              
226             L<App::depak>.
227              
228             =head1 AUTHOR
229              
230             perlancar <perlancar@cpan.org>
231              
232             =head1 COPYRIGHT AND LICENSE
233              
234             This software is copyright (c) 2016 by perlancar@cpan.org.
235              
236             This is free software; you can redistribute it and/or modify it under
237             the same terms as the Perl 5 programming language system itself.
238              
239             =cut