Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages


home | help
Alzabo::MethodMaker(3)User Contributed Perl DocumentatioAlzabo::MethodMaker(3)



	   $pod	.= $params if $params;

	   return $pod;

       package Alzabo::ClassDocs;

       use Params::Validate qw(	validate SCALAR	);

       use base	qw(Alzabo::Docs);

       sub new {
	   my $class = shift;
	   my %p = validate( @_, { group   => {	type =>	SCALAR },
				   description => { type => SCALAR },
				 } );

	   return bless	\%p, $class;

       sub as_pod {
	   my $self = shift;

	   return ucfirst "$self->{description}\n\n";



       Alzabo::MethodMaker - Auto-generate useful methods based	on an existing

	 use Alzabo::MethodMaker ( schema => 'schema_name', all	=> 1 );

       This module can take an existing	schema and generate a number of	useful
       methods for this	schema and its tables and rows.	 The method making is
       controlled by the parameters given along	with the use statement,	as
       seen in the SYNOPSIS section.

       These parameters	are all	passed to the module when it is	imported via

       o   schema => $schema_name

	   This	parameter is required.

       o   class_root => $class_name

	   If given, this will be used as the root of the class	names
	   generated by	this module.  This root	should not end in '::'.	 If
	   none	is given, then the calling module's name is used as the	root.
	   See New Class Names for more	information.

       o   all => $bool

	   This	tells this module to make all of the methods it	possibly can.
	   See METHOD CREATION OPTIONS for more	details.

	   If individual method	creation options are set as false, then	that
	   setting will	be respected, so you could use

	     use Alzabo::MethodMaker( schema =>	'foo', all => 1, tables	=> 0 );

	   to turn on all of the regular options except	for "tables".

       o   name_maker => \&naming_sub

	   If provided,	then this callback will	be called any time a method
	   name	needs to be generated.	This allows you	to have	full control
	   over	the resulting names.  Otherwise	names are generated as
	   described in	the documentation.

	   The callback	is expected to return a	name for the method to be
	   used.  This name should not be fully	qualified or contain any class
	   designation as this will be handled by MethodMaker.

	   It is important that	none of	the names returned conflict with
	   existing methods for	the object the method is being added to.

	   For example,	when adding methods that return	column objects to a
	   table, if you have a	column called 'name' and try to	use that as
	   the method name, it won't work.  "Alzabo::Table" objects already
	   have	such a method, which returns the name of the table.  See the
	   relevant documentation of the schema, table,	and row	objects	for a
	   list	of methods they	contain.

	   The NAMING SUB PARAMETERS section contains the details of what
	   parameters are passed to this callback.

	   Please note that if you have	a large	complex	schema you will	almost
	   certainly need to provide a custom naming subroutine	to avoid name

       Using this module has several effects on	your schema's objects.

   New Class Names
       Your schema, table, and row objects to be blessed into subclasses of
       "Alzabo::Runtime::Schema", "Alzabo::Runtime::Table",
       "Alzabo::Runtime::Row", respectively.  These subclasses contain the
       various methods created by this module.	The new	class names are	formed
       by using	the "class_root" parameter and adding onto it.

       In order	to make	it convenient to add new methods to the	table and row
       classes,	the created table classes are all subclasses of	a new class
       based on	your class root, and the same thing is done for	all created
       row classes.

       o   Schema

	     <class root>::Schema

       o   Tables

	     <class root>::Table::<table name>

	   All tables will be subclasses of:

	     <class root>::Table

       o   Rows

	     <class root>::Row::<table name>

	   All rows will be subclasses of:

	     <class root>::Row

       With a root of "My::MovieDB", and a schema with only two	tables,
       "Movie" and "Image", this would result in the following class names:


	My::MovieDB::Table::Movie - subclass of	My::MovieDB::Table
	My::MovieDB::Row::Movie	  - subclass of	My::MovieDB::Row

	My::MovieDB::Table::Image - subclass of	My::MovieDB::Table
	My::MovieDB::Row::Image	  - subclass of	My::MovieDB::Row

   Loading Classes
       For each	class into which an object is blessed, this module will
       attempt to load that class via a	"use" statement.  If there is no
       module found this will not cause	an error.  If this class defines any
       methods that have the same name as those	this module generates, then
       this module will	not attempt to generate	them.

       When using Alzabo::MethodMaker, you may specify any of the following
       parameters.  Specifying "all" causes all	of them	to be used.

   Schema object methods
       o   tables => $bool

	   Creates methods for the schema that return the table	object
	   matching the	name of	the method.

	   For example,	given a	schema containing tables named "Movie" and
	   "Image", this would create methods that could be called as
	   "$schema->Movie" and	"$schema->Image".

   Table object	methods.
       o   table_columns => $bool

	   Creates methods for the tables that return the column object
	   matching the	name of	the method.  This is quite similar to the
	   "tables" option for schemas.	 So if our "Movie" table had a column
	   called "title", we could write "$schema->Movie->title".

       o   insert_hooks	=> $bool

	   Look	for hooks to wrap around the "insert()"	method in
	   "Alzabo::Runtime::Table".  See Loading Classes for more details.
	   You have to define either a "pre_insert()" and/or "post_insert()"
	   method for the generated table class	or this	parameter will not do
	   anything.  See the HOOKS section for	more details.

   Row object methods
       o   row_columns => $bool

	   This	tells MethodMaker to create get/set methods for	each column a
	   row has.  These methods take	a single optional argument, which if
	   given will cause that column	to be updated for the row.

       o   update_hooks	=> $bool

	   Look	for hooks to wrap around the "update" method in
	   "Alzabo::Runtime::Row".  See	Loading	Classes	for more details.  You
	   have	to define a "pre_update()" and/or "post_update()" method for
	   the generated row class or this parameter will not do anything.
	   See the HOOKS section for more details.

       o   select_hooks	=> $bool

	   Look	for hooks to wrap around the "select" method in
	   "Alzabo::Runtime::Row".  See	Loading	Classes	for more details.  You
	   have	to define either a "pre_select()" and/or "post_select()"
	   method for the generated row	class or this parameter	will not do
	   anything.  See the HOOKS section for	more details.

       o   delete_hooks	=> $bool

	   Look	for hooks to wrap around the "delete" method in
	   "Alzabo::Runtime::Row".  See	Loading	Classes	for more details.  You
	   have	to define either a "pre_delete()" and/or "post_delete()"
	   method for the generated row	class or this parameter	will not do
	   anything.  See the HOOKS section for	more details.

       o   foreign_keys	=> $bool

	   Creates methods in row objects named	for the	table to which the
	   relationship	exists.	 These methods return either a single
	   "Alzabo::Runtime::Row" object or a single
	   "Alzabo::Runtime::RowCursor"	object,	depending on the cardinality
	   of the relationship.

	   For exa

	     Movie		       Credit
	     ---------		       --------
	     movie_id		       movie_id
	     title		       person_id

	   This	would create a method for Movie	row objects called "Credit()"
	   which would return a	cursor for the associated Credit table rows.
	   Similarly, Credit row objects would have a method called "Movie()"
	   which would return the associated Movie row object.

       o   linking_tables => $bool

	   A linking table, as defined here, is	a table	with a two column
	   primary key,	with each column being a foreign key to	another
	   table's primary key.	 These tables exist to facilitate n..n logical
	   relationships.  If both "foreign_keys" and "linking_tables" are
	   true, then methods will be created that skip	the intermediate
	   linking tables.

	   For example,	with the following tables:

	     User	    UserGroup	     Group
	     -------	    ---------	     --------
	     user_id	    user_id	     group_id
	     user_name	    group_id	     group_name

	   The "UserGroup" table exists	solely to facilitate the n..n
	   relationship	between	"User" and "Group".  User row objects will
	   have	a "Group()" method, which returns a row	cursor of Group	row
	   objects.  And Group row objects will	have a "User()"	method which
	   returns a row cursor	of User	row objects.

       o   lookup_columns => $bool

	   Lookup columns are columns in foreign tables	to which a table has a
	   many-to-one or one-to-one relationship to the foreign table's
	   primary key.	 For example, given the	tables below:

	     Restaurant			   Cuisine
	     ---------			   --------
	     restaurant_id		   cuisine_id
	     restaurant_name   (n..1)	   description
	     phone			   spiciness

	   In this example, Restaurant row objects would have
	   "Cuisine_description()" and "Cuisine_spiciness" methods which
	   returned the	corresponding values from the "Cuisine"	table.

       o   self_relations => $bool

	   A self relation is when a table has a parent/child relationship
	   with	itself.	 Here is an example:


	   NOTE: If the	relationship has a cardinality of 1..1 then no methods
	   will	be created, as this option is really intended for parent/child
	   relationships.  This	may change in the future.

	   In this case, Location row objects will have	both "parent()"	and
	   "children()"	methods.  The parent method returns a single row,
	   while the "children()" method returns a row cursor of Location

       As was mentioned	previously, it is possible to create pre- and post-
       execution hooks to wrap around a	number of methods.  This allows	you to
       do data validation on inserts and updates as well as giving you a
       chance to filter	incoming or outgoing data as needed.  For example,
       this can	be used	to convert dates to and	from a specific	RDBMS format.

       All hooks are inside a transaction which	is rolled back if any part of
       the process fails.

       It should be noted that Alzabo uses both	the
       "Alzabo::Runtime::Row->select" and "Alzabo::Runtime::Row->delete"
       methods internally.  If their behavior is radically altered through the
       use of hooks, then some of Alzabo's functionality may be	broken.

       Given this, it may be safer to create new methods to fetch and massage
       data rather than	to create post-select hooks that alter data.

       Each of these hooks receives different parameters, documented below:

   Insert Hooks
       o   pre_insert

	   This	method receives	a hash reference of all	the parameters that
	   are passed to the "Alzabo::Runtime::Table->insert()"	method.

	   These are the actual	parameters that	will be	passed to the "insert"
	   method so alterations to this reference will	be seen	by that
	   method.  This allows	you to alter the values	that actually end up
	   going into the database or change any other parameters as you see

       o   post_insert

	   This	method also receives a hash reference containing all of	the
	   parameters passed to	the "insert()" method.	In addition, the hash
	   reference contains an additional key, "row",	which contains the
	   newly created row.

   Update Hooks
       o   pre_update

	   This	method receives	a hash reference of the	parameters that	will
	   be passed to	the "Alzabo::Runtime::Row->update()" method.  Again,
	   alterations to these	parameters will	be seen	by the "update"

       o   post_update

	   This	method receives	the same parameters as "pre_update()"

   Select Hooks
       o   pre_select

	   This	method receives	an array reference containing the names	of the
	   requested columns.  This is called when either the
	   "Alzabo::Runtime::Row->select()" or
	   "Alzabo::Runtime::Row->select_hash()" methods are called.

       o   post_select

	   This	method is called after the "Alzabo::Runtime::Row->select()" or
	   "Alzabo::Runtime::Row->select_hash()" methods.  It receives a hash
	   containing the name and values returned from	the revelant method,
	   which it may	modify.	 If the	values of this hash reference are
	   modified, then this will be seen by the original caller.

   Delete hooks
       o   pre_delete

	   This	method receives	no parameters.

       The naming sub will receive a hash containing the following parameters:

       o   type	=> $method_type

	   This	will always be the same	as one of the parameters you give to
	   the import method.  It will be one of the following:	"foreign_key",
	   "linking_table", "lookup_columns", "row_column", "self_relation",
	   "table", "table_column".

       The following parameters	vary from case to case,	depending on the value
       of "type".

       When the	type is	"table":

       o   table => Alzabo::Table object

	   This	parameter will be passed when the type is "table".  It is the
	   table object	the schema object's method will	return.

       When the	type is	"table_column" or "row_column":

       o   column => Alzabo::Column object

	   When	the type is "table_column", this is the	column object the
	   method will return.	When the type is "row_column", then it is the
	   column whose	value the method will return.

       When the	type is	"foreign_key", "linking_table",	or "self_relation":

       o   foreign_key => Alzabo::ForeignKey object

	   This	is the foreign key on which the	method is based.

       It is possible to create	an n..n	relationship between a table and
       itself, and MethodMaker will attempt to generate	linking	table methods
       for such	relationships, so your naming sub may need to take this	into

       When the	type is	"foreign_key":

       o   plural => $bool

	   This	indicates whether or not the method that is being created will
	   return a cursor object (true) or a row object (false).

       When the	type is	"linking_table":

       o   foreign_key_2 => Alzabo::ForeignKey object

	   When	making a linking table method, two foreign keys	are used.  The
	   "foreign_key" is from the table being linked	from to	the linking
	   table.  This	parameter is the foreign key from the linking table to
	   the table being linked to.

       When the	type is	"lookup_columns":

       o   column => Alzabo::Column object

	   When	making lookup column methods, this column is the column	in the
	   foreign table for which a method is being made.

       When the	type is	"self_relation":

       o   parent => $boolean

	   This	indicates whether or not the method being created will return
	   parent objects (true) or child objects (false).

       Here is an example that covers all of the possible options:

	use Lingua::EN::Inflect;

	sub namer
	    my %p = @_;

	    # Table object can be returned from	the schema via methods such as $schema->User_t;
	    return $p{table}->name . '_t' if $p{type} eq 'table';

	    # Column objects are returned similarly, via $schema->User_t->username_c;
	    return $p{column}->name . '_c' if $p{type} eq 'table_column';

	    # If I have	a row object, I	can get	at the columns via their
	    # names, for example $user->username;
	    return $p{column}->name if $p{type}	eq 'row_column';

	    # This manipulates the table names a bit to	generate names.	 For
	    # example, if I have a table called	UserRating and a 1..n
	    # relationship from	User to	UserRating, I'll end up	with a method
	    # on rows in the User table	called ->Ratings which returns a row
	    # cursor of	rows from the UserRating table.
	    if ( $p{type} eq 'foreign_key' )
		my $name = $p{foreign_key}->table_to->name;
		my $from = $p{foreign_key}->table_from->name;
		$name =~ s/$from//;

		if ($p{plural})
		    return my_PL( $name	);
		    return $name;

	    # This is very similar to how foreign keys are handled.  Assume
	    # we have the tables Restaurant, Cuisine, and RestaurantCuisine.
	    # If we are	generating a method for	the link from Restaurant
	    # through to Cuisine, we'll	have a method on Restaurant table
	    # rows called ->Cuisines, which will return	a cursor of rows from
	    # the Cuisine table.
	    # Note: this will generate a bad name if given a linking table
	    # that links a table to itself.
	    if ( $p{type} eq 'linking_table' )
		my $method = $p{foreign_key}->table_to->name;
		my $tname = $p{foreign_key}->table_from->name;
		$method	=~ s/$tname//;

		return my_PL($method);

	    # Lookup columns are columns if foreign tables for which there
	    # exists a one-to-one or many-to-one relationship.	In cases such
	    # as these,	it is often the	case that the foreign table is rarely
	    # used on its own, but rather it primarily used as a lookup	table
	    # for values that should appear to be part of other	tables.
	    # For example, an Address table might have a many-to-one
	    # relationship with	a State	table.	The State table	would contain
	    # the columns 'name' and 'abbreviation'.  If we have
	    # an Address table row, it is convenient to	simply be able to say
	    # $address->state_name and $address->state_abbreviation.

	    if ( $p{type} eq 'lookup_columns' )
		return join '_', map { lc $_->name } $p{foreign_key}->table_to,	$p{column};

	    # This should be fairly self-explanatory.
	    return $p{parent} ?	'parent' : 'children'
		if $p{type} eq 'self_relation';

	    # And just to make sure that nothing slips by us we	do this.
	    die	"unknown type in call to naming	sub: $p{type}\n";

	# Lingua::EN::Inflect did not handle the word 'hours' properly when this was written
	sub my_PL
	    my $name = shift;
	    return $name if $name =~ /hours$/i;

	    return Lingua::EN::Inflect::PL($name);

       This module keeps track of methods that are generated and can in	turn
       generate	basic POD for those methods.

       Any schema that has had methods generated for it	by Alzabo::MethodMaker
       will have an additional method, "docs_as_pod".  This will return
       documentation for the schema object's methods, as well as any
       documentation available for objects that	the schema contains, in	this
       case tables.  The tables	in turn	return their own documentation plus
       that of their contained row classes.

       It is also possible to call the "docs_as_pod" method on any generated
       table or	row class individually.

       A simple	script like the	following can be used to send all of the
       generated documentation to "STDOUT".

	 use Alzabo::MethodMaker ( schema => 'foo', all	=> 1 );

	 my $s = Alzabo::Runtime::Schema->load_from_file( name => 'foo'	);

	 print $s->docs_as_pod;

       Dave Rolsky, <>

perl v5.24.1			  2017-07-02		Alzabo::MethodMaker(3)


Want to link to this manual page? Use this URL:

home | help