Commit | Line | Data |
daa0fd7d |
1 | package Moose::Cookbook::Extending::Recipe3; |
2 | |
3 | # ABSTRACT: Providing an alternate base object class |
4 | |
5 | __END__ |
6 | |
d5e84b86 |
7 | |
8 | =pod |
9 | |
5547fba7 |
10 | =begin testing-SETUP |
c79239a2 |
11 | |
0adca353 |
12 | use Test::Requires { |
13 | 'Test::Output' => '0', |
14 | }; |
c79239a2 |
15 | |
5547fba7 |
16 | =end testing-SETUP |
c79239a2 |
17 | |
d5e84b86 |
18 | =head1 SYNOPSIS |
19 | |
20 | package MyApp::Base; |
21 | use Moose; |
22 | |
23 | extends 'Moose::Object'; |
24 | |
25 | before 'new' => sub { warn "Making a new " . $_[0] }; |
26 | |
27 | no Moose; |
28 | |
29 | package MyApp::UseMyBase; |
30 | use Moose (); |
554b7648 |
31 | use Moose::Exporter; |
d5e84b86 |
32 | |
aedcb7d9 |
33 | Moose::Exporter->setup_import_methods( also => 'Moose' ); |
d5e84b86 |
34 | |
554b7648 |
35 | sub init_meta { |
36 | shift; |
a8de959b |
37 | return Moose->init_meta( @_, base_class => 'MyApp::Base' ); |
d5e84b86 |
38 | } |
39 | |
40 | =head1 DESCRIPTION |
41 | |
9ac00c2b |
42 | A common extension is to provide an alternate base class. One way to |
1abd7f66 |
43 | do that is to make a C<MyApp::Base> and add C<S<extends |
9ac00c2b |
44 | 'MyApp::Base'>> to every class in your application. That's pretty |
45 | tedious. Instead, you can create a Moose-alike module that sets the |
46 | base object class to C<MyApp::Base> for you. |
d5e84b86 |
47 | |
48 | Then, instead of writing C<S<use Moose>> you can write C<S<use |
49 | MyApp::UseMyBase>>. |
50 | |
51 | In this particular example, our base class issues some debugging |
9ac00c2b |
52 | output every time a new object is created, but you can think of some |
53 | more interesting things to do with your own base class. |
d5e84b86 |
54 | |
5a87a5ca |
55 | This uses the magic of L<Moose::Exporter>. When we call C<< |
56 | Moose::Exporter->setup_import_methods( also => 'Moose' ) >> it builds |
57 | C<import> and C<unimport> methods for you. The C<< also => 'Moose' >> |
58 | bit says that we want to export everything that Moose does. |
554b7648 |
59 | |
60 | The C<import> method that gets created will call our C<init_meta> |
5a87a5ca |
61 | method, passing it C<< for_caller => $caller >> as its |
9ac00c2b |
62 | arguments. The C<$caller> is set to the class that actually imported |
63 | us in the first place. |
554b7648 |
64 | |
65 | See the L<Moose::Exporter> docs for more details on its API. |
66 | |
67 | =head1 USING MyApp::UseMyBase |
68 | |
69 | To actually use our new base class, we simply use C<MyApp::UseMyBase> |
70 | I<instead> of C<Moose>. We get all the Moose sugar plus our new base |
71 | class. |
72 | |
73 | package Foo; |
74 | |
75 | use MyApp::UseMyBase; |
76 | |
77 | has 'size' => ( is => 'rw' ); |
78 | |
79 | no MyApp::UseMyBase; |
80 | |
9ac00c2b |
81 | =head1 CONCLUSION |
82 | |
83 | This is an awful lot of magic for a simple base class. You will often |
84 | want to combine a metaclass trait with a base class extension, and |
85 | that's when this technique is useful. |
86 | |
c79239a2 |
87 | =begin testing |
88 | |
89 | { |
90 | package Foo; |
91 | |
92 | MyApp::UseMyBase->import; |
93 | |
94 | has( 'size' => ( is => 'rw' ) ); |
95 | } |
96 | |
97 | ok( Foo->isa('MyApp::Base'), 'Foo isa MyApp::Base' ); |
98 | |
99 | ok( Foo->can('size'), 'Foo has a size method' ); |
100 | |
101 | my $foo; |
8b23f9f8 |
102 | stderr_like( |
c79239a2 |
103 | sub { $foo = Foo->new( size => 2 ) }, |
8b23f9f8 |
104 | qr/^Making a new Foo/, |
c79239a2 |
105 | 'got expected warning when calling Foo->new' |
106 | ); |
107 | |
108 | is( $foo->size(), 2, '$foo->size is 2' ); |
109 | |
110 | =end testing |
111 | |
d5e84b86 |
112 | =cut |