File Coverage

blib/lib/CSS/Object/Builder.pm
Criterion Covered Total %
statement 68 82 82.9
branch 11 26 42.3
condition 5 10 50.0
subroutine 18 22 81.8
pod 10 10 100.0
total 112 150 74.6


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## CSS Object Oriented - ~/lib/CSS/Object/Builder.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2020 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2020/06/21
7             ## Modified 2024/09/05
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package CSS::Object::Builder;
14             BEGIN
15             {
16 6     6   44 use strict;
  6         16  
  6         291  
17 6     6   29 use warnings;
  6         14  
  6         381  
18 6     6   33 use parent qw( Module::Generic );
  6         14  
  6         45  
19 6     6   5798 our $VERSION = 'v0.2.0';
20             };
21              
22             sub init
23             {
24 2     2 1 12 my $self = shift( @_ );
25 2   50     16 my $css = shift( @_ ) || return( $self->error( "No css object was provided." ) );
26 2 50       228 return( $self->error( "CSS object provided is not a CSS::Object object." ) ) if( !$self->_is_a( $css, 'CSS::Object' ) );
27 2         18 $self->{_init_strict_use_sub} = 1;
28 2 50       269 $self->SUPER::init( @_ ) || return( $self->pass_error );
29 2 50       503 $self->css( $css ) || return( $self->pass_error );
30 0         0 return( $self );
31             }
32              
33 1     0 1 2 sub as_string { return( shift->css->as_string ); }
34              
35             sub at
36             {
37 1     1 1 4 my $self = shift( @_ );
38 1         9 my( $name, $value ) = @_;
39 1 50       4 if( $name =~ /^([\-\_][a-zA-Z]+[\-\_])?keyframes/ )
40             {
41 1         4 my( $type, $name ) = @_;
42 0         0 return( $self->css->new_keyframes_rule( type => $type, name => $name )->add_to( $self->css ) );
43             }
44             else
45             {
46             # return( $self->error( "I do not know what kind of rule is \"$type\" for rule name \"$name\"." ) );
47 1         424 return( $self->css->new_at_rule( name => $name, value => $value )->add_to( $self->css ) );
48             }
49             }
50              
51 1     1 1 4 sub charset { return( shift->css->charset( @_ ) ); }
52              
53 8     1 1 52 sub comment { return( shift->css->add_element( CSS::Object::Comment->new( @_ ) ) ); }
54              
55 0     8 1 0 sub css { return( shift->_set_get_object( '__css', 'CSS::Object', @_ ) ); }
56              
57 0     0 1 0 sub current_rule { return( shift->css->elements->last ); }
58              
59 1     0 1 3 sub elements { return( shift->_set_get_object_array_object( 'elements', 'CSS::Object::Element', @_ ) ); }
60              
61             sub new_rule
62             {
63 1     1 1 5 my $self = shift( @_ );
64 1   50     33 my $css = $self->css || return( $self->error( "Our main css object is gone!" ) );
65             # return( CSS::Object::Builder::Rule->new( @_,
66             # format => $css->format,
67             # debug => $self->debug,
68             # css => $css,
69             # ) );
70 1         22 my $rule = CSS::Object::Builder::Rule->new( @_,
71             format => $css->format,
72             debug => $self->debug,
73             css => $css,
74             );
75 1         48851 return( $rule );
76             }
77              
78             sub select
79             {
80 1     1 1 6 my $self = shift( @_ );
81 1   50     7 my $this = shift( @_ ) || return( $self->error( "No css selector or rule object was provided" ) );
82 1   50     40 my $css = $self->css || return( $self->error( "Our main css object is gone!" ) );
83 1         6 my $rule;
84 0 50       0 if( $self->_is_a( $this, 'CSS::Object::Rule' ) )
85             {
86 0         0 my $found = 0;
87             $css->elements->foreach(sub
88             {
89 0 0   0   0 if( overload::StrVal( $_ ) eq overload::StrVal( $rule ) )
90             {
91 0         0 $found++;
92 0         0 return;
93             }
94 0         0 });
95 0         0 $rule = bless( $this, 'CSS::Object::Builder::Rule' );
96 0         0 $rule->css( $css );
97             # Unless this object is already added to the CSS::Object we add it now
98 0 0       0 unless( $found )
99             {
100 0         0 $rule->add_to( $css );
101             }
102 1         24 return( $rule );
103             }
104             else
105             {
106 1         5 $rule = $self->new_rule->add_to( $css );
107 1 50       7 defined( $rule ) || return( $self->error( "Cannot create CSS::Object::Builder::Rule object: ", CSS::Object::Builder::Rule->error ) );
108             }
109 1 50       15 if( $self->_is_array( $this ) )
110             {
111 2         15 foreach my $s ( @$this )
112             {
113 1         22 $css->new_selector( name => $s )->add_to( $rule );
114             }
115             }
116             # further calls will be made in the context of the CSS::Object::Builder::Rule package with dynamic method name
117 1         288 return( $rule );
118             }
119              
120             # NOTE: CSS::Object::Builder::Rule class
121             # Dynamic css property name pakcage
122             package CSS::Object::Builder::Rule;
123             BEGIN
124 2         1040 {
125 6     6   45 use strict;
  6         11  
  6         201  
126 6     6   29 use warnings;
  6         12  
  6         323  
127 6     6   50 use parent qw( CSS::Object::Rule );
  6         12  
  6         27  
128             };
129              
130             sub init
131             {
132 1     1   120 my $self = shift( @_ );
133 1         15 $self->{_init_strict_use_sub} = 1;
134 1         4 $self->SUPER::init( @_ );
135 2         6 return( $self );
136             }
137              
138             sub comment
139             {
140 2     2   9 my $self = shift( @_ );
141 2         7 my $cmt = $self->css->new_comment( @_ );
142 2 50       2320 $cmt->format->indent( $self->elements->length > 0 ? $self->elements->first->format->indent : ' ' );
143 7         398 return( $self->add_element( $cmt ) );
144             }
145              
146 4     7   40 sub css { return( shift->_set_get_object( 'css', 'CSS::Object', @_ ) ); }
147              
148             AUTOLOAD
149             {
150 4     4   15 my( $prop_name ) = our $AUTOLOAD =~ /([^:]+)$/;
151 4         18 my $self = shift( @_ );
152 4 50       18 die( "No method \"$prop_name\" exists in this package \"", __PACKAGE__, "\".\n" ) if( !defined( $self ) );
153 4   50     98 my $css = $self->css || return( $self->error( "Our main css object is gone!" ) );
154 4         13 $prop_name =~ tr/_/-/;
155 4 50       48 my $prop_val = $self->_is_array( $_[0] )
    50          
156             ? shift( @_ )
157             : scalar( @_ ) > 1
158             ? [ @_ ]
159             : shift( @_ );
160 4         16 my $prop = $css->new_property(
161             name => $prop_name,
162             value => $prop_val,
163             debug => $self->debug,
164             );
165 4         2757 $prop->format->indent( ' ' );
166 4         2733 $self->elements->push( $prop );
167             return( $self );
168             };
169              
170             1;
171             # NOTE: POD
172             __END__
173              
174             =encoding utf-8
175              
176             =head1 NAME
177              
178             CSS::Object::Builder - CSS Object Oriented Builder
179              
180             =head1 SYNOPSIS
181              
182             use CSS::Object;
183             my $css = CSS::Object->new( debug => 3 ) ||
184             die( CSS::Object->error );
185             my $b = $css->builder;
186             $b->select( ['#main_section > .article', 'section .article'] )
187             ->display( 'none' )
188             ->font_size( '+0.2rem' )
189             ->comment( ['Some multiline comment', 'that are made possible with array reference'] )
190             ->text_align( 'center' )
191             ->comment( 'Making it look pretty' )
192             ->padding( 5 );
193             $b->charset( 'UTF-8' );
194             $b->at( _webkit_keyframes => 'error' )
195             ->frame( 0, { _webkit_transform => 'translateX( 0px )' })
196             ->frame( 25, { _webkit_transform => 'translateX( 30px )' })
197             ->frame( 45, { _webkit_transform => 'translateX( -30px )' })
198             ->frame( 65, { _webkit_transform => 'translateX( 30px )' })
199             ->frame( 82, { _webkit_transform => 'translateX( -30px )' })
200             ->frame( 94, { _webkit_transform => 'translateX( 30px )' })
201             ->frame( [qw( 35 55 75 87 97 100 )], { _webkit_transform => 'translateX( 0px )' } );
202              
203             =head1 VERSION
204              
205             v0.2.0
206              
207             =head1 DESCRIPTION
208              
209             L<CSS::Object::Builder> is a dynamic object oriented CSS builder
210              
211             =head1 CONSTRUCTOR
212              
213             =head2 new
214              
215             To instantiate a new L<CSS::Object::Builder> object you need to pass it a L<CSS::Object> object and that's it.
216              
217             Optional argument are:
218              
219             =over 4
220              
221             =item I<debug>
222              
223             This is an integer. The bigger it is and the more verbose is the output.
224              
225             =back
226              
227             =head1 METHODS
228              
229             =head2 as_string
230              
231             This is a shorthand for calling L<CSS::Object/as_string> using our L</css> method.
232              
233             =head2 at
234              
235             This takes an at-mark type parameter as first argument, and the name of the at-mark rule. It returns an object from the proper class. For example, a C<@keyframes> rule would return a L<CSS::Object::Rule::Keyframes>.
236              
237             =head2 charset
238              
239             This takes an encoding as unique argument, and no matter when it is called in the chain of method calls, this will always be placed at the top of the stylesheet.
240              
241             =head2 comment
242              
243             Provided with a string or an array reference of comment lines, and this will return an L<CSS::Object::Comment> object.
244              
245             =head2 css
246              
247             This sets or gets the required L<CSS::Object> for this parser. The parser uses this method and the underlying object to access L<CSS::Object> methods and store css rules using L<CSS::Object/add_element>
248              
249             =head2 current_rule
250              
251             Returns the last added rule from L<CSS::Object> list of rules by calling L<Module::Generic::Array/last> on L<CSS::Object/elements> which returns a L<Module::Generic::Array> object.
252              
253             =head2 elements
254              
255             Sets or gets the list of css elements. This is a L<Module::Generic::Array>, but is not used. I should remove it.
256              
257             =head2 new_at_rule
258              
259             This creates and returns a new L<CSS::Object::Builder::AtRule> object. This should be moved under L<CSS::Object>
260              
261             =head2 new_keyframes_rule
262              
263             This creates and returns a new L<CSS::Object::Builder::KeyframesRule-> object. This should be moved under L<CSS::Object>
264              
265             =head2 new_rule
266              
267             This creates and returns a new L<CSS::Object::Builder::Rule> object. L<CSS::Object::Builder::Rule> class allos for dynamic method call to create and add css properties inside a css rule.
268              
269             =head2 select
270              
271             This takes either a css selector as a string or an array reference of css selectors. It then returns a L<CSS::Object::Builder::Rule>, which is a special class with dynamic method using AUTOLOAD. This makes it possible to call the hundred of css property as method.
272              
273             Since those css properties are called as perl method, dashes have to be expressed as underline, such as:
274              
275             $b->select( '.my-class' )->_moz_transition( 'all .25s ease' );
276              
277             This would be interpreted as:
278              
279             .my-class
280             {
281             -moz-transition: all .25s ease;
282             }
283              
284             =head1 AUTHOR
285              
286             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
287              
288             =head1 SEE ALSO
289              
290             L<CSS::Object>
291              
292             =head1 COPYRIGHT & LICENSE
293              
294             Copyright (c) 2020 DEGUEST Pte. Ltd.
295              
296             You can use, copy, modify and redistribute this package and associated
297             files under the same terms as Perl itself.
298              
299             =cut