File Coverage

blib/lib/Data/SCORM/Manifest.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Data::SCORM::Manifest;
2              
3 4     4   27 use Any::Moose;
  4         7  
  4         25  
4 4     4   1854 use Any::Moose qw/ X::AttributeHelpers /;
  4         8  
  4         17  
5 4     4   11902 use XML::Twig;
  0            
  0            
6             use Data::SCORM::Organization;
7             use Data::SCORM::Item;
8             use Data::SCORM::Resource;
9             use JSON::Any;
10              
11             use Data::Dumper;
12              
13             =head1 NAME
14              
15             Data::SCORM::Manifest - represent the Manifest
16              
17             =head1 SYNOPSIS
18              
19             use Data::SCORM::Manifest;
20              
21             my $foo = Data::SCORM::Manifest->new();
22             ...
23              
24             =cut
25              
26             has 'metadata' => (
27             metaclass => 'Collection::Hash',
28             is => 'rw',
29             isa => 'HashRef',
30             default => sub { +{} },
31             provides => {
32             exists => 'has_metadata',
33             keys => 'metadata_ids',
34             get => 'get_metadata',
35             set => 'set_metadata',
36             },
37             );
38              
39             has 'organizations' => (
40             metaclass => 'Collection::Hash',
41             is => 'rw',
42             isa => 'HashRef[Data::SCORM::Organization]',
43             default => sub { +{} },
44             provides => {
45             exists => 'has_organization',
46             keys => 'organization_ids',
47             get => 'get_organization',
48             set => 'set_organization',
49             },
50             );
51              
52             has 'resources' => (
53             metaclass => 'Collection::Hash',
54             is => 'rw',
55             isa => 'HashRef',
56             default => sub { +{} },
57             provides => {
58             exists => 'has_resource',
59             keys => 'resource_ids',
60             get => 'get_resource',
61             set => 'set_resource',
62             },
63             );
64              
65             sub _simplify {
66             my $node = shift;
67             my $data = $node->simplify;
68             return {
69             map {
70             my $v = $data->{$_};
71             (my $k = $_) =~ s/^adlseq://;
72             ($k => $v);
73             }
74             keys %$data
75             };
76             }
77              
78             around 'new' => sub {
79             my ($code, @params) = @_;
80             my $self = $code->(@params);
81             for my $org (values %{$self->organizations}) {
82             for my $item ($org->all_items) {
83             my $id = $item->identifierref or next;
84             if (my $resource = $self->get_resource($id)) {
85             $item->resource($resource);
86             }
87             else {
88             warn "Couldn't get resource $id, if it is an Aggregation item (only contains children) then it should not have an identifierredf";
89             }
90             }
91             }
92             return $self;
93             };
94              
95             sub get_default_organization {
96             my ($self) = @_;
97             return $self->get_organization('default');
98             }
99              
100             sub parsefile {
101             my ($class, $file) = @_;
102              
103             my %data;
104              
105             # TODO: consider whether I want to create the objects from HoH structures
106             # /here/ or to do it in coercions in each class
107              
108             my $t = XML::Twig->new(
109             twig_handlers => {
110             'manifest/metadata' => sub {
111             my ($t, $metadata) = @_;
112             $data{metadata} = _simplify($metadata);
113             # alternatively $metadata->findnodes('lom:lom')... etc. ->delete
114             # i.e. to do something cleverer with lom:lom
115             },
116             'manifest/organizations' => sub {
117             my ($t, $organizations) = @_;
118             my $default = $organizations->att('default'); # required
119             my %organizations = map {
120             my $id = $_->att('identifier');
121             # we want only to be an array
122             my @items = map {
123             $_->delete;
124             Data::SCORM::Item->new(%{
125             _simplify($_)
126             })
127             }
128             $_->findnodes('item');
129             my $org = $_->simplify;
130             $org->{items} = \@items;
131              
132             ($id => Data::SCORM::Organization->new( %$org ))
133             }
134             $organizations->children;
135             # findnodes('organization')
136             $organizations{default} = $organizations{$default};
137             $data{organizations} = \%organizations;
138             },
139             'manifest/resources' => sub {
140             my ($t, $resources) = @_;
141             my %resources = map
142             {
143             my $res = _simplify($_);
144             my $res_object = Data::SCORM::Resource->new(%$res);
145             # warn Dumper($res, $res_object);
146             ($_->att('identifier') => $res_object);
147             }
148             $resources->children;
149             # findnodes('resource')
150             $data{resources} = \%resources;
151             },
152             }
153             );
154              
155             # we could just let it die, or use safe_parsefile, but I think it's useful
156             # to wrap the error message
157             eval {
158             $t->parsefile( $file )
159             };
160             die "Couldn't parse SCORM manifest $file\: $@" if $@;
161              
162             return $class->new(%data);
163             }
164              
165             sub as_hoh {
166             # turn this into a normal perl data structure that we can jsonnify
167             my ($self, $url_base) = @_;
168             $url_base ||= '';
169              
170             my %organizations = map {
171             my $org_name = $_;
172             my $org = $self->get_organization($org_name);
173             my @resources = map {
174             my $id = $_->identifierref;
175             my $res = $self->get_resource($id);
176             my @files = map {
177             "$url_base/$_->{href}"
178             } $res->all_files;
179             +{ %$res, file => \@files }; # naive object flattening
180             } $org->all_items;
181              
182             my %org = %$org;
183             $org{resources} = \@resources;
184              
185             my @items = map {
186             my $item = { %$_ };
187             $item->{resource} = { %{ $item->{resource} } };
188             $item;
189             } @{ $org{items} };
190             $org{items} = \@items;
191              
192             ( $org_name => \%org );
193             } $self->organization_ids;
194              
195             return {
196             metadata => +{ %{$self->metadata} },
197             organizations => \%organizations,
198             };
199             }
200              
201             sub to_json {
202             my $self = shift;
203             my $hoh = $self->as_hoh(@_); # e.g. the $url_base param
204             my $js = JSON::Any->new( allow_blessed => 1 );
205             return $js->to_json($hoh);
206             }
207              
208             # __PACKAGE__->make_immutable;
209             no Any::Moose;
210              
211             =head1 AUTHOR
212              
213             osfameron, C<< >>
214              
215             =head1 BUGS
216              
217             Please report any bugs or feature requests to C, or through
218             the web interface at L. I will be notified, and then you'll
219             automatically be notified of progress on your bug as I make changes.
220              
221             =head1 SUPPORT
222              
223             You can find documentation for this module with the perldoc command.
224              
225             perldoc Data::SCORM::Manifest
226              
227             You can also look for information at:
228              
229             =over 4
230              
231             =item * RT: CPAN's request tracker
232              
233             L
234              
235             =item * Search CPAN
236              
237             L
238              
239             =back
240              
241              
242             =head1 ACKNOWLEDGEMENTS
243              
244              
245             =head1 COPYRIGHT & LICENSE
246              
247             Copyright 2009 OSFAMERON.
248              
249             This program is free software; you can redistribute it and/or modify it
250             under the terms of either: the GNU General Public License as published
251             by the Free Software Foundation; or the Artistic License.
252              
253             See http://dev.perl.org/licenses/ for more information.
254              
255             =cut
256              
257             1; # End of Data::SCORM::Manifest