Commit | Line | Data |
5447ee45 |
1 | package MooseX::UndefTolerant; |
5447ee45 |
2 | |
02e25b00 |
3 | use strict; |
4 | use warnings; |
5 | |
5447ee45 |
6 | use Moose qw(); |
7 | use Moose::Exporter; |
5447ee45 |
8 | |
9 | use MooseX::UndefTolerant::Attribute; |
d6ce838b |
10 | use MooseX::UndefTolerant::Class; |
2d1c57bd |
11 | use MooseX::UndefTolerant::Constructor; |
5447ee45 |
12 | |
5447ee45 |
13 | |
fff0a09d |
14 | my %metaroles = ( |
15d56027 |
15 | base_class_roles => [ 'MooseX::UndefTolerant::Object' ], |
fff0a09d |
16 | class_metaroles => { |
17 | attribute => [ 'MooseX::UndefTolerant::Attribute' ], |
18 | } |
19 | ); |
d6ce838b |
20 | if ( $Moose::VERSION < 1.9900 ) { |
fff0a09d |
21 | $metaroles{class_metaroles}{constructor} = [ |
22 | 'MooseX::UndefTolerant::Constructor', |
23 | ]; |
d6ce838b |
24 | } |
25 | else { |
fff0a09d |
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 | }; |
d6ce838b |
43 | } |
44 | |
45 | |
fff0a09d |
46 | Moose::Exporter->setup_import_methods(%metaroles); |
5447ee45 |
47 | |
48 | 1; |
49 | |
b2c5b43c |
50 | # ABSTRACT: Make your attribute(s) tolerant to undef initialization |
5447ee45 |
51 | |
b2c5b43c |
52 | __END__ |
5447ee45 |
53 | |
54 | =head1 SYNOPSIS |
55 | |
19258058 |
56 | package My::Class; |
5447ee45 |
57 | |
19258058 |
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 |
94f9d198 |
91 | the attributes they will not be initialized, effectively behaving as if you |
15d56027 |
92 | had not provided a value at all. Such values are also cleaned from BUILDARGS. |
19258058 |
93 | |
1686337d |
94 | You can also apply the 'UndefTolerant' trait to individual attributes. See |
793ea91a |
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 | |
2b46c488 |
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 | |
19258058 |
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)) { |
1cbb963b |
128 | $class = My:Class->new(foo => $foo, bar => 123); |
19258058 |
129 | } else { |
1cbb963b |
130 | $class = My:Class->new(bar => 123); |
19258058 |
131 | } |
132 | |
c5dd383b |
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 |
19258058 |
135 | example: have your cake and eat it too! |
136 | |
137 | =head1 PER ATTRIBUTE |
5447ee45 |
138 | |
1cbb963b |
139 | See L<MooseX::UndefTolerant::Attribute>. |
140 | |
94f9d198 |
141 | =head1 CAVEATS |
142 | |
143 | This extension does not currently work in immutable classes when applying the |
15d56027 |
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 |
0796f935 |
146 | L<Moose::Meta::Class>, not L<Moose::Meta::Attribute>. The good news is that |
147 | this is expected to be changing shortly. |
94f9d198 |
148 | |
15d56027 |
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 | |
5447ee45 |
152 | =head1 ACKNOWLEDGEMENTS |
153 | |
19258058 |
154 | Many thanks to the crew in #moose who talked me through this module: |
155 | |
5447ee45 |
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 | |
5447ee45 |
168 | =cut |