Introduction to Jifty

Post on 01-Sep-2014

6.077 views 0 download

Tags:

description

A brief introduction to Jifty using a demo app that emulates Qublog

Transcript of Introduction to Jifty

Introduction to Jifty

Sterling Hanenkamp, PPW 2008

I Work for a Living

I Work for a Living

• Contract work / Billable Hours: How do I track them?

I Work for a Living

• Contract work / Billable Hours: How do I track them?

• Weekly Status Reports: What did I do?

I Work for a Living

• Contract work / Billable Hours: How do I track them?

• Weekly Status Reports: What did I do?

• Reliable Memory: Did I do that? How? When? Why?

I Work for a Living

• Contract work / Billable Hours: How do I track them?

• Weekly Status Reports: What did I do?

• Reliable Memory: Did I do that? How? When? Why?

• Task Lists: What should I do next?

Existing Solutions

Time Sheet

Existing Solutions

Planner

Existing Solutions

Scratch Paper

Existing Solutions

Existing Solutions

Hiveminder

Stikkit

Day Planner

iCal/Apple Mail

Sunbird

Lightning

Entourage

Things

OmniFocus

Qublog

• I don’t want to store this information in four places

Qublog

• I don’t want to store this information in four places

• I have a habit of writing down what I’m doing to focus

Qublog

• I don’t want to store this information in four places

• I have a habit of writing down what I’m doing to focus

• I needed something else to do with my lack of spare time

Qublog

• I don’t want to store this information in four places

• I have a habit of writing down what I’m doing to focus

• I needed something else to do with my lack of spare time

• I built Qublog (originally MyToDo and Kiln for a short while)

Qublog

• I don’t want to store this information in four places

• I have a habit of writing down what I’m doing to focus

• I needed something else to do with my lack of spare time

• I built Qublog (originally MyToDo and Kiln for a short while)

• It’s written using Jifty

Qublog

• I don’t want to store this information in four places

• I have a habit of writing down what I’m doing to focus

• I needed something else to do with my lack of spare time

• I built Qublog (originally MyToDo and Kiln for a short while)

• It’s written using Jifty

• We’re going to look at how I did it to explain Jifty

Qublog

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

What’s it look like?

Qublog

Qublog

• Provides a Journal for keeping thoughts

Qublog

• Provides a Journal for keeping thoughts

• Provides a To Do List for organizing tasks

Qublog

• Provides a Journal for keeping thoughts

• Provides a To Do List for organizing tasks

• Journal Comments are grouped into Timers that keep track of time spent

Qublog

• Provides a Journal for keeping thoughts

• Provides a To Do List for organizing tasks

• Journal Comments are grouped into Timers that keep track of time spent

• Timers are grouped into Entries to sum up work for reporting

Qublog

• Provides a Journal for keeping thoughts

• Provides a To Do List for organizing tasks

• Journal Comments are grouped into Timers that keep track of time spent

• Timers are grouped into Entries to sum up work for reporting

• Comments also connect with Tasks to keep track of what’s next

This is a talk about Jifty

This is a talk about Jifty

• Not Qublog

• Okay, so let’s see if we can build QublogMini in the time remaining...

Install Jifty

As with “that-other-framework,” the worst part is over when you

finish the install.

Install Jifty

• Method 1: Shipwright

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

Install Jifty

“notest” saves ~60 minutes according to Audrey

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

Install Jifty

“notest” saves ~60 minutes according to Audrey

“notest” will get you no support according to Jesse

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

• ActiveState installation on Windows: http://jifty.org/view/DownloadJifty

Install Jifty

• Method 1: Shipwright

svn co http://code.bestpractical.com/shipwright/jifty/ jifty-buildercd jifty-builder./bin/shipwright-builder # optionally --skip perl --skip-testcp -rvp /tmp/jifty-$random/jifty /usr/local/jiftyln -s /usr/local/jifty/bin/jifty /usr/local/bin/jifty

• Method 2: CPAN

perl -MCPAN -eshell # or just cpancpan> notest install Jifty

• ActiveState installation on Windows: http://jifty.org/view/DownloadJifty

• Jifty is in Debian unstable

Install Jifty

% jifty app --name QublogMini

% jifty app --name QublogMiniCreating new application QublogMiniCreating directory QublogMini/libCreating directory QublogMini/lib/QublogMiniCreating directory QublogMini/binCreating directory QublogMini/etcCreating directory QublogMini/docCreating directory QublogMini/logCreating directory QublogMini/varCreating directory QublogMini/var/masonCreating directory QublogMini/shareCreating directory QublogMini/share/poCreating directory QublogMini/share/webCreating directory QublogMini/share/web/templatesCreating directory QublogMini/share/web/staticCreating directory QublogMini/lib/QublogMini/ModelCreating directory QublogMini/lib/QublogMini/ActionCreating directory QublogMini/tCreating configuration file QublogMini/etc/config.yml

% cd QublogMini% find . -type f

% cd QublogMini% find . -type f./bin/jifty./etc/config.yml./Makefile.PL

% ./bin/jifty server

WARN - Application schema has no version in the database.WARN - Automatically creating your database.INFO - Generating SQL for application QublogMini...INFO - Using Jifty::Model::Session, as it appears to be new.INFO - Using Jifty::Model::Metadata, as it appears to be new.INFO - Set up version v0.0.1, jifty version 0.804080INFO - You can connect to your server at http://localhost:8888/

% ./bin/jifty server

WARN - Application schema has no version in the database.WARN - Automatically creating your database.INFO - Generating SQL for application QublogMini...INFO - Using Jifty::Model::Session, as it appears to be new.INFO - Using Jifty::Model::Metadata, as it appears to be new.INFO - Set up version v0.0.1, jifty version 0.804080INFO - You can connect to your server at http://localhost:8888/

% ./bin/jifty server

Take a Look

OMG! Ponies!!!

Not a very interesting journaling app... let’s fix it.

% ./bin/jifty model --name Comment

% ./bin/jifty model --name CommentWriting file t/00-model-Comment.tWriting file lib/QublogMini/Model/Comment.pm

% ./bin/jifty model --name CommentWriting file t/00-model-Comment.tWriting file lib/QublogMini/Model/Comment.pm

% vim lib/QublogMini/Model/Comment.pm

package QublogMini::Model::Comment;use Jifty::DBI::Schema;

use QublogMini::Record schema {

};

package QublogMini::Model::Comment;use Jifty::DBI::Schema;

use QublogMini::Record schema { column commented_on => type is 'datetime', label is 'Commented on', filters are qw( Jifty::DBI::Filter::DateTime );

column name => type is 'text', label is 'Name', render as 'textarea';};

% bin/jifty server

% bin/jifty server WARN - Application schema has no version in the database.WARN - Automatically creating your database.INFO - Generating SQL for application QublogMini...INFO - Using QublogMini::Model::Comment, as it appears to be new.INFO - Using Jifty::Model::Session, as it appears to be new.INFO - Using Jifty::Model::Metadata, as it appears to be new.INFO - Set up version v0.0.1, jifty version 0.804080INFO - You can connect to your server at http://localhost:8888/

% bin/jifty model --name Timer

% bin/jifty model --name TimerWriting file lib/QublogMini/Model/Timer.pmWriting file t/00-model-Timer.t

% bin/jifty model --name TimerWriting file lib/QublogMini/Model/Timer.pmWriting file t/00-model-Timer.t

% vim lib/QublogMini/Model/Timer.pm

package QublogMini::Model::Timer;use Jifty::DBI::Schema;

use QublogMini::Record schema {

};

package QublogMini::Model::Timer; use Jifty::DBI::Schema;

use QublogMini::Record schema { column started_on => label is 'Started on', filters are qw( Jifty::DBI::Filter::DateTime );

column stopped_on => label is 'Stopped on', filters are qw( Jifty::DBI::Filter::DateTime );

column comments => references QublogMini::Model::CommentCollection by 'timer';

};

sub since { '0.0.2' }

package QublogMini::Model::Comment;use Jifty::DBI::Schema;

use QublogMini::Record schema { column commented_on => type is 'datetime', label is 'Commented on', filters are qw( Jifty::DBI::Filter::DateTime ), ;

column name => type is 'text', label is 'Name', render as 'textarea', ;

};

package QublogMini::Model::Comment;use Jifty::DBI::Schema;

use QublogMini::Record schema { column commented_on => type is 'datetime', label is 'Commented on', filters are qw( Jifty::DBI::Filter::DateTime ), ;

column name => type is 'text', label is 'Name', render as 'textarea', ;

column timer => label is 'Timer', since '0.0.2', references QublogMini::Model::Timer;};

% bin/jifty server

% bin/jifty server WARN - Application schema version in database (v0.0.1) doesn't match application schema version (0.0.2)WARN - Automatically upgrading your database to match the current application schema.Jifty version 0.809130 up to date.Jifty::Plugin::LetMe version v0.0.1 up to date.Jifty::Plugin::SkeletonApp version v0.0.1 up to date.Jifty::Plugin::REST version v0.0.1 up to date.Jifty::Plugin::Halo version v0.0.1 up to date.Jifty::Plugin::ErrorTemplates version v0.0.1 up to date.Jifty::Plugin::OnlineDocs version v0.0.1 up to date.Jifty::Plugin::CompressedCSSandJS version v0.0.1 up to date.Jifty::Plugin::AdminUI version v0.0.1 up to date.INFO - Generating SQL to upgrade QublogMini v0.0.1 database to v0.0.2INFO - Upgrading through 0.0.2INFO - Running upgrade scriptINFO - Upgraded to version v0.0.2INFO - You can connect to your server at http://localhost:8888/

package QublogMini::Model::Timer;

# ...

sub name { my $self = shift; $self->started_on . ' to ' . $self->stopped_on}

% bin/jifty model --name Entry

% bin/jifty model --name EntryWriting file t/00-model-Entry.tWriting file lib/QublogMini/Model/Entry.pm

% bin/jifty model --name EntryWriting file t/00-model-Entry.tWriting file lib/QublogMini/Model/Entry.pm

% vim lib/QublogMini/Model/Entry.pm

package QublogMini::Model::Entry;use Jifty::DBI::Schema;

use QublogMini::Record schema {

};

package QublogMini::Model::Entry; use Jifty::DBI::Schema;

use QublogMini::Record schema { column name => type is 'text', label is 'Name', is mandatory;

column url => type is 'text', label is 'Name';

column timers => references QublogMini::Model::TimerCollection by 'entry';};

sub since { '0.0.3' }

package QublogMini::Model::Timer;use Jifty::DBI::Schema;

use QublogMini::Record schema { # ...

column entry => label is 'Entry', since '0.0.3', references QublogMini::Model::Entry;

# ...};

sub name { my $self = shift; my $name = ''; $name .= $self->entry->name . ': ' if $self->entry->id; $name .= $self->started_on . ' to ' . $self->stopped_on return $name;}

--- framework: AdminMode: 1 ApplicationClass: QublogMini ApplicationName: QublogMini ApplicationUUID: 1198420A-95A9-11DD-8ED4-3E4643B9151E ConfigFileVersion: 4 Database: AutoUpgrade: 1 CheckSchema: 1 Database: qublogmini Driver: SQLite Host: localhost Password: '' RecordBaseClass: Jifty::DBI::Record::Cachable User: '' Version: 0.0.2

--- framework: AdminMode: 1 ApplicationClass: QublogMini ApplicationName: QublogMini ApplicationUUID: 1198420A-95A9-11DD-8ED4-3E4643B9151E ConfigFileVersion: 4 Database: AutoUpgrade: 1 CheckSchema: 1 Database: qublogmini Driver: SQLite Host: localhost Password: '' RecordBaseClass: Jifty::DBI::Record::Cachable User: '' Version: 0.0.2

--- framework: AdminMode: 1 ApplicationClass: QublogMini ApplicationName: QublogMini ApplicationUUID: 1198420A-95A9-11DD-8ED4-3E4643B9151E ConfigFileVersion: 4 Database: AutoUpgrade: 1 CheckSchema: 1 Database: qublogmini Driver: SQLite Host: localhost Password: '' RecordBaseClass: Jifty::DBI::Record::Cachable User: '' Version: 0.0.3

% bin/jifty server

% bin/jifty server WARN - Application schema version in database (v0.0.2) doesn't match application schema version (0.0.3)WARN - Automatically upgrading your database to match the current application schema.Jifty version 0.809130 up to date.Jifty::Plugin::LetMe version v0.0.1 up to date.Jifty::Plugin::SkeletonApp version v0.0.1 up to date.Jifty::Plugin::REST version v0.0.1 up to date.Jifty::Plugin::Halo version v0.0.1 up to date.Jifty::Plugin::ErrorTemplates version v0.0.1 up to date.Jifty::Plugin::OnlineDocs version v0.0.1 up to date.Jifty::Plugin::CompressedCSSandJS version v0.0.1 up to date.Jifty::Plugin::AdminUI version v0.0.1 up to date.INFO - Generating SQL to upgrade QublogMini v0.0.2 database to v0.0.3INFO - Upgrading through 0.0.3INFO - Running upgrade scriptINFO - Upgraded to version v0.0.3INFO - You can connect to your server at http://localhost:8888/

Ponies are great!

Ponies are great!

But where’s the UI?

# Dispatcher maps URLs to Views% vim lib/QublogMini/Dispatcher.pm

package QublogMini::Dispatcher;use Jifty::Dispatcher -base;

package QublogMini::Dispatcher;use Jifty::Dispatcher -base;

on '/' => dispatch '/today';

package QublogMini::Dispatcher;use Jifty::Dispatcher -base;

on '/' => dispatch '/today';on '/today' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '>=', value => DateTime->today, entry_aggregator => 'AND', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '<', value => DateTime->today->add( days => 1 ), entry_aggregator => 'AND', ); set entries => $entries; show '/today';};

package QublogMini::View;use Jifty::View::Declare -base;

template '/today' => page { { title is "Today's Journal" }

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url }; }};

template '/today' => page { { title is "Today's Journal" }

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url };

show '/timers', $entry; }};

private template '/timers' => sub { my ($self, $entry) = @_;

my $timers = $entry->timers; while (my $timer = $timers->next) { show '/show_comment', $timer->stopped_on, _('Stopped timer.') if $timer->stopped_on; show '/comments', $timer; show '/show_comment', $timer->started_on, _('Started timer.'); }};

private template '/comments' => sub { my ($self, $timer) = @_;

my $comments = $timer->comments;

while (my $comment = $comments->next) { show '/show_comment', $comment->commented_on, $comment->name; }};

private template '/show_comment' => sub { my ($self, $time, $message) = @_; p { { class is 'time' } $time }; p { { class is 'comment' } $message };};

Fine. We already knew that. Where’s the

“interface”?

template '/today' => page { { title is "Today's Journal" }

form { render_region name => 'add_entry', path => '/add_entry_button', ; };

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url; };

show '/timers', $entry; }};

template '/add_entry_button' => sub { form_submit label => _('Add Entry'), onclick => { region => Jifty->web->current_region, replace_with => '/add_entry_form', }, ;};

template '/add_entry_form' => sub { my $entry_action = new_action class => 'CreateEntry'; render_action $entry_action, [ qw( name url ) ]; form_submit label => _('Add Entry'), onclick => { submit => $entry_action, region => Jifty->web->current_region, replace_with => '/add_entry_button', }, ;};

Where’d it go?

Where’d it go?

2 Problems

Where’d it go?

2 ProblemsWe need a timer

too.

Where’d it go?

2 ProblemsWe need a timer

too.We need to

redraw the journal.

package QublogMini::Model::Entry;use Jifty::DBI::Schema;

use DateTime;

# ...

# Add a timer when we add an entrysub after_create { my ($self, $id) = @_; return unless $$id;

my $timer = QublogMini::Model::Timer->new; $timer->create( entry => $$id, started_on => DateTime->now, );

return 1;}

template '/today' => page { { title is "Today's Journal" }

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url };

show '/timers', $entry; }};

template '/today' => page { { title is "Today's Journal" }

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url };

show '/timers', $entry; }};

# Replace /today with a regiontemplate '/today' => page { { title is "Today's Journal" }

render_region name => 'entries', path => '/entries', ;};

# Replace /today with a regiontemplate '/today' => page { { title is "Today's Journal" }

render_region name => 'entries', path => '/entries', ;};

# Add a new entries fragmenttemplate '/entries' => sub { form { render_region name => 'add_entry', path => '/add_entry_button', ; };

my $entries = get 'entries'; while (my $entry = $entries->next) { h2 { $entry->name };

p { hyperlink label => $entry->url, url => $entry->url; };

show '/timers', $entry; }};

on '/today' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '>=', value => DateTime->today, entry_aggregator => 'AND', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '<', value => DateTime->today->add( days => 1 ), entry_aggregator => 'AND', ); set entries => $entries;};

on '/today' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '>=', value => DateTime->today, entry_aggregator => 'AND', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '<', value => DateTime->today->add( days => 1 ), entry_aggregator => 'AND', ); set entries => $entries;};

on '/entries' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '>=', value => DateTime->today, entry_aggregator => 'AND', ); $entries->limit( alias => $timer_alias, column => 'started_on', operator => '<', value => DateTime->today->add( days => 1 ), entry_aggregator => 'AND', ); set entries => $entries;};

template '/add_entry_form' => sub { my $entry_action = new_action class => 'CreateEntry'; render_action $entry_action, [ qw( name url ) ]; form_submit label => _('Add Entry'), onclick => { submit => $entry_action, region => Jifty->web->current_region, replace_with => '/add_entry_button', }, ;};

template '/add_entry_form' => sub { my $entry_action = new_action class => 'CreateEntry'; render_action $entry_action, [ qw( name url ) ]; form_submit label => _('Add Entry'), onclick => { submit => $entry_action, region => Jifty->web->current_region, replace_with => '/add_entry_button', }, ;};

# Make our entry form update everything properlytemplate '/add_entry_form' => sub { my $entry_action = new_action class => 'CreateEntry'; render_action $entry_action, [ qw( name url ) ]; form_submit label => _('Add Entry'), onclick => [ { submit => $entry_action, }, { region => Jifty->web->current_region, replace_with => '/add_entry_button', }, { refresh => Jifty->web->current_region->parent, }, ], ;};

# Make our entry form update everything properlytemplate '/add_entry_form' => sub { my $entry_action = new_action class => 'CreateEntry'; render_action $entry_action, [ qw( name url ) ]; form_submit label => _('Add Entry'), onclick => [ { submit => $entry_action, }, { region => Jifty->web->current_region, replace_with => '/add_entry_button', }, { refresh => Jifty->web->current_region->parent, }, ], ;};

Bummer.Wrong Order.

Bummer.Wrong Order.

on '/entries' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', );

# ...

$entries->order_by({ alias => $timer_alias, column => 'started_on', order => 'DES', }); set entries => $entries;};

on '/entries' => run { my $entries = QublogMini::Model::EntryCollection->new; my $timer_alias = $entries->join( column1 => 'id', table2 => QublogMini::Model::Timer->table, column2 => 'entry', );

# ...

$entries->order_by({ alias => $timer_alias, column => 'started_on', order => 'DES', }); set entries => $entries;};

We have entries and timers, but what about comments?

package QublogMini::Model::Comment;use Jifty::DBI::Schema;

use DateTime;

# ...

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

$args->{commented_on} = DateTime->now;

return 1;}

template '/add_entry_button' => sub { form_submit label => _('Add Entry'), onclick => { region => Jifty->web->current_region, replace_with => '/add_entry_form', }, ; form_submit label => _('Add Comment'), onclick => { region => Jifty->web->current_region, replace_with => '/add_comment_form', }, ;};

template '/add_entry_button' => sub { form_submit label => _('Add Entry'), onclick => { region => Jifty->web->current_region, replace_with => '/add_entry_form', }, ; form_submit label => _('Add Comment'), onclick => { region => Jifty->web->current_region, replace_with => '/add_comment_form', }, ;};

template '/add_comment_form' => sub { my $timer = QublogMini::Model::Timer->new; $timer->load_current; # loads the latest running timer my $comment_action = new_action class => 'CreateComment'; render_action $comment_action, [ qw( name ) ]; form_submit label => _('Add Comment'), onclick => [ { submit => { action => $comment_action, arguments => { timer => $timer->id, }, }, }, { region => Jifty->web->current_region, replace_with => '/add_entry_button', }, { refresh => Jifty->web->current_region->parent, }, ], ; # ...

# ...

form_submit label => _('Cancel'), onclick => { region => Jifty->web->current_region, replace_with => '/add_entry_button', }, ;};

What about Validation

What about Validation

• Easy

What about Validation

• Easy

• Add a validate_XXX method to the model

What about Validation

• Easy

• Add a validate_XXX method to the model

• Let’s try it

sub validate_url { my ($self, $url) = @_;

if ($url !~ m[^https?://]) { return (0, 'That does not look like a URL.'); }

return 1;}

Canonicalization

Canonicalization

• You can also canonicalize something

Canonicalization

• You can also canonicalize something

• What the heck is that?

Canonicalization

• You can also canonicalize something

• What the heck is that?

• Rather than slap their hand, fix it for them

Canonicalization

• You can also canonicalize something

• What the heck is that?

• Rather than slap their hand, fix it for them

• For example, we could... (but won’t)...

sub canonicalize_url { my ($self, $url) = @_;

if ($url !~ m[^https?://]) { return 'http://'.$url; }

return $url;}

sub canonicalize_url { my ($self, $url) = @_;

if ($url !~ m[^https?://]) { return 'http://'.$url; }

return $url;}

Too naïve, I refuse to implement it!

sub canonicalize_url { my ($self, $url) = @_;

if ($url !~ m[^https?://]) { return 'http://'.$url; }

return $url;}

Too naïve, I refuse to implement it!

But it was the best I could come up with on

short notice.

Halos

Halos

Halos

Halos

I’m the view code for this “frame” in the

page!

Halos

Halos

Halos

Halos

View the HTML source, Luke.

Halos

Halos

Halos

Page Info

Page Info

Page Info

Page Info

Page Info

Page Info

Page Info

• Click on things to view info

Page Info

• Click on things to view info

• SQL Logging

Page Info

• Click on things to view info

• SQL Logging

• SQL Profiling

Page Info

• Click on things to view info

• SQL Logging

• SQL Profiling

• Template Profiling

Developer Mode

Developer Mode

• Module::Refresh

Developer Mode

• Module::Refresh

• Tracing with Log4Perl

Developer Mode

• Module::Refresh

• Tracing with Log4Perl

• Administration Console

Developer Mode

• Module::Refresh

• Tracing with Log4Perl

• Administration Console

• (actually, that’s Administrator Mode)

Deployment

Deployment

• Choice of web servers: FastCGI on Apache / Lighttpd, HTTP::Server::Simple, mod_perl support (a little weak at the moment)

Deployment

• Choice of web servers: FastCGI on Apache / Lighttpd, HTTP::Server::Simple, mod_perl support (a little weak at the moment)

• Choice of DB servers: SQLite (great for getting started/testing, but production is possible), PostgreSQL, MySQL, and other servers supported

Deployment

• Choice of web servers: FastCGI on Apache / Lighttpd, HTTP::Server::Simple, mod_perl support (a little weak at the moment)

• Choice of DB servers: SQLite (great for getting started/testing, but production is possible), PostgreSQL, MySQL, and other servers supported

• Use a fast static server to server CSS, JS, images, etc.

Deployment

• Choice of web servers: FastCGI on Apache / Lighttpd, HTTP::Server::Simple, mod_perl support (a little weak at the moment)

• Choice of DB servers: SQLite (great for getting started/testing, but production is possible), PostgreSQL, MySQL, and other servers supported

• Use a fast static server to server CSS, JS, images, etc.

• Use CSS::Squish and JS::Squish to help compress and cache things

Other Goodies

Other Goodies

• Several authentication plugins: typical email sign-up login, OpenID, Facebook, LDAP, CAS

• Additional CRUD view helpers I haven’t demonstrated

• Built-in REST interface

• Full I18N support built-in

• Several Plugins for lots of things: Comments, Single Page Apps, Client-Side Templating, Charting, Tab helpers

• Built-in support for Comet (server push) in addition to Ajax

Applications

Applications

• Hiveminder (http://hiveminder.com)

Applications

• Hiveminder (http://hiveminder.com)

• Wifty (http://jifty.org)

Applications

• Hiveminder (http://hiveminder.com)

• Wifty (http://jifty.org)

• Qublog (http://qublog.net)

Applications

• Hiveminder (http://hiveminder.com)

• Wifty (http://jifty.org)

• Qublog (http://qublog.net)

• Doxory (http://doxory.com)

Applications

• Hiveminder (http://hiveminder.com)

• Wifty (http://jifty.org)

• Qublog (http://qublog.net)

• Doxory (http://doxory.com)

• CommitBit (http://code.bestpractical.com/)

Applications

• Hiveminder (http://hiveminder.com)

• Wifty (http://jifty.org)

• Qublog (http://qublog.net)

• Doxory (http://doxory.com)

• CommitBit (http://code.bestpractical.com/)

• Ping / Chat / Clock (examples available with Jifty)

Jifty Resources

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

• Jifty SVN (http://svn.jifty.org/svn/jifty.org)

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

• Jifty SVN (http://svn.jifty.org/svn/jifty.org)

• Tutorial (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual/Tutorial.pod)

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

• Jifty SVN (http://svn.jifty.org/svn/jifty.org)

• Tutorial (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual/Tutorial.pod)

• Manual (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual.pod)

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

• Jifty SVN (http://svn.jifty.org/svn/jifty.org)

• Tutorial (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual/Tutorial.pod)

• Manual (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual.pod)

• Mailing List: jifty‐devel@lists.jifty.org

Jifty Resources

• CPAN (http://search.cpan.org/dist/Jifty/)

• Jifty Wiki (http://jifty.org)

• Jifty SVN (http://svn.jifty.org/svn/jifty.org)

• Tutorial (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual/Tutorial.pod)

• Manual (http://search.cpan.org/dist/Jifty/lib/Jifty/Manual.pod)

• Mailing List: jifty‐devel@lists.jifty.org

• IRC Channel: #jifty on irc.freenode.org

Qublog

• See http://qublog.net

• Sample code for QublogMini in SVN: http://qublog.net/svn/QublogMini

Thank you!