File Coverage

blib/lib/MooseX/hasn/t.pm
Criterion Covered Total %
statement 17 55 30.9
branch 0 22 0.0
condition 0 24 0.0
subroutine 6 11 54.5
pod n/a
total 23 112 20.5


line stmt bran cond sub pod time code
1             package MooseX::hasn::t;
2              
3             BEGIN {
4 1     1   20200 $MooseX::hasn::t::AUTHORITY = 'cpan:TOBYINK';
5 1         21 $MooseX::hasn::t::VERSION = '0.003';
6             }
7              
8 1     1   26 use 5.010;
  1         4  
  1         41  
9 1     1   5 use strict qw(subs vars);
  1         2  
  1         50  
10 1     1   5 no warnings;
  1         2  
  1         68  
11              
12             our @CARP_NOT = qw(Moose::Meta::Method::Overridden);
13              
14 1     1   6 use Carp qw/croak/;
  1         2  
  1         72  
15 1     1   5 use Scalar::Util qw/blessed/;
  1         2  
  1         798  
16              
17             sub hasn::t
18             {
19 0 0   0     my $opts = ref($_[0]) eq 'HASH' ? shift : {};
20 0           my ($symbol, %args) = @_;
21 0   0       my $caller = $opts->{caller} || caller;
22            
23 0 0         if (ref $symbol eq 'ARRAY')
24             {
25 0           hasn::t({caller=>$caller}, $_, %args) for @$symbol;
26 0           return;
27             }
28            
29 0           my $ERROR = q(Can't locate object method "%s" via package "%s");
30            
31 0           my @subs;
32 0 0         if (my $attr = $caller->meta->find_attribute_by_name($symbol))
33             {
34 0           foreach my $role (qw(accessor reader writer predicate clearer initializer))
35             {
36 0           my $sub = $attr->$role;
37 0 0 0       push @subs, $sub if defined $sub && !ref $sub;
38             }
39            
40 0 0 0       if ($attr->is_required and $attr->has_default)
    0 0        
    0          
41             {
42             # OK
43             }
44             elsif ($attr->is_required and exists $args{default})
45             {
46 0   0       my $init_arg = $attr->init_arg || $symbol;
47            
48 0 0         unless ($caller->can('BUILDARGS'))
49             {
50 0     0     *{"$caller\::BUILDARGS"} = sub { shift; @_ };
  0            
  0            
  0            
51             }
52            
53             $caller->meta->add_around_method_modifier(BUILDARGS => sub
54             {
55 0     0     my ($orig, $class, @args) = @_;
56 0 0         my $d = ref $args{default} eq 'CODE' ? $args{default}->() : $args{default};
57 0 0 0       if (@args==1 and ref $args[0] eq 'HASH')
58             {
59 0   0       $args[0]{$init_arg} //= $d;
60             }
61             else
62             {
63 0           push @args, $init_arg, $d;
64             }
65            
66 0           $class->$orig(@args);
67 0           });
68             }
69             elsif ($attr->is_required)
70             {
71 0           croak "can't \"hasn't $symbol\", because $symbol is required and has no default";
72             }
73             }
74             else
75             {
76 0           @subs = $symbol;
77             }
78            
79 0           foreach my $sub (@subs)
80             {
81             $caller->meta->add_override_method_modifier($sub => sub
82             {
83 0     0     my ($invocant, @args) = @_;
84 0   0       croak sprintf($ERROR, $sub, (blessed $invocant or $caller));
85 0           });
86             }
87            
88 0           my $can = $caller->can('can');
89 0           *{"$caller\::can"} = sub {
90 0     0     my ($invocant, $m) = @_;
91 0 0         return if $m ~~ [@subs];
92 0           goto $can;
93             }
94 0           }
95              
96             __PACKAGE__
97             __END__
98              
99             =head1 NAME
100              
101             MooseX::hasn't - syntactic sugar to complement "has"
102              
103             =head1 SYNOPSIS
104              
105             {
106             package Person;
107             use Moose;
108             has name => (is => "ro", writer => "_rename", required => 1);
109             }
110            
111             {
112             package AnonymousPerson;
113             use Moose;
114             use MooseX::hasn't;
115             extends "Person";
116             hasn't name => ();
117             }
118            
119             my $dude = AnonymousPerson->new;
120             say($dude->can('_rename') ? 'true' : 'false'); # false
121             say($dude->name); # croaks
122              
123             =head1 DESCRIPTION
124              
125             C<< hasn't >> is a counter-part for Moose's C<< has >>.
126              
127             It tries to stop a child class inheriting something (an attribute or a
128             method) from its parent class - though it's not always 100% successful.
129              
130             =head1 FAQ
131              
132             =head2 Doesn't this break polymorphism?
133              
134             The idea behind polymorphism is that if I<Bar> inherits from I<Foo>,
135             then I should be able to use an object of type I<Bar> wherever I'd
136             normally use I<Foo>.
137              
138             In particular, if I can do:
139              
140             Foo->new()->some_method();
141              
142             then I should be able to do:
143              
144             Bar->new()->some_method();
145              
146             But if I<Bar> can explicitly indicate that it hasn't got method
147             C<some_method> then this breaks. So, yes, this module does break
148             polymorphism.
149              
150             But observe that it's not especially difficult to break polymorphism
151             manually:
152              
153             {
154             package Foo;
155             use Moose;
156             sub some_method {}
157             }
158            
159             {
160             package Bar;
161             use Moose;
162             extends 'Foo';
163             sub some_method { die "some_method not found in package Bar" }
164             }
165              
166             This module just makes it easier and more declarative.
167              
168             =head2 How exactly is this achieved?
169              
170             For C<< hasn't $method >>, it simply adds an override method modifier
171             to the given method that croaks.
172              
173             For C<< hasn't $attribute >>, it finds the names of the accessor, reader,
174             writer, clearer, predicate and initializer methods for that attribute
175             (if any) and overrides them all.
176              
177             In both cases, it overrides the class' C<can> method too.
178              
179             =head2 What about required attributes?
180              
181             If the parent class has an attribute which is required and has a default,
182             then you can use C<< hasn't >> in a child class safely.
183              
184             If the parent class has an attribute which is required but has no default,
185             then you must explicitly specify a default in the child class:
186              
187             hasn't name => (default => 'anon');
188              
189             This latter technique is probably not foolproof. Defaults may be coderefs,
190             like in C<has>.
191              
192             =head1 BUGS AND LIMITATIONS
193              
194             =over
195              
196             =item * C<< hasn't $attr (default => sub {}) >> will execute the coderef
197             as a function with no arguments, not as a method.
198              
199             =item * C<< $object->meta >> can still see attributes and methods
200             which have been "hasn'ted". Some serious Class::MOP fu is needed to
201             fix this.
202              
203             =back
204              
205             Report anything else here:
206              
207             L<http://rt.cpan.org/Dist/Display.html?Queue=Moose-hasn-t>.
208              
209             =head1 SEE ALSO
210              
211             L<Moose>.
212              
213             =head1 AUTHOR
214              
215             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
216              
217             =head1 COPYRIGHT AND LICENCE
218              
219             This software is copyright (c) 2012 by Toby Inkster.
220              
221             This is free software; you can redistribute it and/or modify it under the
222             same terms as the Perl 5 programming language system itself.
223              
224             =head1 DISCLAIMER OF WARRANTIES
225              
226             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
227             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
228             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.