File Coverage

blib/lib/CSS/Object/Builder.pm
Criterion Covered Total %
statement 76 91 83.5
branch 11 26 42.3
condition 5 10 50.0
subroutine 20 25 80.0
pod 10 10 100.0
total 122 162 75.3


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