Merge branch 'b-3.6.1' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / SL / DB / Helper / Presenter.pm
diff --git a/SL/DB/Helper/Presenter.pm b/SL/DB/Helper/Presenter.pm
new file mode 100644 (file)
index 0000000..1dae8cc
--- /dev/null
@@ -0,0 +1,95 @@
+package SL::DB::Helper::Presenter;
+
+use strict;
+
+sub new {
+  # lightweight: 0: class, 1: object
+  bless [ $_[1], $_[2] ], $_[0];
+}
+
+sub AUTOLOAD {
+  our $AUTOLOAD;
+
+  my ($self, @args) = @_;
+
+  my $method = $AUTOLOAD;
+  $method    =~ s/.*:://;
+
+  return if $method eq 'DESTROY';
+
+  eval "require $self->[0]";
+
+  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+
+  if (my $sub = $self->[0]->can($method)) {
+    return $sub->($self->[1], @args);
+  }
+}
+
+sub can {
+  my ($self, $method) = @_;
+  eval "require $self->[0]";
+  $self->[0]->can($method);
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::DB::Helper::Presenter - proxy class to allow models to access presenters
+
+=head1 SYNOPSIS
+
+  # assuming SL::Presenter::Part exists
+  # and contains a sub link_to($class, $object) {}
+  SL::DB::Part->new(%args)->presenter->link_to
+
+=head1 DESCRIPTION
+
+When coding controllers one often encounters objects that are not crucial to
+the current task, but must be presented in some form to the user. Instead of
+recreating that all the time the C<SL::Presenter> namepace was introduced to
+hold such code.
+
+Unfortunately the Presenter code is designed to be stateless and thus acts _on_
+objects, but can't be instanced or wrapped. The early band-aid to that was to
+export all sub-presenter calls into the main presenter namespace. Fixing it
+would have meant accessing presenter functions like this:
+
+  SL::Presenter::Object->method($object, %additional_args)
+
+which is extremely inconvenient.
+
+This glue code allows C<SL::DB::Object> instances to access routines in their
+presenter without additional boilerplate. C<SL::DB::Object> contains a
+C<presenter> call for all objects, which will return an instance of this proxy
+class. All calls on this will then be forwarded to the appropriate presenter.
+
+=head1 INTERNAL STRUCTURE
+
+The created proxy objects are lightweight blessed arrayrefs instead of the
+usual blessed hashrefs. They only store two elements:
+
+=over 4
+
+=item * The presenter class
+
+=item * The invocing object
+
+=back
+
+Further delegation is done with C<AUTOLOAD>.
+
+=head1 BUGS
+
+None yet :)
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut