Showing posts with label perl module. Show all posts
Showing posts with label perl module. Show all posts

Tuesday, November 23, 2010

Intro to App::CLI

My first meet with App::CLI is in the summer of 2008, trying to write JiftyX::Fixtures. Without abundant PODs coming with this module, it took me almost one day to understand this module through trial and error. Since, I write this intro to reveal more power from this module; even I have contribute some code and DOCs to the project.

Actually, App::CLI is a really powerful module having central idea similar to the dispatcher in many web application frameworks such as Rails. When we are trying to create yet another handy CLI tool for our daily working, here is a recommended architecture to use App::CLI:

MyApp
-> The kernel of our new tool

MyApp::*
-> All other module forming the mechanism
-> of our new tool, e.g. MyApp::Config
-> for loading configuration

MyApp::Command
-> The dispatcher serving the invoking commands

MyApp::Command::*
-> All subcommands invoked by MyApp::Command

MyApp::Command::*::*
-> All subsubcommands invoked by specific subcommand

MyApp::Command::*::*::*
-> subsubsubcommands *god*

After deciding what command we need to implement, we only have two things should be done. The first is making MyApp::Command be able to dispatch. What we need to do is only add use base qw(App::CLI); into it.

Now, in MyApp.pm or our script, where we want to invoke command, we can just say
MyApp::Command->dispatch();
MyApp::Command would automatically use @ARGV to determine what (sub)command it should invoke and find its package to *require*. For example, if we type $ myapp list user --sort age in terminal, the @ARGV would be qw(list user --sort age), and MyApp::Command would require MyApp::Command::List::User, create its instance as $cmd, assign $cmd->{sort} as 'age', finally invoke $cmd->run_command().

So, The second thing, most of we need to effort, is to implement our (sub)commands. it's a bit like writing Controllers of Rails. If the URL matches /foo/bar/:id, the Action #list() of Controller Foo::Bar would handle the request, and @id would be assign value. If users type $ myapp list nickname --name /mark/ MyApp::Command::List::Nickname would handle the command and $instance->{name} would be assign string /mark/. For this case, we can write as below.

package MyApp::Command::List;
use base qw(App::CLI::Command);
use constant subcommands => qw(User Nickname);
use constant options => (
"h|help" => "help",
);

sub run {
my ($self, @args) = @_;
if ($self->{help}){
# output PODs when $ myapp list --help
}
# do something when user type like
# $ myapp list arg1 arg2 --opt1 --opt2 opt_arg2
# and arg1 doesn't equal to User or Nickname
}



package MyApp::Command::List::Nickname;
use base qw(App::CLI::Command);
use constant options => (
"name=s" => "name",
);

sub run {
my ($self, @args) = @_;
$name = $self->{name} #=> /mark/
# query the data base via condition /mark/
}

It completes. Note the we can specify all possible subcommands in constant subcommands of and give all possible options in constant options to all (sub)commands and just implement &run() the (sub)command would be done. And finally, note we can cascading subcommands infinitely. That is the core power of this module starting from v0.2.

Enjoy.

Monday, July 6, 2009

Data::Model

Data::Model is a new ORM created by yappo, Its usage style is similar to DataMapper and Jifty::DBI.

Different from most famous equivalent, Data::Model handles multi-database. In order to do that, every model was appointed its database and table name in addition to its schema specification.

Let's see a simple example:



This is a simplest model in Data::Model, Only two DDL in it.

The first, base_driver( $driver ) specify which database the model would connect to. $driver is a Data::Model::Driver::DBI object. Write those diffusion code in every model is really not perlish, so we extract all possible drivers to MyApp::DB::driver().



The second, pass the table name and schema to install_model(), this step is the same as above two ORM system. and then, all thing done. By the way, columns() is the best syntax sugar provided by Data::Model::Schema.

Wait... Because Data::Model is too young to do auto_migration, we simply write MyApp::DB::make_schema() to do that. Finally, we could do simple CRUD in our application. as below.



Though Data::Model is too young to have some important feature such as validator and hook, even has no ability to handle relation between two table. The prototype is really exciting.

[Chinese Version]

 

.

Tuesday, April 7, 2009

Class::Implant - No &import() !

Class::implant is a experimental little helper implemented when I developed Railsish with gugod. Manipulating mixin and inheritance outside package is its primary function, also with abilities to select methods which would import.

Original idea is inspire by the purpose: we want to import whole Helper methods into Controller which the Helper is named after. And it is should not be left to framework users to do it by themself. In Ruby, we just need to write
XxxController.send(:include, XxxHelper)
In Perl, it can also be written as
eval qq{ package XxxController; use XxxHelper; }
But a tedious &import() should be write in XxxHelper as follow.
sub import {
for (qw(hello world foo bar method...)) {
*{blah::$_} = \&$_
}
}
and edit the export methods list by hand.

Even use Exporter and edit @EXPORT and export by symbol :all, users still need to do follow by hand
use base Exporter;
our @EXPORT = qw(..);
Class::Implant provide simplified equivalent.

package main;
use Class::Implant;
implant "XxxHelper", { into => "XxxController" };

Everything done! All methods in XxxHelper is imported into XxxController!

If call use Class::Implant in other package, default import target would become it. That means, above code is the same as follow:

package XxxController;
use Class::Implant;
implant "XxxHelper";

In the release 0.02_01, two other options work. { inherit => 1 } make imported packages appear in @ISA of import target. { match => pattern } filter, and import methods conform to pattern.

For example,

package main;
use Class::Implant;
implant qw(Foo Bar), { into => "Cat", match => qr{h\w+} };

means import methods whose name start with h in Foo and Bar into Cat.

I also write UNIVERSAL::Implant. As its name, require once, and write as follow everywhere.

Cat->implant qw(Foo Bar), { match => qr{h\w+} }

Do the same thing as previous example. That means, caller package Cat assign { into => "Cat" }.



Chinese Version is @ Chupei.pm.org

 

Thursday, November 6, 2008

perl work with growl

Excerpt from
http://oreilly.com/catalog/9780596526740/toc.html
and
http://search.cpan.org/~cnandor/Mac-Growl-0.67/lib/Mac/Growl.pm

A quick method to post notification to Growl.


#!/usr/bin/env perl

use warnings;
use strict;

use Mac::Growl qw(:all);

Mac::Growl::RegisterNotifications(
# register your application in Growl before posting.
# just one time enough.
'growlalert', # app name
['alert'], # all notification
['alert'], # default notification
);

Mac::Growl::PostNotification(
# post notification to Growl
"growlalert", # app name
"alert", # notification type
"this is a title", # alert title
"this is a description", # alert content
);