При использовании адресной книги вам наверно понадобится импортировать и экспортировать данные в стандартном формате. Вы можете добавить веб интерфейс для этого, но иногда легче написать простой shell скрипт для парсирования данных и внедрение в базу данных. Благодаря гибкостьи Catalyst, скрипт запушенный вне приложения может использовать ту же Model (а также другие компоненты) что и приложение, и манипулировать базой данных напрямую.
Использование схемы DBIx::Class извне очень просто. Все что нам надо это использовать схему (use Shema), подключится к ней и добавить данные.
Давайте напишем скрипт в папке script, который будет принимать строки в формате CSV и добавлять их в базу данных. Начнем с создания файла script/import_csv.pl и напишем немного документации
#!/usr/bin/env perl
use strict;
use warnings;
=head1 NAME
import_csv.pl - imports CSV files into the address book
=head1 FORMAT
The data should be in CSV format with the following fields:
firstname, lastname, address location, address, phone, email
=cut
Потом для парсирования CSV файла загрузим в наш скрипт модуль Text::CSV_XS
use Text::CSV_XS;
А вот хитрая часть
use FindBin qw($Bin);
use Path::Class;
use lib dir($Bin, '..', 'lib')->stringify;
Мы хотим что бы наше приложение работало на любой платформе и имела возможность нахождения конфигурационного файла так, что бы мы могли автоматически находить базу данных. Мы начали с использования FindBin для нахождения имени нашего скрипта ( которое отныне хранится в переменной $Bin ). Поскольку мы разместили наш скрипт в script/import_csv.pl и наше приложение Catalyst укоренилось на уровень выше, мы использовали модуль Path::Class для генереции имени каталога который находится на уровень выше каталога нашего скрипта. Мы использовали Path::Class вместо того что бы просто написать $Bin/../lib для того что бы наш скриптработал в любой операционной системе (а не только UNIX ). после того как мы имеем имя файла мы передаем его прагме lib для того что бы Perl искал модули (наш Schema ) в этом пути.
Теперь мы можем безопасно загрузить схему и YAML для парсирования файла config.
use AddressBook::Schema::AddressDB;
use Config::JFDI;
use strict;
Мы используем Config::JFDI для загрузки значений из конфигурационного файла. Config::JFDI заменяет __HOME__ соответсвующим путем подобно Catalyst::ConfigLoader. Более подробно об этом модуле можете почитать по адресу http://search.cpan.org/~rkrimen/Config-JFDI-0.064/lib/Config/JFDI.pm.
my $filename = file($Bin, '..', 'addressbook.conf');
my $config = Config::JFDI->new(path => $filename);
my $dsn = $config->get->{'Model::AddressDB'}->{connect_info};
Теперь строка подключения к базе данных в переменной $dsn и мы можем подключится к схеме.
my $schema =
AddressBook::Schema::AddressDB->connect($dsn)
or die "Failed to connect to database at $dsn";
Отсюда все работает также как и в нашем приложении, с несколькоми исключениями. Вместо использования $c->model('AddressDB::TableName') мы пишем $schema->resultset("TableName").
while(my $line = <>){
eval {
my $csv = Text::CSV_XS->new();
$csv->parse($line) or die "Invalid data";
my ($first, $last, $location, $address, $phone, $email)
= $csv->fields();
my $person = $schema->resultset('People')->
find_or_create({ firstname => $first, lastname => $last,
});
$schema->resultset('Addresses')->
create({ person => $person, location => $location, postal =>
$address,
phone => $phone, email => $email,
});
print "Added @{[$person->name]}'s $location address.\n";
};
if($@){
warn "Problem adding address: $@";
}
}
Основная цепочка очен проста. Сначала мы используем Text::CSV_XS для парсирования данных в поля имя, фамилия и тд. Потом мы используем имя и фамилию для нахождения записи в таблице people( или создания таковой если она отсутствует). Потом с помощю обекта person мы создаем соответсвующую строку в таблице address, используя оставшиеся данные. Если это сработало, то печатаем сообшение о том что мы добавили в базу данных нового пользователья и переходим к следующей строке файла данных.
Мы также заключили всю процедуру в eval{}, так что мы можем печатать сообшение об ошибке, если таковая имеется, и переходить к следующей строке.
Нам надо свелать еще что то до того как мы сможем запустить скрипт. Метод find_or_create работает только если вы гаранируете DBIx::Class что каждая комбинация firstname и Lastname уникальна (это делается для того что бы добавлять адреса к уже существующим людам). Мы сделаем это создав UNIQUE ограничение в базе данных и добавим следующую строку в AddressBook::Schema::Result::People:
__PACKAGE__->add_unique_constraint(name =>
[qw/firstname lastname/]);
Это укажет DBIx::Class что столбци firstname и lastname вместе уникальны (как primary key).Мы дали имя этому ограничению name что бы отличить его от других ограничений.
После токо как вы добавили эту строку к вашей схеме вы можете запустить ваш скрипт.
$ perl script/import_csv.pl
Test,Person,Home,123 Home St.,123-456-7890,home@example.com
Test,Person,Work,123 Work ST.,890-123-4567,work@example.com
^D
$
Тепер у нас на сайте мы можем увидеть Test Person с адресами HOMe и Work. Как видите иногда очень удобно обращатся к вашему приложению без веб интерфейса. Некоторые редкие операции, такие как например массовый импорт, легче исполнять через командную строку.
В завершение напишем скрипт генерирующий произвольные CSV данные.
#!/usr/bin/perl
=head1 NAME
random_addresses.pl – generate random addresses
=head1 USAGE
Run like C<perl script/random_addresses.pl | perl script/import_csv.
pl>
=cut
my @names = qw(Foo Bar Baz Test Jonathan Rockway Person Another A);
my @locations = qw(Home Work Mobile Fax Test);
my @streets = qw(Green Oak Elm 60th Fake State Halsted);
for(1..200){
my $first = $names[rand @names];
my $last = $names[rand @names];
my $where = $locations[rand @locations];
my $number = int rand 9900 + 100; # 3-digit street number my $street
= $streets[rand @streets];
my $address = "$number $street St.";
my $phone = join '-', (int rand 800 + 100, int rand 899 + 100, int
rand 8999 + 1000);
my $email = "$first.$last\@$where.example.com";
print "$first,$last,$where,$address,$phone,$email\n";
}
Потом запустим
$ perl script/random_addresses.pl | perl script/import_csv.pl
После чего у нас в базе окажется 200 новых адресов, это просто для следуюшего раздела про поиск.
Благодаря импорту скрипта наше приложение теперь использует всю мощ командной строки UNIX.