diff options
| author | Andreas Brachold <vdr07@deltab.de> | 2007-08-13 18:41:27 +0000 |
|---|---|---|
| committer | Andreas Brachold <vdr07@deltab.de> | 2007-08-13 18:41:27 +0000 |
| commit | bcbf441e09fb502cf64924ff2530fa144bdf52c5 (patch) | |
| tree | f377707a2dac078db8cd0c7d7abfe69ac1006d71 /lib/Class/MakeMethods/Docs/Examples.pod | |
| download | xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.gz xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.bz2 | |
* Move files to trunk
Diffstat (limited to 'lib/Class/MakeMethods/Docs/Examples.pod')
| -rw-r--r-- | lib/Class/MakeMethods/Docs/Examples.pod | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/lib/Class/MakeMethods/Docs/Examples.pod b/lib/Class/MakeMethods/Docs/Examples.pod new file mode 100644 index 0000000..787ace7 --- /dev/null +++ b/lib/Class/MakeMethods/Docs/Examples.pod @@ -0,0 +1,554 @@ +=head1 NAME + +Class::MakeMethods::Docs::Examples - Sample Declarations and Usage + +=head1 EXAMPLES + +The following examples indicate some of the capabilities of +Class::MakeMethods. + +=head2 A Contrived Example + +Object-oriented Perl code is widespread -- you've probably seen code like the below a million times: + + my $obj = MyStruct->new( foo=>"Foozle", bar=>"Bozzle" ); + if ( $obj->foo() =~ /foo/i ) { + $obj->bar("Barbados!"); + } + +Here's a possible implementation for the class whose interface is +shown above: + + package MyStruct; + + sub new { + my $callee = shift; + my $self = bless { @_ }, (ref $callee || $callee); + return $self; + } + + sub foo { + my $self = shift; + if ( scalar @_ ) { + $self->{'foo'} = shift(); + } else { + $self->{'foo'} + } + } + + sub bar { + my $self = shift; + if ( scalar @_ ) { + $self->{'bar'} = shift(); + } else { + $self->{'bar'} + } + } + +Class::MakeMethods allows you to simply declare those methods to +be of a predefined type, and it generates and installs the necessary +methods in your package at compile-time. + +Here's the equivalent declaration for that same basic class: + + package MyStruct; + use Class::MakeMethods::Standard::Hash ( + 'new' => 'new', + 'scalar' => 'foo', + 'scalar' => 'bar', + ); + +=head2 A Typical Example + +The following example shows a common case of constructing a class with several types of accessor methods + + package MyObject; + use Class::MakeMethods::Standard::Hash ( + new => 'new', + scalar => [ 'foo', 'bar' ], + array => 'my_list', + hash => 'my_index', + ); + +This class now has a constructor named new, two scalar accessors named foo and bar, and a pair of reference accessors named my_list and my_index. Typical usage of the class might include calls like the following: + + my $obj = MyObject->new( foo => 'Foozle' ); + print $obj->foo(); + + $obj->bar('Barbados'); + print $obj->bar(); + + $obj->my_list(0 => 'Foozle', 1 => 'Bang!'); + print $obj->my_list(1); + + $obj->my_index('broccoli' => 'Blah!', 'foo' => 'Fiddle'); + print $obj->my_index('foo'); + +=head2 Lvalue Accessors + +The Template subclasses support an optional "--lvalue" modifer that causes your accessors method to be marked as returning an lvalue which can be assigned to. (This feature is only available on Perl 5.6 or later.) + + package MyStruct; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'scalar --get --lvalue' => 'foo', + 'array --get --lvalue' => 'bar', + ); + + $obj->foo = "Foozle"; + print $obj->foo; + + $obj->bar = ( 'baz', 'beep', 'boop' ); + print $obj->bar->[1]; # beep + +=head2 String and Numeric Accessors + +In addition to the C<scalar> accessor supported by the C<Standard::*> classes, the Template subclasses also provide specialized accessors that can facilitate the use of specific types of data. + +For example, we could declare the following class to hold information +about available Perl packages: + + package MyVersionInfo; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'string' => 'package', + 'number' => 'version', + ); + + sub summary { + my $self = shift; + return $self->package() . " is at version " . $self->version() + } + +You could use this class as follows: + + package main; + use MyVersionInfo; + + my $obj = MyVersionInfo->new( package=>"Class::MakeMethods"); + $obj->version( 2.0 ); + print $obj->summary(); + +These accessors will provide a bit of diagnostic type checking; +an attempt to call C<$obj-E<gt>version("foo")> will cause your +program to croak. + + +=head2 String Concatenation Interface + +The following defines a get_concat method C<i>, and specifies +a string to use when joining additional values when this method is +called. + + use Class::MakeMethods::Template::Hash + 'string' => [ '--get_concat', 'i', { join => ' - ' } ]; + +(See L<Class::MakeMethods::Template::Generic> for information about the C<string> C<get_concat> interface.) + + +=head2 Access Control Example + +The following defines a secret_password method, which will croak +if it is called from outside of the declaring package. + + use Class::MakeMethods::Composite::Hash + 'scalar' => [ 'secret_password' => { permit => 'pp' } ]; + +(See L<Class::MakeMethods::Composite> for information +about the C<permit> modifier.) + +For template classes, the same thing is accomplished with '--private': + + use Class::MakeMethods::Template::Hash + 'scalar' => [ '--private', 'secret_password' ]; + +(See L<Class::MakeMethods::Template::Universal> for information +about the C<private> modifier.) + + +=head2 Lazy-Init Interface + +Templapte scalar accessors declared with the "init_and_get" interface +can be used for "memoization" or lazy-evaluation for object +attributes. If the current accessor value is undefined, they will +first call a user-provided init_* method and save its value. + + package MyWidget; + use Class::MakeMethods::Template::Hash ( + 'new --with_values' => [ 'new' ], + 'scalar --init_and_get' => [ 'foo', 'count', 'result' ], + ); + + sub init_foo { + return 'foofle'; + } + + sub init_count { + return '3'; + } + + sub init_result { + my $self = shift; + return $self->foo x $self->count; + } + ... + + my $widget = MyWidget->new(); + print $widget->result; # output: fooflefooflefoofle + + # if values are predefined, the init methods are not used + my $other_widget = MyWidget->new( foo => 'bar', count => 2 ); + print $widget->result; # output: barbar + +(See L<Class::MakeMethods::Template::Generic> for more information about +C<init_and_get>. This interface is also supported by all of Generic's +subclasses, so you can add lazy-init methods for global data, class +data, array objects, etc. Unfortunately, to date it is only supported +for scalar-value accessors...) + + +=head2 Helper Methods + +Template methods often include similarly-named "helper" methods. For example, specifying the "--with_clear" interface for Template::*:scalar methods creates an extra method for each accessor x named clear_x. + + package MyClass; + use Class::MakeMethods::Template::Hash('scalar --with_clear' => 'foo'); + + my $obj = MyClass->new; + $obj->foo(23); + $obj->clear_foo; + print $obj->foo(); + + +=head2 Reference Accessor and Helper Methods + +For references to arrays and hashes, the Template subclasses provide +accessors with extra "helper methods" to facilitate method-based +interaction. + +Here's a class whose instances each store a string and an array +reference, along with a method to search the directories: + + package MySearchPath; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'string' => 'name', + 'array' => 'directories', + ); + + sub search { + my $self = shift; + my $target = shift; + foreach my $dir ( $self->directories ) { + my $candidate = $dir . '/' . $target; + return $candidate if ( -e $candidate ); + } + return; + } + +Note that the directories accessor returns the contents of the +array when called in a list context, making it easier to loop over. + +And here's a sample usage: + + package main; + use MySearchPath; + + my $libs = MySearchPath->new( name=>"libs", directories=>['/usr/lib'] ); + $libs->push_directories( '/usr/local/lib' ); + + print "Searching in " . $libs->count_directories() . "directories.\n"; + foreach ( 'libtiff', 'libjpeg' ) { + my $file = $libs->search("$_.so"); + print "Checking $_: " . ( $file || 'not found' ) . "\n"; + } + +Note the use of the push_* and count_* "helper" accessor methods, +which are defined by default for all 'Template::*:array' declarations. + +Consult L<Class::MakeMethods::Template::Generic> for more information about +the available types of reference accessors, and the various methods +they define. + + +=head2 Object Accessors + +There's also a specialized accessor for object references: + + package MyStruct; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'object' => [ 'widget' => {class=>'MyWidgetClass', delegate=>"twiddle"} ], + ); + +(Note that the C<class> and C<delegate> values specified above are +method parameters, which provide additional information about the +C<widget> declaration; see L<"Standard Declaration Syntax"> for more information.) + +The above declaration creates methods equivalent to the following: + + package MyStruct; + + sub widget { + my $self = shift; + if ( scalar @_ ) { + if (ref $_[0] and UNIVERSAL::isa($_[0], 'MyWidgetClass')) { + $self->{widget} = shift; + } else { + $self->{widget} = MyWidgetClass->new(@_); + } + } else { + return $self->{widget}; + } + } + + sub clear_widget { + my $self = shift; + $self->{widget} = undef; + } + + sub twiddle { + my $self = shift; + my $obj = $self->widget() + or Carp::croak("Can't forward twiddle because widget is empty"); + $obj->twiddle(@_) + } + + +=head2 Mixing Object and Global Methods + +Here's a package declaration using two of the included subclasses, C<Standard::Hash>, for creating and accessing hash-based objects, and C<Basic::Global>, for simple global-value accessors: + + package MyQueueItem; + + use Class::MakeMethods::Standard::Hash ( + new => { name => 'new', defaults=>{ foo => 'Foozle' } }, + scalar => [ 'foo', 'bar' ], + hash => 'history' + ); + + use Class::MakeMethods::Basic::Global ( + scalar => 'Debug', + array => 'InQueue', + ); + + sub AddQueueItem { + my $class = shift; + my $instance = shift; + $instance->history('AddQueueItem' => time()); + $class->InQueue([0, 0], $instance); + } + + sub GetQueueItem { + my $class = shift; + $class->InQueue([0, 1], []) or $class->new + } + +=head2 Adding Custom Initialization to Constructors + +Frequently you'll want to provide some custom code to initialize new objects of your class. Most of the C<*:new> constructor methods provides a way to ensure that this code is consistently called every time a new instance is created. + +=over 4 + +=item Composite::Hash:new { post_rules => [] } + +The Composite classes allow you to add pre- and post-operations to any method, so you can pass in a code-ref to be executed after the new() method. + + package MyClass; + + sub new_post_init { + my $self = ${(pop)->{result}}; # get result of original new() + length($self->foo) or $self->foo('FooBar'); # default value + warn "Initialized new object '$self'"; + } + + use Class::MakeMethods ( + 'Composite::Hash:new' => [ + 'new' => { post_rules=>[ \&new_post_init ] } + ], + 'Composite::Hash:scalar' => 'foo;, + ); + ... + package main; + my $self = MyClass->new( foo => 'Foozle' ) + +=item Template::Hash:new --and_then_init + +Use 'Template::Hash:new --and_then_init', which will first create the object and initialize it with the provided values, and then call an init() method on the new object before returning it. + + package MyClass; + use Class::MakeMethods::Template::Hash ( + 'new --and_then_init' => 'new' + 'string' => 'foo' + ); + sub init { + my $self = shift; + length($self->foo) or $self->foo('FooBar'); # default value + warn "Initialized new object '$self'"; + } + ... + package main; + my $self = MyClass->new( foo => 'Foozle' ) + +=item Template::Hash:new --with_init + +If you don't want your constructor to use the default hash-of-method-names style of initialization, use 'Template::Hash:new --with_init', which will create an empty object, pass its arguments to the init() method on the new object, and then return it. + + package MyClass; + use Class::MakeMethods::Template::Hash ( + 'new --with_init' => 'new' + 'string' => 'foo' + ); + sub init { + my $self = shift; + $self->foo( shift || 'FooBar' ); # init with arg or default + warn "Initialized new object '$self'"; + } + ... + package main; + my $self = MyClass->new( 'Foozle' ) + +=back + +Some additional notes about these constructors: + +=over 4 + +=item * + +The C<Template::*:new> methods allow you to specify a name for your method other than C<init> by passing the C<init_method> parameter: + + use Class::MakeMethods::Template::Hash ( + 'new --and_then_init' => [ + 'new' => { init_method => 'my_init' } + ], + ); + +=item * + +If you know that you're not going to have a complex class hierarchy, you can reduce resource consumption a bit by changing the above declarations from "*::Hash" to "*::Array" so your objects end up as blessed arrays rather than blessed hashes. + +=back + + +=head2 Changing Method Names + +The Template subclasses allow you to control the names assigned to +the methods you generate by selecting from several naming interfaces. + +For example, the accessors declared above use a default, Perl-ish +style interface, in which a single method can be called without an +argument to retrieve the value, or with an argument to set it. +However, you can also select a more Java-like syntax, with separate +get* and set* methods, by including the '--java' template specification: + + package MyStruct; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'scalar' => '--java Foo', + ); + +(Note that the declaration of Foo could also have been written as +C<'scalar --java' =E<gt> 'Foo'> or C<'scalar' =E<gt> ['--java', +'Foo']>, or C<'scalar' =E<gt> [ 'foo' => { 'interface'=>'java' } +], all of which are interpreted identically; see the +L<Class::MakeMethods> section on "Argument Normalization" for +details.) + +Usage of this accessor would then be as follows: + + package main; + use MyStruct; + + my $obj = MyStruct->new( setFoo => "Foozle" ); + print $obj->getFoo(); + $obj->setFoo("Bozzle"); + + +=head2 Selecting Specific Helper Methods + +You can use the ability to specify interfaces to select specific helper methods rather than getting the default collection. + +For example, let's say you wanted to use a Template::Hash:array, but you only wanted two methods to be installed in your class, a foo() accessor and a shift_foo() mutator. Any of the below combinations of syntax should do the trick: + + use Class::MakeMethods::Template::Hash + 'array' => [ + 'foo' => { interface=>{'foo'=>'get_set', 'shift_foo'=>'shift'} }, + ]; + +If you're going to have a lot of methods with the same interface, you could pre-declare a named interface once and use it repeatedly: + + BEGIN { + require Class::MakeMethods::Template::Hash; + Class::MakeMethods::Template::Hash->named_method('array')-> + {'interface'}->{'my_get_set_shift'} = + { '*'=>'get_set', 'shift_*'=>'shift' }; + } + + use Class::MakeMethods::Template::Hash + 'array --my_get_set_shift' => [ 'foo', 'bar' ]; + + +=head2 Tree Structure Example + +In this example we will create a pair of classes with references +to other objects. + +The first class is a single-value data object implemented as a +reference to a scalar. + + package MyTreeData; + use Class::MakeMethods::Template::Scalar ( + 'new' => 'new', + 'string' => 'value', + ); + +The second class defines a node in a tree, with a constructor, an +accessor for a data object from the class above, and accessors for +a list of child nodes. + + package MyTreeNode; + use Class::MakeMethods::Template::Hash ( + 'new' => 'new', + 'object -class MyTreeData' => 'data', + 'array_of_objects -class MyTreeNode' => 'children', + ); + + sub depth_first_data { + my $self = shift; + return $self->data, map { $_->depth_first_data() } $self->children; + } + +Here's a sample of how the above classes could be used in a program. + + package main; + use MyTreeData; + use MyTreeNode; + + my $node = MyTreeNode->new( + data => { value=>'data1' }, + children => [ { value=>'data3' } ] + ); + $node->push_children( MyTreeNode->new( data => { value=>'data2' } ) ); + + foreach my $data ( $node->depth_first_data ) { + print $data->value(); + } + + +=head1 SEE ALSO + +See L<Class::MakeMethods> for general information about this distribution. + +=head2 Annotated Tutorials + +Ron Savage has posted a pair of annotated examples, linked to below. +Each demonstrates building a class with MakeMethods, and each +includes scads of comments that walk you through the logic and +demonstrate how the various methods work together. + + http://savage.net.au/Perl-tutorials.html + http://savage.net.au/Perl-tutorials/tut-33.tgz + http://savage.net.au/Perl-tutorials/tut-34.tgz + +=cut |
