Make the generate_* methods in CMOP::Method::Constructor private
[gitmo/Class-MOP.git] / lib / Class / MOP / Method / Constructor.pm
CommitLineData
d90b42a6 1
2package Class::MOP::Method::Constructor;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
8use Scalar::Util 'blessed', 'weaken', 'looks_like_number';
9
eca95e04 10our $VERSION = '0.78';
d519662a 11$VERSION = eval $VERSION;
d90b42a6 12our $AUTHORITY = 'cpan:STEVAN';
13
565f0cbb 14use base 'Class::MOP::Method::Generated';
d90b42a6 15
16sub new {
17 my $class = shift;
18 my %options = @_;
8d2d4c67 19
ad315b75 20 (blessed $options{metaclass} && $options{metaclass}->isa('Class::MOP::Class'))
21 || confess "You must pass a metaclass instance if you want to inline"
8d2d4c67 22 if $options{is_inline};
23
b38f3848 24 ($options{package_name} && $options{name})
32202ce2 25 || confess "You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT";
b38f3848 26
0bfc85b8 27 my $self = $class->_new(\%options);
d90b42a6 28
8d2d4c67 29 # we don't want this creating
30 # a cycle in the code, if not
d90b42a6 31 # needed
8683db0e 32 weaken($self->{'associated_metaclass'});
d90b42a6 33
8f7852d8 34 $self->_initialize_body;
d90b42a6 35
8d2d4c67 36 return $self;
d90b42a6 37}
38
28b97bef 39sub _new {
0bfc85b8 40 my $class = shift;
41 my $options = @_ == 1 ? $_[0] : {@_};
28b97bef 42
43 bless {
44 # from our superclass
45 'body' => undef,
0bfc85b8 46 'package_name' => $options->{package_name},
47 'name' => $options->{name},
28b97bef 48 # specific to this subclass
0bfc85b8 49 'options' => $options->{options} || {},
50 'associated_metaclass' => $options->{metaclass},
51 'is_inline' => ($options->{is_inline} || 0),
28b97bef 52 }, $class;
53}
54
f0de47d9 55sub can_be_inlined { 1 }
56
8d2d4c67 57## accessors
c23184fc 58
8683db0e 59sub options { (shift)->{'options'} }
60sub associated_metaclass { (shift)->{'associated_metaclass'} }
c23184fc 61
565f0cbb 62## cached values ...
d90b42a6 63
8d2d4c67 64sub meta_instance {
565f0cbb 65 my $self = shift;
8683db0e 66 $self->{'meta_instance'} ||= $self->associated_metaclass->get_meta_instance;
565f0cbb 67}
c23184fc 68
8d2d4c67 69sub attributes {
565f0cbb 70 my $self = shift;
8683db0e 71 $self->{'attributes'} ||= [ $self->associated_metaclass->compute_all_applicable_attributes ]
565f0cbb 72}
d90b42a6 73
74## method
75
565f0cbb 76sub initialize_body {
8f7852d8 77 warn 'The initialize_body method has been made private.'
78 . " The public version is deprecated and will be removed in a future release.\n";
79 goto &_initialize_body;
80}
81
82sub _initialize_body {
565f0cbb 83 my $self = shift;
ecb874a0 84 my $method_name = '_generate_constructor_method';
8d2d4c67 85
565f0cbb 86 $method_name .= '_inline' if $self->is_inline;
8d2d4c67 87
8683db0e 88 $self->{'body'} = $self->$method_name;
565f0cbb 89}
90
91sub generate_constructor_method {
ecb874a0 92 warn 'The generate_constructor_method method has been made private.'
93 . " The public version is deprecated and will be removed in a future release.\n";
94 goto &_generate_constructor_method;
95}
96
97sub _generate_constructor_method {
2a2b8458 98 return sub { Class::MOP::Class->initialize(shift)->new_object(@_) }
565f0cbb 99}
100
101sub generate_constructor_method_inline {
ecb874a0 102 warn 'The generate_constructor_method_inline method has been made private.'
103 . " The public version is deprecated and will be removed in a future release.\n";
104 goto &_generate_constructor_method_inline;
105}
106
107sub _generate_constructor_method_inline {
d90b42a6 108 my $self = shift;
565f0cbb 109
0c6f3280 110 my $close_over = {};
111
d90b42a6 112 my $source = 'sub {';
26ffbb36 113 $source .= "\n" . 'my $class = shift;';
8d2d4c67 114
26ffbb36 115 $source .= "\n" . 'return Class::MOP::Class->initialize($class)->new_object(@_)';
8d2d4c67 116 $source .= "\n" . ' if $class ne \'' . $self->associated_metaclass->name . '\';';
117
26ffbb36 118 $source .= "\n" . 'my $params = @_ == 1 ? $_[0] : {@_};';
119
d90b42a6 120 $source .= "\n" . 'my $instance = ' . $self->meta_instance->inline_create_instance('$class');
8d2d4c67 121 $source .= ";\n" . (join ";\n" => map {
82cff1ae 122 $self->_generate_slot_initializer($_, $close_over)
64adcd8d 123 } @{$self->attributes});
d90b42a6 124 $source .= ";\n" . 'return $instance';
8d2d4c67 125 $source .= ";\n" . '}';
126 warn $source if $self->options->{debug};
127
a6eef5a3 128 my $code = $self->_eval_closure(
ffe92c8b 129 $close_over,
130 $source
131 );
a6eef5a3 132 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
133
134 return $code;
d90b42a6 135}
136
137sub _generate_slot_initializer {
138 my $self = shift;
64adcd8d 139 my $attr = shift;
82cff1ae 140 my $close = shift;
8d2d4c67 141
d90b42a6 142 my $default;
143 if ($attr->has_default) {
144 # NOTE:
145 # default values can either be CODE refs
8d2d4c67 146 # in which case we need to call them. Or
d90b42a6 147 # they can be scalars (strings/numbers)
148 # in which case we can just deal with them
149 # in the code we eval.
150 if ($attr->is_default_a_coderef) {
82cff1ae 151 my $idx = @{$close->{'@defaults'}||=[]};
152 push(@{$close->{'@defaults'}}, $attr->default);
153 $default = '$defaults[' . $idx . ']->($instance)';
d90b42a6 154 }
155 else {
156 $default = $attr->default;
157 # make sure to quote strings ...
158 unless (looks_like_number($default)) {
159 $default = "'$default'";
160 }
161 }
8d2d4c67 162 } elsif( $attr->has_builder ) {
163 $default = '$instance->'.$attr->builder;
d90b42a6 164 }
8d2d4c67 165
c16c9c1b 166 if ( defined $attr->init_arg ) {
167 return (
26ffbb36 168 'if(exists $params->{\'' . $attr->init_arg . '\'}){' . "\n" .
c16c9c1b 169 $self->meta_instance->inline_set_slot_value(
170 '$instance',
e9a19694 171 $attr->name,
26ffbb36 172 '$params->{\'' . $attr->init_arg . '\'}' ) . "\n" .
c16c9c1b 173 '} ' . (!defined $default ? '' : 'else {' . "\n" .
174 $self->meta_instance->inline_set_slot_value(
175 '$instance',
e9a19694 176 $attr->name,
c16c9c1b 177 $default ) . "\n" .
178 '}')
179 );
180 } elsif ( defined $default ) {
181 return (
182 $self->meta_instance->inline_set_slot_value(
183 '$instance',
e9a19694 184 $attr->name,
c16c9c1b 185 $default ) . "\n"
186 );
187 } else { return '' }
d90b42a6 188}
189
1901;
191
d90b42a6 192__END__
193
194=pod
195
8d2d4c67 196=head1 NAME
d90b42a6 197
198Class::MOP::Method::Constructor - Method Meta Object for constructors
199
200=head1 SYNOPSIS
201
96e38ba6 202 use Class::MOP::Method::Constructor;
8d2d4c67 203
96e38ba6 204 my $constructor = Class::MOP::Method::Constructor->new(
8d2d4c67 205 metaclass => $metaclass,
96e38ba6 206 options => {
207 debug => 1, # this is all for now
8d2d4c67 208 },
96e38ba6 209 );
8d2d4c67 210
96e38ba6 211 # calling the constructor ...
b7045e66 212 $constructor->body->execute($metaclass->name, %params);
8d2d4c67 213
d90b42a6 214=head1 DESCRIPTION
215
3fd960d9 216This is a subclass of C<Class::MOP::Method> which generates
217constructor methods.
96e38ba6 218
d90b42a6 219=head1 METHODS
220
221=over 4
222
3fd960d9 223=item B<< Class::MOP::Method::Constructor->new(%options) >>
d90b42a6 224
3fd960d9 225This creates a new constructor object. It accepts a hash reference of
226options.
96e38ba6 227
3fd960d9 228=over 8
96e38ba6 229
3fd960d9 230=item * metaclass
96e38ba6 231
3fd960d9 232This should be a L<Class::MOP::Class> object. It is required.
c23184fc 233
3fd960d9 234=item * name
d90b42a6 235
3fd960d9 236The method name (without a package name). This is required.
96e38ba6 237
3fd960d9 238=item * package_name
d90b42a6 239
3fd960d9 240The package name for the method. This is required.
c23184fc 241
3fd960d9 242=item * is_inline
96e38ba6 243
3fd960d9 244This indicates whether or not the constructor should be inlined. This
245defaults to false.
d90b42a6 246
3fd960d9 247=back
f0de47d9 248
3fd960d9 249=item B<< $metamethod->is_inline >>
f0de47d9 250
3fd960d9 251Returns a boolean indicating whether or not the constructor is
252inlined.
d90b42a6 253
3fd960d9 254=item B<< $metamethod->associated_metaclass >>
96e38ba6 255
3fd960d9 256This returns the L<Class::MOP::Class> object for the method.
d90b42a6 257
3fd960d9 258=item B<< $metamethod->is_inline >>
565f0cbb 259
3fd960d9 260Returns a boolean indicating whether or not the constructor is
261inlined.
565f0cbb 262
3fd960d9 263=item B<< $metamethod->can_be_inlined >>
565f0cbb 264
3fd960d9 265This method always returns true in this class. It exists so that
266subclasses (as in Moose) can do some sort of checking to determine
267whether or not inlining the constructor is safe.
565f0cbb 268
269=back
270
d90b42a6 271=head1 AUTHORS
272
273Stevan Little E<lt>stevan@iinteractive.comE<gt>
274
275=head1 COPYRIGHT AND LICENSE
276
070bb6c9 277Copyright 2006-2009 by Infinity Interactive, Inc.
d90b42a6 278
279L<http://www.iinteractive.com>
280
281This library is free software; you can redistribute it and/or modify
8d2d4c67 282it under the same terms as Perl itself.
d90b42a6 283
284=cut
285