File Coverage

blib/lib/Mom.pm
Criterion Covered Total %
statement 44 66 66.6
branch 14 28 50.0
condition 0 4 0.0
subroutine 8 8 100.0
pod n/a
total 66 106 62.2


line stmt bran cond sub pod time code
1 1     1   55764 use 5.008008;
  1         3  
2 1     1   5 use strict;
  1         1  
  1         16  
3 1     1   4 use warnings;
  1         2  
  1         52  
4              
5             package Mom;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.004';
9              
10 1     1   385 use parent qw( MooX::Press );
  1         233  
  1         5  
11 1     1   126692 use Carp qw();
  1         2  
  1         14  
12 1     1   4 use Import::Into;
  1         2  
  1         16  
13 1     1   484 use Regexp::Common;
  1         2182  
  1         3  
14              
15             my $token_re = qr{(?:
16             (?: [^0-9\W]\w* )
17             | \: (?: isa|enum|does|type|handles|with|extends|default|requires|builder|trigger|clearer ) $RE{balanced}
18             | \: (?: rw|ro|rwp|bare|private|lazy|required|clearer|builder|trigger|role|std|common|path )
19             )}x;
20              
21             sub import {
22 1     1   8 my $me = shift;
23 1         3 my $caller = caller;
24 1         7 my $import = join q( ), @_;
25            
26 1         2 my $attr;
27 1         3 my %opts = ( factory => undef );
28 1         1 my $kind = 'class';
29 1         2 my %import_types;
30            
31 1         2 $import =~ s/\s+//;
32 1         139 while ( $import =~ /^($token_re)/ ) {
33            
34 1         5 my $token = $1;
35 1         3 $import = substr($import, length($token));
36 1         2 $import =~ s/\s+//;
37            
38 1 50       3 if ( $token =~ /^:(role)$/ ) {
39 0         0 $kind = 'role';
40             }
41 1 50       10 if ( $token =~ /^:(common|std|path)$/ ) {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
42 0         0 $import_types{$1} = 1;
43             }
44             elsif ( $token =~ /^:(extends|with).(.+).$/ ) {
45 0         0 $opts{$1} = [ split /\s*,\s*/, $2 ];
46             }
47             elsif ( $token =~ /^:(rw|ro|rwp|bare|private)$/ ) {
48 0         0 $opts{has}{$attr}{is} = $1;
49             }
50             elsif ( $token =~ /^:(lazy)$/ ) {
51 0         0 $opts{has}{$attr}{$1} = 1;
52             }
53             elsif ( $token =~ /^:(required|clearer|trigger|builder)$/ ) {
54 0         0 $opts{has}{$attr}{$1} = 1;
55             }
56             elsif ( $token =~ /^:(enum|handles).(.+).$/ ) {
57 0   0     0 push @{ $opts{has}{$attr}{$1} ||= [] }, split /\s*,\s*/, $2;
  0         0  
58             }
59             elsif ( $token =~ /^:(isa|does|type|default|builder|trigger|clearer).(.+).$/ ) {
60 0         0 $opts{has}{$attr}{$1} = $2;
61             }
62             elsif ( $token =~ /^:(requires).(.+).$/ ) {
63 0   0     0 push @{ $opts{requires} ||= [] }, split /\s*,\s*/, $2;
  0         0  
64             }
65             else {
66 1         7 $opts{has}{$attr = $token} = {};
67             }
68             }
69            
70 1 50       3 if ( $import ) {
71 0         0 Carp::croak("Unrecognized syntax: $import");
72             }
73            
74             $me->SUPER::import(
75 1         15 factory_package => $me,
76             type_library => "$me\::Types",
77             prefix => undef,
78             $kind => [ $caller => \%opts ],
79             );
80 1 50       104091 ($kind eq 'role' ? 'Moo::Role' : 'Moo')->_install_subs($caller);
81 1         151 'Scalar::Util'->import::into($caller, qw(blessed));
82 1         196 'Carp'->import::into($caller, qw(croak confess carp));
83            
84 1 50       162 if ($import_types{std}) {
85 0         0 require Types::Standard;
86 0         0 'Types::Standard'->import::into($caller, qw(-types -is -assert));
87             }
88            
89 1 50       4 if ($import_types{common}) {
90 0         0 require Types::Common::Numeric;
91 0         0 'Types::Common::Numeric'->import::into($caller, qw(-types -is -assert));
92 0         0 require Types::Common::String;
93 0         0 'Types::Common::String'->import::into($caller, qw(-types -is -assert));
94             }
95            
96 1 50       3 if ($import_types{path}) {
97 0         0 require Types::Path::Tiny;
98 0         0 'Types::Path::Tiny'->import::into($caller, qw(-types -is -assert));
99 0         0 require Path::Tiny;
100 0         0 'Path::Tiny'->import::into($caller);
101             }
102            
103 1         5 'namespace::autoclean'->import::into($caller);
104             }
105              
106             1;
107              
108             __END__
109              
110             =pod
111              
112             =encoding utf-8
113              
114             =head1 NAME
115              
116             Mom - Moo objects minimally
117              
118             =head1 SYNOPSIS
119              
120             This:
121              
122             use Mom;
123              
124             Is (roughly) a shortcut for:
125              
126             use Moo;
127             use Scalar::Util qw( blessed );
128             use Carp qw( carp croak confess );
129             use namespace::autoclean;
130              
131             But Mom takes care of a lot more. This:
132              
133             use Mom q{
134             foo
135             bar :rw :type(Int)
136             baz :required
137             };
138              
139             Is (roughly) a shortcut for:
140              
141             use Moo;
142             use Scalar::Util qw( blessed );
143             use Carp qw( carp croak confess );
144             use Types::Standard qw();
145             use namespace::autoclean;
146            
147             has foo => ( is => "ro" );
148             has bar => ( is => "rw", isa => Types::Standard::Int );
149             has baz => ( is => "ro", required => 1 );
150              
151             Tokens which don't start with a colon are created as attributes in
152             your package. Tokens starting with a colon are flags that affect either
153             the preceeding attribute or the package as a whole.
154              
155             =head1 DESCRIPTION
156              
157             This documentation assumes familiarity with L<Moo>.
158              
159             =head2 Motivation
160              
161             The documentation for L<MooX::ShortHas> says instead of this:
162              
163             use Moo;
164            
165             has hro => is => ro => required => 1;
166             has hlazy => is => lazy => builder => sub { 2 };
167             has hrwp => is => rwp => required => 1;
168             has hrw => is => rw => required => 1;
169              
170             You can now write this:
171              
172             use Moo;
173             use MooX::ShortHas;
174            
175             ro "hro";
176             lazy hlazy => sub { 2 };
177             rwp "hrwp";
178             rw "hrw";
179              
180             I thought I could go even shorter.
181              
182             use Mom q{
183             hro :required
184             hlazy :lazy :default(2)
185             hrwp :required :rwp
186             hrw :required :rw
187             };
188              
189             Is it a lot shorter? No, but that's mostly because MooX::ShortHas makes it
190             very concise to write required attributes, and uses that in the demonstration
191             but less concise for optional attributes. If we make the attributes optional,
192             it becomes shorter.
193              
194             use Mom q{ hro hlazy :lazy :default(2) hrwp :rwp hrw :rw };
195              
196             =head1 IMPORT
197              
198             All of Mom's magic happens in the import statement.
199              
200             =head2 Flags Affecting Attributes
201              
202             =over
203              
204             =item C<< :rw >>
205              
206             Like C<< is => "rw" >> in Moo.
207              
208             =item C<< :ro >>
209              
210             Like C<< is => "ro" >> in Moo, though this is already the default.
211              
212             =item C<< :rwp >>
213              
214             Like C<< is => "rwp" >> in Moo
215              
216             =item C<< :bare >>
217              
218             Like C<< is => "bare" >> in Moo
219              
220             =item C<< :lazy >>
221              
222             Like C<< lazy => 1 >> in Moo.
223              
224             =item C<< :required >>
225              
226             Like C<< required => 1 >> in Moo.
227              
228             =item C<< :clearer >>
229              
230             Like C<< clearer => 1 >> in Moo.
231              
232             =item C<< :clearer(methodname) >>
233              
234             Like C<< clearer => "methodname" >> in Moo.
235              
236             =item C<< :builder >>
237              
238             Like C<< builder => 1 >> in Moo.
239              
240             =item C<< :builder(methodname) >>
241              
242             Like C<< builder => "methodname" >> in Moo.
243              
244             =item C<< :trigger >>
245              
246             Like C<< trigger => 1 >> in Moo.
247              
248             =item C<< :trigger(methodname) >>
249              
250             Like C<< trigger => "methodname" >> in Moo.
251              
252             =item C<< :isa(Class::Name) >>
253              
254             Like C<< isa => InstanceOf[Class::Name] >> in Moo/Types::Standard.
255              
256             =item C<< :does(Role::Name) >>
257              
258             Like C<< isa => ConsumerOf[Role::Name] >> in Moo/Types::Standard.
259              
260             =item C<< :type(TypeName) >>
261              
262             Like C<< isa => TypeName >> in Moo/Types::Standard.
263              
264             =item C<< :enum(list,of,strings) >>
265              
266             Like C<< isa => Enum["list","of","strings"] >> in Moo/Types::Standard.
267              
268             =item C<< :default(value) >>
269              
270             Like C<< default => "value" >> in Moo.
271              
272             For simple (string/numeric) defaults. Doesn't accept coderefs.
273              
274             =item C<< :handles(list,of,methods) >>
275              
276             Like C<< handles => ["list","of","methods"] >> in Moo.
277              
278             Currently no support for a hashref of delegations.
279              
280             =back
281              
282             =head2 Flags Affecting Package
283              
284             =over
285              
286             =item C<< :role >>
287              
288             Creates a Moo::Role instead of a Moo class.
289              
290             =item C<< :extends(Class::Name) >>
291              
292             Like C<< extends "Class::Name" >> in Moo.
293              
294             =item C<< :with(Role::Name) >>
295              
296             Like C<< with "Role::Name" >> in Moo.
297              
298             =item C<< :requires(list,of,methods) >>
299              
300             Like C<< requires ("list", "of", "methods"); >> in Moo::Role.
301              
302             =item C<< :std >>
303              
304             Like C<< use Types::Standard qw( -types -is -assert ) >>
305              
306             =item C<< :common >>
307              
308             Like:
309              
310             use Types::Common::Numeric qw( -types -is -assert );
311             use Types::Common::String qw( -types -is -assert );
312              
313             =item C<< :path >>
314              
315             Like:
316              
317             use Types::Path::Tiny qw( -types -is -assert );
318             use Path::Tiny qw( path );
319              
320             =back
321              
322             =head1 BUGS
323              
324             Please report any bugs to
325             L<http://rt.cpan.org/Dist/Display.html?Queue=Mom>.
326              
327             =head1 SEE ALSO
328              
329             L<Moo>, L<Types::Standard>.
330              
331             =head1 AUTHOR
332              
333             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
334              
335             =head1 COPYRIGHT AND LICENCE
336              
337             This software is copyright (c) 2020 by Toby Inkster.
338              
339             This is free software; you can redistribute it and/or modify it under
340             the same terms as the Perl 5 programming language system itself.
341              
342             =head1 DISCLAIMER OF WARRANTIES
343              
344             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
345             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
346             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
347