MooX::Press - quickly create a bunch of Moo/Moose/Mouse classes and roles
package MyApp; use Types::Standard qw(Str Num); use MooX::Press ( role => [ 'Livestock', 'Pet', 'Milkable' => { can => [ 'milk' => sub { print "giving milk\n"; }, ], }, ], class => [ 'Animal' => { has => [ 'name' => Str, 'colour', 'age' => Num, 'status' => { enum => ['alive', 'dead'], default => 'alive' }, ], subclass => [ 'Panda', 'Cat' => { with => ['Pet'] }, 'Dog' => { with => ['Pet'] }, 'Cow' => { with => ['Livestock', 'Milkable'] }, 'Pig' => { with => ['Livestock'] }, ], }, ], );
Using your classes:
use MyApp; my $kitty = MyApp->new_cat(name => "Grey", status => "alive"); # or: MyApp::Cat->new(name => "Grey", status => "alive"); MyApp->new_cow(name => "Daisy")->milk();
I realize this is a longer synopsis than most CPAN modules give, but considering it sets up six classes and three roles with some attributes and methods, applies the roles to the classes, and creates a type library with nine types in it, it's pretty concise.
MooX::Press (pronounced "Moo Express") is a quick way of creating a bunch of simple Moo classes and roles at once without needing to create separate Perl modules for each class and each role, and without needing to add a bunch of boilerplate to each file.
It also supports Moose and Mouse, though Moo classes and roles play nicely with Moose (and to a certain extent with Mouse) anyway.
MooX::Press is called like:
use MooX::Press %import_opts;
The following options are supported. To make these easier to remember, options follow the convention of using lower-case singular, and reusing keywords from Perl and Moo/Moose/Mouse when possible.
class
This is the list of classes to create as an optlist. An optlist is an arrayref of strings, where each string is optionally followed by a reference.
[ "A", "B", "C", \%opt_for_C, "D", "E", \%opts_for_E, "F" ]
In particular, for the class optlist the references should be hashrefs of class options (see "Class Options"), though key-value pair arrayrefs are also accepted.
role
This is the list of roles to create, structured almost the same as the optlist for classes, but see "Role Options".
class_generator
Kind of like class, but:
[ "A", \&generator_for_A, "B", \&generator_for_B, ... ]
"A" and "B" are not classes, but when MyApp->generate_a(...) is called, it will pass arguments to &generator_for_A which is expected to return a hashref like \%opts_for_A. Then a new pseudononymous class will be created with those options.
MyApp->generate_a(...)
&generator_for_A
\%opts_for_A
See the FAQ for an example.
role_generator
The same but for roles.
toolkit
The strings "Moo", "Moose", or "Mouse" are accepted and instruct MooX::Press to use your favourite OO toolkit. "Moo" is the default.
version
This has nothing to do with the version of MooX::Press you are using. It sets the our $VERSION variable for the classes and roles being generated.
our $VERSION
authority
This sets the our $AUTHORITY variable for the classes and roles being generated.
our $AUTHORITY
version and authority will be copied from the caller if they are not set, but you can set them to undef explicitly if you want to avoid that.
prefix
A namespace prefix for MooX::Press to put all your classes into. If MooX::Press is told to create a class "Animal" and prefix is set to "MyApp::OO", then it will create a class called "MyApp::OO::Animal".
This is optional and defaults to the caller. If you wish to have no prefix, then pass an explicit prefix => undef option.
prefix => undef
You can bypass the prefix for a specific class or a specific role using a leading double colon, like "::Animal".
factory_package
A package name to install methods like the new_cat and new_cow methods in "SYNOPSIS".
new_cat
new_cow
This defaults to prefix, but may be explicitly set to undef to suppress the creation of such methods.
In every class (but not role) that MooX::Press builds, there will be a FACTORY method created so that, for example
FACTORY
MyApp::Cow->FACTORY # returns "MyApp"
type_library
MooX::Press will automatically create a Type::Library-based type library with type constraints for all your classes and roles. It will be named using your prefix followed by "::Types".
You can specify a new name or explicitly set to undef to suppress this behaviour, but a lot of the coercion features of MooX::Press rely on there being a type library.
MooX::Press will create a get_type_for_package method that allows you to do this:
MyApp::Types->get_type_for_package(class => "MyApp::Animal")
MooX::Press will mark "MyApp/Types.pm" as loaded in %INC, so you can do things like:
use MyApp::Types qw(Animal);
And it won't complain about "MyApp/Types.pm" not being found.
MooX::Press will install a type_library method into the factory package which returns the name of the type library, so you can do:
MyApp->type_library->get_type_for_package(class => "MyApp::Animal")
caller
MooX::Press determines some things based on which package called it. If you are wrapping MooX::Press, you can fake the caller by passing it as an option.
end
After creating each class or role, this coderef will be called. It will be passed two parameters; the fully-qualified package name of the class or role, plus the string "class" or "role" as appropriate.
Optional; defaults to nothing.
begin
Like end, but called before setting up any attributes, methods, or method modifiers. (But after loading Moo/Moose/Mouse.)
mutable
Boolean to indicate that classes should be left mutable after creating them rather than making them immutable. Constructors for mutable classes are considerably slower than for immutable classes, so this is usually a bad idea.
Only supported for Moose. Unnecessary for Moo anyway. Defaults to false.
factory_package_can
Hashref of additional subs to install into the factory package.
type_library_can
At this top level, a shortcut is available for the 'class' and 'role' keys. Rather than:
use MooX::Press ( role => [ 'Quux', 'Quuux' => { ... }, ], class => [ 'Foo', 'Bar' => { ... }, 'Baz' => { ... }, ], );
It is possible to write:
use MooX::Press ( 'role:Quux' => {}, 'role:Quuux' => { ... }, 'class:Foo' => {}, 'class:Bar' => { ... }, 'class:Baz' => { ... }, );
This saves a level of indentation. (=> undef or => 1 are supported as synonyms for => {}.)
=> undef
=> 1
=> {}
Each class in the list of classes can be followed by a hashref of options:
use MooX::Press ( class => [ 'Foo' => \%options_for_foo, 'Bar' => \%options_for_bar, ], );
The following class options are supported.
extends
The parent class for this class.
The prefix is automatically added. Include a leading "::" if you don't want the prefix to be added.
Multiple inheritance is supported.
with
Roles for this class to consume.
Roles may include a trailing "?". When these are seen, the role will be created if it doesn't seem to exist. This is because sometimes it's useful to have roles to classify classes (and check them with the does method) even if those roles don't have any other functionality.
does
use MooX::Press ( prefix => 'Farm', class => [ 'Sheep' => { with => ['Bleat?'] }, ], ); if (Farm::Sheep->new->does('Farm::Bleat')) { ...; }
Without the "?", trying to compose a role that does not exist is an error.
has
The list of attributes to add to the class as an optlist.
The strings are the names of the attributes, but these strings may be "decorated" with sigils and suffixes:
$foo
Creates an attribute "foo" intended to hold a single value. This adds a type constraint forbidding arrayrefs and hashrefs but allowing any other value, including undef, strings, numbers, and any other reference.
@foo
Creates an attribute "foo" intended to hold a list of values. This adds a type constraint allowing arrayrefs or objects overloading @{}.
@{}
%foo
Creates an attribute "foo" intended to hold a collection of key-value pairs. This adds a type constraint allowing hashrefs or objects overloading %{}.
%{}
foo!
Creates an attribute "foo" which will be required by the constructor.
An attribute can have both a sigil and a suffix.
The references in the optlist may be attribute specification hashrefs, type constraint objects, or builder coderefs.
# These mean the same thing... "name!" => Str, "name" => { is => "rw", required => 1, isa => Str }, # These mean the same thing... "age" => sub { return 0 }, "age" => { is => "rw", lazy => 1, builder => sub { return 0 }, clearer => "clear_age", },
Type constraints can be any blessed object supported by the toolkit. For Moo, use Type::Tiny. For Moose, use Type::Tiny, MooseX::Types, or Specio. For Mouse, use Type::Tiny or MouseX::Types.
Builder coderefs are automatically installed as methods like "YourPrefix::YourClass::_build_age()".
For details of the hashrefs, see "Attribute Specifications".
can
A hashref of coderefs to install into the package.
package MyApp; use MooX::Press ( class => [ 'Foo' => { can => { 'bar' => sub { print "in bar" }, }, }, ], ); package main; MyApp->new_foo()->bar();
As an alternative, you can do this to prevent your import from getting cluttered with coderefs. Which you choose depends a lot on stylistic preference.
package MyApp; use MooX::Press ( class => ['Foo'], ); package MyApp::Foo; sub bar { print "in bar" }, package main; MyApp->new_foo()->bar();
constant
A hashref of scalar constants to define in the package.
package MyApp; use MooX::Press ( class => [ 'Foo' => { constant => { 'BAR' => 42, }, }, ], ); package main; print MyApp::Foo::BAR, "\n"; print MyApp->new_foo->BAR, "\n";
around
before
after
Installs method modifiers.
package MyApp; use MooX::Press ( role => [ 'Loud' => { around => [ 'greeting' => sub { my $orig = shift; my $self = shift; return uc( $self->$orig(@_) ); }, ], } ], class => [ 'Person' => { can => { 'greeting' => sub { "hello" }, } subclass => [ 'LoudPerson' => { with => 'Loud' }, ], }, ], ); package main; print MyApp::LoudPerson->new->greeting, "\n"; # prints "HELLO"
coerce
When creating a class or role "Foo", MooX::Press will also create a Type::Tiny::Class or Type::Tiny::Role called "Foo". The coerce option allows you to add coercions to that type constraint. Coercions are called as methods on the class or role. This is perhaps best explained with an example:
package MyApp; use Types::Standard qw(Str); use MooX::Press ( class => [ 'Person' => { has => [ 'name!' => Str ], can => { 'from_name' => sub { my ($class, $name) = @_; return $class->new(name => $name); }, }, coerce => [ Str, 'from_name', ], }, 'Company' => { has => [ 'name!' => Str, 'owner!' => { isa => 'Person' } ], }, ], );
This looks simple but it's like the swan, graceful above the surface of the water, legs paddling frantically below.
It creates a class called "MyApp::Person" with a "name" attribute, so you can do this kind of thing:
my $bob = MyApp::Person->new(name => "Bob"); my $bob = MyApp->new_person(name => "Bob");
As you can see from the can option, it also creates a method "from_name" which can be used like this:
my $bob = MyApp::Person->from_name("Bob");
But here's where coercions come in. It also creates a type constraint called "Person" in "MyApp::Types" and adds a coercion from the Str type. The coercion will just call the "from_name" method.
Str
Then when the "MyApp::Company" class is created and the "owner" attribute is being set up, MooX::Press knows about the coercion from Str, and will set up coercion for that attribute.
# So this should just work... my $acme = MyApp->new_company(name => "Acme Inc", owner => "Bob"); print $acme->owner->name, "\n";
Now that's out of the way, the exact structure for the arrayref of coercions can be explained. It is essentially a list of type-method pairs.
The type may be either a blessed type constraint object (Type::Tiny, etc) or it may be a string type name for something that your type library knows about.
The method is a string containing the method name to perform the coercion.
This may optionally be followed by coderef to install as the method. The following two examples are equivalent:
use MooX::Press ( class => [ 'Person' => { has => [ 'name!' => Str ], can => { 'from_name' => sub { my ($class, $name) = @_; return $class->new(name => $name); }, }, coerce => [ Str, 'from_name', ], }, ], ); use MooX::Press ( class => [ 'Person' => { has => [ 'name!' => Str ], coerce => [ Str, 'from_name' => sub { my ($class, $name) = @_; return $class->new(name => $name); }, ], }, ], );
In the second example, you can see the can option to install the "from_name" method has been dropped and the coderef put into coerce instead.
In case it's not obvious, I suppose it's worth explicitly stating that it's possible to have coercions from many different types.
use MooX::Press ( class => [ 'Foo::Bar' => { coerce => [ Str, 'from_string', sub { ... }, ArrayRef, 'from_array', sub { ... }, HashRef, 'from_hash', sub { ... }, 'FBaz', 'from_foobaz', sub { ... }, ], }, 'Foo::Baz' => { type_name => 'FBaz', }, ], );
You should generally order the coercions from most specific to least specific. If you list "Num" before "Int", "Int" will never be used because all integers are numbers.
There is no automatic inheritance for coercions because that does not make sense. If Mammal->from_string($str) is a coercion returning a "Mammal" object, and "Person" is a subclass of "Mammal", then there's no way for MooX::Press to ensure that when Person->from_string($str) is called, it will return a "Person" object and not some other kind of mammal. If you want "Person" to have a coercion, define the coercion in the "Person" class and don't rely on it being inherited from "Mammal".
Mammal->from_string($str)
Person->from_string($str)
subclass
Set up subclasses of this class. This accepts an optlist like the class list. It allows subclasses to be nested as deep as you like:
package MyApp; use MooX::Press ( class => [ 'Animal' => { has => ['name!'], subclass => [ 'Fish', 'Bird', 'Mammal' => { can => { 'lactate' => sub { ... } }, subclass => [ 'Cat', 'Dog', 'Primate' => { subclass => ['Monkey', 'Gorilla', 'Human'], }, ], }, ], }, ]; ); package main; my $uncle = MyApp->new_human(name => "Bob"); $uncle->isa('MyApp::Human'); # true $uncle->isa('MyApp::Primate'); # true $uncle->isa('MyApp::Mammal'); # true $uncle->isa('MyApp::Animal'); # true $uncle->isa('MyApp::Bird'); # false $uncle->can('lactate'); # eww, but true
We just defined a nested heirarchy with ten classes there!
Subclasses can be named with a leading "+" to tell them to use their parent class name as a prefix. So, in the example above, if you'd called your subclasses "+Mammal", "+Dog", etc, you'd end up with packages like "MyApp::Animal::Mammal::Dog". (In cases of multiple inheritance, it uses $ISA[0].)
$ISA[0]
factory
This is the name for the method installed into the factory package. So for class "Cat", it might be "new_cat".
The default is the class name (excluding the prefix), lowercased, with double colons replaced by single underscores, and with "new_" added in front. To suppress the creation of this method, set factory to an explicit undef.
If set to an arrayref, it indicates you wish to create multiple methods in the factory package to make objects of this class.
factory => [ "grow_pig" => \"new_from_embryo", "new_pork", "new_bacon", "new_ham" => sub { ... }, "new_pig", "new_swine", ],
A scalarref indicates the name of a constructor and that the methods before are shortcuts for that constructor. So MyApp->grow_pig(@args) is a shortcut for MyApp::Pig->new_from_embryo(@args).
MyApp->grow_pig(@args)
MyApp::Pig->new_from_embryo(@args)
A coderef will have a custom method installed into the factory package so that MyApp->new_pork(@args) will act as a shortcut for: $coderef->("MyApp", "MyApp::Pig", @args). Note that new_bacon and new_ham are just aliases for new_bacon.
MyApp->new_pork(@args)
$coderef->("MyApp", "MyApp::Pig", @args)
new_bacon
new_ham
The new_pig and new_swine method names are followed by neither a coderef nor a scalarref, so are treated as if they had been followed by \"new".
new_pig
new_swine
\"new"
type_name
The name for the type being installed into the type library.
The default is the class name (excluding the prefix), with double colons replaced by single underscores.
This:
use MooX::Press prefix => "ABC::XYZ", class => ["Foo::Bar"];
Will create class "ABC::XYZ::Foo::Bar", a factory method ABC::XYZ->new_foo_bar(), and a type constraint "Foo_Bar" in type library "ABC::XYZ::Types".
ABC::XYZ->new_foo_bar()
Override toolkit choice for this class and any child classes.
Override version number for this class and any child classes.
Override authority for this class and any child classes.
See "Import Options".
Override namespace prefix for this class and any child classes.
Override factory_package for this class and any child classes.
Override mutability for this class and any child classes.
Override end for this class and any child classes.
Override begin for this class and any child classes.
use MooX::Press::Keywords qw( true false ); use MooX::Press ( prefix => 'Library', class => [ 'Book' => { begin => sub { my $classname = shift; # "Library::Book" my $registry = Type::Registry->for_class($classname); $registry->alias_type('ArrayRef[Str]' => 'StrList') }, has => { 'title' => { type => 'Str', required => true }, 'authors' => { type => 'StrList', required => true }, }, }, ], );
import
Allows you to import packages into classes.
use MooX::Press ( prefix => 'Library', class => [ toolkit => 'Moose', import => [ 'MooseX::StrictConstructor' ], ..., ], );
Note that the coderefs you pass to MooX::Press are evaluated in the caller namespace, so this isn't very useful if you're looking to import functions. It can be useful for many MooX, MooseX, and MouseX extensions though.
Options for roles are largely the same as for classes with the following exceptions:
requires
A list of methods required by the role.
package MyApp; use MooX::Press ( role => [ 'Milkable' => { requires => ['get_udder'], ..., }, ], );
Each method can optionally be followed by a method-defining hashref like in can:
package MyApp; use MooX::Press ( role => [ 'Milkable' => { requires => [ 'get_udder', { signature => [...], named => 0 }, ], ..., }, ], );
These hashrefs are currently ignored, but may be useful for people reading your role declarations.
This option is disallowed.
The alternative style for defining methods may cause problems with the order in which things happen. Because use MooX::Press happens at compile time, the following might not do what you expect:
use MooX::Press
package MyApp; use MooX::Press ( role => ["MyRole"], class => ["MyClass" => { with => "MyRole" }], ); package MyApp::MyRole; sub my_function { ... }
The "my_function" will not be copied into "MyApp::MyClass" because at the time the class is constructed, "my_function" doesn't yet exist within the role "MyApp::MyRole".
You can combat this by changing the order you define things in:
package MyApp::MyRole; sub my_function { ... } package MyApp; use MooX::Press ( role => ["MyRole"], class => ["MyClass" => { with => "MyRole" }], );
If you don't like having method definitions "above" MooX::Press in your file, then you can move them out into a module.
# MyApp/Methods.pm # package MyApp::MyRole; sub my_function { ... } # MyApp.pm # package MyApp; use MyApp::Methods (); # load extra methods use MooX::Press ( role => ["MyRole"], class => ["MyClass" => { with => "MyRole" }], );
Or force MooX::Press to happen at runtime instead of compile time.
package MyApp; require MooX::Press; import MooX::Press ( role => ["MyRole"], class => ["MyClass" => { with => "MyRole" }], ); package MyApp::MyRole; sub my_function { ... }
This option is silently ignored.
Attribute specifications are mostly just passed to the OO toolkit unchanged, somewhat like:
has $attribute_name => %attribute_spec;
So whatever specifications (required, trigger, coerce, etc) the underlying toolkit supports should be supported.
required
trigger
The following are exceptions:
is
This is optional rather than being required, and defaults to "rw". (Yes, I prefer "ro" generally, but whatever.)
MooX::Press supports the Moo-specific values of "rwp" and "lazy", and will translate them if you're using Moose or Mouse.
isa
When the type constraint is a string, it is always assumed to be a class name and your application's namespace prefix is added. So isa => "HashRef" doesn't mean what you think it means. It means an object blessed into the "YourApp::HashRef" class.
isa => "HashRef"
That is a feature though, not a weakness.
use MooX::Press ( prefix => 'Nature', class => [ 'Leaf' => {}, 'Tree' => { has => { 'nicest_leaf' => { isa => 'Leaf' }, }, }, ], );
The Nature::Tree and Nature::Leaf classes will be built, and MooX::Press knows that the nicest_leaf is supposed to be a blessed Nature::Leaf object.
Nature::Tree
Nature::Leaf
nicest_leaf
String type names can be prefixed with @ or % to indicate an arrayref or hashref of a type:
@
%
use MooX::Press ( prefix => 'Nature', class => [ 'Leaf' => {}, 'Tree' => { has => { 'foliage' => { isa => '@Leaf' }, }, }, ], );
For more everything else, use blessed type constraint objects, such as those from Types::Standard, or use type as documented below.
type
use Types::Standard qw( Str ); use MooX::Press ( prefix => 'Nature', class => [ 'Leaf' => {}, 'Tree' => { has => { 'foliage' => { isa => '@Leaf' }, 'species' => { isa => Str }, }, }, ], );
type => "HashRef" does what you think isa => "HashRef" should do. More specifically it searches (by name) your type library, along with Types::Standard, Types::Common::Numeric, and Types::Common::String to find the type constraint it thinks you wanted. It's smart enough to deal with parameterized types, unions, intersections, and complements.
type => "HashRef"
use MooX::Press ( prefix => 'Nature', class => [ 'Leaf' => {}, 'Tree' => { has => { 'foliage' => { isa => '@Leaf' }, 'species' => { type => 'Str' }, }, }, ], );
type => $blessed_type_object does still work.
type => $blessed_type_object
type and isa are basically the same as each other, but differ in how they'll interpret a string. isa assumes it's a class name as applies the package prefix to it; type assumes it's the name of a type constraint which has been defined in some type library somewhere.
Similarly to isa, these will be given your namespace prefix.
# These mean the same... does => 'SomeRole', type => Types::Standard::ConsumerOf['MyApp::SomeRole'],
enum
This is a cute shortcut for an enum type constraint.
# These mean the same... enum => ['foo', 'bar'], type => Types::Standard::Enum['foo', 'bar'],
If the type constraint is set to an enum and handles is provided, then MooX::Press will automatically load MooX::Enumeration or MooseX::Enumeration as appropriate. (This is not supported for Mouse.)
handles
use MooX::Press ( prefix => 'Nature', class => [ 'Leaf' => { has => { 'colour' => { enum => ['green', 'red', 'brown'], handles => 2, default => 'green', }, }, }, ], ); my $leaf = Nature->new_leaf; if ( $leaf->colour_is_green ) { print "leaf is green!\n"; }
handles_via
If your attribute has a handles_via option, MooX::Press will load Sub::HandlesVia for you.
MooX::Press automatically implies coerce => 1 when you give a type constraint that has a coercion. If you don't want coercion then explicitly provide coerce => 0.
coerce => 1
coerce => 0
coerce => sub { ... } is supported even for Moose and Mouse.
coerce => sub { ... }
builder
MooX::Press supports the Moo-specific builder => 1 and builder => sub { ... } and will translate them if you're using Moose or Mouse.
builder => 1
builder => sub { ... }
MooX::Press supports the Moo-specific trigger => 1 and trigger => $methodname and will translate them if you're using Moose or Mouse.
trigger => 1
trigger => $methodname
clearer
MooX::Press supports the Moo-specific clearer => 1 and will translate it if you're using Moose or Mouse.
clearer => 1
Most places where a coderef is expected, MooX::Press will also accept a hashref of the form:
{ signature => [ ... ], named => 1, code => sub { ... }, }
The signature is a specification to be passed to compile or compile_named_oo from Type::Params (depending on whether named is true or false).
signature
compile
compile_named_oo
named
Unlike Type::Params, these signatures allow type constraints to be given as strings, which will be looked up by name.
This should work for can, factory_can, type_library_can, factory, builder methods, and method modifiers. (Though if you are doing type checks in both the methods and method modifiers, this may result in unnecessary duplication of checks.)
factory_can
The invocant ($self) is not included in the signature. (For around method modifiers, the original coderef $orig is logically a second invocant. For factory methods installed in the factory package, the factory package name and class name are both considered invocants.)
$self
$orig
Example with named parameters:
use MooX::Press ( prefix => 'Wedding', class => [ 'Person' => { has => [qw( $name $spouse )] }, 'Officiant' => { can => { 'marry' => { signature => [ bride => 'Person', groom => 'Person' ], named => 1, code => sub { my ($self, $args) = @_; $args->bride->spouse($args->groom); $args->groom->spouse($args->bride); printf("%s, you may kiss the bride\n", $args->groom->name); return $self; }, }, }, }, ], ); my $alice = Wedding->new_person(name => 'Alice'); my $bob = Wedding->new_person(name => 'Robert'); my $carol = Wedding->new_officiant(name => 'Carol'); $carol->marry(bride => $alice, groom => $bob);
Example with positional parameters:
use MooX::Press ( prefix => 'Wedding', class => [ 'Person' => { has => [qw( $name $spouse )] }, 'Officiant' => { can => { 'marry' => { signature => [ 'Person', 'Person' ], code => sub { my ($self, $bride, $groom) = @_; $bride->spouse($groom); $groom->spouse($bride); printf("%s, you may kiss the bride\n", $groom->name); return $self; }, }, }, }, ], ); my $alice = Wedding->new_person(name => 'Alice'); my $bob = Wedding->new_person(name => 'Robert'); my $carol = Wedding->new_officiant(name => 'Carol'); $carol->marry($alice, $bob);
Methods with a mixture of named and positional parameters are not supported. If you really want such a method, don't provide a signature; just provide a coderef and manually unpack @_.
@_
Advanced features:
signature may be a coderef, which is passed @_ (minus invocants) and is expected to return a new @_ in list context after checking and optionally coercing parameters.
Setting optimize => 1 tells MooX::Press to attempt to perform additional compile-time optimizations on the signature to make it slightly faster at runtime. (Sometimes it will find it's unable to optimize anything, so you've just wasted time at compile time.)
optimize => 1
code can be a string of Perl code like sub { ... } instead of a real coderef. This doesn't let you close over any variables, but if you can provide code this way, it might be slightly faster.
code
sub { ... }
MooX::Press will automatically load MooX::TypeTiny if it's installed, which optimizes how Type::Tiny constraints and coercions are inlined into Moo constructors. This is only used for Moo classes.
MooX::Press will automatically load MooseX::XSAccessor if it's installed, which speeds up some Moose accessors. This is only used for Moose classes.
All the internals of MooX::Press are called as methods, which should make subclassing it possible.
package MyX::Press; use parent 'MooX::Press'; use Class::Method::Modifiers; around make_class => sub { my $orig = shift; my $self = shift; my ($name, %opts) = @_; ## Alter %opts here my $qname = $self->$orig($name, %opts); ## Maybe do something to the returned class return $qname; };
It is beyond the scope of this documentation to fully describe all the methods you could potentially override, but here is a quick summary of some that may be useful.
import(%opts|\%opts)
qualify_name($name, $prefix)
croak($error)
prepare_type_library($qualified_name)
make_type_for_role($name, %opts)
make_type_for_class($name, %opts)
make_role($name, %opts)
make_class($name, %opts)
install_methods($qualified_name, \%methods)
install_constants($qualified_name, \%values)
This is a new module so I haven't had any questions about it yet, let alone any frequently asked ones, but I will anticipate some.
Your MooX::Press import will typically contain a lot of strings, maybe some as barewords, some coderefs, etc. You should manually import strict and warnings before importing MooX::Press to ensure all of that is covered by strictures.
Factories are big and cool and they put lots of smoke into our atmosphere.
Also, if you do something like:
use constant APP => 'MyGarden'; use MooX::Press ( prefix => APP, role => [ 'LeafGrower' => { has => [ '@leafs' => sub { [] } ], can => { 'grow_leaf' => sub { my $self = shift; my $leaf = $self->FACTORY->new_leaf; push @{ $self->leafs }, $leaf; return $leaf; }, }, }, ], class => [ 'Leaf', 'Tree' => { with => ['LeafGrower'] }, ], ); my $tree = APP->new_tree; my $leaf = $tree->grow_leaf;
And you will notice that the string "MyGarden" doesn't appear anywhere in the definitions for any of the classes and roles. The prefix could be changed to something else entirely and all the classes and roles, all the methods within them, would continue to work.
Whole collections of classes and roles now have portable namespaces. The same classes and roles could be used with different prefixes in different scripts. You could load two different versions of your API in the same script with different prefixes. The possibilities are interesting.
$object->isa("Leaf")
In the previous question, $object->isa("Leaf") won't work to check if an object is a Leaf. This is because the full name of the class is "MyGarden::Leaf".
You can of course check $object->isa("MyGarden::Leaf") but this means you're starting to hard-code class names and prefixes again, which is one of the things MooX::Press aims to reduce.
$object->isa("MyGarden::Leaf")
The "correct" way to check something is a leaf is:
use MyGarden::Types qw( is_Leaf ); if ( is_Leaf($object) ) { ...; }
Or if you really want to use isa:
use MyGarden::Types qw( Leaf ); if ( $object->isa(Leaf->class) ) { ...; }
However, the type library is only available after you've used MooX::Press. This can make it tricky to refer to types within your methods.
use constant APP => 'MyGarden'; use MooX::Press ( prefix => APP, class => [ 'Leaf', 'Tree' => { can => { 'add_leaf' => sub { my ($self, $leaf) = @_; # How to check is_Leaf() here? # It's kind of tricky! my $t = $self->FACTORY->type_library->get_type('Leaf'); if ($t->check($leaf)) { ...; } }, }, }, ], );
As of version 0.019, MooX::Press has method signatures, so you're less likely to need to check types within your methods; you can just do it in the signature. This won't cover every case you need to check types, but it will cover the common ones.
use constant APP => 'MyGarden'; use MooX::Press ( prefix => APP, class => [ 'Leaf', 'Tree' => { can => { 'add_leaf' => { signature => ['Leaf'], code => sub { my ($self, $leaf) = @_; ...; }, }, }, }, ], );
This also makes your code more declarative and less imperative, and that is a Good Thing, design-wise.
Yeah, but that sounds like something is leaving.
A class generator is like a class of classes.
A role generator is like a class of roles.
use MooX::Press ( prefix => 'MyApp', class => [ 'Animal' => { has => ['$name'], }, ], class_generator => [ 'Species' => sub { my ($gen, $binomial) = @_; return { extends => ['Animal'], constant => { binomial => $binomial }, }; }, ], );
This generates MyApp::Animal as a class, as you might expect, but also creates a class generator called MyApp::Species.
MyApp::Species is not itself a class but it can make classes. Calling either MyApp::Species->generate_package or MyApp->generate_species will compile a new class and return the class name as a string.
MyApp::Species->generate_package
MyApp->generate_species
my $Human = MyApp->generate_species('Homo sapiens'); my $Dog = MyApp->generate_species('Canis familiaris'); my $alice = $Human->new(name => 'Alice'); say $alice->name; # Alice say $alice->binomial; # Homo sapiens my $fido = $Dog->new(name => 'Fido'); $fido->isa($Dog); # true $fido->isa($Human); # false $fido->isa('MyApp::Animal'); # true $fido->isa('MyApp::Species'); # false!!! use Types::Standard -types; use MyApp::Types -types; is_ClassName($fido) # false is_Object($fido) # true is_Animal($fido); # true is_SpeciesInstance($fido); # true is_SpeciesClass($fido); # false is_ClassName($Dog) # true is_Object($Dog) # false is_Animal($Dog); # false is_SpeciesInstance($Dog); # false is_SpeciesClass($Dog); # true
Note that there is no Species type created, but instead a pair of types is created: SpeciesClass and SpeciesInstance.
It is also possible to inherit from generated classes.
use MooX::Press ( prefix => 'MyApp', class => [ 'Animal' => { has => ['$name'], }, 'Dog' => { extends => [ 'Species' => ['Canis familiaris'] ] }, ], class_generator => [ 'Species' => sub { my ($gen, $binomial) = @_; return { extends => ['Animal'], constant => { binomial => $binomial }, }; }, ], ); my $fido = MyApp->new_dog(name => 'Fido');
The inheritance heirarchy for $fido is something like:
$fido
Moo::Object -> MyApp::Animal -> MyApp::Species::__GEN000001__ -> MyApp::Dog
Note that MyApp::Species itself isn't in that heirarchy!
Generated roles work pretty much the same, but role_generator instead of class_generator, does instead of isa, and with instead of extends.
No type constraints are automatically created for generated roles.
Quite possibly.
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=MooX-Press.
Moo, MooX::Struct, Types::Standard.
portable::loader.
Toby Inkster <tobyink@cpan.org>.
This software is copyright (c) 2019-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.