branching for lazy_build changes
[catagits/Reaction.git] / lib / Reaction / Class.pm
CommitLineData
7adfd53f 1package Reaction::Class;
2
3use Moose qw(confess);
4use Sub::Exporter ();
5use Sub::Name ();
6use Reaction::Types::Core;
7use Reaction::Object;
8
9sub exporter_for_package {
10 my ($self, $package) = @_;
11 my %exports_proto = $self->exports_for_package($package);
12 my %exports = (
13 map { my $cr = $exports_proto{$_}; ($_, sub { Sub::Name::subname "${self}::$_" => $cr; }) }
14 keys %exports_proto
15 );
16
17 my $exporter = Sub::Exporter::build_exporter({
18 exports => \%exports,
19 groups => {
20 default => [':all']
21 }
22 });
23
24 return $exporter;
25}
26
27sub do_import {
28 my ($self, $pkg, $args) = @_;
29 my $exporter = $self->exporter_for_package($pkg, $args);
30 $exporter->($self, { into => $pkg }, @$args);
31 if (my @default_base = $self->default_base) {
32 no strict 'refs';
33 @{"${pkg}::ISA"} = @default_base unless @{"${pkg}::ISA"};
34 }
35}
36
37sub default_base { ('Reaction::Object'); }
38
39sub exports_for_package {
40 my ($self, $package) = @_;
41 return (
42 set_or_lazy_build => sub {
43 my $name = shift;
44 my $build = "build_${name}";
45 return (required => 1, lazy => 1,
46 default => sub { shift->$build(); });
47 },
48 set_or_lazy_fail => sub {
49 my $name = shift;
50 my $message = "${name} must be provided before calling reader";
51 return (required => 1, lazy => 1,
52 default => sub { confess($message); });
53 },
54 trigger_adopt => sub {
55 my $type = shift;
56 my @args = @_;
57 my $adopt = "adopt_${type}";
58 return (trigger => sub { shift->$adopt(@args); });
59 },
60 register_inc_entry => sub {
61 my $inc = $package;
62 $inc =~ s/::/\//g;
63 $inc .= '.pm';
64 $INC{$inc} = 1;
65 },
66 #this needs to go away soon. its never used. pollution.
67 reflect_attributes_from => sub {
68 my ($from_class, @attrs) = @_;
69
70 #Should we use Class::Inspector to make sure class is loaded?
71 #unless( Class::Inspector->loaded($from_class) ){
72 # eval "require $from_class" || die("Failed to load: $from_class");
73 #}
74 foreach my $attr_name (@attrs){
75 my $from_attr = $from_class->meta->get_attribute($attr_name);
76 confess("$from_attr does not exist in $from_class")
77 unless $from_attr;
78 #Not happy
79 #$package->meta->add_attribute( $from_attr->name, %{$from_attr} );
80 $package->meta->add_attribute( bless { %{$from_attr} } =>
81 $package->meta->attribute_metaclass );
82 }
83 },
84 class => sub {
85 $self->do_class_sub($package, @_);
86 },
87 does => sub {
88 $package->can('with')->(@_);
89 },
90 overrides => sub {
91 $package->can('override')->(@_)
92 },
93 $self->make_package_sub($package),
94 implements => sub { confess "implements only valid within class block" },
95 $self->make_sugar_sub('is'),
96 $self->make_code_sugar_sub('which'),
97 $self->make_code_sugar_sub('as'),
98 run => sub (;&@) { @_ },
99 );
100}
101
102sub do_class_sub {
103 my ($self, $package, $class, @args) = @_;
104 my $error = "Invalid class declaration, should be: class Class (is Superclass)*, which { ... }";
105 confess $error if (@args % 1);
106 my @supers;
107 while (@args > 2) {
108 my $should_be_is = shift(@args);
109 confess $error unless $should_be_is eq 'is';
110 push(@supers, shift(@args));
111 }
112 confess $error unless $args[0] eq 'which' && ref($args[1]) eq 'CODE';
113 my $setup = $args[1];
114
115 #this eval is fucked, but I can't fix it
116 unless ($class->can('meta')) {
117 print STDERR "** MAKING CLASS $class useing Reaction::Class **\n";
118 eval "package ${class}; use Reaction::Class;";
119 if ($@) { confess "Couldn't make ${class} a Reaction class: $@"; }
120 }
121 if (@supers) {
122 Class::MOP::load_class($_) for @supers;
123 $class->meta->_fix_metaclass_incompatability(@supers);
124 $class->meta->superclasses(@supers);
125 }
126 $self->setup_and_cleanup($package, $setup);
127
128 #immutable code
129 #print STDERR "$package \n";
130 #print STDERR $package->meta->blessed, " \n";
131 $package->meta->make_immutable;
132# (inline_accessor => 0, inline_destructor => 0,inline_constructor => 0,);
133}
134
135sub setup_and_cleanup {
136 my ($self, $package, $setup) = @_;
137 my @methods;
138 my @apply_after;
139 my %save_delayed;
140 {
141 no strict 'refs';
142 no warnings 'redefine';
143 local *{"${package}::implements"} =
144 Sub::Name::subname "${self}::implements" => sub {
145 my $name = shift;
146 shift if $_[0] eq 'as';
147 push(@methods, [ $name, shift ]);
148 };
149 foreach my $meth ($self->delayed_methods) {
150 $save_delayed{$meth} = $package->can($meth);
151 local *{"${package}::${meth}"} =
152 Sub::Name::subname "${self}::${meth}" => sub {
153 push(@apply_after, [ $meth => @_ ]);
154 };
155 }
156 # XXX - need additional fuckery to handle multi-class-per-file
157 $setup->(); # populate up the crap
158 }
159 my %exports = $self->exports_for_package($package);
160 {
161 no strict 'refs';
162 foreach my $nuke (keys %exports) {
163 delete ${"${package}::"}{$nuke};
164 }
165 }
166 my $unimport_class = $self->next_import_package;
167 eval "package ${package}; no $unimport_class;";
168 confess "$unimport_class unimport from ${package} failed: $@" if $@;
169 foreach my $m (@methods) {
170 $package->meta->add_method(@$m);
171 }
172 foreach my $a (@apply_after) {
173 my $call = shift(@$a);
174 $save_delayed{$call}->(@$a);
175 }
176}
177
178sub delayed_methods {
179 return (qw/has with extends before after around override augment/);
180}
181
182sub make_package_sub {
183 my ($self, $package) = @_;
184 my ($last) = (split('::', $package))[-1];
185 return $last => sub {
186 $self->do_package_sub($package => @_);
187 };
188}
189
190sub do_package_sub {
191 my $self = shift;
192 my $package = shift;
193 return (@_ ? ($package => @_) : $package);
194}
195
196sub make_sugar_sub {
197 my ($self, $name) = @_;
198 return $name => sub {
199 return ($name => @_);
200 };
201}
202
203sub make_code_sugar_sub {
204 my ($self, $name) = @_;
205 return $name => sub (;&@) {
206 return ($name => @_);
207 };
208}
209
210sub import {
211 my $self = shift;
212 my $pkg = caller;
213 my @args = @_;
214 &strict::import;
215 &warnings::import;
216 $self->do_import($pkg, \@args);
217 goto &{$self->next_import};
218}
219
220sub next_import {
221 return shift->next_import_package(@_)->can('import');
222}
223
224sub next_import_package { 'Moose' }
225
2261;
227
228#---------#---------#---------#---------#---------#---------#---------#--------#
229
230=head1 NAME
231
232Reaction::Class
233
234=head1 DESCRIPTION
235
236=head1 SEE ALSO
237
238=over
239
240=item * L<Catalyst>
241
242=item * L<Reaction::Manual>
243
244=back
245
246=head1 Unstructured reminders
247
248(will properly format and stuff later. no time right now)
249
250C<use>ing C<Reaction::Class> will alias the current package name
251see L<aliased>.
252
253 package MyApp::Pretty::Picture
254
255 # Picture expands to 'MyApp::Pretty::Picture'
256 class Picture, which { ...
257
258=head2 default_base
259
260=head2 set_or_lazy_build $attrname
261
262Will make your attributes lazy and required, if they are not set they
263will default to the value returned by C<&build_$attrname>
264
265 has created_d => (isa => 'DateTime', set_or_lazy_build('created_d') );
266 sub build_created_d{ DateTime->now }
267
268=head2 set_or_lazy_fail $attrname
269
270Will make your attributes lazy and required, if they are not set
271and their accessor is called an exception will be thrown
272
273=head2 trigger_adopt $attrname
274
275=head2 register_inc_entry
276
277=head2 reflect_attributes_from $from_class, @attrs
278
279Create attributes in the local class that mirror the specified C<@attrs>
280in C<$from_class>
281
282=head2 class $name [, is $superclass ], which {
283
284Sugary class declaration, will create a a package C<$name> with an
285optional base class of $superclass. The class declaration, should be placed inside
286the brackets using C<implements> to declare a method and C<has> to declare an
287attribute.
288
289=head2 does
290
291Alias to C<with> for the current package, see C<Moose::Role>
292
293=head2 implements $method_name [is | which | as]
294
295Only valid whithin a class block, allows you to declare a method for the class.
296
297 implements 'current_date' => as { DateTime->today };
298
299=head2 run
300
301=head1 AUTHORS
302
303=over
304
305=item * Matt S. Trout
306
307=item * K. J. Cheetham
308
309=item * Guillermo Roditi
310
311=item * Jess Robinson (Documentation)
312
313=item * Kaare Rasmussen (Documentation)
314
315=item * Andres N. Kievsky (Documentation)
316
317=back
318
319=head1 LICENSE
320
321This library is free software, you can redistribute it and/or modify
322it under the same terms as Perl itself.
323
324=cut