FAQ
Hi


This is more of a general MVC question, but hopefully it's okay to ask in
here.


I've set up a model to generate data in iCal format (see my previous thread
and thanks for the responses on that), but it has thrown up an interesting
general logic separation question that I don't know the answer to.


By way of an example, I'm generating iCal events for a data table called
team_matches (DBIx::Class model name TeamMatch). I was hoping, as would
seem to be the cleanest way to do it, to have a result class "helper"
method, such that on each match object I could call something like:
$match->generate_ical_data, which would generate a hashref of calendar
values to be passed through to my MyApp::Model::ICal module that will
generate the actual data. The problem with this is that certain properties
require access to the Catalyst application: for example, there's a uri
property, the value for which will need to be generated by
$c->uri_for_action and since this particular application has the ability to
be multi-language (via CatalstX::I18N) the description value needs to be
generated by $c->maketext.


All of this brings up a quandary: there are only two ways around this that
I can see:
   * Pass $c into the $match->generate_ical_data method (which I know is
STRONGLY discouraged and very very bad).
   * Generate the hashref in the controller (which potentially makes for a
'fatter' controller than you ideally want and I'm trying to stick to the
ideals here).


I'm guessing the preference of the above options should really be the
latter (which is what I'm currently doing in the interim), but I am hoping
that there's a super-clever way that I've not thought of that someone can
tell me I've completely overlooked?


Thank you in advance.




Chris
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.scsys.co.uk/pipermail/catalyst/attachments/20160309/c760950f/attachment.htm>

Search Discussions

  • Darren Duncan at Mar 9, 2016 at 10:11 pm
    The best option is to keep the concerns separated. Models should always take
    their inputs via explicit arguments for a Model-specific API, and they should
    not have any knowledge of or direct access to a Controller. If this means you
    have to bundle up a larger amount of data in the Controller so the data passage
    is clean, so be it, that is the lesser evil. -- Darren Duncan

    On 2016-03-09 11:03 AM, Chris Welch wrote:
    Hi

    This is more of a general MVC question, but hopefully it's okay to ask in here.

    I've set up a model to generate data in iCal format (see my previous thread and
    thanks for the responses on that), but it has thrown up an interesting general
    logic separation question that I don't know the answer to.

    By way of an example, I'm generating iCal events for a data table called
    team_matches (DBIx::Class model name TeamMatch). I was hoping, as would seem to
    be the cleanest way to do it, to have a result class "helper" method, such that
    on each match object I could call something like: $match->generate_ical_data,
    which would generate a hashref of calendar values to be passed through to my
    MyApp::Model::ICal module that will generate the actual data. The problem with
    this is that certain properties require access to the Catalyst application: for
    example, there's a uri property, the value for which will need to be generated
    by $c->uri_for_action and since this particular application has the ability to
    be multi-language (via CatalstX::I18N) the description value needs to be
    generated by $c->maketext.

    All of this brings up a quandary: there are only two ways around this that I can
    see:
    * Pass $c into the $match->generate_ical_data method (which I know is
    STRONGLY discouraged and very very bad).
    * Generate the hashref in the controller (which potentially makes for a
    'fatter' controller than you ideally want and I'm trying to stick to the ideals
    here).

    I'm guessing the preference of the above options should really be the latter
    (which is what I'm currently doing in the interim), but I am hoping that there's
    a super-clever way that I've not thought of that someone can tell me I've
    completely overlooked?

    Thank you in advance.


    Chris
  • Chris Welch at Mar 10, 2016 at 11:58 am
    Thank you for the validation - I thought that was the case.


    On 9 March 2016 at 22:11, Darren Duncan wrote:

    The best option is to keep the concerns separated. Models should always
    take their inputs via explicit arguments for a Model-specific API, and they
    should not have any knowledge of or direct access to a Controller. If this
    means you have to bundle up a larger amount of data in the Controller so
    the data passage is clean, so be it, that is the lesser evil. -- Darren
    Duncan

    On 2016-03-09 11:03 AM, Chris Welch wrote:

    Hi

    This is more of a general MVC question, but hopefully it's okay to ask in
    here.

    I've set up a model to generate data in iCal format (see my previous
    thread and
    thanks for the responses on that), but it has thrown up an interesting
    general
    logic separation question that I don't know the answer to.

    By way of an example, I'm generating iCal events for a data table called
    team_matches (DBIx::Class model name TeamMatch). I was hoping, as would
    seem to
    be the cleanest way to do it, to have a result class "helper" method,
    such that
    on each match object I could call something like:
    $match->generate_ical_data,
    which would generate a hashref of calendar values to be passed through to
    my
    MyApp::Model::ICal module that will generate the actual data. The
    problem with
    this is that certain properties require access to the Catalyst
    application: for
    example, there's a uri property, the value for which will need to be
    generated
    by $c->uri_for_action and since this particular application has the
    ability to
    be multi-language (via CatalstX::I18N) the description value needs to be
    generated by $c->maketext.

    All of this brings up a quandary: there are only two ways around this
    that I can
    see:
    * Pass $c into the $match->generate_ical_data method (which I know is
    STRONGLY discouraged and very very bad).
    * Generate the hashref in the controller (which potentially makes for a
    'fatter' controller than you ideally want and I'm trying to stick to the
    ideals
    here).

    I'm guessing the preference of the above options should really be the
    latter
    (which is what I'm currently doing in the interim), but I am hoping that
    there's
    a super-clever way that I've not thought of that someone can tell me I've
    completely overlooked?

    Thank you in advance.


    Chris

    _______________________________________________
    List: Catalyst at lists.scsys.co.uk
    Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
    Searchable archive:
    http://www.mail-archive.com/catalyst at lists.scsys.co.uk/
    Dev site: http://dev.catalyst.perl.org/
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: <http://lists.scsys.co.uk/pipermail/catalyst/attachments/20160310/68a48b6f/attachment.htm>
  • Aristotle Pagaltzis at Mar 10, 2016 at 12:42 pm

    * Chris Welch [2016-03-09 20:10]:
    All of this brings up a quandary: there are only two ways around this
    that I can see:

    There?s plenty more. E.g. you could have generate_ical_data expect one
    or several callbacks to generate those values for it, something like


         $match->generate_ical_data(
             get_uri => sub { $c->uri_for_action( ... ) },
             get_description => sub { $c->maketext( ... ) },
             # ...
         );


    Possibly you want to set these as instance data somewhere and then not
    pass them explicitly in every call of that method, but that can easily
    hurt testability and understandability of the code, so it depends on the
    exact specifics of the case.


    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>
  • Chris Welch at Mar 10, 2016 at 12:59 pm

    On 10 March 2016 at 12:42, Aristotle Pagaltzis wrote:


    * Chris Welch [2016-03-09 20:10]:
    All of this brings up a quandary: there are only two ways around this
    that I can see:
    There?s plenty more. E.g. you could have generate_ical_data expect one
    or several callbacks to generate those values for it, something like

    ??
    $match->generate_ical_data(
    get_uri => sub { $c->uri_for_action( ... ) },
    get_description => sub { $c->maketext( ... ) },
    # ...
    );
    ?

    ?Oh I very much like that - I *knew* ?I had to be missing a better way of
    doing it, I bow to your superior ideas, thank you very much.


    Thinking about it: any way you would pass in an anonymous sub rather than
    the return value from the actual methods - i.e.:


    ?
         $match->generate_ical_data(
             get_uri => $c->uri_for_action( ... ) ,
             get_description => $c->maketext( ... ) ,
             # ...
         );
    ?




    ?Thanks again.?
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: <http://lists.scsys.co.uk/pipermail/catalyst/attachments/20160310/8f56897c/attachment.htm>
  • Aristotle Pagaltzis at Mar 10, 2016 at 2:23 pm

    * Chris Welch [2016-03-10 14:05]:
    Thinking about it: any way you would pass in an anonymous sub rather
    than the return value from the actual methods - i.e.:

    $match->generate_ical_data(
    get_uri => $c->uri_for_action( ... ) ,
    get_description => $c->maketext( ... ) ,
    # ...
    );

    I?m not sure what you?re asking. Do you mean you want to get rid of the
    `sub { ... }` noise and just pass in ?a method call?? If so, no, there
    is no way of doing that in Perl.


    Also, that would tie you to the signature of the Catalyst method. Which
    may be OK, or may not be.


    Using a closure allows you to write generate_ical_data generically and
    keep the knowledge of how to translate that to Catalyst within the part
    of your code that knows about Catalyst. Consider e.g.


         get_uri => sub {
             my ( $date, $event_id ) = @_;
             $c->uri_for_action( '/day/event', [ $date, $event_id ] );
         },


    Now generate_ical_data doesn?t have to hard-code the '/day/event' action
    path. It doesn?t need to know anything about how URIs are made, it just
    tells the closure which date and ID it needs a URI for, and the closure
    produces one. That makes generate_ical_data easier to test too. Likewise
    the closure might close over variables besides $c ? maybe $self, or some
    other lexical from the action method.


    That?s what I meant when I used `...` in the example instead of just
    writing `@_` to pass through the arguments. Using a closure gives you
    control over what parameters are passed in what order, which doesn?t
    have to be identical to the signature of the Catalyst method you wind
    up calling.


    This the same principle as the reason for having the controller, model
    and view be separate from each other: isolating responsibilities.


    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>
  • Chris Welch at Mar 10, 2016 at 2:40 pm
    ?Thank you so much for this, that makes a lot more sense now.?



    I?m not sure what you?re asking. Do you mean you want to get rid of the
    `sub { ... }` noise and just pass in ?a method call?? If so, no, there
    is no way of doing that in Perl.

    ?My original question was not about passing the method call in per se, but
    the return value from that method call, but I see it would be a lot cleaner
    the way you have suggested.


    ?Thanks again.?
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: <http://lists.scsys.co.uk/pipermail/catalyst/attachments/20160310/84f4e738/attachment.htm>
  • Aristotle Pagaltzis at Mar 11, 2016 at 12:03 am

    * Chris Welch [2016-03-10 15:45]:
    My original question was not about passing the method call in per se,
    but the return value from that method call

    You could do that of course.


    The question I?d ask is, does the caller have to know which values from
    the match object it needs to pick out and put together to produce the
    required value?


    If yes, then that would leak responsibility from generate_ical_data back
    into its caller ? which means e.g. if you want to change exactly how the
    iCal data is generated then you also have to change the caller, not just
    generate_ical_data.


    If not, such as if the values you pass to uri_for_action depend on the
    action only, then you can just pass the return value without causing
    problems, sure. And if you *can* do that, then it?s the better choice,
    because the code will be more readable that way ? not just the caller
    but more importantly generate_ical_data itself. Callbacks will be quite
    a bit more clunky than simple values there.


    Like I said, it depends on the exact specifics.


    Regards,
    --
    Aristotle Pagaltzis // <http://plasmasturm.org/>
  • Trevor Leffler at Mar 11, 2016 at 6:57 pm
    Chris,


    Related to this, I've a scheduled job that sends email that include URLs
    back to my catalyst app, so... can't really use a callback here. URL
    details such as hostname and path are configuration info. It would be
    nice to have a light-weight method for accessing the app's routing, but
    I'm okay with the balance and separation of concerns achieved via some
    minor "duplication."


    As a second example, my app does something similar to yours...


        my $cj = $c->model('Collection::JSON');
        $cj->add_links({href => $c->uri_for(...)->as_string);


    This model holds data returned via my Web API. It's bothered me that if
    I wanted to use it outside of a web app I'd need some other way to
    supply URIs, but I've also shrugged this off because, um... *it's for a
    Web API* and the coupling seems necessary. ;) Regardless, the model's
    interface is clean, and it's not the model's job to figure out what the
    end-point URIs are supposed to me.


    Okay, enough with my ramblings!


    Cheers,
    --Trevor

    On 03/10/2016 04:03 PM, Aristotle Pagaltzis wrote:
    * Chris Welch [2016-03-10 15:45]:
    My original question was not about passing the method call in per se,
    but the return value from that method call
    You could do that of course.

    The question I?d ask is, does the caller have to know which values from
    the match object it needs to pick out and put together to produce the
    required value?

    If yes, then that would leak responsibility from generate_ical_data back
    into its caller ? which means e.g. if you want to change exactly how the
    iCal data is generated then you also have to change the caller, not just
    generate_ical_data.

    If not, such as if the values you pass to uri_for_action depend on the
    action only, then you can just pass the return value without causing
    problems, sure. And if you *can* do that, then it?s the better choice,
    because the code will be more readable that way ? not just the caller
    but more importantly generate_ical_data itself. Callbacks will be quite
    a bit more clunky than simple values there.

    Like I said, it depends on the exact specifics.

    Regards,
  • Chris Welch at Mar 14, 2016 at 10:12 am
    Thank you all very much for your help, it's given me some real food for
    thought.
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: <http://lists.scsys.co.uk/pipermail/catalyst/attachments/20160314/ad4e41d3/attachment.htm>

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedMar 9, '16 at 7:03p
activeMar 14, '16 at 10:12a
posts10
users4
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2021 Grokbase