diff options
| author | Franck Cuny <franckcuny@gmail.com> | 2016-08-04 11:45:44 -0700 |
|---|---|---|
| committer | Franck Cuny <franckcuny@gmail.com> | 2016-08-04 11:45:44 -0700 |
| commit | 585b48b6a605cb71ef99dd767880e1b7ee5bf24e (patch) | |
| tree | c65377350d12bd1e62e0bdd58458c1044541c27b /posts/2009-11-09-modules-i-like-devel-declare.md | |
| parent | Use Bullet list for the index. (diff) | |
| parent | Mass convert all posts from markdown to org. (diff) | |
| download | lumberjaph-585b48b6a605cb71ef99dd767880e1b7ee5bf24e.tar.gz | |
Merge branch 'convert-to-org'
Diffstat (limited to '')
| -rw-r--r-- | posts/2009-11-09-modules-i-like-devel-declare.md | 242 |
1 files changed, 0 insertions, 242 deletions
diff --git a/posts/2009-11-09-modules-i-like-devel-declare.md b/posts/2009-11-09-modules-i-like-devel-declare.md deleted file mode 100644 index 67c6b14..0000000 --- a/posts/2009-11-09-modules-i-like-devel-declare.md +++ /dev/null @@ -1,242 +0,0 @@ -For [$work](http://linkfluence.net/), I've been working on a job queue system, using Moose, Catalyst (for a REST API) and DBIx::Class to store the jobs and some meta (yeah I know, there is not enough job queue system already, the world really needs a new one ...). - -Basicaly, I've got a XXX::Worker class that all the workers extends. This class provide methods for fetching job, add a new job, mark a job as fail, retry, ... - -The main loop in the XXX::Worker class look like this: - -```perl -# $context is a hashref with some info the job or method may need -while (1) { - my @jobs = $self->fetch_jobs(); - foreach my $job (@jobs) { - my $method = $job->{funcname}; - $self->$method($context, $job); - } - $self->wait; -} -``` - -and the worker look like this - -```perl -package MyWorker; -use Moose; -extends 'XXX::Worker'; - -sub foo { - my ($self, $context, $job) = @_; - - # do something - $self->job_success(); -} -``` - -But as I'm using Moose, I want to add more sugar to the syntax, so writing a new worker would be really more easy. - -Here comes [Devel::Declare](http://search.cpan.org/perldoc?Devel::Declare). - -The syntax I want for my worker is this one: - -```perl -work foo { - $self->logger->info("start to work on job"); - # do something with $job -}; - -work bar { - # do something with $job -}; - -success foo { - $self->logger->info("woot job success"); -}; - -fail bar { - $self->logger->info("ho noez this one failed"); -}; -``` - -Where with `work` I write the code the writer will execute on a task, `success`, a specific code that will be executed after a job is marked as successfull, and `fail` for when the job fail. - -I will show how to add the `work` keyword. I start by writing a new package: - -```perl -package XXX::Meta; - -use Moose; -use Moose::Exporter; -use Moose::Util::MetaRole; - -use Devel::Declare; - -use XXX::Meta::Class; -use XXX::Keyword::Work; - -Moose::Exporter->setup_import_methods(); - -sub init_meta { - my ($me, %options) = @_; - - my $for = $options{for_class}; - - XXX::Keyword::Work->install_methodhandler(into => $for,); - - Moose::Util::MetaRole::apply_metaclass_roles( - for_class => $for, - metaclass_roles => ['XXX::Meta::Class'], - ); - -} - -1; -``` - -The `init_meta` method is provided by Moose: (from the POD) - -> The `init_meta` method sets up the metaclass object for the class specified by `for_class`. This method injects a a meta accessor into the class so you can get at this object. It also sets the class's superclass to base_class, with Moose::Object as the default. - -So I inject into the class that will use XXX::Meta a new metaclass, XXX::Meta::Class. - -Let's take a look to XXX::Meta::Class: - -```perl -package XXX::Meta::Class; - -use Moose::Role; -use Moose::Meta::Class; -use MooseX::Types::Moose qw(Str ArrayRef ClassName Object); - -has work_metaclass => ( - is => 'ro', - isa => Object, - builder => '_build_metaclass', - lazy => 1, -); - -has 'local_work' => ( - traits => ['Array'], - is => 'ro', - isa => ArrayRef [Str], - required => 1, - default => sub { [] }, - auto_deref => 1, - handles => {'_add_work' => 'push',} -); - -sub _build_metaclass { - my $self = shift; - return Moose::Meta::Class->create_anon_class( - superclasses => [$self->method_metaclass], - cache => 1, - ); -} - -sub add_local_method { - my ($self, $method, $name, $code) = @_; - - my $method_name = $method . "_" . $name; - my $body = $self->work_metaclass->name->wrap( - $code, - original_body => $code, - name => $method_name, - package_name => $self->name, - ); - - my $method_add = "_add_" . $method; - $self->add_method($method_name, $body); - $self->$method_add($method_name); -} - -1; -``` - -Here I add to the `->meta` provided by Moose `local_work`, which is an array that contains all my `work` methods. So each time I do something like - -```perl -work foo {}; - -work bar {}; -``` - -in my worker, I add this method to **->meta->local_work**. - -And the class for our keyword work: - -```perl -package XXX::Keyword::Work; - -use strict; -use warnings; - -use Devel::Declare (); -use Sub::Name; - -use base 'Devel::Declare::Context::Simple'; - -sub install_methodhandler { - my $class = shift; - my %args = @_; - { - no strict 'refs'; - *{$args{into} . '::work'} = sub (&) { }; - } - - my $ctx = $class->new(%args); - Devel::Declare->setup_for( - $args{into}, - { work => { - const => sub { $ctx->parser(@_) } - }, - } - ); -} - -sub parser { - my $self = shift; - $self->init(@_); - - $self->skip_declarator; - my $name = $self->strip_name; - $self->strip_proto; - $self->strip_attrs; - - my $inject = $self->scope_injector_call(); - $self->inject_if_block( - $inject . " my (\$self, \$content, \$job) = \@_; "); - - my $pack = Devel::Declare::get_curstash_name; - Devel::Declare::shadow_sub( - "${pack}::work", - sub (&) { - my $work_method = shift; - $pack->meta->add_local_method('work', $name, $work_method); - } - ); - return; -} - -1; -``` - -The `install_methodhandler` add the `work` keyword, with a block of code. This code is sent to the parser, that will add more sugar. With the inject_if_block, I inject the following line - -```perl -my ($self, $context, $job) = @_; -``` - -as this will always be my 3 arguments for a work method. - -Now, for each new worker, I write something like this: - -```perl -package MyWorker; -use Moose; -extends 'XXX::Worker'; -use XXX::Meta; - -work foo {}; -``` - -The next step is too find the best way to reduce the first four lines to two. - -(some of this code is ripped from other modules that use Devel::Declare. The best way to learn what you can do with this module is to read code from other modules that use it) |
