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

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