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

Last change on this file since 109 was 109, checked in by nanardon, 15 years ago
  • use good functions
  • 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                    warn "$_";
172                    if ($destbase->delete_object($otype, $_)) {
173                        print "delete " . $destbase->label . '::' . $otype . '::' . "$_\n";
174                    } else {
175                        warn "cannot delete " . $destbase->label . '::' . $otype . '::' . "$_\n";
176                    }
177                }
178            }
179        }
180    );
181}
182
183sub sync_object {
184    my ($self, $otype, $uid) = @_;
185    my $sobj = $self->from->get_object($otype, $uid) or return;
186    foreach ($self->to) {
187        $_->sync_object($sobj)
188    }
189    foreach ($self->to) {
190        $_->commit;
191    }
192    1;
193}
194
195sub process {
196    my ($self) = @_;
197
198    $self->from->load or return;
199    # if any cannot be loaded, return,
200    # TODO we need a way to force if some still can be sync
201    $self->load_dest and return;
202   
203    # tracking current base revision:
204    $self->{current_rev} = $self->from->current_rev;
205
206    my %delayed;
207    $self->_traverse_delete;
208    $self->_traverse_update(
209        attr_cb => sub {
210            my ($from, $to, $otype) = @_;
211            my %fields = ();
212            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
213            foreach ($from->list_canonical_fields($otype, 'r')) {
214                $delayed{$_} and next;
215                $fields{$_} ||= 0; # avoid
216                $fields{$_}++;
217            }
218            foreach ($to->list_canonical_fields($otype, 'w')) {
219                $delayed{$_} and next;
220                $fields{$_} ||= 0; # avoid
221                $fields{$_}++;
222            }
223            # field having value are in both
224            grep { $fields{$_} == 2 } keys %fields;
225        },
226    );
227    $self->_traverse_update(
228        attr_cb => sub {
229            my ($from, $to, $otype) = @_;
230            my %fields = ();
231            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
232            foreach ($from->list_canonical_fields($otype, 'r')) {
233                $delayed{$_} or next;
234                $fields{$_} ||= 0; # avoid
235                $fields{$_}++;
236            }
237            foreach ($to->list_canonical_fields($otype, 'w')) {
238                $delayed{$_} or next;
239                $fields{$_} ||= 0; # avoid
240                $fields{$_}++;
241            }
242            # field having value are in both
243            grep { $fields{$_} == 2 } keys %fields;
244        },
245        nocreate => 1,
246    ) unless($self->{options}{nocreate} || $self->{options}{test});
247
248    foreach ($self->to) {
249        $_->commit or next;
250        $self->newval($self->from->label, $_->label, $self->{current_rev}) if($self->{current_rev});
251    }
252
253    $self->RewriteConfig
254        if($self->GetFileName && !($self->{options}{nocreate} || $self->{options}{test}));
255   
256    1;
257}
258
2591;
260
261__END__
262# Below is stub documentation for your module. You'd better edit it!
263
264=head1 SEE ALSO
265
266Mention other useful documentation such as the documentation of
267related modules or operating system documentation (such as man pages
268in UNIX), or any relevant external documentation such as RFCs or
269standards.
270
271If you have a mailing list set up for your module, mention it here.
272
273If you have a web site set up for your module, mention it here.
274
275=head1 AUTHOR
276
277Thauvin Olivier, E<lt>olivier.thauvin@latmosipsl.frE<gt>
278
279=head1 COPYRIGHT AND LICENSE
280
281Copyright (C) 2009 by Thauvin Olivier
282
283This library is free software; you can redistribute it and/or modify
284it under the same terms as Perl itself, either Perl version 5.10.0 or,
285at your option, any later version of Perl 5 you may have available.
286
287
288=cut
Note: See TracBrowser for help on using the repository browser.