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

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