File Coverage

blib/lib/Class/DBI/DataMigration/Mapper.pm
Criterion Covered Total %
statement 9 41 21.9
branch 0 22 0.0
condition 0 3 0.0
subroutine 3 5 60.0
pod 1 1 100.0
total 13 72 18.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             =head1 Name
4              
5             Class::DBI::DataMigration::Mapper - Abstract class for mapping a single row in
6             the source database to a single row in the target database.
7              
8             =head1 Synopsis
9              
10             use Class::DBI::DataMigration::Mapper;
11              
12             # ... later ...
13             # Assume we've retrieved a $source_object of class Class from the source
14             # database, and have assembled $mappings, a ref to an appropriate hash of
15             # Class::DBI::DataMigration::Mapping objects:
16              
17             my $mapper = new Class::DBI::DataMigration::Mapper({
18             target_cdbi_class => Class,
19             mappings => $mappings,
20             target_search_keys => \@search_keys
21             });
22              
23             my $new_db_object = $mapper->map($source_object);
24              
25             # ... now $new_db_object is in the new database ...
26              
27             =head1 Description
28              
29             Class::DBI::DataMigration::Mapper is an abstract parent class for objects that
30             will map a single row at a time from the source database into a single row in
31             the new one. This is accomplished via Class::DBI; it's assumed that appropriate
32             classes exist representing the tables in the source and target databases.
33              
34             Mapping is accomplished using a hash of instances of
35             Class::DBI::DataMigration::Mapping objects.
36              
37             =cut
38              
39 1     1   901 use strict;
  1         1  
  1         41  
40              
41             package Class::DBI::DataMigration::Mapper;
42              
43 1     1   5 use Carp;
  1         1  
  1         96  
44 1     1   5 use base 'Class::Accessor';
  1         1  
  1         811  
45              
46             __PACKAGE__->mk_accessors(
47             qw/target_cdbi_class target_keys target_search_keys mappings/
48             );
49              
50             =head1 Methods
51              
52             =head2 mappings
53              
54             Gets/sets a ref to a hash of Class::DBI::DataMigration::Mapping objects, keyed
55             on keys into the source class whose values will be used to produce values for
56             the target class.
57              
58             =head2 target_cdbi_class
59              
60             Gets/sets the target class in which to build a new object (or edit an existing
61             one) using the mappings and the source_object supplied to map()
62              
63             =head2 target_keys
64              
65             Gets/sets a ref to a hash that acts as a dictionary between the target and
66             source classes; the keys in this hash are keys into the target class, and the
67             values are the corresponding keys into the source class.
68              
69             =head2 target_search_keys
70              
71             Gets/sets a ref to a list of keys that will be used during mapping to search
72             for a target class object; if found, data from the matching source db object will
73             be used to edit the already-existing target db object. Otherwise, a new object will
74             be created in the target db. If target_search_keys is left empty, no searching
75             will be done, and all objects from the source db will be mirrored as new
76             objects in the target db.
77              
78             =head2 map
79              
80             Expects one parameter: the source_object in the source database whose data is to
81             be mapped into an object in the target_cdbi_class.
82              
83             This method causes the Mapper to iterate through its target_keys hash, calling
84             map() on each mapping with the source object and the source key under which it
85             was stored in the mappings hash. The returned values of each of these map()
86             calls are collected into a hash and used to do one of the following:
87              
88             - if an object matching our target_search_keys in the data hash is found in
89             the target_cdbi_class (we use the first one found), that object is synchronized
90             using the rest of the data in the data hash and returned; and,
91              
92             - if our target_search_keys is empty, or if no object matching the
93             those keys in the data hash exists in the target_cdbi_class, a new target
94             class object is created and returned.
95              
96             If errors are encountered during this process, an error message is returned
97             instead of the affected object.
98              
99             Subclasses may do something fancier.
100              
101             =cut
102              
103             sub map {
104 0     0 1   my ($self, $source_object) = @_;
105 0           my %newobj_data = ();
106 0           while ((my $source_key, my $target_key) = each %{$self->target_keys}) {
  0            
107 0 0         my $mapping = $self->mappings->{$source_key}
108             or confess "Couldn't retrieve mapping for source key $source_key";
109 0           my $mapped = $mapping->map($source_key, $source_object);
110 0 0         $newobj_data{$target_key} = $mapped if $target_key;
111             }
112              
113 0           return $self->_create_or_edit_object($source_object, \%newobj_data);
114             }
115              
116             sub _create_or_edit_object {
117             # Useful for subclasses to override for post-mapping-processing
118             # (in this version we don't use $source_object, but subclasses can).
119              
120 0     0     my ($self, $source_object, $newobj_data) = @_;
121              
122 0 0         eval "require " . $self->target_cdbi_class unless $self->target_cdbi_class->can('new');
123 0 0         confess $@ if $@;
124              
125 0 0 0       if (($self->target_search_keys) and
  0            
126             (scalar(@{$self->target_search_keys}) > 0)) {
127 0           my %search_criteria;
128 0           foreach (@{$self->target_search_keys}) {
  0            
129 0           $search_criteria{$_} = $newobj_data->{$_};
130             }
131 0           my $search_results = $self->target_cdbi_class->search(%search_criteria);
132 0           my $search_obj = $search_results->next;
133 0           my $errstr = '';
134 0 0         if ($search_obj) {
135 0           while (my ($key, $value) = each %$newobj_data) {
136 0 0         if (ref $value) {
137 0           eval qq{ \$search_obj->$key($value) };
138             } else {
139             # quote $value if it's not a reference:
140 0 0         eval qq{ \$search_obj->$key('$value') } if $value;
141             }
142 0 0         $errstr .= $@ if $@;
143             }
144 0 0         return $errstr if $errstr;
145 0           return $search_obj;
146             }
147             }
148              
149             # If we've gotten this far, then either there were no target class search keys,
150             # or no target class object matched the search keys. Either way, we create a new one.
151              
152 0           my $created_obj = eval { return $self->target_cdbi_class->create($newobj_data); };
  0            
153 0 0         $created_obj = $@ if $@;
154 0           return $created_obj;
155             }
156              
157             =begin testing
158              
159             use_ok('Class::DBI::DataMigration::Mapper');
160             can_ok('Class::DBI::DataMigration::Mapper', 'map');
161             can_ok('Class::DBI::DataMigration::Mapper', 'mappings');
162             can_ok('Class::DBI::DataMigration::Mapper', 'target_keys');
163             can_ok('Class::DBI::DataMigration::Mapper', 'target_cdbi_class');
164              
165             =end testing
166              
167             =head1 See Also
168              
169             C
170              
171             =head1 Author
172              
173             Dan Friedman
174              
175             =head1 Copyright & License
176              
177             Copyright 2004 Dan Friedman, All Rights Reserved.
178              
179             This program is free software; you can redistribute it and/or modify it
180             under the same terms as Perl itself.
181              
182             Please note that these modules are not products of or supported by the
183             employers of the various contributors to the code.
184              
185             =cut
186              
187             1;