clean undef-tolerant and undefined args in BUILDARGS
[gitmo/MooseX-UndefTolerant.git] / lib / MooseX / UndefTolerant.pm
1 package MooseX::UndefTolerant;
2
3 use strict;
4 use warnings;
5
6 use Moose qw();
7 use Moose::Exporter;
8
9 use MooseX::UndefTolerant::Attribute;
10 use MooseX::UndefTolerant::Class;
11 use MooseX::UndefTolerant::Constructor;
12
13
14 my %metaroles = (
15     base_class_roles => [ 'MooseX::UndefTolerant::Object' ],
16     class_metaroles => {
17         attribute => [ 'MooseX::UndefTolerant::Attribute' ],
18     }
19 );
20 if ( $Moose::VERSION < 1.9900 ) {
21     $metaroles{class_metaroles}{constructor} = [
22         'MooseX::UndefTolerant::Constructor',
23     ];
24 }
25 else {
26     $metaroles{class_metaroles}{class} = [
27         'MooseX::UndefTolerant::Class',
28     ];
29     $metaroles{role_metaroles} = {
30         applied_attribute => [
31             'MooseX::UndefTolerant::Attribute',
32         ],
33         role => [
34             'MooseX::UndefTolerant::Role',
35         ],
36         application_to_class => [
37             'MooseX::UndefTolerant::ApplicationToClass',
38         ],
39         application_to_role => [
40             'MooseX::UndefTolerant::ApplicationToRole',
41         ],
42     };
43 }
44
45
46 Moose::Exporter->setup_import_methods(%metaroles);
47
48 1;
49
50 # ABSTRACT: Make your attribute(s) tolerant to undef initialization
51
52 __END__
53
54 =head1 SYNOPSIS
55
56   package My::Class;
57
58   use Moose;
59   use MooseX::UndefTolerant;
60
61   has 'name' => (
62     is => 'ro',
63     isa => 'Str',
64     predicate => 'has_name'
65   );
66
67   # Meanwhile, under the city...
68
69   # Doesn't explode
70   my $class = My::Class->new(name => undef);
71   $class->has_name # False!
72
73 Or, if you only want one attribute to have this behaviour:
74
75   package My:Class;
76   use Moose;
77
78   use MooseX::UndefTolerant::Attribute;
79
80   has 'bar' => (
81       traits => [ qw(MooseX::UndefTolerant::Attribute)],
82       is => 'ro',
83       isa => 'Num',
84       predicate => 'has_bar'
85   );
86
87 =head1 DESCRIPTION
88
89 Loading this module in your L<Moose> class makes initialization of your
90 attributes tolerant of undef.  If you specify the value of undef to any of
91 the attributes they will not be initialized, effectively behaving as if you
92 had not provided a value at all.  Such values are also cleaned from BUILDARGS.
93
94 You can also apply the 'UndefTolerant' trait to individual attributes. See
95 L<MooseX::UndefTolerant::Attribute> for details.
96
97 There will be no change in behaviour to any attribute with a type constraint
98 that accepts undef values (for example C<Maybe> types), as it is presumed that
99 since the type is already "undef tolerant", there is no need to avoid
100 initializing the attribute value with C<undef>.
101
102 As of Moose 1.9900, this module can also be used in a role, in which case all
103 of that role's attributes will be undef-tolerant.
104
105 =head1 MOTIVATION
106
107 I often found myself in this quandry:
108
109   package My:Class;
110   use Moose;
111
112   has 'foo' => (
113     is => 'ro',
114     isa => 'Str',
115   );
116
117   # ... then
118
119   my $foo = ... # get the param from something
120
121   my $class = My:Class->new(foo => $foo, bar => 123);
122
123 What if foo is undefined?  I didn't want to change my attribute to be
124 Maybe[Str] and I still want my predicate (C<has_foo>) to work.  The only
125 real solution was:
126
127   if(defined($foo)) {
128     $class = My:Class->new(foo => $foo, bar => 123);
129   } else {
130     $class = My:Class->new(bar => 123);
131   }
132
133 Or some type of codemulch using ternary conditionals.  This module allows you
134 to make your attributes more tolerant of undef so that you can keep the first
135 example: have your cake and eat it too!
136
137 =head1 PER ATTRIBUTE
138
139 See L<MooseX::UndefTolerant::Attribute>.
140
141 =head1 CAVEATS
142
143 This extension does not currently work in immutable classes when applying the
144 trait to a specific attributes in the class, as opposed to the class itself.
145 This is because the inlined constructor initialization code currently lives in
146 L<Moose::Meta::Class>, not L<Moose::Meta::Attribute>. The good news is that
147 this is expected to be changing shortly.
148
149 Also, BUILDARGS cannot be cleaned up if the entire class is not made undef
150 tolerant, as attribute traits cannot modify class constructor behaviour.
151
152 =head1 ACKNOWLEDGEMENTS
153
154 Many thanks to the crew in #moose who talked me through this module:
155
156 Hans Dieter Pearcey (confound)
157
158 Jesse Luehrs (doy)
159
160 Tomas Doran (t0m)
161
162 Dylan Hardison (dylan)
163
164 Jay Shirley (jshirley)
165
166 Mike Eldridge (diz)
167
168 =cut