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