Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Catalyst: Users

Dynamic model and controller creation

 

 

Catalyst users RSS feed   Index | Next | Previous | View Threaded


moseley at hank

Dec 31, 2011, 8:58 PM

Post #1 of 4 (239 views)
Permalink
Dynamic model and controller creation

I'm looking for examples of creating controllers and models via
configuration. As I'm doing it now (below) I'm having to add a number of
pretty small classes that are almost entirely configuration. It's pretty
simple as-is, but wondering if you might have a simpler approach (or doc
link) so that I can dynamically add actions and models either at runtime or
app config.


I have a very simple library I want provide a RESTful API for. The library
has a base class, say Inventory::Service that provides a simple API with
CRUD-like metods add() and get(). This base class in not used directly,
rather subclasses exists. For example, Inventory::Service::Nut and
Inventory::Service::Bolt inherit from the base class. There's potentially
a large number of these sub-classes.

So, I want a direct mapping "POST /service/bolt" to create and "GET
/service/bolt/<id>" to fetch the bolt, and likewise for the Nut and other
subclasses.

I've created a base class "MyApp::ControllerBase::Inventory" that has
generic methods thing_POST and thing_GET as per C::A::REST (that implement
the common ->add() and ->get() methods) and has an attribute "model_class".

Then I have Controller classes inheriting from this class that are simply
config setting the "model_class" attribute:

package MyApp::Controller::Service::Bolt;
use base 'MyApp::ControllerBase::Inventory;
__PACKAGE__->config( model_class => 'Inventory::Bolt',);
1;


Which means the base class just does $c->model( $self->model_class ) to get
at the Inventory::Bolt model.

Then, likewise, I have a Model base class that uses ACCEPT_CONTEXT to
return specific instances:

package MyApp::ModelBase::Inventory;
use Moose;
use namespace::autoclean;
extends 'Catalyst::Model';

has service => ( is => 'rw' ); # instance of model

has service_class => (
is => 'ro',
isa => 'Str',
required => 1,
);


# wrapping "setup_components" might be another approach
sub ACCEPT_CONTEXT {
my ( $self, $c, @args ) = @_;

my $service = $self->service;

unless ( $service ) {
my $service_class = $self->service_class;
Class::MOP::load_class( $service_class );

$service = $service_class->new;

# Save our instance
$self->service( $service );
}

return $service;
}


__PACKAGE__->meta->make_immutable;
1;


And then the specific model classes are simply:

package MyApp::Model::Inventory::Bolt;
use base 'MyApp::ModelBase::Inventory';
__PACKAGE__->config( service_class => 'Inventory::Service::Bolt' );
1;


Of course, after doing that a few times I'd rather use a programatic
solution.

So, what I'd like is to remove the need to create those stub Controller and
Model classes and instead use config (or maybe detection of available
subclasses at startup).


--
Bill Moseley
moseley [at] hank


bobtfish at bobtfish

Jan 2, 2012, 3:42 AM

Post #2 of 4 (229 views)
Permalink
Re: Dynamic model and controller creation [In reply to]

On 1 Jan 2012, at 04:58, Bill Moseley wrote:
> Then I have Controller classes inheriting from this class that are
> simply config setting the "model_class" attribute:
>
> package MyApp::Controller::Service::Bolt;
> use base 'MyApp::ControllerBase::Inventory;
> __PACKAGE__->config( model_class => 'Inventory::Bolt',);
> 1;
>
> Which means the base class just does $c->model( $self->model_class )
> to get at the Inventory::Bolt model.
>
> Then, likewise, I have a Model base class that uses ACCEPT_CONTEXT
> to return specific instances:
>
> package MyApp::ModelBase::Inventory;
> use Moose;
> use namespace::autoclean;
> extends 'Catalyst::Model';
>
> has service => ( is => 'rw' ); # instance of model
>
> has service_class => (
> is => 'ro',
> isa => 'Str',
> required => 1,
> );
>
>
> # wrapping "setup_components" might be another approach
> sub ACCEPT_CONTEXT {
> my ( $self, $c, @args ) = @_;
>
> my $service = $self->service;
>
> unless ( $service ) {
> my $service_class = $self->service_class;
> Class::MOP::load_class( $service_class );
>
> $service = $service_class->new;
>
> # Save our instance
> $self->service( $service );
> }
>
> return $service;
> }
>
>
> __PACKAGE__->meta->make_immutable;
> 1;
>
> And then the specific model classes are simply:
>
> package MyApp::Model::Inventory::Bolt;
> use base 'MyApp::ModelBase::Inventory';
> __PACKAGE__->config( service_class => 'Inventory::Service::Bolt' );
> 1;
>
> Of course, after doing that a few times I'd rather use a programatic
> solution.
>

Why are you messing around with ACCEPT_CONTEXT to do this?

Why not just use COMPONENT to return the service class once?

I.E., what does your current solution do which using
Catalyst::Model::Adaptor doesn't do (and that does the initialisation
of the service classes as startup, rather than at first request -
which is nicer for catching issues, and memory usage).

> So, what I'd like is to remove the need to create those stub
> Controller and Model classes and instead use config (or maybe
> detection of available subclasses at startup).

Have a look at this:

https://github.com/bobtfish/CatalystX-JobServer/blob/master/lib/CatalystX/JobServer/Web/Plugin/ModelsFromConfig.pm
#L7

Which is code to do exactly what you want - i.e. setup any models
there is config for, but which don't have an explicit class on-disk,
and then go on to setup controllers for each model setup.

Cheers
t0m


_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


moseley at hank

Jan 2, 2012, 8:30 AM

Post #3 of 4 (226 views)
Permalink
Re: Dynamic model and controller creation [In reply to]

On Mon, Jan 2, 2012 at 6:42 PM, Tomas Doran <bobtfish [at] bobtfish> wrote:

>
>
> Why are you messing around with ACCEPT_CONTEXT to do this?
>
> Why not just use COMPONENT to return the service class once?
>

Oh, I'm glad you brought that up.

Couple of reasons. Not shown in my sample code, but I'm setting an
attribute at construction time from another model instance.

That is,

sub ACCEPT_CONTEXT {
my ( $self, $c, @args ) = @_;

my $service = $self->service;

unless ( $service ) {
my $service_class = $self->service_class;
Class::MOP::load_class( $service_class );

$service = $service_class->new({
*connection => $c->model( 'Some::Client' ),*
});
...

Where Model::Foo does use Model::Adaptor. Maybe I could work with
setup_components to make sure the model instances are created in the
correct order (or user Bread::Board as discussed earlier).

I'm also using a few models that create a socket at instance time and are
not smart about forking. (In some cases I wrap those in a separate class,
though.)



https://github.com/bobtfish/**CatalystX-JobServer/blob/**
> master/lib/CatalystX/**JobServer/Web/Plugin/**ModelsFromConfig.pm#L7<https://github.com/bobtfish/CatalystX-JobServer/blob/master/lib/CatalystX/JobServer/Web/Plugin/ModelsFromConfig.pm#L7>
>
> Which is code to do exactly what you want - i.e. setup any models there is
> config for, but which don't have an explicit class on-disk, and then go on
> to setup controllers for each model setup.
>

Sure does! Thanks,


--
Bill Moseley
moseley [at] hank


bobtfish at bobtfish

Jan 2, 2012, 8:44 AM

Post #4 of 4 (229 views)
Permalink
Re: Dynamic model and controller creation [In reply to]

On 2 Jan 2012, at 16:30, Bill Moseley wrote:

>
>
> On Mon, Jan 2, 2012 at 6:42 PM, Tomas Doran <bobtfish [at] bobtfish>
> wrote:
>
>
> Why are you messing around with ACCEPT_CONTEXT to do this?
>
> Why not just use COMPONENT to return the service class once?
>
> Oh, I'm glad you brought that up.

<snip>

> Where Model::Foo does use Model::Adaptor. Maybe I could work with
> setup_components to make sure the model instances are created in the
> correct order (or user Bread::Board as discussed earlier).

Gotcha! Yes, that's then totally valid - and one of the things that
_should_ be simple, but isn't right now.

As you note - you can mess around with the model load order to ensure
things work - letting you have everything setup at startup time - if
that (the setup at startup time) is a significant advantage or not is
of course your call - but messing with the load order obviously isn't
nice, or extensible!

This is one of the things that is very much solved by the Bread::Board
branch..

> Which is code to do exactly what you want - i.e. setup any models
> there is config for, but which don't have an explicit class on-disk,
> and then go on to setup controllers for each model setup.
>
> Sure does! Thanks,

No worries :)

Cheers
t0m



_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/

Catalyst users RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.