Mailing List Archive

An MVC logic separation conundrum
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
Re: An MVC logic separation conundrum [ In reply to ]
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@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: An MVC logic separation conundrum [ In reply to ]
Thank you for the validation - I thought that was the case.

On 9 March 2016 at 22:11, Darren Duncan <darren@darrenduncan.net> 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@lists.scsys.co.uk
> Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
> Searchable archive:
> http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
> Dev site: http://dev.catalyst.perl.org/
>
Re: An MVC logic separation conundrum [ In reply to ]
* Chris Welch <welch.chris@gmail.com> [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/>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: An MVC logic separation conundrum [ In reply to ]
On 10 March 2016 at 12:42, Aristotle Pagaltzis <pagaltzis@gmx.de> wrote:

> * Chris Welch <welch.chris@gmail.com> [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.​
Re: An MVC logic separation conundrum [ In reply to ]
* Chris Welch <welch.chris@gmail.com> [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/>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: An MVC logic separation conundrum [ In reply to ]
​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.​
Re: An MVC logic separation conundrum [ In reply to ]
* Chris Welch <welch.chris@gmail.com> [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/>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: An MVC logic separation conundrum [ In reply to ]
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 <welch.chris@gmail.com> [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,
>

_______________________________________________
List: Catalyst@lists.scsys.co.uk
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/
Dev site: http://dev.catalyst.perl.org/
Re: An MVC logic separation conundrum [ In reply to ]
Thank you all very much for your help, it's given me some real food for
thought.