Moved a directory, broke RDGC

3 views
Skip to first unread message

mackler

unread,
Jan 10, 2011, 1:15:43 PM1/10/11
to CatalystX::CRUD
I'm trying to do something with RDGC, and I'm not sure whether I'm
doing something wrong, or whether RDGC just isn't made for what I'm
trying.

Some time ago I used RDGC to evaluate my postgresql database and to
create an RDGC application, with an RDBO-class, a Catalyst controller,
and an HTML form class for each table. The app works fine.

Later I copied all the RDBO files into another directory, and I've
been using those RDBO-derived classes to access the same database,
just without any Catalyst application, without using the controllers
or HTML forms.

So, now I've made a bunch of changes to the RDBO classes that I had
originally copied from the RDGC application, and now I want to use the
RDGC web font-end with the modified RDBO classes, in the new
directory. What I've tried to do is to copy everything except the
RDBO classes from the original to the new directory, and edit the
files to make it work. It almost works, but I'm having an issue.

A Rose::HTMLx::Form::Related::RelInfo object does not have a
controller. I tracked that down to the get_controller method() of
RelInfo, line 78 in version 0.22. $self->controller returns undef.
Also, I don't understand all the error output that Catalyst gives me,
but I see something called controller_class, and it's wrong.

My forms are subclassed from Rose::DBx::Garden::Catalyst::Form, and
they have a parameter called controller_prefix. I feel like that's
related to the problem I'm experiencing, but with some experimentation
I haven't been able to change that controller_prefix to get this
working.

In general my question is how do I make this work? More particularly
I'm interested in knowing how the directory structure where the
Catalyst Controllers are located has to be related to the one where
the RDBO class files are. My point is that in the future I want to be
able to make a second Catalyst application to access the same
database, using the same RDBO classes, but with custom controllers and
forms that I make from scratch. Therefore I want to keep such new
controllers separate from the RDGC-created controllers, and both
separate from the RDBO classes, as much a possible.

Does this make any sense? How I can lay out my directories to enable
two Catalyst applications, one of which is RDGC to use the same set of
RDBO classes? What is controller_prefix and how does it fit in?

Thank you very very much.

Adam Mackler

Peter Karman

unread,
Jan 10, 2011, 11:28:34 PM1/10/11
to CatalystX::CRUD


On Jan 10, 12:15 pm, mackler <adammack...@gmail.com> wrote:
>
> Later I copied all the RDBO files into another directory, and I've
> been using those RDBO-derived classes to access the same database,
> just without any Catalyst application, without using the controllers
> or HTML forms.

You're on the right track. It's a great idea to re-use the generated
RDBO classes outside the web app. Most of my apps do not have any RDBO
classes within the web app itself; I just alter the Perl @INC path to
my shared lib location and pull them in.

>
> So, now I've made a bunch of changes to the RDBO classes that I had
> originally copied from the RDGC application, and now I want to use the
> RDGC web font-end with the modified RDBO classes, in the new
> directory.  What I've tried to do is to copy everything except the
> RDBO classes from the original to the new directory, and edit the
> files to make it work.  It almost works, but I'm having an issue.

I would recommend against copying files back and forth. Just have one
set of .pm files, and then alter the @INC path to find them. Read up
on @INC in the Perl documentation. I tend to use either PERL5LIB env
var or 'use lib ...' at the top of my MyApp.pm catalyst class.


>
> A Rose::HTMLx::Form::Related::RelInfo object does not have a
> controller.  I tracked that down to the get_controller method() of
> RelInfo, line 78 in version 0.22.  $self->controller returns undef.
> Also, I don't understand all the error output that Catalyst gives me,
> but I see something called controller_class, and it's wrong.

If you're not using the Form-related classes outside the web app, you
can (should) leave those in the RDGC-generated app location, and just
move the RDBO-based files.

The error reporting can be very tricky to debug because IME errors can
be generated very far from where they actually occur. Part of that is
Catalyst, part of that is RDBO and part of that is RDGC. I typically
have the hardest time when my inheritance chain is broken somehow.


>
> My forms are subclassed from Rose::DBx::Garden::Catalyst::Form, and
> they have a parameter called controller_prefix.  I feel like that's
> related to the problem I'm experiencing, but with some experimentation
> I haven't been able to change that controller_prefix to get this
> working.

The controller_prefix is used when generating URIs and mapping Models
to Controllers, because the assumption is one Controller == one Form
== one Model.


>
> In general my question is how do I make this work?  More particularly
> I'm interested in knowing how the directory structure where the
> Catalyst Controllers are located has to be related to the one where
> the RDBO class files are.  My point is that in the future I want to be
> able to make a second Catalyst application to access the same
> database, using the same RDBO classes, but with custom controllers and
> forms that I make from scratch.  Therefore I want to keep such new
> controllers separate from the RDGC-created controllers, and both
> separate from the RDBO classes, as much a possible.

What you want to do is very possible. I usually set up my file
structure like:

code/
code/lib <-- RDBO classes in here
code/app-one <-- Catalyst app here, with all RDGC-generated code
except for the RDBO moved above
code/app-two <-- diff Catalyst app, with @INC altered to include
'code/lib'

I've had several Catalyst apps sharing the same code/lib before. It's
very do-able.

>
> Does this make any sense?  How I can lay out my directories to enable
> two Catalyst applications, one of which is RDGC to use the same set of
> RDBO classes?  What is controller_prefix and how does it fit in?
>

Start by getting all your RDBO classes into one tree, and not having
multiple copies. You have the right idea; you just need to master
@INC.

You can also share a single lib amongst apps using source control
features like svn:external (you *are* using src control, right?) and
that can have some advantages depending on your deployment strategy.
But a shared code/lib is the easiest to start with.

mackler

unread,
Jan 13, 2011, 9:58:51 PM1/13/11
to CatalystX::CRUD
Thank you for the suggestions, Peter. It makes sense in general, but
hopefully you can give me some more guidance.

So far, I've done as follows. If you recall I have two copies of my
RDBO-bases classes. One is that which was generated by RDGC, the
other is the copy of the first set, located outside the RDGC
application.

What I did was to edit each of the Catalyst Model files, changing the
"name" parameter in the configuration, replacing the reference to the
old generated RDBO files to the new copies in an unrelated directory.
Similarly I changed each of the Form files, changing the return value
of 'object_class' by making the same replacement.

After making the above changes, the problem I had was that the
application did not seem to be noticing the changes to the Form files,
in which I had changed the return values of the subroutine
'object_class'.

Next I removed the init_metadata subroutine from the base class that
the Forms inherit from and put that init_metadata in each of the Form
class files, setting the 'object_class' parameter that is passed to
the metadata_class->new method to the name of the new RDBO subclass
(i.e., the same value that the 'object_class' returns). And that seems
to work. I'm not sure if the 'object_class' parameter and the
'object_class' subroutine are both necessary or conflicting.

Anyway, the problem I'm having now regards relationships. There's a
TT template that came with CXC-YUI called filter_relationships.tt that
is apparently trying to pass the controller for related tables to
livegrid. It tries to get the controller from the related table's
Rose::HTMLx::Form::Related::RelInfo object. But that object doesn't
have the controller, and controller_class returns the wrong name. It
appears that the name it's returning is the value of
'controller_prefix' (passed to the metadata constructor) prepended to
the value of 'object_class' (with two colons between them). But since
'object_class' refers to the new copies of the RDBO in the other,
unrelated directory, the controller class name is wrong. Perhaps this
is related to the fact that the related table's
Rose::HTMLx::Form::Related::RelInfo is not returning a controller.

So, I'm not sure if that description is as clear as I'd like.
Basically my questions are first whether my first steps were correct:
specifically the changes to the Catalyst Models (changing the name
parameter) and the Form classes (moving the init_metadata definition
from the base class and changing the 'object_class' parameter)--and
then possibly deleting the 'object_class' method from the Form
classes.

And if that much is right, then how do I get the
Rose::HTMLx::Form::Related::RelInfo objects to return their
controllers? I took a guess and changed 'controller_prefix' to
'controller_class' and specified the controller's full class name
(didn't work) but maybe there's something like that, or perhaps a
parameter that specifies that part of the Controller name that goes
between the controller_prefix and everything that follows the last two
colons.

Yeah, those are pretty much my questions. Those and a general "how do
I make it work?" of course.

Thank you very much.
Adam

Peter Karman

unread,
Jan 13, 2011, 11:04:05 PM1/13/11
to cataly...@googlegroups.com
mackler wrote on 1/13/11 8:58 PM:

I have a hard time following all this narrative. I need code examples, the app
files preferably.

If it were me, I'd try and get my app back to the place it was before I started
moving files around and changing the param values. You might just try re-running
your rdgc.pl script to re-generate the app and then diff the newly generated
files against your existing ones.

Alternately, the debugging approach I take is to set a couple of environment
variables when you run the myapp_server.pl test server and see if stderr
messages might help.

% PERL_DEBUG=1 RHTMLO_DEBUG=1 perl script/myapp_server.pl

When you move your RDBO-based files out of the app directory, you shouldn't have
to change their 'package' (class) names. Unless of course you feel you need to.
But then you should understand what you're doing. My guess is that you're really
not grokking the relationship between @INC and the package names in each .pm
file. Until you do, you'll be shooting in the dark.

For example:

package MyApp::Foo::Bar;
1;

could be the contents of a file in:

myapp/lib/MyApp/Foo/Bar.pm

but it could just as easily be the contents of a file in:

somewhere/else/MyApp/Foo/Bar.pm

and if @INC was:

@INC = ('myapp/lib', 'somewhere/else');

then when perl tries to compile this:

use MyApp::Foo::Bar;

it's going to look first in 'myapp/lib' and stop, because it found a package
there by that name.

If you moved your .pm files, but you didn't change the package name, then your
@INC settings are likely affecting which files perl is loading and using.


--
Peter Karman . http://peknet.com/ . pe...@peknet.com

mackler

unread,
Jan 14, 2011, 2:54:28 PM1/14/11
to CatalystX::CRUD
Okay, I think I figured it out, or I figured something out.

For anyone who may be interested in the future (I'm thinking mostly of
myself actually), here's exactly how I took my automatically-created
Garden-Catalyst application and separated the database objects into
another directory so that more than one Catalyst application will be
able to use them.

In summary:
1) Copy the Rose DB files to the new directory, and change their
package names and base class (if necessary).
2) Copy the base class files, changing the package names (if
necessary) and the return value of 'schema', and 'garden_prefix'.
3) Change the Catalyst application so @ISA reflects the new location
of DB files. Alternately do this with an environment variable or
otherwise.
4) Change all the Catalyst Model files to know about the new copies
of the DB files by changing the name parameter of the configuration.
5) Change the HTM Form files, adding an init_metadata method to
correctly set controller_prefix and object_class.
5(a): Possibly remove the object_class method from the Form classes
(not sure about that).
6) Delete init_metadata from the Form classes' base class

Here is everything in detail:

The original app "called bk" is in one directory, let's call it the
garden directory. The database classes were under the garden
directory in garden/discharge. 'discharge' was the name of the
database I used when creating the app using
Rose::DBx::Garden::Catalyst. In that directory was one .pm file for
each table in the database.

Let's call the new location for DB files the new directory. Under the
new directory I copied the Rose DB class files into BK/DB. In other
words, in addition to putting the Rose DB object files in a completely
new application hierarchy, I also changed Garden/Discharge to BK/DB
within the new hierarchy.

Next I changed these new Rose DB files according to this example of
the Card.pm file. Before the change it began:

package Garden::Discharge::Card;
use strict;

use base qw(Garden::Discharge);

__PACKAGE__->meta->setup(

After the change it begins:

package BK::DB::Card;
use strict;

use base qw(BK::DB);

__PACKAGE__->meta->setup(


I also copied and modified the base class files for the DB classes.
Specifically, I copied Garden/Discharge.pm from the garden directory
to BK/DB.pm under the new directory. Changing it from this:

package Garden::Discharge;
use strict;
use base qw( Garden );

sub schema {'discharge'}

1;

to this:

package BK::DB;
use strict;
use base qw( BK::DB::Object );

sub schema {'db'}

1;

As well I copied the base class this class inherits from, and made a
change to garden_prefix described below.

After making the copies of the Rose DB classes that I want to
separate, the first thing I did was to add this line to the Catalyst
application class file, located under the garden directory as BK.pm.
This is the line I added:

use lib qw( /home/mackler/bk/lib );

Next, in order for the Catalyst application to look for the new copies
of the DB files rather than the old ones, I changed all the Model (as
in Model-View-Controller) files. So, for example, I have a table
called cards. Because of that, RDGC created a Model called Cards
located under the garden directory in Garden/Discharge/Model/Card.pm.
Originally it looked like this:

package BK::Model::RDGC::Discharge::Card;
use strict;
use base qw( BK::Base::Model::RDBO );
use MRO::Compat;
use mro 'c3';

__PACKAGE__->config(
name => 'Garden::Discharge::Card',
page_size => 50,
);

1;

I charged the line setting name to:

name => 'BK::DB::Card',

I made that same change in each Model file all at once using sed.

The next step was to change the Rose::HTML::Form class files. There
is one for each table under the garden directory in Garden/Discharge/
<table_name>/Form.pm. So for example, the form for the cards table is
in Garden/Discharge/Card/Form.pm. Before the change it began as this:

package Garden::Discharge::Card::Form;
use strict;
use base qw( Garden::Form );

sub object_class {'Garden::Discharge::Card'}

sub init_with_card {


After the change it began as this:

package Garden::Discharge::Card::Form;
use strict;
use base qw( Garden::Form );

sub init_metadata {
my $self = shift;
return $self->metadata_class->new(
form => $self,
controller_prefix => 'RDGC::Discharge',
object_class => 'BK::DB::Card',
);
}

sub object_class {'BK::DB::Card'}

sub init_with_card {

So there's three changes there. (1) One is the modification of the
return value of the object_class subroutine. The others are the
insertion of the init_metadata subroutine and (2) the addition of an
'object_class' parameter, and (3) a change to the controller_prefix
parameter. The init_metadata subroutine had been located in the base
class, called Garden/Form.pm, but originally it had looked like this:

sub init_metadata {
my $self = shift;
return $self->metadata_class->new(
form => $self,
controller_prefix => 'RDGC',
);
}

I deleted it completely from the base class, since now each Form has
it's own version with the correct controller_prefix and object_class
parameters.

Note: I'm not sure whether the 'object_class' subroutine is necessary
in the Form class file anymore, since init_metadata now passes an
object_class parameter to the metadata_class constructor. For now
I've left it in.


Next, and finally, I made a change to the base class of the copied
Rose DB files in the new directory. Each of those DB classes inherits
(indirectly) from BK::DB::Object, located under the new directory in
BK/DB/Object.pm. BK/DB/Object.pm has this line.

use base qw( Rose::DBx::Garden::Catalyst::Object );

Rose::DBx::Garden::Catalyst::Object is, as the docs say, "a subclass
of Rose::DB::Object for using with YUI, RHTMLO and CatalystX::CRUD."
So even though my new DB files are no longer in the same directory as
the Garden application, I'm guessing I need to be subclassing RDGC-
Object rather than RDBO to keep this working.

In any event, there in my BK::DB::Object base class file (BK/DB/
Object.pm) I changed the garden_prefix subroutine to this:

sub garden_prefix {'BK::DB'}

I'm pretty sure that's all I did, and so far it seems to be working.
Whew! Thank you Peter for writing this. I was afraid that I wouldn't
be able to do what I wanted, but it turned out just to be a matter of
configuration.
Reply all
Reply to author
Forward
0 new messages