[Catalyst] Out of Memory - File delivery issue

Lukas Thiemeier spamcatcher at thiemeier.net
Thu May 2 14:04:01 GMT 2013


Hi again,

FIRST:

Your Controller Code looks good. I would use build-in  functionality
whenever possible. In this case, using $c->res->content_type instead of
setting the content type by hand. But your code should work just fine.

Your are right. You  have to set the headers before using $c->res->print
(or write). But this does not mean that you have to do this in the
model.  You can set the headers in your Controller before running the
model code.

SECOND:

IMO you are right about your concerns regarding the MVC architecture.
The Model should provide the data and not deal with HTTP responses. On
the other hand, the data has to be provided in some format. XML is a
well known standard format for textual data presentation. Providing the
data as XML is as good or as bad as providing the data as DBIC objects.
(Well, not really. But close enough for this explanation).

The cleanest (most MVC conform) way to do this would be to fetch the raw
data from your model and create your XML in a special view. There are
several ways to do this. You can create XML using a TT view. ( I have
done this before, it works fine). Or you can use existing XML views like
Catalyst::View::XML::Generator or Catalyst::View::XML::Hash::LX. (I have
not used any of them, but the names are promising). I guess there are
even more ways to do it...

In your Case, you already have a Model which provides the XML Data
(which is fine, as I said before). IMO, one of the great things about
Catalyst is that it allows you to get the job done quickly. It makes
reusing existing code easy. There is no reason to abandon your existing
XML-creation code just because it doesn't fit the MVC layout. Doing so
would be contra-productive.

So, what can be done to re-use your XML Model and still fit into the MVC
architecture? I see two ways:

The first one would be to update your model that it writes its data to
any IO::Handle compatible object. You can pass $c->res to your Model,
which is IO::Handle compatible. Your model uses a Catalyst independent
API to write out the data. Catalyst streams the data to the client. Your
Model Code is still Catalyst independent and does not know that it is
writing to a Catalyst::Response object. No tight coupling. You can reuse
your model in non-catalyst applications and easily test its
functionality using Test::More or any other test suite. ( I think this
is more or less the way proposed by Neil)

The second way (which I would prefer, since it is even more MVC conform)
is the following: Update your Model to return an IO::Handle-style object
instead of a string. You can fetch this object from the model in your
controller, and pass it to $c->res->body. Catalyst will take care of
streaming the data to the client in small chunks. You don't have to pass
any Catalyst related objects to the model anymore. Your model returns
the data as a well known standard object which happens to be suitable
for streaming large amounts data with catalyst. No tight coupling at
all. Problem solved. Have a Tea and celebrate.

This is my opinion on this topic. I hope it helps you to find a way
which fits your needs, and reduces your confusion about Models, Views
and controllers in this specific case.

Lukas



More information about the Catalyst mailing list