|
1 |
package SL::DB::Helper::CustomVariables;
|
|
2 |
|
|
3 |
use strict;
|
|
4 |
use Carp;
|
|
5 |
use Data::Dumper;
|
|
6 |
use List::Util qw(first);
|
|
7 |
use SL::DB::CustomVariableConfig;
|
|
8 |
|
|
9 |
use constant META_CVARS => 'cvars_config';
|
|
10 |
|
|
11 |
sub import {
|
|
12 |
my ($class, %params) = @_;
|
|
13 |
my $caller_package = caller;
|
|
14 |
|
|
15 |
# TODO: if module is empty, module overloading needs to take effect
|
|
16 |
# certain stuff may have more than one overload, odr even more than one type
|
|
17 |
defined $caller_package or croak 'need to be included from a caller reference';
|
|
18 |
|
|
19 |
$params{module} ||= _calc_modules_from_overloads(%params) if $params{overloads};
|
|
20 |
$params{sub_module} ||= '';
|
|
21 |
$params{id} ||= 'id';
|
|
22 |
|
|
23 |
$params{module} || $params{sub_module} or croak 'need param module or sub_module';
|
|
24 |
|
|
25 |
save_meta_info($caller_package, %params);
|
|
26 |
make_cvar_accessor($caller_package, %params);
|
|
27 |
make_cvar_alias($caller_package, %params) if $params{cvars_alias};
|
|
28 |
make_cvar_by_configs($caller_package, %params);
|
|
29 |
make_cvar_by_name($caller_package, %params);
|
|
30 |
}
|
|
31 |
|
|
32 |
sub save_meta_info {
|
|
33 |
my ($caller_package, %params) = @_;
|
|
34 |
|
|
35 |
my $meta = $caller_package->meta;
|
|
36 |
return 0 if $meta->{META_CVARS()};
|
|
37 |
|
|
38 |
$meta->{META_CVARS()} = \%params;
|
|
39 |
}
|
|
40 |
|
|
41 |
sub make_cvar_accessor {
|
|
42 |
my ($caller_package, %params) = @_;
|
|
43 |
|
|
44 |
my @module_filter = $params{module} ?
|
|
45 |
("config.module" => $params{module}) :
|
|
46 |
();
|
|
47 |
|
|
48 |
$caller_package->meta->add_relationships(
|
|
49 |
custom_variables => {
|
|
50 |
type => 'one to many',
|
|
51 |
class => 'SL::DB::CustomVariable',
|
|
52 |
column_map => { ($params{id} || 'id') => 'trans_id' },
|
|
53 |
manager_args => { with_objects => 'config' },
|
|
54 |
query_args => [ sub_module => $params{sub_module}, @module_filter ],
|
|
55 |
}
|
|
56 |
);
|
|
57 |
}
|
|
58 |
|
|
59 |
sub make_cvar_alias {
|
|
60 |
my ($caller_package) = @_;
|
|
61 |
no strict 'refs';
|
|
62 |
*{ $caller_package . '::cvars' } = sub {
|
|
63 |
goto &{ $caller_package . '::custom_variables' };
|
|
64 |
}
|
|
65 |
}
|
|
66 |
|
|
67 |
# this is used for templates where you need to list every applicable config
|
|
68 |
# auto vivifies non existant cvar objects as necessary.
|
|
69 |
sub make_cvar_by_configs {
|
|
70 |
my ($caller_package, %params) = @_;
|
|
71 |
|
|
72 |
no strict 'refs';
|
|
73 |
*{ $caller_package . '::cvars_by_config' } = sub {
|
|
74 |
my ($self) = @_;
|
|
75 |
@_ > 1 and croak "not an accessor";
|
|
76 |
|
|
77 |
my $configs = _all_configs(%params);
|
|
78 |
my $cvars = $self->custom_variables;
|
|
79 |
my %cvars_by_config = map { $_->config_id => $_ } @$cvars;
|
|
80 |
|
|
81 |
my @return = map { $cvars_by_config{$_->id} || _new_cvar($self, %params, config => $_) } @$configs;
|
|
82 |
|
|
83 |
return \@return;
|
|
84 |
}
|
|
85 |
}
|
|
86 |
|
|
87 |
# this is used for print templates where you need to refer to a variable by name
|
|
88 |
# TODO typically these were referred as prefix_'cvar'_name
|
|
89 |
sub make_cvar_by_name {
|
|
90 |
my ($caller_package, %params) = @_;
|
|
91 |
|
|
92 |
no strict 'refs';
|
|
93 |
*{ $caller_package . '::cvar_by_name' } = sub {
|
|
94 |
my ($self, $name) = @_;
|
|
95 |
|
|
96 |
my $configs = _all_configs(%params);
|
|
97 |
my $cvars = $self->custom_variables;
|
|
98 |
my $config = first { $_->name eq $name } @$configs;
|
|
99 |
|
|
100 |
croak "unknown cvar name $name" unless $config;
|
|
101 |
|
|
102 |
my $cvar = first { $_->config_id eq $config->id } @$cvars;
|
|
103 |
|
|
104 |
if (!$cvar) {
|
|
105 |
$cvar = _new_cvar($self, %params, config => $config);
|
|
106 |
$self->add_custom_variables($cvar);
|
|
107 |
}
|
|
108 |
|
|
109 |
return $cvar;
|
|
110 |
}
|
|
111 |
}
|
|
112 |
|
|
113 |
sub _all_configs {
|
|
114 |
my (%params) = @_;
|
|
115 |
$params{module}
|
|
116 |
? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ])
|
|
117 |
: SL::DB::Manager::CustomVariableConfig->get_all;
|
|
118 |
}
|
|
119 |
|
|
120 |
sub _overload_by_module {
|
|
121 |
my ($module, %params) = @_;
|
|
122 |
|
|
123 |
while (my ($fk, $class) = each %{ $params{overloads} }) {
|
|
124 |
return ($fk, $class) if $class->meta->{META_CVARS()}->{module} eq $module;
|
|
125 |
}
|
|
126 |
|
|
127 |
croak "unknown overload, cannot resolve module $module";
|
|
128 |
}
|
|
129 |
|
|
130 |
sub _new_cvar {
|
|
131 |
my ($self, %params) = @_;
|
|
132 |
my $inherited_value;
|
|
133 |
# check overloading first
|
|
134 |
if ($params{sub_module}) {
|
|
135 |
my ($fk, $class) = _overload_by_module($params{config}->module, %params);
|
|
136 |
my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name);
|
|
137 |
$inherited_value = $base_cvar->value;
|
|
138 |
}
|
|
139 |
|
|
140 |
my $cvar = SL::DB::CustomVariable->new(
|
|
141 |
config => $params{config},
|
|
142 |
trans_id => $self->${ \ $params{id} },
|
|
143 |
sub_module => $params{sub_module},
|
|
144 |
);
|
|
145 |
# value needs config
|
|
146 |
$inherited_value
|
|
147 |
? $cvar->value($inherited_value)
|
|
148 |
: $cvar->value($params{config}->default_value);
|
|
149 |
return $cvar;
|
|
150 |
}
|
|
151 |
|
|
152 |
sub _calc_modules_from_overloads {
|
|
153 |
my (%params) = @_;
|
|
154 |
my %modules;
|
|
155 |
|
|
156 |
while (my ($fk, $class) = each %{ $params{overloads} }) {
|
|
157 |
eval "require $class"; # make sure the class is loaded
|
|
158 |
my $module = $class->meta->{META_CVARS()}->{module};
|
|
159 |
next if ref $module;
|
|
160 |
$modules{$module} = 1;
|
|
161 |
}
|
|
162 |
|
|
163 |
return [ keys %modules ];
|
|
164 |
}
|
|
165 |
|
|
166 |
|
|
167 |
1;
|
|
168 |
|
|
169 |
__END__
|
|
170 |
|
|
171 |
=encoding utf-8
|
|
172 |
|
|
173 |
=head1 NAME
|
|
174 |
|
|
175 |
SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations
|
|
176 |
|
|
177 |
=head1 SYNOPSIS
|
|
178 |
|
|
179 |
# use in a primary class
|
|
180 |
use SL::DB::Helper::CustomVariables (
|
|
181 |
module => 'IC',
|
|
182 |
cvars_alias => 1,
|
|
183 |
);
|
|
184 |
|
|
185 |
# use overloading in a secondary class
|
|
186 |
use SL::DB::Helper::CustomVariables (
|
|
187 |
sub_module => 'orderitems',
|
|
188 |
cvars_alias => 1,
|
|
189 |
overloads => {
|
|
190 |
parts_id => 'SL::DB::Part',
|
|
191 |
}
|
|
192 |
);
|
|
193 |
|
|
194 |
=head1 DESCRIPTION
|
|
195 |
|
|
196 |
This module provides methods to deal with named custom variables. Two concepts are understood.
|
|
197 |
|
|
198 |
=head2 Primary CVar Classes
|
|
199 |
|
|
200 |
Primary classes are those that feature cvars for themselves. Currently those
|
|
201 |
are Part, Contact, Customer and Vendor. cvars for these will get saved directly
|
|
202 |
for the object.
|
|
203 |
|
|
204 |
=head2 Secondary CVar Classes
|
|
205 |
|
|
206 |
Secondary classes inherit their cvars from member relationships. This is built
|
|
207 |
so that orders can save a copy of the cvars of their parts, customers and the
|
|
208 |
like to be immutable later on.
|
|
209 |
|
|
210 |
Secondary classes may currently not have cvars of their own.
|
|
211 |
|
|
212 |
=head1 INSTALLED METHODS
|
|
213 |
|
|
214 |
=over 4
|
|
215 |
|
|
216 |
=item C<custom_variables [ CUSTOM_VARIABLES ]>
|
|
217 |
|
|
218 |
This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
|
|
219 |
like any other OneToMany relationship.
|
|
220 |
|
|
221 |
=item C<cvars [ CUSTOM_VARIABLES ]>
|
|
222 |
|
|
223 |
Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
|
|
224 |
passed to import.
|
|
225 |
|
|
226 |
=item C<cvars_by_config>
|
|
227 |
|
|
228 |
Thi will return a list of CVars with the following changes over the standard accessor:
|
|
229 |
|
|
230 |
=over 4
|
|
231 |
|
|
232 |
=item *
|
|
233 |
|
|
234 |
The list will be returned in the sorted order of the configs.
|
|
235 |
|
|
236 |
=item *
|
|
237 |
|
|
238 |
For every config exactly one CVar will be returned.
|
|
239 |
|
|
240 |
=item *
|
|
241 |
|
|
242 |
If no cvar was found for a config, a new one will be vivified, set to the
|
|
243 |
correct config, module etc, and registered into the object.
|
|
244 |
|
|
245 |
=item *
|
|
246 |
|
|
247 |
Vivified cvars for secondary classes will first try to find their base object
|
|
248 |
and use that value. If no such value or cvar is found the default value from
|
|
249 |
configs applies.
|
|
250 |
|
|
251 |
=back
|
|
252 |
|
|
253 |
This is useful if you need to list every possible CVar, like in CRUD masks.
|
|
254 |
|
|
255 |
=item C<cvar_by_name NAME [ VALUE ]>
|
|
256 |
|
|
257 |
Returns the CVar object for this object which matches the given internal name.
|
|
258 |
Useful for print templates. If the requested cvar is not present, it will be
|
|
259 |
vivified with the same rules as in C<cvars_by_config>.
|
|
260 |
|
|
261 |
=back
|
|
262 |
|
|
263 |
=head1 AUTHOR
|
|
264 |
|
|
265 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
|
|
266 |
|
|
267 |
=cut
|
CVars als Object Mixin.
Conflicts: