]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm
teaching everyone how to note the pkey delete restriction policy for specific classes
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Utils / Fieldmapper.pm
1 package Fieldmapper;
2 use OpenSRF::Utils::JSON;
3 use Data::Dumper;
4 use base 'OpenSRF::Application';
5 use OpenSRF::Utils::Logger;
6 use OpenSRF::Utils::SettingsClient;
7 use OpenSRF::System;
8 use XML::Simple;
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12 use vars qw/$fieldmap $VERSION/;
13
14 sub publish_fieldmapper {
15         my ($self,$client,$class) = @_;
16
17         return $fieldmap unless (defined $class);
18         return undef unless (exists($$fieldmap{$class}));
19         return {$class => $$fieldmap{$class}};
20 }
21 __PACKAGE__->register_method(
22         api_name        => 'opensrf.open-ils.system.fieldmapper',
23         api_level       => 1,
24         method          => 'publish_fieldmapper',
25 );
26
27 #
28 # To dump the Javascript version of the fieldmapper struct use the command:
29 #
30 #       PERL5LIB=~/cvs/ILS/OpenSRF/src/perlmods/:~/cvs/ILS/Open-ILS/src/perlmods/ GEN_JS=1 perl -MOpenILS::Utils::Fieldmapper -e 'print "\n";'
31 #
32 # ... adjusted for your CVS sandbox, of course.
33 #
34
35 sub classes {
36         return () unless (defined $fieldmap);
37         return keys %$fieldmap;
38 }
39
40 import();
41 sub import {
42         my $class = shift;
43         my %args = @_;
44
45         return if (keys %$fieldmap);
46         return if (!OpenSRF::System->connected && !$args{IDL});
47
48         # parse the IDL ...
49         my $file = $args{IDL} || OpenSRF::Utils::SettingsClient->new->config_value( 'IDL' );
50         #my $idl = XMLin( $file, ForceArray => 0, KeyAttr => ['name', 'id'], ValueAttr => {link =>'key'} )->{class};
51         my $idl = XMLin( $file, ForceArray => 0, KeyAttr => ['name', 'id', 'field'] )->{class};
52
53         for my $c ( keys %$idl ) {
54                 next unless ($idl->{$c}{'oils_obj:fieldmapper'});
55                 my $n = 'Fieldmapper::'.$idl->{$c}{'oils_obj:fieldmapper'};
56
57                 $log->debug("Building Fieldmapper class for [$n] from IDL");
58
59                 $$fieldmap{$n}{hint} = $c;
60                 $$fieldmap{$n}{virtual} = ($idl->{$c}{'oils_persist:virtual'} && $idl->{$c}{'oils_persist:virtual'} eq 'true') ? 1 : 0;
61                 $$fieldmap{$n}{table} = $idl->{$c}{'oils_persist:tablename'};
62                 $$fieldmap{$n}{restrict_primary} = $idl->{$c}{'oils_persist:restrict_primary'};
63                 $$fieldmap{$n}{sequence} = $idl->{$c}{fields}{'oils_persist:sequence'};
64                 $$fieldmap{$n}{identity} = $idl->{$c}{fields}{'oils_persist:primary'};
65
66                 for my $f ( keys %{ $idl->{$c}{fields}{field} } ) {
67                         $$fieldmap{$n}{fields}{$f} =
68                                 { virtual => ($idl->{$c}{fields}{field}{$f}{'oils_persist:virtual'} eq 'true') ? 1 : 0,
69                                   position => $idl->{$c}{fields}{field}{$f}{'oils_obj:array_position'},
70                                 };
71
72                         if ($idl->{$c}{fields}{field}{$f}{'reporter:selector'}) {
73                                 $$fieldmap{$n}{selector} = $idl->{$c}{fields}{field}{$f}{'reporter:selector'};
74                         }
75                 }
76                 for my $f ( keys %{ $idl->{$c}{links}{link} } ) {
77                         $$fieldmap{$n}{links}{$f} =
78                                 { class => $idl->{$c}{links}{link}{$f}{class},
79                                   reltype => $idl->{$c}{links}{link}{$f}{reltype},
80                                   key => $idl->{$c}{links}{link}{$f}{key},
81                                 };
82                 }
83         }
84
85
86         #-------------------------------------------------------------------------------
87         # Now comes the evil!  Generate classes
88
89         for my $pkg ( __PACKAGE__->classes ) {
90                 (my $cdbi = $pkg) =~ s/^Fieldmapper:://o;
91
92                 eval <<"                PERL";
93                         package $pkg;
94                         use base 'Fieldmapper';
95                 PERL
96
97                 my $pos = 0;
98                 for my $vfield ( qw/isnew ischanged isdeleted/ ) {
99                         $$fieldmap{$pkg}{fields}{$vfield} = { position => $pos, virtual => 1 };
100                         $pos++;
101                 }
102
103                 if (exists $$fieldmap{$pkg}{proto_fields}) {
104                         for my $pfield ( sort keys %{ $$fieldmap{$pkg}{proto_fields} } ) {
105                                 $$fieldmap{$pkg}{fields}{$pfield} = { position => $pos, virtual => $$fieldmap{$pkg}{proto_fields}{$pfield} };
106                                 $pos++;
107                         }
108                 }
109
110                 OpenSRF::Utils::JSON->register_class_hint(
111                         hint => $pkg->json_hint,
112                         name => $pkg,
113                         type => 'array',
114                 );
115
116         }
117 }
118
119 sub new {
120         my $self = shift;
121         my $value = shift;
122         $value = [] unless (defined $value);
123         return bless $value => $self->class_name;
124 }
125
126 sub decast {
127         my $self = shift;
128         return [ @$self ];
129 }
130
131 sub DESTROY {}
132
133 sub AUTOLOAD {
134         my $obj = shift;
135         my $value = shift;
136         (my $field = $AUTOLOAD) =~ s/^.*://o;
137         my $class_name = $obj->class_name;
138
139         my $fpos = $field;
140         $fpos  =~ s/^clear_//og ;
141
142         my $pos = $$fieldmap{$class_name}{fields}{$fpos}{position};
143
144         if ($field =~ /^clear_/o) {
145                 {       no strict 'subs';
146                         *{$obj->class_name."::$field"} = sub {
147                                 my $self = shift;
148                                 $self->[$pos] = undef;
149                                 return 1;
150                         };
151                 }
152                 return $obj->$field();
153         }
154
155         die "No field by the name $field in $class_name!"
156                 unless (exists $$fieldmap{$class_name}{fields}{$field} && defined($pos));
157
158
159         {       no strict 'subs';
160                 *{$obj->class_name."::$field"} = sub {
161                         my $self = shift;
162                         my $new_val = shift;
163                         $self->[$pos] = $new_val if (defined $new_val);
164                         return $self->[$pos];
165                 };
166         }
167         return $obj->$field($value);
168 }
169
170 sub Selector {
171         my $self = shift;
172         return $$fieldmap{$self->class_name}{selector};
173 }
174
175 sub Identity {
176         my $self = shift;
177         return $$fieldmap{$self->class_name}{identity};
178 }
179
180 sub RestrictPrimary {
181         my $self = shift;
182         return $$fieldmap{$self->class_name}{restrict_primary};
183 }
184
185 sub Sequence {
186         my $self = shift;
187         return $$fieldmap{$self->class_name}{sequence};
188 }
189
190 sub Table {
191         my $self = shift;
192         return $$fieldmap{$self->class_name}{table};
193 }
194
195 sub class_name {
196         my $class_name = shift;
197         return ref($class_name) || $class_name;
198 }
199
200 sub real_fields {
201         my $self = shift;
202         my $class_name = $self->class_name;
203         my $fields = $$fieldmap{$class_name}{fields};
204
205         my @f = grep {
206                         !$$fields{$_}{virtual}
207                 } sort {$$fields{$a}{position} <=> $$fields{$b}{position}} keys %$fields;
208
209         return @f;
210 }
211
212 sub has_field {
213         my $self = shift;
214         my $field = shift;
215         my $class_name = $self->class_name;
216         return 1 if grep { $_ eq $field } keys %{$$fieldmap{$class_name}{fields}};
217         return 0;
218 }
219
220 sub properties {
221         my $self = shift;
222         my $class_name = $self->class_name;
223         return keys %{$$fieldmap{$class_name}{fields}};
224 }
225
226 sub to_bare_hash {
227         my $self = shift;
228
229         my %hash = ();
230         for my $f ($self->properties) {
231                 my $val = $self->$f;
232                 $hash{$f} = $val;
233         }
234
235         return \%hash;
236 }
237
238 sub clone {
239         my $self = shift;
240         return $self->new( [@$self] );
241 }
242
243 sub api_level {
244         my $self = shift;
245         return $fieldmap->{$self->class_name}->{api_level};
246 }
247
248 sub cdbi {
249         my $self = shift;
250         return $fieldmap->{$self->class_name}->{cdbi};
251 }
252
253 sub is_virtual {
254         my $self = shift;
255         my $field = shift;
256         return $fieldmap->{$self->class_name}->{proto_fields}->{$field} if ($field);
257         return $fieldmap->{$self->class_name}->{virtual};
258 }
259
260 sub is_readonly {
261         my $self = shift;
262         my $field = shift;
263         return $fieldmap->{$self->class_name}->{readonly};
264 }
265
266 sub json_hint {
267         my $self = shift;
268         return $fieldmap->{$self->class_name}->{hint};
269 }
270
271
272 1;