more typo fixes
[gitmo/Moose.git] / t / 200_examples / 006_example_Protomoose.t
CommitLineData
edfe85eb 1#!/usr/local/bin/perl
2
3use strict;
4use warnings;
5
28412c0b 6use Test::More tests => 28;
edfe85eb 7
8=pod
9
10This is an example of making Moose behave
11more like a prototype based object system.
12
13Why?
14
15Well cause merlyn asked if it could :)
16
17=cut
18
19## ------------------------------------------------------------------
20## make some metaclasses
21
22{
23 package ProtoMoose::Meta::Instance;
24 use Moose;
25
26 BEGIN { extends 'Moose::Meta::Instance' };
27
28 # NOTE:
29 # do not let things be inlined by
30 # the attribute or accessor generator
31 sub is_inlinable { 0 }
32}
33
34{
35 package ProtoMoose::Meta::Method::Accessor;
36 use Moose;
37
38 BEGIN { extends 'Moose::Meta::Method::Accessor' };
39
40 # customize the accessors to always grab
9bac92f6 41 # the correct instance in the accessors
42
43 sub find_instance {
6549b0d1 44 my ($self, $candidate, $accessor_type) = @_;
9bac92f6 45
6549b0d1 46 my $instance = $candidate;
9bac92f6 47 my $attr = $self->associated_attribute;
48
49 # if it is a class calling it ...
50 unless (blessed($instance)) {
51 # then grab the class prototype
52 $instance = $attr->associated_class->prototype_instance;
53 }
54 # if its an instance ...
55 else {
56 # and there is no value currently
57 # associated with the instance and
58 # we are trying to read it, then ...
59 if ($accessor_type eq 'r' && !defined($attr->get_value($instance))) {
60 # again, defer the prototype in
61 # the class in which is was defined
62 $instance = $attr->associated_class->prototype_instance;
63 }
64 # otherwise, you want to assign
65 # to your local copy ...
66 }
67 return $instance;
68 }
edfe85eb 69
70 sub generate_accessor_method {
9bac92f6 71 my $self = shift;
72 my $attr = $self->associated_attribute;
edfe85eb 73 return sub {
9bac92f6 74 if (scalar(@_) == 2) {
75 $attr->set_value(
76 $self->find_instance($_[0], 'w'),
77 $_[1]
78 );
79 }
80 $attr->get_value($self->find_instance($_[0], 'r'));
edfe85eb 81 };
82 }
83
84 sub generate_reader_method {
9bac92f6 85 my $self = shift;
86 my $attr = $self->associated_attribute;
87 return sub {
edfe85eb 88 confess "Cannot assign a value to a read-only accessor" if @_ > 1;
9bac92f6 89 $attr->get_value($self->find_instance($_[0], 'r'));
edfe85eb 90 };
91 }
92
93 sub generate_writer_method {
9bac92f6 94 my $self = shift;
95 my $attr = $self->associated_attribute;
edfe85eb 96 return sub {
9bac92f6 97 $attr->set_value(
98 $self->find_instance($_[0], 'w'),
99 $_[1]
100 );
edfe85eb 101 };
102 }
103
104 # deal with these later ...
105 sub generate_predicate_method {}
106 sub generate_clearer_method {}
107
108}
109
110{
111 package ProtoMoose::Meta::Attribute;
112 use Moose;
113
114 BEGIN { extends 'Moose::Meta::Attribute' };
115
116 sub accessor_metaclass { 'ProtoMoose::Meta::Method::Accessor' }
117}
118
119{
120 package ProtoMoose::Meta::Class;
121 use Moose;
122
123 BEGIN { extends 'Moose::Meta::Class' };
124
9bac92f6 125 has 'prototype_instance' => (
edfe85eb 126 is => 'rw',
127 isa => 'Object',
9bac92f6 128 predicate => 'has_prototypical_instance',
edfe85eb 129 lazy => 1,
130 default => sub { (shift)->new_object }
131 );
132
133 sub initialize {
134 # NOTE:
135 # I am not sure why 'around' does
136 # not work here, have to investigate
137 # it later - SL
138 (shift)->SUPER::initialize(@_,
139 instance_metaclass => 'ProtoMoose::Meta::Instance',
140 attribute_metaclass => 'ProtoMoose::Meta::Attribute',
141 );
142 }
143
144 around 'construct_instance' => sub {
145 my $next = shift;
146 my $self = shift;
147 # NOTE:
148 # we actually have to do this here
149 # to tie-the-knot, if you take it
150 # out, then you get deep recursion
151 # several levels deep :)
9bac92f6 152 $self->prototype_instance($next->($self, @_))
153 unless $self->has_prototypical_instance;
154 return $self->prototype_instance;
edfe85eb 155 };
9bac92f6 156
157}
158
159{
160 package ProtoMoose::Object;
161 use metaclass 'ProtoMoose::Meta::Class';
162 use Moose;
163
164 sub new {
165 my $prototype = blessed($_[0])
166 ? $_[0]
167 : $_[0]->meta->prototype_instance;
168 my (undef, %params) = @_;
169 my $self = $prototype->meta->clone_object($prototype, %params);
170 $self->BUILDALL(\%params);
171 return $self;
172 }
edfe85eb 173}
174
175## ------------------------------------------------------------------
176## make some classes now
177
178{
179 package Foo;
edfe85eb 180 use Moose;
181
9bac92f6 182 extends 'ProtoMoose::Object';
183
edfe85eb 184 has 'bar' => (is => 'rw');
185}
186
187{
188 package Bar;
189 use Moose;
190
191 extends 'Foo';
192
193 has 'baz' => (is => 'rw');
194}
195
196## ------------------------------------------------------------------
197
9bac92f6 198## ------------------------------------------------------------------
199## Check that metaclasses are working/inheriting properly
edfe85eb 200
9bac92f6 201foreach my $class (qw/ProtoMoose::Object Foo Bar/) {
edfe85eb 202 isa_ok($class->meta,
203 'ProtoMoose::Meta::Class',
204 '... got the right metaclass for ' . $class . ' ->');
205
206 is($class->meta->instance_metaclass,
207 'ProtoMoose::Meta::Instance',
208 '... got the right instance meta for ' . $class);
209
210 is($class->meta->attribute_metaclass,
211 'ProtoMoose::Meta::Attribute',
212 '... got the right attribute meta for ' . $class);
213}
214
215## ------------------------------------------------------------------
216
9bac92f6 217# get the prototype for Foo
218my $foo_prototype = Foo->meta->prototype_instance;
219isa_ok($foo_prototype, 'Foo');
220
221# set a value in the prototype
222$foo_prototype->bar(100);
223is($foo_prototype->bar, 100, '... got the value stored in the prototype');
224
225# the "class" defers to the
226# the prototype when asked
227# about attributes
228is(Foo->bar, 100, '... got the value stored in the prototype (through the Foo class)');
edfe85eb 229
9bac92f6 230# now make an instance, which
231# is basically a clone of the
232# prototype
edfe85eb 233my $foo = Foo->new;
9bac92f6 234isa_ok($foo, 'Foo');
edfe85eb 235
9bac92f6 236# the instance is *not* the prototype
237isnt($foo, $foo_prototype, '... got a new instance of Foo');
edfe85eb 238
9bac92f6 239# but it has the same values ...
240is($foo->bar, 100, '... got the value stored in the instance (inherited from the prototype)');
edfe85eb 241
9bac92f6 242# we can even change the values
243# in the instance
244$foo->bar(300);
245is($foo->bar, 300, '... got the value stored in the instance (overwriting the one inherited from the prototype)');
edfe85eb 246
9bac92f6 247# and not change the one in the prototype
248is($foo_prototype->bar, 100, '... got the value stored in the prototype');
249is(Foo->bar, 100, '... got the value stored in the prototype (through the Foo class)');
edfe85eb 250
9bac92f6 251## subclasses
edfe85eb 252
9bac92f6 253# now we can check that the subclass
254# will seek out the correct prototypical
255# value from it's "parent"
256is(Bar->bar, 100, '... got the value stored in the Foo prototype (through the Bar class)');
edfe85eb 257
9bac92f6 258# we can then also set it's local attrs
259Bar->baz(50);
260is(Bar->baz, 50, '... got the value stored in the prototype (through the Bar class)');
edfe85eb 261
9bac92f6 262# now we clone the Bar prototype
263my $bar = Bar->new;
264isa_ok($bar, 'Bar');
265isa_ok($bar, 'Foo');
266
267# and we see that we got the right values
268# in the instance/clone
269is($bar->bar, 100, '... got the value stored in the instance (inherited from the Foo prototype)');
270is($bar->baz, 50, '... got the value stored in the instance (inherited from the Bar prototype)');
edfe85eb 271
9bac92f6 272# nowe we can change the value
273$bar->bar(200);
274is($bar->bar, 200, '... got the value stored in the instance (overriding the one inherited from the Foo prototype)');
275
276# and all our original and
277# prototypical values are still
278# the same
279is($foo->bar, 300, '... still got the original value stored in the instance (inherited from the prototype)');
280is(Foo->bar, 100, '... still got the original value stored in the prototype (through the Foo class)');
281is(Bar->bar, 100, '... still got the original value stored in the prototype (through the Bar class)');
edfe85eb 282
283