File Coverage

blib/lib/Net/LDAP/Batch.pm
Criterion Covered Total %
statement 69 96 71.8
branch 11 28 39.2
condition 3 12 25.0
subroutine 14 16 87.5
pod 6 6 100.0
total 103 158 65.1


line stmt bran cond sub pod time code
1             package Net::LDAP::Batch;
2 3     3   63259 use strict;
  3         7  
  3         190  
3 3     3   18 use warnings;
  3         7  
  3         100  
4 3     3   20 use Carp;
  3         10  
  3         279  
5 3     3   11548 use Data::Dump qw( dump );
  3         22738  
  3         548  
6 3     3   28 use base qw( Class::Accessor::Fast );
  3         6  
  3         5544  
7             __PACKAGE__->mk_accessors(qw( ldap debug ));
8             __PACKAGE__->mk_ro_accessors(qw( actions error ));
9 3     3   51049 use Net::LDAP::Batch::Action::Add;
  3         11  
  3         28  
10 3     3   2771 use Net::LDAP::Batch::Action::Update;
  3         10  
  3         30  
11 3     3   4730 use Net::LDAP::Batch::Action::Delete;
  3         9  
  3         29  
12 3     3   181 use Scalar::Util qw( blessed );
  3         11  
  3         3140  
13              
14             our $VERSION = '0.02';
15              
16             =head1 NAME
17              
18             Net::LDAP::Batch - perform a batch of LDAP actions
19              
20             =head1 SYNOPSIS
21              
22             use Net::LDAP::Batch;
23            
24             my $BaseDN = 'ou=People,dc=MyDomain';
25             my $ldap = make_and_bind_Net_LDAP_object(); # you write this
26            
27             my $batch = Net::LDAP::Batch->new( ldap => $ldap );
28             $batch->add_actions(
29             add => [
30             {
31             dn => "cn=MyGroup,ou=Group,$BaseDN",
32             attr => [
33             objectClass => [ 'top', 'posixGroup' ],
34             cn => 'MyGroup',
35             gidNumber => '1234'
36             ]
37             }
38             ],
39             delete => [
40             {
41             search => [
42             base => "ou=Group,$BaseDN",
43             scope => 'sub',
44             filter => "(cn=MyOldGroup)"
45             ]
46             }
47             ],
48             update => [
49             {
50             search => [
51             base => "ou=Group,$BaseDN",
52             scope => 'sub',
53             filter => "(cn=OtherGroup)"
54             ],
55             replace => { gidNumber => '5678' },
56             delete => { foo => [ 'bar' ] },
57             }
58             ]
59             );
60              
61             $batch->do or die $batch->error;
62            
63             =head1 DESCRIPTION
64              
65             Net::LDAP::Batch performs a series of actions against a LDAP
66             server. If any of the actions fails, then all the actions in the batch
67             are reverted.
68              
69             B This is not a true ACID-compliant transaction feature,
70             since no locking is performed. Instead it is simply a way to execute
71             a series of actions without having to worry about checking return values, or
72             error codes, or un-doing the changes should any of them fail. Of course,
73             since no ACID compliance is claimed, anything could (and likely will)
74             happen if there is more than one client attempting to make changes
75             on the same server at the same time. B
76              
77             =head1 METHODS
78              
79             =head2 new
80              
81             Create a batch instance.
82              
83             You must pass in a valid Net::LDAP object that has
84             already been bound to the server with whatever credentials are necessary
85             to complete the actions you will specify.
86              
87             You may optionally pass in an array ref of actions. See also the add_actions()
88             method.
89              
90             =cut
91              
92             sub new {
93 1     1 1 8596 my $class = shift;
94 1 50       49 my $opts = ref( $_[0] ) ? $_[0] : {@_};
95 1         44 my $self = $class->SUPER::new($opts);
96 1         36 $self->_setup;
97 1         98 return $self;
98             }
99              
100             sub _setup {
101 1     1   17 my $self = shift;
102 1 50       31 if ( $self->{actions} ) {
103 0         0 my $actions = $self->clear_actions;
104 0         0 $self->add_actions($actions);
105             }
106 1 50       22 $self->debug( $ENV{PERL_DEBUG} ) unless defined $self->debug;
107             }
108              
109             =head2 actions
110              
111             Get the array ref of Net::LDAP::Batch::Action objects in the batch.
112             To set the array, use add_actions().
113              
114             =head2 add_actions( I )
115              
116             Set the array of actions to be executed. I may be either
117             an array or array ref, and may either be key/value pairs as in
118             the SYNOPSIS or Net::LDAP::Batch::Action objects. You may not mix
119             the two types of values.
120              
121             Returns the total number of actions in batch.
122              
123             =cut
124              
125             my %action_classes = (
126             'add' => 'Net::LDAP::Batch::Action::Add',
127             'update' => 'Net::LDAP::Batch::Action::Update',
128             'delete' => 'Net::LDAP::Batch::Action::Delete',
129             );
130              
131             sub add_actions {
132 1     1 1 8 my $self = shift;
133 1         2 my @arg;
134 1 50 33     44 if ( @_ == 1 && ref( $_[0] ) eq 'ARRAY' ) {
135 0         0 @arg = @{ $_[0] };
  0         0  
136             }
137             else {
138 1         12 @arg = @_;
139             }
140              
141 1 50 33     14 if ( blessed( $arg[0] ) && $arg[0]->isa('Net::LDAP::Batch::Action') ) {
142 0         0 push( @{ $self->{actions} }, @arg );
  0         0  
143             }
144             else {
145 1 50       4 if ( @arg % 2 ) {
146 0         0 croak "uneven number of action key/value pairs";
147             }
148 1         3 while ( scalar(@arg) ) {
149 3         5 my $what = shift(@arg);
150 3         4 my $todo = shift(@arg);
151 3 50       19 if ( !exists $action_classes{$what} ) {
152 0         0 croak "unsupported action: $what";
153             }
154 3         8 my $class = $action_classes{$what};
155 3         4 my @todo;
156 3 50       16 if ( ref($todo) eq 'ARRAY' ) {
157 3         7 @todo = @$todo;
158             }
159             else {
160 0         0 @todo = ($todo);
161             }
162 3         5 for my $params (@todo) {
163 3         10 $params->{ldap} = $self->ldap;
164 3         22 $params->{debug} = $self->debug;
165 3         21 push( @{ $self->{actions} }, $class->new($params) );
  3         114  
166             }
167             }
168             }
169 1         3 return scalar( @{ $self->{actions} } );
  1         6  
170             }
171              
172             =head2 clear_actions
173              
174             Sets the number of actions to zero. Returns the former contents
175             of actions().
176              
177             =cut
178              
179             sub clear_actions {
180 0     0 1 0 my $self = shift;
181 0         0 my $actions = $self->{actions};
182 0         0 $self->{actions} = [];
183 0         0 return $actions;
184             }
185              
186             =head2 do
187              
188             Perform the actions and rollback() if any are fatal. Same thing as calling:
189              
190             eval { $batch->execute };
191             if ($@) {
192             warn "batch failed: $@";
193             $batch->rollback; # could be fatal
194             }
195              
196             The code above is nearly verbatim what do() actually does.
197              
198             =cut
199              
200             sub do {
201 1     1 1 2 my $self = shift;
202 1         12 eval { $self->execute };
  1         4  
203 1 50       7 if ($@) {
204 0         0 $self->{error} = $@;
205 0         0 $self->rollback;
206 0         0 return 0;
207             }
208 1         10 return 1;
209             }
210              
211             =head2 execute
212              
213             Calls execute() method on each action.
214              
215             =cut
216              
217             sub execute {
218 1     1 1 2 my $self = shift;
219 1 50 33     4 if ( !$self->actions or !scalar( @{ $self->actions } ) ) {
  1         9  
220 0         0 croak "no actions to execute";
221             }
222 1         6 for my $action ( @{ $self->actions } ) {
  1         3  
223 3 50       16 warn "executing $action\n" if $self->debug;
224 3         38 $action->execute;
225             }
226 1         16 return 1;
227             }
228              
229             =head2 rollback
230              
231             Calls rollback() method on each action.
232              
233             =cut
234              
235             sub rollback {
236 0     0 1   my $self = shift;
237 0 0 0       if ( !$self->actions or !scalar( @{ $self->actions } ) ) {
  0            
238 0           croak "no actions to rollback";
239             }
240 0           for my $action ( reverse @{ $self->actions } ) {
  0            
241 0 0         next unless $action->complete;
242 0 0         warn "rolling back $action\n" if $self->debug;
243 0           $action->rollback;
244             }
245 0           return 1;
246             }
247              
248             1;
249              
250             __END__