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@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/AuthenticateSession.pm#L135-L150
 

>  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/Plugin/Interchange6/Routes.pm#L325).

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@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@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.