Codebase list libmoose-perl / upstream/0.55
[svn-upgrade] Integrating new upstream version, libmoose-perl (0.55) Krzysztof Krzyzaniak 15 years ago
115 changed file(s) with 6246 addition(s) and 4665 deletion(s). Raw diff Collapse all Expand all
00 Revision history for Perl extension Moose
1
2 0.55 Sun August 3, 2008
3 * Moose::Meta::Attribute
4 - breaking down the way 'handles' methods are
5 created so that the process can be more easily
6 overridden by subclasses (stevan)
7
8 * Moose::Meta::TypeConstraint
9 - fixing what is passed into a ->message with
10 the type constraints (RT #37569)
11 - added tests for this (Charles Alderman)
12
13 * Moose::Util::TypeConstraints
14 - fix coerce to accept anon types like subtype can (mst)
15
16 * Moose::Cookbook
17 - reorganized the recipes into sections - Basics, Roles, Meta,
18 Extending - and wrote abstracts for each section (Dave Rolsky)
19
20 * Moose::Cookbook::Basics::Recipe10
21 - A new recipe that demonstrates operator overloading
22 in combination with Moose. (bluefeet)
23
24 * Moose::Cookbook::Meta::Recipe1
25 - an introduction to what meta is and why you'd want to make
26 your own metaclass extensions (Dave Rolsky)
27
28 * Moose::Cookbook::Meta::Recipe4
29 - a very simple metaclass example (Dave Rolsky)
30
31 * Moose::Cookbook::Extending::Recipe1
32 - how to write a Moose-alike module to use your own object base
33 class (Dave Rolsky)
34
35 * Moose::Cookbook::Extending::Recipe2
36 - how to write modules with an API just like C<Moose.pm> (Dave
37 Rolsky)
38
39 * all documentation
40 - Tons of fixes, both syntactical and grammatical (Dave
41 Rolsky, Paul Fenwick)
142
243 0.54 Thurs. July 3, 2008
344 ... this is not my day today ...
1212 inc/Module/Install/WriteAll.pm
1313 lib/Moose.pm
1414 lib/Moose/Cookbook.pod
15 lib/Moose/Cookbook/Basics/Recipe1.pod
16 lib/Moose/Cookbook/Basics/Recipe10.pod
17 lib/Moose/Cookbook/Basics/Recipe2.pod
18 lib/Moose/Cookbook/Basics/Recipe3.pod
19 lib/Moose/Cookbook/Basics/Recipe4.pod
20 lib/Moose/Cookbook/Basics/Recipe5.pod
21 lib/Moose/Cookbook/Basics/Recipe6.pod
22 lib/Moose/Cookbook/Basics/Recipe7.pod
23 lib/Moose/Cookbook/Basics/Recipe9.pod
24 lib/Moose/Cookbook/Extending/Recipe1.pod
25 lib/Moose/Cookbook/Extending/Recipe2.pod
1526 lib/Moose/Cookbook/FAQ.pod
16 lib/Moose/Cookbook/Recipe1.pod
17 lib/Moose/Cookbook/Recipe10.pod
18 lib/Moose/Cookbook/Recipe11.pod
19 lib/Moose/Cookbook/Recipe2.pod
20 lib/Moose/Cookbook/Recipe21.pod
21 lib/Moose/Cookbook/Recipe22.pod
22 lib/Moose/Cookbook/Recipe3.pod
23 lib/Moose/Cookbook/Recipe4.pod
24 lib/Moose/Cookbook/Recipe5.pod
25 lib/Moose/Cookbook/Recipe6.pod
26 lib/Moose/Cookbook/Recipe7.pod
27 lib/Moose/Cookbook/Recipe9.pod
27 lib/Moose/Cookbook/Meta/Recipe1.pod
28 lib/Moose/Cookbook/Meta/Recipe2.pod
29 lib/Moose/Cookbook/Meta/Recipe3.pod
30 lib/Moose/Cookbook/Meta/Recipe4.pod
31 lib/Moose/Cookbook/Roles/Recipe1.pod
32 lib/Moose/Cookbook/Roles/Recipe2.pod
2833 lib/Moose/Cookbook/Snack/Keywords.pod
2934 lib/Moose/Cookbook/Snack/Types.pod
3035 lib/Moose/Cookbook/Style.pod
7176 META.yml
7277 README
7378 t/000_load.t
74 t/000_recipes/001_point.t
75 t/000_recipes/002_bank_account.t
76 t/000_recipes/003_binary_tree.t
77 t/000_recipes/004_company.t
78 t/000_recipes/005_coercion.t
79 t/000_recipes/006_augment_inner.t
80 t/000_recipes/010_roles.t
81 t/000_recipes/011_advanced_role_composition.t
82 t/000_recipes/021_meta_attribute.t
83 t/000_recipes/022_attribute_trait.t
79 t/000_recipes/basics/001_point.t
80 t/000_recipes/basics/002_bank_account.t
81 t/000_recipes/basics/003_binary_tree.t
82 t/000_recipes/basics/004_company.t
83 t/000_recipes/basics/005_coercion.t
84 t/000_recipes/basics/006_augment_inner.t
85 t/000_recipes/basics/010_genes.t
86 t/000_recipes/meta/002_meta_attribute.t
87 t/000_recipes/meta/003_attribute_trait.t
88 t/000_recipes/roles/001_roles.t
89 t/000_recipes/roles/002_advanced_role_composition.t
8490 t/010_basics/001_basic_class_setup.t
8591 t/010_basics/002_require_superclasses.t
8692 t/010_basics/003_super_and_override.t
136142 t/030_roles/016_runtime_roles_and_nonmoose.t
137143 t/030_roles/017_extending_role_attrs.t
138144 t/030_roles/018_runtime_roles_w_params.t
145 t/030_roles/019_build.t
139146 t/030_roles/020_role_composite.t
140147 t/030_roles/021_role_composite_exclusion.t
141148 t/030_roles/022_role_composition_req_methods.t
192199 t/100_bugs/013_lazybuild_required_undef.t
193200 t/100_bugs/014_DEMOLISHALL.t
194201 t/100_bugs/016_inheriting_from_roles.t
202 t/100_bugs/017_type_constraint_messages.t
203 t/100_bugs/018_immutable_metaclass_does_role.t
195204 t/200_examples/001_example.t
196205 t/200_examples/002_example_Moose_POOP.t
197206 t/200_examples/003_example.t
216225 t/500_test_moose/002_test_moose_does_ok.t
217226 t/500_test_moose/003_test_moose_has_attribute_ok.t
218227 t/500_test_moose/004_test_moose_meta_ok.t
228 t/600_todo_tests/001_exception_reflects_failed_constraint.t
229 t/600_todo_tests/002_various_role_shit.t
230 t/600_todo_tests/003_immutable_n_around.t
231 t/600_todo_tests/004_inlined_constructor_modified_new.t
232 t/600_todo_tests/005_moose_and_threads.t
219233 t/lib/Bar.pm
220234 t/lib/Foo.pm
221235 t/lib/MyMooseA.pm
0 ---
1 abstract: 'A postmodern object system for Perl 5'
2 author:
3 - 'Stevan Little <stevan@iinteractive.com>'
4 build_requires:
0 ---
1 abstract: A postmodern object system for Perl 5
2 author: Stevan Little <stevan@iinteractive.com>
3 build_requires:
54 Test::Exception: 0.21
65 Test::LongString: 0
76 Test::More: 0.62
87 distribution_type: module
9 generated_by: 'Module::Install version 0.75'
8 generated_by: Module::Install version 0.67
109 license: perl
11 meta-spec:
10 meta-spec:
1211 url: http://module-build.sourceforge.net/META-spec-v1.3.html
1312 version: 1.3
1413 name: Moose
15 no_index:
16 directory:
14 no_index:
15 directory:
1716 - inc
1817 - t
19 requires:
18 requires:
2019 Carp: 0
21 Class::MOP: 0.59
20 Class::MOP: 0.64
2221 Filter::Simple: 0
2322 Scalar::Util: 1.18
2423 Sub::Exporter: 0.972
25 version: 0.54
24 tests: t/*.t t/000_recipes/basics/*.t t/000_recipes/meta/*.t t/000_recipes/roles/*.t t/010_basics/*.t t/020_attributes/*.t t/030_roles/*.t t/040_type_constraints/*.t t/050_metaclasses/*.t t/060_compat/*.t t/100_bugs/*.t t/200_examples/*.t t/300_immutable/*.t t/400_moose_util/*.t t/500_test_moose/*.t t/600_todo_tests/*.t
25 version: 0.55
1111 # prereqs
1212 requires 'Scalar::Util' => $win32 ? '1.17' : '1.18';
1313 requires 'Carp';
14 requires 'Class::MOP' => '0.59';
14 requires 'Class::MOP' => '0.64';
1515 requires 'Sub::Exporter' => '0.972';
1616
1717 # only used by oose.pm, not Moose.pm :P
0 Moose version 0.54
0 Moose version 0.55
11 ===========================
22
33 See the individual module documentation for more information
55
66 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
99 $ISCORE = 1;
1010 @ISA = qw{Module::Install::Base};
1111 }
00 #line 1
11 package Module::Install::Base;
22
3 $VERSION = '0.75';
3 $VERSION = '0.67';
44
55 # Suspend handler for "redefined" warnings
66 BEGIN {
1010
1111 use vars qw{$VERSION $ISCORE @ISA};
1212 BEGIN {
13 $VERSION = '0.75';
13 $VERSION = '0.67';
1414 $ISCORE = 1;
1515 @ISA = qw{Module::Install::Base};
1616 }
55
66 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
99 $ISCORE = 1;
1010 @ISA = qw{Module::Install::Base};
1111 }
55
66 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
99 $ISCORE = 1;
1010 @ISA = qw{Module::Install::Base};
1111 }
66
77 use vars qw{$VERSION $ISCORE @ISA};
88 BEGIN {
9 $VERSION = '0.75';
9 $VERSION = '0.67';
1010 $ISCORE = 1;
1111 @ISA = qw{Module::Install::Base};
1212 }
3636 sub makemaker_args {
3737 my $self = shift;
3838 my $args = ($self->{makemaker_args} ||= {});
39 %$args = ( %$args, @_ ) if @_;
39 %$args = ( %$args, @_ ) if @_;
4040 $args;
4141 }
4242
6262 sub clean_files {
6363 my $self = shift;
6464 my $clean = $self->makemaker_args->{clean} ||= {};
65 %$clean = (
65 %$clean = (
6666 %$clean,
67 FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_),
67 FILES => join(' ', grep length, $clean->{FILES}, @_),
6868 );
6969 }
7070
7171 sub realclean_files {
72 my $self = shift;
72 my $self = shift;
7373 my $realclean = $self->makemaker_args->{realclean} ||= {};
74 %$realclean = (
74 %$realclean = (
7575 %$realclean,
76 FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_),
76 FILES => join(' ', grep length, $realclean->{FILES}, @_),
7777 );
7878 }
7979
103103 unless ( -d $dir ) {
104104 die "tests_recursive dir '$dir' does not exist";
105105 }
106 require File::Find;
106107 %test_dir = ();
107 require File::Find;
108108 File::Find::find( \&_wanted_t, $dir );
109109 $self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
110110 }
113113 my $self = shift;
114114 die "&Makefile->write() takes no arguments\n" if @_;
115115
116 # Make sure we have a new enough
117 require ExtUtils::MakeMaker;
118 $self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION );
119
120 # Generate the
121116 my $args = $self->makemaker_args;
122117 $args->{DISTNAME} = $self->name;
123 $args->{NAME} = $self->module_name || $self->name;
124 $args->{VERSION} = $self->version;
118 $args->{NAME} = $self->module_name || $self->name || $self->determine_NAME($args);
119 $args->{VERSION} = $self->version || $self->determine_VERSION($args);
125120 $args->{NAME} =~ s/-/::/g;
126121 if ( $self->tests ) {
127122 $args->{test} = { TESTS => $self->tests };
146141 map { @$_ }
147142 map { @$_ }
148143 grep $_,
149 ($self->configure_requires, $self->build_requires, $self->requires)
144 ($self->build_requires, $self->requires)
150145 );
151
152 # Remove any reference to perl, PREREQ_PM doesn't support it
153 delete $args->{PREREQ_PM}->{perl};
154146
155147 # merge both kinds of requires into prereq_pm
156148 my $subdirs = ($args->{DIR} ||= []);
212204 #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
213205
214206 # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
215 $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g;
207 $makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
216208
217209 # XXX - This is currently unused; not sure if it breaks other MM-users
218210 # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
241233
242234 __END__
243235
244 #line 371
236 #line 363
55
66 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
99 $ISCORE = 1;
1010 @ISA = qw{Module::Install::Base};
1111 }
1212
1313 my @scalar_keys = qw{
14 name
15 module_name
16 abstract
17 author
18 version
19 license
20 distribution_type
21 perl_version
22 tests
23 installdirs
14 name module_name abstract author version license
15 distribution_type perl_version tests installdirs
2416 };
2517
2618 my @tuple_keys = qw{
27 configure_requires
28 build_requires
29 requires
30 recommends
31 bundles
32 resources
19 build_requires requires recommends bundles
3320 };
3421
3522 sub Meta { shift }
3724 sub Meta_TupleKeys { @tuple_keys }
3825
3926 foreach my $key (@scalar_keys) {
40 *$key = sub {
41 my $self = shift;
42 return $self->{values}{$key} if defined wantarray and !@_;
43 $self->{values}{$key} = shift;
44 return $self;
45 };
46 }
47
48 sub requires {
49 my $self = shift;
50 while ( @_ ) {
51 my $module = shift or last;
52 my $version = shift || 0;
53 push @{ $self->{values}->{requires} }, [ $module, $version ];
54 }
55 $self->{values}{requires};
56 }
57
58 sub build_requires {
59 my $self = shift;
60 while ( @_ ) {
61 my $module = shift or last;
62 my $version = shift || 0;
63 push @{ $self->{values}->{build_requires} }, [ $module, $version ];
64 }
65 $self->{values}{build_requires};
66 }
67
68 sub configure_requires {
69 my $self = shift;
70 while ( @_ ) {
71 my $module = shift or last;
72 my $version = shift || 0;
73 push @{ $self->{values}->{configure_requires} }, [ $module, $version ];
74 }
75 $self->{values}->{configure_requires};
76 }
77
78 sub recommends {
79 my $self = shift;
80 while ( @_ ) {
81 my $module = shift or last;
82 my $version = shift || 0;
83 push @{ $self->{values}->{recommends} }, [ $module, $version ];
84 }
85 $self->{values}->{recommends};
86 }
87
88 sub bundles {
89 my $self = shift;
90 while ( @_ ) {
91 my $module = shift or last;
92 my $version = shift || 0;
93 push @{ $self->{values}->{bundles} }, [ $module, $version ];
94 }
95 $self->{values}->{bundles};
96 }
97
98 # Resource handling
99 sub resources {
100 my $self = shift;
101 while ( @_ ) {
102 my $resource = shift or last;
103 my $value = shift or next;
104 push @{ $self->{values}->{resources} }, [ $resource, $value ];
105 }
106 $self->{values}->{resources};
107 }
108
109 sub repository {
110 my $self = shift;
111 $self->resources( repository => shift );
112 return 1;
113 }
27 *$key = sub {
28 my $self = shift;
29 return $self->{values}{$key} if defined wantarray and !@_;
30 $self->{values}{$key} = shift;
31 return $self;
32 };
33 }
34
35 foreach my $key (@tuple_keys) {
36 *$key = sub {
37 my $self = shift;
38 return $self->{values}{$key} unless @_;
39
40 my @rv;
41 while (@_) {
42 my $module = shift or last;
43 my $version = shift || 0;
44 if ( $module eq 'perl' ) {
45 $version =~ s{^(\d+)\.(\d+)\.(\d+)}
46 {$1 + $2/1_000 + $3/1_000_000}e;
47 $self->perl_version($version);
48 next;
49 }
50 my $rv = [ $module, $version ];
51 push @rv, $rv;
52 }
53 push @{ $self->{values}{$key} }, @rv;
54 @rv;
55 };
56 }
57
58 # configure_requires is currently a null-op
59 sub configure_requires { 1 }
11460
11561 # Aliases for build_requires that will have alternative
11662 # meanings in some future version of META.yml.
117 sub test_requires { shift->build_requires(@_) }
118 sub install_requires { shift->build_requires(@_) }
63 sub test_requires { shift->build_requires(@_) }
64 sub install_requires { shift->build_requires(@_) }
11965
12066 # Aliases for installdirs options
12167 sub install_as_core { $_[0]->installdirs('perl') }
12470 sub install_as_vendor { $_[0]->installdirs('vendor') }
12571
12672 sub sign {
127 my $self = shift;
128 return $self->{'values'}{'sign'} if defined wantarray and ! @_;
129 $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
130 return $self;
73 my $self = shift;
74 return $self->{'values'}{'sign'} if defined wantarray and ! @_;
75 $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
76 return $self;
13177 }
13278
13379 sub dynamic_config {
13682 warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
13783 return $self;
13884 }
139 $self->{values}{dynamic_config} = $_[0] ? 1 : 0;
85 $self->{'values'}{'dynamic_config'} = $_[0] ? 1 : 0;
14086 return $self;
14187 }
14288
14389 sub all_from {
144 my ( $self, $file ) = @_;
145
146 unless ( defined($file) ) {
147 my $name = $self->name
148 or die "all_from called with no args without setting name() first";
149 $file = join('/', 'lib', split(/-/, $name)) . '.pm';
150 $file =~ s{.*/}{} unless -e $file;
151 die "all_from: cannot find $file from $name" unless -e $file;
152 }
153
154 # Some methods pull from POD instead of code.
155 # If there is a matching .pod, use that instead
156 my $pod = $file;
157 $pod =~ s/\.pm$/.pod/i;
158 $pod = $file unless -e $pod;
159
160 # Pull the different values
161 $self->name_from($file) unless $self->name;
162 $self->version_from($file) unless $self->version;
163 $self->perl_version_from($file) unless $self->perl_version;
164 $self->author_from($pod) unless $self->author;
165 $self->license_from($pod) unless $self->license;
166 $self->abstract_from($pod) unless $self->abstract;
167
168 return 1;
90 my ( $self, $file ) = @_;
91
92 unless ( defined($file) ) {
93 my $name = $self->name
94 or die "all_from called with no args without setting name() first";
95 $file = join('/', 'lib', split(/-/, $name)) . '.pm';
96 $file =~ s{.*/}{} unless -e $file;
97 die "all_from: cannot find $file from $name" unless -e $file;
98 }
99
100 $self->version_from($file) unless $self->version;
101 $self->perl_version_from($file) unless $self->perl_version;
102
103 # The remaining probes read from POD sections; if the file
104 # has an accompanying .pod, use that instead
105 my $pod = $file;
106 if ( $pod =~ s/\.pm$/.pod/i and -e $pod ) {
107 $file = $pod;
108 }
109
110 $self->author_from($file) unless $self->author;
111 $self->license_from($file) unless $self->license;
112 $self->abstract_from($file) unless $self->abstract;
169113 }
170114
171115 sub provides {
172 my $self = shift;
173 my $provides = ( $self->{values}{provides} ||= {} );
174 %$provides = (%$provides, @_) if @_;
175 return $provides;
116 my $self = shift;
117 my $provides = ( $self->{values}{provides} ||= {} );
118 %$provides = (%$provides, @_) if @_;
119 return $provides;
176120 }
177121
178122 sub auto_provides {
179 my $self = shift;
180 return $self unless $self->is_admin;
181 unless (-e 'MANIFEST') {
182 warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
183 return $self;
184 }
185 # Avoid spurious warnings as we are not checking manifest here.
186 local $SIG{__WARN__} = sub {1};
187 require ExtUtils::Manifest;
188 local *ExtUtils::Manifest::manicheck = sub { return };
189
190 require Module::Build;
191 my $build = Module::Build->new(
192 dist_name => $self->name,
193 dist_version => $self->version,
194 license => $self->license,
195 );
196 $self->provides( %{ $build->find_dist_packages || {} } );
123 my $self = shift;
124 return $self unless $self->is_admin;
125
126 unless (-e 'MANIFEST') {
127 warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
128 return $self;
129 }
130
131 # Avoid spurious warnings as we are not checking manifest here.
132
133 local $SIG{__WARN__} = sub {1};
134 require ExtUtils::Manifest;
135 local *ExtUtils::Manifest::manicheck = sub { return };
136
137 require Module::Build;
138 my $build = Module::Build->new(
139 dist_name => $self->name,
140 dist_version => $self->version,
141 license => $self->license,
142 );
143 $self->provides(%{ $build->find_dist_packages || {} });
197144 }
198145
199146 sub feature {
200 my $self = shift;
201 my $name = shift;
202 my $features = ( $self->{values}{features} ||= [] );
203 my $mods;
204
205 if ( @_ == 1 and ref( $_[0] ) ) {
206 # The user used ->feature like ->features by passing in the second
207 # argument as a reference. Accomodate for that.
208 $mods = $_[0];
209 } else {
210 $mods = \@_;
211 }
212
213 my $count = 0;
214 push @$features, (
215 $name => [
216 map {
217 ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_
218 } @$mods
219 ]
220 );
221
222 return @$features;
147 my $self = shift;
148 my $name = shift;
149 my $features = ( $self->{values}{features} ||= [] );
150
151 my $mods;
152
153 if ( @_ == 1 and ref( $_[0] ) ) {
154 # The user used ->feature like ->features by passing in the second
155 # argument as a reference. Accomodate for that.
156 $mods = $_[0];
157 } else {
158 $mods = \@_;
159 }
160
161 my $count = 0;
162 push @$features, (
163 $name => [
164 map {
165 ref($_) ? ( ref($_) eq 'HASH' ) ? %$_
166 : @$_
167 : $_
168 } @$mods
169 ]
170 );
171
172 return @$features;
223173 }
224174
225175 sub features {
226 my $self = shift;
227 while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
228 $self->feature( $name, @$mods );
229 }
230 return $self->{values}->{features}
231 ? @{ $self->{values}->{features} }
232 : ();
176 my $self = shift;
177 while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
178 $self->feature( $name, @$mods );
179 }
180 return $self->{values}->{features}
181 ? @{ $self->{values}->{features} }
182 : ();
233183 }
234184
235185 sub no_index {
236 my $self = shift;
237 my $type = shift;
238 push @{ $self->{values}{no_index}{$type} }, @_ if $type;
239 return $self->{values}{no_index};
186 my $self = shift;
187 my $type = shift;
188 push @{ $self->{values}{no_index}{$type} }, @_ if $type;
189 return $self->{values}{no_index};
240190 }
241191
242192 sub read {
243 my $self = shift;
244 $self->include_deps( 'YAML::Tiny', 0 );
245
246 require YAML::Tiny;
247 my $data = YAML::Tiny::LoadFile('META.yml');
248
249 # Call methods explicitly in case user has already set some values.
250 while ( my ( $key, $value ) = each %$data ) {
251 next unless $self->can($key);
252 if ( ref $value eq 'HASH' ) {
253 while ( my ( $module, $version ) = each %$value ) {
254 $self->can($key)->($self, $module => $version );
255 }
256 } else {
257 $self->can($key)->($self, $value);
193 my $self = shift;
194 $self->include_deps( 'YAML', 0 );
195
196 require YAML;
197 my $data = YAML::LoadFile('META.yml');
198
199 # Call methods explicitly in case user has already set some values.
200 while ( my ( $key, $value ) = each %$data ) {
201 next unless $self->can($key);
202 if ( ref $value eq 'HASH' ) {
203 while ( my ( $module, $version ) = each %$value ) {
204 $self->can($key)->($self, $module => $version );
205 }
206 }
207 else {
208 $self->can($key)->($self, $value);
209 }
210 }
211 return $self;
212 }
213
214 sub write {
215 my $self = shift;
216 return $self unless $self->is_admin;
217 $self->admin->write_meta;
218 return $self;
219 }
220
221 sub version_from {
222 my ( $self, $file ) = @_;
223 require ExtUtils::MM_Unix;
224 $self->version( ExtUtils::MM_Unix->parse_version($file) );
225 }
226
227 sub abstract_from {
228 my ( $self, $file ) = @_;
229 require ExtUtils::MM_Unix;
230 $self->abstract(
231 bless(
232 { DISTNAME => $self->name },
233 'ExtUtils::MM_Unix'
234 )->parse_abstract($file)
235 );
236 }
237
238 sub _slurp {
239 my ( $self, $file ) = @_;
240
241 local *FH;
242 open FH, "< $file" or die "Cannot open $file.pod: $!";
243 do { local $/; <FH> };
244 }
245
246 sub perl_version_from {
247 my ( $self, $file ) = @_;
248
249 if (
250 $self->_slurp($file) =~ m/
251 ^
252 use \s*
253 v?
254 ([\d_\.]+)
255 \s* ;
256 /ixms
257 )
258 {
259 my $v = $1;
260 $v =~ s{_}{}g;
261 $self->perl_version($1);
262 }
263 else {
264 warn "Cannot determine perl version info from $file\n";
265 return;
266 }
267 }
268
269 sub author_from {
270 my ( $self, $file ) = @_;
271 my $content = $self->_slurp($file);
272 if ($content =~ m/
273 =head \d \s+ (?:authors?)\b \s*
274 ([^\n]*)
275 |
276 =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
277 .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
278 ([^\n]*)
279 /ixms) {
280 my $author = $1 || $2;
281 $author =~ s{E<lt>}{<}g;
282 $author =~ s{E<gt>}{>}g;
283 $self->author($author);
284 }
285 else {
286 warn "Cannot determine author info from $file\n";
287 }
288 }
289
290 sub license_from {
291 my ( $self, $file ) = @_;
292
293 if (
294 $self->_slurp($file) =~ m/
295 (
296 =head \d \s+
297 (?:licen[cs]e|licensing|copyright|legal)\b
298 .*?
299 )
300 (=head\\d.*|=cut.*|)
301 \z
302 /ixms
303 )
304 {
305 my $license_text = $1;
306 my @phrases = (
307 'under the same (?:terms|license) as perl itself' => 'perl', 1,
308 'GNU public license' => 'gpl', 1,
309 'GNU lesser public license' => 'gpl', 1,
310 'BSD license' => 'bsd', 1,
311 'Artistic license' => 'artistic', 1,
312 'GPL' => 'gpl', 1,
313 'LGPL' => 'lgpl', 1,
314 'BSD' => 'bsd', 1,
315 'Artistic' => 'artistic', 1,
316 'MIT' => 'mit', 1,
317 'proprietary' => 'proprietary', 0,
318 );
319 while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
320 $pattern =~ s{\s+}{\\s+}g;
321 if ( $license_text =~ /\b$pattern\b/i ) {
322 if ( $osi and $license_text =~ /All rights reserved/i ) {
323 warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
258324 }
259 }
260 return $self;
261 }
262
263 sub write {
264 my $self = shift;
265 return $self unless $self->is_admin;
266 $self->admin->write_meta;
267 return $self;
268 }
269
270 sub version_from {
271 require ExtUtils::MM_Unix;
272 my ( $self, $file ) = @_;
273 $self->version( ExtUtils::MM_Unix->parse_version($file) );
274 }
275
276 sub abstract_from {
277 require ExtUtils::MM_Unix;
278 my ( $self, $file ) = @_;
279 $self->abstract(
280 bless(
281 { DISTNAME => $self->name },
282 'ExtUtils::MM_Unix'
283 )->parse_abstract($file)
284 );
285 }
286
287 # Add both distribution and module name
288 sub name_from {
289 my ($self, $file) = @_;
290 if (
291 Module::Install::_read($file) =~ m/
292 ^ \s*
293 package \s*
294 ([\w:]+)
295 \s* ;
296 /ixms
297 ) {
298 my ($name, $module_name) = ($1, $1);
299 $name =~ s{::}{-}g;
300 $self->name($name);
301 unless ( $self->module_name ) {
302 $self->module_name($module_name);
303 }
304 } else {
305 die "Cannot determine name from $file\n";
306 }
307 }
308
309 sub perl_version_from {
310 my $self = shift;
311 if (
312 Module::Install::_read($_[0]) =~ m/
313 ^
314 (?:use|require) \s*
315 v?
316 ([\d_\.]+)
317 \s* ;
318 /ixms
319 ) {
320 my $perl_version = $1;
321 $perl_version =~ s{_}{}g;
322 $self->perl_version($perl_version);
323 } else {
324 warn "Cannot determine perl version info from $_[0]\n";
325 return;
326 }
327 }
328
329 sub author_from {
330 my $self = shift;
331 my $content = Module::Install::_read($_[0]);
332 if ($content =~ m/
333 =head \d \s+ (?:authors?)\b \s*
334 ([^\n]*)
335 |
336 =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
337 .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
338 ([^\n]*)
339 /ixms) {
340 my $author = $1 || $2;
341 $author =~ s{E<lt>}{<}g;
342 $author =~ s{E<gt>}{>}g;
343 $self->author($author);
344 } else {
345 warn "Cannot determine author info from $_[0]\n";
346 }
347 }
348
349 sub license_from {
350 my $self = shift;
351 if (
352 Module::Install::_read($_[0]) =~ m/
353 (
354 =head \d \s+
355 (?:licen[cs]e|licensing|copyright|legal)\b
356 .*?
357 )
358 (=head\\d.*|=cut.*|)
359 \z
360 /ixms ) {
361 my $license_text = $1;
362 my @phrases = (
363 'under the same (?:terms|license) as perl itself' => 'perl', 1,
364 'GNU public license' => 'gpl', 1,
365 'GNU lesser public license' => 'lgpl', 1,
366 'BSD license' => 'bsd', 1,
367 'Artistic license' => 'artistic', 1,
368 'GPL' => 'gpl', 1,
369 'LGPL' => 'lgpl', 1,
370 'BSD' => 'bsd', 1,
371 'Artistic' => 'artistic', 1,
372 'MIT' => 'mit', 1,
373 'proprietary' => 'proprietary', 0,
374 );
375 while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
376 $pattern =~ s{\s+}{\\s+}g;
377 if ( $license_text =~ /\b$pattern\b/i ) {
378 if ( $osi and $license_text =~ /All rights reserved/i ) {
379 print "WARNING: 'All rights reserved' in copyright may invalidate Open Source license.\n";
380 }
381 $self->license($license);
382 return 1;
383 }
384 }
385 }
386
387 warn "Cannot determine license info from $_[0]\n";
388 return 'unknown';
389 }
390
391 sub install_script {
392 my $self = shift;
393 my $args = $self->makemaker_args;
394 my $exe = $args->{EXE_FILES} ||= [];
395 foreach ( @_ ) {
396 if ( -f $_ ) {
397 push @$exe, $_;
398 } elsif ( -d 'script' and -f "script/$_" ) {
399 push @$exe, "script/$_";
400 } else {
401 die "Cannot find script '$_'";
402 }
403 }
325 $self->license($license);
326 return 1;
327 }
328 }
329 }
330
331 warn "Cannot determine license info from $file\n";
332 return 'unknown';
404333 }
405334
406335 1;
33 use strict;
44 use Module::Install::Base;
55
6 use vars qw{$VERSION @ISA $ISCORE};
6 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
9 $ISCORE = 1;
910 @ISA = qw{Module::Install::Base};
10 $ISCORE = 1;
1111 }
1212
1313 # determine if the user needs nmake, and download it if needed
1515 my $self = shift;
1616 $self->load('can_run');
1717 $self->load('get_file');
18
18
1919 require Config;
2020 return unless (
2121 $^O eq 'MSWin32' and
3737 remove => 1,
3838 );
3939
40 die <<'END_MESSAGE' unless $rv;
40 if (!$rv) {
41 die <<'END_MESSAGE';
4142
4243 -------------------------------------------------------------------------------
4344
5758
5859 -------------------------------------------------------------------------------
5960 END_MESSAGE
60
61 }
6162 }
6263
6364 1;
33 use strict;
44 use Module::Install::Base;
55
6 use vars qw{$VERSION @ISA $ISCORE};
6 use vars qw{$VERSION $ISCORE @ISA};
77 BEGIN {
8 $VERSION = '0.75';
8 $VERSION = '0.67';
9 $ISCORE = 1;
910 @ISA = qw{Module::Install::Base};
10 $ISCORE = 1;
1111 }
1212
1313 sub WriteAll {
14 my $self = shift;
15 my %args = (
16 meta => 1,
17 sign => 0,
18 inline => 0,
19 check_nmake => 1,
20 @_,
21 );
14 my $self = shift;
15 my %args = (
16 meta => 1,
17 sign => 0,
18 inline => 0,
19 check_nmake => 1,
20 @_
21 );
2222
23 $self->sign(1) if $args{sign};
24 $self->Meta->write if $args{meta};
25 $self->admin->WriteAll(%args) if $self->is_admin;
23 $self->sign(1) if $args{sign};
24 $self->Meta->write if $args{meta};
25 $self->admin->WriteAll(%args) if $self->is_admin;
2626
27 $self->check_nmake if $args{check_nmake};
28 unless ( $self->makemaker_args->{PL_FILES} ) {
29 $self->makemaker_args( PL_FILES => {} );
30 }
31
32 if ( $args{inline} ) {
33 $self->Inline->write;
34 } else {
35 $self->Makefile->write;
36 }
27 if ( $0 =~ /Build.PL$/i ) {
28 $self->Build->write;
29 } else {
30 $self->check_nmake if $args{check_nmake};
31 unless ( $self->makemaker_args->{'PL_FILES'} ) {
32 $self->makemaker_args( PL_FILES => {} );
33 }
34 if ($args{inline}) {
35 $self->Inline->write;
36 } else {
37 $self->Makefile->write;
38 }
39 }
3740 }
3841
3942 1;
1616 # 3. The ./inc/ version of Module::Install loads
1717 # }
1818
19 BEGIN {
20 require 5.004;
21 }
19 use 5.004;
2220 use strict 'vars';
2321
2422 use vars qw{$VERSION};
2523 BEGIN {
26 # All Module::Install core packages now require synchronised versions.
27 # This will be used to ensure we don't accidentally load old or
28 # different versions of modules.
29 # This is not enforced yet, but will be some time in the next few
30 # releases once we can make sure it won't clash with custom
31 # Module::Install extensions.
32 $VERSION = '0.75';
33
34 *inc::Module::Install::VERSION = *VERSION;
35 @inc::Module::Install::ISA = __PACKAGE__;
36
37 }
38
39
40
41
24 # All Module::Install core packages now require synchronised versions.
25 # This will be used to ensure we don't accidentally load old or
26 # different versions of modules.
27 # This is not enforced yet, but will be some time in the next few
28 # releases once we can make sure it won't clash with custom
29 # Module::Install extensions.
30 $VERSION = '0.67';
31 }
4232
4333 # Whether or not inc::Module::Install is actually loaded, the
4434 # $INC{inc/Module/Install.pm} is what will still get set as long as
4737 # they may not have a MI version that works with the Makefile.PL. This would
4838 # result in false errors or unexpected behaviour. And we don't want that.
4939 my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
50 unless ( $INC{$file} ) { die <<"END_DIE" }
51
40 unless ( $INC{$file} ) {
41 die <<"END_DIE";
5242 Please invoke ${\__PACKAGE__} with:
5343
54 use inc::${\__PACKAGE__};
44 use inc::${\__PACKAGE__};
5545
5646 not:
5747
58 use ${\__PACKAGE__};
48 use ${\__PACKAGE__};
5949
6050 END_DIE
61
62
63
64
51 }
6552
6653 # If the script that is loading Module::Install is from the future,
6754 # then make will detect this and cause it to re-run over and over
6855 # again. This is bad. Rather than taking action to touch it (which
6956 # is unreliable on some platforms and requires write permissions)
7057 # for now we should catch this and refuse to run.
71 if ( -f $0 and (stat($0))[9] > time ) { die <<"END_DIE" }
72
58 if ( -f $0 and (stat($0))[9] > time ) {
59 die << "END_DIE";
7360 Your installer $0 has a modification time in the future.
7461
7562 This is known to create infinite loops in make.
7764 Please correct this, then run $0 again.
7865
7966 END_DIE
80
81
82
83
84
85 # Build.PL was formerly supported, but no longer is due to excessive
86 # difficulty in implementing every single feature twice.
87 if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
88
89 Module::Install no longer supports Build.PL.
90
91 It was impossible to maintain duel backends, and has been deprecated.
92
93 Please remove all Build.PL files and only use the Makefile.PL installer.
94
95 END_DIE
96
97
98
99
100
101 # To save some more typing in Module::Install installers, every...
102 # use inc::Module::Install
103 # ...also acts as an implicit use strict.
104 $^H |= strict::bits(qw(refs subs vars));
105
106
107
108
67 }
10968
11069 use Cwd ();
11170 use File::Find ();
11271 use File::Path ();
11372 use FindBin;
11473
74 *inc::Module::Install::VERSION = *VERSION;
75 @inc::Module::Install::ISA = __PACKAGE__;
76
11577 sub autoload {
116 my $self = shift;
117 my $who = $self->_caller;
118 my $cwd = Cwd::cwd();
119 my $sym = "${who}::AUTOLOAD";
120 $sym->{$cwd} = sub {
121 my $pwd = Cwd::cwd();
122 if ( my $code = $sym->{$pwd} ) {
123 # delegate back to parent dirs
124 goto &$code unless $cwd eq $pwd;
125 }
126 $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
127 unshift @_, ( $self, $1 );
128 goto &{$self->can('call')} unless uc($1) eq $1;
129 };
78 my $self = shift;
79 my $who = $self->_caller;
80 my $cwd = Cwd::cwd();
81 my $sym = "${who}::AUTOLOAD";
82 $sym->{$cwd} = sub {
83 my $pwd = Cwd::cwd();
84 if ( my $code = $sym->{$pwd} ) {
85 # delegate back to parent dirs
86 goto &$code unless $cwd eq $pwd;
87 }
88 $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
89 unshift @_, ($self, $1);
90 goto &{$self->can('call')} unless uc($1) eq $1;
91 };
13092 }
13193
13294 sub import {
133 my $class = shift;
134 my $self = $class->new(@_);
135 my $who = $self->_caller;
136
137 unless ( -f $self->{file} ) {
138 require "$self->{path}/$self->{dispatch}.pm";
139 File::Path::mkpath("$self->{prefix}/$self->{author}");
140 $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
141 $self->{admin}->init;
142 @_ = ($class, _self => $self);
143 goto &{"$self->{name}::import"};
144 }
145
146 *{"${who}::AUTOLOAD"} = $self->autoload;
147 $self->preload;
148
149 # Unregister loader and worker packages so subdirs can use them again
150 delete $INC{"$self->{file}"};
151 delete $INC{"$self->{path}.pm"};
152
153 return 1;
95 my $class = shift;
96 my $self = $class->new(@_);
97 my $who = $self->_caller;
98
99 unless ( -f $self->{file} ) {
100 require "$self->{path}/$self->{dispatch}.pm";
101 File::Path::mkpath("$self->{prefix}/$self->{author}");
102 $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
103 $self->{admin}->init;
104 @_ = ($class, _self => $self);
105 goto &{"$self->{name}::import"};
106 }
107
108 *{"${who}::AUTOLOAD"} = $self->autoload;
109 $self->preload;
110
111 # Unregister loader and worker packages so subdirs can use them again
112 delete $INC{"$self->{file}"};
113 delete $INC{"$self->{path}.pm"};
154114 }
155115
156116 sub preload {
157 my $self = shift;
158 unless ( $self->{extensions} ) {
159 $self->load_extensions(
160 "$self->{prefix}/$self->{path}", $self
161 );
162 }
163
164 my @exts = @{$self->{extensions}};
165 unless ( @exts ) {
166 my $admin = $self->{admin};
167 @exts = $admin->load_all_extensions;
168 }
169
170 my %seen;
171 foreach my $obj ( @exts ) {
172 while (my ($method, $glob) = each %{ref($obj) . '::'}) {
173 next unless $obj->can($method);
174 next if $method =~ /^_/;
175 next if $method eq uc($method);
176 $seen{$method}++;
177 }
178 }
179
180 my $who = $self->_caller;
181 foreach my $name ( sort keys %seen ) {
182 *{"${who}::$name"} = sub {
183 ${"${who}::AUTOLOAD"} = "${who}::$name";
184 goto &{"${who}::AUTOLOAD"};
185 };
186 }
117 my ($self) = @_;
118
119 unless ( $self->{extensions} ) {
120 $self->load_extensions(
121 "$self->{prefix}/$self->{path}", $self
122 );
123 }
124
125 my @exts = @{$self->{extensions}};
126 unless ( @exts ) {
127 my $admin = $self->{admin};
128 @exts = $admin->load_all_extensions;
129 }
130
131 my %seen;
132 foreach my $obj ( @exts ) {
133 while (my ($method, $glob) = each %{ref($obj) . '::'}) {
134 next unless $obj->can($method);
135 next if $method =~ /^_/;
136 next if $method eq uc($method);
137 $seen{$method}++;
138 }
139 }
140
141 my $who = $self->_caller;
142 foreach my $name ( sort keys %seen ) {
143 *{"${who}::$name"} = sub {
144 ${"${who}::AUTOLOAD"} = "${who}::$name";
145 goto &{"${who}::AUTOLOAD"};
146 };
147 }
187148 }
188149
189150 sub new {
190 my ($class, %args) = @_;
191
192 # ignore the prefix on extension modules built from top level.
193 my $base_path = Cwd::abs_path($FindBin::Bin);
194 unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
195 delete $args{prefix};
196 }
197
198 return $args{_self} if $args{_self};
199
200 $args{dispatch} ||= 'Admin';
201 $args{prefix} ||= 'inc';
202 $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
203 $args{bundle} ||= 'inc/BUNDLES';
204 $args{base} ||= $base_path;
205 $class =~ s/^\Q$args{prefix}\E:://;
206 $args{name} ||= $class;
207 $args{version} ||= $class->VERSION;
208 unless ( $args{path} ) {
209 $args{path} = $args{name};
210 $args{path} =~ s!::!/!g;
211 }
212 $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
213 $args{wrote} = 0;
214
215 bless( \%args, $class );
151 my ($class, %args) = @_;
152
153 # ignore the prefix on extension modules built from top level.
154 my $base_path = Cwd::abs_path($FindBin::Bin);
155 unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
156 delete $args{prefix};
157 }
158
159 return $args{_self} if $args{_self};
160
161 $args{dispatch} ||= 'Admin';
162 $args{prefix} ||= 'inc';
163 $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
164 $args{bundle} ||= 'inc/BUNDLES';
165 $args{base} ||= $base_path;
166 $class =~ s/^\Q$args{prefix}\E:://;
167 $args{name} ||= $class;
168 $args{version} ||= $class->VERSION;
169 unless ( $args{path} ) {
170 $args{path} = $args{name};
171 $args{path} =~ s!::!/!g;
172 }
173 $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
174
175 bless( \%args, $class );
216176 }
217177
218178 sub call {
223183 }
224184
225185 sub load {
226 my ($self, $method) = @_;
227
228 $self->load_extensions(
229 "$self->{prefix}/$self->{path}", $self
230 ) unless $self->{extensions};
231
232 foreach my $obj (@{$self->{extensions}}) {
233 return $obj if $obj->can($method);
234 }
235
236 my $admin = $self->{admin} or die <<"END_DIE";
186 my ($self, $method) = @_;
187
188 $self->load_extensions(
189 "$self->{prefix}/$self->{path}", $self
190 ) unless $self->{extensions};
191
192 foreach my $obj (@{$self->{extensions}}) {
193 return $obj if $obj->can($method);
194 }
195
196 my $admin = $self->{admin} or die <<"END_DIE";
237197 The '$method' method does not exist in the '$self->{prefix}' path!
238198 Please remove the '$self->{prefix}' directory and run $0 again to load it.
239199 END_DIE
240200
241 my $obj = $admin->load($method, 1);
242 push @{$self->{extensions}}, $obj;
243
244 $obj;
201 my $obj = $admin->load($method, 1);
202 push @{$self->{extensions}}, $obj;
203
204 $obj;
245205 }
246206
247207 sub load_extensions {
248 my ($self, $path, $top) = @_;
249
250 unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
251 unshift @INC, $self->{prefix};
252 }
253
254 foreach my $rv ( $self->find_extensions($path) ) {
255 my ($file, $pkg) = @{$rv};
256 next if $self->{pathnames}{$pkg};
257
258 local $@;
259 my $new = eval { require $file; $pkg->can('new') };
260 unless ( $new ) {
261 warn $@ if $@;
262 next;
263 }
264 $self->{pathnames}{$pkg} = delete $INC{$file};
265 push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
266 }
267
268 $self->{extensions} ||= [];
208 my ($self, $path, $top) = @_;
209
210 unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
211 unshift @INC, $self->{prefix};
212 }
213
214 foreach my $rv ( $self->find_extensions($path) ) {
215 my ($file, $pkg) = @{$rv};
216 next if $self->{pathnames}{$pkg};
217
218 local $@;
219 my $new = eval { require $file; $pkg->can('new') };
220 unless ( $new ) {
221 warn $@ if $@;
222 next;
223 }
224 $self->{pathnames}{$pkg} = delete $INC{$file};
225 push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
226 }
227
228 $self->{extensions} ||= [];
269229 }
270230
271231 sub find_extensions {
272 my ($self, $path) = @_;
273
274 my @found;
275 File::Find::find( sub {
276 my $file = $File::Find::name;
277 return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
278 my $subpath = $1;
279 return if lc($subpath) eq lc($self->{dispatch});
280
281 $file = "$self->{path}/$subpath.pm";
282 my $pkg = "$self->{name}::$subpath";
283 $pkg =~ s!/!::!g;
284
285 # If we have a mixed-case package name, assume case has been preserved
286 # correctly. Otherwise, root through the file to locate the case-preserved
287 # version of the package name.
288 if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
289 my $content = Module::Install::_read($subpath . '.pm');
290 my $in_pod = 0;
291 foreach ( split //, $content ) {
292 $in_pod = 1 if /^=\w/;
293 $in_pod = 0 if /^=cut/;
294 next if ($in_pod || /^=cut/); # skip pod text
295 next if /^\s*#/; # and comments
296 if ( m/^\s*package\s+($pkg)\s*;/i ) {
297 $pkg = $1;
298 last;
299 }
300 }
301 }
302
303 push @found, [ $file, $pkg ];
304 }, $path ) if -d $path;
305
306 @found;
307 }
308
309
310
311
312
313 #####################################################################
314 # Utility Functions
232 my ($self, $path) = @_;
233
234 my @found;
235 File::Find::find( sub {
236 my $file = $File::Find::name;
237 return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
238 my $subpath = $1;
239 return if lc($subpath) eq lc($self->{dispatch});
240
241 $file = "$self->{path}/$subpath.pm";
242 my $pkg = "$self->{name}::$subpath";
243 $pkg =~ s!/!::!g;
244
245 # If we have a mixed-case package name, assume case has been preserved
246 # correctly. Otherwise, root through the file to locate the case-preserved
247 # version of the package name.
248 if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
249 open PKGFILE, "<$subpath.pm" or die "find_extensions: Can't open $subpath.pm: $!";
250 my $in_pod = 0;
251 while ( <PKGFILE> ) {
252 $in_pod = 1 if /^=\w/;
253 $in_pod = 0 if /^=cut/;
254 next if ($in_pod || /^=cut/); # skip pod text
255 next if /^\s*#/; # and comments
256 if ( m/^\s*package\s+($pkg)\s*;/i ) {
257 $pkg = $1;
258 last;
259 }
260 }
261 close PKGFILE;
262 }
263
264 push @found, [ $file, $pkg ];
265 }, $path ) if -d $path;
266
267 @found;
268 }
315269
316270 sub _caller {
317 my $depth = 0;
318 my $call = caller($depth);
319 while ( $call eq __PACKAGE__ ) {
320 $depth++;
321 $call = caller($depth);
322 }
323 return $call;
324 }
325
326 sub _read {
327 local *FH;
328 open FH, "< $_[0]" or die "open($_[0]): $!";
329 my $str = do { local $/; <FH> };
330 close FH or die "close($_[0]): $!";
331 return $str;
332 }
333
334 sub _write {
335 local *FH;
336 open FH, "> $_[0]" or die "open($_[0]): $!";
337 foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!" }
338 close FH or die "close($_[0]): $!";
339 }
340
341 sub _version {
342 my $s = shift || 0;
343 $s =~ s/^(\d+)\.?//;
344 my $l = $1 || 0;
345 my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g;
346 $l = $l . '.' . join '', @v if @v;
347 return $l + 0;
271 my $depth = 0;
272 my $call = caller($depth);
273 while ( $call eq __PACKAGE__ ) {
274 $depth++;
275 $call = caller($depth);
276 }
277 return $call;
348278 }
349279
350280 1;
351
352 # Copyright 2008 Adam Kennedy.
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe1 - The (always classic) B<Point> example.
6
7 =head1 SYNOPSIS
8
9 package Point;
10 use Moose;
11
12 has 'x' => (isa => 'Int', is => 'ro');
13 has 'y' => (isa => 'Int', is => 'rw');
14
15 sub clear {
16 my $self = shift;
17 $self->{x} = 0;
18 $self->y(0);
19 }
20
21 package Point3D;
22 use Moose;
23
24 extends 'Point';
25
26 has 'z' => (isa => 'Int');
27
28 after 'clear' => sub {
29 my $self = shift;
30 $self->{z} = 0;
31 };
32
33 =head1 DESCRIPTION
34
35 This is the classic Point example. This one in particular I took
36 from the Perl 6 Apocalypse 12 document, but it is similar to the
37 example found in the classic K&R C book as well, and many other
38 places. And now, onto the code:
39
40 As with all Perl 5 classes, a Moose class is defined in a package.
41 Moose now handles turning on C<strict> and C<warnings> for you, so
42 all you need to do is say C<use Moose>, and no kittens will die.
43
44 By loading Moose, we are enabling the loading of the Moose
45 "environment" into our package. This means that we import some
46 functions which serve as Moose "keywords". These aren't anything
47 fancy, just plain old exported functions.
48
49 Another important thing happens at this stage as well. Moose will
50 automatically set your package's superclass to be L<Moose::Object>.
51 The reason we do this, is so that we can be sure that your class
52 will inherit from L<Moose::Object> and get the benefits that
53 provides (such as a constructor; see L<Moose::Object> for details).
54 However, you don't actually I<have> to inherit from L<Moose::Object>
55 if you don't want to. All Moose features will still be accessible to
56 you.
57
58 Now, onto the keywords. The first one we see here is C<has>, which
59 defines an instance attribute in your class:
60
61 has 'x' => (isa => 'Int', is => 'ro');
62
63 This will create an attribute named C<x>, which will expect the
64 value stored in the attribute to pass the type constraint C<Int> (1),
65 and the accessor generated for this attribute will be read-only
66 (abbreviated as C<ro>).
67
68 The next C<has> line is very similar, with only one difference:
69
70 has 'y' => (isa => 'Int', is => 'rw');
71
72 A read/write (abbreviated as C<rw>) accessor will be generated for
73 the C<y> attribute.
74
75 At this point the attributes have been defined, and it is time to
76 define our methods. In Moose, as with regular Perl 5 OO, a method
77 is just a subroutine defined within the package. So here we create
78 the C<clear> method.
79
80 sub clear {
81 my $self = shift;
82 $self->{x} = 0;
83 $self->y(0);
84 }
85
86 It is pretty standard, the only thing to note is that we are directly
87 accessing the C<x> slot in the instance L<(2)>. This is because the
88 value was created with a read-only accessor. This also shows that Moose
89 objects are not anything out of the ordinary, but just regular old
90 blessed HASH references. This means they are very compatible with
91 other Perl 5 (non-Moose) classes as well.
92
93 The next part of the code to review is the B<Point> subclass,
94 B<Point3D>. The first item you might notice is that we do not use
95 the standard C<use base> declaration here. Instead we use the Moose
96 keyword C<extends> like so:
97
98 extends 'Point';
99
100 This keyword will function very much like C<use base> does in that
101 it will make an attempt to load your class if it has not already been
102 loaded. However, it differs on one important point. The C<extends>
103 keyword will overwrite any previous values in your package's C<@ISA>,
104 where C<use base> will C<push> values onto the package's C<@ISA>. It
105 is my opinion that the behavior of C<extends> is more intuitive in
106 that it is more explicit about defining the superclass relationship.
107
108 A small digression here: both Moose and C<extends> support multiple
109 inheritance. You simply pass all the superclasses to C<extends>,
110 like so:
111
112 extends 'Foo', 'Bar', 'Baz';
113
114 Now, back to our B<Point3D> class. The next thing we do is to create
115 a new attribute for B<Point3D> called C<z>.
116
117 has 'z' => (isa => 'Int');
118
119 As with B<Point>'s C<x> and C<y> attributes, this attribute has a
120 type constraint of C<Int>, but it differs in that it does B<not>
121 ask for any autogenerated accessors. The result being (aside from
122 broken object encapsulation) that C<z> is a private attribute.
123
124 Next comes another Moose feature which we call method "modifiers"
125 (or method "advice" for the AOP inclined). The modifier used here
126 is the C<after> modifier, and looks like this:
127
128 after 'clear' => sub {
129 my $self = shift;
130 $self->{z} = 0;
131 };
132
133 This modifier tells Moose to install a C<clear> method for
134 B<Point3D> that will first run the C<clear> method for the
135 superclass (in this case C<Point::clear>), and then run this
136 method I<after> it (passing in the same arguments as the original
137 method).
138
139 Now, of course using the C<after> modifier is not the only way to
140 accomplish this. I mean, after all, this B<is> Perl right? You
141 would get the same results with this code:
142
143 sub clear {
144 my $self = shift;
145 $self->SUPER::clear();
146 $self->{z} = 0;
147 }
148
149 You could also use another Moose method modifier, C<override> here,
150 and get the same results again. Here is how that would look:
151
152 override 'clear' => sub {
153 my $self = shift;
154 super();
155 $self->{z} = 0;
156 };
157
158 The C<override> modifier allows you to use the C<super> keyword
159 within it to dispatch to the superclass's method in a very Ruby-ish
160 style.
161
162 Now, of course, what use is a class if you can't instantiate objects
163 with it? Since B<Point> inherits from L<Moose::Object>, it will also
164 inherit the default L<Moose::Object> constructor: C<new>. Here
165 are two examples of how that is used:
166
167 my $point = Point->new(x => 1, y => 2);
168 my $point3d = Point3D->new(x => 1, y => 2, z => 3);
169
170 As you can see, C<new> accepts named argument pairs for any of the
171 attributes. It does not I<require> that you pass in the all the
172 attributes, and it will politely ignore any named arguments it does
173 not recognize.
174
175 From here on, you can use C<$point> and C<$point3d> just as you would
176 any other Perl 5 object. For a more detailed example of what can be
177 done, you can refer to the F<t/000_recipes/001_recipe.t> test file.
178
179 =head1 CONCLUSION
180
181 I hope this recipe has given you some explanation of how to use
182 Moose to build your Perl 5 classes. The next recipe will build upon
183 the basics shown here with more complex attributes and methods.
184 Please read on :)
185
186 =head1 FOOTNOTES
187
188 =over 4
189
190 =item (1)
191
192 Several default type constraints are provided by Moose, of which
193 C<Int> is one. For more information on the builtin type constraints
194 and the type constraint system in general, see the
195 L<Moose::Util::TypeConstraints> documentation.
196
197 =item (2)
198
199 Moose supports using instance structures other than blessed hash
200 references (such as in a glob reference -- see
201 L<MooseX::GlobRef::Object>). If you want your Moose classes to
202 be interchangeable, it is advisable to avoid direct instance
203 access, like that shown above. Moose does let you get and set
204 attributes directly without exposing the instance structure, but
205 that's an advanced topic (intrepid readers should refer to the
206 L<Moose::Meta::Attribute documentation>).
207
208 =back
209
210 =head1 SEE ALSO
211
212 =over 4
213
214 =item Method Modifiers
215
216 The concept of method modifiers is directly ripped off from CLOS. A
217 great explanation of them can be found by following this link.
218
219 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
220
221 =back
222
223 =head1 AUTHOR
224
225 Stevan Little E<lt>stevan@iinteractive.comE<gt>
226
227 =head1 COPYRIGHT AND LICENSE
228
229 Copyright 2006-2008 by Infinity Interactive, Inc.
230
231 L<http://www.iinteractive.com>
232
233 This library is free software; you can redistribute it and/or modify
234 it under the same terms as Perl itself.
235
236 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe10 - Operator overloading, subtypes, and coercion
6
7 =head1 SYNOPSIS
8
9 package Human;
10
11 use Moose;
12 use Moose::Util::TypeConstraints;
13
14 subtype 'Gender'
15 => as 'Str'
16 => where { $_ =~ m{^[mf]$}s };
17
18 has 'gender' => ( is => 'ro', isa => 'Gender', required => 1 );
19
20 has 'mother' => ( is => 'ro', isa => 'Human' );
21 has 'father' => ( is => 'ro', isa => 'Human' );
22
23 use overload '+' => \&_overload_add, fallback => 1;
24
25 sub _overload_add {
26 my ($one, $two) = @_;
27
28 die('Only male and female humans may create children')
29 if ($one->gender() eq $two->gender());
30
31 my ( $mother, $father ) = ( $one->gender eq 'f' ? ($one, $two) : ($two, $one) );
32
33 my $gender = 'f';
34 $gender = 'm' if (rand() >= 0.5);
35
36 return Human->new(
37 gender => $gender,
38 mother => $mother,
39 father => $father,
40 );
41 }
42
43 =head1 DESCRIPTION
44
45 This Moose cookbook recipe shows how operator overloading, coercion,
46 and sub types can be used to mimic the human reproductive system
47 (well, the selection of genes at least). Assumes a basic
48 understanding of Moose.
49
50 =head1 INTRODUCTION
51
52 The example in the SYNOPSIS outlines a very basic use of
53 operator overloading and Moose. The example creates a class
54 that allows you to add together two humans and produce a
55 child from them.
56
57 The two parents must be of the opposite gender, as to do
58 otherwise wouldn't be biologically possible no matter how much
59 I might want to allow it.
60
61 While this example works and gets the job done, it really isn't
62 all that useful. To take this a step further let's play around
63 with genes. Particularly the genes that dictate eye color. Why
64 eye color? Because it is simple. There are two genes that have
65 the most affect on eye color and each person carries two of each
66 gene. Now that will be useful!
67
68 Oh, and don't forget that you were promised some coercion goodness.
69
70 =head1 TECHNIQUES
71
72 First, let's quickly define the techniques that will be used.
73
74 =head2 Operator Overloading
75
76 Overloading operators takes a simple declaration of which operator
77 you want to overload and what method to call. See the perldoc for
78 overload to see some good, basic, examples.
79
80 =head2 Subtypes
81
82 Moose comes with 21 default type constraints, as documented in
83 L<Moose::Util::TypeConstraints>. Int, Str, and CodeRef are
84 all examples. Subtypes give you the ability to inherit the
85 constraints of an existing type, and adding additional
86 constraints on that type. An introduction to type constraints
87 is available in the L<Moose::Cookbook::Basics::Recipe4>.
88
89 =head2 Coercion
90
91 When an attribute is assigned a value its type constraint
92 is checked to validate the value. Normally, if the value
93 does not pass the constraint, an exception will be thrown.
94 But, it is possible with Moose to define the rules to coerce
95 values from one type to another. A good introduction to
96 this can be found in L<Moose::Cookbook::Basics::Recipe5>.
97
98 =head1 GENES
99
100 As I alluded to in the introduction, there are many different
101 genes that affect eye color. But, there are 2 genes that play
102 the most prominent role: gey and bey2. To get started let us
103 make classes for these genes.
104
105 =head2 bey2
106
107 package Human::Gene::bey2;
108
109 use Moose;
110 use Moose::Util::TypeConstraints;
111
112 type 'bey2Color' => where { $_ =~ m{^(?:brown|blue)$}s };
113
114 has 'color' => ( is => 'ro', isa => 'bey2Color' );
115
116 This class is really simple. All we need to know about the bey2
117 gene is whether it is of the blue or brown variety. As you can
118 see a type constraint for the color attribute has been created
119 which validates for the two possible colors.
120
121 =head2 gey
122
123 package Human::Gene::gey;
124
125 use Moose;
126 use Moose::Util::TypeConstraints;
127
128 type 'geyColor' => where { $_ =~ m{^(?:green|blue)$}s };
129
130 has 'color' => ( is => 'ro', isa => 'geyColor' );
131
132 The gey gene is nearly identical to the bey2, except that it
133 has a green or blue variety.
134
135 =head1 EYE COLOR
136
137 Rather than throwing the 4 gene object (2xbey, 2xgey2) straight
138 on to the Human class, let's create an intermediate class that
139 abstracts the logic behind eye color. This way the Human class
140 won't get all cluttered up with the details behind the different
141 characteristics that makes up a Human.
142
143 package Human::EyeColor;
144
145 use Moose;
146 use Moose::Util::TypeConstraints;
147
148 subtype 'bey2Gene'
149 => as 'Object'
150 => where { $_->isa('Human::Gene::bey2') };
151
152 coerce 'bey2Gene'
153 => from 'Str'
154 => via { Human::Gene::bey2->new( color => $_ ) };
155
156 subtype 'geyGene'
157 => as 'Object'
158 => where { $_->isa('Human::Gene::gey') };
159
160 coerce 'geyGene'
161 => from 'Str'
162 => via { Human::Gene::gey->new( color => $_ ) };
163
164 has 'bey2_1' => ( is => 'ro', isa => 'bey2Gene', coerce => 1 );
165 has 'bey2_2' => ( is => 'ro', isa => 'bey2Gene', coerce => 1 );
166
167 has 'gey_1' => ( is => 'ro', isa => 'geyGene', coerce => 1 );
168 has 'gey_2' => ( is => 'ro', isa => 'geyGene', coerce => 1 );
169
170 So, we now have a class that can hold the four genes that dictate
171 eye color. This isn't quite enough, as we also need to calculate
172 what the human's actual eye color is as a result of the genes.
173
174 As with most genes there are recessive and dominant genes. The bey2
175 brown gene is dominant to both blue and green. The gey green gene is
176 recessive to the brown bey gene and dominant to the blues. Finally,
177 the bey and gey2 blue genes are recessive to both brown and green.
178
179 sub color {
180 my ( $self ) = @_;
181
182 return 'brown' if ($self->bey2_1->color() eq 'brown' or $self->bey2_2->color() eq 'brown');
183 return 'green' if ($self->gey_1->color() eq 'green' or $self->gey_2->color() eq 'green');
184 return 'blue';
185 }
186
187 To top it off, if I want to access color(), I want to be really lazy
188 about it. Perl overloading supports the ability to overload the
189 stringification of an object. So, normally if I did "$eye_color"
190 I'd get something like "Human::EyeColor=HASH(0xba9348)". What I
191 really want is "brown", "green", or "blue". To do this you overload
192 the stringification of the object.
193
194 use overload '""' => \&color, fallback => 1;
195
196 That's all and good, but don't forget the spawn! Our
197 humans have to have children, and those children need to inherit
198 genes from their parents. Let's use operator overloading so
199 that we can add (+) together two EyeColor characteristics to
200 create a new EyeColor that is derived in a similar manner as
201 the gene selection in human reproduction.
202
203 use overload '+' => \&_overload_add, fallback => 1;
204
205 sub _overload_add {
206 my ($one, $two) = @_;
207
208 my $one_bey2 = 'bey2_' . _rand2();
209 my $two_bey2 = 'bey2_' . _rand2();
210
211 my $one_gey = 'gey_' . _rand2();
212 my $two_gey = 'gey_' . _rand2();
213
214 return Human::EyeColor->new(
215 bey2_1 => $one->$one_bey2->color(),
216 bey2_2 => $two->$two_bey2->color(),
217 gey_1 => $one->$one_gey->color(),
218 gey_2 => $two->$two_gey->color(),
219 );
220 }
221
222 sub _rand2 {
223 return 1 + int( rand(2) );
224 }
225
226 What is happening here is we are overloading the addition
227 operator. When two eye color objects are added together
228 the _overload_add() method will be called with the two
229 objects on the left and right side of the + as arguments.
230 The return value of this method should be the expected
231 result of the addition. I'm not going to go in to the
232 details of how the gene's are selected as it should be
233 fairly self-explanatory.
234
235 =head1 HUMAN EVOLUTION
236
237 Our original human class in the SYNOPSIS requires very little
238 change to support the new EyeColor characteristic. All we
239 need to do is define a new subtype called EyeColor, a new
240 attribute called eye_color, and just for the sake of simple code
241 we'll coerce an arrayref of colors in to an EyeColor object.
242
243 use List::MoreUtils qw( zip );
244
245 subtype 'EyeColor'
246 => as 'Object'
247 => where { $_->isa('Human::EyeColor') };
248
249 coerce 'EyeColor'
250 => from 'ArrayRef'
251 => via {
252 my @genes = qw( bey2_1 bey2_2 gey_1 gey_2 );
253 return Human::EyeColor->new( zip( @genes, @$_ ) );
254 };
255
256 has 'eye_color' => ( is => 'ro', isa => 'EyeColor', coerce => 1, required => 1 );
257
258 And then in the _overload_add() of the Human class we modify
259 the creation of the child object to include the addition of
260 the mother and father's eye colors.
261
262 return Human->new(
263 gender => $gender,
264 eye_color => ( $one->eye_color() + $two->eye_color() ),
265 mother => $mother,
266 father => $father,
267 );
268
269 =head1 CONCLUSION
270
271 The three techniques used in this article - overloading, subtypes,
272 and coercion - provide the power to produce simple, flexible, powerful,
273 explicit, inheritable, and enjoyable interfaces.
274
275 If you want to get your hands on this code all combined together, and
276 working, download the Moose tarball and look at "t/000_recipes/012_genes.t".
277
278 =head1 NEXT STEPS
279
280 Has this been a real project we'd probably want to:
281
282 =over 4
283
284 =item Better Randomization with Crypt::Random
285
286 =item Characteristic Base Class
287
288 =item Mutating Genes
289
290 =item More Characteristics
291
292 =item Artificial Life
293
294 =back
295
296 =head1 AUTHOR
297
298 Aran Clary Deltac <bluefeet@cpan.org>
299
300 =head1 LICENSE
301
302 This work is licensed under a Creative Commons Attribution 3.0 Unported License.
303
304 License details are at: L<http://creativecommons.org/licenses/by/3.0/>
305
306 =cut
307
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe2 - A simple B<BankAccount> example
6
7 =head1 SYNOPSIS
8
9 package BankAccount;
10 use Moose;
11
12 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
13
14 sub deposit {
15 my ($self, $amount) = @_;
16 $self->balance($self->balance + $amount);
17 }
18
19 sub withdraw {
20 my ($self, $amount) = @_;
21 my $current_balance = $self->balance();
22 ($current_balance >= $amount)
23 || confess "Account overdrawn";
24 $self->balance($current_balance - $amount);
25 }
26
27 package CheckingAccount;
28 use Moose;
29
30 extends 'BankAccount';
31
32 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
33
34 before 'withdraw' => sub {
35 my ($self, $amount) = @_;
36 my $overdraft_amount = $amount - $self->balance();
37 if ($self->overdraft_account && $overdraft_amount > 0) {
38 $self->overdraft_account->withdraw($overdraft_amount);
39 $self->deposit($overdraft_amount);
40 }
41 };
42
43 =head1 DESCRIPTION
44
45 In the first recipe we demonstrated the construction of basic
46 Moose classes whose attributes had various accessor schemes and
47 builtin type constraints. However, our objects were very data-
48 oriented, and did not have many behavioral aspects (i.e. methods)
49 to them. In this recipe, we will expand upon the concepts from
50 the first recipe and give a more realistic scenario of more
51 behavior oriented classes.
52
53 We are using the example of a bank account, which has a standard
54 account (you can deposit money, withdraw money and check your
55 current balance), and a checking account which has optional
56 overdraft protection. The overdraft protection will protect the
57 owner of the checking account by automatically withdrawing the
58 needed funds from the overdraft account to ensure that a check
59 will not bounce.
60
61 Now, onto the code. The first class, B<BankAccount>, introduces a
62 new attribute feature: a default value.
63
64 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
65
66 This tells us that a B<BankAccount> has a C<balance> attribute,
67 which has the C<Int> type constraint, a read/write accessor,
68 and a default value of C<0>. This means that every instance of
69 B<BankAccount> that is created will have its C<balance> slot
70 initialized to C<0>. Very simple really :)
71
72 Next come the methods. The C<deposit> and C<withdraw> methods
73 should be fairly self-explanatory; they are nothing specific to
74 Moose, just your standard Perl 5 OO.
75
76 Now, onto the B<CheckingAccount> class. As you know from the
77 first recipe, the keyword C<extends> sets a class's superclass
78 relationship. Here we see that B<CheckingAccount> is a
79 B<BankAccount>. The next line introduces yet another new aspect
80 of Moose, that of class-based type-constraints:
81
82 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
83
84 Up until now, we have only had C<Int> type constraints, which
85 (as I said in the first recipe) is a builtin type constraint
86 that Moose provides for you. The C<BankAccount> type constraint
87 is new, and was actually defined the moment we created the
88 B<BankAccount> class itself. In fact, for every class in
89 your program, a corresponding type constraint will be created. This
90 means that in the first recipe, both C<Point> and C<Point3D> type
91 constraints were created, and in this recipe, both C<BankAccount>
92 and C<CheckingAccount> type constraints were created. Moose does
93 this as a convenience so that your class model and the type
94 constraint model can be kept in sync with one another. In short,
95 Moose makes sure that it will just DWIM (1).
96
97 Next, we come to the behavioral part of B<CheckingAccount>, and
98 again we see a method modifier, but this time it is a C<before>
99 modifier.
100
101 before 'withdraw' => sub {
102 my ($self, $amount) = @_;
103 my $overdraft_amount = $amount - $self->balance();
104 if ($self->overdraft_account && $overdraft_amount > 0) {
105 $self->overdraft_account->withdraw($overdraft_amount);
106 $self->deposit($overdraft_amount);
107 }
108 };
109
110 Just as with the C<after> modifier from the first recipe, Moose
111 will handle calling the superclass method (in this case the
112 C<BankAccount::withdraw> method). The C<before> modifier shown
113 above will run (obviously) I<before> the code from the superclass
114 with run. The C<before> modifier here implements the overdraft
115 protection by first checking if there are enough available
116 funds in the checking account and if not (and if there is an overdraft
117 account available), it transfers the appropriate funds into the
118 checking account.
119
120 As with the method modifier in the first recipe, there is another
121 way to accomplish this same thing using the built in C<SUPER::>
122 pseudo-package. So the above method is equivalent to the one here.
123
124 sub withdraw {
125 my ($self, $amount) = @_;
126 my $overdraft_amount = $amount - $self->balance();
127 if ($self->overdraft_account && $overdraft_amount > 0) {
128 $self->overdraft_account->withdraw($overdraft_amount);
129 $self->deposit($overdraft_amount);
130 }
131 $self->SUPER::withdraw($amount);
132 }
133
134 The benefits of taking the method modifier approach is that the
135 author of the B<BankAccount> subclass does not need to remember
136 to call C<SUPER::withdraw> and to pass it the C<$amount> argument.
137 Instead the method modifier ensures that all arguments make it
138 to the superclass method correctly. But this is actually more
139 than just a convenience for forgetful programmers, it also helps
140 isolate subclasses from changes in the superclasses. For instance,
141 if B<BankAccount::withdraw> were to add an additional argument
142 of some kind, the version of B<CheckingAccount::withdraw> which
143 uses C<SUPER::withdraw> would not pass that extra argument
144 correctly, whereas the method modifier version would automatically
145 pass along all arguments correctly.
146
147 Just as with the first recipe, object instantiation is a fairly
148 normal process, here is an example:
149
150 my $savings_account = BankAccount->new(balance => 250);
151 my $checking_account = CheckingAccount->new(
152 balance => 100,
153 overdraft_account => $savings_account
154 );
155
156 And as with the first recipe, a more in-depth example of using
157 these classes can be found in the F<t/000_recipes/002_recipe.t> test file.
158
159 =head1 CONCLUSION
160
161 The aim of this recipe was to take the knowledge gained in the
162 first recipe and expand upon it with a more realistic use case. I
163 hope that this recipe has accomplished this goal. The next recipe
164 will expand even more upon the capabilities of attributes in Moose
165 to create a behaviorally sophisticated class almost entirely
166 defined by attributes.
167
168 =head1 FOOTNOTES
169
170 =over 4
171
172 =item (1)
173
174 Moose does not attempt to encode a class's is-a relationships
175 within the type constraint hierarchy. Instead, Moose just
176 considers the class type constraint to be a subtype of C<Object>,
177 and specializes the constraint check to allow for subclasses. This
178 means that an instance of B<CheckingAccount> will pass a
179 C<BankAccount> type constraint successfully. For more details,
180 please refer to the L<Moose::Util::TypeConstraints> documentation.
181
182 =back
183
184 =head1 SEE ALSO
185
186 =over 4
187
188 =item Acknowledgment
189
190 The BankAccount example in this recipe is directly taken from the
191 examples in this chapter of "Practical Common Lisp":
192
193 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
194
195 =back
196
197 =head1 AUTHOR
198
199 Stevan Little E<lt>stevan@iinteractive.comE<gt>
200
201 =head1 COPYRIGHT AND LICENSE
202
203 Copyright 2006-2008 by Infinity Interactive, Inc.
204
205 L<http://www.iinteractive.com>
206
207 This library is free software; you can redistribute it and/or modify
208 it under the same terms as Perl itself.
209
210 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe3 - A lazy B<BinaryTree> example
6
7 =head1 SYNOPSIS
8
9 package BinaryTree;
10 use Moose;
11
12 has 'node' => (is => 'rw', isa => 'Any');
13
14 has 'parent' => (
15 is => 'rw',
16 isa => 'BinaryTree',
17 predicate => 'has_parent',
18 weak_ref => 1,
19 );
20
21 has 'left' => (
22 is => 'rw',
23 isa => 'BinaryTree',
24 predicate => 'has_left',
25 lazy => 1,
26 default => sub { BinaryTree->new(parent => $_[0]) },
27 );
28
29 has 'right' => (
30 is => 'rw',
31 isa => 'BinaryTree',
32 predicate => 'has_right',
33 lazy => 1,
34 default => sub { BinaryTree->new(parent => $_[0]) },
35 );
36
37 before 'right', 'left' => sub {
38 my ($self, $tree) = @_;
39 $tree->parent($self) if defined $tree;
40 };
41
42 =head1 DESCRIPTION
43
44 In this recipe we take a closer look at attributes, and see how
45 some of their more advanced features can be used to create fairly
46 complex behaviors.
47
48 The class in this recipe is a classic binary tree, each node in the
49 tree is represented by an instance of the B<BinaryTree> class. Each
50 instance has a C<node> slot to hold an arbitrary value, a C<right>
51 slot to hold the right node, a C<left> slot to hold the left node,
52 and finally a C<parent> slot to hold a reference back up the tree.
53
54 Now, let's start with the code. Our first attribute is the C<node>
55 slot, defined as such:
56
57 has 'node' => (is => 'rw', isa => 'Any');
58
59 If you recall from the previous recipes, this slot will have a read/write
60 accessor generated for it, and has a type constraint on it. The new item here is
61 the type constraint of C<Any>. C<Any> is the "root" of the
62 L<Moose::Util::TypeConstraints> type hierarchy. It means exactly what it says:
63 I<any> value passes the constraint. Now, you could just as easily have left out
64 the C<isa>, leaving the C<node> slot unconstrained and retaining this
65 behavior. But in this case, we are really including the type constraint for the
66 benefit of other programmers, not the computer. It makes clear my intent that
67 the C<node> attribute can be of any type, and that the class is a polymorphic
68 container.
69
70 Next, let's move on to the C<parent> slot:
71
72 has 'parent' => (
73 is => 'rw',
74 isa => 'BinaryTree',
75 predicate => 'has_parent',
76 weak_ref => 1,
77 );
78
79 As you already know, this code tells you that C<parent> gets a read/write
80 accessor and is constrained to only accept instances of B<BinaryTree>. You will
81 of course remember from the second recipe that the C<BinaryTree> type constraint
82 is automatically created for us by Moose.
83
84 The next attribute option is new, though: the C<predicate> option.
85 This option creates a method which can be used to check whether
86 a given slot (in this case C<parent>) has been initialized. In
87 this case it will create a method called C<has_parent>. Quite simple,
88 and quite handy too.
89
90 This brings us to our last attribute option, also a new one. Since C<parent> is
91 a circular reference (the tree in C<parent> should already have a reference to
92 this one, in its C<left> or C<right> node), we want to make sure that it is also
93 a weakened reference to avoid memory leaks. The C<weak_ref> attribute option
94 will do just that, C<weak_ref> simply takes a boolean value (C<1> or C<0>) and
95 then alters the accessor function to weaken the reference to any value stored in
96 the C<parent> slot (1).
97
98 Now, onto the C<left> and C<right> attributes. They are essentially identical,
99 save for different names, so I will just describe one here:
100
101 has 'left' => (
102 is => 'rw',
103 isa => 'BinaryTree',
104 predicate => 'has_left',
105 lazy => 1,
106 default => sub { BinaryTree->new(parent => $_[0]) },
107 );
108
109 You already know what the C<is>, C<isa> and C<predicate> options do, but now we
110 have two new options. These two options are actually linked together, in fact:
111 you cannot use the C<lazy> option unless you have set the C<default> option.
112 Class creation will fail with an exception (2).
113
114 Before I go into detail about how C<lazy> works, let me first
115 explain how C<default> works, and in particular why it is wrapped
116 in a CODE ref.
117
118 In the second recipe the B<BankAccount>'s C<balance> slot had a
119 default value of C<0>. Since Perl will copy strings and numbers
120 by value, this was all we had to say. But for any other item
121 (ARRAY ref, HASH ref, object instance, etc) you would need to
122 wrap it in a CODE reference, so this:
123
124 has 'foo' => (is => 'rw', default => []);
125
126 is actually illegal in Moose. Instead, what you really want is this:
127
128 has 'foo' => (is => 'rw', default => sub { [] });
129
130 This ensures that each instance of this class will get its own ARRAY ref in the
131 C<foo> slot.
132
133 One other feature of the CODE ref version of the C<default> option is that when
134 the subroutine is executed (to get the default value), we pass in the instance
135 where the slot will be stored. This can come in quite handy at times, as
136 illustrated above, with this code:
137
138 default => sub { BinaryTree->new(parent => $_[0]) },
139
140 The default value being generated is a new C<BinaryTree> instance for the
141 C<left> (or C<right>) slot. Here we set up the correct relationship by passing
142 the current instance as the C<parent> argument to the constructor.
143
144 Now, before we go on to the C<lazy> option, I want you to think
145 for a moment. When an instance of this class is created, and the
146 slots are being initialized, the "normal" behavior would be for
147 the C<left> and C<right> slots to be populated with a new instance
148 of B<BinaryTree>. In creating that instance of the C<left> or
149 C<right> slots, we would need to create new instances to populate
150 the C<left> and C<right> slots of I<those> instances. This would
151 continue in an I<infinitely recursive spiral of death> until you had
152 exhausted all available memory on your machine.
153
154 This is, of course, not good :)
155
156 Which brings us to the C<lazy> attribute option. The C<lazy> option does just
157 what it says: it lazily initializes the slot within the instance. This means
158 that it waits till absolutely the I<latest> possible moment to populate the
159 slot. So if you, the user, store a value in the slot, everything works normally,
160 and what you pass in is stored. However, if you I<read> the slot I<before>
161 storing a value in it, then at that I<exact> moment (and no sooner), the slot
162 will be populated with the value of the C<default> option.
163
164 This option is what allows the B<BinaryTree> class to instantiate
165 objects without fear of the I<infinitely recursive spiral of death>
166 mentioned earlier.
167
168 So, we have described a quite complex set of behaviors here, and not one method
169 had to be written. But wait, we aren't quite done yet; the autogenerated
170 C<right> and C<left> accessors are not completely correct. They will not install
171 the parental relationships that we need. We could write our own accessors, but
172 that would require us to implement all those features we got automatically (type
173 constraints, lazy initialization, and so on). Instead, we use method modifiers
174 again:
175
176 before 'right', 'left' => sub {
177 my ($self, $tree) = @_;
178 $tree->parent($self) if defined $tree;
179 };
180
181 This is a C<before> modifier, just like we saw in the second recipe, but with
182 two slight differences. First, we are applying this to more than one method at a
183 time. Since both the C<left> and C<right> methods need the same feature, it
184 makes sense. The second difference is that we are not wrapping an inherited
185 method anymore, but instead a method of our own local class. Wrapping local
186 methods is no different, the only requirement is that the wrappee be created
187 before the wrapper (after all, you cannot wrap something which doesn't exist,
188 right?).
189
190 Now, as with all the other recipes, you can go about using
191 B<BinaryTree> like any other Perl 5 class. A more detailed example of its
192 usage can be found in F<t/000_recipes/003_recipe.t>.
193
194 =head1 CONCLUSION
195
196 This recipe introduced you to some of the more advanced behavioral
197 possibilities of Moose's attribute mechanism. I hope that it has
198 opened your mind to the powerful possibilities of Moose. In the next
199 recipe we explore how we can create custom subtypes and take
200 advantage of the plethora of useful modules out on CPAN with Moose.
201
202 =head1 FOOTNOTES
203
204 =over 4
205
206 =item (1)
207
208 Weak references are tricky things, and should be used sparingly
209 and appropriately (such as in the case of circular refs). If you
210 are not careful, you will have slot values disappear "mysteriously"
211 because perls reference counting garbage collector has gone and
212 removed the item you are weak-referencing.
213
214 In short, don't use them unless you know what you are doing :)
215
216 =item (2)
217
218 You I<can> use the C<default> option without the C<lazy> option if
219 you like, as we showed in the second recipe.
220
221 And actually, you can use C<builder> instead of C<default>. See
222 L<Moose::Cookbook::Basics::Recipe9> for details.
223
224 =back
225
226 =head1 AUTHOR
227
228 Stevan Little E<lt>stevan@iinteractive.comE<gt>
229
230 =head1 COPYRIGHT AND LICENSE
231
232 Copyright 2006-2008 by Infinity Interactive, Inc.
233
234 L<http://www.iinteractive.com>
235
236 This library is free software; you can redistribute it and/or modify
237 it under the same terms as Perl itself.
238
239 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
6
7 =head1 SYNOPSIS
8
9 package Address;
10 use Moose;
11 use Moose::Util::TypeConstraints;
12
13 use Locale::US;
14 use Regexp::Common 'zip';
15
16 my $STATES = Locale::US->new;
17
18 subtype USState
19 => as Str
20 => where {
21 (exists $STATES->{code2state}{uc($_)} ||
22 exists $STATES->{state2code}{uc($_)})
23 };
24
25 subtype USZipCode
26 => as Value
27 => where {
28 /^$RE{zip}{US}{-extended => 'allow'}$/
29 };
30
31 has 'street' => (is => 'rw', isa => 'Str');
32 has 'city' => (is => 'rw', isa => 'Str');
33 has 'state' => (is => 'rw', isa => 'USState');
34 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
35
36 package Company;
37 use Moose;
38 use Moose::Util::TypeConstraints;
39
40 has 'name' => (is => 'rw', isa => 'Str', required => 1);
41 has 'address' => (is => 'rw', isa => 'Address');
42 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
43
44 sub BUILD {
45 my ($self, $params) = @_;
46 if ($params->{employees}) {
47 foreach my $employee (@{$params->{employees}}) {
48 $employee->company($self);
49 }
50 }
51 }
52
53 after 'employees' => sub {
54 my ($self, $employees) = @_;
55 if (defined $employees) {
56 foreach my $employee (@{$employees}) {
57 $employee->company($self);
58 }
59 }
60 };
61
62 package Person;
63 use Moose;
64
65 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
66 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
67 has 'middle_initial' => (is => 'rw', isa => 'Str',
68 predicate => 'has_middle_initial');
69 has 'address' => (is => 'rw', isa => 'Address');
70
71 sub full_name {
72 my $self = shift;
73 return $self->first_name .
74 ($self->has_middle_initial ?
75 ' ' . $self->middle_initial . '. '
76 :
77 ' ') .
78 $self->last_name;
79 }
80
81 package Employee;
82 use Moose;
83
84 extends 'Person';
85
86 has 'title' => (is => 'rw', isa => 'Str', required => 1);
87 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
88
89 override 'full_name' => sub {
90 my $self = shift;
91 super() . ', ' . $self->title
92 };
93
94 =head1 DESCRIPTION
95
96 In this recipe we introduce the C<subtype> keyword, and show
97 how it can be useful for specifying type constraints
98 without building an entire class to represent them. We
99 will also show how this feature can be used to leverage the
100 usefulness of CPAN modules. In addition to this, we will
101 introduce another attribute option.
102
103 Let's first look at the C<subtype> feature. In the B<Address> class we have
104 defined two subtypes. The first C<subtype> uses the L<Locale::US> module, which
105 provides two hashes which can be used to perform existential checks for state
106 names and their two letter state codes. It is a very simple and very useful
107 module, and perfect for use in a C<subtype> constraint.
108
109 my $STATES = Locale::US->new;
110 subtype USState
111 => as Str
112 => where {
113 (exists $STATES->{code2state}{uc($_)} ||
114 exists $STATES->{state2code}{uc($_)})
115 };
116
117 Because we know that states will be passed to us as strings, we
118 can make C<USState> a subtype of the built-in type constraint
119 C<Str>. This will ensure that anything which is a C<USState> will
120 also pass as a C<Str>. Next, we create a constraint specializer
121 using the C<where> keyword. The value being checked against in
122 the C<where> clause can be found in the C<$_> variable (1). Our
123 constraint specializer will then check whether the given string
124 is either a state name or a state code. If the string meets this
125 criteria, then the constraint will pass, otherwise it will fail.
126 We can now use this as we would any built-in constraint, like so:
127
128 has 'state' => (is => 'rw', isa => 'USState');
129
130 The C<state> accessor will now check all values against the
131 C<USState> constraint, thereby only allowing valid state names or
132 state codes to be stored in the C<state> slot.
133
134 The next C<subtype> does pretty much the same thing using the L<Regexp::Common>
135 module, and is used as the constraint for the C<zip_code> slot.
136
137 subtype USZipCode
138 => as Value
139 => where {
140 /^$RE{zip}{US}{-extended => 'allow'}$/
141 };
142
143 Using subtypes can save a lot of unnecessary abstraction by not requiring you to
144 create many small classes for these relatively simple values. They also allow
145 you to reuse the same constraints in a number of classes (thereby avoiding
146 duplication), since all type constraints are stored in a global registry and
147 always accessible to C<has>.
148
149 With these two subtypes and some attributes, we have defined
150 as much as we need for a basic B<Address> class. Next, we define
151 a basic B<Company> class, which itself has an address. As we saw in
152 earlier recipes, we can use the C<Address> type constraint that
153 Moose automatically created for us:
154
155 has 'address' => (is => 'rw', isa => 'Address');
156
157 A company also needs a name, so we define that as well:
158
159 has 'name' => (is => 'rw', isa => 'Str', required => 1);
160
161 Here we introduce another attribute option, the C<required> option.
162 This option tells Moose that C<name> is a required parameter in
163 the B<Company> constructor, and that the C<name> accessor cannot
164 accept an undefined value for the slot. The result is that C<name>
165 will always have a value.
166
167 The next attribute option is not actually new, but a new variant
168 of options we have already introduced:
169
170 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
171
172 Here, we are passing a more complex string to the C<isa> option, we
173 are passing a container type constraint. Container type constraints
174 can either be C<ArrayRef> or C<HashRef> with a contained type given
175 inside the square brackets. This basically checks that all the values
176 in the ARRAY ref are instances of the B<Employee> class.
177
178 This will ensure that our employees will all be of the correct type. However,
179 the B<Employee> object (which we will see in a moment) also maintains a
180 reference to its associated B<Company>. In order to maintain this relationship
181 (and preserve the referential integrity of our objects), we need to perform some
182 processing of the employees over and above that of the type constraint check.
183 This is accomplished in two places. First we need to be sure that any employees
184 array passed to the constructor is properly initialized. For this we can use the
185 C<BUILD> method (2):
186
187 sub BUILD {
188 my ($self, $params) = @_;
189 if ($params->{employees}) {
190 foreach my $employee (@{$params->{employees}}) {
191 $employee->company($self);
192 }
193 }
194 }
195
196 The C<BUILD> method will be executed after the initial type constraint
197 check, so we can simply perform a basic existential check on the C<employees>
198 param here, and assume that if it does exist, it is both an ARRAY ref
199 and contains I<only> instances of B<Employee>.
200
201 The next aspect we need to address is the C<employees> read/write
202 accessor (see the C<employees> attribute declaration above). This
203 accessor will correctly check the type constraint, but we need to extend it
204 with some additional processing. For this we use an C<after> method modifier,
205 like so:
206
207 after 'employees' => sub {
208 my ($self, $employees) = @_;
209 if (defined $employees) {
210 foreach my $employee (@{$employees}) {
211 $employee->company($self);
212 }
213 }
214 };
215
216 Again, as with the C<BUILD> method, we know that the type constraint
217 check has already happened, so we can just check for defined-ness on the
218 C<$employees> argument.
219
220 At this point, our B<Company> class is complete. Next comes our B<Person>
221 class and its subclass, the previously mentioned B<Employee> class.
222
223 The B<Person> class should be obvious to you at this point. It has a few
224 C<required> attributes, and the C<middle_initial> slot has an additional
225 C<predicate> method (which we saw in the previous recipe with the
226 B<BinaryTree> class).
227
228 Next, the B<Employee> class, which should also be pretty obvious at this
229 point. It requires a C<title>, and maintains a weakened reference to a
230 B<Company> instance. The only new item, which we have seen before in
231 examples, but never in the recipe itself, is the C<override> method
232 modifier:
233
234 override 'full_name' => sub {
235 my $self = shift;
236 super() . ', ' . $self->title
237 };
238
239 This just tells Moose that I am intentionally overriding the superclass
240 C<full_name> method here, and adding the value of the C<title> slot at
241 the end of the employee's full name.
242
243 And that's about it.
244
245 Once again, as with all the other recipes, you can go about using
246 these classes like any other Perl 5 class. A more detailed example of
247 usage can be found in F<t/000_recipes/004_recipe.t>.
248
249 =head1 CONCLUSION
250
251 This recipe was intentionally longer and more complex to illustrate both
252 how easily Moose classes can interact (using class type constraints, etc.)
253 and the sheer density of information and behaviors which Moose can pack
254 into a relatively small amount of typing. Ponder for a moment how much
255 more code a non-Moose plain old Perl 5 version of this recipe would have
256 been (including all the type constraint checks, weak references, and so on).
257
258 And of course, this recipe also introduced the C<subtype> keyword, and
259 its usefulness within the Moose toolkit. In the next recipe we will
260 focus more on subtypes, and introduce the idea of type coercion as well.
261
262 =head1 FOOTNOTES
263
264 =over 4
265
266 =item (1)
267
268 The value being checked is also passed as the first argument to
269 the C<where> block as well, so it can also be accessed as C<$_[0]>
270 as well.
271
272 =item (2)
273
274 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
275 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
276 inheritance graph and call the appropriate C<BUILD> methods in the
277 correct order.
278
279 =back
280
281 =head1 AUTHOR
282
283 Stevan Little E<lt>stevan@iinteractive.comE<gt>
284
285 =head1 COPYRIGHT AND LICENSE
286
287 Copyright 2006-2008 by Infinity Interactive, Inc.
288
289 L<http://www.iinteractive.com>
290
291 This library is free software; you can redistribute it and/or modify
292 it under the same terms as Perl itself.
293
294 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe5 - More subtypes, coercion in a B<Request> class
6
7 =head1 SYNOPSIS
8
9 package Request;
10 use Moose;
11 use Moose::Util::TypeConstraints;
12
13 use HTTP::Headers ();
14 use Params::Coerce ();
15 use URI ();
16
17 subtype 'Header'
18 => as 'Object'
19 => where { $_->isa('HTTP::Headers') };
20
21 coerce 'Header'
22 => from 'ArrayRef'
23 => via { HTTP::Headers->new( @{ $_ } ) }
24 => from 'HashRef'
25 => via { HTTP::Headers->new( %{ $_ } ) };
26
27 subtype 'Uri'
28 => as 'Object'
29 => where { $_->isa('URI') };
30
31 coerce 'Uri'
32 => from 'Object'
33 => via { $_->isa('URI')
34 ? $_
35 : Params::Coerce::coerce( 'URI', $_ ) }
36 => from 'Str'
37 => via { URI->new( $_, 'http' ) };
38
39 subtype 'Protocol'
40 => as Str
41 => where { /^HTTP\/[0-9]\.[0-9]$/ };
42
43 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
44 has 'uri' => (is => 'rw', isa => 'Uri', coerce => 1);
45 has 'method' => (is => 'rw', isa => 'Str');
46 has 'protocol' => (is => 'rw', isa => 'Protocol');
47 has 'headers' => (
48 is => 'rw',
49 isa => 'Header',
50 coerce => 1,
51 default => sub { HTTP::Headers->new }
52 );
53
54 =head1 DESCRIPTION
55
56 This recipe introduces the idea of type coercions, and the C<coerce>
57 keyword. Coercions can be attached to existing type constraints,
58 and can be used to transform input of one type into input of another
59 type. This can be an extremely powerful tool if used correctly, which
60 is why it is off by default. If you want your accessor to attempt
61 a coercion, you must specifically ask for it with the B<coerce> option.
62
63 Now, onto the coercions.
64
65 First we need to create a subtype to attach our coercion to. Here we
66 create a basic I<Header> subtype, which matches any instance of the
67 class B<HTTP::Headers>:
68
69 subtype 'Header'
70 => as 'Object'
71 => where { $_->isa('HTTP::Headers') };
72
73 The simplest thing from here would be create an accessor declaration
74 like this:
75
76 has 'headers' => (
77 is => 'rw',
78 isa => 'Header',
79 default => sub { HTTP::Headers->new }
80 );
81
82 We would then have a self-validating accessor whose default value is
83 an empty instance of B<HTTP::Headers>. This is nice, but it is not
84 ideal.
85
86 The constructor for B<HTTP::Headers> accepts a list of key-value pairs
87 representing the HTTP header fields. In Perl, such a list could
88 easily be stored in an ARRAY or HASH reference. We would like our
89 class's interface to be able to accept this list of key-value pairs
90 in place of the B<HTTP::Headers> instance, and just DWIM. This is where
91 coercion can help. First, let's declare our coercion:
92
93 coerce 'Header'
94 => from 'ArrayRef'
95 => via { HTTP::Headers->new( @{ $_ } ) }
96 => from 'HashRef'
97 => via { HTTP::Headers->new( %{ $_ } ) };
98
99 We first tell it that we are attaching the coercion to the 'Header'
100 subtype. We then give it a set of C<from> clauses which map other
101 subtypes to coercion routines (through the C<via> keyword). Fairly
102 simple really; however, this alone does nothing. We have to tell
103 our attribute declaration to actually use the coercion, like so:
104
105 has 'headers' => (
106 is => 'rw',
107 isa => 'Header',
108 coerce => 1,
109 default => sub { HTTP::Headers->new }
110 );
111
112 This will coerce any B<ArrayRef> or B<HashRef> which is passed into
113 the C<headers> accessor into an instance of B<HTTP::Headers>. So the
114 the following lines of code are all equivalent:
115
116 $foo->headers(HTTP::Headers->new(bar => 1, baz => 2));
117 $foo->headers([ 'bar', 1, 'baz', 2 ]);
118 $foo->headers({ bar => 1, baz => 2 });
119
120 As you can see, careful use of coercions can produce a very open
121 interface for your class, while still retaining the "safety" of
122 your type constraint checks.
123
124 Our next coercion takes advantage of the power of CPAN to handle
125 the details of our coercion. In this particular case it uses the
126 L<Params::Coerce> module, which fits in rather nicely with L<Moose>.
127
128 Again, we create a simple subtype to represent instances of the
129 B<URI> class:
130
131 subtype 'Uri'
132 => as 'Object'
133 => where { $_->isa('URI') };
134
135 Then we add the coercion:
136
137 coerce 'Uri'
138 => from 'Object'
139 => via { $_->isa('URI')
140 ? $_
141 : Params::Coerce::coerce( 'URI', $_ ) }
142 => from 'Str'
143 => via { URI->new( $_, 'http' ) };
144
145 The first C<from> clause we introduce is for the 'Object' subtype. An 'Object'
146 is simply any C<bless>ed value. This means that if the coercion encounters
147 another object, it should use this clause. Now we look at the C<via> block.
148 First it checks to see if the object is a B<URI> instance. Since the coercion
149 process occurs prior to any type constraint checking, it is entirely possible
150 for this to happen, and if it does happen, we simply want to pass the instance
151 on through. However, if it is not an instance of B<URI>, then we need to coerce
152 it. This is where L<Params::Coerce> can do its magic, and we can just use its
153 return value. Simple really, and much less work since we used a module from CPAN
154 :)
155
156 The second C<from> clause is attached to the 'Str' subtype, and
157 illustrates how coercions can also be used to handle certain
158 'default' behaviors. In this coercion, we simple take any string
159 and pass it to the B<URI> constructor along with the default
160 'http' scheme type.
161
162 And of course, our coercions do nothing unless they are told to,
163 like so:
164
165 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
166 has 'uri' => (is => 'rw', isa => 'Uri', coerce => 1);
167
168 As you can see, re-using the coercion allows us to enforce a
169 consistent and very flexible API across multiple accessors.
170
171 =head1 CONCLUSION
172
173 This recipe illustrated the power of coercions to build a more
174 flexible and open API for your accessors, while still retaining
175 all the safety that comes from using Moose's type constraints.
176 Using coercions it becomes simple to manage (from a single
177 location) a consistent API not only across multiple accessors,
178 but across multiple classes as well.
179
180 In the next recipe, we will introduce roles, a concept originally
181 borrowed from Smalltalk, which made it's way into Perl 6, and
182 now into Moose.
183
184 =head1 AUTHOR
185
186 Stevan Little E<lt>stevan@iinteractive.comE<gt>
187
188 =head1 COPYRIGHT AND LICENSE
189
190 Copyright 2006-2008 by Infinity Interactive, Inc.
191
192 L<http://www.iinteractive.com>
193
194 This library is free software; you can redistribute it and/or modify
195 it under the same terms as Perl itself.
196
197 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe6 - The augment/inner example
6
7 =head1 SYNOPSIS
8
9 package Document::Page;
10 use Moose;
11
12 has 'body' => (is => 'rw', isa => 'Str', default => sub {''});
13
14 sub create {
15 my $self = shift;
16 $self->open_page;
17 inner();
18 $self->close_page;
19 }
20
21 sub append_body {
22 my ($self, $appendage) = @_;
23 $self->body($self->body . $appendage);
24 }
25
26 sub open_page { (shift)->append_body('<page>') }
27 sub close_page { (shift)->append_body('</page>') }
28
29 package Document::PageWithHeadersAndFooters;
30 use Moose;
31
32 extends 'Document::Page';
33
34 augment 'create' => sub {
35 my $self = shift;
36 $self->create_header;
37 inner();
38 $self->create_footer;
39 };
40
41 sub create_header { (shift)->append_body('<header/>') }
42 sub create_footer { (shift)->append_body('<footer/>') }
43
44 package TPSReport;
45 use Moose;
46
47 extends 'Document::PageWithHeadersAndFooters';
48
49 augment 'create' => sub {
50 my $self = shift;
51 $self->create_tps_report;
52 };
53
54 sub create_tps_report {
55 (shift)->append_body('<report type="tps"/>')
56 }
57
58 print TPSReport->new->create # <page><header/><report type="tps"/><footer/></page>
59
60 =head1 DESCRIPTION
61
62 Coming Soon.
63
64 =head1 CONCLUSION
65
66 =head1 FOOTNOTES
67
68 =over 4
69
70 =back
71
72 =head1 AUTHOR
73
74 Stevan Little E<lt>stevan@iinteractive.comE<gt>
75
76 =head1 COPYRIGHT AND LICENSE
77
78 Copyright 2007 by Infinity Interactive, Inc.
79
80 L<http://www.iinteractive.com>
81
82 This library is free software; you can redistribute it and/or modify
83 it under the same terms as Perl itself.
84
85 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe7 - Making Moose fast with immutable
6
7 =head1 SYNOPSIS
8
9 package Point;
10 use Moose;
11
12 has 'x' => (isa => 'Int', is => 'ro');
13 has 'y' => (isa => 'Int', is => 'rw');
14
15 __PACKAGE__->meta->make_immutable;
16
17 =head1 DESCRIPTION
18
19 The Moose metaclass API provides a method C<make_immutable()>. At a
20 high level, this calling this method does two things to your
21 class. One, it makes it faster. In particular, object construction and
22 accessors are effectively "inlined" in your class, and no longer go
23 through the meta-object system.
24
25 Second, you can no longer make changes via the metaclass API such as
26 adding attributes. In practice, this won't be a problem, as you don't
27 usually need to do this at runtime after first loading the class.
28
29 =head2 Immutabilization and C<new()>
30
31 If you override C<new()> in your class, then the immutabilization code
32 will not be able to provide an optimized constructor for your
33 class. Instead, consider providing a C<BUILD()> method. You can
34 probably do the same thing in a C<BUILD()> method.
35
36 Alternately, if you really need to provide a different C<new()>, you
37 can also provide your own immutabilization method.
38
39 Discussing this is beyond the scope of this recipe, however.
40
41 =head1 CONCLUSION
42
43 We strongly recommend you make your classes immutable. It makes your
44 code much faster, basically for free. This will be especially
45 noticeable when creating many objects or calling accessors frequently.
46
47 =head1 AUTHOR
48
49 Dave Rolsky E<lt>autarch@urth.orgE<gt>
50
51 =head1 COPYRIGHT AND LICENSE
52
53 Copyright 2006-2008 by Infinity Interactive, Inc.
54
55 L<http://www.iinteractive.com>
56
57 This library is free software; you can redistribute it and/or modify
58 it under the same terms as Perl itself.
59
60 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Basics::Recipe9 - Builder methods and lazy_build
6
7 =head1 SYNOPSIS
8
9 package BinaryTree;
10 use Moose;
11
12 has 'node' => (is => 'rw', isa => 'Any');
13
14 has 'parent' => (
15 is => 'rw',
16 isa => 'BinaryTree',
17 predicate => 'has_parent',
18 weak_ref => 1,
19 );
20
21 has 'left' => (
22 is => 'rw',
23 isa => 'BinaryTree',
24 predicate => 'has_left',
25 lazy => 1,
26 builder => '_build_child_tree',
27 );
28
29 has 'right' => (
30 is => 'rw',
31 isa => 'BinaryTree',
32 predicate => 'has_right',
33 lazy => 1,
34 builder => '_build_child_tree',
35 );
36
37 before 'right', 'left' => sub {
38 my ($self, $tree) = @_;
39 $tree->parent($self) if defined $tree;
40 };
41
42 sub _build_child_tree {
43 my $self = shift;
44
45 return BinaryTree->new( parent => $self );
46 }
47
48 =head1 DESCRIPTION
49
50 If you've already read L<Moose::Cookbook::Basics::Recipe3>, then this example
51 should look awfully familiar. In fact, all we've done here is replace
52 the attribute C<default> with a C<builder> method.
53
54 In this particular case, the C<default> and C<builder> options act in
55 exactly the same way. When the C<left> or C<right> attribute get
56 method is called, Moose will call the builder method to initialize the
57 attribute.
58
59 Note that Moose calls the builder method I<on the object which has the
60 attribute>. Here's an example in code:
61
62 my $tree = BinaryTree->new();
63
64 my $left = $tree->left();
65
66 At this point, Moose will call C<< $tree->_build_child_tree() >> in
67 order to populate the C<left> attribute. If we had passed C<left> to
68 the original constructor, the builer would not be called.
69
70 =head2 Subclassable
71
72 There are some differences between C<default> and C<builder>. Because
73 C<builder> is called I<by name>, it goes through Perl's normal
74 inheritance system. This means that builder methods are both
75 inheritable and overrideable.
76
77 For example, we might make a C<BinaryTree> subclass:
78
79 package TrinaryTree;
80 use Moose;
81
82 extends 'BinaryTree';
83
84 has 'middle' => (
85 is => 'rw',
86 isa => 'BinaryTree',
87 predicate => 'has_middle',
88 lazy => 1,
89 builder => '_build_child_tree',
90 );
91
92 This doesn't quite work though. If you look closely at the
93 C<_build_child_tree> method defined in C<BinaryTree>, you'll notice
94 that it hard-codes a class name. Naughty us!
95
96 Also, as a bonus, we'll pass C<@_> through, so subclasses can override
97 the method to pass additional options to the constructor.
98
99 Good object-oriented code should allow itself to be subclassed
100 gracefully. Let's tweak C<_build_child_tree>:
101
102 sub _build_child_tree {
103 my $self = shift;
104
105 return (ref $self)->new( parent => $self, @_ );
106 }
107
108 Now C<_build_child_tree> can be gracefully inherited and overridden.
109
110 =head2 Composable
111
112 There's more to builders than just subclassing, though. The fact that
113 builders are called by name also makes them suitable for use in a
114 role.
115
116 package HasAnimal;
117 use Moose::Role;
118
119 requires '_build_animal';
120
121 has 'animal' => (
122 is => 'ro',
123 isa => 'Animal',
124 lazy => 1,
125 builder => '_build_animal',
126 );
127
128 This role provides an animal attribute, but requires that the consumer
129 of the role provide a builder method it.
130
131 package CatLover;
132 use Moose;
133
134 with 'HasAnimal';
135
136 sub _build_animal {
137 return Cat->new();
138 }
139
140 =head2 The lazy_build shortcut
141
142 The C<lazy_build> attribute parameter can be used as sugar to specify
143 a whole bunch of options at once.
144
145 has 'animal' => (
146 is => 'ro',
147 isa => 'Animal',
148 lazy_build => 1,
149 );
150
151 This is a shorthand for this:
152
153 has 'animal' => (
154 is => 'ro',
155 isa => 'Animal',
156 required => 1,
157 lazy => 1,
158 builder => '_build_animal',
159 predicate => 'has_animal',
160 clearer => 'clear_animal',
161 );
162
163 If your attribute starts with an underscore, Moose is smart and will
164 do the right thing with the C<predicate> and C<clearer>, making them
165 both start with an underscore. The C<builder> method I<always> starts
166 with an underscore, since you will want this to be private the vast
167 majority of the time.
168
169 Note that the C<builder> method name is created by simply taking
170 "_build_" and appending the attribute name. This means that attributes
171 with a leading underscore like C<_animal> end up with a builder named
172 C<_build__animal>.
173
174 =head1 CONCLUSION
175
176 The C<builder> option is a more OO-friendly version of the C<default>
177 functionality. It also has the property of separating out the code
178 into a separate well-defined method. This alone makes it valuable. It
179 is quite ugly to jam a long default code reference into your attribute
180 definition.
181
182 Here are some good rules for determining when to use C<builder> vs
183 C<default>.
184
185 If the default value is a simple scalar that only needs to be
186 calculated once (or a constant), use C<default>.
187
188 If the default value is an empty reference that needs to be wrapped in
189 a coderef like C<sub { [] }>, use C<default>.
190
191 Otherwise, use C<builder>.
192
193 This ensures that your classes are easily subclassable, and also helps
194 keep crufty code out of your attribute definition blocks.
195
196 =head1 AUTHOR
197
198 Dave Rolsky E<lt>autarch@urth.orgE<gt>
199
200 =head1 COPYRIGHT AND LICENSE
201
202 Copyright 2006-2008 by Infinity Interactive, Inc.
203
204 L<http://www.iinteractive.com>
205
206 This library is free software; you can redistribute it and/or modify
207 it under the same terms as Perl itself.
208
209 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Extending::Recipe1 - Providing an alternate base object class
6
7 =head1 SYNOPSIS
8
9 package MyApp::Base;
10 use Moose;
11
12 extends 'Moose::Object';
13
14 before 'new' => sub { warn "Making a new " . $_[0] };
15
16 no Moose;
17
18 package MyApp::UseMyBase;
19 use Moose ();
20
21 sub import {
22 my $caller = caller();
23
24 return if $caller eq 'main';
25
26 Moose::init_meta( $caller,
27 'MyApp::Object',
28 );
29
30 Moose->import( { into => $caller }, @_ );
31 }
32
33 sub unimport {
34 my $caller = caller();
35
36 Moose->unimport( { into => $caller }, @_ );
37 }
38
39 =head1 DESCRIPTION
40
41 Often you find that you want to share some behavior between all your
42 classes. One way to do that is to make a base class and simply add
43 C<S<extends 'MyApp::Base'>> to every class in your
44 application. However, that can get tedious. Instead, you can simply
45 create your Moose-alike module that sets the base object class to
46 C<MyApp::Base> for you.
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
52 output every time a new object is created, but you can surely think of
53 some more interesting things to do with your own base class.
54
55 =head1 AUTHOR
56
57 Dave Rolsky E<lt>autarch@urth.orgE<gt>
58
59 =head1 COPYRIGHT AND LICENSE
60
61 Copyright 2006-2008 by Infinity Interactive, Inc.
62
63 L<http://www.iinteractive.com>
64
65 This library is free software; you can redistribute it and/or modify
66 it under the same terms as Perl itself.
67
68 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Extending::Recipe2 - Acting like Moose.pm and providing sugar Moose-style
6
7 =head1 SYNOPSIS
8
9 package MyApp::Mooseish;
10
11 use strict;
12 use warnings;
13
14 our @EXPORT = qw( has_table );
15
16 use base 'Exporter';
17 use Class::MOP;
18 use Moose ();
19
20 sub import {
21 my $caller = caller();
22
23 return if $caller eq 'main';
24
25 Moose::init_meta( $caller,
26 undef, # object base class
27 'MyApp::Meta::Class',
28 );
29
30 Moose->import( { into => $caller }, @_ );
31
32 __PACKAGE__->export_to_level( 1, @_ );
33 }
34
35 sub unimport {
36 my $caller = caller();
37
38 no strict 'refs';
39 foreach my $name (@EXPORT) {
40 if ( defined &{ $caller . '::' . $name } ) {
41 my $keyword = \&{ $caller . '::' . $name };
42
43 my ($pkg_name) = Class::MOP::get_code_info($keyword);
44
45 next if $pkg_name ne __PACKAGE__;
46
47 delete ${ $caller . '::' }{$name};
48 }
49 }
50
51 Moose::unimport( { into_level => 1 } );
52 }
53
54 sub has_table {
55 my $caller = caller();
56
57 $caller->meta()->table(shift);
58 }
59
60 =head1 DESCRIPTION
61
62 The code above shows what it takes to provide an import-based
63 interface just like C<Moose.pm>. This recipe builds on
64 L<Moose::Cookbook::Extending::Recipe1>. Instead of providing our own
65 object base class, we provide our own metaclass class, and we also
66 export a sugar subroutine C<has_table()>.
67
68 Given the above code, you can now replace all instances of C<use
69 Moose> with C<use MyApp::Mooseish>. Similarly, C<no Moose> is now
70 replaced with C<no MyApp::Mooseish>.
71
72 =head1 WARNING
73
74 This recipe covers a fairly undocumented and ugly part of Moose, and
75 the techniques described here may be deprecated in a future
76 release. If this happens, there will be plenty of warning, as a number
77 of C<MooseX> modules on CPAN already use these techniques.
78
79 =head1 HOW IT IS USED
80
81 The purpose of all this code is to provide a Moose-like
82 interface. Here's what it would look like in actual use:
83
84 package MyApp::User;
85
86 use MyApp::Mooseish;
87
88 has_table 'User';
89
90 has 'username';
91 has 'password';
92
93 sub login { ... }
94
95 no MyApp::Mooseish;
96
97 All of the normal Moose sugar (C<has()>, C<with()>, etc) is available
98 when you C<use MyApp::Mooseish>.
99
100 =head1 DISSECTION
101
102 The first bit of magic is the call to C<Moose::init_meta()>. What this
103 does is create a metaclass for the specified class. Normally, this is
104 called by C<Moose.pm> in its own C<import()> method. However, we can
105 call it first in order to provide an alternate metaclass class. We
106 could also provide an alternate base object class to replace
107 C<Moose::Object> (see L<Moose::Cookbook::Extending::Recipe1> for an
108 example).
109
110 The C<Moose::init_meta()> call takes three parameters. The first is
111 the class for which we are initializing a metaclass object. The second
112 is the base object, which is L<Moose::Object> by default. The third
113 argument is the metaclass class, which is C<Moose::Meta::Class> by
114 default.
115
116 The next bit of magic is this:
117
118 Moose->import( { into => $caller } );
119
120 This use of "into" is actually part of the C<Sub::Exporter> API, which
121 C<Moose.pm> uses internally to export things like C<has()> and
122 C<extends()>.
123
124 Finally, we call C<< __PACKAGE__->export_to_level() >>. This method
125 actually comes from C<Exporter>.
126
127 This is all a bit fragile since it doesn't stack terribly well. You
128 can basically only have one Moose-alike module. This may be fixed in
129 the still-notional C<MooseX::Exporter> module someday.
130
131 The C<unimport()> subroutine is basically a copy of the C<unimport()>
132 from C<Moose.pm>. You can copy this verbatim into your code. Again,
133 this doesn't stack well.
134
135 Finally, we have our C<has_table()> subroutine. This provides a bit of
136 sugar that looks a lot like C<has()>.
137
138 =head1 AUTHOR
139
140 Dave Rolsky E<lt>autarch@urth.orgE<gt>
141
142 =head1 COPYRIGHT AND LICENSE
143
144 Copyright 2006-2008 by Infinity Interactive, Inc.
145
146 L<http://www.iinteractive.com>
147
148 This library is free software; you can redistribute it and/or modify
149 it under the same terms as Perl itself.
150
151 =pod
8383 or any other format
8484
8585 To change the handling of individual parameters, there are I<coercions>
86 (See the L<Moose::Cookbook::Recipe5> for a complete example and
86 (See the L<Moose::Cookbook::Basics::Recipe5> for a complete example and
8787 explaination of coercions). With coercions it is possible to morph
8888 argument values into the correct expected types. This approach is the
8989 most flexible and robust, but does have a slightly higher learning
9696 coercions, and C<lazy_build>, so subclassing is often not the
9797 ideal route.
9898
99 That said, the default Moose constructors is inherited from
99 That said, the default Moose constructor is inherited from
100100 L<Moose::Object>. When inheriting from a non-Moose class, the
101101 inheritance chain to L<Moose::Object> is broken. The simplest way
102102 to fix this is to simply explicitly inherit from L<Moose::Object>
203203 in the C<via> block.
204204
205205 For a more comprehensive example of using coercions, see the
206 L<Moose::Cookbook::Recipe5>.
206 L<Moose::Cookbook::Basics::Recipe5>.
207207
208208 If you need to deflate your attribute, the current best practice is to
209209 add an C<around> modifier to your accessor. Here is some example code:
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Meta::Recipe1 - Welcome to the meta world (Why Go Meta?)
6
7 =head1 SUMMARY
8
9 If you've ever found yourself thinking "Moose is great, but I wish it
10 did X differently", then you've gone meta. The meta recipes are all
11 about how to change and extend the way Moose does its thing, by
12 changing how the various meta classes (C<Moose::Meta::Class>,
13 C<Moose::Meta::Attribute>, etc) work.
14
15 The metaclass system is a set of classes that describe classes, roles,
16 attributes, etc. The metaclass API lets you ask questions about a
17 class, like "what attributes does it have?", or "what roles does the
18 class do?"
19
20 The metaclass system also lets you actively make changes to a class,
21 for example by adding new methods.
22
23 The interface with which you normally use Moose (C<has>, C<with>,
24 C<extends>) is just a thin layer of syntactic sugar over the
25 underlying metaclass system.
26
27 By extending and changing how this metaclass system works, you can in
28 effect create a modified object implementation for your classes.
29
30 =head2 Examples
31
32 Let's say that you want to additional properties to
33 attributes. Specifically, we want to add a "label" property to each
34 attribute, so we can write C<<
35 My::Class->meta()->get_attribute('size')->label() >>. The first two
36 recipes show two different ways to do this, one with a full
37 meta-attribute subclass, and the other with an attribute trait.
38
39 You might also want to add additional properties to your
40 metaclass. For example, if you were writing an ORM based on Moose, you
41 could associate a table name with each class via the class's metaclass
42 object, letting you write C<< My::Class->meta()->table_name() >>.
43
44 =head1 SEE ALSO
45
46 Many of the MooseX modules on CPAN implement metaclass extensions. A
47 couple good examples include C<MooseX::Singleton> and
48 C<MooseX::AttributeHelpers>. For a more complex example see
49 C<Fey::ORM>.
50
51 =head1 AUTHOR
52
53 Dave Rolsky E<lt>autarch@urth.org<gt>
54
55 =head1 COPYRIGHT AND LICENSE
56
57 Copyright 2008 by Infinity Interactive, Inc.
58
59 L<http://www.iinteractive.com>
60
61 This library is free software; you can redistribute it and/or modify
62 it under the same terms as Perl itself.
63
64 =cut
65
66
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Meta::Recipe2 - A meta-attribute, attributes with labels
6
7 =head1 SYNOPSIS
8
9 package MyApp::Meta::Attribute::Labeled;
10 use Moose;
11 extends 'Moose::Meta::Attribute';
12
13 has label => (
14 is => 'rw',
15 isa => 'Str',
16 predicate => 'has_label',
17 );
18
19 package Moose::Meta::Attribute::Custom::Labeled;
20 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
21
22 package MyApp::Website;
23 use Moose;
24 use MyApp::Meta::Attribute::Labeled;
25
26 has url => (
27 metaclass => 'Labeled',
28 is => 'rw',
29 isa => 'Str',
30 label => "The site's URL",
31 );
32
33 has name => (
34 is => 'rw',
35 isa => 'Str',
36 );
37
38 sub dump {
39 my $self = shift;
40
41 # iterate over all the attributes in $self
42 my %attributes = %{ $self->meta->get_attribute_map };
43 while (my ($name, $attribute) = each %attributes) {
44
45 # print the label if available
46 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
47 && $attribute->has_label) {
48 print $attribute->label;
49 }
50 # otherwise print the name
51 else {
52 print $name;
53 }
54
55 # print the attribute's value
56 my $reader = $attribute->get_read_method;
57 print ": " . $self->$reader . "\n";
58 }
59 }
60
61 package main;
62 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
63 $app->dump;
64
65 =head1 SUMMARY
66
67 In this recipe, we begin to really delve into the wonder of meta-programming.
68 Some readers may scoff and claim that this is the arena only of the most
69 twisted Moose developers. Absolutely not! Any sufficiently twisted developer
70 can benefit greatly from going more meta.
71
72 The high-level goal of this recipe's code is to allow each attribute to have a
73 human-readable "label" attached to it. Such labels would be used when showing
74 data to an end user. In this recipe we label the "url" attribute with "The
75 site's URL" and create a simple method to demonstrate how to use that label.
76
77 =head1 REAL ATTRIBUTES 101
78
79 All the attributes of a Moose-based object are actually objects themselves.
80 These objects have methods and (surprisingly) attributes. Let's look at a
81 concrete example.
82
83 has 'x' => (isa => 'Int', is => 'ro');
84 has 'y' => (isa => 'Int', is => 'rw');
85
86 Ahh, the veritable x and y of the Point example. Internally, every Point has an
87 x object and a y object. They have methods (such as "get_value") and attributes
88 (such as "is_lazy"). What class are they instances of?
89 L<Moose::Meta::Attribute>. You don't normally see the objects lurking behind
90 the scenes, because you usually just use C<< $point->x >> and C<< $point->y >>
91 and forget that there's a lot of machinery lying in such methods.
92
93 So you have a C<$point> object, which has C<x> and C<y> methods. How can you
94 actually access the objects behind these attributes? Here's one way:
95
96 $point->meta->get_attribute_map()
97
98 C<get_attribute_map> returns a hash reference that maps attribute names to
99 their objects. In our case, C<get_attribute_map> might return something that
100 looks like the following:
101
102 {
103 x => Moose::Meta::Attribute=HASH(0x196c23c),
104 y => Moose::Meta::Attribute=HASH(0x18d1690),
105 }
106
107 Another way to get a handle on an attribute's object is
108 C<< $self->meta->get_attribute('name') >>. Here's one thing you can do now that
109 you can interact with the attribute's object directly:
110
111 print $point->meta->get_attribute('x')->type_constraint;
112 => Int
113
114 (As an aside, it's not called C<< ->isa >> because C<< $obj->isa >> is already
115 taken)
116
117 So to actually beef up attributes, what we need to do is:
118
119 =over 4
120
121 =item Create a new attribute metaclass
122
123 =item Create attributes using that new metaclass
124
125 =back
126
127 Moose makes both of these easy!
128
129 Let's start dissecting the recipe's code.
130
131 =head1 DISSECTION
132
133 We get the ball rolling by creating a new attribute metaclass. It starts off
134 somewhat ungloriously.
135
136 package MyApp::Meta::Attribute::Labeled;
137 use Moose;
138 extends 'Moose::Meta::Attribute';
139
140 You subclass metaclasses the same way you subclass regular classes. (Extra
141 credit: how in the actual hell can you use the MOP to extend itself?)
142
143 has label => (
144 is => 'rw',
145 isa => 'Str',
146 predicate => 'has_label',
147 );
148
149 Hey, this looks pretty reasonable! This is plain jane Moose code. Recipe 1
150 fare. This is merely making a new attribute. An attribute that attributes have.
151 A meta-attribute. It may sound scary, but it really isn't! Reread
152 L<REAL ATTRIBUTES 101> if this really is terrifying.
153
154 The name is "label", it will have a regular accessor, and is a string.
155 C<predicate> is a standard part of C<has>. It just creates a method that asks
156 the question "Does this attribute have a value?"
157
158 package Moose::Meta::Attribute::Custom::Labeled;
159 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
160
161 This lets Moose discover our new metaclass. That way attributes can actually
162 use it. More on what this is doing in a moment.
163
164 Note that we're done defining the new metaclass! Only nine lines of code, and
165 not particularly difficult lines, either. Now to start using the metaclass.
166
167 package MyApp::Website;
168 use Moose;
169 use MyApp::Meta::Attribute::Labeled;
170
171 Nothing new here. We do have to actually load our metaclass to be able to use
172 it.
173
174 has url => (
175 metaclass => 'Labeled',
176 is => 'rw',
177 isa => 'Str',
178 label => "The site's URL",
179 );
180
181 Ah ha! Now we're using the metaclass. We're adding a new attribute, C<url>, to
182 C<MyApp::Website>. C<has> lets you set the metaclass of the attribute.
183 Ordinarily (as we've seen), the metaclass is C<Moose::Meta::Attribute>.
184
185 When C<has> sees that you're using a new metaclass, it will take the
186 metaclass's name, prepend C<Moose::Meta::Attribute::Custom::>, and call the
187 C<register_implementation> function in that package. So here Moose calls
188 C<Moose::Meta::Attribute::Custom::Labeled::register_implementation>. We defined
189 that function in the beginning -- it just returns our "real" metaclass'
190 package, C<MyApp::Meta::Attribute::Labeled>. So Moose uses that metaclass for
191 the attribute. It may seem a bit convoluted, but the alternative would be to
192 use C<< metaclass => 'MyApp::Meta::Attribute::Labeled' >> on every attribute.
193 As usual, Moose optimizes in favor of the end user, not the metaprogrammer. :)
194 We also could have just defined the metaclass in
195 C<Moose::Meta::Attribute::Custom::Labeled>, but it's probably better to keep to
196 your own namespaces.
197
198 Finally, we see that C<has> is setting our new meta-attribute, C<label>, to
199 C<"The site's URL">. We can access this meta-attribute with:
200
201 $website->meta->get_attribute('url')->label()
202
203 Well, back to the code.
204
205 has name => (
206 is => 'rw',
207 isa => 'Str',
208 );
209
210 Of course, you don't have to use the new metaclass for B<all> new attributes.
211
212 Now we begin defining a method that will dump the C<MyApp::Website> instance
213 for human readers.
214
215 sub dump {
216 my $self = shift;
217
218 # iterate over all the attributes in $self
219 my %attributes = %{ $self->meta->get_attribute_map };
220 while (my ($name, $attribute) = each %attributes) {
221
222 Recall that C<get_attribute_map> returns a hashref of attribute names and their
223 associated objects.
224
225 # print the label if available
226 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
227 && $attribute->has_label) {
228 print $attribute->label;
229 }
230
231 We have two checks here. The first is "is this attribute an instance of
232 C<MyApp::Meta::Attribute::Labeled>?". It's good to code defensively. Even if
233 all of your attributes have this metaclass, you never know when someone is
234 going to subclass your work of art. Poorly. In other words, it's likely that
235 there will still be (many) attributes that are instances of the default
236 C<Moose::Meta::Attribute>.
237
238 The second check is "does this attribute have a label?". This method was
239 defined in the new metaclass as the "predicate". If we pass both checks, we
240 print the attribute's label.
241
242 # otherwise print the name
243 else {
244 print $name;
245 }
246
247 Another good, defensive coding practice: Provide reasonable defaults.
248
249 # print the attribute's value
250 my $reader = $attribute->get_read_method;
251 print ": " . $self->$reader . "\n";
252 }
253 }
254
255 Here's another example of using the attribute metaclass.
256 C<< $attribute->get_read_method >> returns the name of the method that can
257 be invoked on the original object to read the attribute's value.
258 C<< $self->$reader >> is an example of "reflection" -- instead of using the
259 name of the method, we're using a variable with the name of the method in it.
260 Perl doesn't mind. Another way to write this would be
261 C<< $self->can($reader)->($self) >>. Yuck. :)
262
263 package main;
264 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
265 $app->dump;
266
267 And we wrap up the example with a script to show off our newfound magic.
268
269 =head1 CONCLUSION
270
271 Why oh why would you want to go through all of these contortions when you can
272 just print "The site's URL" directly in the C<dump> method? For one, the DRY
273 (Don't Repeat Yourself) principle. If you have it in the C<dump> method, you'll
274 probably also have it in the C<as_form> method, and C<to_file>, and so on. So
275 why not have a method that maps attribute names to labels? That could work, but
276 why not include the label where it belongs, in the attribute's definition?
277 That way you're also less likely to forget to add the label.
278
279 More importantly, this was a very simple example. Your metaclasses aren't
280 limited to just adding new meta-attributes. For example, you could implement
281 a metaclass that expires attributes after a certain amount of time. You
282 might use it as such:
283
284 has site_cache => (
285 metaclass => 'TimedExpiry',
286 expires_after => { hours => 1 },
287 refresh_with => sub { get($_->url) },
288 isa => 'Str',
289 is => 'ro',
290 );
291
292 The sky's the limit!
293
294 =head1 AUTHOR
295
296 Shawn M Moore E<lt>sartak@gmail.comE<gt>
297
298 =head1 COPYRIGHT AND LICENSE
299
300 Copyright 2006-2008 by Infinity Interactive, Inc.
301
302 L<http://www.iinteractive.com>
303
304 This library is free software; you can redistribute it and/or modify
305 it under the same terms as Perl itself.
306
307 =cut
308
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Meta::Recipe3 - Labels implemented via attribute traits
6
7 =head1 SYNOPSIS
8
9 package MyApp::Meta::Attribute::Trait::Labeled;
10 use Moose::Role;
11
12 has label => (
13 is => 'rw',
14 isa => 'Str',
15 predicate => 'has_label',
16 );
17
18 package Moose::Meta::Attribute::Custom::Trait::Labeled;
19 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
20
21 package MyApp::Website;
22 use Moose;
23 use MyApp::Meta::Attribute::Trait::Labeled;
24
25 has url => (
26 traits => [qw/Labeled/],
27 is => 'rw',
28 isa => 'Str',
29 label => "The site's URL",
30 );
31
32 has name => (
33 is => 'rw',
34 isa => 'Str',
35 );
36
37 sub dump {
38 my $self = shift;
39
40 # iterate over all the attributes in $self
41 my %attributes = %{ $self->meta->get_attribute_map };
42 while (my ($name, $attribute) = each %attributes) {
43
44 # print the label if available
45 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
46 && $attribute->has_label) {
47 print $attribute->label;
48 }
49 # otherwise print the name
50 else {
51 print $name;
52 }
53
54 # print the attribute's value
55 my $reader = $attribute->get_read_method;
56 print ": " . $self->$reader . "\n";
57 }
58 }
59
60 package main;
61 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
62 $app->dump;
63
64 =head1 BUT FIRST
65
66 This recipe is a continuation of
67 L<Moose::Cookbook::Meta::Recipe2>. Please read that recipe first.
68
69 =head1 MOTIVATION
70
71 In L<Moose::Cookbook::Meta::Recipe2>, we created an attribute
72 metaclass that gives attributes a "label" that can be set in
73 L<Moose/has>. That works well until you want a second meta-attribute,
74 or until you want to adjust the behavior of the attribute. You could
75 define a specialized attribute metaclass to use in every attribute.
76 However, you may want different attributes to have different
77 behaviors. You might end up with a unique attribute metaclass for
78 B<every single attribute>, with a lot of code copying and pasting!
79
80 Or, if you've been drinking deeply of the Moose kool-aid, you'll have a role
81 for each of the behaviors. One role would give a label meta-attribute. Another
82 role would signify that this attribute is not directly modifiable via the
83 REST interface. Another role would write to a logfile when this attribute
84 was read.
85
86 Unfortunately, you'd still be left with a bunch of attribute metaclasses that
87 do nothing but compose a bunch of roles. If only there were some way to specify
88 in L<Moose/has> a list of roles to apply to the attribute metaclass...
89
90 =head1 TRAITS
91
92 Roles that apply to metaclasses have a special name: traits. Don't let the
93 change in nomenclature fool you, B<traits are just roles>.
94
95 L<Moose/has> provides a C<traits> option. It takes a list of trait names to
96 compose into an anonymous metaclass. That means you do still have a bunch of
97 attribute metaclasses that do nothing but compose a bunch of roles, but they're
98 managed automatically by Moose. You don't need to declare them in advance, or
99 worry whether changing one will affect some other attribute.
100
101 What can traits do? Anything roles can do. They can add or refine attributes,
102 wrap methods, provide more methods, define an interface, etc. The only
103 difference is that you're now changing the attribute metaclass instead of a
104 user-level class.
105
106 =head1 DISSECTION
107
108 A side-by-side look of the code examples in this recipe and recipe 2 should
109 indicate that defining and using a trait is very similar to defining and using
110 a new attribute metaclass.
111
112 package MyApp::Meta::Attribute::Trait::Labeled;
113 use Moose::Role;
114
115 has label => (
116 is => 'rw',
117 isa => 'Str',
118 predicate => 'has_label',
119 );
120
121 Instead of subclassing L<Moose::Meta::Attribute>, we define a role. Traits
122 don't need any special methods or attributes. You just focus on whatever it is
123 you actually need to get done. Here we're adding a new meta-attribute for use
124 in our application.
125
126 package Moose::Meta::Attribute::Custom::Trait::Labeled;
127 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
128
129 Much like when we define a new attribute metaclass, we can provide a shorthand
130 name for the trait. Moose looks at the C<register_implementation> method in
131 C<Moose::Meta::Attribute::Custom::Trait::$TRAIT_NAME> to find the full
132 name of the trait.
133
134 Now we begin writing our application logic. I'll only cover what has changed
135 since recipe 2.
136
137 has url => (
138 traits => [qw/Labeled/],
139 is => 'rw',
140 isa => 'Str',
141 label => "The site's URL",
142 );
143
144 L<Moose/has> provides a C<traits> option. Just pass the list of trait names and
145 it will compose them together to form the (anonymous) attribute metaclass used
146 by the attribute. We provide a label for the attribute in the same way.
147
148 # print the label if available
149 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
150 && $attribute->has_label) {
151 print $attribute->label;
152 }
153
154 Previously, this code asked the question "Does this attribute use our attribute
155 metaclass?" Since we're now using a trait, we ask "Does this attribute's
156 metaclass do the C<Labeled> role?" If not, the attribute metaclass won't have
157 the C<has_label> method, and so it would be an error to blindly call
158 C<< $attribute->has_label >>.
159
160 That's all. Everything else is the same!
161
162 =head1 METACLASS + TRAIT
163
164 "But wait!" you protest. "I've already written all of my extensions as
165 attribute metaclasses. I don't want to break all that code out there."
166
167 All is not lost. If you rewrite your extension as a trait, then you can
168 easily get a regular metaclass extension out of it. You just compose the trait
169 in the attribute metaclass, as normal.
170
171 package MyApp::Meta::Attribute::Labeled;
172 use Moose;
173 extends 'Moose::Meta::Attribute';
174 with 'MyApp::Meta::Attribute::Trait::Labeled';
175
176 package Moose::Meta::Attribute::Custom::Labeled;
177 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
178
179 Unfortunately, going the other way (providing a trait created from a metaclass)
180 is more tricky. Thus, defining your extensions as traits is just plain better
181 than defining them as subclassed metaclasses.
182
183 =head1 CONCLUSION
184
185 If you're extending your attributes, it's easier and more flexible to provide
186 composable bits of behavior than to subclass L<Moose::Meta::Attribute>.
187 Using traits (which are just roles applied to a metaclass!) let you choose
188 exactly which behaviors each attribute will have. Moose makes it easy to create
189 attribute metaclasses on the fly by providing a list of trait names to
190 L<Moose/has>.
191
192 =head1 AUTHOR
193
194 Shawn M Moore E<lt>sartak@gmail.comE<gt>
195
196 =head1 COPYRIGHT AND LICENSE
197
198 Copyright 2006-2008 by Infinity Interactive, Inc.
199
200 L<http://www.iinteractive.com>
201
202 This library is free software; you can redistribute it and/or modify
203 it under the same terms as Perl itself.
204
205 =cut
206
207
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Meta::Recipe4 - Adding a "table" attribute to the metaclass
6
7 =head1 SYNOPSIS
8
9 package MyApp::Meta::Class;
10 use Moose;
11 extends 'Moose::Meta::Class';
12
13 has table =>
14 ( is => 'rw',
15 isa => 'Str',
16 );
17
18 =head1 DESCRIPTION
19
20 In this recipe, we'll create a new metaclass which has a "table"
21 attribute. This metaclass is for classes associated with a DBMS table,
22 as one might do for an ORM.
23
24 In this example, the table name is just a string, but in a real ORM
25 the table might be an object describing the table.
26
27 =head1 THE METACLASS
28
29 The metaclass example really is as simple as the one in the
30 synopsis. The trick is getting your classes to use this metaclass, and
31 providing some sort of sugar for declaring the table. This is covered
32 in L<Moose::Cookbook::Extending::Recipe2>, which shows how to make a
33 module like C<Moose.pm> itself, with sugar like C<has_table()>.
34
35 =head2 Using It
36
37 Using this new "table" attribute is quite simple. Let's say we have a
38 class named C<MyApp::User>, we could simply write the following:
39
40 my $table = MyApp::User->meta()->table();
41
42 As long as MyApp::User has arranged to use C<MyApp::Meta::Class> as
43 its metaclass, this method call just works.
44
45 =head1 SEE ALSO
46
47 L<Moose::Cookbook::Meta::Recipe5> - The "table" attribute implemented
48 via a metaclass trait
49
50 =head1 AUTHOR
51
52 Dave Rolsky E<lt>autarch@urth.orgE<gt>
53
54 =head1 COPYRIGHT AND LICENSE
55
56 Copyright 2006-2008 by Infinity Interactive, Inc.
57
58 L<http://www.iinteractive.com>
59
60 This library is free software; you can redistribute it and/or modify
61 it under the same terms as Perl itself.
62
63 =pod
+0
-237
lib/Moose/Cookbook/Recipe1.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe1 - The (always classic) B<Point> example.
6
7 =head1 SYNOPSIS
8
9 package Point;
10 use Moose;
11
12 has 'x' => (isa => 'Int', is => 'ro');
13 has 'y' => (isa => 'Int', is => 'rw');
14
15 sub clear {
16 my $self = shift;
17 $self->{x} = 0;
18 $self->y(0);
19 }
20
21 package Point3D;
22 use Moose;
23
24 extends 'Point';
25
26 has 'z' => (isa => 'Int');
27
28 after 'clear' => sub {
29 my $self = shift;
30 $self->{z} = 0;
31 };
32
33 =head1 DESCRIPTION
34
35 This is the classic Point example. This one in particular I took
36 from the Perl 6 Apocalypse 12 document, but it is similar to the
37 example found in the classic K&R C book as well, and many other
38 places. And now, onto the code:
39
40 As with all Perl 5 classes, a Moose class is defined in a package.
41 Moose now handles turning on C<strict> and C<warnings> for you, so
42 all you need to do is say C<use Moose>, and no kittens will die.
43
44 By loading Moose, we are enabling the loading of the Moose
45 "environment" into our package. This means that we import some
46 functions which serve as Moose "keywords". These aren't anything
47 fancy, just plain old exported functions.
48
49 Another important thing happens at this stage as well. Moose will
50 automatically set your package's superclass to be L<Moose::Object>.
51 The reason we do this, is so that we can be sure that your class
52 will inherit from L<Moose::Object> and get the benefits that
53 provides (such as a constructor; see L<Moose::Object> for details).
54 However, you don't actually I<have> to inherit from L<Moose::Object>
55 if you don't want to. All Moose features will still be accessible to
56 you.
57
58 Now, onto the keywords. The first one we see here is C<has>, which
59 defines an instance attribute in your class:
60
61 has 'x' => (isa => 'Int', is => 'ro');
62
63 This will create an attribute named C<x>, which will expect the
64 value stored in the attribute to pass the type constraint C<Int> (1),
65 and the accessor generated for this attribute will be read-only
66 (abbreviated as C<ro>).
67
68 The next C<has> line is very similar, with only one difference:
69
70 has 'y' => (isa => 'Int', is => 'rw');
71
72 A read/write (abbreviated as C<rw>) accessor will be generated for
73 the C<y> attribute.
74
75 At this point the attributes have been defined, and it is time to
76 define our methods. In Moose, as with regular Perl 5 OO, a method
77 is just a subroutine defined within the package. So here we create
78 the C<clear> method.
79
80 sub clear {
81 my $self = shift;
82 $self->{x} = 0;
83 $self->y(0);
84 }
85
86 It is pretty standard, the only thing to note is that we are directly
87 accessing the C<x> slot in the instance L<(2)>. This is because the
88 value was created with a read-only accessor. This also shows that Moose
89 objects are not anything out of the ordinary, but just regular old
90 blessed HASH references. This means they are very compatible with
91 other Perl 5 (non-Moose) classes as well.
92
93 The next part of the code to review is the B<Point> subclass,
94 B<Point3D>. The first item you might notice is that we do not use
95 the standard C<use base> declaration here. Instead we use the Moose
96 keyword C<extends> like so:
97
98 extends 'Point';
99
100 This keyword will function very much like C<use base> does in that
101 it will make an attempt to load your class if it has not already been
102 loaded. However, it differs on one important point. The C<extends>
103 keyword will overwrite any previous values in your package's C<@ISA>,
104 where C<use base> will C<push> values onto the package's C<@ISA>. It
105 is my opinion that the behavior of C<extends> is more intuitive in
106 that it is more explicit about defining the superclass relationship.
107
108 A small digression here: both Moose and C<extends> support multiple
109 inheritance. You simply pass all the superclasses to C<extends>,
110 like so:
111
112 extends 'Foo', 'Bar', 'Baz';
113
114 Now, back to our B<Point3D> class. The next thing we do is to create
115 a new attribute for B<Point3D> called C<z>.
116
117 has 'z' => (isa => 'Int');
118
119 As with B<Point>'s C<x> and C<y> attributes, this attribute has a
120 type constraint of C<Int>, but it differs in that it does B<not>
121 ask for any autogenerated accessors. The result being (aside from
122 broken object encapsulation) that C<z> is a private attribute.
123
124 Next comes another Moose feature which we call method "modifiers"
125 (or method "advice" for the AOP inclined). The modifier used here
126 is the C<after> modifier, and looks like this:
127
128 after 'clear' => sub {
129 my $self = shift;
130 $self->{z} = 0;
131 };
132
133 This modifier tells Moose to install a C<clear> method for
134 B<Point3D> that will first run the C<clear> method for the
135 superclass (in this case C<Point::clear>), and then run this
136 method I<after> it (passing in the same arguments as the original
137 method).
138
139 Now, of course using the C<after> modifier is not the only way to
140 accomplish this. I mean, after all, this B<is> Perl right? You
141 would get the same results with this code:
142
143 sub clear {
144 my $self = shift;
145 $self->SUPER::clear();
146 $self->{z} = 0;
147 }
148
149 You could also use another Moose method modifier, C<override> here,
150 and get the same results again. Here is how that would look:
151
152 override 'clear' => sub {
153 my $self = shift;
154 super();
155 $self->{z} = 0;
156 };
157
158 The C<override> modifier allows you to use the C<super> keyword
159 within it to dispatch to the superclass's method in a very Ruby-ish
160 style.
161
162 Now, of course, what use is a class if you can't instantiate objects
163 with it? Since B<Point> inherits from L<Moose::Object>, it will also
164 inherit the default L<Moose::Object> constructor: C<new>. Here
165 are two examples of how that is used:
166
167 my $point = Point->new(x => 1, y => 2);
168 my $point3d = Point3D->new(x => 1, y => 2, z => 3);
169
170 As you can see, C<new> accepts named argument pairs for any of the
171 attributes. It does not I<require> that you pass in the all the
172 attributes, and it will politely ignore any named arguments it does
173 not recognize.
174
175 From here on, you can use C<$point> and C<$point3d> just as you would
176 any other Perl 5 object. For a more detailed example of what can be
177 done, you can refer to the F<t/000_recipes/001_recipe.t> test file.
178
179 =head1 CONCLUSION
180
181 I hope this recipe has given you some explanation of how to use
182 Moose to build your Perl 5 classes. The next recipe will build upon
183 the basics shown here with more complex attributes and methods.
184 Please read on :)
185
186 =head1 FOOTNOTES
187
188 =over 4
189
190 =item (1)
191
192 Several default type constraints are provided by Moose, of which
193 C<Int> is one. For more information on the builtin type constraints
194 and the type constraint system in general, see the
195 L<Moose::Util::TypeConstraints> documentation.
196
197 =item (2)
198
199 Moose supports using instance structures other than blessed hash
200 references (such as in a glob reference -- see
201 L<MooseX::GlobRef::Object>). If you want your Moose classes to
202 be interchangeable, it is advisable to avoid direct instance
203 access, like that shown above. Moose does let you get and set
204 attributes directly without exposing the instance structure, but
205 that's an advanced topic (intrepid readers should refer to the
206 L<Moose::Meta::Attribute documentation>).
207
208 =back
209
210 =head1 SEE ALSO
211
212 =over 4
213
214 =item Method Modifiers
215
216 The concept of method modifiers is directly ripped off from CLOS. A
217 great explanation of them can be found by following this link.
218
219 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
220
221 =back
222
223 =head1 AUTHOR
224
225 Stevan Little E<lt>stevan@iinteractive.comE<gt>
226
227 =head1 COPYRIGHT AND LICENSE
228
229 Copyright 2006-2008 by Infinity Interactive, Inc.
230
231 L<http://www.iinteractive.com>
232
233 This library is free software; you can redistribute it and/or modify
234 it under the same terms as Perl itself.
235
236 =cut
+0
-212
lib/Moose/Cookbook/Recipe10.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe10 - The Moose::Role example
6
7 =head1 SYNOPSIS
8
9 package Eq;
10 use Moose::Role;
11
12 requires 'equal_to';
13
14 sub not_equal_to {
15 my ($self, $other) = @_;
16 not $self->equal_to($other);
17 }
18
19 package Comparable;
20 use Moose::Role;
21
22 with 'Eq';
23
24 requires 'compare';
25
26 sub equal_to {
27 my ($self, $other) = @_;
28 $self->compare($other) == 0;
29 }
30
31 sub greater_than {
32 my ($self, $other) = @_;
33 $self->compare($other) == 1;
34 }
35
36 sub less_than {
37 my ($self, $other) = @_;
38 $self->compare($other) == -1;
39 }
40
41 sub greater_than_or_equal_to {
42 my ($self, $other) = @_;
43 $self->greater_than($other) || $self->equal_to($other);
44 }
45
46 sub less_than_or_equal_to {
47 my ($self, $other) = @_;
48 $self->less_than($other) || $self->equal_to($other);
49 }
50
51 package Printable;
52 use Moose::Role;
53
54 requires 'to_string';
55
56 package US::Currency;
57 use Moose;
58
59 with 'Comparable', 'Printable';
60
61 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
62
63 sub compare {
64 my ($self, $other) = @_;
65 $self->amount <=> $other->amount;
66 }
67
68 sub to_string {
69 my $self = shift;
70 sprintf '$%0.2f USD' => $self->amount
71 }
72
73 =head1 DESCRIPTION
74
75 In this recipe we examine the role support provided in Moose. "Roles" may be
76 described in many ways, but there are two main ways in which they are used: as
77 interfaces, and as a means of code reuse. This recipe demonstrates the
78 construction and incorporation of roles that define comparison and display of
79 objects.
80
81 Let's start by examining B<Eq>. You'll notice that instead of the familiar C<use
82 Moose> you might be expecting, here we use C<Moose::Role> to make it clear that
83 this is a role. We encounter a new keyword, C<requires>:
84
85 requires 'equal_to';
86
87 What this does is to indicate that any class which "consumes" (that is to say,
88 "includes using C<with>", as we'll see a little later) the B<Eq> role I<must>
89 include an C<equal_to> method, whether this is provided by the class itself, one
90 of its superclasses, or another role consumed by the class (1).
91
92 In addition to requiring an C<equal_to> method, B<Eq> defines a C<not_equal_to>
93 method, which simply inverts the result of C<equal_to>. Defining additional
94 methods in this way, by using only a few base methods that target classes must
95 define, is a useful pattern to provide maximum functionality with minimum
96 effort.
97
98 After the minimal B<Eq>, we next move on to B<Comparable>. The first thing you
99 will notice is another new keyword, C<with>:
100
101 with 'Eq';
102
103 C<with> is used to provide a list of roles which this class (or role) consumes.
104 Here, B<Comparable> only consumes one role (B<Eq>). In effect, it is as if we
105 defined a C<not_equal_to> method within Comparable, and also promised to fulfill
106 the requirement of an C<equal_to> method.
107
108 B<Comparable> itself states that it requires C<compare>. Again, it means that
109 any classes consuming this role must implement a C<compare> method.
110
111 requires 'compare';
112
113 B<Comparable> defines an C<equal_to> method which satisfies the B<Eq> role's
114 requirements. This, along with a number of other methods (C<greater_than>,
115 C<less_than>, C<greater_than_or_equal_to>, and C<less_than_or_equal_to>) is
116 simply defined in terms of C<compare>, once again demonstrating the pattern of
117 defining a number of utility methods in terms of only a single method that the
118 target class need implement.
119
120 sub equal_to {
121 my ($self, $other) = @_;
122 $self->compare($other) == 0;
123 }
124
125 sub greater_than {
126 my ($self, $other) = @_;
127 $self->compare($other) == 1;
128 }
129
130 sub less_than {
131 my ($self, $other) = @_;
132 $self->compare($other) == -1;
133 }
134
135 sub greater_than_or_equal_to {
136 my ($self, $other) = @_;
137 $self->greater_than($other) || $self->equal_to($other);
138 }
139
140 sub less_than_or_equal_to {
141 my ($self, $other) = @_;
142 $self->less_than($other) || $self->equal_to($other);
143 }
144
145 Next up is B<Printable>. This is a very simple role, akin to B<Eq>. It merely
146 requires a C<to_string> method.
147
148 Finally, we come to B<US::Currency>, a class that allows us to reap the benefits
149 of our hard work. This is a regular Moose class, so we include the normal C<use
150 Moose>. It consumes both B<Comparable> and B<Printable>, as the following line
151 shows:
152
153 with 'Comparable', 'Printable';
154
155 It also defines a regular Moose attribute, C<amount>, with a type constraint of
156 C<Num> and a default of C<0>:
157
158 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
159
160 Now we come to the core of the class. First up, we define a C<compare> method:
161
162 sub compare {
163 my ($self, $other) = @_;
164 $self->amount <=> $other->amount;
165 }
166
167 As you can see, it simply compares the C<amount> attribute of this object with
168 the C<amount> attribute of the other object passed to it. With the single
169 definition of this method, we gain the following methods for free: C<equal_to>,
170 C<greater_than>, C<less_than>, C<greater_than_or_equal_to> and
171 C<less_than_or_equal_to>.
172
173 We end the class with a definition of the C<to_string> method, which formats the
174 C<amount> attribute for display:
175
176 sub to_string {
177 my $self = shift;
178 sprintf '$%0.2f USD' => $self->amount
179 }
180
181 =head1 CONCLUSION
182
183 This recipe has shown that roles can be very powerful and immensely useful, and
184 save a great deal of repetition.
185
186 =head1 FOOTNOTES
187
188 =over 4
189
190 =item (1)
191
192 At present, method requirements from roles cannot be satisfied by attribute
193 accessors. This is a limitation of Moose, and will most likely be rectified in a
194 future release.
195
196 =back
197
198 =head1 AUTHOR
199
200 Stevan Little E<lt>stevan@iinteractive.comE<gt>
201
202 =head1 COPYRIGHT AND LICENSE
203
204 Copyright 2006-2008 by Infinity Interactive, Inc.
205
206 L<http://www.iinteractive.com>
207
208 This library is free software; you can redistribute it and/or modify
209 it under the same terms as Perl itself.
210
211 =cut
+0
-121
lib/Moose/Cookbook/Recipe11.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe11 - Advanced Role Composition - method exclusion and aliasing
6
7 =head1 SYNOPSIS
8
9 package Restartable;
10 use Moose::Role;
11
12 has 'is_paused' => (
13 is => 'rw',
14 isa => 'Bool',
15 default => 0,
16 );
17
18 requires 'save_state', 'load_state';
19
20 sub stop { ... }
21
22 sub start { ... }
23
24 package Restartable::ButUnreliable;
25 use Moose::Role;
26
27 with 'Restartable' => { alias => { stop => '_stop',
28 start => '_start' } };
29
30 sub stop {
31 my $self = shift;
32
33 $self->explode() if rand(1) > .5;
34
35 $self->_stop();
36 }
37
38 sub start {
39 my $self = shift;
40
41 $self->explode() if rand(1) > .5;
42
43 $self->_start();
44 }
45
46 package Restartable::ButBroken;
47 use Moose::Role;
48
49 with 'Restartable' => { excludes => [ 'stop', 'start' ] };
50
51 sub stop {
52 my $self = shift;
53
54 $self->explode();
55 }
56
57 sub start {
58 my $self = shift;
59
60 $self->explode();
61 }
62
63 =head1 DESCRIPTION
64
65 Sometimes when you include a role in a class, you may want to leave
66 out some of its methods. In this example, we have a role C<Restartable>
67 which provides an C<is_paused> attribute, and two methods, C<stop> and
68 C<start>. The implementation of those two methods is irrelevant.
69
70 Then we have two more roles which also implement the same interface,
71 each putting their own spin on the C<stop> and C<start> method.
72
73 In the C<Restartable::ButUnreliable> role, we want to provide a new
74 implementation of C<stop> and C<start>, but still have access to the
75 original implementation. To do this, we alias the methods from
76 C<Restartable> to private methods, and provide wrappers around the
77 originals (1).
78
79 In the C<Restartable::ButBroken> role, we want to provide an entirely
80 new behavior for C<stop> and C<start>, so we exclude them when
81 composing the C<Restartable> role into C<Restartable::ButBroken>.
82
83 It's worth noting that the C<excludes> parameter also accepts a single
84 string as an argument if you just want to exclude one method.
85
86 =head1 CONCLUSION
87
88 Method exclusion and renaming can come in handy, especially when
89 building roles out of other roles. In this example, all of our roles
90 implement the C<Restartable> role. Each role provides same API, but
91 each has a different implementation under the hood.
92
93 You can also use the method aliasing and excluding features when
94 composing a role into a class.
95
96 =head1 FOOTNOTES
97
98 =over 4
99
100 =item (1)
101
102 The mention of wrapper should tell you that we could do the same thing
103 using method modifiers, but for the sake of this example, we don't.
104
105 =back
106
107 =head1 AUTHOR
108
109 Dave Rolsky E<lt>autarch@urth.orgE<gt>
110
111 =head1 COPYRIGHT AND LICENSE
112
113 Copyright 2006-2008 by Infinity Interactive, Inc.
114
115 L<http://www.iinteractive.com>
116
117 This library is free software; you can redistribute it and/or modify
118 it under the same terms as Perl itself.
119
120 =cut
+0
-211
lib/Moose/Cookbook/Recipe2.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe2 - A simple B<BankAccount> example
6
7 =head1 SYNOPSIS
8
9 package BankAccount;
10 use Moose;
11
12 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
13
14 sub deposit {
15 my ($self, $amount) = @_;
16 $self->balance($self->balance + $amount);
17 }
18
19 sub withdraw {
20 my ($self, $amount) = @_;
21 my $current_balance = $self->balance();
22 ($current_balance >= $amount)
23 || confess "Account overdrawn";
24 $self->balance($current_balance - $amount);
25 }
26
27 package CheckingAccount;
28 use Moose;
29
30 extends 'BankAccount';
31
32 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
33
34 before 'withdraw' => sub {
35 my ($self, $amount) = @_;
36 my $overdraft_amount = $amount - $self->balance();
37 if ($self->overdraft_account && $overdraft_amount > 0) {
38 $self->overdraft_account->withdraw($overdraft_amount);
39 $self->deposit($overdraft_amount);
40 }
41 };
42
43 =head1 DESCRIPTION
44
45 In the first recipe we demonstrated the construction of basic
46 Moose classes whose attributes had various accessor schemes and
47 builtin type constraints. However, our objects were very data-
48 oriented, and did not have many behavioral aspects (i.e. methods)
49 to them. In this recipe, we will expand upon the concepts from
50 the first recipe and give a more realistic scenario of more
51 behavior oriented classes.
52
53 We are using the example of a bank account, which has a standard
54 account (you can deposit money, withdraw money and check your
55 current balance), and a checking account which has optional
56 overdraft protection. The overdraft protection will protect the
57 owner of the checking account by automatically withdrawing the
58 needed funds from the overdraft account to ensure that a check
59 will not bounce.
60
61 Now, onto the code. The first class, B<BankAccount>, introduces a
62 new attribute feature: a default value.
63
64 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
65
66 This tells us that a B<BankAccount> has a C<balance> attribute,
67 which has the C<Int> type constraint, a read/write accessor,
68 and a default value of C<0>. This means that every instance of
69 B<BankAccount> that is created will have its C<balance> slot
70 initialized to C<0>. Very simple really :)
71
72 Next come the methods. The C<deposit> and C<withdraw> methods
73 should be fairly self-explanatory; they are nothing specific to
74 Moose, just your standard Perl 5 OO.
75
76 Now, onto the B<CheckingAccount> class. As you know from the
77 first recipe, the keyword C<extends> sets a class's superclass
78 relationship. Here we see that B<CheckingAccount> is a
79 B<BankAccount>. The next line introduces yet another new aspect
80 of Moose, that of class-based type-constraints:
81
82 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
83
84 Up until now, we have only had C<Int> type constraints, which
85 (as I said in the first recipe) is a builtin type constraint
86 that Moose provides for you. The C<BankAccount> type constraint
87 is new, and was actually defined the moment we created the
88 B<BankAccount> class itself. In fact, for every class in
89 your program, a corresponding type constraint will be created. This
90 means that in the first recipe, both C<Point> and C<Point3D> type
91 constraints were created, and in this recipe, both C<BankAccount>
92 and C<CheckingAccount> type constraints were created. Moose does
93 this as a convenience so that your class model and the type
94 constraint model can be kept in sync with one another. In short,
95 Moose makes sure that it will just DWIM (1).
96
97 Next, we come to the behavioral part of B<CheckingAccount>, and
98 again we see a method modifier, but this time it is a C<before>
99 modifier.
100
101 before 'withdraw' => sub {
102 my ($self, $amount) = @_;
103 my $overdraft_amount = $amount - $self->balance();
104 if ($self->overdraft_account && $overdraft_amount > 0) {
105 $self->overdraft_account->withdraw($overdraft_amount);
106 $self->deposit($overdraft_amount);
107 }
108 };
109
110 Just as with the C<after> modifier from the first recipe, Moose
111 will handle calling the superclass method (in this case the
112 C<BankAccount::withdraw> method). The C<before> modifier shown
113 above will run (obviously) I<before> the code from the superclass
114 with run. The C<before> modifier here implements the overdraft
115 protection by first checking if there are enough available
116 funds in the checking account and if not (and if there is an overdraft
117 account available), it transfers the appropriate funds into the
118 checking account.
119
120 As with the method modifier in the first recipe, there is another
121 way to accomplish this same thing using the built in C<SUPER::>
122 pseudo-package. So the above method is equivalent to the one here.
123
124 sub withdraw {
125 my ($self, $amount) = @_;
126 my $overdraft_amount = $amount - $self->balance();
127 if ($self->overdraft_account && $overdraft_amount > 0) {
128 $self->overdraft_account->withdraw($overdraft_amount);
129 $self->deposit($overdraft_amount);
130 }
131 $self->SUPER::withdraw($amount);
132 }
133
134 The benefits of taking the method modifier approach is that the
135 author of the B<BankAccount> subclass does not need to remember
136 to call C<SUPER::withdraw> and to pass it the C<$amount> argument.
137 Instead the method modifier ensures that all arguments make it
138 to the superclass method correctly. But this is actually more
139 than just a convenience for forgetful programmers, it also helps
140 isolate subclasses from changes in the superclasses. For instance,
141 if B<BankAccount::withdraw> were to add an additional argument
142 of some kind, the version of B<CheckingAccount::withdraw> which
143 uses C<SUPER::withdraw> would not pass that extra argument
144 correctly, whereas the method modifier version would automatically
145 pass along all arguments correctly.
146
147 Just as with the first recipe, object instantiation is a fairly
148 normal process, here is an example:
149
150 my $savings_account = BankAccount->new(balance => 250);
151 my $checking_account = CheckingAccount->new(
152 balance => 100,
153 overdraft_account => $savings_account
154 );
155
156 And as with the first recipe, a more in-depth example of using
157 these classes can be found in the F<t/000_recipes/002_recipe.t> test file.
158
159 =head1 CONCLUSION
160
161 The aim of this recipe was to take the knowledge gained in the
162 first recipe and expand upon it with a more realistic use case. I
163 hope that this recipe has accomplished this goal. The next recipe
164 will expand even more upon the capabilities of attributes in Moose
165 to create a behaviorally sophisticated class almost entirely
166 defined by attributes.
167
168 =head1 FOOTNOTES
169
170 =over 4
171
172 =item (1)
173
174 Moose does not attempt to encode a class's is-a relationships
175 within the type constraint hierarchy. Instead, Moose just
176 considers the class type constraint to be a subtype of C<Object>,
177 and specializes the constraint check to allow for subclasses. This
178 means that an instance of B<CheckingAccount> will pass a
179 C<BankAccount> type constraint successfully. For more details,
180 please refer to the L<Moose::Util::TypeConstraints> documentation.
181
182 =back
183
184 =head1 SEE ALSO
185
186 =over 4
187
188 =item Acknowledgment
189
190 The BankAccount example in this recipe is directly taken from the
191 examples in this chapter of "Practical Common Lisp":
192
193 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
194
195 =back
196
197 =head1 AUTHOR
198
199 Stevan Little E<lt>stevan@iinteractive.comE<gt>
200
201 =head1 COPYRIGHT AND LICENSE
202
203 Copyright 2006-2008 by Infinity Interactive, Inc.
204
205 L<http://www.iinteractive.com>
206
207 This library is free software; you can redistribute it and/or modify
208 it under the same terms as Perl itself.
209
210 =cut
+0
-309
lib/Moose/Cookbook/Recipe21.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe21 - The meta-attribute example
6
7 =head1 SYNOPSIS
8
9 package MyApp::Meta::Attribute::Labeled;
10 use Moose;
11 extends 'Moose::Meta::Attribute';
12
13 has label => (
14 is => 'rw',
15 isa => 'Str',
16 predicate => 'has_label',
17 );
18
19 package Moose::Meta::Attribute::Custom::Labeled;
20 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
21
22 package MyApp::Website;
23 use Moose;
24 use MyApp::Meta::Attribute::Labeled;
25
26 has url => (
27 metaclass => 'Labeled',
28 is => 'rw',
29 isa => 'Str',
30 label => "The site's URL",
31 );
32
33 has name => (
34 is => 'rw',
35 isa => 'Str',
36 );
37
38 sub dump {
39 my $self = shift;
40
41 # iterate over all the attributes in $self
42 my %attributes = %{ $self->meta->get_attribute_map };
43 while (my ($name, $attribute) = each %attributes) {
44
45 # print the label if available
46 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
47 && $attribute->has_label) {
48 print $attribute->label;
49 }
50 # otherwise print the name
51 else {
52 print $name;
53 }
54
55 # print the attribute's value
56 my $reader = $attribute->get_read_method;
57 print ": " . $self->$reader . "\n";
58 }
59 }
60
61 package main;
62 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
63 $app->dump;
64
65 =head1 SUMMARY
66
67 In this recipe, we begin to really delve into the wonder of meta-programming.
68 Some readers may scoff and claim that this is the arena only of the most
69 twisted Moose developers. Absolutely not! Any sufficiently twisted developer
70 can benefit greatly from going more meta.
71
72 The high-level goal of this recipe's code is to allow each attribute to have a
73 human-readable "label" attached to it. Such labels would be used when showing
74 data to an end user. In this recipe we label the "url" attribute with "The
75 site's URL" and create a simple method to demonstrate how to use that label.
76
77 =head1 REAL ATTRIBUTES 101
78
79 All the attributes of a Moose-based object are actually objects themselves.
80 These objects have methods and (surprisingly) attributes. Let's look at a
81 concrete example.
82
83 has 'x' => (isa => 'Int', is => 'ro');
84 has 'y' => (isa => 'Int', is => 'rw');
85
86 Ahh, the veritable x and y of the Point example. Internally, every Point has an
87 x object and a y object. They have methods (such as "get_value") and attributes
88 (such as "is_lazy"). What class are they instances of?
89 L<Moose::Meta::Attribute>. You don't normally see the objects lurking behind
90 the scenes, because you usually just use C<< $point->x >> and C<< $point->y >>
91 and forget that there's a lot of machinery lying in such methods.
92
93 So you have a C<$point> object, which has C<x> and C<y> methods. How can you
94 actually access the objects behind these attributes? Here's one way:
95
96 $point->meta->get_attribute_map()
97
98 C<get_attribute_map> returns a hash reference that maps attribute names to
99 their objects. In our case, C<get_attribute_map> might return something that
100 looks like the following:
101
102 {
103 x => Moose::Meta::Attribute=HASH(0x196c23c),
104 y => Moose::Meta::Attribute=HASH(0x18d1690),
105 }
106
107 Another way to get a handle on an attribute's object is
108 C<< $self->meta->get_attribute('name') >>. Here's one thing you can do now that
109 you can interact with the attribute's object directly:
110
111 print $point->meta->get_attribute('x')->type_constraint;
112 => Int
113
114 (As an aside, it's not called C<< ->isa >> because C<< $obj->isa >> is already
115 taken)
116
117 So to actually beef up attributes, what we need to do is:
118
119 =over 4
120
121 =item Create a new attribute metaclass
122
123 =item Create attributes using that new metaclass
124
125 =back
126
127 Moose makes both of these easy!
128
129 Let's start dissecting the recipe's code.
130
131 =head1 DISSECTION
132
133 We get the ball rolling by creating a new attribute metaclass. It starts off
134 somewhat ungloriously.
135
136 package MyApp::Meta::Attribute::Labeled;
137 use Moose;
138 extends 'Moose::Meta::Attribute';
139
140 You subclass metaclasses the same way you subclass regular classes. (Extra
141 credit: how in the actual hell can you use the MOP to extend itself?)
142
143 has label => (
144 is => 'rw',
145 isa => 'Str',
146 predicate => 'has_label',
147 );
148
149 Hey, this looks pretty reasonable! This is plain jane Moose code. Recipe 1
150 fare. This is merely making a new attribute. An attribute that attributes have.
151 A meta-attribute. It may sound scary, but it really isn't! Reread
152 L<REAL ATTRIBUTES 101> if this really is terrifying.
153
154 The name is "label", it will have a regular accessor, and is a string.
155 C<predicate> is a standard part of C<has>. It just creates a method that asks
156 the question "Does this attribute have a value?"
157
158 package Moose::Meta::Attribute::Custom::Labeled;
159 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
160
161 This lets Moose discover our new metaclass. That way attributes can actually
162 use it. More on what this is doing in a moment.
163
164 Note that we're done defining the new metaclass! Only nine lines of code, and
165 not particularly difficult lines, either. Now to start using the metaclass.
166
167 package MyApp::Website;
168 use Moose;
169 use MyApp::Meta::Attribute::Labeled;
170
171 Nothing new here. We do have to actually load our metaclass to be able to use
172 it.
173
174 has url => (
175 metaclass => 'Labeled',
176 is => 'rw',
177 isa => 'Str',
178 label => "The site's URL",
179 );
180
181 Ah ha! Now we're using the metaclass. We're adding a new attribute, C<url>, to
182 C<MyApp::Website>. C<has> lets you set the metaclass of the attribute.
183 Ordinarily (as we've seen), the metaclass is C<Moose::Meta::Attribute>.
184
185 When C<has> sees that you're using a new metaclass, it will take the
186 metaclass's name, prepend C<Moose::Meta::Attribute::Custom::>, and call the
187 C<register_implementation> function in that package. So here Moose calls
188 C<Moose::Meta::Attribute::Custom::Labeled::register_implementation>. We defined
189 that function in the beginning -- it just returns our "real" metaclass'
190 package, C<MyApp::Meta::Attribute::Labeled>. So Moose uses that metaclass for
191 the attribute. It may seem a bit convoluted, but the alternative would be to
192 use C<< metaclass => 'MyApp::Meta::Attribute::Labeled' >> on every attribute.
193 As usual, Moose optimizes in favor of the end user, not the metaprogrammer. :)
194 We also could have just defined the metaclass in
195 C<Moose::Meta::Attribute::Custom::Labeled>, but it's probably better to keep to
196 your own namespaces.
197
198 Finally, we see that C<has> is setting our new meta-attribute, C<label>, to
199 C<"The site's URL">. We can access this meta-attribute with:
200
201 $website->meta->get_attribute('url')->label()
202
203 Well, back to the code.
204
205 has name => (
206 is => 'rw',
207 isa => 'Str',
208 );
209
210 Of course, you don't have to use the new metaclass for B<all> new attributes.
211
212 Now we begin defining a method that will dump the C<MyApp::Website> instance
213 for human readers.
214
215 sub dump {
216 my $self = shift;
217
218 # iterate over all the attributes in $self
219 my %attributes = %{ $self->meta->get_attribute_map };
220 while (my ($name, $attribute) = each %attributes) {
221
222 Recall that C<get_attribute_map> returns a hashref of attribute names and their
223 associated objects.
224
225 # print the label if available
226 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
227 && $attribute->has_label) {
228 print $attribute->label;
229 }
230
231 We have two checks here. The first is "is this attribute an instance of
232 C<MyApp::Meta::Attribute::Labeled>?". It's good to code defensively. Even if
233 all of your attributes have this metaclass, you never know when someone is
234 going to subclass your work of art. Poorly. In other words, it's likely that
235 there will still be (many) attributes that are instances of the default
236 C<Moose::Meta::Attribute>.
237
238 The second check is "does this attribute have a label?". This method was
239 defined in the new metaclass as the "predicate". If we pass both checks, we
240 print the attribute's label.
241
242 # otherwise print the name
243 else {
244 print $name;
245 }
246
247 Another good, defensive coding practice: Provide reasonable defaults.
248
249 # print the attribute's value
250 my $reader = $attribute->get_read_method;
251 print ": " . $self->$reader . "\n";
252 }
253 }
254
255 Here's another example of using the attribute metaclass.
256 C<< $attribute->get_read_method >> returns the name of the method that can
257 be invoked on the original object to read the attribute's value.
258 C<< $self->$reader >> is an example of "reflection" -- instead of using the
259 name of the method, we're using a variable with the name of the method in it.
260 Perl doesn't mind. Another way to write this would be
261 C<< $self->can($reader)->($self) >>. Yuck. :)
262
263 package main;
264 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
265 $app->dump;
266
267 And we wrap up the example with a script to show off our newfound magic.
268
269 =head1 CONCLUSION
270
271 Why oh why would you want to go through all of these contortions when you can
272 just print "The site's URL" directly in the C<dump> method? For one, the DRY
273 (Don't Repeat Yourself) principle. If you have it in the C<dump> method, you'll
274 probably also have it in the C<as_form> method, and C<to_file>, and so on. So
275 why not have a method that maps attribute names to labels? That could work, but
276 why not include the label where it belongs, in the attribute's definition?
277 That way you're also less likely to forget to add the label.
278
279 More importantly, this was a very simple example. Your metaclasses aren't
280 limited to just adding new meta-attributes. For example, you could implement
281 a metaclass that expires attributes after a certain amount of time. You
282 might use it as such:
283
284 has site_cache => (
285 metaclass => 'TimedExpiry',
286 expires_after => { hours => 1 },
287 refresh_with => sub { get($_->url) },
288 isa => 'Str',
289 is => 'ro',
290 );
291
292 The sky's the limit!
293
294 =head1 AUTHOR
295
296 Shawn M Moore E<lt>sartak@gmail.comE<gt>
297
298 =head1 COPYRIGHT AND LICENSE
299
300 Copyright 2006-2008 by Infinity Interactive, Inc.
301
302 L<http://www.iinteractive.com>
303
304 This library is free software; you can redistribute it and/or modify
305 it under the same terms as Perl itself.
306
307 =cut
308
+0
-207
lib/Moose/Cookbook/Recipe22.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe22 - The attribute trait example
6
7 =head1 SYNOPSIS
8
9 package MyApp::Meta::Attribute::Trait::Labeled;
10 use Moose::Role;
11
12 has label => (
13 is => 'rw',
14 isa => 'Str',
15 predicate => 'has_label',
16 );
17
18 package Moose::Meta::Attribute::Custom::Trait::Labeled;
19 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
20
21 package MyApp::Website;
22 use Moose;
23 use MyApp::Meta::Attribute::Trait::Labeled;
24
25 has url => (
26 traits => [qw/Labeled/],
27 is => 'rw',
28 isa => 'Str',
29 label => "The site's URL",
30 );
31
32 has name => (
33 is => 'rw',
34 isa => 'Str',
35 );
36
37 sub dump {
38 my $self = shift;
39
40 # iterate over all the attributes in $self
41 my %attributes = %{ $self->meta->get_attribute_map };
42 while (my ($name, $attribute) = each %attributes) {
43
44 # print the label if available
45 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
46 && $attribute->has_label) {
47 print $attribute->label;
48 }
49 # otherwise print the name
50 else {
51 print $name;
52 }
53
54 # print the attribute's value
55 my $reader = $attribute->get_read_method;
56 print ": " . $self->$reader . "\n";
57 }
58 }
59
60 package main;
61 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
62 $app->dump;
63
64 =head1 BUT FIRST
65
66 This recipe is a continuation of L<Moose::Cookbook::Recipe21>. Please read that
67 first.
68
69 =head1 MOTIVATION
70
71 In Recipe 21, we created an attribute metaclass that gives attributes a "label"
72 that can be set in L<Moose/has>. That works well until you want a second
73 meta-attribute, or until you want to adjust the behavior of the attribute. You
74 could define a specialized attribute metaclass to use in every attribute.
75 However, you may want different attributes to have different behaviors. You
76 might end up with a unique attribute metaclass for B<every single attribute>,
77 with a lot of code copying and pasting!
78
79 Or, if you've been drinking deeply of the Moose kool-aid, you'll have a role
80 for each of the behaviors. One role would give a label meta-attribute. Another
81 role would signify that this attribute is not directly modifiable via the
82 REST interface. Another role would write to a logfile when this attribute
83 was read.
84
85 Unfortunately, you'd still be left with a bunch of attribute metaclasses that
86 do nothing but compose a bunch of roles. If only there were some way to specify
87 in L<Moose/has> a list of roles to apply to the attribute metaclass...
88
89 =head1 TRAITS
90
91 Roles that apply to metaclasses have a special name: traits. Don't let the
92 change in nomenclature fool you, B<traits are just roles>.
93
94 L<Moose/has> provides a C<traits> option. It takes a list of trait names to
95 compose into an anonymous metaclass. That means you do still have a bunch of
96 attribute metaclasses that do nothing but compose a bunch of roles, but they're
97 managed automatically by Moose. You don't need to declare them in advance, or
98 worry whether changing one will affect some other attribute.
99
100 What can traits do? Anything roles can do. They can add or refine attributes,
101 wrap methods, provide more methods, define an interface, etc. The only
102 difference is that you're now changing the attribute metaclass instead of a
103 user-level class.
104
105 =head1 DISSECTION
106
107 A side-by-side look of the code examples in this recipe and recipe 21 should
108 indicate that defining and using a trait is very similar to defining and using
109 a new attribute metaclass.
110
111 package MyApp::Meta::Attribute::Trait::Labeled;
112 use Moose::Role;
113
114 has label => (
115 is => 'rw',
116 isa => 'Str',
117 predicate => 'has_label',
118 );
119
120 Instead of subclassing L<Moose::Meta::Attribute>, we define a role. Traits
121 don't need any special methods or attributes. You just focus on whatever it is
122 you actually need to get done. Here we're adding a new meta-attribute for use
123 in our application.
124
125 package Moose::Meta::Attribute::Custom::Trait::Labeled;
126 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
127
128 Much like when we define a new attribute metaclass, we can provide a shorthand
129 name for the trait. Moose looks at the C<register_implementation> method in
130 C<Moose::Meta::Attribute::Custom::Trait::$TRAIT_NAME> to find the full
131 name of the trait.
132
133 Now we begin writing our application logic. I'll only cover what has changed
134 since recipe 21.
135
136 has url => (
137 traits => [qw/Labeled/],
138 is => 'rw',
139 isa => 'Str',
140 label => "The site's URL",
141 );
142
143 L<Moose/has> provides a C<traits> option. Just pass the list of trait names and
144 it will compose them together to form the (anonymous) attribute metaclass used
145 by the attribute. We provide a label for the attribute in the same way.
146
147 # print the label if available
148 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
149 && $attribute->has_label) {
150 print $attribute->label;
151 }
152
153 Previously, this code asked the question "Does this attribute use our attribute
154 metaclass?" Since we're now using a trait, we ask "Does this attribute's
155 metaclass do the C<Labeled> role?" If not, the attribute metaclass won't have
156 the C<has_label> method, and so it would be an error to blindly call
157 C<< $attribute->has_label >>.
158
159 That's all. Everything else is the same!
160
161 =head1 METACLASS + TRAIT
162
163 "But wait!" you protest. "I've already written all of my extensions as
164 attribute metaclasses. I don't want to break all that code out there."
165
166 All is not lost. If you rewrite your extension as a trait, then you can
167 easily get a regular metaclass extension out of it. You just compose the trait
168 in the attribute metaclass, as normal.
169
170 package MyApp::Meta::Attribute::Labeled;
171 use Moose;
172 extends 'Moose::Meta::Attribute';
173 with 'MyApp::Meta::Attribute::Trait::Labeled';
174
175 package Moose::Meta::Attribute::Custom::Labeled;
176 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
177
178 Unfortunately, going the other way (providing a trait created from a metaclass)
179 is more tricky. Thus, defining your extensions as traits is just plain better
180 than defining them as subclassed metaclasses.
181
182 =head1 CONCLUSION
183
184 If you're extending your attributes, it's easier and more flexible to provide
185 composable bits of behavior than to subclass L<Moose::Meta::Attribute>.
186 Using traits (which are just roles applied to a metaclass!) let you choose
187 exactly which behaviors each attribute will have. Moose makes it easy to create
188 attribute metaclasses on the fly by providing a list of trait names to
189 L<Moose/has>.
190
191 =head1 AUTHOR
192
193 Shawn M Moore E<lt>sartak@gmail.comE<gt>
194
195 =head1 COPYRIGHT AND LICENSE
196
197 Copyright 2006-2008 by Infinity Interactive, Inc.
198
199 L<http://www.iinteractive.com>
200
201 This library is free software; you can redistribute it and/or modify
202 it under the same terms as Perl itself.
203
204 =cut
205
206
+0
-240
lib/Moose/Cookbook/Recipe3.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe3 - A lazy B<BinaryTree> example
6
7 =head1 SYNOPSIS
8
9 package BinaryTree;
10 use Moose;
11
12 has 'node' => (is => 'rw', isa => 'Any');
13
14 has 'parent' => (
15 is => 'rw',
16 isa => 'BinaryTree',
17 predicate => 'has_parent',
18 weak_ref => 1,
19 );
20
21 has 'left' => (
22 is => 'rw',
23 isa => 'BinaryTree',
24 predicate => 'has_left',
25 lazy => 1,
26 default => sub { BinaryTree->new(parent => $_[0]) },
27 );
28
29 has 'right' => (
30 is => 'rw',
31 isa => 'BinaryTree',
32 predicate => 'has_right',
33 lazy => 1,
34 default => sub { BinaryTree->new(parent => $_[0]) },
35 );
36
37 before 'right', 'left' => sub {
38 my ($self, $tree) = @_;
39 $tree->parent($self) if defined $tree;
40 };
41
42 =head1 DESCRIPTION
43
44 In this recipe we take a closer look at attributes, and see how
45 some of their more advanced features can be used to create fairly
46 complex behaviors.
47
48 The class in this recipe is a classic binary tree, each node in the
49 tree is represented by an instance of the B<BinaryTree> class. Each
50 instance has a C<node> slot to hold an arbitrary value, a C<right>
51 slot to hold the right node, a C<left> slot to hold the left node,
52 and finally a C<parent> slot to hold a reference back up the tree.
53
54 Now, let's start with the code. Our first attribute is the C<node>
55 slot, defined as such:
56
57 has 'node' => (is => 'rw', isa => 'Any');
58
59 If you recall from the previous recipes, this slot will have a read/write
60 accessor generated for it, and has a type constraint on it. The new item here is
61 the type constraint of C<Any>. C<Any> is the "root" of the
62 L<Moose::Util::TypeConstraints> type hierarchy. It means exactly what it says:
63 I<any> value passes the constraint. Now, you could just as easily have left out
64 the C<isa>, leaving the C<node> slot unconstrained and retaining this
65 behavior. But in this case, we are really including the type constraint for the
66 benefit of other programmers, not the computer. It makes clear my intent that
67 the C<node> attribute can be of any type, and that the class is a polymorphic
68 container.
69
70 Next, let's move on to the C<parent> slot:
71
72 has 'parent' => (
73 is => 'rw',
74 isa => 'BinaryTree',
75 predicate => 'has_parent',
76 weak_ref => 1,
77 );
78
79 As you already know, this code tells you that C<parent> gets a read/write
80 accessor and is constrained to only accept instances of B<BinaryTree>. You will
81 of course remember from the second recipe that the C<BinaryTree> type constraint
82 is automatically created for us by Moose.
83
84 The next attribute option is new, though: the C<predicate> option.
85 This option creates a method which can be used to check whether
86 a given slot (in this case C<parent>) has been initialized. In
87 this case it will create a method called C<has_parent>. Quite simple,
88 and quite handy too.
89
90 This brings us to our last attribute option, also a new one. Since C<parent> is
91 a circular reference (the tree in C<parent> should already have a reference to
92 this one, in its C<left> or C<right> node), we want to make sure that it is also
93 a weakened reference to avoid memory leaks. The C<weak_ref> attribute option
94 will do just that, C<weak_ref> simply takes a boolean value (C<1> or C<0>) and
95 then alters the accessor function to weaken the reference to any value stored in
96 the C<parent> slot (1).
97
98 Now, onto the C<left> and C<right> attributes. They are essentially identical,
99 save for different names, so I will just describe one here:
100
101 has 'left' => (
102 is => 'rw',
103 isa => 'BinaryTree',
104 predicate => 'has_left',
105 lazy => 1,
106 default => sub { BinaryTree->new(parent => $_[0]) },
107 );
108
109 You already know what the C<is>, C<isa> and C<predicate> options do, but now we
110 have two new options. These two options are actually linked together, in fact:
111 you cannot use the C<lazy> option unless you have set the C<default> option.
112 Class creation will fail with an exception (2).
113
114 Before I go into detail about how C<lazy> works, let me first
115 explain how C<default> works, and in particular why it is wrapped
116 in a CODE ref.
117
118 In the second recipe the B<BankAccount>'s C<balance> slot had a
119 default value of C<0>. Since Perl will copy strings and numbers
120 by value, this was all we had to say. But for any other item
121 (ARRAY ref, HASH ref, object instance, etc) you would need to
122 wrap it in a CODE reference, so this:
123
124 has 'foo' => (is => 'rw', default => []);
125
126 is actually illegal in Moose. Instead, what you really want is this:
127
128 has 'foo' => (is => 'rw', default => sub { [] });
129
130 This ensures that each instance of this class will get its own ARRAY ref in the
131 C<foo> slot.
132
133 One other feature of the CODE ref version of the C<default> option is that when
134 the subroutine is executed (to get the default value), we pass in the instance
135 where the slot will be stored. This can come in quite handy at times, as
136 illustrated above, with this code:
137
138 default => sub { BinaryTree->new(parent => $_[0]) },
139
140 The default value being generated is a new C<BinaryTree> instance for the
141 C<left> (or C<right>) slot. Here we set up the correct relationship by passing
142 the current instance as the C<parent> argument to the constructor.
143
144 Now, before we go on to the C<lazy> option, I want you to think
145 for a moment. When an instance of this class is created, and the
146 slots are being initialized, the "normal" behavior would be for
147 the C<left> and C<right> slots to be populated with a new instance
148 of B<BinaryTree>. In creating that instance of the C<left> or
149 C<right> slots, we would need to create new instances to populate
150 the C<left> and C<right> slots of I<those> instances. This would
151 continue in an I<infinitely recursive spiral of death> until you had
152 exhausted all available memory on your machine.
153
154 This is, of course, not good :)
155
156 Which brings us to the C<lazy> attribute option. The C<lazy> option does just
157 what it says: it lazily initializes the slot within the instance. This means
158 that it waits till absolutely the I<latest> possible moment to populate the
159 slot. So if you, the user, store a value in the slot, everything works normally,
160 and what you pass in is stored. However, if you I<read> the slot I<before>
161 storing a value in it, then at that I<exact> moment (and no sooner), the slot
162 will be populated with the value of the C<default> option.
163
164 This option is what allows the B<BinaryTree> class to instantiate
165 objects without fear of the I<infinitely recursive spiral of death>
166 mentioned earlier.
167
168 So, we have described a quite complex set of behaviors here, and not one method
169 had to be written. But wait, we aren't quite done yet; the autogenerated
170 C<right> and C<left> accessors are not completely correct. They will not install
171 the parental relationships that we need. We could write our own accessors, but
172 that would require us to implement all those features we got automatically (type
173 constraints, lazy initialization, and so on). Instead, we use method modifiers
174 again:
175
176 before 'right', 'left' => sub {
177 my ($self, $tree) = @_;
178 $tree->parent($self) if defined $tree;
179 };
180
181 This is a C<before> modifier, just like we saw in the second recipe, but with
182 two slight differences. First, we are applying this to more than one method at a
183 time. Since both the C<left> and C<right> methods need the same feature, it
184 makes sense. The second difference is that we are not wrapping an inherited
185 method anymore, but instead a method of our own local class. Wrapping local
186 methods is no different, the only requirement is that the wrappee be created
187 before the wrapper (after all, you cannot wrap something which doesn't exist,
188 right?).
189
190 Now, as with all the other recipes, you can go about using
191 B<BinaryTree> like any other Perl 5 class. A more detailed example of its
192 usage can be found in F<t/000_recipes/003_recipe.t>.
193
194 =head1 CONCLUSION
195
196 This recipe introduced you to some of the more advanced behavioral
197 possibilities of Moose's attribute mechanism. I hope that it has
198 opened your mind to the powerful possibilities of Moose. In the next
199 recipe we explore how we can create custom subtypes and take
200 advantage of the plethora of useful modules out on CPAN with Moose.
201
202 =head1 FOOTNOTES
203
204 =over 4
205
206 =item (1)
207
208 Weak references are tricky things, and should be used sparingly
209 and appropriately (such as in the case of circular refs). If you
210 are not careful, you will have slot values disappear "mysteriously"
211 because perls reference counting garbage collector has gone and
212 removed the item you are weak-referencing.
213
214 In short, don't use them unless you know what you are doing :)
215
216 =item (2)
217
218 You I<can> use the C<default> option without the C<lazy> option if
219 you like, as we showed in the second recipe.
220
221 And actually, you can use C<builder> instead of C<default>. See
222 L<Moose::Cookbook::Recipe9> for details.
223
224 =back
225
226 =head1 AUTHOR
227
228 Stevan Little E<lt>stevan@iinteractive.comE<gt>
229
230 =head1 COPYRIGHT AND LICENSE
231
232 Copyright 2006-2008 by Infinity Interactive, Inc.
233
234 L<http://www.iinteractive.com>
235
236 This library is free software; you can redistribute it and/or modify
237 it under the same terms as Perl itself.
238
239 =cut
+0
-295
lib/Moose/Cookbook/Recipe4.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe4 - Subtypes, and modeling a simple B<Company> class hierarchy
6
7 =head1 SYNOPSIS
8
9 package Address;
10 use Moose;
11 use Moose::Util::TypeConstraints;
12
13 use Locale::US;
14 use Regexp::Common 'zip';
15
16 my $STATES = Locale::US->new;
17
18 subtype USState
19 => as Str
20 => where {
21 (exists $STATES->{code2state}{uc($_)} ||
22 exists $STATES->{state2code}{uc($_)})
23 };
24
25 subtype USZipCode
26 => as Value
27 => where {
28 /^$RE{zip}{US}{-extended => 'allow'}$/
29 };
30
31 has 'street' => (is => 'rw', isa => 'Str');
32 has 'city' => (is => 'rw', isa => 'Str');
33 has 'state' => (is => 'rw', isa => 'USState');
34 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
35
36 package Company;
37 use Moose;
38 use Moose::Util::TypeConstraints;
39
40 has 'name' => (is => 'rw', isa => 'Str', required => 1);
41 has 'address' => (is => 'rw', isa => 'Address');
42 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
43
44 sub BUILD {
45 my ($self, $params) = @_;
46 if ($params->{employees}) {
47 foreach my $employee (@{$params->{employees}}) {
48 $employee->company($self);
49 }
50 }
51 }
52
53 after 'employees' => sub {
54 my ($self, $employees) = @_;
55 if (defined $employees) {
56 foreach my $employee (@{$employees}) {
57 $employee->company($self);
58 }
59 }
60 };
61
62 package Person;
63 use Moose;
64
65 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
66 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
67 has 'middle_initial' => (is => 'rw', isa => 'Str',
68 predicate => 'has_middle_initial');
69 has 'address' => (is => 'rw', isa => 'Address');
70
71 sub full_name {
72 my $self = shift;
73 return $self->first_name .
74 ($self->has_middle_initial ?
75 ' ' . $self->middle_initial . '. '
76 :
77 ' ') .
78 $self->last_name;
79 }
80
81 package Employee;
82 use Moose;
83
84 extends 'Person';
85
86 has 'title' => (is => 'rw', isa => 'Str', required => 1);
87 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
88
89 override 'full_name' => sub {
90 my $self = shift;
91 super() . ', ' . $self->title
92 };
93
94 =head1 DESCRIPTION
95
96 In this recipe we introduce the C<subtype> keyword, and show
97 how it can be useful for specifying type constraints
98 without building an entire class to represent them. We
99 will also show how this feature can be used to leverage the
100 usefulness of CPAN modules. In addition to this, we will
101 introduce another attribute option.
102
103 Let's first look at the C<subtype> feature. In the B<Address> class we have
104 defined two subtypes. The first C<subtype> uses the L<Locale::US> module, which
105 provides two hashes which can be used to perform existential checks for state
106 names and their two letter state codes. It is a very simple and very useful
107 module, and perfect for use in a C<subtype> constraint.
108
109 my $STATES = Locale::US->new;
110 subtype USState
111 => as Str
112 => where {
113 (exists $STATES->{code2state}{uc($_)} ||
114 exists $STATES->{state2code}{uc($_)})
115 };
116
117 Because we know that states will be passed to us as strings, we
118 can make C<USState> a subtype of the built-in type constraint
119 C<Str>. This will ensure that anything which is a C<USState> will
120 also pass as a C<Str>. Next, we create a constraint specializer
121 using the C<where> keyword. The value being checked against in
122 the C<where> clause can be found in the C<$_> variable (1). Our
123 constraint specializer will then check whether the given string
124 is either a state name or a state code. If the string meets this
125 criteria, then the constraint will pass, otherwise it will fail.
126 We can now use this as we would any built-in constraint, like so:
127
128 has 'state' => (is => 'rw', isa => 'USState');
129
130 The C<state> accessor will now check all values against the
131 C<USState> constraint, thereby only allowing valid state names or
132 state codes to be stored in the C<state> slot.
133
134 The next C<subtype> does pretty much the same thing using the L<Regexp::Common>
135 module, and is used as the constraint for the C<zip_code> slot.
136
137 subtype USZipCode
138 => as Value
139 => where {
140 /^$RE{zip}{US}{-extended => 'allow'}$/
141 };
142
143 Using subtypes can save a lot of unnecessary abstraction by not requiring you to
144 create many small classes for these relatively simple values. They also allow
145 you to reuse the same constraints in a number of classes (thereby avoiding
146 duplication), since all type constraints are stored in a global registry and
147 always accessible to C<has>.
148
149 With these two subtypes and some attributes, we have defined
150 as much as we need for a basic B<Address> class. Next, we define
151 a basic B<Company> class, which itself has an address. As we saw in
152 earlier recipes, we can use the C<Address> type constraint that
153 Moose automatically created for us:
154
155 has 'address' => (is => 'rw', isa => 'Address');
156
157 A company also needs a name, so we define that as well:
158
159 has 'name' => (is => 'rw', isa => 'Str', required => 1);
160
161 Here we introduce another attribute option, the C<required> option.
162 This option tells Moose that C<name> is a required parameter in
163 the B<Company> constructor, and that the C<name> accessor cannot
164 accept an undefined value for the slot. The result is that C<name>
165 will always have a value.
166
167 The next attribute option is not actually new, but a new variant
168 of options we have already introduced:
169
170 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
171
172 Here, we are passing a more complex string to the C<isa> option, we
173 are passing a container type constraint. Container type constraints
174 can either be C<ArrayRef> or C<HashRef> with a contained type given
175 inside the square brackets. This basically checks that all the values
176 in the ARRAY ref are instances of the B<Employee> class.
177
178 This will ensure that our employees will all be of the correct type. However,
179 the B<Employee> object (which we will see in a moment) also maintains a
180 reference to its associated B<Company>. In order to maintain this relationship
181 (and preserve the referential integrity of our objects), we need to perform some
182 processing of the employees over and above that of the type constraint check.
183 This is accomplished in two places. First we need to be sure that any employees
184 array passed to the constructor is properly initialized. For this we can use the
185 C<BUILD> method (2):
186
187 sub BUILD {
188 my ($self, $params) = @_;
189 if ($params->{employees}) {
190 foreach my $employee (@{$params->{employees}}) {
191 $employee->company($self);
192 }
193 }
194 }
195
196 The C<BUILD> method will be executed after the initial type constraint
197 check, so we can simply perform a basic existential check on the C<employees>
198 param here, and assume that if it does exist, it is both an ARRAY ref
199 and contains I<only> instances of B<Employee>.
200
201 The next aspect we need to address is the C<employees> read/write
202 accessor (see the C<employees> attribute declaration above). This
203 accessor will correctly check the type constraint, but we need to extend it
204 with some additional processing. For this we use an C<after> method modifier,
205 like so:
206
207 after 'employees' => sub {
208 my ($self, $employees) = @_;
209 if (defined $employees) {
210 foreach my $employee (@{$employees}) {
211 $employee->company($self);
212 }
213 }
214 };
215
216 Again, as with the C<BUILD> method, we know that the type constraint
217 check has already happened, so we can just check for defined-ness on the
218 C<$employees> argument.
219
220 At this point, our B<Company> class is complete. Next comes our B<Person>
221 class and its subclass, the previously mentioned B<Employee> class.
222
223 The B<Person> class should be obvious to you at this point. It has a few
224 C<required> attributes, and the C<middle_initial> slot has an additional
225 C<predicate> method (which we saw in the previous recipe with the
226 B<BinaryTree> class).
227
228 Next, the B<Employee> class, which should also be pretty obvious at this
229 point. It requires a C<title>, and maintains a weakened reference to a
230 B<Company> instance. The only new item, which we have seen before in
231 examples, but never in the recipe itself, is the C<override> method
232 modifier:
233
234 override 'full_name' => sub {
235 my $self = shift;
236 super() . ', ' . $self->title
237 };
238
239 This just tells Moose that I am intentionally overriding the superclass
240 C<full_name> method here, and adding the value of the C<title> slot at
241 the end of the employee's full name.
242
243 And that's about it.
244
245 Once again, as with all the other recipes, you can go about using
246 these classes like any other Perl 5 class. A more detailed example of
247 usage can be found in F<t/000_recipes/004_recipe.t>.
248
249 =head1 CONCLUSION
250
251 This recipe was intentionally longer and more complex to illustrate both
252 how easily Moose classes can interact (using class type constraints, etc.)
253 and the sheer density of information and behaviors which Moose can pack
254 into a relatively small amount of typing. Ponder for a moment how much
255 more code a non-Moose plain old Perl 5 version of this recipe would have
256 been (including all the type constraint checks, weak references, and so on).
257
258 And of course, this recipe also introduced the C<subtype> keyword, and
259 its usefulness within the Moose toolkit. In the next recipe we will
260 focus more on subtypes, and introduce the idea of type coercion as well.
261
262 =head1 FOOTNOTES
263
264 =over 4
265
266 =item (1)
267
268 The value being checked is also passed as the first argument to
269 the C<where> block as well, so it can also be accessed as C<$_[0]>
270 as well.
271
272 =item (2)
273
274 The C<BUILD> method is called by C<Moose::Object::BUILDALL>, which is
275 called by C<Moose::Object::new>. C<BUILDALL> will climb the object
276 inheritance graph and call the appropriate C<BUILD> methods in the
277 correct order.
278
279 =back
280
281 =head1 AUTHOR
282
283 Stevan Little E<lt>stevan@iinteractive.comE<gt>
284
285 =head1 COPYRIGHT AND LICENSE
286
287 Copyright 2006-2008 by Infinity Interactive, Inc.
288
289 L<http://www.iinteractive.com>
290
291 This library is free software; you can redistribute it and/or modify
292 it under the same terms as Perl itself.
293
294 =cut
+0
-198
lib/Moose/Cookbook/Recipe5.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe5 - More subtypes, coercion in a B<Request> class
6
7 =head1 SYNOPSIS
8
9 package Request;
10 use Moose;
11 use Moose::Util::TypeConstraints;
12
13 use HTTP::Headers ();
14 use Params::Coerce ();
15 use URI ();
16
17 subtype 'Header'
18 => as 'Object'
19 => where { $_->isa('HTTP::Headers') };
20
21 coerce 'Header'
22 => from 'ArrayRef'
23 => via { HTTP::Headers->new( @{ $_ } ) }
24 => from 'HashRef'
25 => via { HTTP::Headers->new( %{ $_ } ) };
26
27 subtype 'Uri'
28 => as 'Object'
29 => where { $_->isa('URI') };
30
31 coerce 'Uri'
32 => from 'Object'
33 => via { $_->isa('URI')
34 ? $_
35 : Params::Coerce::coerce( 'URI', $_ ) }
36 => from 'Str'
37 => via { URI->new( $_, 'http' ) };
38
39 subtype 'Protocol'
40 => as Str
41 => where { /^HTTP\/[0-9]\.[0-9]$/ };
42
43 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
44 has 'uri' => (is => 'rw', isa => 'Uri', coerce => 1);
45 has 'method' => (is => 'rw', isa => 'Str');
46 has 'protocol' => (is => 'rw', isa => 'Protocol');
47 has 'headers' => (
48 is => 'rw',
49 isa => 'Header',
50 coerce => 1,
51 default => sub { HTTP::Headers->new }
52 );
53
54 =head1 DESCRIPTION
55
56 This recipe introduces the idea of type coercions, and the C<coerce>
57 keyword. Coercions can be attached to existing type constraints,
58 and can be used to transform input of one type into input of another
59 type. This can be an extremely powerful tool if used correctly, which
60 is why it is off by default. If you want your accessor to attempt
61 a coercion, you must specifically ask for it with the B<coerce> option.
62
63 Now, onto the coercions.
64
65 First we need to create a subtype to attach our coercion to. Here we
66 create a basic I<Header> subtype, which matches any instance of the
67 class B<HTTP::Headers>:
68
69 subtype 'Header'
70 => as 'Object'
71 => where { $_->isa('HTTP::Headers') };
72
73 The simplest thing from here would be create an accessor declaration
74 like this:
75
76 has 'headers' => (
77 is => 'rw',
78 isa => 'Header',
79 default => sub { HTTP::Headers->new }
80 );
81
82 We would then have a self-validating accessor whose default value is
83 an empty instance of B<HTTP::Headers>. This is nice, but it is not
84 ideal.
85
86 The constructor for B<HTTP::Headers> accepts a list of key-value pairs
87 representing the HTTP header fields. In Perl, such a list could
88 easily be stored in an ARRAY or HASH reference. We would like our
89 class's interface to be able to accept this list of key-value pairs
90 in place of the B<HTTP::Headers> instance, and just DWIM. This is where
91 coercion can help. First, let's declare our coercion:
92
93 coerce 'Header'
94 => from 'ArrayRef'
95 => via { HTTP::Headers->new( @{ $_ } ) }
96 => from 'HashRef'
97 => via { HTTP::Headers->new( %{ $_ } ) };
98
99 We first tell it that we are attaching the coercion to the 'Header'
100 subtype. We then give it a set of C<from> clauses which map other
101 subtypes to coercion routines (through the C<via> keyword). Fairly
102 simple really; however, this alone does nothing. We have to tell
103 our attribute declaration to actually use the coercion, like so:
104
105 has 'headers' => (
106 is => 'rw',
107 isa => 'Header',
108 coerce => 1,
109 default => sub { HTTP::Headers->new }
110 );
111
112 This will coerce any B<ArrayRef> or B<HashRef> which is passed into
113 the C<headers> accessor into an instance of B<HTTP::Headers>. So the
114 the following lines of code are all equivalent:
115
116 $foo->headers(HTTP::Headers->new(bar => 1, baz => 2));
117 $foo->headers([ 'bar', 1, 'baz', 2 ]);
118 $foo->headers({ bar => 1, baz => 2 });
119
120 As you can see, careful use of coercions can produce a very open
121 interface for your class, while still retaining the "safety" of
122 your type constraint checks.
123
124 Our next coercion takes advantage of the power of CPAN to handle
125 the details of our coercion. In this particular case it uses the
126 L<Params::Coerce> module, which fits in rather nicely with L<Moose>.
127
128 Again, we create a simple subtype to represent instances of the
129 B<URI> class:
130
131 subtype 'Uri'
132 => as 'Object'
133 => where { $_->isa('URI') };
134
135 Then we add the coercion:
136
137 coerce 'Uri'
138 => from 'Object'
139 => via { $_->isa('URI')
140 ? $_
141 : Params::Coerce::coerce( 'URI', $_ ) }
142 => from 'Str'
143 => via { URI->new( $_, 'http' ) };
144
145 The first C<from> clause we introduce is for the 'Object' subtype. An 'Object'
146 is simply any C<bless>ed value. This means that if the coercion encounters
147 another object, it should use this clause. Now we look at the C<via> block.
148 First it checks to see if the object is a B<URI> instance. Since the coercion
149 process occurs prior to any type constraint checking, it is entirely possible
150 for this to happen, and if it does happen, we simply want to pass the instance
151 on through. However, if it is not an instance of B<URI>, then we need to coerce
152 it. This is where L<Params::Coerce> can do its magic, and we can just use its
153 return value. Simple really, and much less work since we used a module from CPAN
154 :)
155
156 The second C<from> clause is attached to the 'Str' subtype, and
157 illustrates how coercions can also be used to handle certain
158 'default' behaviors. In this coercion, we simple take any string
159 and pass it to the B<URI> constructor along with the default
160 'http' scheme type.
161
162 And of course, our coercions do nothing unless they are told to,
163 like so:
164
165 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
166 has 'uri' => (is => 'rw', isa => 'Uri', coerce => 1);
167
168 As you can see, re-using the coercion allows us to enforce a
169 consistent and very flexible API across multiple accessors.
170
171 =head1 CONCLUSION
172
173 This recipe illustrated the power of coercions to build a more
174 flexible and open API for your accessors, while still retaining
175 all the safety that comes from using Moose's type constraints.
176 Using coercions it becomes simple to manage (from a single
177 location) a consistent API not only across multiple accessors,
178 but across multiple classes as well.
179
180 In the next recipe, we will introduce roles, a concept originally
181 borrowed from Smalltalk, which made it's way into Perl 6, and
182 now into Moose.
183
184 =head1 AUTHOR
185
186 Stevan Little E<lt>stevan@iinteractive.comE<gt>
187
188 =head1 COPYRIGHT AND LICENSE
189
190 Copyright 2006-2008 by Infinity Interactive, Inc.
191
192 L<http://www.iinteractive.com>
193
194 This library is free software; you can redistribute it and/or modify
195 it under the same terms as Perl itself.
196
197 =cut
+0
-86
lib/Moose/Cookbook/Recipe6.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe6 - The augment/inner example
6
7 =head1 SYNOPSIS
8
9 package Document::Page;
10 use Moose;
11
12 has 'body' => (is => 'rw', isa => 'Str', default => sub {''});
13
14 sub create {
15 my $self = shift;
16 $self->open_page;
17 inner();
18 $self->close_page;
19 }
20
21 sub append_body {
22 my ($self, $appendage) = @_;
23 $self->body($self->body . $appendage);
24 }
25
26 sub open_page { (shift)->append_body('<page>') }
27 sub close_page { (shift)->append_body('</page>') }
28
29 package Document::PageWithHeadersAndFooters;
30 use Moose;
31
32 extends 'Document::Page';
33
34 augment 'create' => sub {
35 my $self = shift;
36 $self->create_header;
37 inner();
38 $self->create_footer;
39 };
40
41 sub create_header { (shift)->append_body('<header/>') }
42 sub create_footer { (shift)->append_body('<footer/>') }
43
44 package TPSReport;
45 use Moose;
46
47 extends 'Document::PageWithHeadersAndFooters';
48
49 augment 'create' => sub {
50 my $self = shift;
51 $self->create_tps_report;
52 };
53
54 sub create_tps_report {
55 (shift)->append_body('<report type="tps"/>')
56 }
57
58 print TPSReport->new->create # <page><header/><report type="tps"/><footer/></page>
59
60 =head1 DESCRIPTION
61
62 Coming Soon.
63
64 =head1 CONCLUSION
65
66 =head1 FOOTNOTES
67
68 =over 4
69
70 =back
71
72 =head1 AUTHOR
73
74 Stevan Little E<lt>stevan@iinteractive.comE<gt>
75
76 =head1 COPYRIGHT AND LICENSE
77
78 Copyright 2007 by Infinity Interactive, Inc.
79
80 L<http://www.iinteractive.com>
81
82 This library is free software; you can redistribute it and/or modify
83 it under the same terms as Perl itself.
84
85 =cut
+0
-61
lib/Moose/Cookbook/Recipe7.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe7 - Making Moose fast with immutable
6
7 =head1 SYNOPSIS
8
9 package Point;
10 use Moose;
11
12 has 'x' => (isa => 'Int', is => 'ro');
13 has 'y' => (isa => 'Int', is => 'rw');
14
15 __PACKAGE__->meta->make_immutable;
16
17 =head1 DESCRIPTION
18
19 The Moose metaclass API provides a method C<make_immutable()>. At a
20 high level, this calling this method does two things to your
21 class. One, it makes it faster. In particular, object construction and
22 accessors are effectively "inlined" in your class, and no longer go
23 through the meta-object system.
24
25 Second, you can no longer make changes via the metaclass API such as
26 adding attributes. In practice, this won't be a problem, as you don't
27 usually need to do this at runtime after first loading the class.
28
29 =head2 Immutabilization and C<new()>
30
31 If you override C<new()> in your class, then the immutabilization code
32 will not be able to provide an optimized constructor for your
33 class. Instead, consider providing a C<BUILD()> method. You can
34 probably do the same thing in a C<BUILD()> method.
35
36 Alternately, if you really need to provide a different C<new()>, you
37 can also provide your own immutabilization method.
38
39 Discussing this is beyond the scope of this recipe, however.
40
41 =head1 CONCLUSION
42
43 We strongly recommend you make your classes immutable. It makes your
44 code much faster, basically for free. This will be especially
45 noticeable when creating many objects or calling accessors frequently.
46
47 =head1 AUTHOR
48
49 Dave Rolsky E<lt>autarch@urth.orgE<gt>
50
51 =head1 COPYRIGHT AND LICENSE
52
53 Copyright 2006-2008 by Infinity Interactive, Inc.
54
55 L<http://www.iinteractive.com>
56
57 This library is free software; you can redistribute it and/or modify
58 it under the same terms as Perl itself.
59
60 =cut
+0
-210
lib/Moose/Cookbook/Recipe9.pod less more
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Recipe9 - Builder methods and lazy_build
6
7 =head1 SYNOPSIS
8
9 package BinaryTree;
10 use Moose;
11
12 has 'node' => (is => 'rw', isa => 'Any');
13
14 has 'parent' => (
15 is => 'rw',
16 isa => 'BinaryTree',
17 predicate => 'has_parent',
18 weak_ref => 1,
19 );
20
21 has 'left' => (
22 is => 'rw',
23 isa => 'BinaryTree',
24 predicate => 'has_left',
25 lazy => 1,
26 builder => '_build_child_tree',
27 );
28
29 has 'right' => (
30 is => 'rw',
31 isa => 'BinaryTree',
32 predicate => 'has_right',
33 lazy => 1,
34 builder => '_build_child_tree',
35 );
36
37 before 'right', 'left' => sub {
38 my ($self, $tree) = @_;
39 $tree->parent($self) if defined $tree;
40 };
41
42 sub _build_child_tree {
43 my $self = shift;
44
45 return BinaryTree->new( parent => $self );
46 }
47
48 =head1 DESCRIPTION
49
50 If you've already read L<Moose::Cookbook::Recipe3>, then this example
51 should look awfully familiar. In fact, all we've done here is replace
52 the attribute C<default> with a C<builder> method.
53
54 In this particular case, the C<default> and C<builder> options act in
55 exactly the same way. When the C<left> or C<right> attribute get
56 method is called, Moose will call the builder method to initialize the
57 attribute.
58
59 Note that Moose calls the builder method I<on the object which has the
60 attribute>. Here's an example in code:
61
62 my $tree = BinaryTree->new();
63
64 my $left = $tree->left();
65
66 At this point, Moose will call C<< $tree->_build_child_tree() >> in
67 order to populate the C<left> attribute. If we had passed C<left> to
68 the original constructor, the builer would not be called.
69
70 =head2 Subclassable
71
72 There are some differences between C<default> and C<builder>. Because
73 C<builder> is called I<by name>, it goes through Perl's normal
74 inheritance system. This means that builder methods are both
75 inheritable and overrideable.
76
77 For example, we might make a C<BinaryTree> subclass:
78
79 package TrinaryTree;
80 use Moose;
81
82 extends 'BinaryTree';
83
84 has 'middle' => (
85 is => 'rw',
86 isa => 'BinaryTree',
87 predicate => 'has_middle',
88 lazy => 1,
89 builder => '_build_child_tree',
90 );
91
92 This doesn't quite work though. If you look closely at the
93 C<_build_child_tree> method defined in C<BinaryTree>, you'll notice
94 that it hard-codes a class name. Naughty us!
95
96 Also, as a bonus, we'll pass C<@_> through, so subclasses can override
97 the method to pass additional options to the constructor.
98
99 Good object-oriented code should allow itself to be subclassed
100 gracefully. Let's tweak C<_build_child_tree>:
101
102 sub _build_child_tree {
103 my $self = shift;
104
105 return (ref $self)->new( parent => $self, @_ );
106 }
107
108 Now C<_build_child_tree> can be gracefully inherited and overridden.
109
110 =head2 Composable
111
112 There's more to builders than just subclassing, though. The fact that
113 builders are called by name also makes them suitable for use in a
114 role.
115
116 package HasAnimal;
117 use Moose::Role;
118
119 requires '_build_animal';
120
121 has 'animal' => (
122 is => 'ro',
123 isa => 'Animal',
124 lazy => 1,
125 builder => '_build_animal',
126 );
127
128 This role provides an animal attribute, but requires that the consumer
129 of the role provide a builder method it.
130
131 package CatLover;
132 use Moose;
133
134 with 'HasAnimal';
135
136 sub _build_animal {
137 return Cat->new();
138 }
139
140 =head2 The lazy_build shortcut
141
142 The C<lazy_build> attribute parameter can be used as sugar to specify
143 a whole bunch of options at once.
144
145 has 'animal' => (
146 is => 'ro',
147 isa => 'Animal',
148 lazy_build => 1,
149 );
150
151 This is a shorthand for this:
152
153 has 'animal' => (
154 is => 'ro',
155 isa => 'Animal',
156 required => 1,
157 lazy => 1,
158 builder => '_build_animal',
159 predicate => 'has_animal',
160 clearer => 'clear_animal',
161 );
162
163 If your attribute starts with an underscore, Moose is smart and will
164 do the right thing with the C<predicate> and C<clearer>, making them
165 both start with an underscore. The C<builder> method I<always> starts
166 with an underscore, since you will want this to be private the vast
167 majority of the time.
168
169 Note that the C<builder> method name is created by simply taking
170 "_build_" and appending the attribute name. This means that attributes
171 with a leading underscore like C<_animal> end up with a builder named
172 C<_build__animal>.
173
174 =head1 CONCLUSION
175
176 The C<builder> option is a more OO-friendly version of the C<default>
177 functionality. It also has the property of separating out the code
178 into a separate well-defined method. This alone makes it valuable. It
179 is quite ugly to jam a long default code reference into your attribute
180 definition.
181
182 Here are some good rules for determining when to use C<builder> vs
183 C<default>.
184
185 If the default value is a simple scalar that only needs to be
186 calculated once (or a constant), use C<default>.
187
188 If the default value is an empty reference that needs to be wrapped in
189 a coderef like C<sub { [] }>, use C<default>.
190
191 Otherwise, use C<builder>.
192
193 This ensures that your classes are easily subclassable, and also helps
194 keep crufty code out of your attribute definition blocks.
195
196 =head1 AUTHOR
197
198 Dave Rolsky E<lt>autarch@urth.orgE<gt>
199
200 =head1 COPYRIGHT AND LICENSE
201
202 Copyright 2006-2008 by Infinity Interactive, Inc.
203
204 L<http://www.iinteractive.com>
205
206 This library is free software; you can redistribute it and/or modify
207 it under the same terms as Perl itself.
208
209 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Roles::Recipe1 - The Moose::Role example
6
7 =head1 SYNOPSIS
8
9 package Eq;
10 use Moose::Role;
11
12 requires 'equal_to';
13
14 sub not_equal_to {
15 my ($self, $other) = @_;
16 not $self->equal_to($other);
17 }
18
19 package Comparable;
20 use Moose::Role;
21
22 with 'Eq';
23
24 requires 'compare';
25
26 sub equal_to {
27 my ($self, $other) = @_;
28 $self->compare($other) == 0;
29 }
30
31 sub greater_than {
32 my ($self, $other) = @_;
33 $self->compare($other) == 1;
34 }
35
36 sub less_than {
37 my ($self, $other) = @_;
38 $self->compare($other) == -1;
39 }
40
41 sub greater_than_or_equal_to {
42 my ($self, $other) = @_;
43 $self->greater_than($other) || $self->equal_to($other);
44 }
45
46 sub less_than_or_equal_to {
47 my ($self, $other) = @_;
48 $self->less_than($other) || $self->equal_to($other);
49 }
50
51 package Printable;
52 use Moose::Role;
53
54 requires 'to_string';
55
56 package US::Currency;
57 use Moose;
58
59 with 'Comparable', 'Printable';
60
61 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
62
63 sub compare {
64 my ($self, $other) = @_;
65 $self->amount <=> $other->amount;
66 }
67
68 sub to_string {
69 my $self = shift;
70 sprintf '$%0.2f USD' => $self->amount
71 }
72
73 =head1 DESCRIPTION
74
75 In this recipe we examine the role support provided in Moose. "Roles" may be
76 described in many ways, but there are two main ways in which they are used: as
77 interfaces, and as a means of code reuse. This recipe demonstrates the
78 construction and incorporation of roles that define comparison and display of
79 objects.
80
81 Let's start by examining B<Eq>. You'll notice that instead of the familiar C<use
82 Moose> you might be expecting, here we use C<Moose::Role> to make it clear that
83 this is a role. We encounter a new keyword, C<requires>:
84
85 requires 'equal_to';
86
87 What this does is to indicate that any class which "consumes" (that is to say,
88 "includes using C<with>", as we'll see a little later) the B<Eq> role I<must>
89 include an C<equal_to> method, whether this is provided by the class itself, one
90 of its superclasses, or another role consumed by the class (1).
91
92 In addition to requiring an C<equal_to> method, B<Eq> defines a C<not_equal_to>
93 method, which simply inverts the result of C<equal_to>. Defining additional
94 methods in this way, by using only a few base methods that target classes must
95 define, is a useful pattern to provide maximum functionality with minimum
96 effort.
97
98 After the minimal B<Eq>, we next move on to B<Comparable>. The first thing you
99 will notice is another new keyword, C<with>:
100
101 with 'Eq';
102
103 C<with> is used to provide a list of roles which this class (or role) consumes.
104 Here, B<Comparable> only consumes one role (B<Eq>). In effect, it is as if we
105 defined a C<not_equal_to> method within Comparable, and also promised to fulfill
106 the requirement of an C<equal_to> method.
107
108 B<Comparable> itself states that it requires C<compare>. Again, it means that
109 any classes consuming this role must implement a C<compare> method.
110
111 requires 'compare';
112
113 B<Comparable> defines an C<equal_to> method which satisfies the B<Eq> role's
114 requirements. This, along with a number of other methods (C<greater_than>,
115 C<less_than>, C<greater_than_or_equal_to>, and C<less_than_or_equal_to>) is
116 simply defined in terms of C<compare>, once again demonstrating the pattern of
117 defining a number of utility methods in terms of only a single method that the
118 target class need implement.
119
120 sub equal_to {
121 my ($self, $other) = @_;
122 $self->compare($other) == 0;
123 }
124
125 sub greater_than {
126 my ($self, $other) = @_;
127 $self->compare($other) == 1;
128 }
129
130 sub less_than {
131 my ($self, $other) = @_;
132 $self->compare($other) == -1;
133 }
134
135 sub greater_than_or_equal_to {
136 my ($self, $other) = @_;
137 $self->greater_than($other) || $self->equal_to($other);
138 }
139
140 sub less_than_or_equal_to {
141 my ($self, $other) = @_;
142 $self->less_than($other) || $self->equal_to($other);
143 }
144
145 Next up is B<Printable>. This is a very simple role, akin to B<Eq>. It merely
146 requires a C<to_string> method.
147
148 Finally, we come to B<US::Currency>, a class that allows us to reap the benefits
149 of our hard work. This is a regular Moose class, so we include the normal C<use
150 Moose>. It consumes both B<Comparable> and B<Printable>, as the following line
151 shows:
152
153 with 'Comparable', 'Printable';
154
155 It also defines a regular Moose attribute, C<amount>, with a type constraint of
156 C<Num> and a default of C<0>:
157
158 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
159
160 Now we come to the core of the class. First up, we define a C<compare> method:
161
162 sub compare {
163 my ($self, $other) = @_;
164 $self->amount <=> $other->amount;
165 }
166
167 As you can see, it simply compares the C<amount> attribute of this object with
168 the C<amount> attribute of the other object passed to it. With the single
169 definition of this method, we gain the following methods for free: C<equal_to>,
170 C<greater_than>, C<less_than>, C<greater_than_or_equal_to> and
171 C<less_than_or_equal_to>.
172
173 We end the class with a definition of the C<to_string> method, which formats the
174 C<amount> attribute for display:
175
176 sub to_string {
177 my $self = shift;
178 sprintf '$%0.2f USD' => $self->amount
179 }
180
181 =head1 CONCLUSION
182
183 This recipe has shown that roles can be very powerful and immensely useful, and
184 save a great deal of repetition.
185
186 =head1 FOOTNOTES
187
188 =over 4
189
190 =item (1)
191
192 At present, method requirements from roles cannot be satisfied by attribute
193 accessors. This is a limitation of Moose, and will most likely be rectified in a
194 future release.
195
196 =back
197
198 =head1 AUTHOR
199
200 Stevan Little E<lt>stevan@iinteractive.comE<gt>
201
202 =head1 COPYRIGHT AND LICENSE
203
204 Copyright 2006-2008 by Infinity Interactive, Inc.
205
206 L<http://www.iinteractive.com>
207
208 This library is free software; you can redistribute it and/or modify
209 it under the same terms as Perl itself.
210
211 =cut
0
1 =pod
2
3 =head1 NAME
4
5 Moose::Cookbook::Roles::Recipe2 - Advanced Role Composition - method exclusion and aliasing
6
7 =head1 SYNOPSIS
8
9 package Restartable;
10 use Moose::Role;
11
12 has 'is_paused' => (
13 is => 'rw',
14 isa => 'Bool',
15 default => 0,
16 );
17
18 requires 'save_state', 'load_state';
19
20 sub stop { ... }
21
22 sub start { ... }
23
24 package Restartable::ButUnreliable;
25 use Moose::Role;
26
27 with 'Restartable' => { alias => { stop => '_stop',
28 start => '_start' } };
29
30 sub stop {
31 my $self = shift;
32
33 $self->explode() if rand(1) > .5;
34
35 $self->_stop();
36 }
37
38 sub start {
39 my $self = shift;
40
41 $self->explode() if rand(1) > .5;
42
43 $self->_start();
44 }
45
46 package Restartable::ButBroken;
47 use Moose::Role;
48
49 with 'Restartable' => { excludes => [ 'stop', 'start' ] };
50
51 sub stop {
52 my $self = shift;
53
54 $self->explode();
55 }
56
57 sub start {
58 my $self = shift;
59
60 $self->explode();
61 }
62
63 =head1 DESCRIPTION
64
65 Sometimes when you include a role in a class, you may want to leave
66 out some of its methods. In this example, we have a role C<Restartable>
67 which provides an C<is_paused> attribute, and two methods, C<stop> and
68 C<start>. The implementation of those two methods is irrelevant.
69
70 Then we have two more roles which also implement the same interface,
71 each putting their own spin on the C<stop> and C<start> method.
72
73 In the C<Restartable::ButUnreliable> role, we want to provide a new
74 implementation of C<stop> and C<start>, but still have access to the
75 original implementation. To do this, we alias the methods from
76 C<Restartable> to private methods, and provide wrappers around the
77 originals (1).
78
79 In the C<Restartable::ButBroken> role, we want to provide an entirely
80 new behavior for C<stop> and C<start>, so we exclude them when
81 composing the C<Restartable> role into C<Restartable::ButBroken>.
82
83 It's worth noting that the C<excludes> parameter also accepts a single
84 string as an argument if you just want to exclude one method.
85
86 =head1 CONCLUSION
87
88 Method exclusion and renaming can come in handy, especially when
89 building roles out of other roles. In this example, all of our roles
90 implement the C<Restartable> role. Each role provides same API, but
91 each has a different implementation under the hood.
92
93 You can also use the method aliasing and excluding features when
94 composing a role into a class.
95
96 =head1 FOOTNOTES
97
98 =over 4
99
100 =item (1)
101
102 The mention of wrapper should tell you that we could do the same thing
103 using method modifiers, but for the sake of this example, we don't.
104
105 =back
106
107 =head1 AUTHOR
108
109 Dave Rolsky E<lt>autarch@urth.orgE<gt>
110
111 =head1 COPYRIGHT AND LICENSE
112
113 Copyright 2006-2008 by Infinity Interactive, Inc.
114
115 L<http://www.iinteractive.com>
116
117 This library is free software; you can redistribute it and/or modify
118 it under the same terms as Perl itself.
119
120 =cut
4141 =item inner
4242
4343 =item augment
44
45 =item make_immutable
4644
4745 =item confess
4846
3535
3636 =head1 DESCRIPTION
3737
38 This is the Point example from (L<Moose::Cookbook::Recipe1>) with added
38 This is the Point example from (L<Moose::Cookbook::Basics::Recipe1>) with added
3939 type checking.
4040
4141 If we try to assign a string value to an attribute that is defined as
5050
5151 =over 4
5252
53 =item L<Moose::Cookbook::Recipe1>
53 =item L<Moose::Cookbook::Basics::Recipe1>
5454
5555 =item L<Moose::Utils::TypeConstraints>
5656
7171 This library is free software; you can redistribute it and/or modify
7272 it under the same terms as Perl itself.
7373
74 =cut
74 =cut
55
66 =for authors
77
8 Please annotate all bad examples with comments so that they won't be copied by accodent
8 Please annotate all bad examples with comments so that they won't be copied by
9 accident
910
1011 =cut
1112
9293
9394 =head2 Use C<BUILDARGS> to alter C<@_> processing
9495
95 If you need to change the way L<@_> is processed, use C<BUILDARGS>, instead of
96 wrapping C<new>. This ensures the behavior is subclassible, it keeps this logic
96 If you need to change the way C<@_> is processed, for example for
97 C<< Class->new( $single_param ) >>, use C<BUILDARGS> instead of wrapping
98 C<new>. This ensures the behavior is subclassible, it keeps this logic
9799 independent of the other aspects of construction, and can be made efficient
98100 using C<make_immutable>.
99101
158160 coerce => 1,
159161 );
160162
161 in a specific way.
163 when the actual coercion applies only to your specific cases.
162164
163165 =head1 Clean up your package
164166
195197
196198 L<http://www.iinteractive.com>
197199
198 This library is free software; you can redistribute it and/or modify
199 it under the same terms as Perl itself.
200 This library is free software; you can redistribute it and/or modify it under
201 the same terms as Perl itself.
200202
201203 =cut
1818
1919 =head2 Basic Moose
2020
21 =over 4
22
23 =item L<Moose::Cookbook::Recipe1> - The (always classic) B<Point> example
21 These recipes will give you a good idea of what Moose is capable,
22 starting with simple attribute declaration, and moving on to more
23 powerful features like laziness, types, type coercion, method
24 modifiers, and more.
25
26 =over 4
27
28 =item L<Moose::Cookbook::Basics::Recipe1> - The (always classic) B<Point> example
2429
2530 A simple Moose-based class. Demonstrated Moose attributes and subclassing.
2631
27 =item L<Moose::Cookbook::Recipe2> - A simple B<BankAccount> example
32 =item L<Moose::Cookbook::Basics::Recipe2> - A simple B<BankAccount> example
2833
2934 A slightly more complex Moose class. Demonstrates using a method
3035 modifier in a subclass.
3136
32 =item L<Moose::Cookbook::Recipe3> - A lazy B<BinaryTree> example
37 =item L<Moose::Cookbook::Basics::Recipe3> - A lazy B<BinaryTree> example
3338
3439 Demonstrates several attribute features, including types, weak
3540 references, predicates ("does this object have a foo?"), defaults, and
36 lazy attribute construction.
37
38 =item L<Moose::Cookbook::Recipe4> - Subtypes, and modeling a simple B<Company> class hierarchy
41 lazy attribute uction.
42
43 =item L<Moose::Cookbook::Basics::Recipe4> - Subtypes, and modeling a simple B<Company> class hierarchy
3944
4045 Introduces the creation and use of custom types, a C<BUILD> method,
4146 and the use of C<override> in a subclass.
4247
43 =item L<Moose::Cookbook::Recipe5> - More subtypes, coercion in a B<Request> class
48 =item L<Moose::Cookbook::Basics::Recipe5> - More subtypes, coercion in a B<Request> class
4449
4550 More type examples, including the use of type coercions.
4651
47 =item L<Moose::Cookbook::Recipe6> - The augment/inner example
52 =item L<Moose::Cookbook::Basics::Recipe6> - The augment/inner example
4853
4954 Demonstrates the use of C<augment> method modifiers, a way of turning
5055 the usual method overriding style "inside-out".
5156
52 =item L<Moose::Cookbook::Recipe7> - Making Moose fast with immutable
57 =item L<Moose::Cookbook::Basics::Recipe7> - Making Moose fast with immutable
5358
5459 Making a class immutable greatly increases the speed of accessors and
5560 object construction.
5661
57 =item L<Moose::Cookbook::Recipe8> - Managing complex relations with trigger (TODO)
62 =item L<Moose::Cookbook::Basics::Recipe8> - Managing complex relations with trigger (TODO)
5863
5964 I<abstract goes here>
6065
6166 Work off of this http://code2.0beta.co.uk/moose/svn/Moose/trunk/t/200_examples/007_Child_Parent_attr_inherit.t
6267
63 =item L<Moose::Cookbook::Recipe9> - Builder methods and lazy_build
68 =item L<Moose::Cookbook::Basics::Recipe9> - Builder methods and lazy_build
6469
6570 The builder feature provides an inheritable and role-composable way to
6671 provide a default attribute value.
6772
73 =item L<Moose::Cookbook::Basics::Recipe10> - Operator overloading, subtypes, and coercion
74
75 Demonstrates using operator overloading, coercion, and subtypes to
76 model how eye color is determined during reproduction.
77
6878 =back
6979
7080 =head2 Moose Roles
7181
72 =over 4
73
74 =item L<Moose::Cookbook::Recipe10> - The Moose::Role example
82 These recipes will show you how to use Moose roles.
83
84 =over 4
85
86 =item L<Moose::Cookbook::Role::Recipe1> - The Moose::Role example
7587
7688 Demonstrates roles, which are also sometimes known as traits or
7789 mix-ins. Roles provide a method of code re-use which is orthogonal to
7890 subclassing.
7991
80 =item L<Moose::Cookbook::Recipe11> - Advanced Role Composition - method exclusion and aliasing
92 =item L<Moose::Cookbook::Role::Recipe2> - Advanced Role Composition - method exclusion and aliasing
8193
8294 Sometimes you just want to include part of a role in your
8395 class. Sometimes you want the whole role but one if its methods
8496 conflicts with one in your class. With method exclusion and aliasing,
8597 you can work around these problems.
8698
87 =item L<Moose::Cookbook::Recipe12> - Runtime Role Composition (TODO)
99 =item L<Moose::Cookbook::Role::Recipe3> - Runtime Role Composition (TODO)
88100
89101 I<abstract goes here>
90102
92104
93105 =head2 Meta Moose
94106
95 =over 4
96
97 =item L<Moose::Cookbook::Recipe20> - Welcome to the meta-world (TODO)
98
99 I<abstract goes here>
100
101 =item L<Moose::Cookbook::Recipe21> - The meta-attribute example
107 These recipes show you how to write your own meta classes, which lets
108 you extend the object system provide by Moose.
109
110 =over 4
111
112 =item L<Moose::Cookbook::Meta::Recipe1> - Welcome to the meta-world (Why Go Meta?)
113
114 If you're wondering what all this "meta" stuff is, and why you should
115 care about it, read this "recipe".
116
117 =item L<Moose::Cookbook::Meta::Recipe2> - A meta-attribute, attributes with labels
102118
103119 One way to extend Moose is to provide your own attribute
104120 metaclasses. Attribute metaclasses let you extend attribute
105121 declarations (with C<has>) and behavior to provide additional
106122 attribute functionality.
107123
108 =item L<Moose::Cookbook::Recipe22> - The meta-attribute trait example
124 =item L<Moose::Cookbook::Meta::Recipe3> - Labels implemented via attribute traits
109125
110126 Extending Moose's attribute metaclass is a great way to add
111127 functionality. However, attributes can only have one metaclass.
112128 Applying roles to the attribute metaclass lets you provide
113129 composable attribute functionality.
114130
115 =item L<Moose::Cookbook::Recipe23> - The meta-instance example (TODO)
116
117 I<abstract goes here>
118
119 =item L<Moose::Cookbook::Recipe24> - The meta-class example (TODO)
120
121 I<abstract goes here>
131 =item L<Moose::Cookbook::Meta::Recipe4> - Adding a "table" attribute to the metaclass
132
133 If you want to store more information about your classes, you'll have
134 to extend C<Moose::Meta::Class>. Doing so is simple, but you'll
135 probably also want to provide some sugar, so see
136 L<Moose::Cookbook::Meta::Recipe6> as well.
137
138 =item L<Moose::Cookbook::Meta::Recipe5> - The "table" attribute implemented via a metaclass trait
139
140 I<abstract goes here>
141
142 =item L<Moose::Cookbook::Meta::Recipe6> - Hooking into the immutabilization system (TODO)
143
144 Moose has a feature known as "immutabilization". By calling C<<
145 __PACKAGE__->meta()->make_immutable() >> after defining your class
146 (attributes, roles, etc), you tell Moose to optimize things like
147 object creation, attribute access, and so on.
148
149 If you are creating your own metaclasses, you may need to hook into
150 the immutabilization system. This cuts across a number of spots,
151 including the metaclass class, meta method classes, and possibly the
152 meta-instance class as well.
153
154 This recipe shows you how to write extensions which immutabilize
155 properly.
156
157 =item L<Moose::Cookbook::Meta::Recipe7> - I<meta-instance> (TODO)
158
159 I<abstract goes here>
160
161 =back
162
163 =head2 Extending Moose
164
165 These recipes cover some more ways to extend Moose, and will be useful
166 if you plan to write your own C<MooseX> module.
167
168 =over 4
169
170 =item L<Moose::Cookbook::Extending::Recipe1> - Providing an alternate base object class
171
172 You may find that you want to provide an alternate base object class
173 along with a meta extension, or maybe you just want to add some
174 functionality to all your classes without typing C<extends
175 'MyApp::Base'> over and over.
176
177 =item L<Moose::Cookbook::Extending::Recipe2> - Acting like Moose.pm and providing sugar Moose-style
178
179 This recipe shows how to provide a replacement for C<Moose.pm>. This
180 is something that you may want to do as part of a C<MooseX> module,
181 especially if you want to default to a new metaclass class or base
182 object class.
122183
123184 =back
124185
125186 =head1 SNACKS
126187
127188 =over 4
189
190 =item L<Moose::Cookbook::Snack::Keywords>
128191
129192 =item L<Moose::Cookbook::Snack::Types>
130193
77 use Carp 'confess';
88 use overload ();
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 use Moose::Meta::Method::Accessor;
554554 # this will sort out any details and always
555555 # return an hash of methods which we want
556556 # to delagate to, see that method for details
557 my %handles = $self->_canonicalize_handles();
557 my %handles = $self->_canonicalize_handles;
558558
559559 # find the accessor method for this attribute
560 my $accessor = $self->get_read_method_ref;
561 # then unpack it if we need too ...
562 $accessor = $accessor->body if blessed $accessor;
560 my $accessor = $self->_get_delegate_accessor;
563561
564562 # install the delegation ...
565563 my $associated_class = $self->associated_class;
608606
609607 # private methods to help delegation ...
610608
609 sub _get_delegate_accessor {
610 my $self = shift;
611 # find the accessor method for this attribute
612 my $accessor = $self->get_read_method_ref;
613 # then unpack it if we need too ...
614 $accessor = $accessor->body if blessed $accessor;
615 # return the accessor
616 return $accessor;
617 }
618
611619 sub _canonicalize_handles {
612620 my $self = shift;
613621 my $handles = $self->handles;
752760 To check a value against a type constraint before setting it, fetch the
753761 attribute instance using L<Class::MOP::Class/find_attribute_by_name>,
754762 fetch the type_constraint from the attribute using L<Moose::Meta::Attribute/type_constraint>
755 and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::RecipeX>
763 and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::Basics::Recipe4>
756764 for an example.
757765
758766 =back
824832 #If your attribute name starts with an underscore:
825833 has '_foo' => (lazy_build => 1);
826834 #is the same as
827 has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', builder => '_build__foo);
835 has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', builder => '_build__foo');
828836 # or
829837 has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', default => sub{shift->_build__foo});
830838
831839 #If your attribute name does not start with an underscore:
832840 has 'foo' => (lazy_build => 1);
833841 #is the same as
834 has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', builder => '_build_foo);
842 has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', builder => '_build_foo');
835843 # or
836844 has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', default => sub{shift->_build_foo});
837845
88 use Carp 'confess';
99 use Scalar::Util 'weaken', 'blessed';
1010
11 our $VERSION = '0.54';
11 our $VERSION = '0.55';
1212 our $AUTHORITY = 'cpan:STEVAN';
1313
1414 use Moose::Meta::Method::Overriden;
281281 foreach my $super (@superclasses) {
282282 # don't bother if it does not have a meta.
283283 next unless $super->can('meta');
284 next unless $super->meta->isa("Class::MOP::Class");
284285 # get the name, make sure we take
285286 # immutable classes into account
286287 my $super_meta_name = ($super->meta->is_immutable
33 use strict;
44 use warnings;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 use base "Class::MOP::Instance";
55
66 use Carp 'confess';
77
8 our $VERSION = '0.54';
8 our $VERSION = '0.55';
99 our $AUTHORITY = 'cpan:STEVAN';
1010
1111 use base 'Moose::Meta::Method',
44
55 use Carp 'confess';
66
7 our $VERSION = '0.54';
7 our $VERSION = '0.55';
88 our $AUTHORITY = 'cpan:STEVAN';
99
1010 use base 'Moose::Meta::Method';
66 use Carp 'confess';
77 use Scalar::Util 'blessed', 'weaken', 'looks_like_number';
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::Method',
66 use Carp 'confess';
77 use Scalar::Util 'blessed', 'weaken';
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::Method',
44
55 use Carp 'confess';
66
7 our $VERSION = '0.54';
7 our $VERSION = '0.55';
88 our $AUTHORITY = 'cpan:STEVAN';
99
1010 use base 'Moose::Meta::Method';
22 use strict;
33 use warnings;
44
5 our $VERSION = '0.54';
5 our $VERSION = '0.55';
66 our $AUTHORITY = 'cpan:STEVAN';
77
88 use base 'Class::MOP::Method';
99
1010 use Moose::Meta::Role::Composite;
1111
12 our $VERSION = '0.54';
12 our $VERSION = '0.55';
1313 our $AUTHORITY = 'cpan:STEVAN';
1414
1515 use base 'Moose::Meta::Role::Application';
88
99 use Data::Dumper;
1010
11 our $VERSION = '0.54';
11 our $VERSION = '0.55';
1212 our $AUTHORITY = 'cpan:STEVAN';
1313
1414 use base 'Moose::Meta::Role::Application';
66 use Carp 'confess';
77 use Scalar::Util 'blessed';
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::Role::Application::ToClass';
88
99 use Data::Dumper;
1010
11 our $VERSION = '0.54';
11 our $VERSION = '0.55';
1212 our $AUTHORITY = 'cpan:STEVAN';
1313
1414 use base 'Moose::Meta::Role::Application';
33 use warnings;
44 use metaclass;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 __PACKAGE__->meta->add_attribute('method_exclusions' => (
66 use Carp 'confess';
77 use Scalar::Util 'blessed';
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::Role';
33 use strict;
44 use warnings;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 use base 'Moose::Meta::Role::Method';
33 use strict;
44 use warnings;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 use base 'Class::MOP::Method';
77 use Carp 'confess';
88 use Scalar::Util 'blessed';
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 use Moose::Meta::Class;
77 use Carp 'confess';
88 use Scalar::Util 'blessed';
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 use base 'Moose::Meta::TypeCoercion';
99 use Moose::Meta::Attribute;
1010 use Moose::Util::TypeConstraints ();
1111
12 our $VERSION = '0.54';
12 our $VERSION = '0.55';
1313 our $AUTHORITY = 'cpan:STEVAN';
1414
1515 __PACKAGE__->meta->add_attribute('type_coercion_map' => (
66 use Scalar::Util 'blessed';
77 use Moose::Util::TypeConstraints ();
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::TypeConstraint';
55
66 use Moose::Util::TypeConstraints ();
77
8 our $VERSION = '0.54';
8 our $VERSION = '0.55';
99 our $AUTHORITY = 'cpan:STEVAN';
1010
1111 use base 'Moose::Meta::TypeConstraint';
33 use warnings;
44 use metaclass;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 use base 'Moose::Meta::TypeConstraint';
77 use Carp 'confess';
88 use Moose::Util::TypeConstraints;
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 use base 'Moose::Meta::TypeConstraint';
77 use Scalar::Util 'blessed';
88 use Carp 'confess';
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 use base 'Class::MOP::Object';
66 use Scalar::Util 'blessed';
77 use Moose::Util::TypeConstraints ();
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::TypeConstraint';
66
77 use Moose::Meta::TypeCoercion::Union;
88
9 our $VERSION = '0.54';
9 our $VERSION = '0.55';
1010 our $AUTHORITY = 'cpan:STEVAN';
1111
1212 use base 'Moose::Meta::TypeConstraint';
110110 =head1 DESCRIPTION
111111
112112 This metaclass represents a union of Moose type constraints. More
113 details to be explained later (possibly in a Cookbook::Recipe).
113 details to be explained later (possibly in a Cookbook recipe).
114114
115115 This actually used to be part of Moose::Meta::TypeConstraint, but it
116116 is now better off in it's own file.
1010 use Carp 'confess';
1111 use Scalar::Util qw(blessed refaddr);
1212
13 our $VERSION = '0.54';
13 our $VERSION = '0.55';
1414 our $AUTHORITY = 'cpan:STEVAN';
1515
1616 __PACKAGE__->meta->add_attribute('name' => (reader => 'name'));
7676
7777 sub get_message {
7878 my ($self, $value) = @_;
79 $value = (defined $value ? overload::StrVal($value) : 'undef');
8079 if (my $msg = $self->message) {
8180 local $_ = $value;
8281 return $msg->($value);
8382 }
8483 else {
84 $value = (defined $value ? overload::StrVal($value) : 'undef');
8585 return "Validation failed for '" . $self->name . "' failed with value $value";
8686 }
8787 }
88
99 use Carp 'confess';
1010
11 our $VERSION = '0.54';
11 our $VERSION = '0.55';
1212 our $AUTHORITY = 'cpan:STEVAN';
1313
1414 sub new {
99 use Data::OptList;
1010 use Sub::Exporter;
1111
12 our $VERSION = '0.54';
12 our $VERSION = '0.55';
1313 our $AUTHORITY = 'cpan:STEVAN';
1414
1515 use Moose ();
44
55 use Scalar::Util 'blessed', 'looks_like_number';
66
7 our $VERSION = '0.54';
7 our $VERSION = '0.55';
88 our $AUTHORITY = 'cpan:STEVAN';
99
1010 sub Value { defined($_[0]) && !ref($_[0]) }
77 use Scalar::Util 'blessed';
88 use Sub::Exporter;
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 ## --------------------------------------------------------
423423
424424 sub _install_type_coercions ($$) {
425425 my ($type_name, $coercion_map) = @_;
426 my $type = $REGISTRY->get_type_constraint($type_name);
426 my $type = find_type_constraint($type_name);
427427 (defined $type)
428428 || confess "Cannot find type '$type_name', perhaps you forgot to load it.";
429429 if ($type->has_coercion) {
77 use Carp 'confess';
88 use Class::MOP 0.56;
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 my @exports = qw[
33 use strict;
44 use warnings;
55
6 our $VERSION = '0.54';
6 our $VERSION = '0.55';
77 our $AUTHORITY = 'cpan:STEVAN';
88
99 use Scalar::Util 'blessed';
1111
1212 use Sub::Exporter;
1313
14 use Class::MOP;
14 use Class::MOP 0.64;
1515
1616 use Moose::Meta::Class;
1717 use Moose::Meta::TypeConstraint;
2727
2828 {
2929 my $CALLER;
30
31 sub init_meta {
32 my ( $class, $base_class, $metaclass ) = @_;
33 $base_class = 'Moose::Object' unless defined $base_class;
34 $metaclass = 'Moose::Meta::Class' unless defined $metaclass;
35
36 confess
37 "The Metaclass $metaclass must be a subclass of Moose::Meta::Class."
38 unless $metaclass->isa('Moose::Meta::Class');
39
40 # make a subtype for each Moose class
41 class_type($class)
42 unless find_type_constraint($class);
43
44 my $meta;
45 if ( $class->can('meta') ) {
46 # NOTE:
47 # this is the case where the metaclass pragma
48 # was used before the 'use Moose' statement to
49 # override a specific class
50 $meta = $class->meta();
51 ( blessed($meta) && $meta->isa('Moose::Meta::Class') )
52 || confess "You already have a &meta function, but it does not return a Moose::Meta::Class";
53 }
54 else {
55 # NOTE:
56 # this is broken currently, we actually need
57 # to allow the possiblity of an inherited
58 # meta, which will not be visible until the
59 # user 'extends' first. This needs to have
60 # more intelligence to it
61 $meta = $metaclass->initialize($class);
62 $meta->add_method(
63 'meta' => sub {
64 # re-initialize so it inherits properly
65 $metaclass->initialize( blessed( $_[0] ) || $_[0] );
66 }
67 );
68 }
69
70 # make sure they inherit from Moose::Object
71 $meta->superclasses($base_class)
72 unless $meta->superclasses();
73
74 return $meta;
75 }
7630
7731 my %exports = (
7832 extends => sub {
257211
258212 }
259213
214 sub init_meta {
215 my ( $class, $base_class, $metaclass ) = @_;
216 $base_class = 'Moose::Object' unless defined $base_class;
217 $metaclass = 'Moose::Meta::Class' unless defined $metaclass;
218
219 confess
220 "The Metaclass $metaclass must be a subclass of Moose::Meta::Class."
221 unless $metaclass->isa('Moose::Meta::Class');
222
223 # make a subtype for each Moose class
224 class_type($class)
225 unless find_type_constraint($class);
226
227 my $meta;
228 if ( $class->can('meta') ) {
229 # NOTE:
230 # this is the case where the metaclass pragma
231 # was used before the 'use Moose' statement to
232 # override a specific class
233 $meta = $class->meta();
234 ( blessed($meta) && $meta->isa('Moose::Meta::Class') )
235 || confess "You already have a &meta function, but it does not return a Moose::Meta::Class";
236 }
237 else {
238 # NOTE:
239 # this is broken currently, we actually need
240 # to allow the possiblity of an inherited
241 # meta, which will not be visible until the
242 # user 'extends' first. This needs to have
243 # more intelligence to it
244 $meta = $metaclass->initialize($class);
245 $meta->add_method(
246 'meta' => sub {
247 # re-initialize so it inherits properly
248 $metaclass->initialize( blessed( $_[0] ) || $_[0] );
249 }
250 );
251 }
252
253 # make sure they inherit from Moose::Object
254 $meta->superclasses($base_class)
255 unless $meta->superclasses();
256
257 return $meta;
258 }
259
260260 ## make 'em all immutable
261261
262262 $_->meta->make_immutable(
335335
336336 =head2 Moose Extensions
337337
338 The L<MooseX::> namespace is the official place to find Moose extensions.
339 There are a number of these modules out on CPAN right now the best way to
340 find them is to search for MooseX:: on search.cpan.org or to look at the
341 latest version of L<Task::Moose> which aims to keep an up to date, easily
342 installable list of these extensions.
338 The C<MooseX::> namespace is the official place to find Moose extensions.
339 These extensions can be found on the CPAN. The easiest way to find them
340 is to search for them (L<http://search.cpan.org/search?query=MooseX::>),
341 or to examine L<Task::Moose> which aims to keep an up-to-date, easily
342 installable list of Moose extensions.
343343
344344 =head1 BUILDING CLASSES WITH MOOSE
345345
423423
424424 This will attempt to use coercion with the supplied type constraint to change
425425 the value passed into any accessors or constructors. You B<must> have supplied
426 a type constraint in order for this to work. See L<Moose::Cookbook::Recipe5>
426 a type constraint in order for this to work. See L<Moose::Cookbook::Basics::Recipe5>
427427 for an example.
428428
429429 =item I<does =E<gt> $role_name>
508508 in the class being delegated to.
509509
510510 This can be very useful for recursive classes like trees. Here is a
511 quick example (soon to be expanded into a Moose::Cookbook::Recipe):
511 quick example (soon to be expanded into a Moose::Cookbook recipe):
512512
513513 package Tree;
514514 use Moose;
572572 attribute. Custom attribute metaclasses are useful for extending the
573573 capabilities of the I<has> keyword: they are the simplest way to extend the MOP,
574574 but they are still a fairly advanced topic and too much to cover here, see
575 L<Moose::Cookbook::Recipe11> for more information.
575 L<Moose::Cookbook::Meta::Recipe1> for more information.
576576
577577 The default behavior here is to just load C<$metaclass_name>; however, we also
578578 have a way to alias to a shorter name. This will first look to see if
629629
630630 package Foo::Role;
631631 use Moose::Role;
632
632
633633 has 'message' => (
634634 is => 'rw',
635635 isa => 'Str',
636636 default => 'Hello, I am a Foo'
637637 );
638
638
639639 package My::Foo;
640640 use Moose;
641
641
642642 with 'Foo::Role';
643
643
644644 has '+message' => (default => 'Hello I am My::Foo');
645645
646646 In this case, we are basically taking the attribute which the role supplied
734734 The keyword C<inner>, much like C<super>, is a no-op outside of the context of
735735 an C<augment> method. You can think of C<inner> as being the inverse of
736736 C<super>; the details of how C<inner> and C<augment> work is best described in
737 the L<Moose::Cookbook::Recipe6>.
737 the L<Moose::Cookbook::Basics::Recipe6>.
738738
739739 =item B<augment ($name, &sub)>
740740
741741 An C<augment> method, is a way of explicitly saying "I am augmenting this
742742 method from my superclass". Once again, the details of how C<inner> and
743 C<augment> work is best described in the L<Moose::Cookbook::Recipe6>.
743 C<augment> work is best described in the L<Moose::Cookbook::Basics::Recipe6>.
744744
745745 =item B<confess>
746746
814814 sets your baseclass to Moose::Object or the value you pass in unless you already
815815 have one. This is all done via C<init_meta> which takes the name of your class
816816 and optionally a baseclass and a metaclass as arguments.
817
818 For more detail on this topic, see L<Moose::Cookbook::Extending::Recipe2>.
817819
818820 =head1 CAVEATS
819821
937939 as well as links to a number of talks and articles on Moose and Moose related
938940 technologies.
939941
942 =item L<Moose::Cookbook> - How to cook a Moose
943
944 =item The Moose is flying, a tutorial by Randal Schwartz
945
946 Part 1 - L<http://www.stonehenge.com/merlyn/LinuxMag/col94.html>
947
948 Part 2 - L<http://www.stonehenge.com/merlyn/LinuxMag/col95.html>
949
940950 =item L<Class::MOP> documentation
941951
942952 =item The #moose channel on irc.perl.org
945955
946956 =item Moose stats on ohloh.net - L<http://www.ohloh.net/projects/moose>
947957
948 =item Several Moose extension modules in the L<MooseX::> namespace.
958 =item Several Moose extension modules in the C<MooseX::> namespace.
959
960 See L<http://search.cpan.org/search?query=MooseX::> for extensions.
949961
950962 =back
951963
77
88 use Moose::Util 'does_role', 'find_meta';
99
10 our $VERSION = '0.54';
10 our $VERSION = '0.55';
1111 our $AUTHORITY = 'cpan:STEVAN';
1212
1313 my @exports = qw[
44
55 use Class::MOP;
66
7 our $VERSION = '0.54';
7 our $VERSION = '0.55';
88 our $AUTHORITY = 'cpan:STEVAN';
99
1010 BEGIN {
+0
-186
t/000_recipes/001_point.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 58;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 {
13 package Point;
14 use Moose;
15
16 has 'x' => (isa => 'Int', is => 'ro');
17 has 'y' => (isa => 'Int', is => 'rw');
18
19 sub clear {
20 my $self = shift;
21 $self->{x} = 0;
22 $self->y(0);
23 }
24
25 __PACKAGE__->meta->make_immutable(debug => 0);
26 }{
27 package Point3D;
28 use Moose;
29
30 extends 'Point';
31
32 has 'z' => (isa => 'Int');
33
34 after 'clear' => sub {
35 my $self = shift;
36 $self->{z} = 0;
37 };
38
39 __PACKAGE__->meta->make_immutable(debug => 0);
40 }
41
42 my $point = Point->new(x => 1, y => 2);
43 isa_ok($point, 'Point');
44 isa_ok($point, 'Moose::Object');
45
46 is($point->x, 1, '... got the right value for x');
47 is($point->y, 2, '... got the right value for y');
48
49 $point->y(10);
50 is($point->y, 10, '... got the right (changed) value for y');
51
52 dies_ok {
53 $point->y('Foo');
54 } '... cannot assign a non-Int to y';
55
56 dies_ok {
57 $point->x(1000);
58 } '... cannot assign to a read-only method';
59 is($point->x, 1, '... got the right (un-changed) value for x');
60
61 $point->clear();
62
63 is($point->x, 0, '... got the right (cleared) value for x');
64 is($point->y, 0, '... got the right (cleared) value for y');
65
66 # check the type constraints on the constructor
67
68 lives_ok {
69 Point->new(x => 0, y => 0);
70 } '... can assign a 0 to x and y';
71
72 dies_ok {
73 Point->new(x => 10, y => 'Foo');
74 } '... cannot assign a non-Int to y';
75
76 dies_ok {
77 Point->new(x => 'Foo', y => 10);
78 } '... cannot assign a non-Int to x';
79
80 # Point3D
81
82 my $point3d = Point3D->new({ x => 10, y => 15, z => 3 });
83 isa_ok($point3d, 'Point3D');
84 isa_ok($point3d, 'Point');
85 isa_ok($point3d, 'Moose::Object');
86
87 is($point3d->x, 10, '... got the right value for x');
88 is($point3d->y, 15, '... got the right value for y');
89 is($point3d->{'z'}, 3, '... got the right value for z');
90
91 dies_ok {
92 $point3d->z;
93 } '... there is no method for z';
94
95 $point3d->clear();
96
97 is($point3d->x, 0, '... got the right (cleared) value for x');
98 is($point3d->y, 0, '... got the right (cleared) value for y');
99 is($point3d->{'z'}, 0, '... got the right (cleared) value for z');
100
101 dies_ok {
102 Point3D->new(x => 10, y => 'Foo', z => 3);
103 } '... cannot assign a non-Int to y';
104
105 dies_ok {
106 Point3D->new(x => 'Foo', y => 10, z => 3);
107 } '... cannot assign a non-Int to x';
108
109 dies_ok {
110 Point3D->new(x => 0, y => 10, z => 'Bar');
111 } '... cannot assign a non-Int to z';
112
113 # test some class introspection
114
115 can_ok('Point', 'meta');
116 isa_ok(Point->meta, 'Moose::Meta::Class');
117
118 can_ok('Point3D', 'meta');
119 isa_ok(Point3D->meta, 'Moose::Meta::Class');
120
121 isnt(Point->meta, Point3D->meta, '... they are different metaclasses as well');
122
123 # poke at Point
124
125 is_deeply(
126 [ Point->meta->superclasses ],
127 [ 'Moose::Object' ],
128 '... Point got the automagic base class');
129
130 my @Point_methods = qw(meta new x y clear);
131 my @Point_attrs = ('x', 'y');
132
133 is_deeply(
134 [ sort @Point_methods ],
135 [ sort Point->meta->get_method_list() ],
136 '... we match the method list for Point');
137
138 is_deeply(
139 [ sort @Point_attrs ],
140 [ sort Point->meta->get_attribute_list() ],
141 '... we match the attribute list for Point');
142
143 foreach my $method (@Point_methods) {
144 ok(Point->meta->has_method($method), '... Point has the method "' . $method . '"');
145 }
146
147 foreach my $attr_name (@Point_attrs ) {
148 ok(Point->meta->has_attribute($attr_name), '... Point has the attribute "' . $attr_name . '"');
149 my $attr = Point->meta->get_attribute($attr_name);
150 ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
151 isa_ok($attr->type_constraint, 'Moose::Meta::TypeConstraint');
152 is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');
153 }
154
155 # poke at Point3D
156
157 is_deeply(
158 [ Point3D->meta->superclasses ],
159 [ 'Point' ],
160 '... Point3D gets the parent given to it');
161
162 my @Point3D_methods = qw(new meta clear);
163 my @Point3D_attrs = ('z');
164
165 is_deeply(
166 [ sort @Point3D_methods ],
167 [ sort Point3D->meta->get_method_list() ],
168 '... we match the method list for Point3D');
169
170 is_deeply(
171 [ sort @Point3D_attrs ],
172 [ sort Point3D->meta->get_attribute_list() ],
173 '... we match the attribute list for Point3D');
174
175 foreach my $method (@Point3D_methods) {
176 ok(Point3D->meta->has_method($method), '... Point3D has the method "' . $method . '"');
177 }
178
179 foreach my $attr_name (@Point3D_attrs ) {
180 ok(Point3D->meta->has_attribute($attr_name), '... Point3D has the attribute "' . $attr_name . '"');
181 my $attr = Point3D->meta->get_attribute($attr_name);
182 ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
183 isa_ok($attr->type_constraint, 'Moose::Meta::TypeConstraint');
184 is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');
185 }
+0
-113
t/000_recipes/002_bank_account.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 24;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 {
13 package BankAccount;
14 use Moose;
15
16 has 'balance' => (isa => 'Num', is => 'rw', default => 0);
17
18 sub deposit {
19 my ($self, $amount) = @_;
20 $self->balance($self->balance + $amount);
21 }
22
23 sub withdraw {
24 my ($self, $amount) = @_;
25 my $current_balance = $self->balance();
26 ($current_balance >= $amount)
27 || confess "Account overdrawn";
28 $self->balance($current_balance - $amount);
29 }
30
31 __PACKAGE__->meta->make_immutable(debug => 0);
32 }{
33 package CheckingAccount;
34 use Moose;
35
36 extends 'BankAccount';
37
38 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
39
40 before 'withdraw' => sub {
41 my ($self, $amount) = @_;
42 my $overdraft_amount = $amount - $self->balance();
43 if ($self->overdraft_account && $overdraft_amount > 0) {
44 $self->overdraft_account->withdraw($overdraft_amount);
45 $self->deposit($overdraft_amount);
46 }
47 };
48
49 __PACKAGE__->meta->make_immutable(debug => 0);
50 }
51
52 my $savings_account = BankAccount->new(balance => 250);
53 isa_ok($savings_account, 'BankAccount');
54
55 is($savings_account->balance, 250, '... got the right savings balance');
56 lives_ok {
57 $savings_account->withdraw(50);
58 } '... withdrew from savings successfully';
59 is($savings_account->balance, 200, '... got the right savings balance after withdrawl');
60
61 $savings_account->deposit(150);
62 is($savings_account->balance, 350, '... got the right savings balance after deposit');
63
64 {
65 my $checking_account = CheckingAccount->new(
66 balance => 100,
67 overdraft_account => $savings_account
68 );
69 isa_ok($checking_account, 'CheckingAccount');
70 isa_ok($checking_account, 'BankAccount');
71
72 is($checking_account->overdraft_account, $savings_account, '... got the right overdraft account');
73
74 is($checking_account->balance, 100, '... got the right checkings balance');
75
76 lives_ok {
77 $checking_account->withdraw(50);
78 } '... withdrew from checking successfully';
79 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl');
80 is($savings_account->balance, 350, '... got the right savings balance after checking withdrawl (no overdraft)');
81
82 lives_ok {
83 $checking_account->withdraw(200);
84 } '... withdrew from checking successfully';
85 is($checking_account->balance, 0, '... got the right checkings balance after withdrawl');
86 is($savings_account->balance, 200, '... got the right savings balance after overdraft withdrawl');
87 }
88
89 {
90 my $checking_account = CheckingAccount->new(
91 balance => 100
92 # no overdraft account
93 );
94 isa_ok($checking_account, 'CheckingAccount');
95 isa_ok($checking_account, 'BankAccount');
96
97 is($checking_account->overdraft_account, undef, '... no overdraft account');
98
99 is($checking_account->balance, 100, '... got the right checkings balance');
100
101 lives_ok {
102 $checking_account->withdraw(50);
103 } '... withdrew from checking successfully';
104 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl');
105
106 dies_ok {
107 $checking_account->withdraw(200);
108 } '... withdrawl failed due to attempted overdraft';
109 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl failure');
110 }
111
112
+0
-121
t/000_recipes/003_binary_tree.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 34;
6 use Test::Exception;
7
8 use Scalar::Util 'isweak';
9
10 BEGIN {
11 use_ok('Moose');
12 }
13
14 {
15 package BinaryTree;
16 use Moose;
17
18 has 'node' => (is => 'rw', isa => 'Any');
19
20 has 'parent' => (
21 is => 'rw',
22 isa => 'BinaryTree',
23 predicate => 'has_parent',
24 weak_ref => 1,
25 );
26
27 has 'left' => (
28 is => 'rw',
29 isa => 'BinaryTree',
30 predicate => 'has_left',
31 lazy => 1,
32 default => sub { BinaryTree->new(parent => $_[0]) },
33 );
34
35 has 'right' => (
36 is => 'rw',
37 isa => 'BinaryTree',
38 predicate => 'has_right',
39 lazy => 1,
40 default => sub { BinaryTree->new(parent => $_[0]) },
41 );
42
43 before 'right', 'left' => sub {
44 my ($self, $tree) = @_;
45 $tree->parent($self) if defined $tree;
46 };
47
48 __PACKAGE__->meta->make_immutable(debug => 0);
49 }
50
51 my $root = BinaryTree->new(node => 'root');
52 isa_ok($root, 'BinaryTree');
53
54 is($root->node, 'root', '... got the right node value');
55
56 ok(!$root->has_left, '... no left node yet');
57 ok(!$root->has_right, '... no right node yet');
58
59 ok(!$root->has_parent, '... no parent for root node');
60
61 # make a left node
62
63 my $left = $root->left;
64 isa_ok($left, 'BinaryTree');
65
66 is($root->left, $left, '... got the same node (and it is $left)');
67 ok($root->has_left, '... we have a left node now');
68
69 ok($left->has_parent, '... lefts has a parent');
70 is($left->parent, $root, '... lefts parent is the root');
71
72 ok(isweak($left->{parent}), '... parent is a weakened ref');
73
74 ok(!$left->has_left, '... $left no left node yet');
75 ok(!$left->has_right, '... $left no right node yet');
76
77 is($left->node, undef, '... left has got no node value');
78
79 lives_ok {
80 $left->node('left')
81 } '... assign to lefts node';
82
83 is($left->node, 'left', '... left now has a node value');
84
85 # make a right node
86
87 ok(!$root->has_right, '... still no right node yet');
88
89 is($root->right->node, undef, '... right has got no node value');
90
91 ok($root->has_right, '... now we have a right node');
92
93 my $right = $root->right;
94 isa_ok($right, 'BinaryTree');
95
96 lives_ok {
97 $right->node('right')
98 } '... assign to rights node';
99
100 is($right->node, 'right', '... left now has a node value');
101
102 is($root->right, $right, '... got the same node (and it is $right)');
103 ok($root->has_right, '... we have a right node now');
104
105 ok($right->has_parent, '... rights has a parent');
106 is($right->parent, $root, '... rights parent is the root');
107
108 ok(isweak($right->{parent}), '... parent is a weakened ref');
109
110 my $left_left = $left->left;
111 isa_ok($left_left, 'BinaryTree');
112
113 ok($left_left->has_parent, '... left does have a parent');
114
115 is($left_left->parent, $left, '... got a parent node (and it is $left)');
116 ok($left->has_left, '... we have a left node now');
117 is($left->left, $left_left, '... got a left node (and it is $left_left)');
118
119 ok(isweak($left_left->{parent}), '... parent is a weakened ref');
120
+0
-276
t/000_recipes/004_company.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More;
6
7 BEGIN {
8 eval "use Regexp::Common; use Locale::US;";
9 plan skip_all => "Regexp::Common & Locale::US required for this test" if $@;
10 plan tests => 66;
11 }
12
13 use Test::Exception;
14 use Scalar::Util 'isweak';
15
16 BEGIN {
17 use_ok('Moose');
18 }
19
20 {
21 package Address;
22 use Moose;
23 use Moose::Util::TypeConstraints;
24
25 use Locale::US;
26 use Regexp::Common 'zip';
27
28 my $STATES = Locale::US->new;
29
30 subtype USState
31 => as Str
32 => where {
33 (exists $STATES->{code2state}{uc($_)} || exists $STATES->{state2code}{uc($_)})
34 };
35
36 subtype USZipCode
37 => as Value
38 => where {
39 /^$RE{zip}{US}{-extended => 'allow'}$/
40 };
41
42 has 'street' => (is => 'rw', isa => 'Str');
43 has 'city' => (is => 'rw', isa => 'Str');
44 has 'state' => (is => 'rw', isa => 'USState');
45 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
46
47 __PACKAGE__->meta->make_immutable(debug => 0);
48 }{
49
50 package Company;
51 use Moose;
52 use Moose::Util::TypeConstraints;
53
54 has 'name' => (is => 'rw', isa => 'Str', required => 1);
55 has 'address' => (is => 'rw', isa => 'Address');
56 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
57
58 sub BUILD {
59 my ($self, $params) = @_;
60 if ($params->{employees}) {
61 foreach my $employee (@{$params->{employees}}) {
62 $employee->company($self);
63 }
64 }
65 }
66
67 sub get_employee_count { scalar @{(shift)->employees} }
68
69 after 'employees' => sub {
70 my ($self, $employees) = @_;
71 # if employees is defined, it
72 # has already been type checked
73 if (defined $employees) {
74 # make sure each gets the
75 # weak ref to the company
76 foreach my $employee (@{$employees}) {
77 $employee->company($self);
78 }
79 }
80 };
81
82 __PACKAGE__->meta->make_immutable(debug => 0);
83 }{
84
85 package Person;
86 use Moose;
87
88 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
89 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
90 has 'middle_initial' => (is => 'rw', isa => 'Str', predicate => 'has_middle_initial');
91 has 'address' => (is => 'rw', isa => 'Address');
92
93 sub full_name {
94 my $self = shift;
95 return $self->first_name .
96 ($self->has_middle_initial ? ' ' . $self->middle_initial . '. ' : ' ') .
97 $self->last_name;
98 }
99
100 __PACKAGE__->meta->make_immutable(debug => 0);
101 }{
102
103 package Employee;
104 use Moose;
105
106 extends 'Person';
107
108 has 'title' => (is => 'rw', isa => 'Str', required => 1);
109 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
110
111 override 'full_name' => sub {
112 my $self = shift;
113 super() . ', ' . $self->title
114 };
115
116 __PACKAGE__->meta->make_immutable(debug => 0);
117 }
118
119 my $ii;
120 lives_ok {
121 $ii = Company->new({
122 name => 'Infinity Interactive',
123 address => Address->new(
124 street => '565 Plandome Rd., Suite 307',
125 city => 'Manhasset',
126 state => 'NY',
127 zip_code => '11030'
128 ),
129 employees => [
130 Employee->new(
131 first_name => 'Jeremy',
132 last_name => 'Shao',
133 title => 'President / Senior Consultant',
134 address => Address->new(city => 'Manhasset', state => 'NY')
135 ),
136 Employee->new(
137 first_name => 'Tommy',
138 last_name => 'Lee',
139 title => 'Vice President / Senior Developer',
140 address => Address->new(city => 'New York', state => 'NY')
141 ),
142 Employee->new(
143 first_name => 'Stevan',
144 middle_initial => 'C',
145 last_name => 'Little',
146 title => 'Senior Developer',
147 address => Address->new(city => 'Madison', state => 'CT')
148 ),
149 ]
150 });
151 } '... created the entire company successfully';
152 isa_ok($ii, 'Company');
153
154 is($ii->name, 'Infinity Interactive', '... got the right name for the company');
155
156 isa_ok($ii->address, 'Address');
157 is($ii->address->street, '565 Plandome Rd., Suite 307', '... got the right street address');
158 is($ii->address->city, 'Manhasset', '... got the right city');
159 is($ii->address->state, 'NY', '... got the right state');
160 is($ii->address->zip_code, 11030, '... got the zip code');
161
162 is($ii->get_employee_count, 3, '... got the right employee count');
163
164 # employee #1
165
166 isa_ok($ii->employees->[0], 'Employee');
167 isa_ok($ii->employees->[0], 'Person');
168
169 is($ii->employees->[0]->first_name, 'Jeremy', '... got the right first name');
170 is($ii->employees->[0]->last_name, 'Shao', '... got the right last name');
171 ok(!$ii->employees->[0]->has_middle_initial, '... no middle initial');
172 is($ii->employees->[0]->middle_initial, undef, '... got the right middle initial value');
173 is($ii->employees->[0]->full_name, 'Jeremy Shao, President / Senior Consultant', '... got the right full name');
174 is($ii->employees->[0]->title, 'President / Senior Consultant', '... got the right title');
175 is($ii->employees->[0]->company, $ii, '... got the right company');
176 ok(isweak($ii->employees->[0]->{company}), '... the company is a weak-ref');
177
178 isa_ok($ii->employees->[0]->address, 'Address');
179 is($ii->employees->[0]->address->city, 'Manhasset', '... got the right city');
180 is($ii->employees->[0]->address->state, 'NY', '... got the right state');
181
182 # employee #2
183
184 isa_ok($ii->employees->[1], 'Employee');
185 isa_ok($ii->employees->[1], 'Person');
186
187 is($ii->employees->[1]->first_name, 'Tommy', '... got the right first name');
188 is($ii->employees->[1]->last_name, 'Lee', '... got the right last name');
189 ok(!$ii->employees->[1]->has_middle_initial, '... no middle initial');
190 is($ii->employees->[1]->middle_initial, undef, '... got the right middle initial value');
191 is($ii->employees->[1]->full_name, 'Tommy Lee, Vice President / Senior Developer', '... got the right full name');
192 is($ii->employees->[1]->title, 'Vice President / Senior Developer', '... got the right title');
193 is($ii->employees->[1]->company, $ii, '... got the right company');
194 ok(isweak($ii->employees->[1]->{company}), '... the company is a weak-ref');
195
196 isa_ok($ii->employees->[1]->address, 'Address');
197 is($ii->employees->[1]->address->city, 'New York', '... got the right city');
198 is($ii->employees->[1]->address->state, 'NY', '... got the right state');
199
200 # employee #3
201
202 isa_ok($ii->employees->[2], 'Employee');
203 isa_ok($ii->employees->[2], 'Person');
204
205 is($ii->employees->[2]->first_name, 'Stevan', '... got the right first name');
206 is($ii->employees->[2]->last_name, 'Little', '... got the right last name');
207 ok($ii->employees->[2]->has_middle_initial, '... got middle initial');
208 is($ii->employees->[2]->middle_initial, 'C', '... got the right middle initial value');
209 is($ii->employees->[2]->full_name, 'Stevan C. Little, Senior Developer', '... got the right full name');
210 is($ii->employees->[2]->title, 'Senior Developer', '... got the right title');
211 is($ii->employees->[2]->company, $ii, '... got the right company');
212 ok(isweak($ii->employees->[2]->{company}), '... the company is a weak-ref');
213
214 isa_ok($ii->employees->[2]->address, 'Address');
215 is($ii->employees->[2]->address->city, 'Madison', '... got the right city');
216 is($ii->employees->[2]->address->state, 'CT', '... got the right state');
217
218 # create new company
219
220 my $new_company = Company->new(name => 'Infinity Interactive International');
221 isa_ok($new_company, 'Company');
222
223 my $ii_employees = $ii->employees;
224 foreach my $employee (@$ii_employees) {
225 is($employee->company, $ii, '... has the ii company');
226 }
227
228 $new_company->employees($ii_employees);
229
230 foreach my $employee (@{$new_company->employees}) {
231 is($employee->company, $new_company, '... has the different company now');
232 }
233
234 ## check some error conditions for the subtypes
235
236 dies_ok {
237 Address->new(street => {}),
238 } '... we die correctly with bad args';
239
240 dies_ok {
241 Address->new(city => {}),
242 } '... we die correctly with bad args';
243
244 dies_ok {
245 Address->new(state => 'British Columbia'),
246 } '... we die correctly with bad args';
247
248 lives_ok {
249 Address->new(state => 'Connecticut'),
250 } '... we live correctly with good args';
251
252 dies_ok {
253 Address->new(zip_code => 'AF5J6$'),
254 } '... we die correctly with bad args';
255
256 lives_ok {
257 Address->new(zip_code => '06443'),
258 } '... we live correctly with good args';
259
260 dies_ok {
261 Company->new(),
262 } '... we die correctly without good args';
263
264 lives_ok {
265 Company->new(name => 'Foo'),
266 } '... we live correctly without good args';
267
268 dies_ok {
269 Company->new(name => 'Foo', employees => [ Person->new ]),
270 } '... we die correctly with good args';
271
272 lives_ok {
273 Company->new(name => 'Foo', employees => []),
274 } '... we live correctly with good args';
275
+0
-118
t/000_recipes/005_coercion.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More;
6
7 BEGIN {
8 eval "use HTTP::Headers; use Params::Coerce; use URI;";
9 plan skip_all => "HTTP::Headers & Params::Coerce & URI required for this test" if $@;
10 plan tests => 18;
11 }
12
13 use Test::Exception;
14
15 BEGIN {
16 use_ok('Moose');
17 }
18
19 {
20 package Request;
21 use Moose;
22 use Moose::Util::TypeConstraints;
23
24 use HTTP::Headers ();
25 use Params::Coerce ();
26 use URI ();
27
28 subtype Header
29 => as Object
30 => where { $_->isa('HTTP::Headers') };
31
32 coerce Header
33 => from ArrayRef
34 => via { HTTP::Headers->new( @{ $_ } ) }
35 => from HashRef
36 => via { HTTP::Headers->new( %{ $_ } ) };
37
38 subtype Uri
39 => as Object
40 => where { $_->isa('URI') };
41
42 coerce Uri
43 => from Object
44 => via { $_->isa('URI') ? $_ : Params::Coerce::coerce( 'URI', $_ ) }
45 => from Str
46 => via { URI->new( $_, 'http' ) };
47
48 subtype Protocol
49 => as Str
50 => where { /^HTTP\/[0-9]\.[0-9]$/ };
51
52
53 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
54 has 'url' => (is => 'rw', isa => 'Uri', coerce => 1);
55 has 'method' => (is => 'rw', isa => 'Str');
56 has 'protocol' => (is => 'rw', isa => 'Protocol');
57 has 'headers' => (
58 is => 'rw',
59 isa => 'Header',
60 coerce => 1,
61 default => sub { HTTP::Headers->new }
62 );
63
64 __PACKAGE__->meta->make_immutable(debug => 0);
65 }
66
67 my $r = Request->new;
68 isa_ok($r, 'Request');
69
70 {
71 my $header = $r->headers;
72 isa_ok($header, 'HTTP::Headers');
73
74 is($r->headers->content_type, '', '... got no content type in the header');
75
76 $r->headers( { content_type => 'text/plain' } );
77
78 my $header2 = $r->headers;
79 isa_ok($header2, 'HTTP::Headers');
80 isnt($header, $header2, '... created a new HTTP::Header object');
81
82 is($header2->content_type, 'text/plain', '... got the right content type in the header');
83
84 $r->headers( [ content_type => 'text/html' ] );
85
86 my $header3 = $r->headers;
87 isa_ok($header3, 'HTTP::Headers');
88 isnt($header2, $header3, '... created a new HTTP::Header object');
89
90 is($header3->content_type, 'text/html', '... got the right content type in the header');
91
92 $r->headers( HTTP::Headers->new(content_type => 'application/pdf') );
93
94 my $header4 = $r->headers;
95 isa_ok($header4, 'HTTP::Headers');
96 isnt($header3, $header4, '... created a new HTTP::Header object');
97
98 is($header4->content_type, 'application/pdf', '... got the right content type in the header');
99
100 dies_ok {
101 $r->headers('Foo')
102 } '... dies when it gets bad params';
103 }
104
105 {
106 is($r->protocol, undef, '... got nothing by default');
107
108 lives_ok {
109 $r->protocol('HTTP/1.0');
110 } '... set the protocol correctly';
111 is($r->protocol, 'HTTP/1.0', '... got nothing by default');
112
113 dies_ok {
114 $r->protocol('http/1.0');
115 } '... the protocol died with bar params correctly';
116 }
117
+0
-76
t/000_recipes/006_augment_inner.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 3;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## Augment/Inner
13
14 {
15 package Document::Page;
16 use Moose;
17
18 has 'body' => (is => 'rw', isa => 'Str', default => sub {''});
19
20 sub create {
21 my $self = shift;
22 $self->open_page;
23 inner();
24 $self->close_page;
25 }
26
27 sub append_body {
28 my ($self, $appendage) = @_;
29 $self->body($self->body . $appendage);
30 }
31
32 sub open_page { (shift)->append_body('<page>') }
33 sub close_page { (shift)->append_body('</page>') }
34
35 package Document::PageWithHeadersAndFooters;
36 use Moose;
37
38 extends 'Document::Page';
39
40 augment 'create' => sub {
41 my $self = shift;
42 $self->create_header;
43 inner();
44 $self->create_footer;
45 };
46
47 sub create_header { (shift)->append_body('<header/>') }
48 sub create_footer { (shift)->append_body('<footer/>') }
49
50 package TPSReport;
51 use Moose;
52
53 extends 'Document::PageWithHeadersAndFooters';
54
55 augment 'create' => sub {
56 my $self = shift;
57 $self->create_tps_report;
58 };
59
60 sub create_tps_report {
61 (shift)->append_body('<report type="tps"/>')
62 }
63 }
64
65 my $tps_report = TPSReport->new;
66 isa_ok($tps_report, 'TPSReport');
67
68 is(
69 $tps_report->create,
70 q{<page><header/><report type="tps"/><footer/></page>},
71 '... got the right TPS report');
72
73
74
75
+0
-190
t/000_recipes/010_roles.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 64;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## Roles
13
14 {
15 package Eq;
16 use Moose::Role;
17
18 requires 'equal_to';
19
20 sub not_equal_to {
21 my ($self, $other) = @_;
22 not $self->equal_to($other);
23 }
24
25 package Comparable;
26 use Moose::Role;
27
28 with 'Eq';
29
30 requires 'compare';
31
32 sub equal_to {
33 my ($self, $other) = @_;
34 $self->compare($other) == 0;
35 }
36
37 sub greater_than {
38 my ($self, $other) = @_;
39 $self->compare($other) == 1;
40 }
41
42 sub less_than {
43 my ($self, $other) = @_;
44 $self->compare($other) == -1;
45 }
46
47 sub greater_than_or_equal_to {
48 my ($self, $other) = @_;
49 $self->greater_than($other) || $self->equal_to($other);
50 }
51
52 sub less_than_or_equal_to {
53 my ($self, $other) = @_;
54 $self->less_than($other) || $self->equal_to($other);
55 }
56
57 package Printable;
58 use Moose::Role;
59
60 requires 'to_string';
61 }
62
63 ## Classes
64
65 {
66 package US::Currency;
67 use Moose;
68
69 with 'Comparable', 'Printable';
70
71 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
72
73 sub compare {
74 my ($self, $other) = @_;
75 $self->amount <=> $other->amount;
76 }
77
78 sub to_string {
79 my $self = shift;
80 sprintf '$%0.2f USD' => $self->amount
81 }
82
83 __PACKAGE__->meta->make_immutable(debug => 0);
84 }
85
86 ok(US::Currency->does('Comparable'), '... US::Currency does Comparable');
87 ok(US::Currency->does('Eq'), '... US::Currency does Eq');
88 ok(US::Currency->does('Printable'), '... US::Currency does Printable');
89
90 my $hundred = US::Currency->new(amount => 100.00);
91 isa_ok($hundred, 'US::Currency');
92
93 ok( $hundred->DOES("US::Currency"), "UNIVERSAL::DOES for class" );
94 ok( $hundred->DOES("Comparable"), "UNIVERSAL::DOES for role" );
95
96 can_ok($hundred, 'amount');
97 is($hundred->amount, 100, '... got the right amount');
98
99 can_ok($hundred, 'to_string');
100 is($hundred->to_string, '$100.00 USD', '... got the right stringified value');
101
102 ok($hundred->does('Comparable'), '... US::Currency does Comparable');
103 ok($hundred->does('Eq'), '... US::Currency does Eq');
104 ok($hundred->does('Printable'), '... US::Currency does Printable');
105
106 my $fifty = US::Currency->new(amount => 50.00);
107 isa_ok($fifty, 'US::Currency');
108
109 can_ok($fifty, 'amount');
110 is($fifty->amount, 50, '... got the right amount');
111
112 can_ok($fifty, 'to_string');
113 is($fifty->to_string, '$50.00 USD', '... got the right stringified value');
114
115 ok($hundred->greater_than($fifty), '... 100 gt 50');
116 ok($hundred->greater_than_or_equal_to($fifty), '... 100 ge 50');
117 ok(!$hundred->less_than($fifty), '... !100 lt 50');
118 ok(!$hundred->less_than_or_equal_to($fifty), '... !100 le 50');
119 ok(!$hundred->equal_to($fifty), '... !100 eq 50');
120 ok($hundred->not_equal_to($fifty), '... 100 ne 50');
121
122 ok(!$fifty->greater_than($hundred), '... !50 gt 100');
123 ok(!$fifty->greater_than_or_equal_to($hundred), '... !50 ge 100');
124 ok($fifty->less_than($hundred), '... 50 lt 100');
125 ok($fifty->less_than_or_equal_to($hundred), '... 50 le 100');
126 ok(!$fifty->equal_to($hundred), '... !50 eq 100');
127 ok($fifty->not_equal_to($hundred), '... 50 ne 100');
128
129 ok(!$fifty->greater_than($fifty), '... !50 gt 50');
130 ok($fifty->greater_than_or_equal_to($fifty), '... !50 ge 50');
131 ok(!$fifty->less_than($fifty), '... 50 lt 50');
132 ok($fifty->less_than_or_equal_to($fifty), '... 50 le 50');
133 ok($fifty->equal_to($fifty), '... 50 eq 50');
134 ok(!$fifty->not_equal_to($fifty), '... !50 ne 50');
135
136 ## ... check some meta-stuff
137
138 # Eq
139
140 my $eq_meta = Eq->meta;
141 isa_ok($eq_meta, 'Moose::Meta::Role');
142
143 ok($eq_meta->has_method('not_equal_to'), '... Eq has_method not_equal_to');
144 ok($eq_meta->requires_method('equal_to'), '... Eq requires_method not_equal_to');
145
146 # Comparable
147
148 my $comparable_meta = Comparable->meta;
149 isa_ok($comparable_meta, 'Moose::Meta::Role');
150
151 ok($comparable_meta->does_role('Eq'), '... Comparable does Eq');
152
153 foreach my $method_name (qw(
154 equal_to not_equal_to
155 greater_than greater_than_or_equal_to
156 less_than less_than_or_equal_to
157 )) {
158 ok($comparable_meta->has_method($method_name), '... Comparable has_method ' . $method_name);
159 }
160
161 ok($comparable_meta->requires_method('compare'), '... Comparable requires_method compare');
162
163 # Printable
164
165 my $printable_meta = Printable->meta;
166 isa_ok($printable_meta, 'Moose::Meta::Role');
167
168 ok($printable_meta->requires_method('to_string'), '... Printable requires_method to_string');
169
170 # US::Currency
171
172 my $currency_meta = US::Currency->meta;
173 isa_ok($currency_meta, 'Moose::Meta::Class');
174
175 ok($currency_meta->does_role('Comparable'), '... US::Currency does Comparable');
176 ok($currency_meta->does_role('Eq'), '... US::Currency does Eq');
177 ok($currency_meta->does_role('Printable'), '... US::Currency does Printable');
178
179 foreach my $method_name (qw(
180 amount
181 equal_to not_equal_to
182 compare
183 greater_than greater_than_or_equal_to
184 less_than less_than_or_equal_to
185 to_string
186 )) {
187 ok($currency_meta->has_method($method_name), '... US::Currency has_method ' . $method_name);
188 }
189
+0
-98
t/000_recipes/011_advanced_role_composition.t less more
0 use strict;
1 use warnings;
2 use Test::More tests => 5;
3 use Class::MOP;
4
5 # This is copied directly from recipe 11
6 {
7 package Restartable;
8 use Moose::Role;
9
10 has 'is_paused' => (
11 is => 'rw',
12 isa => 'Bool',
13 default => 0,
14 );
15
16 requires 'save_state', 'load_state';
17
18 sub stop { }
19
20 sub start { }
21
22 package Restartable::ButUnreliable;
23 use Moose::Role;
24
25 with 'Restartable' => {
26 alias => {
27 stop => '_stop',
28 start => '_start'
29 }
30 };
31
32 sub stop {
33 my $self = shift;
34
35 $self->explode() if rand(1) > .5;
36
37 $self->_stop();
38 }
39
40 sub start {
41 my $self = shift;
42
43 $self->explode() if rand(1) > .5;
44
45 $self->_start();
46 }
47
48 package Restartable::ButBroken;
49 use Moose::Role;
50
51 with 'Restartable' => { excludes => [ 'stop', 'start' ] };
52
53 sub stop {
54 my $self = shift;
55
56 $self->explode();
57 }
58
59 sub start {
60 my $self = shift;
61
62 $self->explode();
63 }
64 }
65
66 # This is the actual tests
67 {
68 my $unreliable = Moose::Meta::Class->create_anon_class(
69 superclasses => [],
70 roles => [qw/Restartable::ButUnreliable/],
71 methods => {
72 explode => sub { }, # nop.
73 'save_state' => sub { },
74 'load_state' => sub { },
75 },
76 )->new_object();
77 ok $unreliable, 'made anon class with Restartable::ButUnreliable role';
78 can_ok $unreliable, qw/start stop/;
79 }
80
81 {
82 my $cnt = 0;
83 my $broken = Moose::Meta::Class->create_anon_class(
84 superclasses => [],
85 roles => [qw/Restartable::ButBroken/],
86 methods => {
87 explode => sub { $cnt++ },
88 'save_state' => sub { },
89 'load_state' => sub { },
90 },
91 )->new_object();
92 ok $broken, 'made anon class with Restartable::ButBroken role';
93 $broken->start();
94 is $cnt, 1, '... start called explode';
95 $broken->stop();
96 is $cnt, 2, '... stop also called explode';
97 }
+0
-80
t/000_recipes/021_meta_attribute.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 2;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## meta-attribute example
13 {
14
15 package MyApp::Meta::Attribute::Labeled;
16 use Moose;
17 extends 'Moose::Meta::Attribute';
18
19 has label => (
20 is => 'rw',
21 isa => 'Str',
22 predicate => 'has_label',
23 );
24
25 package Moose::Meta::Attribute::Custom::Labeled;
26 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
27
28 package MyApp::Website;
29 use Moose;
30
31 has url => (
32 metaclass => 'Labeled',
33 isa => 'Str',
34 is => 'rw',
35 label => "The site's URL",
36 );
37
38 has name => (
39 is => 'rw',
40 isa => 'Str',
41 );
42
43 sub dump {
44 my $self = shift;
45
46 my $dump_value = '';
47
48 # iterate over all the attributes in $self
49 my %attributes = %{ $self->meta->get_attribute_map };
50 foreach my $name (sort keys %attributes) {
51
52 my $attribute = $attributes{$name};
53
54 # print the label if available
55 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
56 && $attribute->has_label) {
57 $dump_value .= $attribute->label;
58 }
59 # otherwise print the name
60 else {
61 $dump_value .= $name;
62 }
63
64 # print the attribute's value
65 my $reader = $attribute->get_read_method;
66 $dump_value .= ": " . $self->$reader . "\n";
67 }
68
69 return $dump_value;
70 }
71
72 }
73
74 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
75 is($app->dump, q{name: Google
76 The site's URL: http://google.com
77 }, '... got the expected dump value');
78
79
+0
-139
t/000_recipes/022_attribute_trait.t less more
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 3;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## attribute trait example
13 {
14
15 package MyApp::Meta::Attribute::Trait::Labeled;
16 use Moose::Role;
17
18 has label => (
19 is => 'rw',
20 isa => 'Str',
21 predicate => 'has_label',
22 );
23
24 package Moose::Meta::Attribute::Custom::Trait::Labeled;
25 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
26
27 package MyApp::Website;
28 use Moose;
29
30 has url => (
31 traits => [qw/Labeled/],
32 isa => 'Str',
33 is => 'rw',
34 label => "The site's URL",
35 );
36
37 has name => (
38 is => 'rw',
39 isa => 'Str',
40 );
41
42 sub dump {
43 my $self = shift;
44
45 my $dump_value = '';
46
47 # iterate over all the attributes in $self
48 my %attributes = %{ $self->meta->get_attribute_map };
49 foreach my $name (sort keys %attributes) {
50
51 my $attribute = $attributes{$name};
52
53 # print the label if available
54 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
55 && $attribute->has_label) {
56 $dump_value .= $attribute->label;
57 }
58 # otherwise print the name
59 else {
60 $dump_value .= $name;
61 }
62
63 # print the attribute's value
64 my $reader = $attribute->get_read_method;
65 $dump_value .= ": " . $self->$reader . "\n";
66 }
67
68 return $dump_value;
69 }
70
71 }
72
73 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
74 is($app->dump, q{name: Google
75 The site's URL: http://google.com
76 }, '... got the expected dump value');
77
78 # using the trait directly in a regular metaclass
79 {
80 package MyApp::Meta::Attribute::Labeled;
81 use Moose;
82 extends 'Moose::Meta::Attribute';
83 with 'MyApp::Meta::Attribute::Trait::Labeled';
84
85 package Moose::Meta::Attribute::Custom::Labeled;
86 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
87
88 package MyApp::Website2;
89 use Moose;
90
91 has url => (
92 metaclass => 'Labeled',
93 isa => 'Str',
94 is => 'rw',
95 label => "The site's URL",
96 );
97
98 has name => (
99 is => 'rw',
100 isa => 'Str',
101 );
102
103 sub dump {
104 my $self = shift;
105
106 my $dump_value = '';
107
108 # iterate over all the attributes in $self
109 my %attributes = %{ $self->meta->get_attribute_map };
110 foreach my $name (sort keys %attributes) {
111
112 my $attribute = $attributes{$name};
113
114 # print the label if available
115 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
116 && $attribute->has_label) {
117 $dump_value .= $attribute->label;
118 }
119 # otherwise print the name
120 else {
121 $dump_value .= $name;
122 }
123
124 # print the attribute's value
125 my $reader = $attribute->get_read_method;
126 $dump_value .= ": " . $self->$reader . "\n";
127 }
128
129 return $dump_value;
130 }
131
132 }
133
134 my $app2 = MyApp::Website2->new(url => "http://google.com", name => "Google");
135 is($app2->dump, q{name: Google
136 The site's URL: http://google.com
137 }, '... got the expected dump value');
138
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 58;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 {
13 package Point;
14 use Moose;
15
16 has 'x' => (isa => 'Int', is => 'ro');
17 has 'y' => (isa => 'Int', is => 'rw');
18
19 sub clear {
20 my $self = shift;
21 $self->{x} = 0;
22 $self->y(0);
23 }
24
25 __PACKAGE__->meta->make_immutable(debug => 0);
26 }{
27 package Point3D;
28 use Moose;
29
30 extends 'Point';
31
32 has 'z' => (isa => 'Int');
33
34 after 'clear' => sub {
35 my $self = shift;
36 $self->{z} = 0;
37 };
38
39 __PACKAGE__->meta->make_immutable(debug => 0);
40 }
41
42 my $point = Point->new(x => 1, y => 2);
43 isa_ok($point, 'Point');
44 isa_ok($point, 'Moose::Object');
45
46 is($point->x, 1, '... got the right value for x');
47 is($point->y, 2, '... got the right value for y');
48
49 $point->y(10);
50 is($point->y, 10, '... got the right (changed) value for y');
51
52 dies_ok {
53 $point->y('Foo');
54 } '... cannot assign a non-Int to y';
55
56 dies_ok {
57 $point->x(1000);
58 } '... cannot assign to a read-only method';
59 is($point->x, 1, '... got the right (un-changed) value for x');
60
61 $point->clear();
62
63 is($point->x, 0, '... got the right (cleared) value for x');
64 is($point->y, 0, '... got the right (cleared) value for y');
65
66 # check the type constraints on the constructor
67
68 lives_ok {
69 Point->new(x => 0, y => 0);
70 } '... can assign a 0 to x and y';
71
72 dies_ok {
73 Point->new(x => 10, y => 'Foo');
74 } '... cannot assign a non-Int to y';
75
76 dies_ok {
77 Point->new(x => 'Foo', y => 10);
78 } '... cannot assign a non-Int to x';
79
80 # Point3D
81
82 my $point3d = Point3D->new({ x => 10, y => 15, z => 3 });
83 isa_ok($point3d, 'Point3D');
84 isa_ok($point3d, 'Point');
85 isa_ok($point3d, 'Moose::Object');
86
87 is($point3d->x, 10, '... got the right value for x');
88 is($point3d->y, 15, '... got the right value for y');
89 is($point3d->{'z'}, 3, '... got the right value for z');
90
91 dies_ok {
92 $point3d->z;
93 } '... there is no method for z';
94
95 $point3d->clear();
96
97 is($point3d->x, 0, '... got the right (cleared) value for x');
98 is($point3d->y, 0, '... got the right (cleared) value for y');
99 is($point3d->{'z'}, 0, '... got the right (cleared) value for z');
100
101 dies_ok {
102 Point3D->new(x => 10, y => 'Foo', z => 3);
103 } '... cannot assign a non-Int to y';
104
105 dies_ok {
106 Point3D->new(x => 'Foo', y => 10, z => 3);
107 } '... cannot assign a non-Int to x';
108
109 dies_ok {
110 Point3D->new(x => 0, y => 10, z => 'Bar');
111 } '... cannot assign a non-Int to z';
112
113 # test some class introspection
114
115 can_ok('Point', 'meta');
116 isa_ok(Point->meta, 'Moose::Meta::Class');
117
118 can_ok('Point3D', 'meta');
119 isa_ok(Point3D->meta, 'Moose::Meta::Class');
120
121 isnt(Point->meta, Point3D->meta, '... they are different metaclasses as well');
122
123 # poke at Point
124
125 is_deeply(
126 [ Point->meta->superclasses ],
127 [ 'Moose::Object' ],
128 '... Point got the automagic base class');
129
130 my @Point_methods = qw(meta new x y clear);
131 my @Point_attrs = ('x', 'y');
132
133 is_deeply(
134 [ sort @Point_methods ],
135 [ sort Point->meta->get_method_list() ],
136 '... we match the method list for Point');
137
138 is_deeply(
139 [ sort @Point_attrs ],
140 [ sort Point->meta->get_attribute_list() ],
141 '... we match the attribute list for Point');
142
143 foreach my $method (@Point_methods) {
144 ok(Point->meta->has_method($method), '... Point has the method "' . $method . '"');
145 }
146
147 foreach my $attr_name (@Point_attrs ) {
148 ok(Point->meta->has_attribute($attr_name), '... Point has the attribute "' . $attr_name . '"');
149 my $attr = Point->meta->get_attribute($attr_name);
150 ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
151 isa_ok($attr->type_constraint, 'Moose::Meta::TypeConstraint');
152 is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');
153 }
154
155 # poke at Point3D
156
157 is_deeply(
158 [ Point3D->meta->superclasses ],
159 [ 'Point' ],
160 '... Point3D gets the parent given to it');
161
162 my @Point3D_methods = qw(new meta clear);
163 my @Point3D_attrs = ('z');
164
165 is_deeply(
166 [ sort @Point3D_methods ],
167 [ sort Point3D->meta->get_method_list() ],
168 '... we match the method list for Point3D');
169
170 is_deeply(
171 [ sort @Point3D_attrs ],
172 [ sort Point3D->meta->get_attribute_list() ],
173 '... we match the attribute list for Point3D');
174
175 foreach my $method (@Point3D_methods) {
176 ok(Point3D->meta->has_method($method), '... Point3D has the method "' . $method . '"');
177 }
178
179 foreach my $attr_name (@Point3D_attrs ) {
180 ok(Point3D->meta->has_attribute($attr_name), '... Point3D has the attribute "' . $attr_name . '"');
181 my $attr = Point3D->meta->get_attribute($attr_name);
182 ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
183 isa_ok($attr->type_constraint, 'Moose::Meta::TypeConstraint');
184 is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');
185 }
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 24;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 {
13 package BankAccount;
14 use Moose;
15
16 has 'balance' => (isa => 'Num', is => 'rw', default => 0);
17
18 sub deposit {
19 my ($self, $amount) = @_;
20 $self->balance($self->balance + $amount);
21 }
22
23 sub withdraw {
24 my ($self, $amount) = @_;
25 my $current_balance = $self->balance();
26 ($current_balance >= $amount)
27 || confess "Account overdrawn";
28 $self->balance($current_balance - $amount);
29 }
30
31 __PACKAGE__->meta->make_immutable(debug => 0);
32 }{
33 package CheckingAccount;
34 use Moose;
35
36 extends 'BankAccount';
37
38 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
39
40 before 'withdraw' => sub {
41 my ($self, $amount) = @_;
42 my $overdraft_amount = $amount - $self->balance();
43 if ($self->overdraft_account && $overdraft_amount > 0) {
44 $self->overdraft_account->withdraw($overdraft_amount);
45 $self->deposit($overdraft_amount);
46 }
47 };
48
49 __PACKAGE__->meta->make_immutable(debug => 0);
50 }
51
52 my $savings_account = BankAccount->new(balance => 250);
53 isa_ok($savings_account, 'BankAccount');
54
55 is($savings_account->balance, 250, '... got the right savings balance');
56 lives_ok {
57 $savings_account->withdraw(50);
58 } '... withdrew from savings successfully';
59 is($savings_account->balance, 200, '... got the right savings balance after withdrawl');
60
61 $savings_account->deposit(150);
62 is($savings_account->balance, 350, '... got the right savings balance after deposit');
63
64 {
65 my $checking_account = CheckingAccount->new(
66 balance => 100,
67 overdraft_account => $savings_account
68 );
69 isa_ok($checking_account, 'CheckingAccount');
70 isa_ok($checking_account, 'BankAccount');
71
72 is($checking_account->overdraft_account, $savings_account, '... got the right overdraft account');
73
74 is($checking_account->balance, 100, '... got the right checkings balance');
75
76 lives_ok {
77 $checking_account->withdraw(50);
78 } '... withdrew from checking successfully';
79 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl');
80 is($savings_account->balance, 350, '... got the right savings balance after checking withdrawl (no overdraft)');
81
82 lives_ok {
83 $checking_account->withdraw(200);
84 } '... withdrew from checking successfully';
85 is($checking_account->balance, 0, '... got the right checkings balance after withdrawl');
86 is($savings_account->balance, 200, '... got the right savings balance after overdraft withdrawl');
87 }
88
89 {
90 my $checking_account = CheckingAccount->new(
91 balance => 100
92 # no overdraft account
93 );
94 isa_ok($checking_account, 'CheckingAccount');
95 isa_ok($checking_account, 'BankAccount');
96
97 is($checking_account->overdraft_account, undef, '... no overdraft account');
98
99 is($checking_account->balance, 100, '... got the right checkings balance');
100
101 lives_ok {
102 $checking_account->withdraw(50);
103 } '... withdrew from checking successfully';
104 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl');
105
106 dies_ok {
107 $checking_account->withdraw(200);
108 } '... withdrawl failed due to attempted overdraft';
109 is($checking_account->balance, 50, '... got the right checkings balance after withdrawl failure');
110 }
111
112
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 34;
6 use Test::Exception;
7
8 use Scalar::Util 'isweak';
9
10 BEGIN {
11 use_ok('Moose');
12 }
13
14 {
15 package BinaryTree;
16 use Moose;
17
18 has 'node' => (is => 'rw', isa => 'Any');
19
20 has 'parent' => (
21 is => 'rw',
22 isa => 'BinaryTree',
23 predicate => 'has_parent',
24 weak_ref => 1,
25 );
26
27 has 'left' => (
28 is => 'rw',
29 isa => 'BinaryTree',
30 predicate => 'has_left',
31 lazy => 1,
32 default => sub { BinaryTree->new(parent => $_[0]) },
33 );
34
35 has 'right' => (
36 is => 'rw',
37 isa => 'BinaryTree',
38 predicate => 'has_right',
39 lazy => 1,
40 default => sub { BinaryTree->new(parent => $_[0]) },
41 );
42
43 before 'right', 'left' => sub {
44 my ($self, $tree) = @_;
45 $tree->parent($self) if defined $tree;
46 };
47
48 __PACKAGE__->meta->make_immutable(debug => 0);
49 }
50
51 my $root = BinaryTree->new(node => 'root');
52 isa_ok($root, 'BinaryTree');
53
54 is($root->node, 'root', '... got the right node value');
55
56 ok(!$root->has_left, '... no left node yet');
57 ok(!$root->has_right, '... no right node yet');
58
59 ok(!$root->has_parent, '... no parent for root node');
60
61 # make a left node
62
63 my $left = $root->left;
64 isa_ok($left, 'BinaryTree');
65
66 is($root->left, $left, '... got the same node (and it is $left)');
67 ok($root->has_left, '... we have a left node now');
68
69 ok($left->has_parent, '... lefts has a parent');
70 is($left->parent, $root, '... lefts parent is the root');
71
72 ok(isweak($left->{parent}), '... parent is a weakened ref');
73
74 ok(!$left->has_left, '... $left no left node yet');
75 ok(!$left->has_right, '... $left no right node yet');
76
77 is($left->node, undef, '... left has got no node value');
78
79 lives_ok {
80 $left->node('left')
81 } '... assign to lefts node';
82
83 is($left->node, 'left', '... left now has a node value');
84
85 # make a right node
86
87 ok(!$root->has_right, '... still no right node yet');
88
89 is($root->right->node, undef, '... right has got no node value');
90
91 ok($root->has_right, '... now we have a right node');
92
93 my $right = $root->right;
94 isa_ok($right, 'BinaryTree');
95
96 lives_ok {
97 $right->node('right')
98 } '... assign to rights node';
99
100 is($right->node, 'right', '... left now has a node value');
101
102 is($root->right, $right, '... got the same node (and it is $right)');
103 ok($root->has_right, '... we have a right node now');
104
105 ok($right->has_parent, '... rights has a parent');
106 is($right->parent, $root, '... rights parent is the root');
107
108 ok(isweak($right->{parent}), '... parent is a weakened ref');
109
110 my $left_left = $left->left;
111 isa_ok($left_left, 'BinaryTree');
112
113 ok($left_left->has_parent, '... left does have a parent');
114
115 is($left_left->parent, $left, '... got a parent node (and it is $left)');
116 ok($left->has_left, '... we have a left node now');
117 is($left->left, $left_left, '... got a left node (and it is $left_left)');
118
119 ok(isweak($left_left->{parent}), '... parent is a weakened ref');
120
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More;
6
7 BEGIN {
8 eval "use Regexp::Common; use Locale::US;";
9 plan skip_all => "Regexp::Common & Locale::US required for this test" if $@;
10 plan tests => 66;
11 }
12
13 use Test::Exception;
14 use Scalar::Util 'isweak';
15
16 BEGIN {
17 use_ok('Moose');
18 }
19
20 {
21 package Address;
22 use Moose;
23 use Moose::Util::TypeConstraints;
24
25 use Locale::US;
26 use Regexp::Common 'zip';
27
28 my $STATES = Locale::US->new;
29
30 subtype USState
31 => as Str
32 => where {
33 (exists $STATES->{code2state}{uc($_)} || exists $STATES->{state2code}{uc($_)})
34 };
35
36 subtype USZipCode
37 => as Value
38 => where {
39 /^$RE{zip}{US}{-extended => 'allow'}$/
40 };
41
42 has 'street' => (is => 'rw', isa => 'Str');
43 has 'city' => (is => 'rw', isa => 'Str');
44 has 'state' => (is => 'rw', isa => 'USState');
45 has 'zip_code' => (is => 'rw', isa => 'USZipCode');
46
47 __PACKAGE__->meta->make_immutable(debug => 0);
48 }{
49
50 package Company;
51 use Moose;
52 use Moose::Util::TypeConstraints;
53
54 has 'name' => (is => 'rw', isa => 'Str', required => 1);
55 has 'address' => (is => 'rw', isa => 'Address');
56 has 'employees' => (is => 'rw', isa => 'ArrayRef[Employee]');
57
58 sub BUILD {
59 my ($self, $params) = @_;
60 if ($params->{employees}) {
61 foreach my $employee (@{$params->{employees}}) {
62 $employee->company($self);
63 }
64 }
65 }
66
67 sub get_employee_count { scalar @{(shift)->employees} }
68
69 after 'employees' => sub {
70 my ($self, $employees) = @_;
71 # if employees is defined, it
72 # has already been type checked
73 if (defined $employees) {
74 # make sure each gets the
75 # weak ref to the company
76 foreach my $employee (@{$employees}) {
77 $employee->company($self);
78 }
79 }
80 };
81
82 __PACKAGE__->meta->make_immutable(debug => 0);
83 }{
84
85 package Person;
86 use Moose;
87
88 has 'first_name' => (is => 'rw', isa => 'Str', required => 1);
89 has 'last_name' => (is => 'rw', isa => 'Str', required => 1);
90 has 'middle_initial' => (is => 'rw', isa => 'Str', predicate => 'has_middle_initial');
91 has 'address' => (is => 'rw', isa => 'Address');
92
93 sub full_name {
94 my $self = shift;
95 return $self->first_name .
96 ($self->has_middle_initial ? ' ' . $self->middle_initial . '. ' : ' ') .
97 $self->last_name;
98 }
99
100 __PACKAGE__->meta->make_immutable(debug => 0);
101 }{
102
103 package Employee;
104 use Moose;
105
106 extends 'Person';
107
108 has 'title' => (is => 'rw', isa => 'Str', required => 1);
109 has 'company' => (is => 'rw', isa => 'Company', weak_ref => 1);
110
111 override 'full_name' => sub {
112 my $self = shift;
113 super() . ', ' . $self->title
114 };
115
116 __PACKAGE__->meta->make_immutable(debug => 0);
117 }
118
119 my $ii;
120 lives_ok {
121 $ii = Company->new({
122 name => 'Infinity Interactive',
123 address => Address->new(
124 street => '565 Plandome Rd., Suite 307',
125 city => 'Manhasset',
126 state => 'NY',
127 zip_code => '11030'
128 ),
129 employees => [
130 Employee->new(
131 first_name => 'Jeremy',
132 last_name => 'Shao',
133 title => 'President / Senior Consultant',
134 address => Address->new(city => 'Manhasset', state => 'NY')
135 ),
136 Employee->new(
137 first_name => 'Tommy',
138 last_name => 'Lee',
139 title => 'Vice President / Senior Developer',
140 address => Address->new(city => 'New York', state => 'NY')
141 ),
142 Employee->new(
143 first_name => 'Stevan',
144 middle_initial => 'C',
145 last_name => 'Little',
146 title => 'Senior Developer',
147 address => Address->new(city => 'Madison', state => 'CT')
148 ),
149 ]
150 });
151 } '... created the entire company successfully';
152 isa_ok($ii, 'Company');
153
154 is($ii->name, 'Infinity Interactive', '... got the right name for the company');
155
156 isa_ok($ii->address, 'Address');
157 is($ii->address->street, '565 Plandome Rd., Suite 307', '... got the right street address');
158 is($ii->address->city, 'Manhasset', '... got the right city');
159 is($ii->address->state, 'NY', '... got the right state');
160 is($ii->address->zip_code, 11030, '... got the zip code');
161
162 is($ii->get_employee_count, 3, '... got the right employee count');
163
164 # employee #1
165
166 isa_ok($ii->employees->[0], 'Employee');
167 isa_ok($ii->employees->[0], 'Person');
168
169 is($ii->employees->[0]->first_name, 'Jeremy', '... got the right first name');
170 is($ii->employees->[0]->last_name, 'Shao', '... got the right last name');
171 ok(!$ii->employees->[0]->has_middle_initial, '... no middle initial');
172 is($ii->employees->[0]->middle_initial, undef, '... got the right middle initial value');
173 is($ii->employees->[0]->full_name, 'Jeremy Shao, President / Senior Consultant', '... got the right full name');
174 is($ii->employees->[0]->title, 'President / Senior Consultant', '... got the right title');
175 is($ii->employees->[0]->company, $ii, '... got the right company');
176 ok(isweak($ii->employees->[0]->{company}), '... the company is a weak-ref');
177
178 isa_ok($ii->employees->[0]->address, 'Address');
179 is($ii->employees->[0]->address->city, 'Manhasset', '... got the right city');
180 is($ii->employees->[0]->address->state, 'NY', '... got the right state');
181
182 # employee #2
183
184 isa_ok($ii->employees->[1], 'Employee');
185 isa_ok($ii->employees->[1], 'Person');
186
187 is($ii->employees->[1]->first_name, 'Tommy', '... got the right first name');
188 is($ii->employees->[1]->last_name, 'Lee', '... got the right last name');
189 ok(!$ii->employees->[1]->has_middle_initial, '... no middle initial');
190 is($ii->employees->[1]->middle_initial, undef, '... got the right middle initial value');
191 is($ii->employees->[1]->full_name, 'Tommy Lee, Vice President / Senior Developer', '... got the right full name');
192 is($ii->employees->[1]->title, 'Vice President / Senior Developer', '... got the right title');
193 is($ii->employees->[1]->company, $ii, '... got the right company');
194 ok(isweak($ii->employees->[1]->{company}), '... the company is a weak-ref');
195
196 isa_ok($ii->employees->[1]->address, 'Address');
197 is($ii->employees->[1]->address->city, 'New York', '... got the right city');
198 is($ii->employees->[1]->address->state, 'NY', '... got the right state');
199
200 # employee #3
201
202 isa_ok($ii->employees->[2], 'Employee');
203 isa_ok($ii->employees->[2], 'Person');
204
205 is($ii->employees->[2]->first_name, 'Stevan', '... got the right first name');
206 is($ii->employees->[2]->last_name, 'Little', '... got the right last name');
207 ok($ii->employees->[2]->has_middle_initial, '... got middle initial');
208 is($ii->employees->[2]->middle_initial, 'C', '... got the right middle initial value');
209 is($ii->employees->[2]->full_name, 'Stevan C. Little, Senior Developer', '... got the right full name');
210 is($ii->employees->[2]->title, 'Senior Developer', '... got the right title');
211 is($ii->employees->[2]->company, $ii, '... got the right company');
212 ok(isweak($ii->employees->[2]->{company}), '... the company is a weak-ref');
213
214 isa_ok($ii->employees->[2]->address, 'Address');
215 is($ii->employees->[2]->address->city, 'Madison', '... got the right city');
216 is($ii->employees->[2]->address->state, 'CT', '... got the right state');
217
218 # create new company
219
220 my $new_company = Company->new(name => 'Infinity Interactive International');
221 isa_ok($new_company, 'Company');
222
223 my $ii_employees = $ii->employees;
224 foreach my $employee (@$ii_employees) {
225 is($employee->company, $ii, '... has the ii company');
226 }
227
228 $new_company->employees($ii_employees);
229
230 foreach my $employee (@{$new_company->employees}) {
231 is($employee->company, $new_company, '... has the different company now');
232 }
233
234 ## check some error conditions for the subtypes
235
236 dies_ok {
237 Address->new(street => {}),
238 } '... we die correctly with bad args';
239
240 dies_ok {
241 Address->new(city => {}),
242 } '... we die correctly with bad args';
243
244 dies_ok {
245 Address->new(state => 'British Columbia'),
246 } '... we die correctly with bad args';
247
248 lives_ok {
249 Address->new(state => 'Connecticut'),
250 } '... we live correctly with good args';
251
252 dies_ok {
253 Address->new(zip_code => 'AF5J6$'),
254 } '... we die correctly with bad args';
255
256 lives_ok {
257 Address->new(zip_code => '06443'),
258 } '... we live correctly with good args';
259
260 dies_ok {
261 Company->new(),
262 } '... we die correctly without good args';
263
264 lives_ok {
265 Company->new(name => 'Foo'),
266 } '... we live correctly without good args';
267
268 dies_ok {
269 Company->new(name => 'Foo', employees => [ Person->new ]),
270 } '... we die correctly with good args';
271
272 lives_ok {
273 Company->new(name => 'Foo', employees => []),
274 } '... we live correctly with good args';
275
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More;
6
7 BEGIN {
8 eval "use HTTP::Headers; use Params::Coerce; use URI;";
9 plan skip_all => "HTTP::Headers & Params::Coerce & URI required for this test" if $@;
10 plan tests => 18;
11 }
12
13 use Test::Exception;
14
15 BEGIN {
16 use_ok('Moose');
17 }
18
19 {
20 package Request;
21 use Moose;
22 use Moose::Util::TypeConstraints;
23
24 use HTTP::Headers ();
25 use Params::Coerce ();
26 use URI ();
27
28 subtype Header
29 => as Object
30 => where { $_->isa('HTTP::Headers') };
31
32 coerce Header
33 => from ArrayRef
34 => via { HTTP::Headers->new( @{ $_ } ) }
35 => from HashRef
36 => via { HTTP::Headers->new( %{ $_ } ) };
37
38 subtype Uri
39 => as Object
40 => where { $_->isa('URI') };
41
42 coerce Uri
43 => from Object
44 => via { $_->isa('URI') ? $_ : Params::Coerce::coerce( 'URI', $_ ) }
45 => from Str
46 => via { URI->new( $_, 'http' ) };
47
48 subtype Protocol
49 => as Str
50 => where { /^HTTP\/[0-9]\.[0-9]$/ };
51
52
53 has 'base' => (is => 'rw', isa => 'Uri', coerce => 1);
54 has 'url' => (is => 'rw', isa => 'Uri', coerce => 1);
55 has 'method' => (is => 'rw', isa => 'Str');
56 has 'protocol' => (is => 'rw', isa => 'Protocol');
57 has 'headers' => (
58 is => 'rw',
59 isa => 'Header',
60 coerce => 1,
61 default => sub { HTTP::Headers->new }
62 );
63
64 __PACKAGE__->meta->make_immutable(debug => 0);
65 }
66
67 my $r = Request->new;
68 isa_ok($r, 'Request');
69
70 {
71 my $header = $r->headers;
72 isa_ok($header, 'HTTP::Headers');
73
74 is($r->headers->content_type, '', '... got no content type in the header');
75
76 $r->headers( { content_type => 'text/plain' } );
77
78 my $header2 = $r->headers;
79 isa_ok($header2, 'HTTP::Headers');
80 isnt($header, $header2, '... created a new HTTP::Header object');
81
82 is($header2->content_type, 'text/plain', '... got the right content type in the header');
83
84 $r->headers( [ content_type => 'text/html' ] );
85
86 my $header3 = $r->headers;
87 isa_ok($header3, 'HTTP::Headers');
88 isnt($header2, $header3, '... created a new HTTP::Header object');
89
90 is($header3->content_type, 'text/html', '... got the right content type in the header');
91
92 $r->headers( HTTP::Headers->new(content_type => 'application/pdf') );
93
94 my $header4 = $r->headers;
95 isa_ok($header4, 'HTTP::Headers');
96 isnt($header3, $header4, '... created a new HTTP::Header object');
97
98 is($header4->content_type, 'application/pdf', '... got the right content type in the header');
99
100 dies_ok {
101 $r->headers('Foo')
102 } '... dies when it gets bad params';
103 }
104
105 {
106 is($r->protocol, undef, '... got nothing by default');
107
108 lives_ok {
109 $r->protocol('HTTP/1.0');
110 } '... set the protocol correctly';
111 is($r->protocol, 'HTTP/1.0', '... got nothing by default');
112
113 dies_ok {
114 $r->protocol('http/1.0');
115 } '... the protocol died with bar params correctly';
116 }
117
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 3;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## Augment/Inner
13
14 {
15 package Document::Page;
16 use Moose;
17
18 has 'body' => (is => 'rw', isa => 'Str', default => sub {''});
19
20 sub create {
21 my $self = shift;
22 $self->open_page;
23 inner();
24 $self->close_page;
25 }
26
27 sub append_body {
28 my ($self, $appendage) = @_;
29 $self->body($self->body . $appendage);
30 }
31
32 sub open_page { (shift)->append_body('<page>') }
33 sub close_page { (shift)->append_body('</page>') }
34
35 package Document::PageWithHeadersAndFooters;
36 use Moose;
37
38 extends 'Document::Page';
39
40 augment 'create' => sub {
41 my $self = shift;
42 $self->create_header;
43 inner();
44 $self->create_footer;
45 };
46
47 sub create_header { (shift)->append_body('<header/>') }
48 sub create_footer { (shift)->append_body('<footer/>') }
49
50 package TPSReport;
51 use Moose;
52
53 extends 'Document::PageWithHeadersAndFooters';
54
55 augment 'create' => sub {
56 my $self = shift;
57 $self->create_tps_report;
58 };
59
60 sub create_tps_report {
61 (shift)->append_body('<report type="tps"/>')
62 }
63 }
64
65 my $tps_report = TPSReport->new;
66 isa_ok($tps_report, 'TPSReport');
67
68 is(
69 $tps_report->create,
70 q{<page><header/><report type="tps"/><footer/></page>},
71 '... got the right TPS report');
72
73
74
75
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 {
6 package Human;
7
8 use Moose;
9 use Moose::Util::TypeConstraints;
10
11 subtype 'EyeColor'
12 => as 'Object'
13 => where { $_->isa('Human::EyeColor') };
14
15 coerce 'EyeColor'
16 => from 'ArrayRef'
17 => via {
18 return Human::EyeColor->new(
19 bey2_1 => $_->[0],
20 bey2_2 => $_->[1],
21 gey_1 => $_->[2],
22 gey_2 => $_->[3],
23 );
24 };
25
26 subtype 'Gender'
27 => as 'Str'
28 => where { $_ =~ m{^[mf]$}s };
29
30 has 'gender' => ( is => 'ro', isa => 'Gender', required => 1 );
31
32 has 'eye_color' => ( is => 'ro', isa => 'EyeColor', coerce => 1, required => 1 );
33
34 has 'mother' => ( is => 'ro', isa => 'Human' );
35 has 'father' => ( is => 'ro', isa => 'Human' );
36
37 use overload '+' => \&_overload_add, fallback => 1;
38
39 sub _overload_add {
40 my ($one, $two) = @_;
41
42 die('Only male and female humans may have children')
43 if ($one->gender() eq $two->gender());
44
45 my ( $mother, $father ) = ( $one->gender eq 'f' ? ($one, $two) : ($two, $one) );
46
47 my $gender = 'f';
48 $gender = 'm' if (rand() >= 0.5);
49
50 # Would be better to use Crypt::Random.
51 #use Crypt::Random qw( makerandom );
52 #$gender = 'm' if (makerandom( Size => 1, Strength => 1, Uniform => 1 ));
53
54 return Human->new(
55 gender => $gender,
56 eye_color => ( $one->eye_color() + $two->eye_color() ),
57 mother => $mother,
58 father => $father,
59 );
60 }
61 }
62
63 {
64 package Human::EyeColor;
65
66 use Moose;
67 use Moose::Util::TypeConstraints;
68
69 subtype 'bey2Gene'
70 => as 'Object'
71 => where { $_->isa('Human::Gene::bey2') };
72
73 coerce 'bey2Gene'
74 => from 'Str'
75 => via { Human::Gene::bey2->new( color => $_ ) };
76
77 subtype 'geyGene'
78 => as 'Object'
79 => where { $_->isa('Human::Gene::gey') };
80
81 coerce 'geyGene'
82 => from 'Str'
83 => via { Human::Gene::gey->new( color => $_ ) };
84
85 has 'bey2_1' => ( is => 'ro', isa => 'bey2Gene', coerce => 1 );
86 has 'bey2_2' => ( is => 'ro', isa => 'bey2Gene', coerce => 1 );
87
88 has 'gey_1' => ( is => 'ro', isa => 'geyGene', coerce => 1 );
89 has 'gey_2' => ( is => 'ro', isa => 'geyGene', coerce => 1 );
90
91 use overload '+' => \&_overload_add, fallback => 1;
92 use overload '""' => \&color, fallback => 1;
93
94 sub color {
95 my ( $self ) = @_;
96
97 return 'brown' if ($self->bey2_1->color() eq 'brown' or $self->bey2_2->color() eq 'brown');
98 return 'green' if ($self->gey_1->color() eq 'green' or $self->gey_2->color() eq 'green');
99 return 'blue';
100 }
101
102 sub _overload_add {
103 my ($one, $two) = @_;
104
105 my $one_bey2 = 'bey2_' . _rand2();
106 my $two_bey2 = 'bey2_' . _rand2();
107
108 my $one_gey = 'gey_' . _rand2();
109 my $two_gey = 'gey_' . _rand2();
110
111 return Human::EyeColor->new(
112 bey2_1 => $one->$one_bey2->color(),
113 bey2_2 => $two->$two_bey2->color(),
114 gey_1 => $one->$one_gey->color(),
115 gey_2 => $two->$two_gey->color(),
116 );
117 }
118
119 sub _rand2 {
120 # Would be better to use Crypt::Random.
121 #use Crypt::Random qw( makerandom );
122 #return 1 + makerandom( Size => 1, Strength => 1, Uniform => 1 );
123 return 1 + int( rand(2) );
124 }
125 }
126
127 {
128 package Human::Gene::bey2;
129
130 use Moose;
131 use Moose::Util::TypeConstraints;
132
133 type 'bey2Color' => where { $_ =~ m{^(?:brown|blue)$}s };
134
135 has 'color' => ( is => 'ro', isa => 'bey2Color' );
136 }
137
138 {
139 package Human::Gene::gey;
140
141 use Moose;
142 use Moose::Util::TypeConstraints;
143
144 type 'geyColor' => where { $_ =~ m{^(?:green|blue)$}s };
145
146 has 'color' => ( is => 'ro', isa => 'geyColor' );
147 }
148
149 use Test::More tests => 10;
150
151 my $gene_color_sets = [
152 [qw( blue blue blue blue ) => 'blue'],
153 [qw( blue blue green blue ) => 'green'],
154 [qw( blue blue blue green ) => 'green'],
155 [qw( blue blue green green ) => 'green'],
156 [qw( brown blue blue blue ) => 'brown'],
157 [qw( brown brown green green ) => 'brown'],
158 [qw( blue brown green blue ) => 'brown'],
159 ];
160
161 foreach my $set (@$gene_color_sets) {
162 my $expected_color = pop( @$set );
163 my $person = Human->new(
164 gender => 'f',
165 eye_color => $set,
166 );
167 is(
168 $person->eye_color(),
169 $expected_color,
170 'gene combination '.join(',',@$set).' produces '.$expected_color.' eye color',
171 );
172 }
173
174 my $parent_sets = [
175 [ [qw( blue blue blue blue )], [qw( blue blue blue blue )] => 'blue' ],
176 [ [qw( blue blue blue blue )], [qw( brown brown green blue )] => 'brown' ],
177 [ [qw( blue blue green green )], [qw( blue blue green green )] => 'green' ],
178 ];
179
180 foreach my $set (@$parent_sets) {
181 my $expected_color = pop( @$set );
182 my $mother = Human->new(
183 gender => 'f',
184 eye_color => shift(@$set),
185 );
186 my $father = Human->new(
187 gender => 'm',
188 eye_color => shift(@$set),
189 );
190 my $child = $mother + $father;
191 is(
192 $child->eye_color(),
193 $expected_color,
194 'mother '.$mother->eye_color().' + father '.$father->eye_color().' = child '.$expected_color,
195 );
196 }
197
198 # Hmm, not sure how to test for random selection of genes since
199 # I could theoretically run an infinite number of iterations and
200 # never find proof that a child has inherited a particular gene.
201
202 # AUTHOR: Aran Clary Deltac <bluefeet@cpan.org>
203
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 2;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## meta-attribute example
13 {
14
15 package MyApp::Meta::Attribute::Labeled;
16 use Moose;
17 extends 'Moose::Meta::Attribute';
18
19 has label => (
20 is => 'rw',
21 isa => 'Str',
22 predicate => 'has_label',
23 );
24
25 package Moose::Meta::Attribute::Custom::Labeled;
26 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
27
28 package MyApp::Website;
29 use Moose;
30
31 has url => (
32 metaclass => 'Labeled',
33 isa => 'Str',
34 is => 'rw',
35 label => "The site's URL",
36 );
37
38 has name => (
39 is => 'rw',
40 isa => 'Str',
41 );
42
43 sub dump {
44 my $self = shift;
45
46 my $dump_value = '';
47
48 # iterate over all the attributes in $self
49 my %attributes = %{ $self->meta->get_attribute_map };
50 foreach my $name (sort keys %attributes) {
51
52 my $attribute = $attributes{$name};
53
54 # print the label if available
55 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
56 && $attribute->has_label) {
57 $dump_value .= $attribute->label;
58 }
59 # otherwise print the name
60 else {
61 $dump_value .= $name;
62 }
63
64 # print the attribute's value
65 my $reader = $attribute->get_read_method;
66 $dump_value .= ": " . $self->$reader . "\n";
67 }
68
69 return $dump_value;
70 }
71
72 }
73
74 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
75 is($app->dump, q{name: Google
76 The site's URL: http://google.com
77 }, '... got the expected dump value');
78
79
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 3;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## attribute trait example
13 {
14
15 package MyApp::Meta::Attribute::Trait::Labeled;
16 use Moose::Role;
17
18 has label => (
19 is => 'rw',
20 isa => 'Str',
21 predicate => 'has_label',
22 );
23
24 package Moose::Meta::Attribute::Custom::Trait::Labeled;
25 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
26
27 package MyApp::Website;
28 use Moose;
29
30 has url => (
31 traits => [qw/Labeled/],
32 isa => 'Str',
33 is => 'rw',
34 label => "The site's URL",
35 );
36
37 has name => (
38 is => 'rw',
39 isa => 'Str',
40 );
41
42 sub dump {
43 my $self = shift;
44
45 my $dump_value = '';
46
47 # iterate over all the attributes in $self
48 my %attributes = %{ $self->meta->get_attribute_map };
49 foreach my $name (sort keys %attributes) {
50
51 my $attribute = $attributes{$name};
52
53 # print the label if available
54 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
55 && $attribute->has_label) {
56 $dump_value .= $attribute->label;
57 }
58 # otherwise print the name
59 else {
60 $dump_value .= $name;
61 }
62
63 # print the attribute's value
64 my $reader = $attribute->get_read_method;
65 $dump_value .= ": " . $self->$reader . "\n";
66 }
67
68 return $dump_value;
69 }
70
71 }
72
73 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
74 is($app->dump, q{name: Google
75 The site's URL: http://google.com
76 }, '... got the expected dump value');
77
78 # using the trait directly in a regular metaclass
79 {
80 package MyApp::Meta::Attribute::Labeled;
81 use Moose;
82 extends 'Moose::Meta::Attribute';
83 with 'MyApp::Meta::Attribute::Trait::Labeled';
84
85 package Moose::Meta::Attribute::Custom::Labeled;
86 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
87
88 package MyApp::Website2;
89 use Moose;
90
91 has url => (
92 metaclass => 'Labeled',
93 isa => 'Str',
94 is => 'rw',
95 label => "The site's URL",
96 );
97
98 has name => (
99 is => 'rw',
100 isa => 'Str',
101 );
102
103 sub dump {
104 my $self = shift;
105
106 my $dump_value = '';
107
108 # iterate over all the attributes in $self
109 my %attributes = %{ $self->meta->get_attribute_map };
110 foreach my $name (sort keys %attributes) {
111
112 my $attribute = $attributes{$name};
113
114 # print the label if available
115 if ($attribute->isa('MyApp::Meta::Attribute::Labeled')
116 && $attribute->has_label) {
117 $dump_value .= $attribute->label;
118 }
119 # otherwise print the name
120 else {
121 $dump_value .= $name;
122 }
123
124 # print the attribute's value
125 my $reader = $attribute->get_read_method;
126 $dump_value .= ": " . $self->$reader . "\n";
127 }
128
129 return $dump_value;
130 }
131
132 }
133
134 my $app2 = MyApp::Website2->new(url => "http://google.com", name => "Google");
135 is($app2->dump, q{name: Google
136 The site's URL: http://google.com
137 }, '... got the expected dump value');
138
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 64;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 ## Roles
13
14 {
15 package Eq;
16 use Moose::Role;
17
18 requires 'equal_to';
19
20 sub not_equal_to {
21 my ($self, $other) = @_;
22 not $self->equal_to($other);
23 }
24
25 package Comparable;
26 use Moose::Role;
27
28 with 'Eq';
29
30 requires 'compare';
31
32 sub equal_to {
33 my ($self, $other) = @_;
34 $self->compare($other) == 0;
35 }
36
37 sub greater_than {
38 my ($self, $other) = @_;
39 $self->compare($other) == 1;
40 }
41
42 sub less_than {
43 my ($self, $other) = @_;
44 $self->compare($other) == -1;
45 }
46
47 sub greater_than_or_equal_to {
48 my ($self, $other) = @_;
49 $self->greater_than($other) || $self->equal_to($other);
50 }
51
52 sub less_than_or_equal_to {
53 my ($self, $other) = @_;
54 $self->less_than($other) || $self->equal_to($other);
55 }
56
57 package Printable;
58 use Moose::Role;
59
60 requires 'to_string';
61 }
62
63 ## Classes
64
65 {
66 package US::Currency;
67 use Moose;
68
69 with 'Comparable', 'Printable';
70
71 has 'amount' => (is => 'rw', isa => 'Num', default => 0);
72
73 sub compare {
74 my ($self, $other) = @_;
75 $self->amount <=> $other->amount;
76 }
77
78 sub to_string {
79 my $self = shift;
80 sprintf '$%0.2f USD' => $self->amount
81 }
82
83 __PACKAGE__->meta->make_immutable(debug => 0);
84 }
85
86 ok(US::Currency->does('Comparable'), '... US::Currency does Comparable');
87 ok(US::Currency->does('Eq'), '... US::Currency does Eq');
88 ok(US::Currency->does('Printable'), '... US::Currency does Printable');
89
90 my $hundred = US::Currency->new(amount => 100.00);
91 isa_ok($hundred, 'US::Currency');
92
93 ok( $hundred->DOES("US::Currency"), "UNIVERSAL::DOES for class" );
94 ok( $hundred->DOES("Comparable"), "UNIVERSAL::DOES for role" );
95
96 can_ok($hundred, 'amount');
97 is($hundred->amount, 100, '... got the right amount');
98
99 can_ok($hundred, 'to_string');
100 is($hundred->to_string, '$100.00 USD', '... got the right stringified value');
101
102 ok($hundred->does('Comparable'), '... US::Currency does Comparable');
103 ok($hundred->does('Eq'), '... US::Currency does Eq');
104 ok($hundred->does('Printable'), '... US::Currency does Printable');
105
106 my $fifty = US::Currency->new(amount => 50.00);
107 isa_ok($fifty, 'US::Currency');
108
109 can_ok($fifty, 'amount');
110 is($fifty->amount, 50, '... got the right amount');
111
112 can_ok($fifty, 'to_string');
113 is($fifty->to_string, '$50.00 USD', '... got the right stringified value');
114
115 ok($hundred->greater_than($fifty), '... 100 gt 50');
116 ok($hundred->greater_than_or_equal_to($fifty), '... 100 ge 50');
117 ok(!$hundred->less_than($fifty), '... !100 lt 50');
118 ok(!$hundred->less_than_or_equal_to($fifty), '... !100 le 50');
119 ok(!$hundred->equal_to($fifty), '... !100 eq 50');
120 ok($hundred->not_equal_to($fifty), '... 100 ne 50');
121
122 ok(!$fifty->greater_than($hundred), '... !50 gt 100');
123 ok(!$fifty->greater_than_or_equal_to($hundred), '... !50 ge 100');
124 ok($fifty->less_than($hundred), '... 50 lt 100');
125 ok($fifty->less_than_or_equal_to($hundred), '... 50 le 100');
126 ok(!$fifty->equal_to($hundred), '... !50 eq 100');
127 ok($fifty->not_equal_to($hundred), '... 50 ne 100');
128
129 ok(!$fifty->greater_than($fifty), '... !50 gt 50');
130 ok($fifty->greater_than_or_equal_to($fifty), '... !50 ge 50');
131 ok(!$fifty->less_than($fifty), '... 50 lt 50');
132 ok($fifty->less_than_or_equal_to($fifty), '... 50 le 50');
133 ok($fifty->equal_to($fifty), '... 50 eq 50');
134 ok(!$fifty->not_equal_to($fifty), '... !50 ne 50');
135
136 ## ... check some meta-stuff
137
138 # Eq
139
140 my $eq_meta = Eq->meta;
141 isa_ok($eq_meta, 'Moose::Meta::Role');
142
143 ok($eq_meta->has_method('not_equal_to'), '... Eq has_method not_equal_to');
144 ok($eq_meta->requires_method('equal_to'), '... Eq requires_method not_equal_to');
145
146 # Comparable
147
148 my $comparable_meta = Comparable->meta;
149 isa_ok($comparable_meta, 'Moose::Meta::Role');
150
151 ok($comparable_meta->does_role('Eq'), '... Comparable does Eq');
152
153 foreach my $method_name (qw(
154 equal_to not_equal_to
155 greater_than greater_than_or_equal_to
156 less_than less_than_or_equal_to
157 )) {
158 ok($comparable_meta->has_method($method_name), '... Comparable has_method ' . $method_name);
159 }
160
161 ok($comparable_meta->requires_method('compare'), '... Comparable requires_method compare');
162
163 # Printable
164
165 my $printable_meta = Printable->meta;
166 isa_ok($printable_meta, 'Moose::Meta::Role');
167
168 ok($printable_meta->requires_method('to_string'), '... Printable requires_method to_string');
169
170 # US::Currency
171
172 my $currency_meta = US::Currency->meta;
173 isa_ok($currency_meta, 'Moose::Meta::Class');
174
175 ok($currency_meta->does_role('Comparable'), '... US::Currency does Comparable');
176 ok($currency_meta->does_role('Eq'), '... US::Currency does Eq');
177 ok($currency_meta->does_role('Printable'), '... US::Currency does Printable');
178
179 foreach my $method_name (qw(
180 amount
181 equal_to not_equal_to
182 compare
183 greater_than greater_than_or_equal_to
184 less_than less_than_or_equal_to
185 to_string
186 )) {
187 ok($currency_meta->has_method($method_name), '... US::Currency has_method ' . $method_name);
188 }
189
0 use strict;
1 use warnings;
2 use Test::More tests => 5;
3 use Class::MOP;
4
5 # This is copied directly from recipe 11
6 {
7 package Restartable;
8 use Moose::Role;
9
10 has 'is_paused' => (
11 is => 'rw',
12 isa => 'Bool',
13 default => 0,
14 );
15
16 requires 'save_state', 'load_state';
17
18 sub stop { }
19
20 sub start { }
21
22 package Restartable::ButUnreliable;
23 use Moose::Role;
24
25 with 'Restartable' => {
26 alias => {
27 stop => '_stop',
28 start => '_start'
29 }
30 };
31
32 sub stop {
33 my $self = shift;
34
35 $self->explode() if rand(1) > .5;
36
37 $self->_stop();
38 }
39
40 sub start {
41 my $self = shift;
42
43 $self->explode() if rand(1) > .5;
44
45 $self->_start();
46 }
47
48 package Restartable::ButBroken;
49 use Moose::Role;
50
51 with 'Restartable' => { excludes => [ 'stop', 'start' ] };
52
53 sub stop {
54 my $self = shift;
55
56 $self->explode();
57 }
58
59 sub start {
60 my $self = shift;
61
62 $self->explode();
63 }
64 }
65
66 # This is the actual tests
67 {
68 my $unreliable = Moose::Meta::Class->create_anon_class(
69 superclasses => [],
70 roles => [qw/Restartable::ButUnreliable/],
71 methods => {
72 explode => sub { }, # nop.
73 'save_state' => sub { },
74 'load_state' => sub { },
75 },
76 )->new_object();
77 ok $unreliable, 'made anon class with Restartable::ButUnreliable role';
78 can_ok $unreliable, qw/start stop/;
79 }
80
81 {
82 my $cnt = 0;
83 my $broken = Moose::Meta::Class->create_anon_class(
84 superclasses => [],
85 roles => [qw/Restartable::ButBroken/],
86 methods => {
87 explode => sub { $cnt++ },
88 'save_state' => sub { },
89 'load_state' => sub { },
90 },
91 )->new_object();
92 ok $broken, 'made anon class with Restartable::ButBroken role';
93 $broken->start();
94 is $cnt, 1, '... start called explode';
95 $broken->stop();
96 is $cnt, 2, '... stop also called explode';
97 }
0 #!/usr/bin/env perl
1 use strict;
2 use warnings;
3 use Test::More tests => 7;
4
5 # this test script ensures that my idiom of:
6 # role: sub BUILD, after BUILD
7 # continues to work to run code after object initialization, whether the class
8 # has a BUILD method or not
9
10 BEGIN {
11 use_ok('Moose::Role');
12 }
13
14 my @CALLS;
15
16 do {
17 package TestRole;
18 use Moose::Role;
19
20 sub BUILD { push @CALLS, 'TestRole::BUILD' }
21 before BUILD => sub { push @CALLS, 'TestRole::BUILD:before' };
22 after BUILD => sub { push @CALLS, 'TestRole::BUILD:after' };
23 };
24
25 do {
26 package ClassWithBUILD;
27 use Moose;
28 with 'TestRole';
29
30 sub BUILD { push @CALLS, 'ClassWithBUILD::BUILD' }
31 };
32
33 do {
34 package ClassWithoutBUILD;
35 use Moose;
36 with 'TestRole';
37 };
38
39 is_deeply([splice @CALLS], [], "no calls to BUILD yet");
40
41 ClassWithBUILD->new;
42
43 is_deeply([splice @CALLS], [
44 'TestRole::BUILD:before',
45 'ClassWithBUILD::BUILD',
46 'TestRole::BUILD:after',
47 ]);
48
49 ClassWithoutBUILD->new;
50
51 is_deeply([splice @CALLS], [
52 'TestRole::BUILD:before',
53 'TestRole::BUILD',
54 'TestRole::BUILD:after',
55 ]);
56
57 ClassWithBUILD->meta->make_immutable;
58 ClassWithoutBUILD->meta->make_immutable;
59
60 is_deeply([splice @CALLS], [], "no calls to BUILD yet");
61
62 ClassWithBUILD->new;
63
64 is_deeply([splice @CALLS], [
65 'TestRole::BUILD:before',
66 'ClassWithBUILD::BUILD',
67 'TestRole::BUILD:after',
68 ]);
69
70 ClassWithoutBUILD->new;
71
72 is_deeply([splice @CALLS], [
73 'TestRole::BUILD:before',
74 'TestRole::BUILD',
75 'TestRole::BUILD:after',
76 ]);
77
22 use strict;
33 use warnings;
44
5 use Test::More tests => 14;
5 use Test::More tests => 24;
66 use Test::Exception;
77
88 BEGIN {
3737 ok(!Header([]), '... this did not pass the type test');
3838 ok(!Header({}), '... this did not pass the type test');
3939
40 my $coercion = find_type_constraint('Header')->coercion;
41 isa_ok($coercion, 'Moose::Meta::TypeCoercion');
40 my $anon_type = subtype Object => where { $_->isa('HTTPHeader') };
4241
43 {
44 my $coerced = $coercion->coerce([ 1, 2, 3 ]);
45 isa_ok($coerced, 'HTTPHeader');
42 lives_ok {
43 coerce $anon_type
44 => from ArrayRef
45 => via { HTTPHeader->new(array => $_[0]) }
46 => from HashRef
47 => via { HTTPHeader->new(hash => $_[0]) };
48 } 'coercion of anonymous subtype succeeds';
4649
47 is_deeply(
48 $coerced->array(),
49 [ 1, 2, 3 ],
50 '... got the right array');
51 is($coerced->hash(), undef, '... nothing assigned to the hash');
50 foreach my $coercion (
51 find_type_constraint('Header')->coercion,
52 $anon_type->coercion
53 ) {
54
55 my $coercion = find_type_constraint('Header')->coercion;
56 isa_ok($coercion, 'Moose::Meta::TypeCoercion');
57
58 {
59 my $coerced = $coercion->coerce([ 1, 2, 3 ]);
60 isa_ok($coerced, 'HTTPHeader');
61
62 is_deeply(
63 $coerced->array(),
64 [ 1, 2, 3 ],
65 '... got the right array');
66 is($coerced->hash(), undef, '... nothing assigned to the hash');
67 }
68
69 {
70 my $coerced = $coercion->coerce({ one => 1, two => 2, three => 3 });
71 isa_ok($coerced, 'HTTPHeader');
72
73 is_deeply(
74 $coerced->hash(),
75 { one => 1, two => 2, three => 3 },
76 '... got the right hash');
77 is($coerced->array(), undef, '... nothing assigned to the array');
78 }
79
80 {
81 my $scalar_ref = \(my $var);
82 my $coerced = $coercion->coerce($scalar_ref);
83 is($coerced, $scalar_ref, '... got back what we put in');
84 }
85
86 {
87 my $coerced = $coercion->coerce("Foo");
88 is($coerced, "Foo", '... got back what we put in');
89 }
5290 }
53
54 {
55 my $coerced = $coercion->coerce({ one => 1, two => 2, three => 3 });
56 isa_ok($coerced, 'HTTPHeader');
57
58 is_deeply(
59 $coerced->hash(),
60 { one => 1, two => 2, three => 3 },
61 '... got the right hash');
62 is($coerced->array(), undef, '... nothing assigned to the array');
63 }
64
65 {
66 my $scalar_ref = \(my $var);
67 my $coerced = $coercion->coerce($scalar_ref);
68 is($coerced, $scalar_ref, '... got back what we put in');
69 }
70
71 {
72 my $coerced = $coercion->coerce("Foo");
73 is($coerced, "Foo", '... got back what we put in');
74 }
75
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More no_plan => 1;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 # RT #37569
13
14 {
15 package MyObject;
16 use Moose;
17
18 package Foo;
19 use Moose;
20 use Moose::Util::TypeConstraints;
21
22 subtype 'MyArrayRef'
23 => as 'ArrayRef'
24 => where { defined $_->[0] }
25 => message { ref $_ ? "ref: ". ref $_ : 'scalar' } # stringy
26 ;
27
28 subtype 'MyObjectType'
29 => as 'Object'
30 => where { $_->isa('MyObject') }
31 => message {
32 if ( $_->isa('SomeObject') ) {
33 return 'More detailed error message';
34 }
35 elsif ( blessed $_ ) {
36 return 'Well it is an object';
37 }
38 else {
39 return 'Doh!';
40 }
41 }
42 ;
43
44 type 'NewType'
45 => where { $_->isa('MyObject') }
46 => message { blessed $_ ? 'blessed' : 'scalar' }
47 ;
48
49 has 'obj' => ( is => 'rw', isa => 'MyObjectType' );
50 has 'ar' => ( is => 'rw', isa => 'MyArrayRef' );
51 has 'nt' => ( is => 'rw', isa => 'NewType' );
52 }
53
54 my $foo = Foo->new;
55 my $obj = MyObject->new;
56
57 throws_ok {
58 $foo->ar([]);
59 } qr/Attribute \(ar\) does not pass the type constraint because: ref: ARRAY/, '... got the right error message';
60
61 throws_ok {
62 $foo->obj($foo); # Doh!
63 } qr/Attribute \(obj\) does not pass the type constraint because: Well it is an object/, '... got the right error message';
64
65 throws_ok {
66 $foo->nt($foo); # scalar
67 } qr/Attribute \(nt\) does not pass the type constraint because: blessed/, '... got the right error message';
68
69
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 37;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 BEGIN {
13 package MyRole;
14 use Moose::Role;
15
16 requires 'foo';
17
18 package MyMetaclass;
19 use Moose qw(extends with);
20 extends 'Moose::Meta::Class';
21 with 'MyRole';
22
23 sub foo { 'i am foo' }
24 }
25
26 {
27 package MyClass;
28 use metaclass ('MyMetaclass');
29 use Moose;
30 }
31
32 my $mc = MyMetaclass->initialize('MyClass');
33 isa_ok($mc, 'MyMetaclass');
34
35 ok($mc->meta->does_role('MyRole'), '... the metaclass does the role');
36
37 is(MyClass->meta, $mc, '... these metas are the same thing');
38 is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
39
40 my $a = MyClass->new;
41 ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
42 is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
43 ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
44 is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
45
46 lives_ok {
47 MyClass->meta->make_immutable;
48 } '... make MyClass immutable okay';
49
50 is(MyClass->meta, $mc, '... these metas are still the same thing');
51 is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
52
53 ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
54 is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
55 ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
56 is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
57
58 lives_ok {
59 MyClass->meta->make_mutable;
60 } '... make MyClass mutable okay';
61
62 is(MyClass->meta, $mc, '... these metas are still the same thing');
63 is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
64
65 ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
66 is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
67 ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
68 is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
69
70 lives_ok {
71 MyMetaclass->meta->make_immutable;
72 } '... make MyClass immutable okay';
73
74 is(MyClass->meta, $mc, '... these metas are still the same thing');
75 is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
76
77 ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
78 is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
79 ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
80 is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
81
82 lives_ok {
83 MyClass->meta->make_immutable;
84 } '... make MyClass immutable okay';
85
86 is(MyClass->meta, $mc, '... these metas are still the same thing');
87 is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
88
89 ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
90 is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
91 ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
92 is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
93
0 #!/usr/bin/perl
1
2 # In the case where a child type constraint's parent constraint fails,
3 # the exception should reference the parent type constraint that actually
4 # failed instead of always referencing the child'd type constraint
5
6 use strict;
7 use warnings;
8
9 use Test::More tests => 4;
10 use Test::Exception;
11
12 BEGIN {
13 use_ok('Moose::Util::TypeConstraints');
14 }
15
16 lives_ok {
17 subtype 'ParentConstraint' => as 'Str' => where {0};
18 } 'specified parent type constraint';
19
20 my $tc;
21 lives_ok {
22 $tc = subtype 'ChildConstraint' => as 'ParentConstraint' => where {1};
23 } 'specified child type constraint';
24
25 {
26 my $errmsg = $tc->validate();
27
28 TODO: {
29 local $TODO = 'Not yet supported';
30 ok($errmsg !~ /Validation failed for 'ChildConstraint'/, 'exception references failing parent constraint');
31 };
32 }
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More 'no_plan';
6 use Test::Exception;
7
8 sub req_or_has ($$) {
9 my ( $role, $method ) = @_;
10 local $Test::Builder::Level = $Test::Builder::Level + 1;
11 if ( $role ) {
12 ok(
13 $role->has_method($method) || $role->requires_method($method),
14 $role->name . " has or requires method $method"
15 );
16 } else {
17 fail("role has or requires method $method");
18 }
19 }
20
21 {
22 package Bar;
23 use Moose::Role;
24
25 # this role eventually adds three methods, qw(foo bar xxy), but only one is
26 # known when it's still a role
27
28 has foo => ( is => "rw" );
29
30 has gorch => ( reader => "bar" );
31
32 sub xxy { "BAAAD" }
33
34 package Gorch;
35 use Moose::Role;
36
37 # similarly this role gives attr and gorch_method
38
39 has attr => ( is => "rw" );
40
41 sub gorch_method { "gorch method" }
42
43 around dandy => sub { shift->(@_) . "bar" };
44
45 package Quxx;
46 use Moose;
47
48 sub dandy { "foo" }
49
50 # this object will be used in an attr of Foo to test that Foo can do the
51 # Gorch interface
52
53 with qw(Gorch);
54
55 package Dancer;
56 use Moose::Role;
57
58 requires "twist";
59
60 package Dancer::Ballerina;
61 use Moose;
62
63 with qw(Dancer);
64
65 sub twist { }
66
67 sub pirouette { }
68
69 package Dancer::Robot;
70 use Moose::Role;
71
72 # this doesn't fail but it produces a requires in the role
73 # the order doesn't matter
74 has twist => ( is => "rw" );
75 ::lives_ok { with qw(Dancer) };
76
77 package Dancer::Something;
78 use Moose;
79
80 # this fail even though the method already exists
81
82 has twist => ( is => "rw" );
83
84 {
85 local our $TODO = "accessors don't satisfy role requires";
86 ::lives_ok { with qw(Dancer) };
87 }
88
89 package Dancer::80s;
90 use Moose;
91
92 # this should pass because ::Robot has the attribute to fill in the requires
93 # but due to the deferrence logic that doesn't actually work
94 {
95 local our $TODO = "attribute accessor in role doesn't satisfy role requires";
96 ::lives_ok { with qw(Dancer::Robot) };
97 }
98
99 package Foo;
100 use Moose;
101
102 with qw(Bar);
103
104 has oink => (
105 is => "rw",
106 handles => "Gorch", # should handles take the same arguments as 'with'? Meta::Role::Application::Delegation?
107 default => sub { Quxx->new },
108 );
109
110 has dancer => (
111 is => "rw",
112 does => "Dancer",
113 handles => "Dancer",
114 default => sub { Dancer::Ballerina->new },
115 );
116
117 sub foo { 42 }
118
119 sub bar { 33 }
120
121 sub xxy { 7 }
122
123 package Tree;
124 use Moose::Role;
125
126 has bark => ( is => "rw" );
127
128 package Dog;
129 use Moose::Role;
130
131 sub bark { warn "woof!" };
132
133 package EntPuppy;
134 use Moose;
135
136 {
137 local our $TODO = "attrs and methods from a role should clash";
138 ::dies_ok { with qw(Tree Dog) }
139 }
140 }
141
142 # these fail because of the deferral logic winning over actual methods
143 # this might be tricky to fix due to the 'sub foo {}; has foo => ( )' hack
144 # we've been doing for a long while, though I doubt people relied on it for
145 # anything other than fulfilling 'requires'
146 {
147 local $TODO = "attributes from role overwrite class methods";
148 is( Foo->new->foo, 42, "attr did not zap overriding method" );
149 is( Foo->new->bar, 33, "attr did not zap overriding method" );
150 }
151 is( Foo->new->xxy, 7, "method did not zap overriding method" ); # duh
152
153 # these pass, simple delegate
154 # mostly they are here to contrast the next blck
155 can_ok( Foo->new->oink, "dandy" );
156 can_ok( Foo->new->oink, "attr" );
157 can_ok( Foo->new->oink, "gorch_method" );
158
159 ok( Foo->new->oink->does("Gorch"), "Quxx does Gorch" );
160
161
162 # these are broken because 'attr' is not technically part of the interface
163 can_ok( Foo->new, "gorch_method" );
164 {
165 local $TODO = "accessor methods from a role are omitted in handles role";
166 can_ok( Foo->new, "attr" );
167 }
168
169 {
170 local $TODO = "handles role doesn't add the role to the ->does of the delegate's parent class";
171 ok( Foo->new->does("Gorch"), "Foo does Gorch" );
172 }
173
174
175 # these work
176 can_ok( Foo->new->dancer, "pirouette" );
177 can_ok( Foo->new->dancer, "twist" );
178
179 can_ok( Foo->new, "twist" );
180 ok( !Foo->new->can("pirouette"), "can't pirouette, not part of the iface" );
181
182 {
183 local $TODO = "handles role doesn't add the role to the ->does of the delegate's parent class";
184 ok( Foo->new->does("Dancer") );
185 }
186
187
188
189
190 my $gorch = Gorch->meta;
191
192 isa_ok( $gorch, "Moose::Meta::Role" );
193
194 ok( $gorch->has_attribute("attr"), "has attribute 'attr'" );
195
196 {
197 local $TODO = "role attribute isn't a meta attribute yet";
198 isa_ok( $gorch->get_attribute("attr"), "Moose::Meta::Attribute" );
199 }
200
201 req_or_has($gorch, "gorch_method");
202 ok( $gorch->has_method("gorch_method"), "has_method gorch_method" );
203 ok( !$gorch->requires_method("gorch_method"), "requires gorch method" );
204
205 {
206 local $TODO = "role method isn't a meta object yet";
207 isa_ok( $gorch->get_method("gorch_method"), "Moose::Meta::Method" );
208 }
209
210 {
211 local $TODO = "method modifier doesn't yet create a method requirement or meta object";
212 req_or_has($gorch, "dandy" );
213
214 # this specific test is maybe not backwards compat, but in theory it *does*
215 # require that method to exist
216 ok( $gorch->requires_method("dandy"), "requires the dandy method for the modifier" );
217 }
218
219 {
220 local $TODO = "attribute related methods are not yet known by the role";
221 # we want this to be a part of the interface, somehow
222 req_or_has($gorch, "attr");
223 ok( $gorch->has_method("attr"), "has_method attr" );
224 isa_ok( $gorch->get_method("attr"), "Moose::Meta::Method" );
225 isa_ok( $gorch->get_method("attr"), "Moose::Meta::Method::Accessor" );
226 }
227
228 my $robot = Dancer::Robot->meta;
229
230 isa_ok( $robot, "Moose::Meta::Role" );
231
232 ok( $robot->has_attribute("twist"), "has attr 'twist'" );
233
234 {
235 local $TODO = "role attribute isn't a meta attribute yet";
236 isa_ok( $robot->get_attribute("twist"), "Moose::Meta::Attribute" );
237 }
238
239 {
240 req_or_has($robot, "twist");
241
242 local $TODO = "attribute related methods are not yet known by the role";
243 ok( $robot->has_method("twist"), "has twist method" );
244 isa_ok( $robot->get_method("twist"), "Moose::Meta::Method" );
245 isa_ok( $robot->get_method("twist"), "Moose::Meta::Method::Accessor" );
246 }
247
248 __END__
249
250 I think Attribute needs to be refactored in some way to better support roles.
251
252 There are several possible ways to do this, all of them seem plausible to me.
253
254 The first approach would be to change the attribute class to allow it to be
255 queried about the methods it would install.
256
257 Then we instantiate the attribute in the role, and instead of deferring the
258 arguments, we just make an C<unpack>ish method.
259
260 Then we can interrogate the attr when adding it to the role, and generate stub
261 methods for all the methods it would produce.
262
263 A second approach is kinda like the Immutable hack: wrap the attr in an
264 anonmyous class that disables part of its interface.
265
266 A third method would be to create an Attribute::Partial object that would
267 provide a more role-ish behavior, and to do this independently of the actual
268 Attribute class.
269
270 Something similar can be done for method modifiers, but I think that's even simpler.
271
272
273
274 The benefits of doing this are:
275
276 * Much better introspection of roles
277
278 * More correctness in many cases (in my opinion anyway)
279
280 * More roles are more usable as interface declarations, without having to split
281 them into two pieces (one for the interface with a bunch of requires(), and
282 another for the actual impl with the problematic attrs (and stub methods to
283 fix the accessors) and method modifiers (dunno if this can even work at all)
284
285
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More 'no_plan';
6
7 # if make_immutable is removed from the following code the tests pass
8
9 {
10 package Foo;
11 use Moose;
12
13 has foo => ( is => "ro" );
14
15 package Bar;
16 use Moose;
17
18 extends qw(Foo);
19
20 around new => sub {
21 my $next = shift;
22 my ( $self, @args ) = @_;
23 $self->$next( foo => 42 );
24 };
25
26 package Gorch;
27 use Moose;
28
29 extends qw(Bar);
30
31 package Zoink;
32 use Moose;
33
34 extends qw(Gorch);
35
36 }
37
38 my @classes = qw(Foo Bar Gorch Zoink);
39
40 tests: {
41 TODO: {
42 is( Foo->new->foo, undef, "base class (" . (Foo->meta->is_immutable ? "immutable" : "mutable") . ")" );
43 is( Bar->new->foo, 42, "around new called on Bar->new (" . (Bar->meta->is_immutable ? "immutable" : "mutable") . ")" );
44 local $TODO = 'these tests fail once Gorch is immutable' if Gorch->meta->is_immutable;
45 is( Gorch->new->foo, 42, "around new called on Gorch->new (" . (Gorch->meta->is_immutable ? "immutable" : "mutable") . ")" );
46 is( Zoink->new->foo, 42, "around new called Zoink->new (" . (Zoink->meta->is_immutable ? "immutable" : "mutable") . ")" );
47 }
48
49 if ( @classes ) {
50 ( shift @classes )->meta->make_immutable;
51 redo tests;
52 }
53 }
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More tests => 6;
6
7 my ($around_new);
8 {
9 package Foo;
10 use Moose;
11
12 around new => sub { my $o = shift; $around_new = 1; $o->(@_); };
13 has 'foo' => (is => 'rw', isa => 'Int');
14
15 package Bar;
16 use Moose;
17 extends 'Foo';
18 Bar->meta->make_immutable;
19 }
20
21 my $orig_new = Foo->meta->find_method_by_name('new');
22 isa_ok($orig_new, 'Class::MOP::Method::Wrapped');
23 $orig_new = $orig_new->get_original_method;
24 isa_ok($orig_new, 'Moose::Meta::Method');
25
26 Foo->meta->make_immutable(debug => 0);
27 my $inlined_new = Foo->meta->find_method_by_name('new');
28 isa_ok($inlined_new, 'Class::MOP::Method::Wrapped');
29 $inlined_new = $inlined_new->get_original_method;
30
31 TODO:
32 {
33 local $TODO = 'but it isa Moose::Meta::Method instead';
34 isa_ok($inlined_new, 'Moose::Meta::Method::Constructor');
35 }
36
37 Foo->new(foo => 100);
38 ok($around_new, 'around new called');
39
40 $around_new = 0;
41 Bar->new(foo => 100);
42
43 TODO:
44 {
45 local $TODO = 'but it is not called';
46 ok($around_new, 'around new called');
47 }
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use Test::More no_plan => 1;
6 use Test::Exception;
7
8 BEGIN {
9 use_ok('Moose');
10 }
11
12 =pod
13
14 See this for some details:
15
16 http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=476579
17
18 Here is the basic test case, it segfaults, so I am going
19 to leave it commented out. Basically it seems that there
20 is some bad interaction between the ??{} construct that
21 is used in the "parser" for type definitions and threading
22 so probably the fix would involve removing the ??{} usage
23 for something else.
24
25 use threads;
26
27 {
28 package Foo;
29 use Moose;
30 has "bar" => (is => 'rw', isa => "Str | Num");
31 }
32
33 my $thr = threads->create(sub {});
34 $thr->join();
35
36 =cut
37
38 {
39 local $TODO = 'This is just a stub for the test, see the POD';
40 fail('Moose type constraints and threads dont get along');
41 }
42
43
44
45