1MakeMethods::Docs::ExamUpsleers(C3o)ntributed Perl DocumMeankteaMteitohnods::Docs::Examples(3)
2
3
4
6 Class::MakeMethods::Docs::Examples - Sample Declarations and Usage
7
9 The following examples indicate some of the capabilities of
10 Class::MakeMethods.
11
12 A Contrived Example
13
14 Object-oriented Perl code is widespread -- you've probably seen code
15 like the below a million times:
16
17 my $obj = MyStruct->new( foo=>"Foozle", bar=>"Bozzle" );
18 if ( $obj->foo() =~ /foo/i ) {
19 $obj->bar("Barbados!");
20 }
21
22 Here's a possible implementation for the class whose interface is shown
23 above:
24
25 package MyStruct;
26
27 sub new {
28 my $callee = shift;
29 my $self = bless { @_ }, (ref $callee ⎪⎪ $callee);
30 return $self;
31 }
32
33 sub foo {
34 my $self = shift;
35 if ( scalar @_ ) {
36 $self->{'foo'} = shift();
37 } else {
38 $self->{'foo'}
39 }
40 }
41
42 sub bar {
43 my $self = shift;
44 if ( scalar @_ ) {
45 $self->{'bar'} = shift();
46 } else {
47 $self->{'bar'}
48 }
49 }
50
51 Class::MakeMethods allows you to simply declare those methods to be of
52 a predefined type, and it generates and installs the necessary methods
53 in your package at compile-time.
54
55 Here's the equivalent declaration for that same basic class:
56
57 package MyStruct;
58 use Class::MakeMethods::Standard::Hash (
59 'new' => 'new',
60 'scalar' => 'foo',
61 'scalar' => 'bar',
62 );
63
64 A Typical Example
65
66 The following example shows a common case of constructing a class with
67 several types of accessor methods
68
69 package MyObject;
70 use Class::MakeMethods::Standard::Hash (
71 new => 'new',
72 scalar => [ 'foo', 'bar' ],
73 array => 'my_list',
74 hash => 'my_index',
75 );
76
77 This class now has a constructor named new, two scalar accessors named
78 foo and bar, and a pair of reference accessors named my_list and
79 my_index. Typical usage of the class might include calls like the fol‐
80 lowing:
81
82 my $obj = MyObject->new( foo => 'Foozle' );
83 print $obj->foo();
84
85 $obj->bar('Barbados');
86 print $obj->bar();
87
88 $obj->my_list(0 => 'Foozle', 1 => 'Bang!');
89 print $obj->my_list(1);
90
91 $obj->my_index('broccoli' => 'Blah!', 'foo' => 'Fiddle');
92 print $obj->my_index('foo');
93
94 Lvalue Accessors
95
96 The Template subclasses support an optional "--lvalue" modifer that
97 causes your accessors method to be marked as returning an lvalue which
98 can be assigned to. (This feature is only available on Perl 5.6 or
99 later.)
100
101 package MyStruct;
102 use Class::MakeMethods::Template::Hash (
103 'new' => 'new',
104 'scalar --get --lvalue' => 'foo',
105 'array --get --lvalue' => 'bar',
106 );
107
108 $obj->foo = "Foozle";
109 print $obj->foo;
110
111 $obj->bar = ( 'baz', 'beep', 'boop' );
112 print $obj->bar->[1]; # beep
113
114 String and Numeric Accessors
115
116 In addition to the "scalar" accessor supported by the "Standard::*"
117 classes, the Template subclasses also provide specialized accessors
118 that can facilitate the use of specific types of data.
119
120 For example, we could declare the following class to hold information
121 about available Perl packages:
122
123 package MyVersionInfo;
124 use Class::MakeMethods::Template::Hash (
125 'new' => 'new',
126 'string' => 'package',
127 'number' => 'version',
128 );
129
130 sub summary {
131 my $self = shift;
132 return $self->package() . " is at version " . $self->version()
133 }
134
135 You could use this class as follows:
136
137 package main;
138 use MyVersionInfo;
139
140 my $obj = MyVersionInfo->new( package=>"Class::MakeMethods");
141 $obj->version( 2.0 );
142 print $obj->summary();
143
144 These accessors will provide a bit of diagnostic type checking; an
145 attempt to call "$obj->version("foo")" will cause your program to
146 croak.
147
148 String Concatenation Interface
149
150 The following defines a get_concat method "i", and specifies a string
151 to use when joining additional values when this method is called.
152
153 use Class::MakeMethods::Template::Hash
154 'string' => [ '--get_concat', 'i', { join => ' - ' } ];
155
156 (See Class::MakeMethods::Template::Generic for information about the
157 "string" "get_concat" interface.)
158
159 Access Control Example
160
161 The following defines a secret_password method, which will croak if it
162 is called from outside of the declaring package.
163
164 use Class::MakeMethods::Composite::Hash
165 'scalar' => [ 'secret_password' => { permit => 'pp' } ];
166
167 (See Class::MakeMethods::Composite for information about the "permit"
168 modifier.)
169
170 For template classes, the same thing is accomplished with '--private':
171
172 use Class::MakeMethods::Template::Hash
173 'scalar' => [ '--private', 'secret_password' ];
174
175 (See Class::MakeMethods::Template::Universal for information about the
176 "private" modifier.)
177
178 Lazy-Init Interface
179
180 Templapte scalar accessors declared with the "init_and_get" interface
181 can be used for "memoization" or lazy-evaluation for object attributes.
182 If the current accessor value is undefined, they will first call a
183 user-provided init_* method and save its value.
184
185 package MyWidget;
186 use Class::MakeMethods::Template::Hash (
187 'new --with_values' => [ 'new' ],
188 'scalar --init_and_get' => [ 'foo', 'count', 'result' ],
189 );
190
191 sub init_foo {
192 return 'foofle';
193 }
194
195 sub init_count {
196 return '3';
197 }
198
199 sub init_result {
200 my $self = shift;
201 return $self->foo x $self->count;
202 }
203 ...
204
205 my $widget = MyWidget->new();
206 print $widget->result; # output: fooflefooflefoofle
207
208 # if values are predefined, the init methods are not used
209 my $other_widget = MyWidget->new( foo => 'bar', count => 2 );
210 print $widget->result; # output: barbar
211
212 (See Class::MakeMethods::Template::Generic for more information about
213 "init_and_get". This interface is also supported by all of Generic's
214 subclasses, so you can add lazy-init methods for global data, class
215 data, array objects, etc. Unfortunately, to date it is only supported
216 for scalar-value accessors...)
217
218 Helper Methods
219
220 Template methods often include similarly-named "helper" methods. For
221 example, specifying the "--with_clear" interface for Template::*:scalar
222 methods creates an extra method for each accessor x named clear_x.
223
224 package MyClass;
225 use Class::MakeMethods::Template::Hash('scalar --with_clear' => 'foo');
226
227 my $obj = MyClass->new;
228 $obj->foo(23);
229 $obj->clear_foo;
230 print $obj->foo();
231
232 Reference Accessor and Helper Methods
233
234 For references to arrays and hashes, the Template subclasses provide
235 accessors with extra "helper methods" to facilitate method-based inter‐
236 action.
237
238 Here's a class whose instances each store a string and an array refer‐
239 ence, along with a method to search the directories:
240
241 package MySearchPath;
242 use Class::MakeMethods::Template::Hash (
243 'new' => 'new',
244 'string' => 'name',
245 'array' => 'directories',
246 );
247
248 sub search {
249 my $self = shift;
250 my $target = shift;
251 foreach my $dir ( $self->directories ) {
252 my $candidate = $dir . '/' . $target;
253 return $candidate if ( -e $candidate );
254 }
255 return;
256 }
257
258 Note that the directories accessor returns the contents of the array
259 when called in a list context, making it easier to loop over.
260
261 And here's a sample usage:
262
263 package main;
264 use MySearchPath;
265
266 my $libs = MySearchPath->new( name=>"libs", directories=>['/usr/lib'] );
267 $libs->push_directories( '/usr/local/lib' );
268
269 print "Searching in " . $libs->count_directories() . "directories.\n";
270 foreach ( 'libtiff', 'libjpeg' ) {
271 my $file = $libs->search("$_.so");
272 print "Checking $_: " . ( $file ⎪⎪ 'not found' ) . "\n";
273 }
274
275 Note the use of the push_* and count_* "helper" accessor methods, which
276 are defined by default for all 'Template::*:array' declarations.
277
278 Consult Class::MakeMethods::Template::Generic for more information
279 about the available types of reference accessors, and the various meth‐
280 ods they define.
281
282 Object Accessors
283
284 There's also a specialized accessor for object references:
285
286 package MyStruct;
287 use Class::MakeMethods::Template::Hash (
288 'new' => 'new',
289 'object' => [ 'widget' => {class=>'MyWidgetClass', delegate=>"twiddle"} ],
290 );
291
292 (Note that the "class" and "delegate" values specified above are method
293 parameters, which provide additional information about the "widget"
294 declaration; see "Standard Declaration Syntax" for more information.)
295
296 The above declaration creates methods equivalent to the following:
297
298 package MyStruct;
299
300 sub widget {
301 my $self = shift;
302 if ( scalar @_ ) {
303 if (ref $_[0] and UNIVERSAL::isa($_[0], 'MyWidgetClass')) {
304 $self->{widget} = shift;
305 } else {
306 $self->{widget} = MyWidgetClass->new(@_);
307 }
308 } else {
309 return $self->{widget};
310 }
311 }
312
313 sub clear_widget {
314 my $self = shift;
315 $self->{widget} = undef;
316 }
317
318 sub twiddle {
319 my $self = shift;
320 my $obj = $self->widget()
321 or Carp::croak("Can't forward twiddle because widget is empty");
322 $obj->twiddle(@_)
323 }
324
325 Mixing Object and Global Methods
326
327 Here's a package declaration using two of the included subclasses,
328 "Standard::Hash", for creating and accessing hash-based objects, and
329 "Basic::Global", for simple global-value accessors:
330
331 package MyQueueItem;
332
333 use Class::MakeMethods::Standard::Hash (
334 new => { name => 'new', defaults=>{ foo => 'Foozle' } },
335 scalar => [ 'foo', 'bar' ],
336 hash => 'history'
337 );
338
339 use Class::MakeMethods::Basic::Global (
340 scalar => 'Debug',
341 array => 'InQueue',
342 );
343
344 sub AddQueueItem {
345 my $class = shift;
346 my $instance = shift;
347 $instance->history('AddQueueItem' => time());
348 $class->InQueue([0, 0], $instance);
349 }
350
351 sub GetQueueItem {
352 my $class = shift;
353 $class->InQueue([0, 1], []) or $class->new
354 }
355
356 Adding Custom Initialization to Constructors
357
358 Frequently you'll want to provide some custom code to initialize new
359 objects of your class. Most of the *:new constructor methods provides a
360 way to ensure that this code is consistently called every time a new
361 instance is created.
362
363 Composite::Hash:new { post_rules => [] }
364 The Composite classes allow you to add pre- and post-operations to
365 any method, so you can pass in a code-ref to be executed after the
366 new() method.
367
368 package MyClass;
369
370 sub new_post_init {
371 my $self = ${(pop)->{result}}; # get result of original new()
372 length($self->foo) or $self->foo('FooBar'); # default value
373 warn "Initialized new object '$self'";
374 }
375
376 use Class::MakeMethods (
377 'Composite::Hash:new' => [
378 'new' => { post_rules=>[ \&new_post_init ] }
379 ],
380 'Composite::Hash:scalar' => 'foo;,
381 );
382 ...
383 package main;
384 my $self = MyClass->new( foo => 'Foozle' )
385
386 Template::Hash:new --and_then_init
387 Use 'Template::Hash:new --and_then_init', which will first create
388 the object and initialize it with the provided values, and then
389 call an init() method on the new object before returning it.
390
391 package MyClass;
392 use Class::MakeMethods::Template::Hash (
393 'new --and_then_init' => 'new'
394 'string' => 'foo'
395 );
396 sub init {
397 my $self = shift;
398 length($self->foo) or $self->foo('FooBar'); # default value
399 warn "Initialized new object '$self'";
400 }
401 ...
402 package main;
403 my $self = MyClass->new( foo => 'Foozle' )
404
405 Template::Hash:new --with_init
406 If you don't want your constructor to use the default hash-of-
407 method-names style of initialization, use 'Template::Hash:new
408 --with_init', which will create an empty object, pass its arguments
409 to the init() method on the new object, and then return it.
410
411 package MyClass;
412 use Class::MakeMethods::Template::Hash (
413 'new --with_init' => 'new'
414 'string' => 'foo'
415 );
416 sub init {
417 my $self = shift;
418 $self->foo( shift ⎪⎪ 'FooBar' ); # init with arg or default
419 warn "Initialized new object '$self'";
420 }
421 ...
422 package main;
423 my $self = MyClass->new( 'Foozle' )
424
425 Some additional notes about these constructors:
426
427 · The "Template::*:new" methods allow you to specify a name for your
428 method other than "init" by passing the "init_method" parameter:
429
430 use Class::MakeMethods::Template::Hash (
431 'new --and_then_init' => [
432 'new' => { init_method => 'my_init' }
433 ],
434 );
435
436 · If you know that you're not going to have a complex class hierar‐
437 chy, you can reduce resource consumption a bit by changing the
438 above declarations from "*::Hash" to "*::Array" so your objects end
439 up as blessed arrays rather than blessed hashes.
440
441 Changing Method Names
442
443 The Template subclasses allow you to control the names assigned to the
444 methods you generate by selecting from several naming interfaces.
445
446 For example, the accessors declared above use a default, Perl-ish style
447 interface, in which a single method can be called without an argument
448 to retrieve the value, or with an argument to set it. However, you can
449 also select a more Java-like syntax, with separate get* and set* meth‐
450 ods, by including the '--java' template specification:
451
452 package MyStruct;
453 use Class::MakeMethods::Template::Hash (
454 'new' => 'new',
455 'scalar' => '--java Foo',
456 );
457
458 (Note that the declaration of Foo could also have been written as
459 'scalar --java' => 'Foo' or "'scalar' => ['--java', 'Foo']", or
460 "'scalar' => [ 'foo' =" { 'interface'=>'java' } ], all of which are
461 interpreted identically; see the Class::MakeMethods section on "Argu‐
462 ment Normalization" for details.)
463
464 Usage of this accessor would then be as follows:
465
466 package main;
467 use MyStruct;
468
469 my $obj = MyStruct->new( setFoo => "Foozle" );
470 print $obj->getFoo();
471 $obj->setFoo("Bozzle");
472
473 Selecting Specific Helper Methods
474
475 You can use the ability to specify interfaces to select specific helper
476 methods rather than getting the default collection.
477
478 For example, let's say you wanted to use a Template::Hash:array, but
479 you only wanted two methods to be installed in your class, a foo()
480 accessor and a shift_foo() mutator. Any of the below combinations of
481 syntax should do the trick:
482
483 use Class::MakeMethods::Template::Hash
484 'array' => [
485 'foo' => { interface=>{'foo'=>'get_set', 'shift_foo'=>'shift'} },
486 ];
487
488 If you're going to have a lot of methods with the same interface, you
489 could pre-declare a named interface once and use it repeatedly:
490
491 BEGIN {
492 require Class::MakeMethods::Template::Hash;
493 Class::MakeMethods::Template::Hash->named_method('array')->
494 {'interface'}->{'my_get_set_shift'} =
495 { '*'=>'get_set', 'shift_*'=>'shift' };
496 }
497
498 use Class::MakeMethods::Template::Hash
499 'array --my_get_set_shift' => [ 'foo', 'bar' ];
500
501 Tree Structure Example
502
503 In this example we will create a pair of classes with references to
504 other objects.
505
506 The first class is a single-value data object implemented as a refer‐
507 ence to a scalar.
508
509 package MyTreeData;
510 use Class::MakeMethods::Template::Scalar (
511 'new' => 'new',
512 'string' => 'value',
513 );
514
515 The second class defines a node in a tree, with a constructor, an
516 accessor for a data object from the class above, and accessors for a
517 list of child nodes.
518
519 package MyTreeNode;
520 use Class::MakeMethods::Template::Hash (
521 'new' => 'new',
522 'object -class MyTreeData' => 'data',
523 'array_of_objects -class MyTreeNode' => 'children',
524 );
525
526 sub depth_first_data {
527 my $self = shift;
528 return $self->data, map { $_->depth_first_data() } $self->children;
529 }
530
531 Here's a sample of how the above classes could be used in a program.
532
533 package main;
534 use MyTreeData;
535 use MyTreeNode;
536
537 my $node = MyTreeNode->new(
538 data => { value=>'data1' },
539 children => [ { value=>'data3' } ]
540 );
541 $node->push_children( MyTreeNode->new( data => { value=>'data2' } ) );
542
543 foreach my $data ( $node->depth_first_data ) {
544 print $data->value();
545 }
546
548 See Class::MakeMethods for general information about this distribution.
549
550 Annotated Tutorials
551
552 Ron Savage has posted a pair of annotated examples, linked to below.
553 Each demonstrates building a class with MakeMethods, and each includes
554 scads of comments that walk you through the logic and demonstrate how
555 the various methods work together.
556
557 http://savage.net.au/Perl-tutorials.html
558 http://savage.net.au/Perl-tutorials/tut-33.tgz
559 http://savage.net.au/Perl-tutorials/tut-34.tgz
560
561
562
563perl v5.8.8 2004-09-06 MakeMethods::Docs::Examples(3)