{"id":29191128,"url":"https://github.com/ovid/moosex-extended","last_synced_at":"2025-07-02T00:12:50.305Z","repository":{"id":36953565,"uuid":"493605794","full_name":"Ovid/moosex-extended","owner":"Ovid","description":"Build a better Moose.","archived":false,"fork":false,"pushed_at":"2023-06-06T04:43:07.000Z","size":485,"stargazers_count":4,"open_issues_count":1,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2023-08-09T12:16:25.066Z","etag":null,"topics":["oop","perl"],"latest_commit_sha":null,"homepage":"https://metacpan.org/pod/MooseX::Extended","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ovid.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-18T09:58:33.000Z","updated_at":"2023-08-09T12:16:25.067Z","dependencies_parsed_at":"2023-01-17T08:01:37.776Z","dependency_job_id":null,"html_url":"https://github.com/Ovid/moosex-extended","commit_stats":null,"previous_names":[],"tags_count":23,"template":null,"template_full_name":null,"purl":"pkg:github/Ovid/moosex-extended","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ovid%2Fmoosex-extended","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ovid%2Fmoosex-extended/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ovid%2Fmoosex-extended/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ovid%2Fmoosex-extended/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ovid","download_url":"https://codeload.github.com/Ovid/moosex-extended/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ovid%2Fmoosex-extended/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263052433,"owners_count":23406106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["oop","perl"],"created_at":"2025-07-02T00:12:49.300Z","updated_at":"2025-07-02T00:12:50.280Z","avatar_url":"https://github.com/Ovid.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nMooseX::Extended - Extend Moose with safe defaults and useful features\n\n# VERSION\n\nversion 0.35\n\n# SYNOPSIS\n\n```perl\npackage My::Names {\n    use MooseX::Extended types =\u003e [qw(compile Num NonEmptyStr Str PositiveInt ArrayRef)];\n    use List::Util 'sum';\n\n    # the distinction between `param` and `field` makes it easier to\n    # see which are available to `new`\n    param _name =\u003e ( isa =\u003e NonEmptyStr, init_arg =\u003e 'name' );\n    param title =\u003e ( isa =\u003e Str,         required =\u003e 0 );\n\n    # forbidden in the constructor\n    field created =\u003e ( isa =\u003e PositiveInt, default =\u003e sub {time} );\n\n    sub name ($self) {\n        my $title = $self-\u003etitle;\n        my $name  = $self-\u003e_name;\n        return $title ? \"$title $name\" : $name;\n    }\n\n    sub add ( $self, $args ) {\n        state $check = compile( ArrayRef [ Num, 1 ] );    # at least one number\n        ($args) = $check-\u003e($args);\n        return sum( $args-\u003e@* );\n    }\n\n    sub warnit ($self) {\n        carp(\"this is a warning\");\n    }\n}\n```\n\n# DESCRIPTION\n\nThis module is **BETA** code. It's feature-complete for release and has no\nknown bugs. We believe it's ready for production, but make no promises.\n\nThis is a quick overview. See [MooseX::Extended::Manual::Tutorial](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3ATutorial) for more\ninformation.\n\nThis class attempts to create a safer version of Moose that defaults to\nread-only attributes and is easier to read and write.\n\nIt tries to bring some of the lessons learned from [the Corinna\nproject](https://github.com/Ovid/Cor), while acknowledging that you can't\nalways get what you want (such as true encapsulation and true methods).\n\nThis:\n\n```perl\npackage My::Class {\n    use MooseX::Extended;\n\n    ... your code here\n}\n```\n\nIs sort of the equivalent to:\n\n```perl\npackage My::Class {\n    use v5.20.0;\n    use Moose;\n    use MooseX::StrictConstructor;\n    use feature qw( signatures postderef postderef_qq );\n    no warnings qw( experimental::signatures experimental::postderef );\n    use namespace::autoclean;\n    use Carp;\n    use mro 'c3';\n\n    ... your code here\n\n    __PACKAGE__-\u003emeta-\u003emake_immutable;\n}\n1;\n```\n\nIt also exports two functions which are similar to Moose `has`: `param` and\n`field`.\n\nA `param` is a required parameter (defaults may be used). A `field` is not\nintended to be passed to the constructor.\n\n**Note**: the `has` function is still available, even if it's not needed.\nUnlike `param` and `field`, it still requires an `is` option.\n\nAlso, while your author likes the postfix block syntax, it's not required. You\ncan even safely inline multiple packages in the same file:\n\n```perl\npackage My::Point;\nuse MooseX::Extended types =\u003e 'Num';\n\nparam [ 'x', 'y' ] =\u003e ( isa =\u003e Num );\n\npackage My::Point::Mutable;\nuse MooseX::Extended;\nextends 'My::Point';\n\nparam [ '+x', '+y' ] =\u003e ( writer =\u003e 1, clearer =\u003e 1, default =\u003e 0 );\n\nsub invert ($self) {\n    my ( $x, $y ) = ( $self-\u003ex, $self-\u003ey );\n    $self-\u003eset_x($y);\n    $self-\u003eset_y($x);\n}\n\n# MooseX::Extended will cause this to return true, even if we try to return\n# false\n0;\n```\n\n# CONFIGURATION\n\nYou may pass an import list to [MooseX::Extended](https://metacpan.org/pod/MooseX%3A%3AExtended).\n\n```perl\nuse MooseX::Extended\n  excludes =\u003e [qw/StrictConstructor carp/],      # I don't want these features\n  types    =\u003e [qw/compile PositiveInt HashRef/]; # I want these type tools\n```\n\n## `types`\n\nAllows you to import any types provided by [MooseX::Extended::Types](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3ATypes).\n\nThis:\n\n```perl\nuse MooseX::Extended::Role types =\u003e [qw/compile PositiveInt HashRef/];\n```\n\nIs identical to this:\n\n```perl\nuse MooseX::Extended::Role;\nuse MooseX::Extended::Types qw( compile PositiveInt HashRef );\n```\n\n## `excludes`\n\nYou may find some features to be annoying, or even cause potential bugs (e.g.,\nif you have a `croak` method, our importing of `Carp::croak` will be a\nproblem.\n\nA single argument to `excludes` can be a string. Multiple `excludes` require\nan array reference:\n\n```perl\n    use MooseX::Extended excludes =\u003e [qw/StrictConstructor autoclean/];\n```\n\nYou can exclude the following:\n\n- `StrictConstructor`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'StrictConstructor';\n    ```\n\n    Excluding this will no longer import `MooseX::StrictConstructor`.\n\n- `autoclean`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'autoclean';\n    ```\n\n    Excluding this will no longer import `namespace::autoclean`.\n\n- `c3`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'c3';\n    ```\n\n    Excluding this will no longer apply the C3 mro.\n\n- `carp`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'carp';\n    ```\n\n    Excluding this will no longer import `Carp::croak` and `Carp::carp`.\n\n- `immutable`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'immutable';\n    ```\n\n    Excluding this will no longer make your class immutable.\n\n- `true`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'true';\n    ```\n\n    Excluding this will require your module to end in a true value.\n\n- `param`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'param';\n    ```\n\n    Excluding this will make the `param` function unavailable.\n\n- `field`\n\n    ```perl\n    use MooseX::Extended excludes =\u003e 'field';\n    ```\n\n    Excluding this will make the `field` function unavailable.\n\n## `includes`\n\nSeveral _optional_ features of [MooseX::Extended](https://metacpan.org/pod/MooseX%3A%3AExtended) make this module much more\npowerful. For example, to include try/catch and a `method` keyword:\n\n```perl\n    use MooseX::Extended includes =\u003e [ 'method', 'try' ];\n```\n\nA single argument to `includes` can be a string. Multiple `includes` require\nan array reference:\n\n```perl\n    use MooseX::Extended includes =\u003e [qw/method try/];\n```\n\nSee [MooseX::Extended::Manual::Includes](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AIncludes) for more information.\n\n# REDUCING BOILERPLATE\n\nLet's say you've settled on the following feature set:\n\n```perl\nuse MooseX::Extended\n  excludes =\u003e [qw/StrictConstructor carp/],\n  includes =\u003e 'method',\n  types    =\u003e ':Standard';\n```\n\nAnd you keep typing that over and over. We've removed a lot of boilerplate,\nbut we've added different boilerplate. Instead, just create\n`My::Custom::Moose` and `use My::Custom::Moose;`. See\n[MooseX::Extended::Custom](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3ACustom) for details.\n\n# IMMUTABILITY\n\n## Making Your Class Immutable\n\nYou no longer need to end your Moose classes with:\n\n```\n__PACKAGE__-\u003emeta-\u003emake_immutable;\n```\n\nThat prevents further changes to the class and provides some optimizations to\nmake the code run much faster. However, it's somewhat annoying to type. We do\nthis for you, via [B::Hooks::AtRuntime](https://metacpan.org/pod/B%3A%3AHooks%3A%3AAtRuntime). You no longer need to do this yourself.\n\n## Making Your Instance Immutable\n\nBy default, attributes defined via `param` and `field` are read-only.\nHowever, if they contain a reference, you can fetch the reference, mutate it,\nand now everyone with a copy of that reference has mutated state.\n\nTo handle that, we offer a new `clone =\u003e $clone_type` pair for attributes.\n\nSee the [MooseX::Extended::Manual::Cloning](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3ACloning) documentation.\n\n# OBJECT CONSTRUCTION\n\nObject construction for [MooseX::Extended](https://metacpan.org/pod/MooseX%3A%3AExtended) is identical to Moose because\nMooseX::Extended _is_ Moose, so no changes are needed.  However, in addition\nto `has`, we also provide `param` and `field` attributes, both of which are\n`is =\u003e 'ro'` by default.\n\nThe `param` is _required_, whether by passing it to the constructor, or using\n`default` or `builder`.\n\nThe `field` is _forbidden_ in the constructor and is lazy if it has a\nbuilder, because that builder is often dependent on attributes set in the\nconstructor (and why call it if it's not used?).\n\nHere's a short example:\n\n```perl\npackage Class::Name {\n    use MooseX::Extended types =\u003e [qw(compile Num NonEmptyStr Str)];\n\n    # these default to 'ro' (but you can override that) and are required\n    param _name =\u003e ( isa =\u003e NonEmptyStr, init_arg =\u003e 'name' );\n    param title =\u003e ( isa =\u003e Str,         required =\u003e 0 );\n\n    # fields must never be passed to the constructor\n    # note that -\u003etitle and -\u003ename are guaranteed to be set before\n    # this because fields are lazy by default\n    field name =\u003e (\n        isa     =\u003e NonEmptyStr,\n        default =\u003e sub ($self) {\n            my $title = $self-\u003etitle;\n            my $name  = $self-\u003e_name;\n            return $title ? \"$title $name\" : $name;\n        },\n    );\n}\n```\n\nSee [MooseX::Extended::Manual::Construction](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AConstruction) for a full explanation.\n\n# ATTRIBUTE SHORTCUTS\n\nWhen using `field` or `param`, we have some attribute shortcuts:\n\n```perl\nparam name =\u003e (\n    isa       =\u003e NonEmptyStr,\n    writer    =\u003e 1,   # set_name\n    reader    =\u003e 1,   # get_name\n    predicate =\u003e 1,   # has_name\n    clearer   =\u003e 1,   # clear_name\n    builder   =\u003e 1,   # _build_name\n);\n\nsub _build_name ($self) {\n    ...\n}\n```\n\nYou can also do this:\n\n```perl\nparam name ( isa =\u003e NonEmptyStr, builder =\u003e sub {...} );\n```\n\nThat's the same as:\n\n```perl\nparam name ( isa =\u003e NonEmptyStr, builder =\u003e '_build_name' );\n\nsub _build_name {...}\n```\n\nSee [MooseX::Extended::Manual::Shortcuts](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AShortcuts) for a full explanation.\n\n# INVALID ATTRIBUTE DEFINITIONS\n\nThe following [Moose](https://metacpan.org/pod/Moose) code will print `WhoAmI`. However, the second attribute\nname is clearly invalid.\n\n```perl\npackage Some::Class {\n    use Moose;\n\n    has name   =\u003e ( is =\u003e 'ro' );\n    has '-bad' =\u003e ( is =\u003e 'ro' );\n}\n\nmy $object = Some::Class-\u003enew( name =\u003e 'WhoAmI' );\nsay $object-\u003ename;\n```\n\n`MooseX::Extended` will throw a\n[Moose::Exception::InvalidAttributeDefinition](https://metacpan.org/pod/Moose%3A%3AException%3A%3AInvalidAttributeDefinition) exception if it encounters an\nillegal method name for an attribute.\n\nThis also applies to various attributes which allow method names, such as\n`clone`, `builder`, `clearer`, `writer`, `reader`, and `predicate`.\n\nTrying to pass a defined `init_arg` to `field` will also throw this\nexception, unless the init\\_arg begins with an underscore. (It is sometimes\nuseful to be able to define an `init_arg` for unit testing.)\n\n# BUGS AND LIMITATIONS\n\nNone known at this time.\n\n# MANUAL\n\n- [MooseX::Extended::Manual::Tutorial](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3ATutorial)\n- [MooseX::Extended::Manual::Overview](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AOverview)\n- [MooseX::Extended::Manual::Construction](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AConstruction)\n- [MooseX::Extended::Manual::Includes](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AIncludes)\n- [MooseX::Extended::Manual::Shortcuts](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3AShortcuts)\n- [MooseX::Extended::Manual::Cloning](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3AManual%3A%3ACloning)\n\n# RELATED MODULES\n\n- [MooseX::Extended::Types](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3ATypes) is included in the distribution.\n\n    This provides core types for you.\n\n- [MooseX::Extended::Role](https://metacpan.org/pod/MooseX%3A%3AExtended%3A%3ARole) is included in the distribution.\n\n    `MooseX::Extended`, but for roles.\n\n# TODO\n\nSome of this may just be wishful thinking. Some of this would be interesting if\nothers would like to collaborate.\n\n## Configurable Types\n\nWe provide `MooseX::Extended::Types` for convenience, along with the `declare` \nfunction. We should write up (and test) examples of extending it.\n\n## `BEGIN::Lift`\n\nThis idea maybe belongs in `MooseX::Extended::OverKill`, but ...\n\nQuite often you see things like this:\n\n```\nBEGIN { extends 'Some::Parent' }\n```\n\nOr this:\n\n```perl\nsub serial_number; # required by a role, must be compile-time\nhas serial_number =\u003e ( ... );\n```\n\nIn fact, there are a variety of Moose functions which would work better if\nthey ran at compile-time instead of runtime, making them look a touch more\nlike native functions. My various attempts at solving this have failed, but I\nconfess I didn't try too hard.\n\n# NOTES\n\nThere are a few things you might be interested to know about this module when\nevaluating it.\n\nMost of this is written with bog-standard [Moose](https://metacpan.org/pod/Moose), so there's nothing\nterribly weird inside, but you may wish to note that we use\n[B::Hooks::AtRuntime](https://metacpan.org/pod/B%3A%3AHooks%3A%3AAtRuntime) and [true](https://metacpan.org/pod/true). They seem sane, but _caveat emptor_.\n\n# SEE ALSO\n\n- [Corinna](https://github.com/Ovid/Cor)\n\n    The RFC of the new version of OOP planned for the Perl core.\n\n- [MooseX::Modern](https://metacpan.org/pod/MooseX::Modern)\n\n    MooseX::Modern - Precision classes for Modern Perl\n\n- [Zydeco](https://metacpan.org/pod/Zydeco)\n\n    Zydeco - Jazz up your Perl\n\n- [Dios](https://metacpan.org/pod/Dios)\n\n    Dios - Declarative Inside-Out Syntax\n\n- [MooseX::AttributeShortcuts](https://metacpan.org/pod/MooseX::AttributeShortcuts)\n\n    MooseX::AttributeShortcuts - Shorthand for common attribute options\n\n# AUTHOR\n\nCurtis \"Ovid\" Poe \u003ccurtis.poe@gmail.com\u003e\n\n# COPYRIGHT AND LICENSE\n\nThis software is Copyright (c) 2022 by Curtis \"Ovid\" Poe.\n\nThis is free software, licensed under:\n\n```\nThe Artistic License 2.0 (GPL Compatible)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovid%2Fmoosex-extended","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovid%2Fmoosex-extended","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovid%2Fmoosex-extended/lists"}