|
NAMERose::DBx::Object::Renderer - Web UI Rendering for Rose::DB::Object SYNOPSIS use Rose::DBx::Object::Renderer;
use CGI;
my $query = new CGI;
print $query->header();
# Load all tables in the local MySQL database named 'company'
my $renderer = Rose::DBx::Object::Renderer->new(
config => {
db => {name => 'company', username => 'root', password => 'password'}
},
load => 1
);
# Render a form to add new employee
Company::Employee->render_as_form();
# Render an object as a form
my $e = Company::Employee->new(id => 1);
$e->load;
$e->render_as_form();
# Render the 'address' column as a link to a google map of the location
print $e->address_for_view();
# Render a table
Company::Employee::Manager->render_as_table();
# Render a table for all the employees who love 'Coding' with create, copy, edit, and delete access
Company::Employee::Manager->render_as_table(
get => {query => [hobby => 'Coding']}
order => ['first_name', 'email', 'address', 'phone'],
create => 1,
copy => 1,
edit => 1,
delete => 1,
searchable => ['first_name', 'address']
);
# Render a menu
my $menu = Company::Employee::Manager->render_as_menu(
order => ['Company::Employee', 'Company::Position'],
edit => 1,
delete => 1,
);
# Render a pie chart via Google Chart API
Company::Employee::Manager->render_as_chart(
type => 'pie',
values => ['Coding', 'Cooking'],
column => 'hobby',
);
# Render a bar chart
Company::Employee::Manager->render_as_chart(
type => 'bar',
title => 'The Employee Bar Chart',
description => 'A useful bar chart.',
columns => ['salary', 'tax'],
objects => [1, 2, 3],
options => {chco => 'ff6600,ffcc00'} # the color for each bar
);
DESCRIPTIONRose::DBx::Object::Renderer generates forms, tables, menus, and charts for Rose::DB::Object. The generated UIs encapsulate sensible web conventions as default behaviours, such as rendering email addresses as 'mailto' links and enforce appropriate validation in forms. These default behaviours are highly configurable. Rose::DBx::Object::Renderer uses CGI::FormBuilder to generate forms and the Google Chart API to render charts. Template::Toolkit is used for template processing, although UIs can be generated out of the box without using templates. RESTRICTIONS
METHODS"new"To instantiate a new Renderer object: my $renderer = Rose::DBx::Object::Renderer->new(config => {db => {name => 'company', username => 'root', password => 'root'}}, load => 1);
Since Renderer inherits from Rose::Object, the above line is equivalent to: my $renderer = Rose::DBx::Object::Renderer->new();
$renderer->config({db => {name => 'company', username => 'root', password => 'root'}});
$renderer->load();
"config"A Renderer instance inherits the default configurations in Renderer, which is accessible by: my $config = $renderer->config(); "config" accepts a hashref for configuring the Renderer object. "db" The "db" option is for configuring database related settings, for instance: $renderer->config({
db => {
name => 'product',
type => 'Pg', # defaulted to 'mysql'
host => '10.0.0.1',
port => '5432',
username => 'admin',
password => 'password',
tables_are_singular => 1, # defines table name conventions, defaulted to undef
table_prefix => 'app_', # specificies the prefix used in your table names if any, defaulted to undef
new_or_cached => 0, # whether to use Rose::DB's new_or_cached() method, defaulted to 1
check_class => 'Company::DB', # skip loading classes if the given class is already loaded (for persistent environments)
}
});
"template" The "template" option specifies the template toolkit "INCLUDE_PATH" and the base URL for static contents, such as javascript libraries or images: $renderer->config({
...
template => {
path => '../templates:../alternative', # TT INCLUDE_PATH, defaulted to 'templates'
url => '../images', # defaulted to 'templates'
options => {ABSOLUTE => 1, RELATIVE => 1, POST_CHOMP => 1} # defaulted to undef
},
});
"upload" Renderer needs a directory with write access to upload files. The "upload" option defines file upload related settings: $renderer->config({
...
upload => {
path => '../files', # the upload directory path, defaulted to 'uploads'
url => '../files', # the corresponding URL path, defaulted to 'uploads'
keep_old_files => 1, # defaulted to undef
},
});
"form" The "form" option defines the global default behaviours of "render_as_form": $renderer->config({
...
form => {
download_message => 'View File', # the name of the link for uploaded files, defaulted to 'View'
remove_files => 1, # allow users to remove uploaded files, default to undef
remove_message => 'Remove File', # the label of the checkbox for removing files, defaulted to 'Remove'
cancel => 'Back', # the name of the built-in 'Cancel' controller, defaulted to 'Cancel'
delimiter => ' ' # the delimiter for handling column with muliple values, defaulted to ','
action => '/app' # set form action, defaulted to undef
},
});
These options can be also passed to "render_as_form" directly to affect only the particular instance. "table" The "table" option defines the global default behaviours of "render_as_table": $renderer->config({
...
table => {
search_result_title => 'Looking for "[% q %]"?',
empty_message => 'No matching records.',
per_page => 25, # number of records per table, defaulted to 15
pages => 5, # the amount of page numbers in the table pagination, defaulted to 9
no_pagination => 1, # do not display pagination, defaulted to undef
or_filter => 1, # column filtering is joined by 'OR', defaulted to undef
delimiter => '/', # the delimiter for joining foreign objects in relationship columns, defaulted to ', '
keyword_delimiter => '\s+', # the delimiter for search keywords, defaulted to ','
like_operator => 'like', # only applicable to Postgres, defaulted to undef, i.e. render_as_table() uses 'ilike' for Postgres by default
form_options => ['order', 'template'], # options to be shared by other forms, defaulted to ['before', 'order', 'fields', 'template']
cascade => ['template_data', 'extra'], # options to be cascaded into all forms, defaulted to ['template_url', 'template_path', 'template_options', 'query', 'renderer_config', 'prepared']
},
});
These options can be also passed to "render_as_table" directly to affect only the particular instance. "menu" The "menu" option defines the global default behaviours of "render_as_menu": $renderer->config({
...
menu => {
cascade => ['template_data', 'extra'], # options to be cascaded into all tables, defaulted to ['create', 'edit', 'copy', 'delete', 'ajax', 'prepared', 'searchable', 'template_url', 'template_path', 'template_options', 'query', 'renderer_config']
},
});
These options can be also passed to "render_as_menu" directly to affect only the particular instance. "columns" Renderer has a built-in list of column definitions that encapsulate web conventions and behaviours. A column definition is a collection of column options. Column definitions are used by the rendering methods to generate web UIs. The built-in column definitions are stored inside "columns": my $config = $renderer->config();
print join (', ', keys %{$config->{columns}});
For example, the column definition for 'email' would be: ...
'email' => {
required => 1,
validate => 'EMAIL',
sortopts => 'LABELNAME',
comment => 'e.g. your.name@work.com',
format => {
for_view => sub {
my ($self, $column) = @_;
my $value = $self->$column;
return unless $value;
return qq(<a href="mailto:$value">$value</a>);}
}
},
...
We can also define new column definitions: $renderer->config({
...
columns => {
hobby => {
label => 'Your Favourite Hobby',
sortopts => 'LABELNAME',
required => 1,
options => ['Reading', 'Coding', 'Shopping']
}
},
});
All options in each column definition are "CGI::FormBuilder" field definitions, i.e. they are passed to CGI::FormBuilder directly, except for:
"misc" Other miscellaneous options are defined in "misc": my $custom_config = $renderer->config();
# Print the built-in doctype and CSS
print $custom_config->{misc}->{html_head};
# Change the object stringify delimiter
$custom_config->{misc}->{stringify_delimiter} = ', '; # defaulted to space
# Change time zone
$custom_config->{misc}->{time_zone} = 'Asia/Hong_Kong'; # defaulted to Australia/Sydney
# loaded the JS or CSS defined in $custom_config->{misc}->{js}, defaulted to the latest jQuery and jQuery UI hosted by Google
$custom_config->{misc}->{load_js} = 1; # defaulted to undef
$renderer->config($custom_config);
$renderer->load();
"load""load" uses Rose::DB::Object::Loader to load Rose::DB::Object classes dynamically. In order to take advantage of the built-in column definitions, "load" employs the following logic to auto-assign column definitions to database columns: Column name exists in the Renderer object's config?
Yes: Use that column definition.
No: Is the column a foreign key?
Yes: Apply the column options designed for foreign keys.
No: Column name matches (regex) a column definition name?
Yes: Use the first matching column definition.
No: Column's metadata object type exists as column definition name?
Yes: Use that column definition.
No: Create a custom column definition by aggregating database column information.
"load" accepts a hashref to pass parameters to the "new" and "make_classes" methods in Rose::DB::Object::Loader.
"load" returns an array of the loaded classes via the "make_classes" method in Rose::DB::Object::Loader. However, if the Rose::DB::Object "base_class" for the particular database already exists, which most likely happens in a persistent environment, "load" will simply skip the loading process and return undef. "load" generates CGI::FormBuilder validation subrefs to validate unique keys in forms. However, since column definitions are identified by column names, custom validation subrefs are required when there are multiple unique keys with the same table column name across different tables loaded via Renderer. RENDERING METHODSRendering methods are exported for Rose::DB::Object subclasses to generate web UIs. Rose::DB::Object subclasses generated by calling "load" will import the rendering methods automatically. However, we can also import the rendering methods directly into custom Rose::DB::Object subclasses: # For object classes package Company::Employee use Rose::DBx::Object::Renderer qw(:object); ... # For manager classes package Company::Employee::Manager use Rose::DBx::Object::Renderer qw(:manager); ... The following is a list of common parameters for the rendering methods:
"render_as_form""render_as_form" renders forms and handles their submission. # Render a form for creating a new object Company::Employee->render_as_form(); # Render a form for updating an existing object my $e = Company::Employee->new(id => 1); $e->load; $e->render_as_form();
"render_as_form" passes the following list of variables to a template: [% self %] - the calling object instance or class [% form %] - CGI::FormBuilder's form object [% field_order %] - the order of the form fields [% form_id %] - the form id [% form_submit %] - the form submit buttons with a custom 'Cancel' button [% title %] - the form title [% description %] - the form description [% doctype %] - the default html doctype [% html_head %] - the default html doctype and css [% no_head %] - the 'no_head' option [% cancel %] - the name of the 'Cancel' controller [% template_url %] - the default template URL [% extra %] - extra template variables "render_as_table""render_as_table" renders tables for CRUD operations.
Within a template, we can loop through objects using the "[% table %]" variable. Alternatively, we can use the "[% objects %]" variable. "render_as_table" passes the following list of variables to a template: [% table %] - the hash for the formatted table, see the sample template 'table.tt' [% objects %] - the raw objects returned by the 'get_object' method [% column_order %] - the order of the columns [% template_url %] - the default template URL [% table_id %] - the table id [% title %] - the table title [% description %] - the table description [% no_pagination %] - the 'no_pagination' option [% q %] - the keyword query for search [% query_string %] - a hash of URL encoded query strings [% query_hidden_fields %] - CGI queries converted into hidden fields; it is used by the keyword search form [% param_list %] - a list of CGI param names with the table prefix, e.g. the name of the keyword search box is [% param_list.q %] [% searchable %] - the 'searchable' option [% sort_by_column %] - the column to be sorted [% doctype %] - the default html doctype [% html_head %] - the default html doctype and css [% no_head %] - the 'no_head' option [% ajax %] - the ajax variable for checking whether the current CGI request is a ajax request [% url %] - the base url [% extra %] - extra template variables "render_as_menu""render_as_menu" generates a menu with the given list of classes and renders a table for the current class. We can have fine-grained control over each table within the menu. For example, we can alter the 'date_of_birth' field inside the 'create' form of the 'Company::Employee' table inside the menu: Company::Employee::Manager->render_as_menu (
create => 1,
edit => 1,
delete => 1,
copy => 1,
searchable => 1,
order => ['Company::Employee', 'Company::Position'],
items => {
'Company::Employee' => {
create => {
fields => {date_of_birth => {required => 1}}
}
},
'Company::Position' => {
title => 'Current Positions',
}
},
);
"render_as_menu" passes the following list of variables to a template: [% template_url %] - the default template URL [% menu_id %] - the menu id [% title %] - the menu title [% description %] - the menu description [% items %] - the hash for the menu items [% item_order %] - the order of the menu items [% current %] - the current menu item [% content %] - the output of the table [% hide %] - whether the menu should be hidden [% doctype %] - the default html doctype [% html_head %] - the default html doctype and css [% no_head %] - the 'no_head' option [% extra %] - extra template variables "render_as_chart""render_as_chart" renders pie, line, and vertical bar charts via the Google Chart API.
"render_as_chart" passes the following list of variables to a template: [% template_url %] - the default template URL [% chart_id %] - the chart id [% title %] - the chart title [% description %] - the chart description [% chart %] - the chart [% options %] - the 'options' hash [% doctype %] - the default html doctype [% html_head %] - the default html doctype and css [% no_head %] - the 'no_head' option [% extra %] - extra template variables OBJECT METHODSApart from the formatting methods injected by "load", there are several lesser-used object methods: "delete_with_file"This is a wrapper of the object's "delete" method to remove any uploaded files associated: $object->delete_with_file(); "stringify_me"Stringifies the object instance, e.g.: $object->first_name('John');
$object->last_name('Smith');
print $object->stringify_me();
# prints 'John Smith';
It also accept the "prepared" parameter. "stringify_class"Stringifies the class name: print Company::Employee->stringify_class(); # prints 'company_employee' "prepare_renderer"This is called by Renderer's "load" method internally. It generates the column formatting methods, column definition methods, as well as a "renderer_config" method for the Rose::DB::Object subclass. These generated methods are called by the rendering methods, e.g. "render_as_form". Thus, it would be useful for physical Rose::DB::Object subclasses to call "prepare_renderer" explicitly, prior to running the rendering methods, unless those relevant methods are handcrafted. "prepare_renderer" returns the renderer config hashref generated for the calling Rose::DB::Object subclass. my $config = Company::Employee->prepare_renderer();
$config->{upload}->{path} = '/path/for/file/uploads'; # set the path for file upload
print Company::Employee->email_for_view(); # call the 'for view' method of the email column
my $employee_config = Company::Employee->renderer_config(); # retrieve the complete config hashref
my $email_definition = Company::Employee->email_definition(); # retrieve just the column definition hashref for the email column
SAMPLE TEMPLATESThere are four sample templates: 'form.tt', 'table.tt', 'menu.tt', and 'chart.tt' in the 'templates' folder of the TAR archive. SEE ALSORose::DB::Object, CGI::FormBuilder, Template::Toolkit, <http://code.google.com/apis/chart/> AUTHORXufeng (Danny) Liang (danny.glue@gmail.com) COPYRIGHT & LICENSECopyright 2008-2010 Xufeng (Danny) Liang, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
|