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