-
-
19 Jun 2022 18:29:25 UTC
- Distribution: MooX-Press
- Module version: 0.085
- Source (raw)
- Browse (raw)
- Changes
- Homepage
- How to Contribute
- Repository
- Issues
- Testers (74 / 0 / 0)
- Kwalitee
Bus factor: 1- 74.28% Coverage
- License: perl_5
- Perl: v5.8.8
- Activity
24 month- Tools
- Download (89.61KB)
- MetaCPAN Explorer
- Permissions
- Subscribe to distribution
- Permalinks
- This version
- Latest version
- NAME
- SYNOPSIS
- DESCRIPTION
- EXAMPLE
- BUGS
- SEE ALSO
- AUTHOR
- COPYRIGHT AND LICENCE
- DISCLAIMER OF WARRANTIES
NAME
Zydeco::Lite - Zydeco without any magic
SYNOPSIS
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();
DESCRIPTION
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.
Syntax Examples
Apps
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.)Classes, Roles, Interfaces, and Abstract Classes
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
, andabstract_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 ]; };
Package Settings
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
, andtoolkit
keywords can be used withinapp
,class
,role
,interface
, orabstract_class
definitions.Attributes
Attributes:
has "myattr" => ( ... ); has [ "myattr1", "myattr2" ] => ( ... );
Private attributes:
has "myattr" => ( is => "private", ..., accessor => \(my $accessor) );
Methods
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
andmulti_factory
exist for multimethods.The keyword
symmethod
exists for symmethods.Types
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
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 };
Utilities
Booleans:
my $truth = true; my $truth = false;
Exceptions:
confess( 'Something bad happened' ); confess( 'Exceeded maximum (%d)', $max );
Formal Syntax
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:use Zydeco::Lite; class "Base"; sub foo { extends "Base" } class "Derived" => sub { foo() };
Keywords used within a
before_apply
orafter_apply
block execute in the scope of the package they're being applied to. They run too late fortype_name
to work, but most other keywords will work okay. In the following example, Derived will be a child class of Base.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"; };
Import
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();
EXAMPLE
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!
BUGS
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=MooX-Press.
SEE ALSO
AUTHOR
Toby Inkster <tobyink@cpan.org>.
COPYRIGHT AND LICENCE
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.
DISCLAIMER OF WARRANTIES
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.
Module Install Instructions
To install MooX::Press, copy and paste the appropriate command in to your terminal.
cpanm MooX::Press
perl -MCPAN -e shell install MooX::Press
For more information on module installation, please visit the detailed CPAN module installation guide.