in reply to Re^5: Trying to understand Catalyst::Model::Adaptor
in thread Trying to understand Catalyst::Model::Adaptor

Thanks. I agree to everything you say but I'm still not 100% sure about the benefits of Catalyst::Model::Adaptor. Let's assume a very simplistic scenario where I have the following model class that I want to use in my web app:

package NonCatalyst::FileCat; sub new{ my ($class, $directory) = @_; bless { _directory = $directory }, $class; } sub cat{ my ($self, $file) = @_; my $cmd = 'cat '.$self->{_directory}.'/'.$file; return `$cmd`; } 1;
So I instantiate with a directory and now I can get the contents of text files with the cat method on a file. Not very useful and realistic but it's just an example. Now I want my web app to display the contents of a file the user selects in a form. My instinct would be to do it like this in my controller:
use NonCatalyst::FileCat; sub show :Local { my ( $self, $c ) = @_; my $fc = NonCatalyst::FileCat->new( $c->config->{file_directory}); my $file_name = $c->request->params->{file}; $c->stash->{result} = $fc->cat( $file_name ) ; }

So I just use the external model class directly. If I need to make a change to FileCat I still only have one file to edit, so no difference to the Model::Adaptor approach there. All the configuration I need for FileCat is passd explicitly when creating the object, so I can use the same module from any script or UI implementation as long as it can pass a directory path. To me, that still seems a lot more natural and I would avoid the drawback you mention, i.e. tying my class too closely to the Catalyst configuration. Maybe this example is too simple but would you agree that this would be a case against using Catalyst::Model::Adaptor?

Replies are listed 'Best First'.
Re^7: Trying to understand Catalyst::Model::Adaptor
by Your Mother (Archbishop) on Apr 07, 2010 at 22:37 UTC

    Me personally? Nope :) Generally the only case I'd agree it would be best to skip the model is the case where the code will only ever be used once in a single Controller. Forever and ever. I could see that sort of functionality being in several Controllers and methods (though if you do write something like that use Path::Class::File->new(path)->slurp or something similar instead of invoking shell commands). These lines-

    use NonCatalyst::FileCat; my $fc = NonCatalyst::FileCat->new($c->config->{file_directory}); my $file_name = $c->request->params->{file}; $c->stash->{result} = $fc->cat($file_name);

    -become-

    $c->stash->{result} = $c->model("FileCat")->cat($c->request->params- +>{file});

    Granted you have cost yourself up front with setting up the adaptor and configuration for the model. But it really is trivial effort and it allows you to change the file_directory in config related to the functionality -- Model::FileCat -> file_directory: /some/place -- instead of in the "global" app config. Theoretical win but better namespacing nevertheless.

    I'm not trying to preach; thinking in MVC instead of scriptese is a really good way to save yourself misery over time. I wish someone had hit me with this particular cluebat much earlier. Your own use case and preference matters, of course. Do what thou wilt.

      OKOK, forget the actual file reading - it was just an example of a simple model that needs a little bit of configuration (the directory) and a parameter to spit out some result.

      I still don't quite get what's wrong with using this in several controllers. the model is completely separated from the controller and if I need to make a change to the way I read files (i.e. use something more sensible than backticks in this case...), I still only change the model, i.e. my NonCatalyst::FileCat module in a single place. How would the adaptor approach help me with that?

      It seems to me that the only benefit of using an Adaptor is that the model has access to the config. I can see the point for that if the model needs a lot of configuration and is tightly tied in with the Catalyst App but if it simply requires a directory to work with or some other trivial data, then it seems that I get much better separation of web app implementation and back-end model by using the NonCatalyst class directly. The way I suggested, I can just as easily use the model class from a command line script or a desktop GUI app or something.

      Apologies for being so persistent :-) and thanks a lot for your patience. It's just that this seems so counterintuitive to me and I can imagine that others have the same questions. I like Catalyst so far but there is not as much information out there as for some other web frameworks.

        I can just as easily use the model class from a command line script or a desktop GUI app or something.

        Quick definition clarification: if it's not in Cat, it's not a Model. It's unrelated code like any other piece of Perl. Model means it's bound to the app regardless of what is underneath. You can bind any code that provides data or services as a Model. The Adaptor stuff is precisely to do that: glue so that you don't have to duplicate external code or write code from scratch to bind it into your app.

        I'll start to sound like a broken record soon. This is the summation of the issues we've discussed + a new one about testing I should have mentioned already-

        • Excepting setup you will end up with 2-5 times as much code and much of it boilerplate, violating DRY and increasing the likelihood of bugs sneaking in.
        • You might change FileCat to the new and improved FileCat2 at some point. With the Model you now have one line of code to edit instead of possibly dozens or even hundreds.
        • This reeks of code smell-
          # APPLICATION ------------------------------------------------- name: "OH HAI" default_view: "TT" default_model: "DBIC" file_directory: "some:/place/else"
        • This doesn't-
          # MODELS ------------------------------------------------------- Model::FileCat: args: file_directory: "some:/place/else"
        • You will never conceptually separate your code from your data and your presentation. You will think exclusively in "scriptese." You will continue to give traditional, inline code preference over strict adherence to the MVC. Dogs and cats, living together.
        • You cannot unit test (as flexibly) without the Model; with a Model, you can reset the Model to whatever you want via config: mock, test fixtures, etc; with hardcoded modules, you may have to resort to contortions to test things. This means you are drastically less likely to test. This means FAIL. :(

        This is Perl. Do it the way you want to do it. Just like life, though, a little discipline up front, even when it seems unecessary, can repay greatly down the road in unforseen ways. I'm not the MVC cop so this is really just advice but I've been doing this almost 12 years now and Catalyst for 5 and the advice is good. :)

        Google for "catalyst model" and read some of the blog posts, tutorials, and list threads. You'll get, one hopes, a deeper feel for things than from the plain Pod. The new Catalyst book ISBN 9781430223658 is also quite worth getting.