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

Last change on this file since 76 was 76, checked in by nanardon, 15 years ago
  • perform delete once, and at beginning
  • Property svn:keywords set to Id Rev
File size: 7.3 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 from {
71    my ($self) = @_;
72    return $self->{from}
73}
74
75sub to {
76    my ($self) = @_;
77    return @{$self->{to} || []};
78}
79
80=head2 load_dest
81
82Try to loaded all base, return the count of filtrered base which cannot
83be loaded
84
85=cut
86
87sub load_dest {
88    my ($self) = @_;
89    my @loaded;
90    my $unloaded = 0;
91    foreach ($self->to) {
92        if($_->load) {
93            push(@loaded, $_);
94        } else {
95            $unloaded++;
96        }
97    }
98    $self->{to} = \@loaded;
99    return $unloaded;
100}
101
102sub _traverse {
103    my ($self, $callback) = @_;
104   
105    # listing existing obj one time:
106    foreach my $otype ($self->from->list_supported_objects) {
107       
108        # If no dest support $otype, we skip
109        my @obj_dest_base = grep {
110            $_->is_supported_object($otype)
111        } $self->to or next;
112
113        # loading object list one time for all
114        # TODO optimize this, using rev of objects
115
116        warn "Sync objects $otype\n";
117        foreach my $destbase (@obj_dest_base) {
118            $callback->($otype, $destbase);
119        }
120    }
121}
122
123sub _traverse_update {
124    my ($self, %options) = @_;
125    $self->_traverse(
126        sub {
127            my ($otype, $destbase) = @_;
128            my @lobjfrom = $self->from->list_objects_from_rev(
129                $otype,
130                $self->val($self->from->label, $destbase->label),
131            );
132            warn "objects $otype " . $self->from->label . " => " . $destbase->label . "\n";
133            my @common_fields = $options{attr_cb}->($self->from, $destbase, $otype) or return;
134            warn "Synching fields: " . join(', ', sort @common_fields) . "\n";
135            my %exists = map { $_ => 1 } $destbase->list_objects($otype);
136            foreach my $uid (@lobjfrom) {
137                my $sobj = $self->from->get_object($otype, $uid);
138                if ($exists{$uid}) {
139                    my %fields = map { $_ => $sobj->get_c_field($_, 'r') } @common_fields;
140                    my $dobj = $destbase->get_object($otype, $uid);
141                    $dobj->set_c_fields(%fields) unless($self->{options}{test});
142                    warn "  $otype $uid: synched\n";
143                } elsif (!$self->{options}{nocreate} && !$options{nocreate}) {
144                    my %fields = map { $_ => $sobj->get_c_field($_, 'r') } @common_fields;
145                    $destbase->create_c_object($otype, $uid, %fields) unless($self->{options}{test});
146                    warn "  $otype $uid: created\n";
147                }
148            }
149        }
150    );
151}
152
153sub _traverse_delete {
154    my ($self, %options) = @_;
155    $self->_traverse(
156        sub {
157            my ($otype, $destbase) = @_;
158            my %exists = map { $_ => 1 } $destbase->list_objects($otype);
159            my %srcexists = map { $_ => 1 } $self->from->list_objects($otype);
160            foreach (keys %exists) {
161                if (!$srcexists{$_}) {
162                    warn "delete $otype $_\n";
163                    $destbase->delete_object($otype, $_);
164                }
165            }
166        }
167    );
168}
169
170sub process {
171    my ($self) = @_;
172
173    $self->from->load or return;
174    # if any cannot be loaded, return,
175    # TODO we need a way to force if some still can be sync
176    $self->load_dest and return;
177   
178    # tracking current base revision:
179    $self->{current_rev} = $self->from->current_rev;
180
181    my %delayed;
182    $self->_traverse_delete;
183    $self->_traverse_update(
184        attr_cb => sub {
185            my ($from, $to, $otype) = @_;
186            my %fields = ();
187            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
188            foreach ($from->list_canonicals_fields($otype, 'r')) {
189                $delayed{$_} and next;
190                $fields{$_} ||= 0; # avoid
191                $fields{$_}++;
192            }
193            foreach ($to->list_canonicals_fields($otype, 'w')) {
194                $delayed{$_} and next;
195                $fields{$_} ||= 0; # avoid
196                $fields{$_}++;
197            }
198            # field having value are in both
199            grep { $fields{$_} == 2 } keys %fields;
200        },
201    );
202    $self->_traverse_update(
203        attr_cb => sub {
204            my ($from, $to, $otype) = @_;
205            my %fields = ();
206            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
207            foreach ($from->list_canonicals_fields($otype, 'r')) {
208                $delayed{$_} or next;
209                $fields{$_} ||= 0; # avoid
210                $fields{$_}++;
211            }
212            foreach ($to->list_canonicals_fields($otype, 'w')) {
213                $delayed{$_} or next;
214                $fields{$_} ||= 0; # avoid
215                $fields{$_}++;
216            }
217            # field having value are in both
218            grep { $fields{$_} == 2 } keys %fields;
219        },
220        nocreate => 1,
221    ) unless($self->{options}{nocreate} || $self->{options}{test});
222
223    foreach ($self->to) {
224        $_->commit or next;
225        $self->newval($self->from->label, $_->label, $self->{current_rev}) if($self->{current_rev});
226    }
227
228    $self->RewriteConfig
229        if($self->GetFileName && !($self->{options}{nocreate} || $self->{options}{test}));
230   
231    1;
232}
233
2341;
235
236__END__
237# Below is stub documentation for your module. You'd better edit it!
238
239=head1 SEE ALSO
240
241Mention other useful documentation such as the documentation of
242related modules or operating system documentation (such as man pages
243in UNIX), or any relevant external documentation such as RFCs or
244standards.
245
246If you have a mailing list set up for your module, mention it here.
247
248If you have a web site set up for your module, mention it here.
249
250=head1 AUTHOR
251
252Thauvin Olivier, E<lt>olivier.thauvin@latmosipsl.frE<gt>
253
254=head1 COPYRIGHT AND LICENSE
255
256Copyright (C) 2009 by Thauvin Olivier
257
258This library is free software; you can redistribute it and/or modify
259it under the same terms as Perl itself, either Perl version 5.10.0 or,
260at your option, any later version of Perl 5 you may have available.
261
262
263=cut
Note: See TracBrowser for help on using the repository browser.