Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Dancer bugs? send_file(), content type, and response_content_is()

by hardburn (Abbot)
on Mar 01, 2013 at 16:25 UTC ( #1021293=perlquestion: print w/ replies, xml ) Need Help??
hardburn has asked for the wisdom of the Perl Monks concerning the following question:

I've been playing with Dancer for the first time, and I think I found some bugs. I'm posting here to see if this is a ligit bug or if I'm doing something wrong.

I added a file under the public dir of /javascripts/test.js with the text var test = 1;. The Dancer route looks like this:

get '/javascripts/:file' => sub { my ($file) = splat; send_file( '/javascripts/' . params->{file}, streaming => 1, content_type => 'text/javascript', ); };

(There may be better ways to do this, but I'm doing it this way for learning purposes.)

I also wrote tests to handle that:

response_status_is [ GET => '/javascripts/test.js' ], 200, 'response status is 200 for /js/test.js'; response_content_is [ GET => '/javascripts/test.js' ], 'var test = 1;' +, 'Fetched JS'; response_headers_include [ GET => '/javascripts/test.js' ], [ 'Content-Type' => 'text/javascript' ], "MIME type set for JS";

Those fail with:

not ok 4 - Fetched JS # Failed test 'Fetched JS' # at t/002_index_route.t line 14. # got: 'GLOB(0x31fa178)' # expected: 'var test = 1;' not ok 5 - MIME type set for JS # Failed test 'MIME type set for JS' # at t/002_index_route.t line 15. # Looks like you failed 2 tests of 5.

In the debugger, I stepped into reponse_headers_include() and found the response looks like this:

0 Dancer::Response=HASH(0x23dca98) 'content' => GLOB(0x23eb3e8) -> *Dancer::FileUtils::$fh FileHandle({*Dancer::FileUtils::$fh}) => fileno(7) 'encoded' => 0 'forward' => '' 'halted' => 0 'headers' => HTTP::Headers=HASH(0x23e0060) 'content-type' => MIME::Type=HASH(0x1706478) 'MT_encoding' => '8bit' 'MT_extensions' => ARRAY(0x1706580) 0 'js' 'MT_simplified' => 'application/javascript' 'MT_type' => 'application/javascript' 'last-modified' => 'Fri, 01 Mar 2013 15:31:21 GMT' 'pass' => 0 'status' => 200

Notice above that the MIME type override (content_type => 'text/javascript') was not respected as documented in Dancer::send_file(). Also, the response_content_is() is failing because send_file() is returning a filehandle, which I feel should have been automatically expanded rather than failing this way. Removing streaming => 1 does not fix the test. It does return the right data when I check it in a browser.

Also, I originally had a response_exists() test, but the test failed with a deprecation warning. There's no mention of the deprecation in the Dancer::Test docs.

If these are indeed a ligit bug, I already see how to fix the response_content_is problem and can probably figure out the content type override. However, I've been giving Dancer a try for an hour and I'm already running into bugs and documentation errors. Frankly, that's not a good sign of a mature framework.


"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Comment on Dancer bugs? send_file(), content type, and response_content_is()
Select or Download Code
Re: Dancer bugs? send_file(), content type, and response_content_is()
by pokki (Scribe) on Mar 01, 2013 at 19:36 UTC

    Regarding "the best way" of serving Javascript files, you should just put them in the /public directory and remove your /javascripts route. Any request that does not match a route, e.g. GET /foo/bar.js will look into /public for a file called /public/foo/bar.js and serve that directly; if it doesn't exist you get a 404 as expected. In your case you're not using system_path in your call to send_file, so the file will only be served from the public directory anyway...

    I've looked at the source for send_file and it does seem to propagate the MIME type from the function call to the rendering of the response. However, I've been bitten by Dancer::Test before. Since it's returning a filehandle instead of a rendered response I'd say it's not calling get_file_response_for_path from Dancer::Renderer, which is responsible for adding the Content-Type header. In short, don't expect Dancer::Test to mimic a real web app from end to end. It's there to allow you to test plugins and such. Regarding response_exists, that does seem like a documentation bug.

    Finally, rest assured that Dancer *is* a mature framework. We use it at $work for many internal applications and we have never had serious problems.

    The Dancer folk at #dancer on irc.perl.org are very responsive and friendly, be sure to check there if you have other questions.

      The files I had were under /public/javascripts. They just don't show up that way in the routes. As far as getting things over a browser was concerned, the file was transferred as expected. Dancer's test framework was the problem.

      I'm doing this as part of a comparison with Mojolicious for a possible future app at $work. Given that we will want tight unit testing, and that Mojo's test framework seems much more sophisticated, I'm leaning that way.


      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        The files I had were under /public/javascripts. They just don't show up that way in the routes. As far as getting things over a browser was concerned, the file was transferred as expected. Dancer's test framework was the problem.

        There is no explicit route associated with stuff in /public. Rather, when Dancer finds no route matching a given request, it tries to find a file in /public. If there is still no match there, then 404. This still serves files in /public via send_file though, unlike regular template rendering, so you'd still get a filehandle and Dancer::Test would still be useless. (Fortunately there is little reason to test that Dancer's static file management works. That's already been tested, in Dancer's own test suite.)

        I'm doing this as part of a comparison with Mojolicious for a possible future app at $work. Given that we will want tight unit testing, and that Mojo's test framework seems much more sophisticated, I'm leaning that way.

        I can't offer any honest opinion on Dancer vs. Mojo, because I haven't tried Mojo at all. (I dislike their philosophy of never reusing the CPAN, reinventing everything by themselves, and keeping it all inside a monolithic distribution so that no one can reuse it either. I have no opinion on the technical merits of Mojo.)

        Regarding unit testing of web apps, I'm not a fan of unit-testing them from request to rendered response because any change in the template for instance is likely to break your tests, and large pages with a lot of information are a pain to test against. I'd rather test separately that the values I'm passing to the template are what I expect, and then that the template renders correctly when passed certain values. This can be done with regular unit testing, like any module. Automated end-to-end testing of web apps, outside of Selenium and similar technologies, doesn't really work in my relatively limited experience.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1021293]
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (9)
As of 2014-07-29 07:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (211 votes), past polls