1 package SL::MoreCommon;
4 our @ISA = qw(Exporter);
6 our @EXPORT = qw(save_form restore_form compare_numbers cross);
7 our @EXPORT_OK = qw(ary_union ary_intersect ary_diff listify ary_to_hash uri_encode uri_decode);
10 use List::MoreUtils qw(zip);
16 $main::lxdebug->enter_sub();
18 my @dont_dump_keys = @_;
19 my %not_dumped_values;
21 foreach my $key (@dont_dump_keys) {
22 $not_dumped_values{$key} = $main::form->{$key};
23 delete $main::form->{$key};
26 my $old_form = SL::YAML::Dump($main::form);
27 $old_form =~ s|!|!:|g;
28 $old_form =~ s|\n|!n|g;
29 $old_form =~ s|\r|!r|g;
31 map { $main::form->{$_} = $not_dumped_values{$_} } keys %not_dumped_values;
33 $main::lxdebug->leave_sub();
39 $main::lxdebug->enter_sub();
41 my ($old_form, $no_delete, @keep_vars) = @_;
43 my $form = $main::form;
44 my %keep_vars_map = map { $_ => 1 } @keep_vars;
46 map { delete $form->{$_} if (!$keep_vars_map{$_}); } keys %{$form} unless ($no_delete);
48 $old_form =~ s|!r|\r|g;
49 $old_form =~ s|!n|\n|g;
50 $old_form =~ s|![!:]|!|g;
52 my $new_form = SL::YAML::Load($old_form);
53 map { $form->{$_} = $new_form->{$_} if (!$keep_vars_map{$_}) } keys %{ $new_form };
55 $main::lxdebug->leave_sub();
59 $main::lxdebug->enter_sub();
61 my ($a, $a_unit, $b, $b_unit) = @_;
63 my $units = AM->retrieve_all_units;
65 if (!$units->{$a_unit} || !$units->{$b_unit} || ($units->{$a_unit}->{base_unit} ne $units->{$b_unit}->{base_unit})) {
66 $main::lxdebug->leave_sub();
70 $a *= $units->{$a_unit}->{factor};
71 $b *= $units->{$b_unit}->{factor};
73 $main::lxdebug->leave_sub();
81 local (*A, *B) = @_; # syms for caller's input arrays
84 my ($caller_a, $caller_b) = do {
87 \*{$pkg.'::a'}, \*{$pkg.'::b'};
90 local(*$caller_a, *$caller_b);
92 # This map expression is also the return value.
93 map { my $a_index = $_;
94 map { my $b_index = $_;
95 # assign to $a, $b as refs to caller's array elements
96 (*$caller_a, *$caller_b) = \($A[$a_index], $B[$b_index]);
97 $op->(); # perform the transformation
102 sub _ary_calc_union_intersect {
107 foreach my $e (@$a, @$b) { $count{$e}++ }
111 foreach my $e (keys %count) {
113 push @isect, $e if $count{$e} == 2;
116 return (\@union, \@isect);
120 return @{ (_ary_calc_union_intersect @_)[0] };
124 return @{ (_ary_calc_union_intersect @_)[1] };
129 my %in_b = map { $_ => 1 } @$b;
130 return grep { !$in_b{$_} } @$a;
134 my @ary = scalar @_ > 1 ? @_ : ref $_[0] eq 'ARRAY' ? @{ $_[0] } : (@_);
135 return wantarray ? @ary : scalar @ary;
140 my $value_key = shift;
142 return map { ($_, 1) } @_ if !defined($idx_key);
144 my @indexes = map { ref $_ eq 'HASH' ? $_->{ $idx_key } : $_->$idx_key(); } @_;
146 !defined($value_key) ? $_
147 : ref $_ eq 'HASH' ? $_->{ $value_key }
151 return zip(@indexes, @values);
157 $str = Encode::encode('utf-8-strict', $str);
158 $str =~ s/([^a-zA-Z0-9_.:-])/sprintf("%%%02x", ord($1))/ge;
164 my $str = $_[0] // '';
169 $str =~ s/%([0-9a-fA-Z]{2})/pack("C",hex($1))/eg;
170 $str = Encode::decode('utf-8-strict', $str);
181 SL::MoreCommon.pm - helper functions
185 this is a collection of helper functions used in kivitendo.
186 Most of them are either obvious or too obscure to care about unless you really have to.
187 The exceptions are documented here.
197 A lot of the old sql-ledger routines are strictly procedural. They search for params in the $form object, do stuff with it, and return a status code.
199 Once in a while you'll want something from such a function without altering $form. Yeah, you could rewrite the routine from scratch... not. Just save you form, execute the routine, grab your results, and restore the previous form while you curse at the original design.
201 =item cross BLOCK ARRAY ARRAY
203 Evaluates BLOCK for each combination of elements in ARRAY1 and ARRAY2
204 and returns a new list consisting of BLOCK's return values.
205 The two elements are set to $a and $b.
206 Note that those two are aliases to the original value so changing them
207 will modify the input arrays.
209 # append each to each
212 @x = cross { "$a$b" } @a, @b;
213 # returns a1, a2, a3, b1, b2, b3, c1, c2, c3
215 As cross expects an array but returns a list it is not directly chainable
216 at the moment. This will be corrected in the future.
218 =item ary_to_hash INDEX_KEY, VALUE_KEY, ARRAY
220 Returns a hash with the content of ARRAY based on the values of
221 INDEX_KEY and VALUE_KEY.
223 If INDEX_KEY is undefined then the elements of ARRAY are the keys and
224 '1' is the value for each of them.
226 If INDEX_KEY is defined then each element of ARRAY is checked whether
227 or not it is a hash. If it is then its element at the position
228 INDEX_KEY will be the resulting hash element's key. Otherwise the
229 element is assumed to be a blessed reference, and its INDEX_KEY
230 function will be called.
232 The values of the resulting hash follow a similar pattern. If
233 VALUE_KEY is undefined then the current element itself is the new hash
234 element's value. If the current element is a hash then its element at
235 the position VALUE_KEY will be the resulting hash element's
236 key. Otherwise the element is assumed to be a blessed reference, and
237 its VALUE_KEY function will be called.