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.