Zydeco::Lite - Zydeco without any magic
use strict; use warnings; use Zydeco::Lite; app "Local::MyApp" => sub { role "Greeting" => sub { method "greeting" => sub { return "Hello"; }; }; role generator "Location" => [ "Str" ] => sub { my ( $gen, $arg ) = @_; method "location" => sub { return $arg; }; }; class "Hello::World" => sub { with "Greeting"; with "Location" => [ "world" ]; method "do_it" => [] => sub { my $self = shift; print $self->greeting, " ", $self->location, "\n"; }; }; }; my $obj = "Local::MyApp""->new_hello_world; $obj->do_it();
Zydeco::Lite is a Zydeco-like module, but without using any parsing tricks. Zydeco requires Perl 5.14 or above, but Zydeco::Lite will run on any version of Perl since 5.8.8.
It's intended to be a happy medium between Zydeco and MooX::Press.
Apps:
app "MyApp" => sub { # definition };
Anonymous apps:
my $app = app sub { # definition }; app \(my $app) => sub { # definition };
As of Zydeco::Lite 0.69, classes and roles no longer need to be defined within an app block, but bundling them into an app block has the advantage that the app is able to define all its classes and roles together, cross-referencing them, and setting them up in a sensible order. (Which becomes important if you define a role after defining a class that consumes it.)
app
Classes:
class "MyClass" => sub { # definition };
Anonymous classes:
my $class = class sub { # definition }; my $obj = $class->new(); class \(my $class) => sub { # definition }; my $obj = $class->new();
Class generators:
class generator "MyGen" => sub { my ( $gen, @args ) = ( shift, @_ ); # definition }; my $class = $app->generate_mygen( @args ); my $obj = $class->new(); class generator "MyGen" => [ @signature ] => sub { my ( $gen, @args ) = ( shift, @_ ); # definition };
Anonymous class generators:
my $gen = class generator sub { my ( $gen, @args ) = ( shift, @_ ); # definition }; my $class = $gen->generate_package( @args ); my $obj = $class->new(); class generator \(my $gen) => sub { my ( $gen, @args ) = ( shift, @_ ); # definition }; my $class = $gen->generate_package( @args ); my $obj = $class->new();
Roles, interfaces, and abstract classes work the same as classes, but use keywords role, interface, and abstract_class.
role
interface
abstract_class
Inheritance:
class "Base" => sub { }; class "Derived" => sub { extends "Base"; };
Inheritance using nested classes:
class "Base" => sub { ...; class "Derived" => sub { ...; }; };
Inheriting from a generated class:
class generator "Base" => sub { my ( $gen, @args ) = ( shift, @_ ); ...; }; class "Derived" => sub { extends "Base" => [ @args ]; };
Composition:
role "Named" => sub { requires "name"; }; class "Thing" => sub { with "Named"; has "name" => (); };
Composing an anonymous role:
class "Thing" => sub { with role sub { requires "name"; }; has "name" => (); };
Composing a generated role:
role generator "Thingy" => sub { my ( $gen, @args ) = ( shift, @_ ); ...; }; class "Derived" => sub { with "Thingy" => [ @args ]; };
Class version:
class "Foo" => sub { version "1.000"; }; class "Foo" => ( version => "1.0" ) => sub { ...; };
Class authority:
class "Foo" => sub { authority "cpan:TOBYINK"; }; class "Foo" => ( version => "1.0", authority => "cpan:TOBYINK" ) => sub { ...; };
Using non-Moo toolkits:
class "Foo" => sub { toolkit "Mouse"; }; class "Bat" => sub { toolkit "Moose" => ( "StrictConstructor" ); };
The version, authority, and toolkit keywords can be used within app, class, role, interface, or abstract_class definitions.
version
authority
toolkit
class
Attributes:
has "myattr" => ( ... ); has [ "myattr1", "myattr2" ] => ( ... );
Private attributes:
has "myattr" => ( is => "private", ..., accessor => \(my $accessor) );
Methods:
method "mymeth" => sub { my ( $self, @args ) = ( shift, @_ ); ...; };
Methods with positional signatures:
method "mymeth" => [ 'Num', 'Str' ] => sub { my ( $self, $age, $name ) = ( shift, @_ ); ...; };
Methods with named signatures:
method "mymeth" => [ age => 'Num', name => 'Str' ] => ( named => 1 ) => sub { my ( $self, $args ) = ( shift, @_ ); ...; };
Anonymous methods:
my $mymeth = method sub { my ( $self, @args ) = ( shift, @_ ); ...; } method \(my $mymeth) => sub { my ( $self, @args ) = ( shift, @_ ); ...; }
Anonymous methods may have signatures.
Required methods in roles:
requires "method1", "method2"; requires "method3";
Method modifiers:
before "somemethod" => sub { my ( $self, @args ) = ( shift, @_ ); ...; }; after [ "method1", "method2"] => sub { my ( $self, @args ) = ( shift, @_ ); ...; }; around "another" => sub { my ( $next, $self, @args ) = ( shift, shift, @_ ); ...; $self->$next( @_ ); ...; };
Constants:
constant "ANSWER_TO_LIFE" => 42;
Overloading:
method "to_string" => sub { my $self = shift; ...; }; overload( q[""] => "to_string", fallback => 1, );
Factory methods:
factory "new_foo" => \"new"; factory "new_foo" => sub { my ( $factory, $class, @args ) = ( shift, shift, @_ ); return $class->new( @args ); };
Factory methods may include signatures like methods.
Indicate you want a class to have no factories:
factory();
The keywords multi_method and multi_factory exist for multimethods.
multi_method
multi_factory
The keyword symmethod exists for symmethods.
symmethod
Setting the type name for a class or role:
class "Foo::Bar" => sub { type_name "Foobar"; ...; };
Coercion:
class "Foo::Bar" => sub { method "from_arrayref" => sub { my ( $class, $aref ) = ( shift, @_ ); ...; }; coerce "ArrayRef" => "from_arrayref"; }; class "Foo::Bar" => sub { coerce "ArrayRef" => "from_arrayref" => sub { my ( $class, $aref ) = @_; ...; }; };
Hooks for classes:
begin { my ( $class ) = ( shift ); # Code that runs early during class definition }; end { my ( $class ) = ( shift ); # Code that runs late during class definition };
Hooks for roles:
begin { my ( $role ) = ( shift ); # Code that runs early during role definition }; end { my ( $role ) = ( shift ); # Code that runs late during role definition }; before_apply { my ( $role, $target, $targetkind ) = ( shift, @_ ); # Code that runs before a role is applied to a package }; after_apply { my ( $role, $target, $targetkind ) = ( shift, @_ ); # Code that runs after a role is applied to a package };
Booleans:
my $truth = true; my $truth = false;
Exceptions:
confess( 'Something bad happened' ); confess( 'Exceeded maximum (%d)', $max );
Scope ANY means the keyword can appear anywhere where Zydeco::Lite is in scope. Scope CLASS means that the keyword may appear only within class or abstract class definition blocks. Scope ROLE means that the keyword may appear only in role/interface definition blocks. Scope APP means that the keyword may appear only within an app definition block.
# Scope: ANY app( Optional[Str|ScalarRef] $name, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY class( Optional[Str|ScalarRef] $name, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY class generator( Optional[Str|ScalarRef] $name, Optional[ArrayRef] $signature, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY role( Optional[Str|ScalarRef] $name, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY role generator( Optional[Str|ScalarRef] $name, Optional[ArrayRef] $signature, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY interface( Optional[Str|ScalarRef] $name, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY interface generator( Optional[Str|ScalarRef] $name, Optional[ArrayRef] $signature, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY abstract_class( Optional[Str|ScalarRef] $name, Hash %args, Optional[CodeRef] $definition, ); # Scope: ANY abstract_class generator( Optional[Str|ScalarRef] $name, Optional[ArrayRef] $signature, Hash %args, Optional[CodeRef] $definition, ); # Scope: CLASS or APP extends( List[Str|ArrayRef] @parents, ); # Scope: ANY with( List[Str|ArrayRef] @parents, ); # Scope: ANY method( Optional[Str|ScalarRef] $name, Optional[ArrayRef] $signature, Hash %args, CodeRef $body, ); # Scope: CLASS factory( Str|ArrayRef $names, Optional[ArrayRef] $signature, Hash %args, CodeRef|ScalarRef $body_or_via, ); # Scope: ANY constant( Str|ArrayRef $names, Any $value, ); # Scope: ANY multi_method( Str $name, ArrayRef $signature, Hash %args, CodeRef $body, ); # Scope: CLASS multi_factory( Str $name, ArrayRef $signature, Hash %args, CodeRef $body, ); # Scope: ANY symmethod( Str $name, ArrayRef $signature, Hash %args, CodeRef $body, ); # Scope: ANY before( Str|ArrayRef $names, Optional[ArrayRef] $signature, Hash %args, CodeRef $body, ); # Scope: ANY after( Str|ArrayRef $names, Optional[ArrayRef] $signature, Hash %args, CodeRef $body, ); # Scope: ANY around( Str|ArrayRef $names, Optional[ArrayRef] $signature, Hash %args, CodeRef $body, ); # Scope: CLASS or ROLE has( Str|ArrayRef $names, Hash|HashRef|ArrayRef %spec, ); # Scope: ROLE requires( List[Str] @names, ); # Scope: ANY confess( Str $template, List @args, ); # Scope: APP or CLASS or ROLE toolkit( Str $toolkit, Optional[List] @imports, ); # Scope: CLASS or ROLE coerce( Object|Str $type, Str $via, Optional[CodeRef] $definition, ); # Scope: CLASS overload( Hash %args, ); # Scope: APP or CLASS or ROLE version( Str $version, ); # Scope: APP or CLASS or ROLE authority( Str $authority, ); # Scope: CLASS or ROLE type_name( Str $name, ); # Scope: CLASS or ROLE begin { my ( $package ) = @_; ...; }; # Scope: CLASS or ROLE end { my ( $package ) = @_; ...; }; # Scope: ROLE before_apply { my ( $role, $target, $targetkind ) = @_; ...; }; # Scope: ROLE after_apply { my ( $role, $target, $targetkind ) = @_; ...; };
Scopes are dynamic rather than lexical. So although extends can only appear in a CLASS, this will work:
extends
use Zydeco::Lite; class "Base"; sub foo { extends "Base" } class "Derived" => sub { foo() };
Keywords used within a before_apply or after_apply block execute in the scope of the package they're being applied to. They run too late for type_name to work, but most other keywords will work okay. In the following example, Derived will be a child class of Base.
before_apply
after_apply
type_name
use Zydeco::Lite; class "Base"; role "ChildOfBase" => sub { after_apply { my ( $role, $target, $kind ) = @_; extends "Base" if $kind eq "class"; }; }; class "Derived" => sub { with "ChildOfBase"; };
Zydeco::Lite uses Exporter::Tiny, so you can choose which keywords to import, rename them, etc.
use Zydeco::Lite { -prefix => 'zy_' }; my $app = zy_app { zy_class 'Foo' => sub {}; }; my $obj = $app->new_foo();
package Zoo; use strict; use warnings; use Zydeco::Lite; my $app = __PACKAGE__; app $app => sub { class 'Park' => sub { has 'name' => ( type => 'Str', ); has 'animals' => ( type => 'ArrayRef', default => sub { [] }, handles_via => 'Array', handles => [ 'add_animal' => 'push', 'list_animals' => 'all', ], ); method 'print_animals' => [] => sub { my ( $self ) = ( shift ); for my $animal ( $self->list_animals ) { $animal->print_animal; } }; }; role generator 'Animal' => [ 'Str' ] => sub { my ( $gen, $species ) = ( shift, @_ ); has 'name' => ( type => 'Str', required => true ); method 'print_animal' => [] => sub { my ( $self ) = ( shift ); printf( "%s (%s)\n", $self->name, $species ); }; }; class 'Lion' => sub { with 'Animal' => [ 'Panthera leo' ]; }; class 'Tiger' => sub { with 'Animal' => [ 'Panthera tigris' ]; }; class 'Bear' => sub { with 'Animal' => [ 'Ursus arctos' ]; }; }; my $zoo = $app->new_park( name => "Oz Zoo" ); $zoo->add_animal( $app->new_lion( name => "Simba" ) ); $zoo->add_animal( $app->new_lion( name => "Aslan" ) ); $zoo->add_animal( $app->new_tiger( name => "Tigger" ) ); $zoo->add_animal( $app->new_tiger( name => "Shere Khan" ) ); $zoo->add_animal( $app->new_bear( name => "Paddington" ) ); $zoo->add_animal( $app->new_bear( name => "Yogi" ) ); $zoo->print_animals; # oh my!
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=MooX-Press.
Zydeco, MooX::Press.
Toby Inkster <tobyink@cpan.org>.
This software is copyright (c) 2020 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
To install MooX::Press, copy and paste the appropriate command in to your terminal.
cpanm
cpanm MooX::Press
CPAN shell
perl -MCPAN -e shell install MooX::Press
For more information on module installation, please visit the detailed CPAN module installation guide.