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

Last change on this file since 74 was 74, checked in by nanardon, 15 years ago
  • support object deletion
  • fix synchro of delayed fields
  • Property svn:keywords set to Id Rev
File size: 6.8 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, $attr_cb, %options) = @_;
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            my @lobjfrom = $self->from->list_objects_from_rev(
119                $otype,
120                $self->val($self->from->label, $destbase->label),
121            );
122            warn "objects $otype " . $self->from->label . " => " . $destbase->label . "\n";
123            my @common_fields = $attr_cb->($self->from, $destbase, $otype) or next;
124            warn "Synching fields: " . join(', ', sort @common_fields) . "\n";
125            my %exists = map { $_ => 1 } $destbase->list_objects($otype);
126            foreach my $uid (@lobjfrom) {
127                my $sobj = $self->from->get_object($otype, $uid);
128                if ($exists{$uid}) {
129                    my %fields = map { $_ => $sobj->get_c_field($_, 'r') } @common_fields;
130                    my $dobj = $destbase->get_object($otype, $uid);
131                    $dobj->set_c_fields(%fields) unless($self->{options}{test});
132                    warn "  $otype $uid: synched\n";
133                } elsif (!$self->{options}{nocreate} && !$options{nocreate}) {
134                    my %fields = map { $_ => $sobj->get_c_field($_, 'r') } @common_fields;
135                    $destbase->create_c_object($otype, $uid, %fields) unless($self->{options}{test});
136                    warn "  $otype $uid: created\n";
137                }
138            }
139            my %srcexists = map { $_ => 1 } $self->from->list_objects($otype);
140            foreach (keys %exists) {
141                if (!$srcexists{$_}) {
142                    warn "delete $otype $_\n";
143                    $destbase->delete_object($otype, $_);
144                }
145            }
146        }
147    }
148}
149
150sub process {
151    my ($self) = @_;
152
153    $self->from->load or return;
154    # if any cannot be loaded, return,
155    # TODO we need a way to force if some still can be sync
156    $self->load_dest and return;
157   
158    # tracking current base revision:
159    $self->{current_rev} = $self->from->current_rev;
160
161    my %delayed;
162    $self->_traverse(
163        sub {
164            my ($from, $to, $otype) = @_;
165            my %fields = ();
166            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
167            foreach ($from->list_canonicals_fields($otype, 'r')) {
168                $delayed{$_} and next;
169                $fields{$_} ||= 0; # avoid
170                $fields{$_}++;
171            }
172            foreach ($to->list_canonicals_fields($otype, 'w')) {
173                $delayed{$_} and next;
174                $fields{$_} ||= 0; # avoid
175                $fields{$_}++;
176            }
177            # field having value are in both
178            grep { $fields{$_} == 2 } keys %fields;
179        },
180    );
181    $self->_traverse(
182        sub {
183            my ($from, $to, $otype) = @_;
184            my %fields = ();
185            my %delayed = map { $_ => 1 } $to->delayed_fields($otype);
186            foreach ($from->list_canonicals_fields($otype, 'r')) {
187                $delayed{$_} or next;
188                $fields{$_} ||= 0; # avoid
189                $fields{$_}++;
190            }
191            foreach ($to->list_canonicals_fields($otype, 'w')) {
192                $delayed{$_} or next;
193                $fields{$_} ||= 0; # avoid
194                $fields{$_}++;
195            }
196            # field having value are in both
197            grep { $fields{$_} == 2 } keys %fields;
198        },
199        nocreate => 1,
200    ) unless($self->{options}{nocreate} || $self->{options}{test});
201
202    foreach ($self->to) {
203        $_->commit or next;
204        $self->newval($self->from->label, $_->label, $self->{current_rev}) if($self->{current_rev});
205    }
206
207    $self->RewriteConfig
208        if($self->GetFileName && !($self->{options}{nocreate} || $self->{options}{test}));
209   
210    1;
211}
212
2131;
214
215__END__
216# Below is stub documentation for your module. You'd better edit it!
217
218=head1 SEE ALSO
219
220Mention other useful documentation such as the documentation of
221related modules or operating system documentation (such as man pages
222in UNIX), or any relevant external documentation such as RFCs or
223standards.
224
225If you have a mailing list set up for your module, mention it here.
226
227If you have a web site set up for your module, mention it here.
228
229=head1 AUTHOR
230
231Thauvin Olivier, E<lt>olivier.thauvin@latmosipsl.frE<gt>
232
233=head1 COPYRIGHT AND LICENSE
234
235Copyright (C) 2009 by Thauvin Olivier
236
237This library is free software; you can redistribute it and/or modify
238it under the same terms as Perl itself, either Perl version 5.10.0 or,
239at your option, any later version of Perl 5 you may have available.
240
241
242=cut
Note: See TracBrowser for help on using the repository browser.