NAME¶
Rose::DB::Tutorial - Best practices for using Rose::DB
INTRODUCTION¶
This tutorial describes "best practices" for using Rose::DB in the
most robust, maintainable manner. It does not replace the actual
documentation, however. The actual Rose::DB documentation is still essential,
and contains some good examples of its own.
In particular, you should read the description section of the Rose::DB
documentation if you have not done so already. It describes the features and
philosophy of Rose::DB. That information that will not be repeated here.
CONVENTIONS¶
The examples in this tutorial will use the fictional "My::" namespace
prefix. Your code should use whatever namespace you deem appropriate. Usually,
it will be more akin to "MyCorp::MyProject::" (i.e., your
corporation, organization, and/or project). I've chosen to use
"My::" simply because it's shorter, and will help this tutorial stay
within an 80-column width.
For the sake of brevity, the "use strict" directive and associated
"my" declarations have been omitted from the example code. Needless
to say, you should always "use strict" in your actual code.
Similarly, the traditional "1;" true value used at the end of each
".pm" file has been omitted from the examples. Don't forget to add
this to the end of your actual Perl module files.
TUTORIAL¶
Creating a subclass¶
The first step when using Rose::DB in anything but a throw-away script is to
create a trivial subclass. This is important because Rose::DB has a
significant amount of class data. Using Rose::DB directly means that you will
be reading and writing the same data as any other badly-behaved code that also
uses Rose::DB directly.
In particular, the registry that contains all the information for each data
source is class data, and is inherited from (that is, shared with) the base
class by default. Creating a subclass allows you to have your own, private
data source registry.
So, here's our initial Rose::DB subclass.
# File: My/DB.pm
package My::DB;
use Rose::DB;
our @ISA = qw(Rose::DB);
# Use a private registry for this class
__PACKAGE__->use_private_registry;
Designing your namespace¶
As described in the Rose::DB documentation, Rose::DB provides a two-level
namespace for data sources, made up of a "domain" and a
"type." These are both arbitrary strings, so there's a lot of
freedom to break up the namespace in any way you see fit. For example,
sub-domains and sub-types can be created within each string using delimiter
characters (e.g., "::" as in Perl's package namespace).
But let's back up. The simplest case is that you have just one data source, and
therefore no need for a namespace at all. If this is the case, you can skip to
the next section.
In the common case, it's usually sufficient to use simple words for both type
and domain. As the name "domain" implies, this value usually
represents the environment or surroundings. For example, a typical server
application might use domains named "development", "qa",
"staging", and "production".
The "type" portion of the namespace tends to be used to differentiate
the applicability or contents of the data sources. Some example type names are
"main" for the primary database, "archive" for a data
warehouse database, and "session" for a database used to store
transient session data.
The goal of namespace design is to allow data sources to be referred to
symbolically, with names that make sense to you in your environment.
Registering data sources¶
Now that you've decided on your namespace design (or lack thereof, if you have
only one data source), it's time to register some data sources. To register a
data source, call the register_db class method.
This can be done nearly anywhere, but it's most convenient to do it
"early" and to link it somehow to your "My::DB" subclass.
That is, when someone "use"s "My::DB", they should not
have to worry about whether or not all the data sources are registered.
In a server environment, there's usually some sort of start-up file that gets
loaded before any "end-user" code (e.g., "startup.pl" by
convention in a mod_perl Apache web server). That may be a good place to
include your data source registration calls, but only if you're absolutely
sure that "My::DB" will never be used outside the server
environment.
A better, safer alternative is to put the data source registration calls
directly in your Rose::DB subclass. This is the recommended approach. Here are
some examples.
Just one data source
First, consider the case where a namespace is not necessary. You have a single
data source and that's all. You don't care what it's named. Luckily, there are
default values for both type and domain. Simply register your data source
using these values and you're all set.
package My::DB;
use Rose::DB;
our @ISA = qw(Rose::DB);
# Use a private registry for this class
__PACKAGE__->use_private_registry;
# Register your lone data source using the default type and domain
__PACKAGE__->register_db(
domain => My::DB->default_domain,
type => My::DB->default_type,
driver => 'pg',
database => 'my_db',
host => 'localhost',
username => 'joeuser',
password => 'mysecret',
);
The domain and type parameters can actually be omitted entirely and they will
still default to the values shown above. In other words, the following call to
register_db is exactly equivalent to the one above.
# Register your lone data source using the default type and domain
__PACKAGE__->register_db(
driver => 'pg',
database => 'my_db',
host => 'localhost',
username => 'joeuser',
password => 'mysecret',
);
To use "My::DB" in this kind of setup, simply omit the domain and type
parameters from your calls to "My::DB->new". They will
automatically get the default values.
use My::DB;
$db = My::DB->new(); # use default type and default domain
print $db->username; # "joeuser"
$dbh = $db->dbh; # connect and get DBI database handle
Multiple data sources
Most commonly, you will have more than one data source. (And if you don't now,
you probably will in the future. Better safe than sorry.) After you've
designed your namespace, data source registration is straightforward. The only
wrinkle is how to deal with the default domain and type.
I recommend setting the default domain and type to the "safest" values
in your environment. For example, a domain of "development" and a
type of "main" are reasonable choices. This allows you to use
"bare" calls to "My::DB->new()" in your code (as shown
in the simple, single data source example above).
Here's an example that includes two domains "development" and
"production", and two types, "main" and
"session." The default data source is the domain
"development" and the type "main".
package My::DB;
use Rose::DB;
our @ISA = qw(Rose::DB);
# Use a private registry for this class
__PACKAGE__->use_private_registry;
# Set the default domain and type
__PACKAGE__->default_domain('development');
__PACKAGE__->default_type('main');
# Register the data sources
# Development:
__PACKAGE__->register_db(
domain => 'development',
type => 'main',
driver => 'pg',
database => 'dev_db',
host => 'localhost',
username => 'devuser',
password => 'mysecret',
);
__PACKAGE__->register_db(
domain => 'development',
type => 'session',
driver => 'mysql',
database => 'session_db',
host => 'localhost',
username => 'devmysql',
password => 'mysqlpw',
);
# Production:
__PACKAGE__->register_db(
domain => 'production',
type => 'main',
driver => 'pg',
database => 'big_db',
host => 'dbserver.mycorp.com',
username => 'dbadmin',
password => 'prodsecret',
);
__PACKAGE__->register_db(
domain => 'production',
type => 'session',
driver => 'mysql',
database => 'session_db',
host => 'sessions.mycorp.com',
username => 'session_user',
password => 'prodsesspw',
);
Ideally, and as shown in the example above, all data source types are available
in each domain. Combined with the consistent practice of never specifying an
explicit domain when constructing your "My::DB" objects, this allows
the domain to be switched as needed, without modifying any code in the actual
application.
For example, imagine a mod_perl Apache web server environment running
application code that constructs its "My::DB" objects like this:
$main_db = My::DB->new('main');
$session_db = My::DB->new('session');
Now imagine a "startup.pl" file that contains the following:
# File: startup.pl
use My::DB;
if($ENV{'MYCORP_PRODUCTION_SERVER'})
{
My::DB->default_domain('production');
}
else
{
My::DB->default_domain('development');
}
This deliberate use of defaults combined with a healthy dose of convention in
your constructor calls can make it simple to move your code from one
environment to another without any changes beyond the usual configuration
management that must be done (e.g., for apache configuration files).
The determination of the current environment can be done in many different ways,
of course. Checking an environment variable as shown above is probably not the
best way to do it, but it makes for a simple example.
Another alternative is to use some sort of configuration/build management system
to generate the Apache configuration files from templates. In that case, the
templates could contain something like this:
[% IF in_production %]
My::DB->default_domain('production');
[% ELSE %]
My::DB->default_domain('development');
[% END %]
This would leave only the single, appropriate call in the completed
"startup.pl" file.
Using your database objects¶
Before trying to use Rose::DB objects, it's important to understand the primary
goals of Rose::DB. The features are described in the Rose::DB documentation,
but there is one thing that is left unsaid. Although Rose::DB is useful in
isolation and provides many convenient methods and abstractions, its primary
purpose is to encapsulate database-specific behaviors on behalf of
Rose::DB::Object.
Of course, it could fill the same role for any Rose::DB::Object-like module, and
for any code that does the same kinds of things. If you need to parse or
format vendor-specific column values or want to use a simple form of reference
counting to keep track of shared database handles, you may find Rose::DB
useful.
The most common non-Rose::DB::Object-related use for Rose::DB is as a way to get
a DBI database handle without sweating the details of how it's created or
where it's connected. The previous sections of this tutorial cover everything
you need to know to set up Rose::DB to be used in this capacity. Please be
sure to read the Rose::DB documentation as well, particularly the database
handle life-cycle management section.
DEVELOPMENT POLICY¶
The Rose development policy applies to this, and all "Rose::*"
modules. Please install Rose from CPAN and then run "perldoc Rose"
for more information.
SUPPORT¶
Any Rose::DB questions or problems can be posted to the Rose::DB::Object mailing
list. (If the volume ever gets high enough, I'll create a separate list for
Rose::DB. But it isn't an issue right now.) To subscribe to the list or view
the archives, go here:
<
http://groups.google.com/group/rose-db-object>
Although the mailing list is the preferred support mechanism, you can also email
the author (see below) or file bugs using the CPAN bug tracking system:
<
http://rt.cpan.org/NoAuth/Bugs.html?Dist=Rose-DB>
There's also a wiki and other resources linked from the Rose project home page:
<
http://rose.googlecode.com>
AUTHOR¶
John C. Siracusa (siracusa@gmail.com)
COPYRIGHT¶
Copyright (c) 2007 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.