AddressBook
view release on metacpan or search on metacpan
lib/AddressBook.pm view on Meta::CPAN
=item Z<>
If one match is found, then:
=over 8
=item Z<>
If the records match, nothing is done.
=item Z<>
If the records do not match, then:
=over 10
=item Z<>
If the slave record's timestamp is newer, the master's entry is merged (see below)
with the slave entry's data.
=item Z<>
If the master record's timestamp is newer, nothing is done.
=back
=back
=back
=item 2
The slave database is truncated.
=item 3
Each record of the master is added to the slave
=back
The 'merging' of the master and slave entries involves taking each attribute in the
slave's entry and replacing the corresponding attribute in the master's entry.
Note that attributes that are deleted only on the slave are therefore effectively ignored
during synchronization.
Similarly, deletions made on the slave database are effectively ignored during
synchronization.
=cut
sub sync {
my %args = @_;
my $master = $args{master};
my $slave = $args{slave};
unless ($master->{key_fields} && $slave->{key_fields}) {
croak "Key fields must be defined for both master and slave backends";
}
$slave->reset;
my ($entry,$filter,$key,$count,@non_keys, $slave_entry_attrs,
%slave_keys,$master_entry,$flag,$master_tmp,$slave_tmp,$msg);
foreach $key (split ',', $slave->{key_fields}) {
$slave_keys{$key} = "";
}
foreach (grep {! exists $slave_keys{$_}} $slave->get_attribute_names) {
push @non_keys, $_;
}
my (%seen, @master_only, @slave_only);
@seen{$slave->get_cannonical_attribute_names} = ();
foreach ($master->get_cannonical_attribute_names) {
push (@master_only,$_) unless exists $seen{$_};
}
@seen{$master->get_cannonical_attribute_names} = ();
foreach ($slave->get_cannonical_attribute_names) {
push (@slave_only,$_) unless exists $seen{$_};
}
while ($entry = $slave->read) {
$filter = AddressBook::Entry->new(config=>$slave->{config},
attr=>$entry->{attr});
$filter->delete(attrs=>\@non_keys,db=>$slave->{db_name});
$count = $master->search(filter=>$filter->{attr});
$msg = join "\n", $filter->dump;
$msg .= "matched: $count\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
if ($count == 1) {
$master_entry = $master->read;
$master_tmp = $master_entry;
$master_tmp->delete(attrs=>\@master_only);
$slave_tmp = $entry;
$slave_tmp->delete(attrs=>\@slave_only);
if (AddressBook::Entry::compare($slave_tmp,$master_tmp)) {
$msg = "**entries match**\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
} else {
$msg = "slave entry timestamp: " . $entry->{timestamp} . "\n";
$msg .= "master entry timestamp: " . $master_entry->{timestamp} . "\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
$flag = Date_Cmp($entry->{timestamp},$master_entry->{timestamp});
if ($flag < 0) {
$msg = "**master is newer**\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
} else {
$msg = "**slave is newer - updating master**\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
$slave_entry_attrs = $entry->get(values_only=>1);
$master_entry->replace(attr=>$slave_entry_attrs);
$master->update(entry=>$master_entry,filter=>$filter->{attr}) || croak $master->code;
}
}
} elsif ($count == 0) {
$msg = "**Entry not found in master - adding**:\n".$entry->dump."\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
$master->add($entry) || croak $master->code;;
} else {croak "Error: entry matched multiple entries in master!\n"}
}
$msg = "Truncating slave\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
$slave->truncate;
$master->reset;
$msg = "Adding master's records to slave\n";
if ($args{debug}) {print $msg}
if ($args{msg_function}) {&{$args{msg_function}}($msg)}
while ($entry = $master->read) {
$slave->write($entry);
}
}
=head2 search
$abook->search(attr=>\%filter);
while ($entry=$abook->read) {
print $entry->dump;
}
\%filter is a list of cannonical attribute/value pairs.
=cut
sub search {
my $self = shift;
my $class = ref $self || croak "Not a method call.";
carp "Method not implemented."
}
=head2 read
$entry=$abook->read;
Returns an AddressBook::Entry object
=cut
sub read {
my $self = shift;
( run in 3.464 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )