Hi Richard - not sure mine is the most elegant
approach vis a vi plugins - but it is working, and it is fast :)
For production I'm running multiple instances of Dancer with
shared memcached for session management, all behind balance.
This is all on a 12-core OS X Mavericks box and barely makes a
blip on utilization.
1) I generated all the Rose::DB::Object classes from the
MySQL schema for the RDBO classes and Manager classes.
2) I wrote a few helper functions to convert DBO objects in
hash or array of hash suitable for Dancer templating (I'll
copy those in below). I still use HTML::Template over TT just
so that I can have non-coder HTML designers work with the
templates, so these data structures work for that template
engine. The result is a route that looks like this:
#----------------------------------------------------------------------
get
'/modal/points/:student_id' => sub {
#----------------------------------------------------------------------
my $student =
NDB::Student->new( id => params->{student_id}
)->load;
my $rv =
dbo2h($student);
template
"staff/modal/points", $rv, {layout => "simple"};
};
Here are a few other sample routes:
#----------------------------------------------------------------------
post '/studio' =>
sub {
#----------------------------------------------------------------------
my @fields = ( qw(
studio_name campus_id location session_rate
calendar_id ) );
dbo_add_update("NDB::Studio", { params }, \@fields );
redirect
"/admin/campus";
};
#----------------------------------------------------------------------
post '/studio/delete'
=> sub {
#----------------------------------------------------------------------
my $p =
NDB::Studio->new( id => params->{id} );
if (
params->{id} == params->{confirm_id} ) {
$p->delete;
}
redirect
"/admin/campus";
};
I also have some other helpers that follow RDBO
relationships and inflate the data structures for templating:
#----------------------------------------------------------------------
get '/campus' =>
sub {
#----------------------------------------------------------------------
my $campuses =
NDB::Campus::Manager->get_campus();
my $rv = dbom2aoh(
$campuses, { studios => 'studio_list', staff =>
'staff_list', } );
template
"admin/campus", { campus_list => $rv, add_navbar() }, {
layout => 'staff'};
};
3) The helper functions help glue Dancer and RDBO together:
=head2 dbo2h()
dbo2h - convert a DBO
object into a hash of key/value pairs.
my $hashref =
dbo2h( $dboobject, $date_format_string, $expandhash,
$prepend );
=cut
sub dbo2h {
my $row = shift;
my $date_format =
shift || '%Y-%m-%d %T';
my $expand =
shift;
my $prepend =
shift;
my $parent_prepend
= shift;
my %hash =
$row->column_value_pairs;
foreach my $key (
keys %$expand ) {
my $reln =
$expand->{$key};
if ( $prepend )
{
$hash{$reln
. '_' . $key} = ( $row->$reln ? $row->$reln->$key
: '' );
} else {
$hash{$key}
= ( $row->$reln ? $row->$reln->$key : '');
}
}
foreach my $key (
keys %hash ) {
if ( ref
$hash{$key} eq 'DateTime' ) {
$hash{$key}
= $hash{$key}->strftime($date_format);
}
elsif ( ref
$hash{$key} eq 'Time::Clock' ) {
$hash{$key}
= $hash{$key}->as_string;
}
$hash{$key} =~
s/\s+$//g;
if (
$parent_prepend ) {
$hash{$parent_prepend.'_'.$key} = delete $hash{$key};
}
}
return \%hash;
}
=head2 dbo2aoh()
dbo2aoh - convert a
DBO set of objects into an array of hash of key/value
pairs.
my $arrayref =
dbo2aoh( $dboarray, $date_format_string, $expandhash,
$prepend );
=cut
sub dbo2aoh {
my $rows = shift;
my $date_format =
shift;
my $expandhash =
shift;
my $prepend =
shift;
my $parent_prepend
= shift;
my @results;
foreach my $row (
@$rows ) {
push(@results,
dbo2h( $row, $date_format, $expandhash, $prepend,
$parent_prepend ) );
}
return \@results;
}
=head2
dbo_add_update()
dbo_add_update -
convert Dancer params into DBO add/update
my @fields = ( qw(
field1 field2 field3 ) );
@errors =
dbo_update( 'NDB::Lecture', { params }, \@fields, );
=cut
sub dbo_add_update {
my ( $class, $p,
$fields ) = @_;
my ( $obj, @errors
);
if ( $p->{id} )
{
$obj =
$class->new( id => $p->{id} );
push(@errors,
"No such record") unless $obj->load_speculative;
unless (
@errors ) {
foreach my
$field ( @$fields ) {
$obj->$field( $p->{$field} ) if exists
$p->{$field};
}
$obj->save;
}
} else {
my %columns;
foreach my
$field ( @$fields ) {
$columns{$field} = $p->{$field} if exists
$p->{$field};
}
$obj =
$class->new( %columns );
$obj->save;
}
return $obj;
}
=head2 dbom2aoh()
dbom2row - convert dbo to AOH and add DBOM
relationships as child AOH
$rv = dbo_update( $dbo, { relationship =>
'hash_key' }, $date_format_string, $expandhash, $prepend
);
=cut
sub dbom2aoh {
my ( $parent, $map, $dateformat, $expand, $prepend )
= @_;
my @rv;
foreach my $dbo ( @$parent ) {
my $row = dbo2h( $dbo, $dateformat, $expand,
$prepend );
foreach my $reln ( keys %$map ) {
my $children = $dbo->$reln;
$row->{ $map->{$reln} } = dbo2aoh(
$children, $dateformat );
}
push @rv, $row;
}
return \@rv;
}
Like I said in my initial post - I'm sure there are
refactor opportunities in my code - so please be kind :) I
tried to go for readability over conciseness so that I could
hand it off at some point.
Hope that helps.
Best,
Mike.