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

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