File Coverage

blib/lib/SPOPS/ClassFactory/DefaultBehavior.pm
Criterion Covered Total %
statement 15 177 8.4
branch 0 76 0.0
condition 0 9 0.0
subroutine 5 15 33.3
pod 0 6 0.0
total 20 283 7.0


line stmt bran cond sub pod time code
1             package SPOPS::ClassFactory::DefaultBehavior;
2              
3             # $Id: DefaultBehavior.pm,v 3.10 2004/06/02 00:48:22 lachoy Exp $
4              
5 17     17   106 use strict;
  17         41  
  17         916  
6 17     17   95 use Log::Log4perl qw( get_logger );
  17         35  
  17         115  
7 17     17   815 use SPOPS;
  17         35  
  17         1030  
8 17     17   20543 use SPOPS::ClassFactory qw( OK DONE ERROR RULESET_METHOD );
  17         65  
  17         72278  
9              
10             my $log = get_logger();
11              
12             $SPOPS::ClassFactory::DefaultBehavior::VERSION = sprintf("%d.%02d", q$Revision: 3.10 $ =~ /(\d+)\.(\d+)/);
13              
14             # Overlap here with DBI...
15             my @PARSE_INTO_HASH = qw( field no_insert no_update skip_undef multivalue );
16             my @PARSE_INTO_ARRAY = qw( sql_defaults fetch_by ldap_object_class );
17              
18             # NOTE: These behaviors are called from SPOPS.pm, although they can be
19             # theoretically called from anywhere.
20              
21             ########################################
22             # BEHAVIOR: manipulate_configuration
23             ########################################
24              
25             sub conf_modify_config {
26 0     0 0   my ( $class ) = @_;
27              
28 0 0         $log->is_info &&
29             $log->info( "Trying to modify configuration for class [$class]" );
30 0           my $CONFIG = $class->CONFIG;
31              
32 0 0         if ( ref $CONFIG->{field} eq 'ARRAY' ) {
    0          
33 0           $CONFIG->{field_list} = [ @{ $CONFIG->{field} } ];
  0            
34             }
35             elsif ( $CONFIG->{field} ) {
36 0           $CONFIG->{field_list} = [ $CONFIG->{field} ];
37             }
38             else {
39 0           $CONFIG->{field_list} = [];
40             }
41              
42             # Store the raw fieldnames before we do anything else to them
43              
44 0 0         unless ( $CONFIG->{field_raw} ) {
45 0           $CONFIG->{field_raw} = [ @{ $CONFIG->{field_list} } ];
  0            
46             }
47              
48             # When we change a listref to a hashref, keep the order
49             # by maintaining a count; that way they can be re-ordered
50             # if desired.
51              
52             HASHITEM:
53 0           foreach my $item ( @PARSE_INTO_HASH ) {
54 0 0         unless ( defined $CONFIG->{ $item } ) {
55 0           $CONFIG->{ $item } = {};
56 0           next HASHITEM;
57             }
58 0 0         if ( ref $CONFIG->{ $item } ne 'ARRAY' ) {
59 0           $CONFIG->{ $item } = [ $CONFIG->{ $item } ];
60             }
61             $log->is_info &&
62 0 0         $log->info( "Parsing key ($item) into a hash" );
63 0           my $count = 1;
64 0           my %new = ();
65 0           foreach my $subitem ( @{ $CONFIG->{ $item } } ) {
  0            
66 0           $new{ $subitem } = $count;
67 0           $count++;
68             }
69 0           $CONFIG->{ $item } = \%new;
70             }
71              
72 0           foreach my $item ( @PARSE_INTO_ARRAY ) {
73 0 0         unless ( defined $CONFIG->{ $item } ) {
74 0           $CONFIG->{ $item } = [];
75             }
76 0 0         if ( ref $CONFIG->{ $item } ne 'ARRAY' ) {
77 0           $CONFIG->{ $item } = [ $CONFIG->{ $item } ];
78             }
79             }
80 0           return ( OK, undef );
81             }
82              
83             ########################################
84             # BEHAVIOR: id_method
85             ########################################
86              
87             my $ID_TEMPLATE = <<'IDTMPL';
88              
89             # Get the ID of this object, and optionally set it as well.
90              
91             sub %%GEN_CLASS%%::id {
92             my ( $self, $new_id ) = @_;
93             my $id_field = $self->id_field ||
94             SPOPS::Exception->throw(
95             "Cannot find ID for object since no ID " .
96             "field specified for class [" .
97             ref( $self ) . ']' );
98             return $self->{ $id_field } unless ( $new_id );
99             return $self->{ $id_field } = $new_id;
100             }
101              
102             IDTMPL
103              
104             # We return 'DONE' here because other behaviors shouldn't redefine
105              
106             sub conf_id_method {
107 0     0 0   my ( $class ) = @_;
108 0           my $id_method = $ID_TEMPLATE;
109 0           $id_method =~ s/%%GEN_CLASS%%/$class/g;
110 0 0         $log->is_debug &&
111             $log->debug( "ID method being created\n$id_method" );
112             {
113 0     0     local $SIG{__WARN__} = sub { return undef };
  0            
  0            
114 0           eval $id_method;
115 0 0         if ( $@ ) {
116 0           return ( ERROR, "Cannot generate method 'id' in class " .
117             "[$class]. Error: $@" );
118             }
119             }
120 0           return ( DONE, undef );
121             }
122              
123              
124             ########################################
125             # BEHAVIOR: read_code
126             ########################################
127              
128             #
129             # Returns: arrayref of files used
130              
131             sub conf_read_code {
132 0     0 0   my ( $class ) = @_;
133              
134 0           my $CONFIG = $class->CONFIG;
135 0           my $code_class = $CONFIG->{code_class};
136 0 0         return ( OK, undef ) unless ( $code_class );
137              
138 0           my @files_used = ();
139 0 0         $code_class = [ $code_class ] unless ( ref $code_class eq 'ARRAY' );
140 0           foreach my $read_code_class ( @{ $code_class } ) {
  0            
141 0 0         $log->is_debug &&
142             $log->debug( "Trying to read code from [$read_code_class]",
143             "into [$class]" );
144 0           my $filename = $read_code_class;
145 0           $filename =~ s|::|/|g;
146 0           my $final_filename = undef;
147              
148             PREFIX:
149 0           foreach my $prefix ( @INC ) {
150 0           my $full_filename = "$prefix/$filename.pm";
151 0 0         $log->is_debug &&
152             $log->debug( "Try file: [$full_filename]" );
153 0 0         if ( -f $full_filename ) {
154 0           $final_filename = $full_filename;
155 0           last PREFIX;
156             }
157             }
158              
159 0 0 0       unless ( $final_filename and -f $final_filename ) {
160 0           return ( ERROR, "Class [$read_code_class] specified in " .
161             "'code_class' configuration defintion " .
162             "for class [$class] was not found in \@INC" );
163             }
164              
165             $log->is_debug &&
166 0 0         $log->debug( "File [$final_filename] will be used for ",
167             "[$read_code_class]" );
168              
169 0 0         eval { open( PKG, $final_filename ) || die $! };
  0            
170 0 0         if ( $@ ) {
171 0           return ( ERROR, "Cannot read [$final_filename] specified in " .
172             "'code_class' configuration definition for " .
173             "class [$class]. Error: $@" );
174             }
175 0           my $code_pkg = undef;
176 0           push @files_used, $final_filename;
177              
178             CODEPKG:
179 0           while ( ) {
180 0 0         if ( s/^\s*package $read_code_class\s*;\s*$/package $class;/ ) {
181 0           $code_pkg .= $_;
182 0 0         $log->is_info &&
183             $log->info( "Package [$read_code_class] will be ",
184             "read in as [$class]" );
185 0           last CODEPKG;
186             }
187 0           $code_pkg .= $_;
188             }
189              
190             # Use a block here because we want the $/ setting to NOT be
191             # localized in the while loop -- that would be bad, since the
192             # 'package' substitution would never work after the first
193             # one...
194              
195             {
196 0           local $/ = undef;
  0            
197 0           $code_pkg .= ;
198             }
199 0           close( PKG );
200 0 0         $log->is_debug &&
201             $log->debug( "Going to eval code:\n\n$code_pkg" );
202             {
203 0     0     local $SIG{__WARN__} = sub { return undef };
  0            
  0            
204 0           eval $code_pkg;
205 0 0         if ( $@ ) {
206 0           return ( ERROR, "Error running 'eval' on code read from " .
207             "[$final_filename] as specified in " .
208             "'code_class' configuration defintion for " .
209             "class [$class]. Error: $@" );
210             }
211             }
212             }
213 0           return ( OK, undef );
214             }
215              
216              
217             ########################################
218             # BEHAVIOR: has_a
219             ########################################
220              
221             # EVAL'ABLE PACKAGE/SUBROUTINES
222              
223             my $GENERIC_HASA = <<'HASA';
224              
225             sub %%GEN_CLASS%%::%%HASA_ALIAS%% {
226             my ( $self, $p ) = @_;
227             return undef unless ( $self->{%%HASA_ID_FIELD%%} );
228             return %%HASA_CLASS%%->fetch( $self->{%%HASA_ID_FIELD%%}, $p );
229             }
230              
231             HASA
232              
233              
234             # First do the 'has_a' aliases; see POD documentation on this (below)
235              
236             sub conf_relate_hasa {
237 0     0 0   my ( $class ) = @_;
238 0           my $CONFIG = $class->CONFIG;
239 0   0       $CONFIG->{has_a} ||= {};
240              
241 0           foreach my $hasa_class ( keys %{ $CONFIG->{has_a} } ) {
  0            
242              
243             # Since the class specified can be a subclass of what's
244             # generated, ensure that it's available
245              
246 0           eval "require $hasa_class";
247 0           my $require_error = $@;
248 0           my $hasa_config = eval { $hasa_class->CONFIG };
  0            
249 0 0         if ( $@ ) {
250 0           return ( ERROR, "Failed to retrieve configuration from " .
251             "'$hasa_class': $@. (Require error: $require_error)" );
252             }
253              
254             $log->is_info &&
255 0 0         $log->info( "Try to alias [$class] hasa [$hasa_class]" );
256 0           my $hasa_id_field = $hasa_config->{id_field};
257 0           my $hasa_sub = $GENERIC_HASA;
258 0           $hasa_sub =~ s/%%GEN_CLASS%%/$class/g;
259 0           $hasa_sub =~ s/%%HASA_CLASS%%/$hasa_class/g;
260              
261             # Each defined relationship can be between more than one instance
262             # of another class, each of which is linked to a separate ID
263             # field.. For instance, if my SPOPS objects had two user_id fields
264             # in it (say, 'created_by' and 'last_updated_by'), then I need to
265             # create *two* links from this class to the user class.
266              
267             # Example:
268              
269             # This specification has two links to one class:
270              
271             # has_a => { 'MySPOPS::User' => [ 'created_by', 'updated_by' ], ... }
272              
273             # This specification has one link to one class:
274              
275             # has_a => { 'MySPOPS::User' => 'created_by', ... }
276              
277 0 0         my $id_fields = ( ref $CONFIG->{has_a}{ $hasa_class } eq 'ARRAY' )
278             ? $CONFIG->{has_a}{ $hasa_class }
279             : [ $CONFIG->{has_a}{ $hasa_class } ];
280 0           my $num_id_fields = scalar @{ $id_fields };
  0            
281 0           foreach my $usea_id_info ( @{ $id_fields } ) {
  0            
282 0           my ( $hasa_alias, $usea_id_field ) = '';
283              
284             # This can be a hash when we want to specify the alias name in
285             # the configuration rather than let SPOPS create it for
286             # us. Something like the following where we want use the alias
287             # 'creator' rather than the alias SPOPS will create,
288             # 'created_by_user':
289              
290             # has_a => { 'MySPOPS::User' => [ { 'created_by' => 'creator' }, ... ], ... }
291              
292 0 0         if ( ref $usea_id_info eq 'HASH' ) {
293 0           $usea_id_field = ( keys %{ $usea_id_info } )[0];
  0            
294 0           $hasa_alias = $usea_id_info->{ $usea_id_field };
295             }
296             else {
297 0           $usea_id_field = $usea_id_info;
298 0 0         if ( $usea_id_field eq $hasa_id_field ) {
299 0           $hasa_alias = $hasa_config->{main_alias}
300             }
301             else {
302 0           $hasa_alias = join( '_', $usea_id_field, $hasa_config->{main_alias} );
303             }
304             }
305              
306 0           my $this_hasa_sub = $hasa_sub;
307 0           $this_hasa_sub =~ s/%%HASA_ALIAS%%/$hasa_alias/g;
308 0           $this_hasa_sub =~ s/%%HASA_ID_FIELD%%/$usea_id_field/g;
309 0 0         $log->is_debug &&
310             $log->debug( "Aliasing [$hasa_class] with field [$usea_id_field] ",
311             "using alias [$hasa_alias] within [$class]" );
312 0 0         $log->is_debug &&
313             $log->debug( "Now going to eval the routine:\n$this_hasa_sub" );
314             {
315 0     0     local $SIG{__WARN__} = sub { return undef };
  0            
  0            
316 0           eval $this_hasa_sub;
317 0 0         if ( $@ ) {
318 0           return ( ERROR, "Error reading 'has_a' code for alias " .
319             "[$hasa_alias] mapped to class " .
320             "[$hasa_class] into [$class]. Error: $@\n" );
321             }
322             }
323             }
324             }
325 0           return ( OK, undef );
326             }
327              
328              
329             ########################################
330             # BEHAVIOR: fetch_by
331             ########################################
332              
333             my $GENERIC_FETCH_BY = <<'FETCHBY';
334              
335             sub %%GEN_CLASS%%::fetch_by_%%FETCH_BY_FIELD%% {
336             my ( $item, $fb_field_value, $p ) = @_;
337             $p ||= {};
338             my $obj_list = $item->fetch_group({ where => "%%FETCH_BY_FIELD%% = ?",
339             value => [ $fb_field_value ],
340             %{ $p } });
341             if ( $p->{return_single} ) {
342             return $obj_list->[0];
343             }
344             return $obj_list;
345             }
346              
347             FETCHBY
348              
349             # Next, process the 'fetch_by' fields
350              
351             sub conf_relate_fetchby {
352 0     0 0   my ( $class ) = @_;
353 0           my $CONFIG = $class->CONFIG;
354 0   0       $CONFIG->{fetch_by} ||= [];
355 0           foreach my $fetch_by_field ( @{ $CONFIG->{fetch_by} } ) {
  0            
356 0           my $fetch_by_sub = $GENERIC_FETCH_BY;
357 0           $fetch_by_sub =~ s/%%GEN_CLASS%%/$class/g;
358 0           $fetch_by_sub =~ s/%%FETCH_BY_FIELD%%/$fetch_by_field/g;
359 0 0         $log->is_debug &&
360             $log->debug( "Creating fetch_by for field ($fetch_by_field)" );
361 0 0         $log->is_debug &&
362             $log->debug( "Now going to eval the routine:\n$fetch_by_sub" );
363             {
364 0     0     local $SIG{__WARN__} = sub { return undef };
  0            
  0            
365 0           eval $fetch_by_sub;
366 0 0         if ( $@ ) {
367 0           return ( ERROR, "Cannot eval 'fetch_by' code for field " .
368             "[$fetch_by_field] into [$class]. Error: $@" );
369             }
370             }
371             }
372 0           return ( OK, undef );
373             }
374              
375              
376             ########################################
377             # BEHAVIOR: add_rule
378             ########################################
379              
380             my $GENERIC_RULESET_REFER = <<'RULESET';
381              
382             $%%GEN_CLASS%%::RULESET = {};
383             sub %%GEN_CLASS%%::RULESET { return $%%GEN_CLASS%%::RULESET }
384              
385             RULESET
386              
387             sub conf_add_rules {
388 0     0 0   my ( $class ) = @_;
389 0           my $CONFIG = $class->CONFIG;
390 0 0         $log->is_info &&
391             $log->info( "Adding rules to ($class)" );
392              
393             # Install the variable/subroutine RULESET into the class
394              
395 0           my $ruleset_info = $GENERIC_RULESET_REFER;
396 0           $ruleset_info =~ s/%%GEN_CLASS%%/$class/g;
397              
398             {
399 17     17   200 no warnings 'redefine';
  17         50  
  17         7299  
  0            
400 0           eval $ruleset_info;
401 0 0         if ( $@ ) {
402 0           return ( ERROR, "Could not eval ruleset info into [$class]. Error: $@" );
403             }
404             }
405              
406             # Now find all the classes that have the method RULESET_METHOD
407             # (and 'ruleset_add' for backwards compatibility)
408              
409 0   0       my $rule_classes = $CONFIG->{rules_from} || [];
410 0           my $subs = SPOPS::ClassFactory->find_parent_methods( $class, $rule_classes, RULESET_METHOD, 'ruleset_add' );
411 0           foreach my $sub_info ( @{ $subs } ) {
  0            
412 0 0         $log->is_debug &&
413             $log->debug( "Calling ruleset generation for [$class] ",
414             "from [$sub_info->[0]]" );
415 0           $sub_info->[1]->( $class, $class->RULESET );
416             }
417 0           return ( OK, undef );
418             }
419              
420              
421             1;
422              
423             __END__