File Coverage

blib/lib/Mom.pm
Criterion Covered Total %
statement 53 67 79.1
branch 22 28 78.5
condition 0 4 0.0
subroutine 8 8 100.0
pod n/a
total 83 107 77.5


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