File Coverage

blib/lib/Rex/Apache/Build.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4             # vim: set ts=3 sw=3 tw=0:
5             # vim: set expandtab:
6              
7             =head1 NAME
8              
9             Rex::Apache::Build - Build your WebApp Package
10              
11             =head1 DESCRIPTION
12              
13             With this module you can prepare your WebApp for deployment.
14              
15             =head1 SYNOPSIS
16              
17             yui_path "./yuicompressor-2.4.6.jar";
18            
19             get_version_from "webapp/lib/MyApp.pm", qr{\$VERSION=([^;]+);};
20            
21             get_version_from "webapp/index.php", qr{\$VERSION=([^;]+);};
22            
23             task "build", sub {
24             sprocketize;
25             sprocketize "app/assets/javascript/*.js",
26             out => "public/js/sprockets.js";
27            
28             coffee;
29             coffee "app/assets/coffee",
30             out => "public/js";
31            
32             sass;
33             sass "app/assets/stylesheets",
34             out => "public/stylesheets";
35            
36             yui;
37             yui compress => "file1.js", "file2.js", "file3.css";
38             yui compress => glob("public/javascript/*.js"), glob("public/css/*.css");
39            
40             build;
41            
42             build "webapp",
43             source => "webapp/",
44             version => "1.0";
45             };
46              
47              
48             =head1 EXPORTED FUNCTIONS
49              
50             =over 4
51              
52             =cut
53              
54            
55             package Rex::Apache::Build;
56            
57             our $VERSION = "0.11.0";
58              
59 1     1   967 use strict;
  1         2  
  1         43  
60 1     1   5 use warnings;
  1         3  
  1         37  
61              
62 1     1   18 use Cwd qw(getcwd);
  1         3  
  1         65  
63 1     1   6 use File::Basename qw(basename);
  1         1  
  1         93  
64            
65             require Exporter;
66 1     1   6 use base qw(Exporter);
  1         1  
  1         108  
67 1     1   6 use vars qw(@EXPORT);
  1         2  
  1         91  
68            
69             @EXPORT = qw(build
70             get_version_from get_version
71             yui yui_path
72             coffee coffee_path
73             sprocketize sprocketize_path
74             sass sass_path);
75              
76 1     1   5 use vars qw($yui_path $coffee_path $sprocketize_path $sass_path $APP_VERSION);
  1         2  
  1         71  
77              
78 1     1   546 use Rex::Commands::Run;
  0            
  0            
79             use Rex::Logger;
80              
81             =item yui_path($path_to_yui_compressor)
82              
83             This function sets the path to the yui_compressor. If a relative path is given it will search from the path where the Rexfile is in.
84              
85             =cut
86             sub yui_path {
87             ($yui_path) = @_;
88              
89             unless($yui_path =~ m/^\//) {
90             $yui_path = getcwd() . "/" . $yui_path;
91             }
92             }
93              
94             =item coffee_path($path_to_coffee)
95              
96             This function sets the path to the coffee compiler. If a relative path is given it will search from the path where the Rexfile is in.
97              
98             =cut
99             sub coffee_path {
100             ($coffee_path) = @_;
101              
102             unless($coffee_path =~ m/^\//) {
103             $coffee_path = getcwd() . "/" . $coffee_path;
104             }
105             }
106              
107             =item sprocketize_path($path_to_sprocketize)
108              
109             This function sets the path to the sprocketize compiler. If a relative path is given it will search from the path where the Rexfile is in.
110              
111             =cut
112             sub sprocketize_path {
113             ($sprocketize_path) = @_;
114              
115             unless($sprocketize_path =~ m/^\//) {
116             $sprocketize_path = getcwd() . "/" . $sprocketize_path;
117             }
118             }
119              
120             =item sass_path($path_to_sass)
121              
122             This function sets the path to the sass compiler. If a relative path is given it will search from the path where the Rexfile is in.
123              
124             =cut
125             sub sass_path {
126             ($sass_path) = @_;
127              
128             unless($sass_path =~ m/^\//) {
129             $sass_path = getcwd() . "/" . $sass_path;
130             }
131             }
132              
133              
134              
135             =item yui($action, @files)
136              
137             Run a yui command.
138              
139             task "build", sub {
140             # this will compress the given files
141             yui compress => "file1.js", "file2.js", ...;
142            
143             # yui without any parameters will compress all files in public/javascripts
144             yui;
145             };
146              
147             =cut
148             sub yui {
149             my ($action, @data) = @_;
150              
151             $yui_path ||= "yuicompressor.jar";
152              
153             unless(-f $yui_path) {
154             die("No yuicompressor.jar found. Please download this file and define its location with yui_path '/path/to/yuicompress.jar';");
155             }
156              
157             unless($action) {
158             $action = "compress";
159             }
160              
161             unless(@data) {
162             @data = glob("public/javascripts/*.js");
163             }
164              
165             if($action eq "compress" || $action eq "-compress") {
166             my @js_files = grep { ! /\.min\.js$/ } grep { /\.js$/i } @data;
167             my @css_files = grep { ! /\.min\.css$/ } grep { /\.css$/i } @data;
168              
169             if(@js_files) {
170             Rex::Logger::info("Compressing javascript files");
171             for my $file (@js_files) {
172             my $new_file = $file;
173             $new_file =~ s/\.js$/.min.js/;
174             Rex::Logger::debug("Compressing $file -> $new_file");
175             run "java -jar $yui_path -o $new_file $file";
176             }
177             }
178              
179             if(@css_files) {
180             Rex::Logger::info("Compressing css files");
181             for my $file (@css_files) {
182             my $new_file = $file;
183             $new_file =~ s/\.css$/.min.css/;
184             Rex::Logger::debug("Compressing $file -> $new_file");
185             run "java -jar $yui_path -o $new_file $file";
186             }
187             }
188             }
189             else {
190             die("Action $action not supported.");
191             }
192             }
193              
194             =item build([$name, %options])
195              
196             This function builds your package. Currently only tar.gz packages are supported.
197              
198             # this will a package of the current directory named after the
199             # directory of the Rexfile and append the version provided by
200             # get_version_from() function
201             # This function builds a tar.gz archive.
202             task "build", sub {
203             build;
204             };
205            
206             # this will build a package of the current directory named "my-web-app" and
207             # append the version provided by get_version_from() function.
208             task "build", sub {
209             build "my-web-app";
210             };
211            
212             # this function will build a package of the directory "html", name it
213             # "my-web-app" and append the version "1.0" to it.
214             task "build", sub {
215             build "my-web-app",
216             path => "html",
217             version => "1.0",
218             exclude => ["yuicompressor.jar", "foobar.html"],
219             type => "tgz";
220             };
221              
222             =cut
223             sub build {
224             my ($name, %option) = @_;
225              
226             unless($name) {
227             $name = basename(getcwd());
228             }
229              
230             if(! %option) {
231             if(Rex::Config->get("package_option")) {
232             %option = %{ Rex::Config->get("package_option") };
233             }
234             }
235              
236             if(! exists $option{version}) {
237             $option{version} = &$APP_VERSION();
238             }
239              
240             my $old_dir = getcwd();
241              
242             my $type = $option{type} || "tgz";
243              
244             my $klass = "Rex::Apache::Build::$type";
245             eval "use $klass";
246             if($@) {
247             die("Can't find build class for type: $type");
248             }
249              
250             Rex::Logger::debug("Using Buildclass: $klass");
251             $option{name} = $name;
252             my $build = $klass->new(%option);
253              
254             $build->build;
255              
256             chdir($old_dir);
257             }
258              
259             =item get_version_from($file, $regexp)
260              
261             Get the version out of a file.
262              
263             =cut
264             sub get_version_from {
265             my ($file, $regex) = @_;
266              
267             if(ref($file) eq "CODE") {
268             $APP_VERSION = $file;
269             return;
270             }
271              
272             $APP_VERSION = sub {
273              
274             unless(-f $file) {
275             Rex::Logger::info("Version file not found ($file). Current Path: " . getcwd() . ". Using current time.");
276             return "" . time;
277             }
278              
279             my ($version) = grep { $_=$1 if $_ =~ $regex; } eval { local(@ARGV) = ($file); <>; };
280            
281             return $version;
282              
283             };
284             }
285              
286             sub get_version {
287             return &$APP_VERSION();
288             }
289              
290              
291             =item sprocketize($path_to_js_files, %option)
292              
293             This function calls the sprocketize command with the given options.
294              
295             task "build", sub {
296             sprocketize "app/javascript/*.js",
297             include => [qw|app/javascripts vendor/sprockets/prototype/src|],
298             asset_root => "public/js",
299             out => "public/js/sprockets.js";
300            
301             # to include more use an arrayRef
302             sprocketize ["app/javascript/*.js", "app/javascript/po/*.js"],
303             include => [qw|app/javascripts vendor/sprockets/prototype/src|],
304             asset_root => "public/js",
305             out => "public/js/sprockets.js";
306            
307             # if called without parameters
308            
309             sprocketize;
310            
311             # it will use the following defaults:
312             # - javascript (sprockets) in assets/javascripts/*.js
313             # - include assets/javascripts
314             # - asset_root public
315             # - out public/${name_of_directory_where_Rexfile_lives}.js
316             };
317              
318             =cut
319              
320             sub sprocketize {
321             my ($files, %option) = @_;
322              
323             my $dirname = basename(getcwd());
324              
325             unless($sprocketize_path) {
326             $sprocketize_path = "sprocketize";
327             }
328              
329             unless($files) {
330             $files = ["assets/javascripts/*.js"];
331             }
332              
333             if(! exists $option{out}) {
334             $option{out} = "public/$dirname.js";
335             }
336              
337             if(! exists $option{asset_root}) {
338             $option{asset_root} = "public";
339             }
340              
341             if(! exists $option{include}) {
342             $option{include} = ["app/javascripts"];
343             }
344              
345             if(ref($files) ne "ARRAY") {
346             $files = [ $files ];
347             }
348              
349             my $files_str = join(" ", @{$files});
350             my $includes = " -I " . join(" -I ", @{$option{include}});
351              
352             Rex::Logger::info("Sprocketizing...");
353             run "$sprocketize_path $includes --asset-root=" . $option{asset_root} . " $files_str > " . $option{out};
354             if($? == 0) {
355             Rex::Logger::info("...done.");
356             }
357             else {
358             Rex::Logger::info("Error running sprocketize");
359             die("Error running sprocketize");
360             }
361             }
362              
363             =item coffee($path, %options)
364              
365             Compile coffee files to javascript.
366              
367             task "build", sub {
368             # this command will build all files in "coffeesrc" and
369             # write the output to "javascripts"
370             coffee "coffeesrc",
371             out => "javascripts";
372            
373             # without parameters it will build all files in assets/coffee
374             # and write the output to public/javascripts.
375             coffee;
376             };
377              
378             =cut
379             sub coffee {
380             my ($path, %option) = @_;
381              
382             unless($coffee_path) {
383             $coffee_path = "coffee";
384             }
385              
386             unless($path) {
387             $path = "assets/coffee";
388             }
389              
390             if(! exists $option{out}) {
391             $option{out} = "public/javascripts";
392             }
393              
394             Rex::Logger::info("Building coffee script files...");
395             my $ret = run "coffee -o " . $option{out} . " -c " . $path . " 2>&1";
396              
397             if($? == 0) {
398             Rex::Logger::info("...done.");
399             }
400             else {
401             Rex::Logger::info("Error building coffeescripts. $ret");
402             die("Error building coffeescripts.");
403             }
404             }
405              
406             =item sass($input_dir, %option)
407              
408             This command will compile all sass files in $input_dir.
409              
410             task "build", sub {
411             # this command will compile all sass files from app/assets/stylesheets
412             # and put the output into public/stylesheets.
413             sass "app/assets/stylesheets",
414             out => "public/stylesheets";
415            
416             # The default is to build all files in assets/sass and put the output
417             # into public/css.
418             sass;
419             };
420              
421             =cut
422             sub sass {
423             my ($input, %option) = @_;
424              
425             unless($sass_path) {
426             $sass_path = "sass";
427             }
428              
429             unless($input) {
430             $input = "assets/sass";
431             }
432              
433             if(! exists $option{out}) {
434             $option{out} = "public/css";
435             }
436              
437             Rex::Logger::info("Building sass files...");
438             my $ret = run "$sass_path -q --update $input:" . $option{out} . " 2>&1";
439              
440             if($? == 0) {
441             Rex::Logger::info("...done.");
442             }
443             else {
444             Rex::Logger::info("Failed building sass files. $ret");
445             die("Failed building sass files.");
446             }
447             }
448              
449             =back
450              
451             =cut
452              
453             1;