File Coverage

blib/lib/Catmandu/Importer/Z3950.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Catmandu::Importer::Z3950;
2              
3 2     2   51012 use Catmandu::Sane;
  2         235538  
  2         9  
4 2     2   404 use Catmandu::Util qw(:is);
  2         3  
  2         445  
5 2     2   11 use Scalar::Util qw(blessed);
  2         7  
  2         72  
6 2     2   6 use Carp;
  2         4  
  2         74  
7 2     2   7 use Moo;
  2         1  
  2         8  
8 2     2   957 use ZOOM;
  0            
  0            
9              
10             our $VERSION = '0.05';
11              
12             with 'Catmandu::Importer';
13              
14             # INFO:
15             # http://www.loc.gov/z3950/
16              
17             # Constants. -------------------------------------------------------------------
18              
19             use constant PREFERREDRECORDSYNTAX => 'USMARC';
20             use constant PORT => 210;
21             use constant QUERYTYPE => 'CQL';
22              
23             # Properties. ------------------------------------------------------------------
24              
25             # required.
26             has host => (is => 'ro', required => 1);
27             has databaseName => (is => 'ro', required => 1);
28             has query => (is => 'rw');
29              
30             # optional.
31             has port => (is => 'ro', default => sub { return PORT; });
32             has preferredRecordSyntax => (is => 'ro', default => sub { return PREFERREDRECORDSYNTAX; });
33             has user => (is => 'ro');
34             has password => (is => 'ro');
35             has queryType => (is =>'ro', default => sub { return QUERYTYPE; }); # <CQL | PQF>
36             has handler => (is => 'rw', lazy => 1 , builder => 1, coerce => \&_coerce_handler );
37              
38             # internal stuff.
39             has _conn => (is => 'ro');
40             has _qry => (is => 'ro');
41             has _currentRecordSet => (is => 'ro');
42             has _n => (is => 'ro', default => sub { 0 });
43              
44             # Internal Methods. ------------------------------------------------------------
45              
46             sub _build_handler {
47             my ($self) = @_;
48              
49             if ($self->preferredRecordSyntax eq 'USMARC') {
50             Catmandu::Util::require_package('Catmandu::Importer::Z3950::Parser::USMARC')->new;
51             }
52             else {
53             return sub { return { record => $_[0] } };
54             }
55             }
56            
57             sub _coerce_handler {
58             my ($handler) = @_;
59            
60             return $handler if is_invocant($handler) or is_code_ref($handler);
61            
62             if ($handler eq 'RAW') {
63             return sub { return { record => $_[0] } };
64             }
65             elsif (is_string($handler) && !is_number($handler)) {
66             my $class = $handler =~ /^\+(.+)/ ? $1
67             : "Catmandu::Importer::Z3950::Parser::$handler";
68            
69             my $handler;
70             eval {
71             $handler = Catmandu::Util::require_package($class)->new;
72             };
73             if ($@) {
74             croak $@;
75             } else {
76             return $handler;
77             }
78             }
79             else {
80             die "unknown handler type $handler";
81             }
82             }
83              
84             sub _setup_connection {
85             my ($self) = @_;
86              
87             my $conn = ZOOM::Connection->new(
88             $self->host,
89             $self->port,
90             databaseName => $self->databaseName
91             );
92              
93             $conn->option(preferredRecordSyntax => $self->preferredRecordSyntax) if $self->preferredRecordSyntax;
94             $conn->option(user => $self->user) if $self->user;
95             $conn->option(password => $self->password) if $self->password;
96              
97             return $conn;
98             }
99              
100             sub _get_query {
101             my ($self) = @_;
102             my $qry;
103              
104             if ($self->queryType eq 'CQL') {
105             $qry = ZOOM::Query::CQL->new($self->query); # 'title=dinosaur'
106             }
107             elsif ($self->queryType eq 'PQF') {
108             $qry = ZOOM::Query::PQF->new($self->query); # '@attr 1=4 dinosaur'
109             }
110              
111             return $qry;
112             }
113              
114             sub _nextRecord {
115             my ($self) = @_;
116            
117             unless ($self->_conn) {
118             $self->_clean;
119             $self->{_conn} = $self->_setup_connection;
120             }
121              
122             unless ($self->_qry) {
123             $self->_clean;
124             $self->{_qry} = $self->_get_query;
125             }
126              
127             unless ($self->_currentRecordSet) {
128             $self->{_currentRecordSet} = $self->{_conn}->search($self->{_qry});
129             $self->{_n} = 0;
130             }
131              
132             my $size = $self->_currentRecordSet->size() || 0;
133              
134             if ($self->{_n} < $size) {
135             my $rec = $self->_currentRecordSet->record($self->{_n}++)->get("raw");
136             return blessed($self->handler)
137             ? $self->handler->parse($rec)
138             : $self->handler->($rec);
139             }
140             else {
141             $self->_clean;
142             return undef;
143             }
144             }
145              
146             sub _clean {
147             my ($self) = @_;
148             $self->{_currentRecordSet}->destroy() if $self->{_currentRecordSet};
149             $self->{_qry}->destroy() if $self->{_qry};
150             $self->{_currentRecordSet} = undef;
151             $self->{_qry} = undef;
152             $self->{_n} = 0;
153             }
154              
155             sub DESTROY {
156             my ($self) = @_;
157            
158             if ($self->_conn) {
159             $self->_conn->destroy();
160             }
161             }
162              
163              
164             # Public Methods. --------------------------------------------------------------
165              
166             sub generator {
167             my ($self) = @_;
168              
169             return sub {
170             $self->_nextRecord;
171             };
172             }
173              
174              
175             # PerlDoc. ---------------------------------------------------------------------
176              
177             =head1 NAME
178              
179             Catmandu::Importer::Z3950 - Package that imports Z3950 data
180              
181             =head1 SYNOPSIS
182              
183             # On the command line
184              
185             $ catmandu convert Z3950 --host z3950.loc.gov --port 7090 --databaseName Voyager --query "(title = dinosaur)"
186            
187             # From Perl
188              
189             use Catmandu;
190              
191             my $importer = Catmandu->importer('Z3950'
192             host => 'z3950.loc.gov',
193             port => 7090,
194             databaseName => "Voyager",
195             preferredRecordSyntax => "USMARC",
196             queryType => 'PQF', # CQL or PQF
197             query => '@attr 1=4 dinosaur'
198             );
199              
200             my $n = $importer->each(sub {
201             my $hashref = $_[0];
202             ...
203             });
204              
205             =cut
206              
207             =head1 CONFIGURAION
208              
209             =over
210              
211             =item host
212              
213             The Z3950 host name
214              
215             =item port
216              
217             The Z3950 port
218              
219             =item user
220              
221             A user name
222              
223             =item password
224              
225             A password
226              
227             =item databaseName
228              
229             The database to connect to
230              
231             =item preferredRecordSyntax
232              
233             The preferred response format (default: USMARC)
234              
235             =item queryType
236              
237             The queryType (CQL or PQF)
238              
239             =item query
240              
241             The query
242              
243             =item handler
244              
245             The Perl handler to parse the response content. This should be a package name in the Catmandu::Importer::Z3950::Parser namespace or 'RAW'
246             for unparsed content.
247              
248             =back
249              
250             =head1 REQUIREMENTS
251              
252             This package uses the ZOOM package internally.
253             For more info visit: http://search.cpan.org/~mirk/Net-Z3950-ZOOM-1.28/lib/ZOOM.pod
254              
255             The ZOOM package has a hard dependency on YAZ toolkit.
256             For more info about YAZ, visit: https://www.indexdata.com/yaz
257              
258             Installing YAZ:
259             - (osx, using homebrew): brew install yaz
260             - (linux, using yum): yum install yaz libyaz
261              
262             =head1 AUTHOR
263              
264             =over
265              
266             =item * Wouter Willaert, C<< <wouterw@inuits.eu> >>
267              
268             =item * Patrick Hochstenbach, C<< <patrick.hochstenbach@ugent.be> >>
269              
270             =back
271              
272             =head1 SEE ALSO
273              
274             L<Catmandu::Iterable>
275              
276             =cut
277              
278             1;