A dispatch table (or dispatch method) is both a table (model) and a router/controller.
Imagine a tabbed navigation where there may be 30 tabs for various end users. Each tab is essentially a page that has its own controller and views. In my case, a dispatch table contains keys for the tabs and then data for each tab (path, displayName, visibility, etc).
my $tabs = {
Home => {
action => \&HomeController::dashboard,
displayName => ‘Home’,
...
},
About => {
action => \&AboutController::info,
displayName => ‘About Us’,
...
},
...
};
Initially I had a TabController and TabView. Inside the controller was a dispatch table; however it didn’t seem to fall in accordance with MVC. It seemed fine when there was only 3 tabs, but different when it grew, especially as it required security trimming or filtering the views.
It made sense to move it to the model since it was being treated more as a data table. However, because it’s Perl (and a dispatch table) all the corresponding packages must also be loaded. So this TabModel is loading many controllers (and in some cases views). I don’t particularly like to load/reference any controllers in the model, so loading multi feels even worse.
Is there a best practice or example for this scenario?
Addendum
In an attempt to provide something a little more tangible. I'm going to attempt to frame this around a makeshift web application. This is only a conceptual example, not fully-working and missing a lot; but hopefully should add a little more description and context. It has the following, directory structure:
index.pl -- entrance
Controllers/
-- Dashboard.pm
-- Home.pm
-- About.pm
Models/
-- Tabs.pm
-- Users.pm
Views/
-- Dashboard.pm
-- Home.pm
-- About.pm
-- Error.pm
I won't break out index.pl, but it essentially parses parameters and directs to the DashboardController::dashboard.
DashboardController
package Controllers::Dashboard;
sub dashboard{
my $users = Models::Users::get_all(); # Users Model
my $tabs = Models::Tabs::get_permitted( # Tabs Model
$users->{CURRENT_USER}{permissions}
);
print Views::Page::render($users,$tabs); # Page View
}
AboutController
package Controllers::About;
sub info {
# No models necessary
print Views::About::render();
}
TabsModel
package Models::Tabs;
use Controllers::Home;
use Controllers::About;
sub get_all {
my $tabs = {
Home => {
action => \&Controllers::Dashboard::dashboard,
displayName => ‘Home’,
...
},
About => {
action => \&Controllers::About::info,
displayName => ‘About Us’,
...
},
...
};
return $tabs;
}
sub get_permitted {
my $user_permissions = shift;
my $tabs = get_all();
if (defined $user_permissions){
foreach my $tab (keys %$tabs){
delete $tabs->{$tab} unless $user_permissions->{"can_access_$tab"};
}
}
}
DashboardView
package Views::Dashboard;
sub render {
my ($users,$tabs) = #_;
my $html_tabs = '<ul>';
foreach my $tab (values %$tabs){
$html_tabs .= "$tab->{displayName}";
}
$html_tabs .= '</ul>;
my $html = <<"END";
$html_tabs
<!-- dashboard content -->
END
return $html;
}
ancient question, but it popped up in the feed today
I think there's something wonky in the way you've categorized things. Models are typically sources of data and don't really care what you do with them or how you display them. However, you've pushed some of the view (presentation) into the model. To me, that Tabs.pm looks like it should be part of some view.
And, because a dispatch table is a table doesn't mean it's a source of data. It's a particular technique to solve a problem that's not related to the particular data or the particular view. If you did the same task without the dispatch table, you wouldn't suddenly move that responsibility into another part of MVC.
Related
I have the following code in our webapp written using Mojolicious, and it doesn't work as expected: the bridge handler doesn't get the correct stash data derived from routes (gets undef), so the rest of code fails, however, debug output of $self->stash('city') in any of route handlers is as expected.
...
# Router.
my $r = $self->routes->bridge->to('Main#common');
$r->route('/')->to('Main#index')->name('start');
$r->route('/:region/:city/category/:id')->to('Main#list_category')->name('list_category');
$r->route('/:region/:city/part/:id/:name')->to('Main#show_part')->name('show_part');
...
# Controller.
sub common
{
my $self=shift;
my $db=$self->db;
my $city=$self->stash('city');
my $region=$self->db->selectrow_hashref('select * from region where LOWER(translit)=? ORDER BY region_id LIMIT 1',undef,$city);
say "City=$city.";
if(!$region)
{
$region={};
}
$self->stash(region=>$region);
return 1;
}
...
I think it's correct behavior.
Look at this code.
Placeholder is added when the appropriate route is taken to the processing, i.e., step by step.
Really, look at you routes.
my $r = $self->routes->bridge->to('Main#common');
$r->route('/')->to('Main#index')->name('start');
$r->route('/:region/:city/category/:id')->to('Main#list_category')->name('list_category');
$r->route('/:region/:city/part/:id/:name')->to('Main#show_part')->name('show_part');
I can't understand what behavior you expect when go to route /.
Sub common will be invoked in this case. There are no value for placeholder city!
So, correct solution for your routes must look like this:
my $r = $self->routes;
$r->route('/')->to('Main#index')->name('start');
my $r_city = $r->bridge('/:region/:city/')->to('Main#common');
$r_city->route('/category/:id')->to('Main#list_category')->name('list_category');
$r_city->route('/part/:id/:name')->to('Main#show_part')->name('show_part');
By the way,
starting from Mojolicious version 6.0 bridge was deprecated to favor under. So, you need to replace bridge on under.
But, if you very-very want to have value of placeholder city in common function, you may look at this two line.
You need to write this BAD code in common sub:
sub common {
my $self = shift;
my $stack = $self->match->stack;
warn $self->dumper($stack);
...
}
Print $stack and you understand how to get value of placeholder city.
Assume the following code:
package Thing;
sub new {
my $this=shift;
bless {#_},$this;
}
sub name {
my $this=shift;
if (#_) {
$this->{_name}=shift;
}
return $this->{_name};
}
Now assume we've instantiated an object thusly:
my $o=Thing->new();
$o->name('Harold');
Good enough. We could also instantiate the same thing more quickly with either of the following:
my $o=Thing->new(_name=>'Harold'); # poor form
my $o=Thing->new()->name('Harold');
To be sure, I allowed attributes to be passed in the constructor to allow "friendly" classes to create objects more completely. It could also allow for a clone-type operator with the following code:
my $o=Thing->new(%$otherthing); # will clone attrs if not deeper than 1 level
This is all well and good. I understand the need for hiding attributes behind methods to allow for validation, etc.
$o->name; # returns 'Harold'
$o->name('Fred'); # sets name to 'Fred' and returns 'Fred'
But what this doesn't allow is easy manipulation of the attribute based on itself, such as:
$o->{_name}=~s/old/ry/; # name is now 'Harry', but this "exposes" the attribute
One alternative is to do the following:
# Cumbersome, not syntactically sweet
my $n=$o->name;
$n=~s/old/ry/;
$o->name($n);
Another potential is the following method:
sub Name :lvalue { # note the capital 'N', not the same as name
my $this=shift;
return $this->{_name};
}
Now I can do the following:
$o->Name=~s/old/ry/;
So my question is this... is the above "kosher"? Or is it bad form to expose the attribute that way? I mean, doing that takes away any validation that might be found in the 'name' method. For example, if the 'name' method enforced a capital first letter and lowercase letters thereafter, the 'Name' (capital 'N') bypasses that and forces the user of the class to police herself in the use of it.
So, if the 'Name' lvalue method isn't exactly "kosher" are there any established ways to do such things?
I have considered (but get dizzy considering) things like tied scalars as attributes. To be sure, it may be the way to go.
Also, are there perhaps overloads that may help?
Or should I create replacement methods in the vein of (if it would even work):
sub replace_name {
my $this=shift;
my $repl=shift;
my $new=shift;
$this->{_name}=~s/$repl/$new/;
}
...
$o->replace_name(qr/old/,'ry');
Thanks in advance... and note, I am not very experienced in Perl's brand of OOP, even though I am fairly well-versed in OOP itself.
Additional info:
I guess I could get really creative with my interface... here's an idea I tinkered with, but I guess it shows that there really are no bounds:
sub name {
my $this=shift;
if (#_) {
my $first=shift;
if (ref($first) eq 'Regexp') {
my $second=shift;
$this->{_name}=~s/$first/$second/;
}
else {
$this->{_name}=$first;
}
}
return $this->{_name};
}
Now, I can either set the name attribute with
$o->name('Fred');
or I can manipulate it with
$o->name(qr/old/,'ry'); # name is now Harry
This still doesn't allow stuff like $o->name.=' Jr.'; but that's not too tough to add. Heck, I could allow calllback functions to be passed in, couldn't I?
Your first code example is abolutely fine. This is a standard method to write accessors. Of course this can get ugly when doing a substitution, the best solution might be:
$o->name($o->name =~ s/old/ry/r);
The /r flag returns the result of the substitution. Equivalently:
$o->name(do { (my $t = $o->name) =~ s/old/ry/; $t });
Well yes, this 2nd solution is admittedly ugly. But I am assuming that accessing the fields is a more common operation than setting them.
Depending on your personal style preferences, you could have two different methods for getting and setting, e.g. name and set_name. (I do not think get_ prefixes are a good idea – 4 unneccessary characters).
If substituting parts of the name is a central aspect of your class, then encapsulating this in a special substitute_name method sounds like a good idea. Otherwise this is just unneccessary ballast, and a bad tradeoff for avoiding occasional syntactic pain.
I do not advise you to use lvalue methods, as these are experimental.
I would rather not see (and debug) some “clever” code that returns tied scalars. This would work, but feels a bit too fragile for me to be comfortable with such solutions.
Operator overloading does not help with writing accessors. Especially assignment cannot be overloaded in Perl.
Writing accessors is boring, especially when they do no validation. There are modules that can handle autogeneration for us, e.g. Class::Accessor. This adds generic accessors get and set to your class, plus specific accessors as requested. E.g.
package Thing;
use Class::Accessor 'antlers'; # use the Moose-ish syntax
has name => (is => 'rw'); # declare a read-write attribute
# new is autogenerated. Achtung: this takes a hashref
Then:
Thing->new({ name => 'Harold'});
# or
Thing->new->name('Harold');
# or any of the other permutations.
If you want a modern object system for Perl, there is a row of compatible implementations. The most feature-rich of these is Moose, and allows you to add validation, type constraints, default values, etc. to your attributes. E.g.
package Thing;
use Moose; # this is now a Moose class
has first_name => (
is => 'rw',
isa => 'Str',
required => 1, # must be given in constructor
trigger => \&_update_name, # run this sub after attribute is set
);
has last_name => (
is => 'rw',
isa => 'Str',
required => 1, # must be given in constructor
trigger => \&_update_name,
);
has name => (
is => 'ro', # readonly
writer => '_set_name', # but private setter
);
sub _update_name {
my $self = shift;
$self->_set_name(join ' ', $self->first_name, $self->last_name);
}
# accessors are normal Moose methods, which we can modify
before first_name => sub {
my $self = shift;
if (#_ and $_[0] !~ /^\pU/) {
Carp::croak "First name must begin with uppercase letter";
}
};
The purpose of class interface is to prevent users from directly manipulating your data. What you want to do is cool, but not a good idea.
In fact, I design my classes, so even the class itself doesn't know it's own structure:
package Thingy;
sub new {
my $class = shift;
my $name = shift;
my $self = {};
bless, $self, $class;
$self->name($name);
return $self;
}
sub name {
my $self = shift;
my $name = shift;
my $attribute = "GLUNKENSPEC";
if ( defined $name ) {
$self->{$attribute} = $name;
}
return $self->{$attribute};
}
You can see by my new constructor that I could pass it a name for my Thingy. However, my constructor doesn't know how I store my name. Instead, it merely uses my name method to set the name. As you can see by my name method, it stores the name in an unusual way, but my constructor doesn't need to know or care.
If you want to manipulate the name, you have to work at it (as you showed):
my $name = $thingy->name;
$name =~ s/old/ry/;
$thingy->name( $name );
In fact, a lot of Perl developers use inside out classes just to prevent this direct object manipulation.
What if you want to be able to directly manipulate a class by passing in a regular expression? You have to write a method to do this:
sub mod_name {
my $self = shift;
my $pattern = shift;
my $replacement = shift;
if ( not defined $replacement ) {
croak qq(Some basic error checking: Need pattern and replacement string);
}
my $name = $self->name; # Using my name method for my class
if ( not defined $name ) {
croak qq(Cannot modify name: Name is not yet set.);
}
$name = s/$pattern/$replacement/;
return $self->name($name);
}
Now, the developer can do this:
my $thingy->new( "Harold" );
$thingy->mod_name( "old", "new" );
say $thingy->name; # Says "Harry"
Whatever time or effort you save by allowing for direct object manipulation is offset by the magnitude of extra effort it will take to maintain your program. Most methods don't take more than a few minutes to create. If I suddenly got an hankering to manipulate my object in a new and surprising way, it's easy enough to create a new method to do this.
1. No. I don't actually use random nonsense words to protect my class. This is purely for demo purposes to show that even my constructor doesn't have to know how methods actually store their data.
I understand the need for hiding attributes behind methods to allow for validation, etc.
Validation is not the only reason, although it is the only one you refer to. I mention this because another is that encapsulation like this leaves the implementation open. For example, if you have a class which needs to have a string "name" which can be get and set, you could just expose a member, name. However, if you instead use get()/set() subroutines, how "name" is stored and represented internally doesn't matter.
That can be very significant if you write bunches of code with uses the class and then suddenly realize that although the user may be accessing "name" as a string, it would be much better stored some other way (for whatever reason). If the user was accessing the string directly, as a member field, you now either have to compensate for this by including code that will change name when the real whatever is changed and...but wait, how can you then compensate for the client code that changed name...
You can't. You're stuck. You now have to go back and change all the code that uses the class -- if you can. I'm sure anyone who has done enough OOP has run into this situation in one form or another.
No doubt you've read all this before, but I'm bringing it up again because there are a few points (perhaps I've misunderstood you) where you seem to outline strategies for changing "name" based on your knowledge of the implementation, and not what was intended to be the API. That is very tempting in perl because of course there is no access control -- everything is essential public -- but it is still a very very bad practice for the reason just described.
That doesn't mean, of course, that you can't simply commit to exposing "name" as a string. That's a decision and it won't be the same in all cases. However, in this particular case, if what you are particularly concerned with is a simple way to transform "name", IMO you might as well stick with a get/set method. This:
# Cumbersome, not syntactically sweet
Maybe true (although someone else might say it is simple and straightforward), but your primary concern should not be syntactic sweetness, and neither should speed of execution. They can be concerns, but your primary concern has to be design, because no matter how sweet and fast your stuff is, if it is badly designed, it will all come down around you in time.
Remember, "Premature optimization is the root of all evil" (Knuth).
So my question is this... is the above "kosher"? Or is it bad form to expose the attribute that way?
It boils down to: Will this continue to work if the internals change? If the answer is yes, you can do many other things including but not limited to validation.)
The answer is yes. This can be done by having the method return a magical value.
{
package Lvalue;
sub TIESCALAR { my $class = shift; bless({ #_ }, $class) }
sub FETCH { my $self = shift; my $m = $self->{getter}; $self->{obj}->$m(#_) }
sub STORE { my $self = shift; my $m = $self->{setter}; $self->{obj}->$m(#_) }
}
sub new { my $class = shift; bless({}, $class) }
sub get_name {
my ($self) = #_;
return $self->{_name};
}
sub set_name {
my ($self, $val) = #_;
die "Invalid name" if !length($val);
$self->{_name} = $val;
}
sub name :lvalue {
my ($self) = #_;
tie my $rv, 'Lvalue', obj=>$self, getter=>'get_name', setter=>'set_name';
return $rv;
}
my $o = __PACKAGE__->new();
$o->name = 'abc';
print $o->name, "\n"; # abc
$o->name = ''; # Invalid name
Greetings,
I'm new to Catalyst and I am attempting to implement some dispatch logic.
My database has a table of items, each with a unique url_part field, and every item has a parent in the same table, making a tree structure. If baz is a child of bar which is a child of foo which is a child of the root, I want the URL /foo/bar/baz to map to this object. The tree can be any depth, and users will need to be able to access any node whether branch or leaf.
I have been looking through the documentation for Chained dispatchers, but I'm not sure if this can do what I want. It seems like each step in a chained dispatcher must have a defined name for the PathPart attribute, but I want my URLs to be determined solely by the database structure.
Is this easy to implement with the existing Catalyst dispatcher, or will I need to write my own dispatch class?
Thanks! :)
ETA:
I figured out that I can use an empty Args attribute to catch an arbitrary number of arguments. The following seems to successfully catch every request under the root:
sub default :Path :Args() {
my ( $self, $c ) = #_;
my $path = $c->request->path;
$c->response->status( 200 );
$c->response->body( "Your path is $path" );
}
From there I can manually parse the path and get what I need, however, I don't know if this is the best way to accomplish what I'm after.
It depends on the structure of your data, which I'm not completely clear on from your question.
If there is a fixed number of levels (or at least a limited range of numbers of levels) with each level corresponding to a specific sort of thing, then Chained can do what you want -- it's valid (and downright common) to have a chained action with :CaptureArgs(1) PathPart('') which will create a /*/ segment in the path -- that is, it gobbles up one segment of the path without requiring any particular fixed string to show up.
If there's not any such thing -- e.g. you're chasing an unlimited number of levels down an arbitrary tree, then a variadic :Args action is probably exactly what you want, and there's nothing dirty in using it. But you don't need to be decoding $c->req->path yourself -- you can get the left-over path segments from $c->req->args, or simply do my ($self, $c, #args) = #_; in your action.
You can write a new DispatchType, but it's just not likely to be worth the payoff.
After playing around with various options, I believe I've arrived at an acceptable solution. Unfortunately, I couldn't get a recursive dispatch going with :Chained (Catalyst complains if you try to chain a handler to itself. That's no fun.)
So I ended up using a single handler with a large CaptureArgs, like this:
sub default : CaptureArgs(10) PathInfo('') {
my ( $self, $c, #args ) = #_;
foreach my $i( 0 .. $#args ) {
my $sub_path = join '/', #args[ 0 .. $i ];
if ( my $ent = $self->_lookup_entity( $c, $sub_path ) ) {
push #{ $c->stash->{ent_chain} }, $ent;
next;
}
$c->detach( 'error_not_found' );
}
my $chain = join "\n", map { $_->entity_id } #{ $c->stash->{ent_chain} };
$c->response->content_type( 'text/plain' );
$c->response->body( $chain );
}
If I do a GET on /foo/bar/baz I get
foo
foo/bar
foo/bar/baz
which is what I want. If any part of the URL doesn't correspond to an object in the DB, I get a 404.
This works fine for my application, which will never have things ten-levels deep, but I wish I could find a more general solution that could support an arbitrary-depth tree.
Given a simple case of two tables - Term and Definition - where Term has_many Definitions and Definition belongs_to Term, all terms and the corresponding definitions are to be fetched and displayed somehow.
Here is what I've come up with so far:
my $terms= $schema->resultset('Term')->search(undef, {
prefetch => 'definitions',
});
while (my $term = $terms->next) {
my #terms;
push #terms, $term->term;
my $definitions = $term->definitions;
my #definitions;
while (my $definition = $definitions->next) {
push #definitions, $definitions;
}
...
}
It does the job but I was wondering if a different, less crufty approach could be taken.
my $terms= $schema->resultset('Term')->search(undef, {
prefetch => 'definitions',
});
my #terms = $terms->all;
my #definitions = map $_->definitions->all, #terms;
This looks like what you are trying to do; I can't really tell. The fact that you make a new array, push onto it, and then let it go out of scope doesn't really make any sense at all. Anyway, if I understand you correctly all you wanted was the all method from DBIx::Class::ResultSet.
DBIx::Class::Manual::Joining should help. See, for example, Whole related objects.
Currently we use DBIx::Class::Schema::Loader to generate and regenerate (when our db schema changes) a set of Result classes.
We add additional relationships and methods to the bottom of these classes and this is causing merge hell when people regenerate or change the schema.
We would like to maintain our custom changes in a separate set of files that sit in parallel with the auto-generated ones.
Is there a simple, clean, recommended way of doing this?
I ran into the same problem. You can just create another class that inherits from the generated classes. However, you need to pull over the table reference, and the relationships into the class you are editing, but you can leave the column definitions and what not in the generated class. I basically wrote a helper for the loader that generates the classes into an "Immutable" namespace, and creates a child for each of them in a "Mutable" namespace along with the table name reference and the relationships from the generated model. It seems to work reasonably well, and I no longer have to worry about developers editing the generated section of the class. I should probably write up the whole thing in a blog post one of these days.
I solved it by Moosifying the schemas and then creating a set of Moose::Roles that I apply to the Schema classes just after schema->connection();
It goes a little like this:
my $schema = My::Schema->connection();
foreach my $source ($schema->sources) {
my $domain_pkg = "My::Domain::$source";
eval "require $domain_pkg";
# ignore failures due to file-not-found
if ($# && $# =~/^Can't locate.*INC/) {
# but barf if class doesnt compile
} elsif ($#) {
confess "Failed to load $domain_pkg for $pkg!!: - $#";
# re-register domain class with the resultsource
# and apply the role
} else {
my $schema_pkg = "${pkg}::$source";
$c->register_class($source, $schema_pkg);
use Moose::Util;
# check schema is moosyfied
if ( $schema_pkg->can('meta') ) {
my $meta = $schema_pkg->meta;
eval {
Moose::Util::apply_all_roles($meta, $domain_pkg);
};
if ($#) {
confess "Failed to add $domain_pkg role to $schema_pkg: $#\n";
} else {
l4p->info("Found and applied Domain role: '$domain_pkg' for schema: '$schema_pkg'");
}
} else {
warn "Cant call meta on $schema_pkg. ";
}
}
}
Nearby..
use MooseX::Declare
role My::Domain::Person {
# modify schema
My::Schema::Person->inflate_column( ..);
My::Schema::Person->belongs_to(..);
My::Schema::Person->set_primary_key(..);
# add some method modifiers to check/modify construction
around new (ClassName $class : $params) {
munge params..
$self->$orig($params);
}
# post insert hook
after insert () {
do_something..
}
# domain methods
sub fullname {
$self->firstname.' '.$self->surname;
}
}
While this isn't technically an answer to the question, it is a solution to the merge hell problem that spawned it. When calling dbicdump or make_schema_at, you could set the omit_version and omit_timestamp flags, which will generate a signature like below:
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CKsL4EO4b/JE3QXBSC4EXg
When re-dumping, this signature shouldn't change unless the actual tables do, so any version control won't see unreasonable conflicts.