File Coverage

blib/lib/CSS/Object/Builder.pm
Criterion Covered Total %
statement 74 89 83.1
branch 11 26 42.3
condition 5 10 50.0
subroutine 20 25 80.0
pod 10 10 100.0
total 120 160 75.0


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