File Coverage

blib/lib/EnsEMBL/Web/Record.pm
Criterion Covered Total %
statement 9 138 6.5
branch 0 66 0.0
condition n/a
subroutine 3 20 15.0
pod 15 15 100.0
total 27 239 11.3


line stmt bran cond sub pod time code
1             package EnsEMBL::Web::Record;
2              
3             ### Inside-out class representing persistent user information. This class
4             ### follows the Active Record design pattern: it contains both the domain
5             ### logic required to create and manipulate a piece of persistent data, and
6             ### the information necessary to maintain this data in a database.
7            
8             ### It allows the storage of arbitrary hash keys and their values
9             ### (user bookmarks, etc) in a single database field, and uses autoloading
10             ### to enable new data to be stored at will without the need for additional code
11              
12             =head1 NAME
13              
14             EnsEMBL::Web::Record - A family of modules used for managing a user's
15             persistant data in a database.
16              
17             =head1 VERSION
18              
19             Version 1.01
20              
21             =cut
22              
23             our $VERSION = '1.01';
24              
25             =head1 SYNOPSIS
26              
27             Many web sites now encourage users to register and login to access
28             more advanced features, and to customise a site to their needs.
29              
30             The EnsEMBL::Web::Record group of Perl modules is design to manage
31             any arbitrary type of user created data in an SQL database. This
32             module follows the Active Record design pattern, in that each new
33             instantiated Record object represents a single row of a database.
34             That object can be manipulated programatically, and any changes made
35             can be stored in the database with a single record->save function
36             call.
37              
38             Because arbitrary Perl data structures can be stored in this
39             manner, EnsEMBL::Web::Record allows user preferences to be easily
40             saved, and allows developers to implement new featurs quickly.
41              
42             This module was first used (and has been abstracted from) the
43             Ensembl genome browser (http://www.ensembl.org).
44              
45             New user data can be added to the database:
46              
47             use EnsEMBL::Web::Record;
48              
49             my $bookmark = EnsEMBL::Web::Record->new();
50             $bookmark->url('http://www.ensembl.org');
51             $bookmark->name('Ensembl');
52             $bookmark->save;
53             ...
54            
55             The Record can be associated with an user id:
56              
57             $record->user($id);
58              
59             The same record can also be removed:
60              
61             $bookmark->delete;
62              
63             EnsEMBL::Web::Record also provides a number of methods for getting
64             collections of records from the database, using a field selector.
65            
66             EnsEMBL::Web::Record::find_bookmarks_by_user_id($id).
67              
68             =cut
69              
70 1     1   58744 use strict;
  1         2  
  1         44  
71 1     1   7 use warnings;
  1         3  
  1         29  
72 1     1   1078 use Data::Dumper;
  1         18103  
  1         1600  
73              
74             our $AUTOLOAD;
75              
76             {
77              
78             my %Adaptor_of;
79             my %Fields_of;
80             my %ParameterSet_of;
81             my %Records_of;
82             my %Tainted_of;
83             my %Id_of;
84             my %CreatedAt_of;
85             my %ModifiedAt_of;
86             my %Type_of;
87             my %Owner_of;
88              
89             =head1 METHODS
90             =cut
91              
92             =head2 AUTOLOAD
93              
94             The AUTOLOAD method allows EnsEMBL::Web::Record to automatically provide
95             getter and setter functionality for an arbitrary set of fields. It also
96             automatically dispatches find_by requests.
97              
98             Field attributes are not validated against the database.
99              
100             =cut
101              
102             sub AUTOLOAD {
103             ### AUTOLOAD method for getting and setting record attributes, and processing
104             ### find_by requests. Attributes should be named after columns in the
105             ### appropriate database table.
106             ###
107             ### Attribute names are not validated against the database table.
108 0     0     my $self = shift;
109 0           my ($key) = ($AUTOLOAD =~ /::([a-z].*)$/);
110 0           my ($value, $options) = @_;
111             #warn "AUTOLOADING $key";
112 0 0         if ($value) {
113 0 0         if (my ($find, $by) = ($key =~ /find_(.*)_by_(.*)/)) {
114 0           my $table = "user";
115 0           my $record_type = "User";
116 0 0         if ($find eq "records") {
117 0           $find = "";
118             }
119 0 0         if ($find eq "group_records") {
120 0           $find = "";
121 0           $table = "group";
122             }
123 0 0         if ($by =~ /group_record/) {
124 0           $table = "group";
125 0           $record_type = "Group";
126             }
127 0           return find_records(( record_type => $record_type, type => $find, $by => $value, table => $table, options => $options));
128             } else {
129 0 0         if (my ($type) = ($key =~ /(.*)_records/)) {
130 0           return $self->records_of_type($type, $value);
131             }
132 0           $self->fields($key, $value);
133             }
134             } else {
135 0 0         if (my ($type) = ($key =~ /(.*)_records/)) {
136 0           return $self->records_of_type($type);
137             }
138             }
139 0           return $self->fields($key);
140             }
141              
142              
143             =head2 new
144              
145             Creates a new Record object. This module follows the Active Record pattern: it contains both the domain
146             logic required to create and manipulate a piece of persistent data, and
147             the information necessary to maintain this data in a database.
148              
149             You should pass in a valid database adaptor, which contains the necessary sql requests. An example adaptor can be found in the distribution.
150              
151             $record = EnsEMBL::Web::Record->new(( adaptor => $adaptor ));
152              
153             =cut
154              
155             sub new {
156             ### c
157 0     0 1   my ($class, %params) = @_;
158 0           my $self = bless \my($scalar), $class;
159 0 0         $Adaptor_of{$self} = defined $params{'adaptor'} ? $params{'adaptor'} : undef;
160 0 0         $Records_of{$self} = defined $params{'records'} ? $params{'records'} : [];
161 0 0         $ParameterSet_of{$self} = defined $params{'parameter_set'} ? $params{'parameter_set'} : undef;
162 0 0         $Id_of{$self} = defined $params{'id'} ? $params{'id'} : undef;
163 0 0         $CreatedAt_of{$self} = defined $params{'created_at'} ? $params{'created_at'} : undef;
164 0 0         $ModifiedAt_of{$self} = defined $params{'modified_at'} ? $params{'modified_at'} : undef;
165 0 0         $Type_of{$self} = defined $params{'type'} ? $params{'type'} : "record";
166 0           $Fields_of{$self} = {};
167 0           $Tainted_of{$self} = {};
168 0 0         if ($params{'data'}) {
169             #$self->data($params{'data'});
170 0           my $eval = eval($params{'data'});
171 0           $Fields_of{$self} = $eval;
172             } else {
173 0           $Fields_of{$self} = {};
174             }
175 0           return $self;
176             }
177              
178              
179             =head2 taint
180              
181             Marks a particular collection of records for an update. Tainted
182             records are updated in the database when the Record's save method
183             is called.
184              
185             =cut
186              
187             sub taint {
188             ### Marks a particular collection of records for an update. Tainted
189             ### records are updated in the database when the Record's save method
190             ### is called.
191 0     0 1   my ($self, $type) = @_;
192 0           $self->tainted->{$type} = 1;
193             }
194              
195             =head2 dump_data
196              
197             Uses Data::Dumper to format a record's data for storage,
198             and also handles escaping of quotes to avoid SQL errors
199              
200             =cut
201              
202             sub dump_data {
203             ### Uses Data::Dumper to format a record's data for storage,
204             ### and also handles escaping of quotes to avoid SQL errors
205 0     0 1   my $self = shift;
206 0           my $temp_fields = {};
207 0           foreach my $key (keys %{ $self->fields }) {
  0            
208 0           $temp_fields->{$key} = $self->fields->{$key};
209 0           $temp_fields->{$key} =~ s/'/\\'/g;
210             }
211 0           my $dump = Dumper($temp_fields);
212             #$dump =~ s/'/\\'/g;
213 0           $dump =~ s/^\$VAR1 = //;
214 0           return $dump;
215             }
216              
217             =head2 fields
218              
219             Accessor for the fields property.
220              
221             =cut
222              
223             sub fields {
224             ### a
225 0     0 1   my ($self, $key, $value) = @_;
226 0 0         if ($key) {
227 0 0         if ($value) {
228 0           $value =~ s/'/\\'/g;
229 0           $Fields_of{$self}->{$key} = $value;
230             }
231 0           return $Fields_of{$self}->{$key}
232             } else {
233 0           return $Fields_of{$self};
234             }
235             }
236              
237             =head2 records
238              
239             Accessor for the records property.
240              
241             =cut
242              
243             sub records {
244             ### a
245 0     0 1   my $self = shift;
246 0 0         $Records_of{$self} = shift if @_;
247 0           return $Records_of{$self};
248             }
249              
250             =head2 type
251              
252             Accessor for the type property.
253              
254             =cut
255              
256             sub type {
257             ### a
258 0     0 1   my $self = shift;
259 0 0         $Type_of{$self} = shift if @_;
260 0           return $Type_of{$self};
261             }
262              
263             =head2 tainted
264              
265             Accessor for the tainted property.
266              
267             =cut
268              
269             sub tainted {
270             ### a
271 0     0 1   my $self = shift;
272 0 0         $Tainted_of{$self} = shift if @_;
273 0           return $Tainted_of{$self};
274             }
275              
276             =head2 adaptor
277              
278             Accessor for the tainted property.
279              
280             =cut
281              
282             sub adaptor {
283             ### a
284 0     0 1   my $self = shift;
285 0 0         $Adaptor_of{$self} = shift if @_;
286 0           return $Adaptor_of{$self};
287             }
288              
289             =head2 parameter_set
290              
291             Accessor for the parameter_set property.
292              
293             =cut
294              
295             sub parameter_set {
296             ### a
297 0     0 1   my $self = shift;
298 0 0         $ParameterSet_of{$self} = shift if @_;
299 0           return $ParameterSet_of{$self};
300             }
301              
302             =head2 id
303              
304             Accessor for the id property.
305              
306             =cut
307              
308             sub id {
309             ### a
310 0     0 1   my $self = shift;
311 0 0         $Id_of{$self} = shift if @_;
312 0           return $Id_of{$self};
313             }
314              
315             =head2 created_at
316              
317             Accessor for the created_at property.
318              
319             =cut
320              
321             sub created_at {
322             ### a
323 0     0 1   my $self = shift;
324 0 0         $CreatedAt_of{$self} = shift if @_;
325 0           return $CreatedAt_of{$self};
326             }
327              
328             =head2 modified_at
329              
330             Accessor for the modified_at property.
331              
332             =cut
333              
334             sub modified_at {
335             ### a
336 0     0 1   my $self = shift;
337 0 0         $ModifiedAt_of{$self} = shift if @_;
338 0           return $ModifiedAt_of{$self};
339             }
340              
341             =head2 records_of_type
342              
343             Returns an array of records, that match a particular type.
344              
345             =cut
346              
347             sub records_of_type {
348             ### Returns an array of records
349             ### Argument 1: Type - string corresponding to a type of record, e.g. 'bookmark'
350             ### Argument 2: Options - hash ref ('order_by' => sort expression, e.g.)
351 0     0 1   my ($self, $type, $options) = @_;
352 0           my @return = ();
353 0 0         if ($self->records) {
354 0           foreach my $record (@{ $self->records }) {
  0            
355 0 0         if ($record->type eq $type) {
356 0           push @return, $record;
357             }
358             }
359             }
360 0 0         if ($options->{'order_by'}) {
361 0           my $sorter = $options->{'order_by'};
362 0           @return = reverse sort { $a->$sorter <=> $b->$sorter } sort @return;
  0            
363             }
364 0           return @return;
365             }
366              
367             =head2 find_records
368              
369             Returns an array of records. This method is called by the autoloading mechanism, and is not intended for
370             public use.
371              
372             =cut
373              
374             sub find_records {
375 0     0 1   my (%params) = @_;
376 0           my $record_type = "User";
377 0 0         if ($params{record_type}) {
378 0           $record_type = $params{record_type};
379 0           delete $params{record_type};
380             }
381 0           $record_type = "EnsEMBL::Web::Record::" . $record_type;
382 0           my $user_adaptor = undef;
383 0 0         if ($params{options}->{adaptor}) {
384 0           $user_adaptor = $params{options}->{adaptor};
385 0           warn "ADAPTOR for FIND: " . $user_adaptor;
386             }
387 0           my $results = $user_adaptor->find_records(%params);
388 0           my @records = ();
389 0           foreach my $result (@{ $results }) {
  0            
390             #if (&dynamic_use($record_type)) {
391 0           my $record = $record_type->new((
392             id => $result->{id},
393             type => $result->{type},
394             user => $result->{user},
395             data => $result->{data},
396             created_at => $result->{created_at},
397             modified_at => $result->{modified_at}
398             ));
399 0           push @records, $record;
400             #}
401             }
402 0 0         if ($params{options}) {
403 0           my %options = %{ $params{options} };
  0            
404 0 0         if ($options{order_by}) {
405 0           @records = sort { $b->click <=> $a->click } @records;
  0            
406             }
407             }
408 0           return @records;
409             }
410              
411             =head2 owner
412              
413             Accessor for the owner property.
414              
415             =cut
416              
417             sub owner {
418             ### a
419 0     0 1   my $self = shift;
420 0 0         $Owner_of{$self} = shift if @_;
421 0           return $Owner_of{$self};
422             }
423              
424             =head2 DESTROY
425              
426             Called automatically by Perl when object reference count reaches zero.
427              
428             =cut
429              
430             sub DESTROY {
431             ### d
432 0     0     my $self = shift;
433 0           delete $Adaptor_of{$self};
434 0           delete $Fields_of{$self};
435 0           delete $Id_of{$self};
436 0           delete $CreatedAt_of{$self};
437 0           delete $ModifiedAt_of{$self};
438 0           delete $Records_of{$self};
439 0           delete $ParameterSet_of{$self};
440 0           delete $Tainted_of{$self};
441 0           delete $Type_of{$self};
442 0           delete $Owner_of{$self};
443             }
444              
445             }
446              
447             =head1 AUTHOR
448              
449             Matt Wood, C<< >>
450              
451             =head1 BUGS
452              
453             Please report any bugs or feature requests to
454             C, or through the web interface at
455             L.
456              
457             =head1 SUPPORT
458              
459             You can find documentation for this module with the perldoc command.
460              
461             perldoc EnsEMBL::Web::Record
462              
463             You can also look for information at: http://www.ensembl.org
464              
465             =over 4
466              
467             =item * AnnoCPAN: Annotated CPAN documentation
468              
469             L
470              
471             =item * CPAN Ratings
472              
473             L
474              
475             =item * RT: CPAN's request tracker
476              
477             L
478              
479             =item * Search CPAN
480              
481             L
482              
483             =back
484              
485             =head1 ACKNOWLEDGEMENTS
486              
487             Many thanks to everyone on the Ensembl team, in particular James Smith, Anne Parker, Fiona Cunningham and Beth Prichard.
488              
489             =head1 COPYRIGHT & LICENSE
490              
491             Copyright (c) 1999-2006 The European Bioinformatics Institute and Genome Research Limited, and others. All rights reserved.
492              
493             Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
494              
495             1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
496             2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
497             3. The name Ensembl must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact ensembl-dev@ebi.ac.uk
498             4. Products derived from this software may not be called "Ensembl" nor may "Ensembl" appear in their names without prior written permission of the Ensembl developers.
499             5. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by Ensembl (http://www.ensembl.org/).
500              
501             THIS SOFTWARE IS PROVIDED BY THE ENSEMBL GROUP "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ENSEMBL GROUP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
502              
503             =cut
504              
505             1;