File Coverage

blib/lib/InterMine/Model.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package InterMine::Model;
2              
3 1     1   27235 use strict;
  1         2  
  1         50  
4 1     1   6 use warnings;
  1         2  
  1         40  
5              
6 1     1   6 use Carp qw/confess/;
  1         6  
  1         72  
7 1     1   2502 use Moose::Util::TypeConstraints;
  1         618355  
  1         11  
8 1     1   2464 use XML::Parser::PerlSAX;
  0            
  0            
9             use InterMine::Model::Handler;
10             use Time::HiRes qw/gettimeofday/;
11              
12             use constant TYPE_PREFIX => "InterMine";
13              
14             our $VERSION = '0.9901';
15              
16             =head1 NAME
17              
18             InterMine::Model - the representation of an InterMine model
19              
20             =head1 SYNOPSIS
21              
22             use InterMine::Model;
23              
24             my $model_file = 'flymine/dbmodel/build/model/genomic_model.xml';
25             my $model = InterMine::Model->new(file => $model_file);
26             my $gene = $model->make_new(
27             Gene => {
28             primaryIdentifier => "FBgn0004053",
29             secondaryIdentifier => "CG1046",
30             symbol => "zen",
31             name => "zerknullt",
32             length => "3474",
33             organism => {
34             shortName => "D. melanogaster",
35             }
36             ncbiGeneNumber => 40828,
37             });
38              
39             $gene->getName(); # "zerknullt"
40              
41             ...
42              
43             =head1 DESCRIPTION
44              
45             The class is the Perl representation of an InterMine data model. The
46             C<new()> method can parse the model file. The
47             C<get_classdescriptor_by_name()> method will return an
48             InterMine::Model::ClassDescriptor object for the class with the given
49             name, and the C<make_new()> method will return an instantiated object
50             of the given class.
51              
52             For an example model see:
53             L<http://trac.flymine.org/browser/trunk/intermine/objectstore/model/testmodel/testmodel_model.xml>
54              
55             =head1 CLASS METHODS
56              
57             =cut
58              
59             =head2 new( %options )
60              
61             Standard constructor - accepts key/value pairs. Possible options are:
62              
63             =over 4
64              
65             =item * source: the source of the xml
66              
67             can be a ScalarRef, filehandle, filename, or string (or anything that overloads "")
68             (tested in that order)
69              
70             =item * file: The file to load the model from
71              
72             [deprecated - use source instead]
73              
74             =item * string: A string containing the xml to load the model from
75              
76             [deprecated - use source instead]
77              
78             =item * origin: Where this model comes from
79              
80             usually a mine name - optional
81              
82             =back
83              
84             =cut
85              
86             sub new {
87             my $class = shift;
88             my %opts = @_;
89              
90             print join('=>', @_) if $ENV{DEBUG};
91              
92             my $source = $opts{source} || $opts{file} || $opts{string}
93             or confess "No source passed to $class constructor";
94              
95             my $self = {%opts};
96              
97             $self->{class_hash} = {};
98             $self->{object_cache} = {};
99              
100             bless $self, $class;
101              
102             {
103             no warnings 'newline';
104              
105             if (ref $source eq 'SCALAR') {
106             $self->_process_string($$source);
107             } elsif (ref $source eq 'GLOB') {
108             $self->_process_string(join('', <$source>));
109             } elsif (-r $source || $opts{file}) {
110             $self->_process_file($source);
111             } else {
112             $self->_process_string("$source");
113             }
114             }
115              
116             $self->_fix_class_descriptors();
117              
118             return $self;
119             }
120              
121             sub _process_string {
122             my ($self, $string) = @_;
123             return $self->_process($string, 1);
124             }
125              
126             sub _process_file {
127             my ($self, $filename) = @_;
128             -r $filename || confess "Cannot read model source file $filename. Aborting";
129             return $self->_process($filename, 0);
130             }
131              
132             sub _process {
133             my $self = shift;
134             my $source_arg = shift;
135             my $source_is_string = shift;
136              
137             warn "PARSING MODEL " . gettimeofday() if $ENV{DEBUG};
138             my $handler = new InterMine::Model::Handler( model => $self );
139             my $parser = XML::Parser::PerlSAX->new( Handler => $handler );
140              
141             my $source;
142              
143             if ($source_is_string) {
144             $source = { String => $source_arg };
145             }
146             else {
147             $source = { SystemId => $source_arg };
148             }
149              
150             $parser->parse( Source => $source );
151             warn "FINISHED PARSING MODEL " . gettimeofday() if $ENV{DEBUG};
152             }
153              
154             sub _add_type_constraint_and_coercion {
155             my $self = shift;
156             my $class_name = shift;
157              
158             subtype $class_name, as "Object", where {$_->isa($self->{perl_package} . $class_name)};
159             subtype "ArrayOf" . $class_name, as "ArrayRef[$class_name]";
160             coerce $class_name, from 'HashRef', via {
161             $self->make_new(($_->{class} || $class_name), $_);
162             };
163             subtype "ArrayOfHashes", as "ArrayRef[HashRef]";
164              
165             coerce "ArrayOf$class_name", from "ArrayOfHashes",
166             via { [map {$self->make_new(($_->{class} || $class_name), $_)} @$_] };
167             }
168              
169             use Moose::Meta::Class;
170              
171             # add fields from base classes to sub-classes so that $class_descriptor->fields()
172             # returns fields from base classes too
173             sub _fix_class_descriptors {
174             my $self = shift;
175             #
176             # warn "BUILDING MODEL " . gettimeofday() if $ENV{DEBUG};
177             # for my $class_name (keys %{ $self->{class_hash} } ) {
178             # $self->_add_type_constraint_and_coercion($class_name);
179             # }
180             #
181             # while ( my ( $class_name, $cd ) = each %{ $self->{class_hash} } ) {
182             # my @fields = $self->_get_fields($cd);
183             # for my $field (@fields) {
184             # $cd->add_field($field);
185             # }
186             # $cd->_make_fields_into_attributes();
187             # $cd->make_immutable;
188             # }
189             # warn "FINISHED BUILDING MODEL " . gettimeofday() if $ENV{DEBUG};
190             }
191              
192             sub _fix_cd {
193             my ($self, $name, $class) = @_;
194             $self->_add_type_constraint_and_coercion($name);
195             my @fields = $self->_get_fields($class);
196             for my $field (@fields) {
197             $class->add_field($field);
198             }
199             $class->_make_fields_into_attributes();
200             #$class->make_immutable;
201             $class->_set_fixed(1);
202             }
203              
204             sub _get_fields {
205             my $self = shift;
206             my $cd = shift;
207              
208             my @fields = ();
209              
210             for my $field ( $cd->fields() ) {
211             my $field_name = $field->name();
212             push @fields, $field;
213             }
214              
215             for my $parent ( $cd->parental_class_descriptors ) {
216             push @fields, $self->_get_fields($parent);
217             }
218              
219             return @fields;
220             }
221              
222             =head2 get_classdescriptor_by_name
223              
224             Get the L<InterMine::Model::ClassDescriptor> (meta-class) with the given name.
225            
226             my $cd = $model->get_classdescriptor_by_name("Gene");
227              
228             =cut
229              
230             sub get_classdescriptor_by_name {
231             my $self = shift;
232             my $classname = shift;
233              
234             if ( !defined $classname ) {
235             confess "no classname passed to get_classdescriptor_by_name()\n";
236             }
237              
238             $classname =~ s/.*:://;
239              
240             # These are always valid
241             if ( $classname eq 'Integer' ) {
242             return InterMine::Model::ClassDescriptor->new(
243             model => $self,
244             name => $classname,
245             extends => ['id'],
246             );
247             }
248              
249             my $class = $self->{class_hash}{$classname}
250             || $self->{class_hash}{ $self->{package_name} . $classname };
251             confess "$classname not in the model" unless $class;
252             unless ($class->_is_ready()) {
253             $self->_fix_cd($classname, $class);
254             }
255             return $class;
256             }
257              
258             =head2 make_new($class_name, [%attributes|$attributes])
259              
260             Return an object of the desired class, with the attributes
261             given
262              
263             my $gene = $model->make_new(Gene => {symbol => "zen", organism => {name => 'D. melanogaster}});
264              
265             say $gene->getSymbol # "zen"
266             say $gene->getOrganism->getName # "D. melanogaster"
267              
268             =cut
269              
270             sub make_new {
271             my $self = shift;
272             my $name = (ref $_[0] eq 'HASH') ? $_[0]->{class} : shift;
273             my $params = (@_ == 1) ? $_[0] : {@_};
274              
275             my $obj = $self->get_classdescriptor_by_name($name)->new_object($params);
276              
277             if ($obj->hasObjectId) {
278             if (my $existing = $self->{object_cache}{$obj->getObjectId}) {
279             $existing->merge($obj);
280             return $existing;
281             } else {
282             $self->{object_cache}{$obj->getObjectId} = $obj;
283             }
284             } else {
285             return $obj;
286             }
287             }
288              
289             =head2 get_all_classdescriptors
290              
291             Return all the L<InterMine::Model::ClassDescriptor>s for this model
292              
293             my @cds = $model->get_all_classdescriptors();
294              
295             =cut
296              
297             sub get_all_classdescriptors {
298             my $self = shift;
299             return values %{ $self->{class_hash} };
300             }
301              
302             =head2 get_referenced_classdescriptor
303              
304             Get the class descriptor at the other end of a reference. The main use for this
305             method is internal, during the construction of a model
306              
307             my $cd = $model->get_referenced_classdescriptor($ref);
308              
309             =cut
310              
311             sub get_referenced_classdescriptor {
312             my $self = shift;
313             my $reference = shift;
314             for my $cd ( $self->get_all_classdescriptors ) {
315             for my $ref ( $cd->references ) {
316             if ( $ref->has_reverse_reference ) {
317             if ( $ref->reverse_reference->name eq $reference ) {
318             return $cd;
319             }
320             }
321             }
322             }
323             return undef;
324             }
325              
326             =head2 find_classes_declaring_field( $name )
327              
328             Get the class descriptors that declare fields of a certain name
329              
330             my @classes = $model->find_classes_declaring_field($str);
331              
332             =cut
333              
334             sub find_classes_declaring_field {
335             my $self = shift;
336             my $field_name = shift;
337             my @returners;
338             for my $cd ( $self->get_all_classdescriptors ) {
339             for my $field ( $cd->get_own_fields ) {
340             push @returners, $cd if ( $field->name eq $field_name );
341             }
342             }
343             return @returners;
344             }
345              
346             =head2 package_name
347              
348             Return the package name derived from the original java name space, eg. org.intermine.model
349              
350             my $java_package = $model->package_name;
351              
352             =cut
353              
354             sub package_name {
355             my $self = shift;
356             return $self->{package_name};
357             }
358              
359             =head2 model_name
360              
361             Return the name of this model. Conceptually, this maps to the enclosing package for the
362             generated classes.
363              
364             my $model_name = $model->model_name();
365              
366             =cut
367              
368             sub model_name {
369             my $self = shift;
370             return $self->{model_name};
371             }
372              
373             =head2 to_xml
374              
375             Returns a string containing an XML representation of the model.
376              
377             =cut
378              
379             sub to_xml {
380             my $self = shift;
381             my $xml = sprintf(qq{<model name="%s" package="%s">\n},
382             $self->model_name, $self->package_name);
383              
384             for my $cd (sort($self->get_all_classdescriptors())) {
385             $xml .= q[ ] x 2 . $cd->to_xml . "\n";
386             }
387              
388             $xml .= "</model>";
389             return $xml;
390             }
391              
392             =head2 lazy_fetch
393              
394             Always returns undef. This can be overriden by subclasses to provide lazy fetching
395             capabilities for items, from a web-service or directly from a database.
396              
397             =cut
398              
399             sub lazy_fetch { undef };
400              
401             1;
402              
403             =head1 AUTHOR
404              
405             FlyMine C<< <support@flymine.org> >>
406              
407             =head1 BUGS
408              
409             Please report any bugs or feature requests to C<support@flymine.org>.
410              
411             =head1 SUPPORT
412              
413             You can find documentation for this module with the perldoc command.
414              
415             perldoc InterMine::Model
416              
417             You can also look for information at:
418              
419             =over 4
420              
421             =item * FlyMine
422              
423             L<http://www.flymine.org>
424              
425             =back
426              
427             =head1 COPYRIGHT & LICENSE
428              
429             Copyright 2006,2007,2008,2009, 2010, 2011 FlyMine, all rights reserved.
430              
431             This program is free software; you can redistribute it and/or modify it
432             under the same terms as Perl itself.
433