Hi Stefan,
Thanks for taking the time to respond to my analysis!
On Sun, Aug 4, 2019 at 1:14 PM Stefan Hornburg (Racke) <racke(a)linuxia.de>
wrote:
Last year, I
started experimenting with Dancer2 (one of the options on
the table for our target
framework when we
rewrite much of our code).
Today, I've been updating that code to the HEAD of the current master
branch.
During the work I've done so far (last
year and today), I've come across several
obstacles which now lead me to
bring up the following for discussion.
[ snip ]
1. Session management (cookies)
As part of the move from our current design to the Dancer2
framework, I'm
aiming for setup.pl <http://setup.pl> and
login.pl <http://login.pl> to become
completely separate applications,
meaning that they require separate
authentication
cookies (to allow separate, concurrent logins
into both apps from a
single browser)
While Dancer2 does support multiple apps
running on a single
instance of its framework (such as required to run
login.pl <http://login.pl> and setup.pl
<http://setup.pl> from the same
backend), it doesn't support - in its
configuration file (config.yml) - that these apps
use disjoint session
management cookies. In order to support this
use-case, I've had to add request hooks,
changing the name of the
session cookie on a per-request basis.
Each of these apps should be able to have their own configuration
directory so you can change the 'session_name'.
Alternatively you could change it directly in the script (setup.pl /
login.pl).
Ok. From what you say, I understand that we can have the same source
hierarchy as today. To that hierarchy, we would add 2 config directories
(now I added only 1). In those 2 config directories, we store the config
for running the "setup.pl" app and separately the "login.pl" app
(after
they have been rewritten to Dancer, of course). Then, with those two config
directories, we can start these 2 dancer2 apps on a single Perl instance.
However, back when I came to my original conclusion, I read all of the
Dancer2 docs and nothing suggests that this can be done. Just now, I've
re-read
https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Config.pod
and the only way to run 2 Dancer2 apps is by starting 2 dancer2 (=perl)
processes. Am I wrong?
2.Pluggable authentication
As LedgerSMB uses a rather unusual authentication scheme where the
user+password+database name are required for
login, our authentication scheme doesn't fit
the standard set by the
framework (Dancer2::Plugin::Auth::Extensible::*),
I've ended up hacking "deep" into
the internals of auth::extensible: our
authentication couldn't be achieved
without
replacing the login templates and finding ways to
make more parameters
available than supported out of the box.
Where do you handle the authentication within LedgerSMB - I would like to
take a look at it.
The code is a bit convoluted, but was recently rewritten as a Plack
middleware. Which is probably the best indication to what we want to
achieve that we have available. Older versions had that scattered around
the code base which was hard to follow/understand:
https://github.com/ledgersmb/LedgerSMB/blob/master/lib/LedgerSMB/Middleware…
3. Templating library abstraction
While abstraction from templating libraries sounds nice, in
practice it's
cumbersome to switch templating
libraries: on the Perl side the abstraction works
fine to swap from one
to another, but templating libraries usually use
different templating "languages" (i.e.
TemplateToolkit uses different
directives and paradigms than XSLate). So, it
seems that managing instantiation of the
templating library is more
important than abstracting it.
I would like to have a generic template abstraction and instantation
outside of Dancer2, e.g. for sending automated
emails.
That would indeed be absolutely great. However, for the scope of our own
project, the templates are likely to be geared to our application anyway:
most of the UI needs to be redeveloped when switching to another template
processor. However, it would be nice if we could offer our users various
different template processors for the templates they create.
4. Route
definition & dispatch
With the ability to define routes, we'll be able to detach URL
structure
from our internal code structure. Dancer2
has a nice DSL to support the definition of
routes for non-webservice
entry points. There are plugins available to
extend the DSL with convenience keywords for
definition of webservice
entry points.
However, the syntactic sugar provided by the
endorsed plugin
(Dancer2::Plugin::REST) provides 1 new DSL keyword
which is nothing more than a way to attach 4
functions to a resource
(create, get, update, delete) providing a generated
route for each; I see very little benefit to this
plugin. In addition,
this plugin doesn't support HTTP's content
negotiation; instead, it expects the requested
resource to have an
"extension" to indicate the desired response
encoding
(e.g.: /users/1.json returns JSON, /users/1.xml
returns XML, etc). This
isn't "http-native" enough for me and
thereby
unacceptable for our use. Then there's the
(not endorsed)
Dancer2::Plugin::WebService; however, this too encodes the
desired response encoding in the URL...
I wouldn't say that that plugin is endorsed, it is rather a way to get a
REST app working quickly. You could write a
custom plugin based on a description file which creates the routes on
startup (see
https://metacpan.org/release/Dancer2-Plugin-Interchange6/source/lib/Dancer2…
).
Ok. I think the route creation itself is the least of our problems, as
route creation is likely to follow pretty standard patterns for which it's
not really hard to develop our own dsl keyword.
What's more interesting is to make sure the content is being "negotiated":
when there's an Accept header available, that header should be used to
determine the resource's representation as returned by the server. Ideally
the framework deals with that requirement by taking a (non-stringy)
response and serializing it into the desired format.
It should be also possible to set a variable for the
response encoding and
use it at the end of the processing.
I'm imagining a "book/1" resource defined as the Perl object
{ title => "Perl Best Practices", author => "Damian Conway" }
to be returned as JSON when the Accept header prefers that (and the server
has a serializer set up for it). Yet the same object gets returrned as an
HTML document (a processed template) when the client prefers HTML or
doesn't even mention JSON in the Accept header.
Also, when content is being encoded according to the requirements as
specified in the Accept header, I'd expect the framework to take care of
setting headers like Vary header to make sure caching still works.
Obviously, we can write that and add it to the Dancer ecosystem by
publishing our modules on CPAN. However, I had hoped that this would be
taken care of by Dancer(2) modules because others already ran into problems
like these... My point being that I had hoped that by turning to a
framework, we'd be building on the shoulders of others who ran into the
same issues before we did -- I hoped to part with code. Did I miss
something and is this really already part of the Dancer2 ecosystem? Would
you say that my idea of the REST api implementation fits with the ideas of
the Dancer(2) team with respect to "being the Dancer way" to achieve
things? (Is it likely that our code becomes *part* of the ecosystem, is my
question, I guess? Or will it be this code base which has its own approach
to using Dancer(2)?)
Thanks again for taking the time to answer these questions!
Regards,
Erik.
Regards
Racke
5. Framework configuration
Dancer2 supports running multiple "apps" on a single instance. We
want to run multiple apps: our setup.pl
<http://setup.pl> and login.pl
<http://login.pl>. Even: we want these
multiple apps to run on different
authentication
domains (i.e. when a user is logged in in
/login.pl <http://login.pl>,
we don't automatically want the user to be
logged
in into /setup.pl <http://setup.pl> too).
During my experiments I found
that this isn't something that Dancer2 supports
in its configuration: it can be
"tricked" to support it by installing
the correct pre- and post-request
hooks in the
framework, but out-of-the-box, it's not
there. I'm not sure what other
areas this will affect in the future, but from
the structure of the configuration, I'm
imagining it'll be an issue in
many places.
So what is our way forward then? Well, while we were working to merge
the Multi
Currency branch and stabilizing the 1.7
release (this process is still on-going),
I've been looking around for
solutions to these issues and I think the Perl
library ecosystem has very nice options for
almost all of the problems
mentioned above, with the remark that there will
obviously not be such a nice DSL as there will be
with Dancer2. The
following alternatives are available:
1. For session management using encrypted cookies, there's
Session::Storage::Secure which works and is agnostic of
underlying technology
2. For pluggable authentication, as we need to write our own
authenticator anyway
(and we actually already *have* that
as a Plack middleware), I there's no effort
if we can reuse that
3. For templating engine abstraction, well, I guess that with my line of
reasoning
above, there's little reason to have
abstraction for templating engines at all (I
mean, there *is* for a
framework which needs to support many code bases,
but there isn't for our specific use-case:
our code base)
4. Route definition and dispatch (including extraction of route
parameters) is
available in a technology agnostic
library Router::XS; while this library
doesn't have the solid DSL that
Dancer2 has, I'm expecting us to move more
and
more to Webservice entrypoints (either JSONRPC,
GraphQL or REST) meaning
that the DSL Dancer2 provides will be far less
useful as stated above
5. Framework configuration could be handled through one of the regular
YAML
libraries, but even better - especially with
inversion of control / dependency injection - we
could turn to
Beam::Wire and be able to deal with the most complex
configurations by deferring configuration
handling to sysadmins
Obviously, Dancer2 will be able to do request parameter handling. That's
something that's supported today too: we
already have Plack::Request to handle parameter
parsing (both URL and
body parameters).
So, after this long story, I'm wondering if moving to Dancer2 is worth
it, or
that we can simply stay on Plack with its
middlewares and put the aforementioned modules to
good use. Lets have
your opinions and a good discussion on the matter
so we lay a strong foundation for next steps for
our project.
Regards,
--
Bye,
Erik.
http://efficito.com <http://efficito.com/> -- Hosted accounting and ERP.
Robust and Flexible. No vendor lock-in.
_______________________________________________
devel mailing list
devel(a)lists.ledgersmb.org
https://lists.ledgersmb.org/mailman/listinfo/devel
--
Ecommerce and Linux consulting + Perl and web application programming.
Debian and Sympa administration. Provisioning with Ansible.
_______________________________________________
devel mailing list
devel(a)lists.ledgersmb.org
https://lists.ledgersmb.org/mailman/listinfo/devel
--
Bye,
Erik.
http://efficito.com -- Hosted accounting and ERP.
Robust and Flexible. No vendor lock-in.