File Coverage

blib/lib/CSS/Tiny.pm
Criterion Covered Total %
statement 75 80 93.8
branch 15 24 62.5
condition 2 2 100.0
subroutine 14 16 87.5
pod 9 9 100.0
total 115 131 87.8


line stmt bran cond sub pod time code
1             package CSS::Tiny;
2              
3             =pod
4            
5             =head1 NAME
6            
7             CSS::Tiny - Read/Write .css files with as little code as possible
8            
9             =head1 SYNOPSIS
10            
11             # In your .css file
12             H1 { color: blue }
13             H2 { color: red; font-family: Arial }
14             .this, .that { color: yellow }
15            
16             # In your program
17             use CSS::Tiny;
18            
19             # Create a CSS stylesheet
20             my $CSS = CSS::Tiny->new();
21            
22             # Open a CSS stylesheet
23             $CSS = CSS::Tiny->read( 'style.css' );
24            
25             # Reading properties
26             my $header_color = $CSS->{H1}->{color};
27             my $header2_hashref = $CSS->{H2};
28             my $this_color = $CSS->{'.this'}->{color};
29             my $that_color = $CSS->{'.that'}->{color};
30            
31             # Changing styles and properties
32             $CSS->{'.newstyle'} = { color => '#FFFFFF' }; # Add a style
33             $CSS->{H1}->{color} = 'black'; # Change a property
34             delete $CSS->{H2}; # Delete a style
35            
36             # Save a CSS stylesheet
37             $CSS->write( 'style.css' );
38            
39             # Get the CSS as a <style>...</style> tag
40             $CSS->html;
41            
42             =head1 DESCRIPTION
43            
44             C<CSS::Tiny> is a perl class to read and write .css stylesheets with as
45             little code as possible, reducing load time and memory overhead. CSS.pm
46             requires about 2.6 meg or ram to load, which is a large amount of
47             overhead if you only want to do trivial things.
48             Memory usage is normally scoffed at in Perl, but in my opinion should be
49             at least kept in mind.
50            
51             This module is primarily for reading and writing simple files, and anything
52             we write shouldn't need to have documentation/comments. If you need
53             something with more power, move up to CSS.pm. With the increasing complexity
54             of CSS, this is becoming more common, but many situations can still live
55             with simple CSS files.
56            
57             =head2 CSS Feature Support
58            
59             C<CSS::Tiny> supports grouped styles of the form
60             C<this, that { color: blue }> correctly when reading, ungrouping them into
61             the hash structure. However, it will not restore the grouping should you
62             write the file back out. In this case, an entry in the original file of
63             the form
64            
65             H1, H2 { color: blue }
66            
67             would become
68            
69             H1 { color: blue }
70             H2 { color: blue }
71            
72             C<CSS::Tiny> handles nested styles of the form C<P EM { color: red }>
73             in reads and writes correctly, making the property available in the
74             form
75            
76             $CSS->{'P EM'}->{color}
77            
78             C<CSS::Tiny> ignores comments of the form C</* comment */> on read
79             correctly, however these comments will not be written back out to the
80             file.
81            
82             =head1 CSS FILE SYNTAX
83            
84             Files are written in a relatively human-orientated form, as follows:
85            
86             H1 {
87             color: blue;
88             }
89             .this {
90             color: red;
91             font-size: 10px;
92             }
93             P EM {
94             color: yellow;
95             }
96            
97             When reading and writing, all property descriptors, for example C<color>
98             and C<font-size> in the example above, are converted to lower case. As an
99             example, take the following CSS.
100            
101             P {
102             Font-Family: Verdana;
103             }
104            
105             To get the value C<'Verdana'> from the object C<$CSS>, you should
106             reference the key C<$CSS-E<gt>{P}-E<gt>{font-family}>.
107            
108             =head1 METHODS
109            
110             =cut
111              
112 3     3   78 use 5.005;
  3         43  
  3         57  
113 3     3   76 use strict;
  3         28  
  3         52  
114              
115 3     3   43 use vars qw{$VERSION $errstr};
  3         29  
  3         46  
116             BEGIN {
117 3     3   46 $VERSION = '1.14';
118 3         32 $errstr  = '';
119             }
120              
121             =pod
122            
123             =head2 new
124            
125             The constructor C<new> creates and returns an empty C<CSS::Tiny> object.
126            
127             =cut
128              
129 3     3 1 41 sub new { bless {}, shift }
130              
131             =pod
132            
133             =head2 read $filename
134            
135             The C<read> constructor reads a CSS stylesheet, and returns a new
136             C<CSS::Tiny> object containing the properties in the file.
137            
138             Returns the object on success, or C<undef> on error.
139            
140             =cut
141              
142             sub read {
143 3     3 1 29 my $class = shift;
144              
145             # Check the file
146 3 50       36 my $file = shift or return $class->_error( 'You did not specify a file name' );
147 3 50       83 return $class->_error( "The file '$file' does not exist" ) unless -e $file;
148 3 50       219 return $class->_error( "'$file' is a directory, not a file" ) unless -f _;
149 3 50       32 return $class->_error( "Insufficient permissions to read '$file'" ) unless -r _;
150              
151             # Read the file
152 3         38 local $/ = undef;
153 3 50       276 open( CSS, $file ) or return $class->_error( "Failed to open file '$file': $!" );
154 3         2109 my $contents = <CSS>;
155 3         132 close( CSS );
156              
157 3         52 $class->read_string( $contents )
158             }
159              
160             =pod
161            
162             =head2 read_string $string
163            
164             The C<read_string> constructor reads a CSS stylesheet from a string.
165            
166             Returns the object on success, or C<undef> on error.
167            
168             =cut
169              
170             sub read_string {
171 5     5 1 77 my $self = bless {}, shift;
172              
173             # Flatten whitespace and remove /* comment */ style comments
174 5         79 my $string = shift;
175 5         152 $string =~ tr/\n\t/ /;
176 5         51 $string =~ s!/\*.*?\*\/!!g;
177              
178             # Split into styles
179 5         153 foreach ( grep { /\S/ } split /(?<=\})/, $string ) {
  21         247  
180 16 50       314 unless ( /^\s*([^{]+?)\s*\{(.*)\}\s*$/ ) {
181 0         0 return $self->_error( "Invalid or unexpected style data '$_'" );
182             }
183              
184             # Split in such a way as to support grouped styles
185 16         179 my $style = $1;
186 16         150 $style =~ s/\s{2,}/ /g;
187 16         168 my @styles = grep { s/\s+/ /g; 1; } grep { /\S/ } split /\s*,\s*/, $style;
  18         157  
  18         180  
  18         191  
188 16   100     150 foreach ( @styles ) { $self->{$_} ||= {} }
  18         1168  
189              
190             # Split into properties
191 16         216 foreach ( grep { /\S/ } split /\;/, $2 ) {
  30         302  
192 22 50       305 unless ( /^\s*([\w._-]+)\s*:\s*(.*?)\s*$/ ) {
193 0         0 return $self->_error( "Invalid or unexpected property '$_' in style '$style'" );
194             }
195 22         187 foreach ( @styles ) { $self->{$_}->{lc $1} = $2 }
  24         419  
196             }
197             }
198              
199             $self
200 5         74 }
201              
202             =pod
203            
204             =head2 clone
205            
206             The C<clone> method creates an identical copy of an existing C<CSS::Tiny>
207             object.
208            
209             =cut
210              
211 3 100   3 1 38 BEGIN { eval "use Clone 'clone';"; eval <<'END_PERL' if $@; }
  3     3   372  
  3     1   185  
  3         44  
  3         67  
  1         28  
  1         15  
  1         27  
  5         43  
  5         74  
  5         51  
  6         75  
  1         13  
212             sub clone {
213             my $self = shift;
214             my $copy = ref($self)->new;
215             foreach my $key ( keys %$self ) {
216             my $section = $self->{$key};
217             $copy->{$key} = {};
218             foreach ( keys %$section ) {
219             $copy->{$key}->{$_} = $section->{$_};
220             }
221             }
222             $copy;
223             }
224             END_PERL
225              
226             =pod
227            
228             =head2 write
229            
230             The C<write $filename> generates the stylesheet for the properties, and
231             writes it to disk. Returns true on success. Returns C<undef> on error.
232            
233             =cut
234              
235             sub write {
236 1     1 1 10 my $self = shift;
237 1 50       12 my $file = shift or return $self->_error( 'No file name provided' );
238              
239             # Write the file
240 1 50       144 open( CSS, '>'. $file ) or return $self->_error( "Failed to open file '$file' for writing: $!" );
241 1         13 print CSS $self->write_string;
242 1         124 close( CSS );
243              
244 1         12 1
245             }
246              
247             =pod
248            
249             =head2 write_string
250            
251             Generates the stylesheet for the object and returns it as a string.
252            
253             =cut
254              
255             sub write_string {
256 6     6 1 58 my $self = shift;
257              
258             # Iterate over the styles
259             # Note: We use 'reverse' in the sort to avoid a special case related
260             # to A:hover even though the file ends up backwards and looks funny.
261             # See http://www.w3.org/TR/CSS2/selector.html#dynamic-pseudo-classes
262 6         53 my $contents = '';
263 6         124 foreach my $style ( reverse sort keys %$self ) {
264 8         75 $contents .= "$style {\n";
265 8         64 foreach ( sort keys %{ $self->{$style} } ) {
  8         93  
266 12         193 $contents .= "\t" . lc($_) . ": $self->{$style}->{$_};\n";
267             }
268 8         84 $contents .= "}\n";
269             }
270              
271             $contents
272 6         135 }
273              
274             =pod
275            
276             =head2 html
277            
278             The C<html> method generates the CSS, but wrapped in a C<style> HTML tag,
279             so that it can be dropped directly onto a HTML page.
280            
281             =cut
282              
283             sub html {
284 2 100   2 1 25 my $css = $_[0]->write_string or return '';
285 1         17 "<style type=\"text/css\">\n<!--\n${css}-->\n</style>";
286             }
287              
288             =pod
289            
290             =head2 xhtml
291            
292             The C<html> method generates the CSS, but wrapped in a C<style> XHTML tag,
293             so that it can be dropped directly onto an XHTML page.
294            
295             =cut
296              
297             sub xhtml {
298 2 100   2 1 32 my $css = $_[0]->write_string or return '';
299 1         19 "<style type=\"text/css\">\n/* <![CDATA[ */\n${css}/* ]]> */\n</style>";
300             }
301              
302             =pod
303            
304             =head2 errstr
305            
306             When an error occurs, you can retrieve the error message either from the
307             C<$CSS::Tiny::errstr> variable, or using the C<errstr> method.
308            
309             =cut
310              
311 0     0 1 0 sub errstr { $errstr }
312 0     0   0 sub _error { $errstr = $_[1]; undef }
  0         0  
313              
314             1;
315              
316             =pod
317            
318             =head1 SUPPORT
319            
320             Bugs should be reported via the CPAN bug tracker at
321            
322             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CSS-Tiny>
323            
324             For other issues, or commercial enhancement or support, contact the author.
325            
326             =head1 AUTHOR
327            
328             Adam Kennedy E<lt>cpan@ali.asE<gt>
329            
330             =head1 SEE ALSO
331            
332             L<CSS>, L<http://www.w3.org/TR/REC-CSS1>, L<Config::Tiny>, L<http://ali.as/>
333            
334             =head1 COPYRIGHT
335            
336             Copyright 2002 - 2006 Adam Kennedy. All rights reserved.
337            
338             This program is free software; you can redistribute
339             it and/or modify it under the same terms as Perl itself.
340            
341             The full text of the license can be found in the
342             LICENSE file included with this module.
343            
344             =cut
345