package Rose::DB::Registry; use strict; use Carp(); use Rose::DB::Registry::Entry; use Rose::Object; our @ISA = qw(Rose::Object); our $VERSION = '0.728'; our $Debug = 0; # # Object data # use Rose::Object::MakeMethods::Generic ( 'scalar' => [ qw(error) ], 'scalar --get_set_init' => [ 'hash', 'parent', ], ); # # Object methods # sub init_hash { {} } sub init_parent { 'Rose::DB' } sub add_entries { my($self) = shift; # Smuggle parent in with an otherwise nonsensical arrayref arg my $parent = shift->[0] if(ref $_[0] eq 'ARRAY'); $parent ||= $self->parent; my $entries = $self->hash; my @added; foreach my $item (@_) { my($domain, $type, $entry); if(ref $item eq 'HASH') { if($entry = delete $item->{'entry'}) { $domain = delete $item->{'domain'}; $type = delete $item->{'type'}; if(keys(%$item)) { Carp::croak "If an 'entry' parameter is passed, no other ", "parameters (other than 'domain' and 'type') ", "may be passed"; } } else { $entry = Rose::DB::Registry::Entry->new(%$item); } } elsif(ref $item && $item->isa('Rose::DB::Registry::Entry')) { $entry = $item; } else { Carp::croak "Don't know how to add registry entry '$item'" } $domain = $entry->domain unless(defined $domain); $type = $entry->type unless(defined $type); unless(defined $domain) { $domain = $parent->default_domain; $entry->domain($domain); } unless(defined $type) { $type = $parent->default_type; $entry->type($type); } Carp::confess "$parent - Missing domain for registry entry: domain '$domain', type '$type'" unless(defined $domain); Carp::confess "$parent - Missing type for registry entry: domain '$domain', type '$type'" unless(defined $type); Carp::confess "$parent - Missing driver for registry entry: domain '$domain', type '$type'" unless(defined $entry->driver); $entries->{$domain}{$type} = $entry; push(@added, $entry); } return wantarray ? @added : \@added; } sub add_entry { my($self) = shift; # Smuggle parent in with an otherwise nonsensical arrayref arg my $parent = shift if(ref $_[0] eq 'ARRAY'); if(@_ == 1 || (ref $_[0] && $_[0]->isa('Rose::DB::Registry::Entry'))) { return ($self->add_entries(($parent ? $parent : ()), @_))[0]; } return ($self->add_entries(($parent ? $parent : ()), { @_ }))[0]; } sub entry_exists { my($self, %args) = @_; Carp::croak "Missing required 'type' argument" unless(defined $args{'type'}); Carp::croak "Missing required 'domain' argument" unless(defined $args{'domain'}); return exists $self->hash->{$args{'domain'}}{$args{'type'}}; } sub delete_entry { my($self, %args) = @_; return undef unless($self->entry_exists(%args)); return delete $self->hash->{$args{'domain'}}{$args{'type'}}; } sub entry { my($self, %args) = @_; return undef unless($self->entry_exists(%args)); return $self->hash->{$args{'domain'}}{$args{'type'}}; } sub delete_domain { my($self, $domain) = @_; my $entries = $self->hash; delete $entries->{$domain}; } sub registered_types { my($self, $domain) = @_; my @types = sort keys %{ $self->hash->{$domain} || {} }; return wantarray ? @types : \@types; } sub registered_domains { my @domains = sort keys %{ shift->hash }; return wantarray ? @domains : \@domains; } sub dump { my($self) = shift; my $entries = $self->hash; my %reg; foreach my $domain ($self->registered_domains) { foreach my $type ($self->registered_types($domain)) { $reg{$domain}{$type} = $entries->{$domain}{$type}->dump; } } return \%reg; } 1; __END__ =head1 NAME Rose::DB::Registry - Data source registry. =head1 SYNOPSIS use Rose::DB::Registry; $registry = Rose::DB::Registry->new; $registry->add_entry( domain => 'development', type => 'main', driver => 'Pg', database => 'dev_db', host => 'localhost', username => 'devuser', password => 'mysecret', server_time_zone => 'UTC'); $entry = Rose::DB::Registry::Entry->new( domain => 'production', type => 'main', driver => 'Pg', database => 'big_db', host => 'dbserver.acme.com', username => 'dbadmin', password => 'prodsecret', server_time_zone => 'UTC'); $registry->add_entry($entry); $entry = $registry->entry(domain => 'development', type => 'main'); $registry->entry_exists(domain => 'foo', type => 'bar'); # false $registry->delete_entry(domain => 'development', type => 'main'); ... =head1 DESCRIPTION L objects manage information about L data sources. Each data source has a corresponding L object that contains its information. The registry entries are organized in a two-level namespace based on a "domain" and a "type." See the L documentation for more information on data source domains and types. L inherits from, and follows the conventions of, L. See the L documentation for more information. =head1 CONSTRUCTOR =over 4 =item B Constructs a L object based on PARAMS, where PARAMS are name/value pairs. Any object method is a valid parameter name. =back =head1 OBJECT METHODS =over 4 =item B Add registry entries. Each ENTRY must be either a L-derived object or reference to a hash of name/value pairs. The name/value pairs must be valid arguments for L's constructor. Each ENTRY must have a defined domain and type, either in the L-derived object or in the name/value pairs. A fatal error will occur if these values are not defined. If a registry entry for the specified domain and type already exists, then the new entry will overwrite it. If you want to know beforehand whether or not an entry exists under a specific domain and type, use the L method. Returns a list (in list context) or reference to an array (in scalar context) of L objects added. =item B Add a registry entry. ENTRY must be either a L-derived object or a list of name/value pairs. The name/value pairs must be valid arguments for L's constructor. The ENTRY must have a defined domain and type, either in the L-derived object or in the name/value pairs. A fatal error will occur if these values are not defined. If a registry entry for the specified domain and type already exists, then the new entry will overwrite it. If you want to know beforehand whether or not an entry exists under a specific domain and type, use the L method. Returns the L object added. =item B Returns a reference to a hash containing information about all registered data sources. The hash is structured like this: { domain1 => { type1 => { # Rose::DB::Registry::Entry attributes # generated by its dump() method driver => ..., database => ..., host => ..., ... }, type2 => { ... }, ... }, domain2 => { ... }, ... } All the registry entry attribute values are copies, not the actual values. =item B Delete an entire domain, including all the registry entries under that domain. =item B Delete the registry entry specified by PARAMS, where PARAMS must be name/value pairs with defined values for C and C. A fatal error will occur if either one is missing or undefined. If the specified entry does not exist, undef is returned. Otherwise, the deleted entry is returned. =item B Get the registry entry specified by PARAMS, where PARAMS must be name/value pairs with defined values for C and C. A fatal error will occur if either one is missing or undefined. If the specified entry does not exist, undef is returned. =item B Returns true if the registry entry specified by PARAMS exists, false otherwise. PARAMS must be name/value pairs with defined values for C and C. A fatal error will occur if either one is missing or undefined. =item B Returns a list (in list context) or reference to an array (in scalar context) of the names of all registered types under the domain named DOMAIN. =item B Returns a list (in list context) or reference to an array (in scalar context) of the names of all registered domains. =back =head1 AUTHOR John C. Siracusa (siracusa@mindspring.com) =head1 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.