Rose::DB::Object::Metadata::Column(3pm) | User Contributed Perl Documentation | Rose::DB::Object::Metadata::Column(3pm) |
Rose::DB::Object::Metadata::Column - Base class for database column metadata objects.
package MyColumnType; use Rose::DB::Object::Metadata::Column; our @ISA = qw(Rose::DB::Object::Metadata::Column); ...
This is the base class for objects that store and manipulate database column metadata. Column metadata objects store information about columns (data type, size, etc.) and are responsible for parsing, formatting, and creating object methods that manipulate column values.
Rose::DB::Object::Metadata::Column objects stringify to the value returned by the name method. This allows full-blown column objects to be used in place of column name strings in most situations.
A Rose::DB::Object::Metadata::Column-derived object is responsible for creating object methods that manipulate column values. Each column object can make zero or more methods for each available column method type. A column method type describes the purpose of a method. The default column method types are:
Methods are created by calling make_methods. A list of method types can be passed to the call to make_methods. If absent, the list of method types is determined by the auto_method_types method. A list of all possible method types is available through the available_method_types method.
These methods make up the "public" interface to column method creation. There are, however, several "protected" methods which are used internally to implement the methods described above. (The word "protected" is used here in a vaguely C++ sense, meaning "accessible to subclasses, but not to the public.") Subclasses will probably find it easier to override and/or call these protected methods in order to influence the behavior of the "public" method maker methods.
A Rose::DB::Object::Metadata::Column object delegates method creation to a Rose::Object::MakeMethods-derived class. Each Rose::Object::MakeMethods-derived class has its own set of method types, each of which takes it own set of arguments.
Using this system, four pieces of information are needed to create a method on behalf of a Rose::DB::Object::Metadata::Column-derived object:
This information can be organized conceptually into a "method map" that connects a column method type to a method maker class and, finally, to one particular method type within that class, and its arguments.
The default method map is:
Each item in the map is a column method type. For each column method type, the method maker class, the method maker method type, and the "interesting" method maker arguments are listed, in that order.
The "..." in the method maker arguments is meant to indicate that other arguments have been omitted. For example, the column object's default value is passed as part of the arguments for all method types. These arguments that are common to all column method types are routinely omitted from the method map for the sake of brevity. If there are no "interesting" method maker arguments, then "..." may appear by itself.
The purpose of documenting the method map is to answer the question, "What kind of method(s) will be created by this column object for a given method type?" Given the method map, it's possible to read the documentation for each method maker class to determine how methods of the specified type behave when passed the listed arguments.
To this end, each Rose::DB::Object::Metadata::Column-derived class in the Rose::DB::Object module distribution will list its method map in its documentation. This is a concise way to document the behavior that is specific to each column class, while omitting the common functionality (which is documented here, in the column base class).
Remember, the existence and behavior of the method map is really implementation detail. A column object is free to implement the public method-making interface however it wants, without regard to any conceptual or actual method map. It must then, of course, document what kinds of methods it makes for each of its method types, but it does not have to use a method map to do so.
Triggers allow code to run in response to certain column-related events. An event may trigger zero or more pieces of code. The names and behaviors of the various kinds of events are as follows.
Each piece of code responding to an "on_get" event will be passed a single argument: a reference to the object itself. The return value is not used.
The "on_set" event occurs after the column value has been set. Each piece of code responding to an "on_set" event will be passed a single argument: a reference to the object itself. The return value is not used.
The "on_load" event occurs after the column value has been loaded. Each piece of code responding to an "on_load" event will be passed a single argument: a reference to the object itself. The return value is not used.
Each piece of code responding to an "on_save" event will be passed a single argument: a reference to the object itself. The return value is not used.
Inflation will only happen "as needed." That is, a value that has already been inflated will not be inflated again, and a value that comes from the database and goes back into it without ever being retrieved by end-user code will never be inflated at all.
Each piece of code responding to an "inflate" event will be passed two arguments: a reference to the object itself and the value to be inflated. It should return an inflated version of that value. Note that the value to be inflated may have come from the database, or from end-user code. Be prepared to handle almost anything.
Deflation will only happen "as needed." That is, a value that has already been deflated will not be deflated again, and a value that comes from the database and goes back into it without ever being retrieved by end-user code will never need to be deflated at all.
Each piece of code responding to a "deflate" event will be passed two arguments: a reference to the object itself and the value to be deflated. It should return a deflated version of that value suitable for saving into the currently connected database. Note that the value to be deflated may have come from the database, or from end-user code. Be prepared to handle almost anything.
All triggers are disabled while inside code called in response to a trigger event. Such code may call any other column methods, including methods that belong to its own column, without fear of infinite recursion into trigger service subroutines. Alternately, triggers may be explicitly enabled if desired. Just watch out for infinite loops.
For performance reasons, none of the column classes bundled with Rose::DB::Object use triggers by default. Some of them do inflate and deflate values, but they do so internally (inside the accessor and mutator methods created by the Rose::Object::MakeMethods-derived classes that service those column types). You can still add triggers to these column types, but the interaction between the internal inflate/deflate actions and the triggers for those same events can become a bit "non-obvious."
This default only applies when the column does not have a parent metadata object or if the metadata object's column_undef_overrides_default method returns undef.
$column->method_name('get') || $column->method_name('get_set');
If there are only two arguments, and the first is a valid event name, then the second must be a code reference. Otherwise, the arguments are taken as named parameters.
Valid parameters are:
If omitted, the position defaults to "end."
Examples:
# Add trigger using an event name and a code reference $column->add_trigger(on_set => sub { print "set!\n" }); # Same as above, but using named parameters $column->add_trigger(event => 'on_set', code => sub { print "set!\n" }); # Same as the above, but with a custom name and explicit position $column->add_trigger(event => 'on_set', code => sub { print "set!\n" }, name => 'debugging', position => 'end');
A fatal error will occur if a matching trigger cannot be found.
Examples:
# Add two named triggers $column->add_trigger(event => 'on_set', code => sub { print "set!\n" }, name => 'debugging'); $column->add_trigger(event => 'on_set', code => sub { shift->do_something() }, name => 'side_effect'); # Delete the side_effect trigger $column->delete_trigger(event => 'on_set', name => 'side_effect'); # Fatal error: no trigger subroutine for this column # named "nonesuch" for the event type "on_set" $column->delete_trigger(event => 'on_set', name => 'nonesuch');
Valid event names are "on_get", "on_set", "on_load", "on_save", "inflate", and "deflate". See the triggers section of this documentation for more information on these event types.
Note: a column that is part of a primary key cannot be loaded on demand.
__PACKAGE__->meta->columns ( ... notes => { type => 'text', length => 1024, lazy => 1 }, );
without requiring the longer "load_on_demand" parameter name to be used.
If any of the methods could not be created for any reason, a fatal error will occur.
Note: the method is named "manager_uses_method" instead of, say, "query_builder_uses_method" because Rose::DB::Object::QueryBuilder is rarely used directly. Instead, it's mostly used indirectly through the Rose::DB::Object::Manager class.
$column->method_name('set') || $column->method_name('get_set');
$column->method_name('get_set');
This method is necessary because some DBI drivers do not (or cannot) always do the right thing when binding values to placeholders in SQL statements. For example, consider the following SQL for the Informix database:
CREATE TABLE test (d DATETIME YEAR TO SECOND); INSERT INTO test (d) VALUES (CURRENT);
This is valid Informix SQL and will insert a row with the current date and time into the "test" table.
Now consider the following attempt to do the same thing using DBI placeholders (assume the table was already created as per the CREATE TABLE statement above):
$sth = $dbh->prepare('INSERT INTO test (d) VALUES (?)'); $sth->execute('CURRENT'); # Error!
What you'll end up with is an error like this:
DBD::Informix::st execute failed: SQL: -1262: Non-numeric character in datetime or interval.
In other words, DBD::Informix has tried to quote the string "CURRENT", which has special meaning to Informix only when it is not quoted.
In order to make this work, the value "CURRENT" must be "inlined" rather than bound to a placeholder when it is the value of a "DATETIME YEAR TO SECOND" column in an Informix database.
All of the information needed to make this decision is available to the call to should_inline_value. It gets passed a Rose::DB-derived object, from which it can determine the database driver, and it gets passed the actual value, which it can check to see if it matches "/^current$/i".
This is just one example. Each subclass of Rose::DB::Object::Metadata::Column must determine for itself when a value needs to be inlined.
If passed a code ref or a reference to an array of code refs, then the list of trigger subroutines for EVENT is replaced with those code ref(s).
Returns a reference to an array of trigger subroutines for the event type EVENT. If there are no triggers for EVENT, undef will be returned.
The default value of this attribute is determined by the parent metadata object's column_undef_overrides_default method, or the column's default_undef_overrides_default class method id the metadata object's column_undef_overrides_default method returns undef, or if the column has no parent metadata object.
Example: consider a Rose::DB::Object-derived "Person" class with a "name" column set up like this:
package Person; ... columns => [ name => { type => 'varchar', default => 'John Doe' }, ... ], ...
The following behavior is the same regardless of the setting of the undef_overrides_default attribute for the "name" column:
$p = Person->new; print $p->name; # John Doe $p->name('Larry Wall'); print $p->name; # Larry Wall
If undef_overrides_default is false for the "name" column, then this is the behavior of explicitly setting the column to undef:
$p->name(undef); print $p->name; # John Doe
If undef_overrides_default is true for the "name" column, then this is the behavior of explicitly setting the column to undef:
$p->name(undef); print $p->name; # undef
The undef_overrides_default attribute can be set directly on the column:
name => { type => 'varchar', default => 'John Doe', undef_overrides_default => 1 },
or it can be set class-wide using the meta object's column_undef_overrides_default attribute:
Person->meta->column_undef_overrides_default(1);
or it can be set for all classes that use a given Rose::DB::Object::Metadata-derived class using the default_column_undef_overrides_default class method:
My::DB::Object::Metadata->default_column_undef_overrides_default(1);
These methods are not part of the public interface, but are supported for use by subclasses. Put another way, given an unknown object that "isa" Rose::DB::Object::Metadata::Column, there should be no expectation that the following methods exist. But subclasses, which know the exact class from which they inherit, are free to use these methods in order to implement the public API described above.
Returns the name of the Rose::Object::MakeMethods-derived class used to create the object method of type TYPE.
Returns the method maker method type for the column method type TYPE.
John C. Siracusa (siracusa@gmail.com)
Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2020-06-21 | perl v5.30.3 |