[cgiapp] Model design in C::A/Titanium
I know we've been around here before in some related topics, but I would appreciate some feedback on a possible MVC structure for a Titanium/CGI::App that I am currently building. I'm using CA::Dispatch to allow multiple apps with a small number of rm's each. Views are handled by CAP::TT. Just the Model aspect is proving a little challenging. I'm trying to ensure that all database calls are handled outside the controllers, and to this end am using a module called WebApp::Model # package WebApp::Model; BEGIN { setmoduledirs('/path/to/app'); # Module::Find useall WebApp::DB; # Rose::DB::Object classes } sub new { my $class = shift; bless { }, $class; } sub get_foo { } sub update_bar { } sub delete_baz { } # WebApp::Model initially loads all WebApp::DB::* (Rose::DB::Object) classes, and is made available to the WebApp object via cgiapp_init: # sub cgiapp_init { my $c = shift; .. $c-param( 'model' = WebApp::Model-new ); } Each controller run-mode uses $c-param( 'model') to get its data: ## sub foo : Runmode { # CAP::AutoRunmode my $c = shift; .. my $data = $c-param('model')-get_foo; } ## This seems to work well, but the problem is that WebApp::Model is growing, as ever more (unrelated) db-related methods are added. Perhaps I could/should sub-class this one? Ideally I would move the WebApp::Model methods into their respective WebApp::DB class (eg get_foo() goes into WebApp::DB::Foo), but then I can't access it any longer from $c-param('model')-get_foo. But the main question is whether this is the right approach to start with - stuffing WebApp::Model into the WebApp object and retrieving it in the run-modes via a param_name. Advantages include decoupling the model from the controllers - they don't know/care how get_foo() gets its data, and I can change the db-related stuff (eg RDBO = DBIC) without affecting the controllers. Disadvantage (so far) is lumping all methods into one (WebApp::Model) module. Comments thoughts most welcome. -- Richard Jones # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
[cgiapp] Re: Model design in C::A/Titanium
Richard, In your design, you have a single entry point to the model, as you describe, this point is growing too large for comfort. I think of the Model as a layer, made of up of potentially lots of model modules. In my own projects, these almost always end up looking like CGI::Application plugins. That's because I want a few CGI::App methods available to them, like 'query()', 'cfg()' and 'dbh()' Sometimes it's cleaner to use completely separate objects, like Rose::DB or CGI::Uploader. If I'm going to use these repeatedly, I wrap them in their own little wrapper class that simplifies initialization. This is much like some CGI::App plugins work, like Session plugin for example. And one other suggestion: If you find you are doing this a lot: $c-param('model') Go ahead and make a simple shortcut method for it, so you can just say $c-model; It will be fast to create, cleans up your code, and gives you more flexibility about implementation in the future. I suggest that as someone who used param() in just the same way for some time. Mark -- . . . . . . . . . . . . . . . . . . . . . . . . . . . Mark StosbergPrincipal Developer [EMAIL PROTECTED] Summersault, LLC 765-939-9301 ext 202 database driven websites . . . . . http://www.summersault.com/ . . . . . . . . # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Model design in C::A/Titanium
On Tue, Sep 23, 2008 at 10:07 AM, Porta [EMAIL PROTECTED] wrote: Ideally I would move the WebApp::Model methods into their respective WebApp::DB class (eg get_foo() goes into WebApp::DB::Foo), but then I can't access it any longer from $c-param('model')-get_foo. I guess that I'm missing something here, because I don't see why you need to crowd all the models into a single one. You can still decouple the models from the controllers if every subclass of WebApp::DB knows how to interact with the database. Then, instead of $c-param('mode')-get_foo(); you'll do: my $foo = WebApp::DB::Foo-new; $foo-get_all(); Where get_all is a sub inherited from WebApp::DB. My 2 cents... I'd rather call it like so: $c-model-foo-get() Have a base class of WebApp::DB; Have WebApp::DB::Foo inherit from base class; Make the base class smart enough to auto-load WebApp::DB::Foo and instantiate it when needed. A little autoloader magic goes a long way, and you get to get rid of all the get_foo stuff, and just do foo-get(). Using autoloader to only require in the subclasses when needed will also keep memory usage down, as rarely used modules won't get loaded unless they're actually called. Here's a very basic example of that. I use something similar in a bunch of my apps, but this snippet hasn't been tested on its own, so my apologies if there's a typo :-) sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; croak Undeclared subroutine call [$attr] unless ref($self); $attr =~ s/.*:://; return if $attr eq 'DESTROY'; # return already initialized object, if it exists if (defined($self-{_objstash}{$attr}) ref($self-{_objstash}{$attr})) { return $self-{_objstash}{$attr}; } my $package = WebApp::DB::$attr; # catch odd case of infinite loop # (child class calling method in parent that calls a method in that child class) if (ref($self) eq $package) { $self-{$attr} = $self; weaken $self-{$attr}; return $self; } # instantiate object unless (eval require $package) { croak Unable to load module [$attr].; } my $obj = $package-new() or croak unable to instatiate [$attr].; $self-{_objstash}{$attr} = $obj; return $obj; } Help that helps, -- Josh I. # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Model design in C::A/Titanium
Joshua Miller wrote: On Tue, Sep 23, 2008 at 10:07 AM, Porta [EMAIL PROTECTED] wrote: I guess that I'm missing something here, because I don't see why you need to crowd all the models into a single one. You can still decouple the models from the controllers if every subclass of WebApp::DB knows how to interact with the database. Then, instead of $c-param('mode')-get_foo(); you'll do: my $foo = WebApp::DB::Foo-new; $foo-get_all(); Where get_all is a sub inherited from WebApp::DB. My 2 cents... I'd rather call it like so: $c-model-foo-get() Or $c-model('Foo')-get(); rhesa # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Model design in C::A/Titanium
Joshua Miller wrote: Using autoloader to only require in the subclasses when needed will also keep memory usage down, as rarely used modules won't get loaded unless they're actually called. Are you running under normal CGI (do people still do that?). If not, and you're running on a good OS (one with copy-on-write memory) and you're using a forking environment (mod_perl, or most FastCGI implementations) you should pre-load all of you modules up front. This will actually decrease memory usage. Or course if you have a really, really rarely used module that is really, really big you might reconsider this advice. Unless I'm missing something? -- Michael Peters Plus Three, LP # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Re: Model design in C::A/Titanium
Hi Mark, Appreciate the reply. Mark Stosberg wrote: And one other suggestion: If you find you are doing this a lot: $c-param('model') Go ahead and make a simple shortcut method for it, so you can just say $c-model; Did you have something like this in mind: sub model { my $c = shift; return $c-param('model'); } Seems a bit, erm, 'trivial' ? # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Model design in C::A/Titanium
On Tue, Sep 23, 2008 at 4:09 PM, Michael Peters [EMAIL PROTECTED]wrote: Joshua Miller wrote: Using autoloader to only require in the subclasses when needed will also keep memory usage down, as rarely used modules won't get loaded unless they're actually called. Are you running under normal CGI (do people still do that?). If not, and you're running on a good OS (one with copy-on-write memory) and you're using a forking environment (mod_perl, or most FastCGI implementations) you should pre-load all of you modules up front. This will actually decrease memory usage. Or course if you have a really, really rarely used module that is really, really big you might reconsider this advice. Unless I'm missing something? You're not missing anything. I'm running under mod_perl under linux boxen, and do pre-load a ton of modules that are used often. It's a million line system though, so there's a whole lot that doesn't load frequently. The pre-load all module is a good rule of thumb - it's a lot easier than defining how many uses a day justifies pre-loading. The above snippet can be updated to just do a use on all the module at the top, and remove the require part... but that will also mean that it'll need updated every time you add a new subclass. The above will automatically find and use the new subclass, which makes it good generic code (IMHO). And you can always put the pre-loads in a separate apache startup file anyway. -- Josh I. # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
[cgiapp] Re: Model design in C::A/Titanium
Are you running under normal CGI (do people still do that?). Yes. Some projects are small, use lightweight frameworks and templating systems, like CGI::App and HTML::Template, and don't get a ton of traffic. There is no reason to add the extra complication of persistent memory, or give these applications dedicated, persistent memory when they don't need it. Most of the projects I've deployed have been in CGI. (The rest in mod_perl). Mark -- . . . . . . . . . . . . . . . . . . . . . . . . . . . Mark StosbergPrincipal Developer [EMAIL PROTECTED] Summersault, LLC 765-939-9301 ext 202 database driven websites . . . . . http://www.summersault.com/ . . . . . . . . # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Re: Model design in C::A/Titanium
Richard Jones wrote: Hi Mark, Appreciate the reply. Mark Stosberg wrote: And one other suggestion: If you find you are doing this a lot: $c-param('model') Go ahead and make a simple shortcut method for it, so you can just say $c-model; Did you have something like this in mind: sub model { my $c = shift; return $c-param('model'); } Seems a bit, erm, 'trivial' ? That may be trivial today, but it gives you the freedom to make it much smarter, without having to change the calling code. You can't do that if you have $c-param('model') littered around the code base. rhesa # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] Re: Model design in C::A/Titanium
On Tue, Sep 23, 2008 at 5:45 PM, Richard Jones [EMAIL PROTECTED]wrote: Rhesa Rozendaal wrote: Did you have something like this in mind: sub model { my $c = shift; return $c-param('model'); } Seems a bit, erm, 'trivial' ? That may be trivial today, but it gives you the freedom to make it much smarter, without having to change the calling code. You can't do that if you have $c-param('model') littered around the code base. Right. Thanks for that - I was really just checking I'd interpreted Marks suggestion correctly. Actually I quite like the idea of grouping associated db-related method calls into WebApp::Model::Foo, WebApp::Model::Bar, etc, but not sure what is the best way to get them into the call to $c-model, so I can call them as $c-model('Foo')-get_stuff, $c-model('Bar')-update_stuff, etc. Pointers appreciated. lots of options for that, but this should cover the basic concept: sub model { my $c = shift; my $subclass = shift or die Required attribute missing; return $c-param(model::$subclass) if $c-param(model::$subclass); my $obj = new MyApp::DB::$subclass-new(); $c-param(model::$subclass, $obj); return $obj; } (most of the code from my previous post could also fit in here to avoid infinite loops, and to automatically require the correct classes - tweak as needed). # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
[cgiapp] RFC: declarative run modes (inspired by Method::Signatures)
Hi all, Thanks to Schwern's recent work on Method::Signatures, something clicked with me. It looks like Devel::Declare (dangerous as it is) allows for very pretty syntactic sugar, without relying on source filters. Inspired by Method::Signatures, I've been hacking on something specifically for use with cgiapp. package My::App; use base 'CGI::Application'; use Runmode::Declare; # proof-of-concept name startmode hello { Hello } runmode world { $self-hello . , World! } Most of the time, you won't need to call setup() anymore, because this takes care of setting the start/run modes for you. Obviously, it checks that you only have one startmode. And it all works fine with inheritance too. Using signatures: # script?rm=invite;party_id=42;names=larry;names=guido;names=matz runmode invite($party_id, @names) { my $party = party_from_id($party_id); $party-add_invitees(@names); } # somewhere else: $self-invite( 36, qw(me myself I) ); This pulls the named parameters from @_, or $self-query-param, or $self-param. Rename the methods (probably silly, but it was easy and fun to try): use Runmode::Declare runmode = 'screen', startmode = 'splash'; splash hello { Hello } screen world { World! } Is anyone interested in seeing this on CPAN? Or should I just keep this to myself, and report back to the asylum? :-) rhesa # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] RFC: declarative run modes (inspired by Method::Signatures)
I was thinking that it would be cool just to use Method::Signatures with CGI::App, and I was also considering giving CAP::AutoRunmode a whirl, but this. this is cool enough to wait for ;) Does this play nicely with CAP::Authen and CAP::Authz? (ie, will anything requiring sub attributes still work?) Don't forget to make an errormode declaration either. Great idea Jason On Sep 23, 2008, at 6:21 PM, Rhesa Rozendaal wrote: Hi all, Thanks to Schwern's recent work on Method::Signatures, something clicked with me. It looks like Devel::Declare (dangerous as it is) allows for very pretty syntactic sugar, without relying on source filters. Inspired by Method::Signatures, I've been hacking on something specifically for use with cgiapp. package My::App; use base 'CGI::Application'; use Runmode::Declare; # proof-of-concept name startmode hello { Hello } runmode world { $self-hello . , World! } Most of the time, you won't need to call setup() anymore, because this takes care of setting the start/run modes for you. Obviously, it checks that you only have one startmode. And it all works fine with inheritance too. Using signatures: # script?rm=invite;party_id=42;names=larry;names=guido;names=matz runmode invite($party_id, @names) { my $party = party_from_id($party_id); $party-add_invitees(@names); } # somewhere else: $self-invite( 36, qw(me myself I) ); This pulls the named parameters from @_, or $self-query-param, or $self-param. Rename the methods (probably silly, but it was easy and fun to try): use Runmode::Declare runmode = 'screen', startmode = 'splash'; splash hello { Hello } screen world { World! } Is anyone interested in seeing this on CPAN? Or should I just keep this to myself, and report back to the asylum? :-) rhesa # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## #### # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
Re: [cgiapp] RFC: declarative run modes (inspired by Method::Signatures)
Jason A. Crome wrote: I was thinking that it would be cool just to use Method::Signatures with CGI::App, and I was also considering giving CAP::AutoRunmode a whirl, but this. this is cool enough to wait for ;) We're using AutoRunmode at work, and it rocks. It's even slightly better because you only need to add the use CAP::AutoRunmode line in your base class, and it'll work across all your subclasses. That's not the case with Method::Signatures (because the method keyword isn't a method itself). Does this play nicely with CAP::Authen and CAP::Authz? (ie, will anything requiring sub attributes still work?) No, it doesn't support attributes (yet): http://use.perl.org/~schwern/journal/37506 I actually asked the same question there, because I was hoping to be able to do: method foo :Runmode {} using AutoRunmode, but that didn't work. So I rolled my own :) Don't forget to make an errormode declaration either. Right. I'll do that. Great idea Thanks :) Jason # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
[cgiapp] Re: RFC: declarative run modes (inspired by Method::Signatures)
Inspired by Method::Signatures, I've been hacking on something specifically for use with cgiapp. package My::App; use base 'CGI::Application'; use Runmode::Declare; # proof-of-concept name startmode hello { Hello } runmode world { $self-hello . , World! } Most of the time, you won't need to call setup() anymore, because this takes care of setting the start/run modes for you. Obviously, it checks that you only have one startmode. And it all works fine with inheritance too. Using signatures: # script?rm=invite;party_id=42;names=larry;names=guido;names=matz runmode invite($party_id, @names) { my $party = party_from_id($party_id); $party-add_invitees(@names); } # somewhere else: $self-invite( 36, qw(me myself I) ); This pulls the named parameters from @_, or $self-query-param, or $self-param. Is anyone interested in seeing this on CPAN? Or should I just keep this to myself, and report back to the asylum? :-) I agree this code is attractive and elegant. (And I hope it also provides $self or $c) to run modes). I would like to see on CPAN. But having just looked at the scary guts of Devel::Declare, I don't expect to use it for real work soon. This just-filed bug report is the kind of thing I worry about it: http://rt.cpan.org/Public/Bug/Display.html?id=34417 Devel::Declare just became incompatible with the latest bleadperl, and because of the deep C magic, I wouldn't be able to patch it myself, as I would be for many other CPAN modules I use. Still, I hope features like this make their way towards stability, and I applaud and support your efforts to help towards that goal. Plus, it looks fun to try! A suggested alternate name: CGI::Application::Plugin::RunmodeDeclare; Mark -- http://mark.stosberg.com/ # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####
[cgiapp] CGI::Application::Server patches now peer reviewed and recommended (was: Re: ::Server vs ::Dispatch::Server: Worth having both?)
I realized that I left some of the original code commented out in the static-content.patch branch. I cleaned it up, tested it again, updated the copy at http://shrimp.alerce.com/cgiapp, and have attached the cleaner copy here for posterity. I've peer-reviewed all the patches here and recommend them all for inclusion, with no further modifications. George has already included additional automated tests for the key functionality. The only bit I see left to do is to update the 'Changes' file. ( The mega patch applies cleanly and easily to the last release ). For the CGI::Application::Server maintainers, would it be helpful if we published the patches to rt.cpan.org? Mark -- http://mark.stosberg.com/ # CGI::Application community mailing list #### ## To unsubscribe, or change your message delivery options, ## ## visit: http://www.erlbaum.net/mailman/listinfo/cgiapp## #### ## Web archive: http://www.erlbaum.net/pipermail/cgiapp/ ## ## Wiki: http://cgiapp.erlbaum.net/ ## ####