"use Dancer2" interferes with package exports
Consider this trivial module, Foo.pm: package Foo; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(foo); use Dancer2; sub foo { map { "You gave me '$_', right?" } @_; } 1; ...and this trivial test program: #!/usr/bin/env perl use Foo; print join "\n", foo("foo", "bar"); If you run it, it will complain that &main::foo is undefined. Yet, it is unconditionally exported from package Foo by Exporter, since we've used @EXPORT. (The symptom doesn't change if you use @EXPORT_OK and import &foo explicitly, though.) The fix is easy: comment out the "use Dancer2" line. Why does that help? Why is Dancer2 roaching my module's exports list? Does it have something to do with the way Dancer exports DSL keywords into the global namespace? This is not a real solution, anyway. There is a real "Foo" module in my program that does useful work, and when I pound out the "use Dancer2" line in that module, all of my static content stops being served. Dancer tries to treat everything like a route. It's like the appdir setting isn't getting defined correctly. I tried to test that guess, putting this in one of the route handlers: debug "APPDIR: ", Dancer2::Config::setting('appdir'), "\n"; That just gets me an undefined subroutine error in the browser. I checked Dancer/Config.pm to make sure that routine really exists, as the docs claim, and it does seem to. Is this what you guys mean by "alpha"? :)
On 1/24/2014 21:36, Warren Young wrote:
debug "APPDIR: ", Dancer2::Config::setting('appdir'), "\n";
I just figured out what I did wrong here. First, I took the Cookbook at its word. Then when I went looking for the code reference, I accidentally loaded up Dancer/Config.pm. The Dancer2 Cookbook describes the Dancer 1 mechanism, not whatever has replaced it in Dancer 2. So, what is the correct way to retrieve config variables at run time within a route handler? The Dancer2::Config docs don't say. They only talk about *changing* settings.
On 01/25/2014 05:56 AM, Warren Young wrote:
On 1/24/2014 21:36, Warren Young wrote:
debug "APPDIR: ", Dancer2::Config::setting('appdir'), "\n";
I just figured out what I did wrong here. First, I took the Cookbook at its word. Then when I went looking for the code reference, I accidentally loaded up Dancer/Config.pm. The Dancer2 Cookbook describes the Dancer 1 mechanism, not whatever has replaced it in Dancer 2.
So, what is the correct way to retrieve config variables at run time within a route handler? The Dancer2::Config docs don't say. They only talk about *changing* settings. _____________________________________________
Dancer(2)::Config isn't the place to look for Dancer's keywords (DSL). For Dancer2, please look at Dancer2::Manual::DSL. To retrieve config variables you use: config->{appdir} Regards Racke -- Perl and Dancer Development Visit our Open Source conference on E-commerce: http://www.ecommerce-innovation.com/
On 1/25/2014 01:40, Stefan Hornburg (Racke) wrote:
Dancer(2)::Config isn't the place to look for Dancer's keywords (DSL).
I think you're assuming that I went into this knowing that I was looking for a keyword, and that the docs are consistent. If you go to the Dancer2 cookbook, about halfway down the page, you find these two lines in the same example: Dancer2::Config::setting('appdir',$appdir); print "environment:".config->{environment}."\n"; #development The first should be config->{appdir} = $appdir ...I guess?
config->{appdir}
Thanks. I've printed this in my debug log, along with config->{public}, and neither change when I comment out the "use Dancer2" in the other module. So, I'm left without any explanation at all. I then tried "debug Dumper(config())", but got tens of thousands of characters. So I then dumped the config() hashref to a file, and diffed the files in the two cases, and got hundreds of lines of changes. All that from changing one line in a file that shouldn't even be *related* to the one where the problem occurs. Can anyone explain how the Foo module exports get changed in my trivial example?
On 26/01/2014 1:24 pm, Warren Young wrote:
Can anyone explain how the Foo module exports get changed in my trivial example?
When you 'use Dancer2', its import adds an import method into your apps namespace, overriding the one Exporter provides. There are some further notes in http://github.com/PerlDancer/Dancer2/issues/487 regarding this, otherwise it looks like its currently undocumented. Cheers, Russell.
On 1/26/2014 05:01, Russell Jenkins wrote:
On 26/01/2014 1:24 pm, Warren Young wrote:
Can anyone explain how the Foo module exports get changed in my trivial example?
When you 'use Dancer2', its import adds an import method into your apps namespace, overriding the one Exporter provides.
Thanks for tracking this down. I guess this has something to do with the order Perl evaluates BEGIN blocks, since swapping the "use Dancer2" and "require Exporter" lines doesn't help. For now, I guess the solution is to very careful to separate the Dancer-using parts of my code from the parts that get *called by* the Dancer-using parts. You should be able to fix this over-reach on Dancer's part by using Perl's reflection features, possibly coupled with eval(). Dancer2 should be able to detect that an import() routine already exists and keep a reference to it, so it can call the overridden version if the caller passes the name of a symbol that Dancer2 doesn't export.
On 01/27/2014 01:51 AM, Warren Young wrote:
On 1/26/2014 05:01, Russell Jenkins wrote:
On 26/01/2014 1:24 pm, Warren Young wrote:
Can anyone explain how the Foo module exports get changed in my trivial example?
When you 'use Dancer2', its import adds an import method into your apps namespace, overriding the one Exporter provides.
Thanks for tracking this down.
I guess this has something to do with the order Perl evaluates BEGIN blocks, since swapping the "use Dancer2" and "require Exporter" lines doesn't help.
For now, I guess the solution is to very careful to separate the Dancer-using parts of my code from the parts that get *called by* the Dancer-using parts.
You should be able to fix this over-reach on Dancer's part by using Perl's reflection features, possibly coupled with eval(). Dancer2 should be able to detect that an import() routine already exists and keep a reference to it, so it can call the overridden version if the caller passes the name of a symbol that Dancer2 doesn't export.
Why don't you roll your util functions into your customer Dancer2 plugin? This would give you access to Dancer's DSL and it will export the functions for you. Regards Racke -- Perl and Dancer Development Visit our Open Source conference on E-commerce: http://www.ecommerce-innovation.com/
On 1/27/2014 01:05, Stefan Hornburg (Racke) wrote:
On 01/27/2014 01:51 AM, Warren Young wrote:
For now, I guess the solution is to very careful to separate the Dancer-using parts of my code from the parts that get *called by* the Dancer-using parts.
Why don't you roll your util functions into your customer Dancer2 plugin?
Because this is an existing body of code, several thousand lines, which doesn't meaningfully extend Dancer. The only reason I tried to "use Dancer2" in this code is so I could share Dancer's existing logging system. The cost of that sharing is too high. I'll just create another log file.
On 1/24/2014 21:36, Warren Young wrote:
This is not a real solution, anyway. There is a real "Foo" module in my program that does useful work, and when I pound out the "use Dancer2" line in that module, all of my static content stops being served.
I found out why this was happening, too. The Dancer2 cookbook is the culprit again. Under "Sessions and logging in," it recommends something like this: hook before => sub { if (!session('user') && request->dispatch_path !~ m{^/login}) { forward '/login', { requested_path => request->dispatch_path }; } }; The only way that can work is if the /login route handler doesn't depend on any static content, because it turns all URL requests into "/login" URLs. It should be modified like this: hook before => sub { if (!session('user') && request->dispatch_path !~ m{^/login} && request->dispatch_path !~ m{^/public/}) { forward '/login', { requested_path => request->dispatch_path }; } }; That is, don't mess with the "/public/" subtree.
participants (3)
-
Russell Jenkins -
Stefan Hornburg (Racke) -
Warren Young