Commit | Line | Data |
c2a0627f |
1 | |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
ec65165a |
6 | Moose::Cookbook::Basics::Recipe10 - Using BUILDARGS and BUILD to hook into object construction |
c2a0627f |
7 | |
8 | =head1 SYNOPSIS |
9 | |
1f476b5f |
10 | package Person; |
c765b254 |
11 | |
1f476b5f |
12 | has 'ssn' => ( |
13 | is => 'ro', |
14 | isa => 'Str', |
15 | predicate => 'has_ssn', |
16 | ); |
c765b254 |
17 | |
1f476b5f |
18 | has 'country_of_residence' => ( |
19 | is => 'ro', |
20 | isa => 'Str', |
21 | default => 'usa' |
22 | ); |
c765b254 |
23 | |
1f476b5f |
24 | has 'first_name' => ( |
25 | is => 'ro', |
26 | isa => 'Str', |
27 | ); |
c765b254 |
28 | |
1f476b5f |
29 | has 'last_name' => ( |
30 | is => 'ro', |
31 | isa => 'Str', |
32 | ); |
c765b254 |
33 | |
1f476b5f |
34 | sub BUILDARGS { |
35 | my $class = shift; |
c765b254 |
36 | |
1f476b5f |
37 | if ( @_ == 1 && ! ref $_[0] ) { |
38 | return { ssn => $_[0] }; |
39 | } |
40 | else { |
41 | return $class->SUPER::BUILDARGS(@_); |
42 | } |
c2a0627f |
43 | } |
44 | |
1f476b5f |
45 | sub BUILD { |
46 | my $self = shift; |
c765b254 |
47 | |
1f476b5f |
48 | if ( $self->country_of_residence eq 'usa' ) { |
49 | die 'Cannot create a Person who lives in the USA without an ssn.' |
50 | unless $self->has_ssn; |
51 | } |
c2a0627f |
52 | } |
53 | |
1f476b5f |
54 | =head1 DESCRIPTION |
c2a0627f |
55 | |
1f476b5f |
56 | This recipe demonstrates the use of C<BUILDARGS> and C<BUILD>. By |
57 | defining these methods, we can hook into the object construction |
58 | process without overriding C<new>. |
c765b254 |
59 | |
1f476b5f |
60 | The C<BUILDARGS> method is called I<before> an object has been |
61 | created. It is called as a class method, and receives all of the |
62 | parameters passed to the C<new> method. It is expected to do something |
63 | with these arguments and return a hash reference. The keys of the hash |
64 | must be attribute C<init_arg>s. |
c765b254 |
65 | |
1f476b5f |
66 | The primary purpose of C<BUILDARGS> is to allow a class to accept |
67 | something other than named arguments. In the case of our C<Person> |
68 | class, we are allowing it to be called with a single argument, a |
69 | social security number: |
c765b254 |
70 | |
1f476b5f |
71 | my $person = Person->new('123-45-6789'); |
c765b254 |
72 | |
1f476b5f |
73 | The key part of our C<BUILDARGS> is this conditional: |
c765b254 |
74 | |
1f476b5f |
75 | if ( @_ == 1 && ! ref $_[0] ) { |
76 | return { ssn => $_[0] }; |
77 | } |
c2a0627f |
78 | |
1f476b5f |
79 | By default, Moose constructors accept a list of key-value pairs, or a |
80 | hash reference. We need to make sure that C<$_[0]> is not a reference |
81 | before assuming it is a social security number. |
c2a0627f |
82 | |
1f476b5f |
83 | We call C<< $class->SUPER::BUILDARGS(@_) >> to handle all the other |
84 | cases. You should always do this in your own C<BUILDARGS> methods, |
85 | since L<Moose::Object> provides its own C<BUILDARGS> method that |
86 | handles hash references and a list of key-value pairs. |
c2a0627f |
87 | |
1f476b5f |
88 | The C<BUILD> method is called I<after> the object is constructed, but |
89 | before it is returned to the caller. The C<BUILD> method provides an |
90 | opportunity to check the object state as a whole. This is a good place |
91 | to put logic that cannot be expressed as a type constraint on a single |
92 | attribute. |
c2a0627f |
93 | |
1f476b5f |
94 | In the C<Person> class, we need to check the relationship between two |
95 | attributes, C<ssn> and C<country_of_residence>. We throw an exception |
96 | if the object is not logically consistent. |
c765b254 |
97 | |
1f476b5f |
98 | =head1 MORE CONSIDERATIONS |
c765b254 |
99 | |
1f476b5f |
100 | This recipe is made significantly simpler because all of the |
101 | attributes are read-only. If the C<country_of_residence> attribute |
102 | were settable, we would need to check that a Person had an C<ssn> if |
103 | the new country was C<usa>. This could be done with a C<before> |
104 | modifier. |
c2a0627f |
105 | |
106 | =head1 CONCLUSION |
107 | |
1f476b5f |
108 | We have repeatedly discouraged overriding C<new> in Moose |
109 | classes. This recipe shows how you can use C<BUILDARGS> and C<BUILD> |
110 | to hook into object construction without overriding C<new> |
c2a0627f |
111 | |
1f476b5f |
112 | The C<BUILDARGS> method lets us expand on Moose's built-in parameter |
113 | handling for constructors. The C<BUILD> method lets us implement |
114 | logical constraints across the whole object after it is created. |
c2a0627f |
115 | |
1f476b5f |
116 | =head1 AUTHOR |
c2a0627f |
117 | |
055dbe8c |
118 | Dave Rolsky E<lt>autarch@urth.orgE<gt> |
119 | |
1f476b5f |
120 | =head1 COPYRIGHT AND LICENSE |
c2a0627f |
121 | |
1f476b5f |
122 | Copyright 2006-2009 by Infinity Interactive, Inc. |
c2a0627f |
123 | |
1f476b5f |
124 | L<http://www.iinteractive.com> |
c2a0627f |
125 | |
1f476b5f |
126 | This library is free software; you can redistribute it and/or modify |
127 | it under the same terms as Perl itself. |
c2a0627f |
128 | |
1f476b5f |
129 | =cut |