source: LATMOS-Accounts/lib/LATMOS/Accounts/Synchro.pm @ 108

Last change on this file since 108 was 108, checked in by nanardon, 15 years ago
  • report error on object delete
  • Property svn:keywords set to Id Rev
File size: 7.7 KB
Line 
1package LATMOS::Accounts::Synchro;
2
3use 5.010000;
4use strict;
5use warnings;
6use base qw(Config::IniFiles);
7use LATMOS::Accounts::Bases;
8
9=head1 NAME
10
11LATMOS::Accounts::Synchro - Perl extension for blah blah blah
12
13=head1 SYNOPSIS
14
15  use LATMOS::Accounts;
16  blah blah blah
17
18=head1 DESCRIPTION
19
20Stub documentation for LATMOS::Accounts, created by h2xs. It looks like the
21author of the extension was negligent enough to leave the stub
22unedited.
23
24Blah blah blah.
25
26=head1 FUNCTIONS
27
28=cut
29
30our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
31
32=head2 new($from, $to, %options)
33
34Create a new synchronisation where $from and $to are LATMOS::Accounts::Base
35based objects. $to can be an array ref of objects.
36
37=cut
38
39sub new {
40    my ($class, $from, $to, %options) = @_;
41
42    if ($options{state_file} && ! -w $options{state_file}) {
43        # don't exists, we have to create it
44        open(my $handle, '>', $options{state_file}) or return;
45        print $handle "[_default_]\n";
46        close($handle);
47    }
48
49    my $self = Config::IniFiles->new(
50        $options{state_file}
51        ? (-file => $options{state_file})
52        : (),
53    );
54
55    if ($options{state_file} && !$self->GetFileName) {
56        $self->SetFileName($options{state_file});
57    }
58   
59    $self->{from} = $from;
60    $self->{options} = { %options };
61
62    # allow ref and array ref of, eg
63    # to = $foo and $to = [ $foo, $bar ]
64    foreach (ref($to) eq 'ARRAY' ? @{ $to || []} : ($to)) {
65        push(@{$self->{to}}, $_);
66    }
67    bless($self, $class)
68}
69
70sub name {
71    $_[0]->{options}{name}
72}
73
74sub from {
75    my ($self) = @_;
76    return $self->{from}
77}
78
79sub to {
80    my ($self) = @_;
81    return @{$self->{to} || []};
82}
83
84=head2 load_dest
85
86Try to loaded all base, return the count of filtrered base which cannot
87be loaded
88
89=cut
90
91sub load_dest {
92    my ($self) = @_;
93    my @loaded;
94    my $unloaded = 0;
95    foreach ($self->to) {
96        if($_->load) {
97            push(@loaded, $_);
98        } else {
99            $unloaded++;
100        }
101    }
102    $self->{to} = \@loaded;
103    return $unloaded;
104}
105
106sub _traverse {
107    my ($self, $callback) = @_;
108   
109    # listing existing obj one time:
110    foreach my $otype ($self->from->list_supported_objects) {
111       
112        # If no dest support $otype, we skip
113        my @obj_dest_base = grep {
114            $_->is_supported_object($otype)
115        } $self->to or next;
116
117        # loading object list one time for all
118        # TODO optimize this, using rev of objects
119
120        foreach my $destbase (@obj_dest_base) {
121            $callback->($otype, $destbase);
122        }
123    }
124}
125
126sub _traverse_update {
127    my ($self, %options) = @_;
128    $self->_traverse(
129        sub {
130            my ($otype, $destbase) = @_;
131            my @lobjfrom = $self->from->list_objects_from_rev(
132                $otype,
133                $self->val($self->from->label, $destbase->label),
134            );
135            my @common_fields = $options{attr_cb}->($self->from, $destbase, $otype) or return;
136            my %exists = map { $_ => 1 } $destbase->list_objects($otype);
137            foreach my $uid (@lobjfrom) {
138                my $sobj = $self->from->get_object($otype, $uid);
139                if (!$self->{options}{test}) {
140                    my $res = $destbase->sync_object(
141                        $sobj,
142                        nocreate => ($self->{options}{nocreate} || $options{nocreate}),
143                    );
144                    if (defined $res) {
145                        printf(
146                            "%s::%s::%s => %s (%s)\n",
147                            $self->from->label, $otype, $uid,
148                            $destbase->label, $res
149                        ) if ($res);
150                    } else {
151                        warn sprintf(
152                            "Error syncing %s::%s::%s => %s\n",
153                            $self->from->label, $otype, $uid, $destbase->label
154                        ); 
155                    }
156                }
157            }
158        }
159    );
160}
161
162sub _traverse_delete {
163    my ($self, %options) = @_;
164    $self->_traverse(
165        sub {
166            my ($otype, $destbase) = @_;
167            my %exists = map { $_ => 1 } $destbase->list_objects($otype);
168            my %srcexists = map { $_ => 1 } $self->from->list_objects($otype);
169            foreach (keys %exists) {
170                if (!$srcexists{$_}) {
171                    if ($destbase->delete_object($otype, $_)) {
172                        print "delete " . $destbase->name . '::' . $otype . '::' . "$_\n";
173                    } else {
174                        warn "cannot delete " . $destbase->name . '::' . $otype . '::' . "$_\n";
175                    }
176                }
177            }
178        }
179    );
180}
181
182sub sync_object {
183    my ($self, $otype, $uid) = @_;
184    my $sobj = $self->from->get_object($otype, $uid) or return;
185    foreach ($self->to) {
186        $_->sync_object($sobj)
187    }
188    foreach ($self->to) {
189        $_->commit;
190    }
191    1;
192}
193
194sub process {
195    my ($self) = @_;
196
197    $self->from->load or return;
198    # if any cannot be loaded, return,
199    # TODO we need a way to force if some still can be sync
200    $self->load_dest and return;
201   
202    # tracking current base revision:
203    $self->{current_rev} = $self->from->current_rev;
204
205    my %delayed;
206    $self->_traverse_delete;
207    $self->_traverse_update(
208        attr_cb => sub {
209            my ($from, $to, $otype) = @_;
210            my %fields = ();
211            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
212            foreach ($from->list_canonical_fields($otype, 'r')) {
213                $delayed{$_} and next;
214                $fields{$_} ||= 0; # avoid
215                $fields{$_}++;
216            }
217            foreach ($to->list_canonical_fields($otype, 'w')) {
218                $delayed{$_} and next;
219                $fields{$_} ||= 0; # avoid
220                $fields{$_}++;
221            }
222            # field having value are in both
223            grep { $fields{$_} == 2 } keys %fields;
224        },
225    );
226    $self->_traverse_update(
227        attr_cb => sub {
228            my ($from, $to, $otype) = @_;
229            my %fields = ();
230            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
231            foreach ($from->list_canonical_fields($otype, 'r')) {
232                $delayed{$_} or next;
233                $fields{$_} ||= 0; # avoid
234                $fields{$_}++;
235            }
236            foreach ($to->list_canonical_fields($otype, 'w')) {
237                $delayed{$_} or next;
238                $fields{$_} ||= 0; # avoid
239                $fields{$_}++;
240            }
241            # field having value are in both
242            grep { $fields{$_} == 2 } keys %fields;
243        },
244        nocreate => 1,
245    ) unless($self->{options}{nocreate} || $self->{options}{test});
246
247    foreach ($self->to) {
248        $_->commit or next;
249        $self->newval($self->from->label, $_->label, $self->{current_rev}) if($self->{current_rev});
250    }
251
252    $self->RewriteConfig
253        if($self->GetFileName && !($self->{options}{nocreate} || $self->{options}{test}));
254   
255    1;
256}
257
2581;
259
260__END__
261# Below is stub documentation for your module. You'd better edit it!
262
263=head1 SEE ALSO
264
265Mention other useful documentation such as the documentation of
266related modules or operating system documentation (such as man pages
267in UNIX), or any relevant external documentation such as RFCs or
268standards.
269
270If you have a mailing list set up for your module, mention it here.
271
272If you have a web site set up for your module, mention it here.
273
274=head1 AUTHOR
275
276Thauvin Olivier, E<lt>olivier.thauvin@latmosipsl.frE<gt>
277
278=head1 COPYRIGHT AND LICENSE
279
280Copyright (C) 2009 by Thauvin Olivier
281
282This library is free software; you can redistribute it and/or modify
283it under the same terms as Perl itself, either Perl version 5.10.0 or,
284at your option, any later version of Perl 5 you may have available.
285
286
287=cut
Note: See TracBrowser for help on using the repository browser.