Bump us up to 0.64
[gitmo/Class-MOP.git] / lib / Class / MOP / Method / Constructor.pm
1
2 package Class::MOP::Method::Constructor;
3
4 use strict;
5 use warnings;
6
7 use Carp         'confess';
8 use Scalar::Util 'blessed', 'weaken', 'looks_like_number';
9
10 our $VERSION   = '0.64';
11 our $AUTHORITY = 'cpan:STEVAN';
12
13 use base 'Class::MOP::Method::Generated';
14
15 sub new {
16     my $class   = shift;
17     my %options = @_;
18
19     (blessed $options{metaclass} && $options{metaclass}->isa('Class::MOP::Class'))
20         || confess "You must pass a metaclass instance if you want to inline"
21             if $options{is_inline};
22
23     ($options{package_name} && $options{name})
24         || confess "You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT";
25
26     my $self = bless {
27         # from our superclass
28         '&!body'                 => undef,
29         '$!package_name'         => $options{package_name},
30         '$!name'                 => $options{name},        
31         # specific to this subclass
32         '%!options'              => $options{options} || {},
33         '$!associated_metaclass' => $options{metaclass},
34         '$!is_inline'            => ($options{is_inline} || 0),
35     } => $class;
36
37     # we don't want this creating
38     # a cycle in the code, if not
39     # needed
40     weaken($self->{'$!associated_metaclass'});
41
42     $self->initialize_body;
43
44     return $self;
45 }
46
47 ## accessors
48
49 sub options              { (shift)->{'%!options'}              }
50 sub associated_metaclass { (shift)->{'$!associated_metaclass'} }
51
52 ## cached values ...
53
54 sub meta_instance {
55     my $self = shift;
56     $self->{'$!meta_instance'} ||= $self->associated_metaclass->get_meta_instance;
57 }
58
59 sub attributes {
60     my $self = shift;
61     $self->{'@!attributes'} ||= [ $self->associated_metaclass->compute_all_applicable_attributes ]
62 }
63
64 ## method
65
66 sub initialize_body {
67     my $self        = shift;
68     my $method_name = 'generate_constructor_method';
69
70     $method_name .= '_inline' if $self->is_inline;
71
72     $self->{'&!body'} = $self->$method_name;
73 }
74
75 sub generate_constructor_method {
76     return sub { Class::MOP::Class->initialize(shift)->new_object(@_) }
77 }
78
79 sub generate_constructor_method_inline {
80     my $self = shift;
81
82     my $source = 'sub {';
83     $source .= "\n" . 'my ($class, %params) = @_;';
84
85     $source .= "\n" . 'return Class::MOP::Class->initialize($class)->new_object(%params)';
86     $source .= "\n" . '    if $class ne \'' . $self->associated_metaclass->name . '\';';
87
88     $source .= "\n" . 'my $instance = ' . $self->meta_instance->inline_create_instance('$class');
89     $source .= ";\n" . (join ";\n" => map {
90         $self->_generate_slot_initializer($_)
91     } 0 .. (@{$self->attributes} - 1));
92     $source .= ";\n" . 'return $instance';
93     $source .= ";\n" . '}';
94     warn $source if $self->options->{debug};
95
96     my $code;
97     {
98         # NOTE:
99         # create the nessecary lexicals
100         # to be picked up in the eval
101         my $attrs = $self->attributes;
102
103         $code = eval $source;
104         confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
105     }
106     return $code;
107 }
108
109 sub _generate_slot_initializer {
110     my $self  = shift;
111     my $index = shift;
112
113     my $attr = $self->attributes->[$index];
114
115     my $default;
116     if ($attr->has_default) {
117         # NOTE:
118         # default values can either be CODE refs
119         # in which case we need to call them. Or
120         # they can be scalars (strings/numbers)
121         # in which case we can just deal with them
122         # in the code we eval.
123         if ($attr->is_default_a_coderef) {
124             $default = '$attrs->[' . $index . ']->default($instance)';
125         }
126         else {
127             $default = $attr->default;
128             # make sure to quote strings ...
129             unless (looks_like_number($default)) {
130                 $default = "'$default'";
131             }
132         }
133     } elsif( $attr->has_builder ) {
134         $default = '$instance->'.$attr->builder;
135     }
136
137     if ( defined $attr->init_arg ) {
138       return (
139           'if(exists $params{\'' . $attr->init_arg . '\'}){' . "\n" .
140                 $self->meta_instance->inline_set_slot_value(
141                     '$instance',
142                     ("'" . $attr->name . "'"),
143                     '$params{\'' . $attr->init_arg . '\'}' ) . "\n" .
144            '} ' . (!defined $default ? '' : 'else {' . "\n" .
145                 $self->meta_instance->inline_set_slot_value(
146                     '$instance',
147                     ("'" . $attr->name . "'"),
148                      $default ) . "\n" .
149            '}')
150         );
151     } elsif ( defined $default ) {
152         return (
153             $self->meta_instance->inline_set_slot_value(
154                 '$instance',
155                 ("'" . $attr->name . "'"),
156                  $default ) . "\n"
157         );
158     } else { return '' }
159 }
160
161 1;
162
163 __END__
164
165 =pod
166
167 =head1 NAME
168
169 Class::MOP::Method::Constructor - Method Meta Object for constructors
170
171 =head1 SYNOPSIS
172
173   use Class::MOP::Method::Constructor;
174
175   my $constructor = Class::MOP::Method::Constructor->new(
176       metaclass => $metaclass,
177       options   => {
178           debug => 1, # this is all for now
179       },
180   );
181
182   # calling the constructor ...
183   $constructor->body->($metaclass->name, %params);
184
185 =head1 DESCRIPTION
186
187 This is a subclass of C<Class::MOP::Method> which deals with
188 class constructors. This is used when making a class immutable
189 to generate an optimized constructor.
190
191 =head1 METHODS
192
193 =over 4
194
195 =item B<new (metaclass => $meta, options => \%options)>
196
197 =item B<options>
198
199 This returns the options HASH which is passed into C<new>.
200
201 =item B<associated_metaclass>
202
203 This returns the metaclass which is passed into C<new>.
204
205 =item B<attributes>
206
207 This returns the list of attributes which are associated with the
208 metaclass which is passed into C<new>.
209
210 =item B<meta_instance>
211
212 This returns the meta instance which is associated with the
213 metaclass which is passed into C<new>.
214
215 =item B<is_inline>
216
217 This returns a boolean, but since constructors are very rarely
218 not inlined, this always returns true for now.
219
220 =item B<initialize_body>
221
222 This creates the code reference for the constructor itself.
223
224 =back
225
226 =head2 Method Generators 
227
228 =over 4
229
230 =item B<generate_constructor_method>
231
232 =item B<generate_constructor_method_inline>
233
234 =back
235
236 =head1 AUTHORS
237
238 Stevan Little E<lt>stevan@iinteractive.comE<gt>
239
240 =head1 COPYRIGHT AND LICENSE
241
242 Copyright 2006-2008 by Infinity Interactive, Inc.
243
244 L<http://www.iinteractive.com>
245
246 This library is free software; you can redistribute it and/or modify
247 it under the same terms as Perl itself.
248
249 =cut
250