File Coverage

blib/lib/CSS/LESS.pm
Criterion Covered Total %
statement 86 109 78.9
branch 24 38 63.1
condition 15 24 62.5
subroutine 11 12 91.6
pod 4 4 100.0
total 140 187 74.8


line stmt bran cond sub pod time code
1             package CSS::LESS;
2              
3 5     5   150394 use warnings;
  5         10  
  5         137  
4 5     5   26 use strict;
  5         9  
  5         140  
5 5     5   25 use Carp;
  5         14  
  5         445  
6 5     5   6594 use File::Temp qw//;
  5         155490  
  5         129  
7 5     5   4547 use IPC::Open3;
  5         15088  
  5         324  
8              
9 5     5   4136 use version; our $VERSION = qv('0.0.3');
  5         11603  
  5         33  
10              
11             our @LESSC_PARAMS = qw/ include_paths compress strict_imports
12             relative_urls rootpath line_numbers version /;
13             our %DEFAULT_PARAMS = (
14             # Parameters for module
15             dont_die => 0,
16             dry_run => 0,
17             path_lessc_bin => 'lessc',
18             path_tmp => undef,
19             # Parameters for lessc
20             include_paths => undef, # Array
21             compress => 'false', # Bool
22             strict_imports => 'false', # Bool
23             relative_urls => 'false', # Bool
24             rootpath => undef, # String (URL)
25             line_numbers => undef, # String ('comments', 'mediaquery', 'both', or undef)
26             );
27              
28             sub new {
29 8     8 1 6306 my ($class, %params) = @_;
30 8         28 my $s = bless({}, $class);
31              
32             # Proceess for parameters of constructor
33 8         74 foreach ( keys %DEFAULT_PARAMS ) {
34 80 100       185 if(defined $params{$_}){
35 11         32 $s->{$_} = $params{$_};
36             } else {
37 69         210 $s->{$_} = $DEFAULT_PARAMS{$_};
38             }
39             }
40              
41 8         30 $s->{last_error} = undef;
42 8         24 $s->{is_lessc_installed} = undef;
43              
44 8         32 return $s;
45             }
46              
47             # Ccompile a less style-sheet (return: Compiled CSS style-sheet)
48             sub compile {
49 7     7 1 1734 my ($s, $buf, %params) = @_;
50              
51 7 50       28 unless (defined $s->{is_lessc_installed} ) {
52 7 100 66     19 if($s->is_lessc_installed() == 0 && $s->{dont_die} == 0) {
53 1         47 die('lessc is not installed');
54             }
55             }
56              
57 6 100       21 unless (%params) {
58 3         4 %params = ();
59             }
60              
61             # Process for parameters (Set property in instance)
62 6         21 foreach ( keys %DEFAULT_PARAMS ) {
63 60 100 100     207 if(!defined $params{$_} && defined $s->{$_}){ # Not defined on params AND defined on member of $self
64 35         59 $params{$_} = $s->{$_};
65             }
66             }
67 6         15 $params{content} = $buf;
68              
69             # Compile less to css
70 6         21 return $s->_exec_lessc(%params);
71             }
72              
73             # Get last error
74             sub last_error {
75 0     0 1 0 my $s = shift;
76 0         0 return $s->{last_error};
77             }
78              
79             # Check for lessc has installed
80             sub is_lessc_installed {
81 11     11 1 74 my $s = shift;
82              
83 11 100       61 if($s->{dry_run}){ # Dry run
84 7         41 return 1;
85             }
86              
87 4         8 my $lessc_ver;
88 4         9 eval {
89 4         19 $lessc_ver = $s->_exec_lessc(version => undef);
90 4 50       30108 }; if($@) { return 0; }
  4         233  
91 0 0 0     0 if(defined $lessc_ver && $lessc_ver =~ /^lessc .*(LESS Compiler).*/i) {
92 0         0 $s->{is_lessc_installed} = 1;
93 0         0 return 1;
94             }
95 0         0 return 0;
96             }
97              
98             # Execute a command with lessc
99             sub _exec_lessc {
100 10     10   33 my ($s, %options) = @_;
101              
102             # Prepare a command
103 10         54 my ($cmd_args_ref, $path_tmpfile) = $s->_generate_cmd_lessc(%options);
104 10         16 my @cmd_args = @{$cmd_args_ref};
  10         30  
105              
106             # Execute a command
107              
108 10 100       44 if($s->{dry_run}){ # Dry run
109 6         8 $" = ' ';
110 6         47 return "@cmd_args"; # return generated command
111             }
112              
113 4         10 my ($fh_in, $fh_out, $fh_err);
114             #open $fh, '-|', @cmd_args, '2>&1' or die('Can not open a pipe to:'. $s->{path_lessc_bin});
115 4         31 my $pid = IPC::Open3::open3($fh_in, $fh_out, 0, @cmd_args);
116 0         0 my ($ret);
117 0         0 while (my $l = <$fh_out>) {
118 0         0 $ret .= $l;
119             }
120 0         0 waitpid($pid, 0);
121              
122             # Error process
123 0 0       0 if($? != 0){
124 0         0 $s->{last_error} = $ret;
125 0 0       0 unless($s->{dont_die}){
126 0 0       0 if(defined $ret){
127 0         0 die ('Compile error: '. $ret);
128             } else {
129 0         0 die ('Compile error: Unknown');
130             }
131             }
132             }
133             # Delete tmp file
134 0 0       0 if(defined $path_tmpfile){
135 0         0 unlink($path_tmpfile);
136             }
137              
138 0         0 return $ret;
139             }
140              
141             # Generate a command for lessc (Return: \@args, $path of temp-file)
142             sub _generate_cmd_lessc {
143 10     10   34 my ($s, %options) = @_;
144 10         19 my @cmd_args = ();
145              
146             # Execute path
147 10         24 push(@cmd_args, $s->{path_lessc_bin});
148              
149             # Process for content
150 10         15 my $path_tmpfile;
151 10 100       32 if(defined $options{content}) {
152 6         8 my $content = $options{content};
153 6         10 delete $options{content};
154              
155 6         4 my $tempfh;
156 6         25 ($tempfh, $path_tmpfile) = File::Temp::tempfile(DIR => $s->{path_tmp});
157 6         2411 print $tempfh $content;
158 6         305 close($tempfh);
159              
160 6         24 push(@cmd_args, $path_tmpfile);
161             }
162              
163             # Process for include paths
164 10 100       42 if(defined $options{include_paths}){
165 2 50       17 if(@{$options{include_paths}} <= 1){
  2         7  
166 0         0 push(@cmd_args, '--include-path='.$options{include_paths}->[0]);
167             } else {
168 2         2 my $paths = '--include-path=';
169             {
170 2         3 local $" = ':';
  2         4  
171 2         3 $paths .= "@{$options{include_paths}}";
  2         8  
172             }
173 2         3 $paths .= '';
174 2         4 push(@cmd_args, $paths);
175             }
176 2         5 delete $options{include_paths};
177             }
178              
179             # Process for other parameters
180 10         34 foreach my $key (@LESSC_PARAMS) {
181 70 50 33     529 if(defined $options{$key} && (!defined $DEFAULT_PARAMS{$key} || $DEFAULT_PARAMS{$key} ne 'false')) {
    100 66        
    100 100        
      66        
      66        
182 0         0 my $arg_name = $key;
183 0         0 $arg_name =~ s/\_/\-/g;
184 0         0 push(@cmd_args, "--".$arg_name."=".$options{$key});
185             } elsif(defined $options{$key} && ($options{$key} eq 'false' || $options{$key} == '0') && $DEFAULT_PARAMS{$key} eq 'false') {
186             # Do not anything
187             } elsif(exists $options{$key}){
188 7         17 my $arg_name = $key;
189 7         30 $arg_name =~ s/\_/\-/g;
190 7         26 push(@cmd_args, "--".$arg_name);
191             }
192             }
193              
194 10         20 push(@cmd_args, '--verbose');
195 10         31 push(@cmd_args, '--no-color');
196             # Return a args (with command path) and a path of temp-file
197 10         42 return (\@cmd_args, $path_tmpfile);
198             }
199              
200             1;
201             __END__