From: Sir Robert Burbridge<[email protected]>
To: The elegant MVC web framework<[email protected]>
Sent: Mon, 22 November, 2010 17:20:16
Subject: Re: [Catalyst] Begginer's question about application structure
I use DBIx::Class with multiple interfaces in the same app; this is the
structure I've found most useful so far:
### the lib dir.
lib/
### MyApp stores everything relevant to my app
lib/MyApp
### "MyApp::Web" stores the web interface
lib/MyApp/Web
lib/MyApp/Web/Model
lib/MyApp/Web/View
lib/MyApp/Web/Controller
### HTML::FormHandler forms that are web-specific.
lib/MyApp/Web/Form
### Stores db stuff for DBIx::Class.
lib/MyApp/Schema/
### Normal DBIx::Class schema, results, and resultsets
lib/MyApp/Schema/MyApp
lib/MyApp/Schema/MyApp/Result
lib/MyApp/Schema/MyApp/ResultSet
### Schema/Result and Schema/ResultSet contain things like Moose roles
### that I want to apply to multiple models, but that really only make
### sense in the context of this application.
lib/MyApp/Schema/Result
lib/MyApp/Schema/ResultSet
### A namespace for script based access to the app
lib/MyApp/Script
lib/MyApp/Script/User
lib/MyApp/Script/User/Create.pm
lib/MyApp/Script/Report
lib/MyApp/Script/Report/Documentation.pm
### Libs specific to the CLI
lib/MyApp/CLI
### Tests
t
### Store
t/lib
A couple of notes:
The contents of lib/MyApp/Script are scripts that use MooseX::GetOpt. I
added a helper script in script/myapp_utils.pl that allows me to do this
(this is mock output, but you get the idea). In the example below, it
scans through the following namespaces in order: MyApp::Script,
$ENV{MYAPP_SCRIPT_NAMESPACE}, CatalystX::Script, Catalyst::Script;
looking for ::Schema::Loader.
### Run with no arguments
$ ./script/myapp_utils.pl
Available scripts:
Report::Testing
Report::Documentation
Schema::Loader
User::Create
User::Modify
...
$ ./script/myapp_utils.pl Schema::Loader
An error has occurred: Required option missing: username
usage: myapp_utils.pl [-?bdnpu] [long options...]
-? --usage --help Prints this usage information.
-u --usr --user --username A username with which to log into
the db
-p --pwd --pass --password A password with which to log into
the db
-b --db --dsn The DSN of the source database (e.g.
"dbi:mysql:myapp")
-d --dir --directory a directory into which to install
the schema
-n --namespace the path to a directory
$ ./script/myapp_utils.pl Schema::Loader \
--user ogopogo \
--pass vonmugwumpus \
--dsn dbi:mysql:my_db \
--dir lib \
--namespace MyApp::Schema::MyApp
Creating schema ... done.
$
Web forms are stored in the path like Halifax::Web::Form::CD::Create
(create a new CD from the script interface).
FWIW =)
-Sir
Hi,
Thanks for taking the time to put this information together. There is some very
useful advice and ideas in there which I will take on board. MooseX::GetOpt
looks very handy, and it's this sort of information - the tips and tricks of the
professionals if you like - that really help to advance the skills and knowledge
of others.
Regards,
Martin
_______________________________________________
List: [email protected]
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/
You're welcome =)
By the way, I forgot to mention one piece of practical advice when
determining what should be in your controller vs. model:
_/*Write your controller first.*/_
The controller acts as a functional specification for your models; it's
a "live test", so to speak. So if you've got an Artist-CD sort of
setup, use this process:
1) Figure out your URI schema (do you want /cd/928420249292/artists or
/cd?id=928420249292&field=artists, or what?)
2) Once you figure out that you want /cd/928420249292/artists ;) write
the controllers. This is very important: /Write the controllers as
though the models already exist, then implement models to the
controllers/. Write your controllers as though it's a perfect world,
and your ideal models exist. That means do things like:
sub some_controller :Path :Args {
### Task: sell 10 copies of a CD to a customer.
$cd = $c->stash->{cd};
$customer = $c->user;
### We could do the following:
###
### my $quantity = 10;
###
### ### Adjust the stock levels in the store
### $c->stash(remaining_stock => $cd->remaining_stock - $quantity);
### $cd->popularity($cd->popularity + $quantity);
###
### ### Record in the customer's data that he purchased this
quantity
### ### of this CD
### $customer->log_purchase(item => $cd, quantity => $quantity);
###
### ### ... and so on.
###
### but why? That may appear clean simply because we've wrapped
things up in
### methods, but those will *always* be done when we purchase
CDs; the cashier
### at the store shouldn't have to do that manually. We could
just wrap all
### that activity up into the concept of "purchasing":
$customer->purchase($cd => 10);
}
When your entire feature is added in the front-end, then go to your
model and implement to spec.
package MyApp::Schema::MyApp::Result::Customer;
### ...
sub purchase {
my ($self, $item, $quantity) = @_;
### Do all the stuff involved in purchasing an item here.
}
-Sir
_______________________________________________
List: [email protected]
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/