File Coverage

blib/lib/Catmandu/Store/FedoraCommons.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Catmandu::Store::FedoraCommons;
2              
3 2     2   169299 use Catmandu::Sane;
  2         297765  
  2         14  
4 2     2   1472 use Catmandu::FedoraCommons;
  0            
  0            
5             use Moo;
6              
7             with 'Catmandu::Store';
8              
9             has baseurl => (is => 'ro' , required => 1);
10             has username => (is => 'ro' , default => sub { '' } );
11             has password => (is => 'ro' , default => sub { '' } );
12             has model => (is => 'ro' , default => sub { 'Catmandu::Store::FedoraCommons::DC' } );
13              
14             has fedora => (
15             is => 'ro',
16             init_arg => undef,
17             lazy => 1,
18             builder => '_build_fedora',
19             );
20             has _repository_description => (
21             is => 'ro',
22             init_arg => undef,
23             lazy => 1,
24             builder => '_build_repository_description'
25             );
26             has _default_namespace => (
27             is => 'ro',
28             init_arg => undef,
29             lazy => 1,
30             builder => '_build_default_namespace'
31             );
32             has _pid_delimiter => (
33             is => 'ro',
34             init_arg => undef,
35             lazy => 1,
36             builder => '_build_pid_delimiter'
37             );
38              
39             sub _build_fedora {
40             my $self = $_[0];
41              
42             Catmandu::FedoraCommons->new($self->baseurl, $self->username, $self->password);
43             }
44              
45             #namespace corresponds to name of bag
46             #don't use "data", but use the internal default namespace of fedora
47             around default_bag => sub {
48             my($orig,$self) = @_;
49             $self->_default_namespace();
50             };
51              
52             sub _build_repository_description {
53             $_[0]->fedora->describeRepository()->parse_content();
54             }
55             sub _build_default_namespace {
56             my $self = $_[0];
57             my $desc = $self->_repository_description();
58             $desc->{repositoryPID}->{'PID-namespaceIdentifier'};
59             }
60             sub _build_pid_delimiter {
61             my $self = $_[0];
62             my $desc = $self->_repository_description();
63             $desc->{repositoryPID}->{'PID-delimiter'};
64             }
65              
66             package Catmandu::Store::FedoraCommons::Bag;
67              
68             use Catmandu::Sane;
69             use Catmandu::Store::FedoraCommons::FOXML;
70             use Moo;
71             use Catmandu::Util qw(:is);
72              
73             with 'Catmandu::Bag';
74              
75             has _namespace_prefix => (
76             is => 'ro',
77             init_arg => undef,
78             lazy => 1,
79             builder => '_build_namespace_prefix'
80             );
81             has _namespace_prefix_re => (
82             is => 'ro',
83             init_arg => undef,
84             lazy => 1,
85             builder => '_build_namespace_prefix_re'
86             );
87              
88             sub _build_namespace_prefix {
89             my $self = $_[0];
90             my $name = $self->name();
91             my $pid_delimiter = $self->store->_pid_delimiter();
92             "${name}${pid_delimiter}";
93             }
94             sub _build_namespace_prefix_re {
95             my $self = $_[0];
96             my $p = $self->_namespace_prefix();
97             qr/$p/;
98             }
99             sub _id_valid {
100             my ($self,$id) = @_;
101             return ( index( $id, $self->_namespace_prefix() ) == 0 ) ? 1 : 0;
102             }
103              
104             #add namespace to generated ID if it does not start with the namespace prefix
105             before add => sub {
106             my ($self, $data) = @_;
107             unless( $self->_id_valid( $data->{_id} ) ) {
108             $data->{_id} = $self->_namespace_prefix().$data->{_id};
109             }
110             };
111             #make it impossible to find 'islandora:1' in bag 'archive.ugent.be'
112             around 'get' => sub {
113             my($orig,$self,$id) = @_;
114              
115             return undef unless $self->_id_valid( $id );
116              
117             $orig->($self,$id);
118             };
119             #make it impossible to delete 'islandora:1' when using bag 'archive.ugent.be'
120             around 'delete' => sub {
121             my($orig,$self,$id) = @_;
122              
123             return undef unless $self->_id_valid( $id );
124              
125             $orig->($self,$id);
126             };
127              
128             sub _get_model {
129             my ($self, $obj) = @_;
130             my $pid = $obj->{pid};
131             my $fedora = $self->store->fedora;
132             my $model = $self->store->model;
133              
134             eval "use $model";
135             my $x = $model->new(fedora => $fedora);
136             my $res = $x->get($pid);
137              
138             return $res;
139             }
140              
141             sub _update_model {
142             my ($self, $obj) = @_;
143             my $fedora = $self->store->fedora;
144             my $model = $self->store->model;
145              
146             eval "use $model";
147             my $x = $model->new(fedora => $fedora);
148             my $res = $x->update($obj);
149              
150             return $res;
151             }
152              
153             sub _ingest_model {
154             my ($self, $data) = @_;
155              
156             my $serializer = Catmandu::Store::FedoraCommons::FOXML->new;
157              
158             my ($valid,$reason) = $serializer->valid($data);
159              
160             unless ($valid) {
161             warn "data is not valid";
162             return undef;
163             }
164              
165             my $xml = $serializer->serialize($data);
166              
167             my %args = (
168             pid => $data->{_id} ,
169             xml => $xml ,
170             format => 'info:fedora/fedora-system:FOXML-1.1'
171             );
172              
173             my $result = $self->store->fedora->ingest(%args);
174              
175             return undef unless $result->is_ok;
176              
177             $data->{_id} = $result->parse_content->{pid};
178              
179             return $self->_update_model($data);
180             }
181              
182             sub generator {
183             my ($self) = @_;
184             my $fedora = $self->store->fedora;
185              
186             sub {
187             state $hits;
188             state $row;
189             state $ns_prefix = $self->_namespace_prefix;
190              
191             if( ! defined $hits) {
192             my $res = $fedora->findObjects( query => "pid~${ns_prefix}*" );
193             unless ($res->is_ok) {
194             warn $res->error;
195             return undef;
196             }
197             $row = 0;
198             $hits = $res->parse_content;
199             }
200             if ($row + 1 == @{ $hits->{results} } && defined $hits->{token}) {
201             my $result = $hits->{results}->[ $row ];
202              
203             my $res = $fedora->findObjects(sessionToken => $hits->{token});
204              
205             unless ($res->is_ok) {
206             warn $res->error;
207             return undef;
208             }
209              
210             $row = 0;
211             $hits = $res->parse_content;
212              
213             return $self->_get_model($result);
214             }
215             else {
216             my $result = $hits->{results}->[ $row++ ];
217             return $self->_get_model($result);
218             }
219             };
220             }
221              
222             sub add {
223             my ($self,$data) = @_;
224              
225             if ( defined $self->get($data->{_id}) ) {
226             my $ok = $self->_update_model($data);
227              
228             die "failed to update" unless $ok;
229             }
230             else {
231             my $ok = $self->_ingest_model($data);
232              
233             die "failed to ingest" unless $ok;
234             }
235              
236             return $data;
237             }
238              
239             sub get {
240             my ($self, $id) = @_;
241             return $self->_get_model({ pid => $id });
242             }
243              
244             sub delete {
245             my ($self, $id) = @_;
246              
247             return undef unless defined $id;
248              
249             my $fedora = $self->store->fedora;
250              
251             $fedora->purgeObject(pid => $id)->is_ok;
252             }
253              
254             sub delete_all {
255             my ($self) = @_;
256              
257             my $count = 0;
258             $self->each(sub {
259             my $obj = $_[0];
260             my $pid = $obj->{_id};
261              
262             my $ret = $self->delete($pid);
263              
264             $count += 1 if $ret;
265             });
266              
267             $count;
268             }
269              
270             1;
271              
272             =head1 NAME
273              
274             Catmandu::Store::FedoraCommons - A Catmandu::Store plugin for the Fedora Commons repository
275              
276             =head1 SYNOPSIS
277              
278             use Catmandu::Store::FedoraCommons;
279              
280             my $store = Catmandu::Store::FedoraCommons->new(
281             baseurl => 'http://localhost:8080/fedora',
282             username => 'fedoraAdmin',
283             password => 'fedoraAdmin',
284             model => 'Catmandu::Store::FedoraCommons::DC' # default
285             );
286              
287             # We use the DC model, lets store some DC
288             my $obj1 = $store->bag->add({
289             title => ['The Master and Margarita'] ,
290             creator => ['Bulgakov, Mikhail'] }
291             );
292              
293             printf "obj1 stored as %s\n" , $obj1->{_id};
294              
295             # Force an id in the store
296             my $obj2 = $store->bag->add({ _id => 'demo:120812' , title => ['The Master and Margarita'] });
297              
298             my $obj3 = $store->bag->get('demo:120812');
299              
300             $store->bag->delete('demo:120812');
301              
302             $store->bag->delete_all;
303              
304             # All bags are iterators
305             $store->bag->each(sub {
306             my $obj = $_[0];
307             my $pid = $obj->{_id};
308             my $ds = $store->fedora->listDatastreams(pid => $pid)->parse_content;
309             });
310              
311             $store->bag->take(10)->each(sub { ... });
312              
313             =head1 DESCRIPTION
314              
315             A Catmandu::Store::FedoraCommons is a Perl package that can store data into
316             FedoraCommons backed databases. The database as a whole is called a 'store'.
317             Databases also have compartments (e.g. tables) called Catmandu::Bag-s.
318             In Fedora we have namespaces. A bag corresponds to a namespace.
319             The default bag corresponds to the default namespace in Fedora.
320              
321             By default Catmandu::Store::FedoraCommons works with a Dublin Core data model.
322             You can use the add,get and delete methods of the store to retrieve and insert Perl HASH-es that
323             mimic Dublin Core records. Optionally other models can be provided by creating
324             a model package that implements a 'get' and 'update' method.
325              
326             =head1 METHODS
327              
328             =head2 new(baseurl => $fedora_baseurl , username => $username , password => $password , model => $model )
329              
330             Create a new Catmandu::Store::FedoraCommons store at $fedora_baseurl. Optionally provide a name of
331             a $model to serialize your Perl hashes into a Fedora Commons model.
332              
333             =head2 bag('$namespace')
334              
335             Create or retrieve a bag. Returns a Catmandu::Bag.
336             Use this for storing or retrieving records from a
337             fedora namespace.
338              
339             =head2 fedora
340              
341             Returns a low level Catmandu::FedoraCommons reference.
342              
343             =head1 INHERITED METHODS
344              
345             This Catmandu::Store implements:
346              
347             =over 3
348              
349             =item L<Catmandu::Store>
350              
351             =back
352              
353             Each Catmandu::Bag in this Catmandu::Store implements:
354              
355             =over 3
356              
357             =item L<Catmandu::Bag>
358              
359             =back
360              
361             =head1 SEE ALSO
362              
363             L<Catmandu::FedoraCommons>
364              
365             =head1 AUTHOR
366              
367             =over
368              
369             =item * Patrick Hochstenbach, C<< <patrick.hochstenbach at ugent.be> >>
370              
371             =back
372              
373             =cut