[Catalyst] Unit Testing
Kieren Diment
diment at gmail.com
Sun Apr 20 14:33:49 BST 2008
On 20 Apr 2008, at 21:26, Octavian Rasnita wrote:
> From: "Jonathan Rockway" <jon at jrock.us>
>> * On Sun, Apr 20 2008, Octavian Rasnita wrote:
>>
>>> Please tell us if we can find an example of putting the logic in the
>>> model than call those methods from the controller.
>>
>> http://www.jrock.us/fp2008/catalyst/start.html
>>
>> Using a DBIC-based model works the same way.
>
> Thank you Jonathan but I couldn't access any kind of helpful text
> on that page. I've look in the source code what link I need to
> access because it was hidden on the page. I accessed it, but I
> couldn't find a text in the following page neither.
> Please tell me at least if it is a text what I need to search for,
> or an image slide show (which unfortunately I can't see).
>
Yeah, there's an accessability issue there. Anyway:
perl -e 'for $i (1 .. 48) { system "lynx -dump -nolist http://
www.jrock.us/fp2008/catalyst/slide$i.html"}' > jrock.txt
gives me:
Catalyst Introducing Catalyst #1
Meta
* I'm Jonathan Rockway
* http://jrock.us/ * Exciting blog at http://blog.jrock.us/
Code and slides at http://jrock.us/
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #2
[1847190952.jpg]
Catalyst Book
* Hands-on tutorial
* Learn DBIx::Class, Template Toolkit, and Catalyst
http://www.packtpub.com/catalyst-perl-web-application/book
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #3
What is Catalyst?
* Web application environment
* You write the code, Catalyst figures out how to run it
* Development Server * FastCGI * mod_perl
Niceties * Debugging pages * Plugins * CPAN-able
components
MVC framework * Separate concerns
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #4
Hello
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #5
Demo the wiki
* Get the code now: http://jrock.us/KitiWiki-0.01.tar.gz
* Code on slides abridged for clarity; tarball has
everything. * Download now and follow along!
Untar, perl Makefile.PL, make installdeps
< < Previous | Index | Next >> ""
Not Found
The requested URL /fp2008/catalyst/slide6.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Not Found
The requested URL /fp2008/catalyst/slide7.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Not Found
The requested URL /fp2008/catalyst/slide8.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Not Found
The requested URL /fp2008/catalyst/slide9.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Catalyst Introducing Catalyst #10
Getting started
* catalyst.pl KitiWiki
* create directories for our application
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #11
Writing the Wiki Class
* In KitiWiki::Backend::Wiki (lib/KitiWiki/Backend/Wiki.pm)
package KitiWiki::Backend::Wiki;use Moose;
has 'storage_root' => ( is => 'ro', isa => Dir, ...);
sub page { my ($self, $page) = @_; my $pagedir = $self-
>storage_root->subdir($page); $self->_validate_page_name($page)
or confess "attempt to load invalid page '$page'"; return
KitiWiki::Backend::Wiki::Page->new( storage => $pagedir );}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #12
Page Class
* A bunch of functions, here's one:
sub new_revision { my ($self, $author, $text) = @_; my $date =
time; my $name = "$date-$author";
my $encoded = Encode::encode('utf8', $text);
write_file($self->storage->file($name)->stringify,
$encoded) or confess "Failed to write $name";
$self->_unshift_revision($name);
return KitiWiki::Backend::Wiki::Page::Revision->new
( storage => $self->storage, filename => $name,
author => $author, date => $date, );}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #13
More Page code
has 'revisions' => ( metaclass => 'Collection::Array',
is => 'ro', isa => 'ArrayRef[Str]', required
=> 0, default => sub { shift->_versions }, lazy =>
1, auto_deref => 1, provides => { unshift =>
'_unshift_revision',
},);
sub _versions { my $self = shift; my $dir = $self->storage;
opendir my $dh, $dir->stringify; my @files = readdir $dh;
closedir $dh;
return [ sort { _revision_sorter($a, $b) } map
{ $_->basename } grep { -f } map { $dir->file
($_) } @files ];}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #14
Revision Class
* Moosey struct
has 'storage' => ( is => 'ro', isa => Dir,
required => 1, coerce => 1,);
has 'filename' => ( is => 'ro', isa => 'Str',
required => 1,);
has 'author' => ( is => 'ro', isa => 'Str',
required => 1,);
subtype DateTime => as Class => where { blessed $_ && $_->isa
('DateTime') };
coerce DateTime => as Num => via { DateTime->from_epoch( epoch =>
$_ ) };
has 'date' => ( is => 'ro', isa => 'DateTime',
coerce => 1, required => 1,);
has 'content' => ( is => 'rw', isa => 'Str',
default => sub { shift->_read_content }, lazy => 1,);
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #15
More revision code
sub _read_content { my ($self) = @_; my $filename = $self->_file;
my $data = read_file($self->_file->stringify); return
Encode::decode('utf8', $data);}
< < Previous | Index | Next >> ""
Not Found
The requested URL /fp2008/catalyst/slide16.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Catalyst Introducing Catalyst #17
More tests
* t/wiki-validation.t
* Tests the page name validation rules:
use KitiWiki::Backend::Wiki;use Test::TableDriven ( validate =>
{ 'foo' => 0,
'FooBar' => 1, 'Foo..' =>
0, 'Foo/../../../etc/passwd' => 0,
'' => 0, 'A' =>
0, 'Ab' => 1, },);
sub validate { my $in = shift; return KitiWiki::Backend::Wiki-
>_validate_page_name($in) ? 1 : 0;}
runtests;
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #18
Catalyst
* Request-handling code lives in Controllers
* Controllers have actions:
package YourApp::Controller::Whatever;sub foo :Local { action goes
here } * Actions have URLs, here /whatever/foo. * Root
controller is /, not /root.
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #19
Setting up the Catalyst app
* We want TT templates, so we need a View that can render them
*$ perl script/kitiwiki_create.pl view TT TT * Creates lib/
KitiWiki/View/TT.pm
* tells Catalyst that $c->stash->{template} is a TT template
to render
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #20
Adding a template to the app
* Templates go in root * In root/hello.tt:
Hello, world! This is [% template | html %]!
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #21
Using that from Catalyst
* In KitiWiki::Controller::Root:
sub hello :Local { my ($self, $c) = @_; $c->stash->{template} =
'hello.tt';}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #22
Viewing the template
* Start the server
$ perl script/kitiwiki_server.pl -d * Browse to http://localhost:
3000/hello.
Hello, world! This is hello.tt! * [% template %] replaced with
$c->stash->{template}
* Everything in stash becomes TT variables * $c is just [%
c %]
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #23
Adding wiki functionality
* Clear out Root controller * Add a default action (for
when no URL matches)
sub default : Private { my ($self, $c) = @_; $c->res->status
(404); $c->stash->{template} = '404.tt';} * Private means
"internal to Catalyst"
< < Previous | Index | Next >> ""
Not Found
The requested URL /fp2008/catalyst/slide24.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Catalyst Introducing Catalyst #25
uri_for
* $c->uri_for('/path') creates links
* use this so you can move your app around
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #26
Adding a Wiki viewer
* New controller Wiki so wiki pages are at /wiki/... * New
model for getting at the Wiki
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #27
KitiWiki::Model::Wiki
package KitiWiki::Model::Wiki;use strict;use warnings;
use base 'Catalyst::Model::Adaptor';
__PACKAGE__->config( class => 'KitiWiki::Backend::Wiki' );
1;
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #28
Model
* Catalyst::Model glues app's data-related logic to
Catalyst * Biggest part of app
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #29
Why not use KitiWiki::Backend::Wiki directly?
* Configure wiki directory from Catalyst * kitiwiki.yml
---Model::Wiki: args: storage_root: "__path_to(wiki_pages)__"
* path_to() keeps path sane
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #30
Controller::Wiki
package KitiWiki::Controller::Wiki;use strict;use warnings;use base
'Catalyst::Controller';
sub wiki : Chained('/') PathPart('wiki') CaptureArgs(1) { my
($self, $c, $page_name) = @_; $c->log->debug("Acting on wiki page
$page_name"); $c->stash->{page_name} = $page_name; $c->stash->
{page} = $c->model('Wiki')->page($page_name);}
sub latest : Chained('wiki') PathPart Args(0) { my ($self, $c) =
@_; $c->stash->{revision} = $c->stash->{page}->latest; $c-
>stash->{formatted} = $self->format_revision($c); $c->stash->
{template} = 'view_wiki.tt';}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #31
Chained
* provides url like /wiki/<page name>/latest * easily add
new actions:
sub foo :Chained('wiki') PathPart Args(0) {} * now /wiki/<page
name>/foo is a URL * dispatch order, left-first
* CaptureArgs (capture /.../) * Args (endpoint, consume /...)
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #32
Templates for wiki viewing
* view_wiki.tt
[% WRAPPER page.tt title = page_name %]<h1>[% page_name | html %]</h1>
[% formatted | html_para %]<hr /><i>Last modified by [%
revision.author %] on [% revision.date %]</i><a href='[% c.uri_for("/
wiki/$page_name/create") %]'>Edit</a>
[% END %]
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #33
Another chained action
* view older revisions:
sub revision : Chained('wiki') PathPart Args(1) { my ($self, $c,
$revision) = @_; $c->stash->{revision} = $c->stash->{page}-
>get_revision($revision); $c->stash->{revision_number} =
$revision; $c->stash->{formatted} = $self->format_revision
($c); $c->stash->{template} = 'view_wiki.tt';} *
template reused * Args(1) captures revision number
* /wiki/PageName/revision/42
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #34
Theme of controller
* get data from model * munge it to make the view happy
* NOTHING ELSE
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #35
Writing wiki pages
* don't want random users writing wiki pages * need
authentication
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #36
Authentication
* needs session
< < Previous | Index | Next >> ""
Not Found
The requested URL /fp2008/catalyst/slide37.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Catalyst Introducing Catalyst #38
Sessions
* $c->session->{anything}
* lasts until session expiration
$c->flash->{anything} * lasts until end of next request
* good for "Logged in as ..." and a redirect
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #39
Authentication
* Plugin loaded above * Very simple configuration in
KitiWiki.pm
__PACKAGE__->config(authentication => { default_realm =>
'editors', realms => { editors => { credential
=> { class => 'Password',
password_field => 'password', password_type =>
'clear' }, store => { class =>
'Minimal', users => { jon =>
{ password =>
"test" }, }, }, }, },});
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #40
Authentication
* realms
* store * credentials
$c->user
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #41
Now we can edit
* Nope, need to login
< < Previous | Index | Next >> ""
Not Found
The requested URL /fp2008/catalyst/slide42.html was not found on
this server.
_________________________________________________________________
Apache/2.2.8 (Debian) mod_perl/2.0.3 Perl/v5.8.8 Server at
www.jrock.us Port 80
Catalyst Introducing Catalyst #43
Authentication
* login /login * logout /logout
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #44
Editing, finally!
* Using REST again. First start with base:
sub create : Chained('wiki') PathPart Args(0) ActionClass('REST')
{ my ($self, $c) = @_; unless($c->user_exists){ $c-
>session->{login_from} = $c->req->uri; $c->flash->{message} =
'You must log in to edit.'; $c->res->redirect($c->uri_for('/
login')); $c->detach; }} * If no user, redirect them to
login. * Login remembers where they came from.
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #45
Editing: show form
sub create_GET { my ($self, $c) = @_; $c->stash->{template} =
'wiki_edit_form.tt'; $c->stash->{existing_content} = eval
{ $c->stash->{page}->latest->content };}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #46
Editing: process form submission
sub create_POST { my ($self, $c) = @_; $c->stash->{page}-
>new_revision( $c->user->id, $c->req->params->
{new_content}, ); my $name = $c->stash->{page_name}; $c-
>response->redirect( $c->uri_for("/wiki/$name/latest") );}
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #47
That's how Catalyst works
* Flexible URI dispatching
* Actions that are dispatched to get data from a model, then
pass that data to the View.
< < Previous | Index | Next >> ""
Catalyst Introducing Catalyst #48
Questions?
* If there's time, let's add a "full history" page.
< < Previous | Index | Next >> ""
More information about the Catalyst
mailing list