{"id":19640403,"url":"https://github.com/kentnl/dist-zilla-plugin-beam-connector","last_synced_at":"2025-07-18T03:33:40.586Z","repository":{"id":56836894,"uuid":"55686538","full_name":"kentnl/Dist-Zilla-Plugin-Beam-Connector","owner":"kentnl","description":"Connect events to listeners in Dist::Zilla plugins","archived":false,"fork":false,"pushed_at":"2017-03-07T07:56:22.000Z","size":100,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-26T22:44:51.898Z","etag":null,"topics":["dist-zilla","perl"],"latest_commit_sha":null,"homepage":null,"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/kentnl.png","metadata":{"files":{"readme":"README.mkdn","changelog":"Changes","contributing":"CONTRIBUTING.pod","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-07T10:43:04.000Z","updated_at":"2017-02-28T02:26:20.000Z","dependencies_parsed_at":"2022-08-27T03:08:14.475Z","dependency_job_id":null,"html_url":"https://github.com/kentnl/Dist-Zilla-Plugin-Beam-Connector","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/kentnl/Dist-Zilla-Plugin-Beam-Connector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kentnl%2FDist-Zilla-Plugin-Beam-Connector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kentnl%2FDist-Zilla-Plugin-Beam-Connector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kentnl%2FDist-Zilla-Plugin-Beam-Connector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kentnl%2FDist-Zilla-Plugin-Beam-Connector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kentnl","download_url":"https://codeload.github.com/kentnl/Dist-Zilla-Plugin-Beam-Connector/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kentnl%2FDist-Zilla-Plugin-Beam-Connector/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265696689,"owners_count":23812825,"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":["dist-zilla","perl"],"created_at":"2024-11-11T14:05:42.178Z","updated_at":"2025-07-18T03:33:40.562Z","avatar_url":"https://github.com/kentnl.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nDist::Zilla::Plugin::Beam::Connector - Connect events to listeners in Dist::Zilla plugins.\n\n# VERSION\n\nversion 0.001004\n\n# SYNOPSIS\n\n    [Some::PluginA / PluginA]\n    [Some::PluginB / PluginB]\n\n    [Beam::Connector]\n    ; PluginA emitting event 'foo' passes the event to PluginB\n    on   = plugin:PluginA#foo    =\u003e   plugin:PluginB#handle_foo\n    on   = plugin:PluginA#bar    =\u003e   plugin:PluginB#handle_bar\n    ; Load 'beam.yml' as a Beam::Wire container\n    container = beam.yml\n    ; Handle Dist::Zilla plugin events with arbitrary classes\n    ; loaded by Beam::Wire\n    on   = plugin:PluginA#foo    =\u003e   container:servicename#handle_foo\n    on   = plugin:PluginA#bar    =\u003e   container:otherservicename#handle_bar\n\n# DESCRIPTION\n\nThis module aims to allow [`Dist::Zilla`](https://metacpan.org/pod/Dist::Zilla) to use plugins\nusing [`Beam::Event`](https://metacpan.org/pod/Beam::Event) and [`Beam::Emitter`](https://metacpan.org/pod/Beam::Emitter),\nand perhaps reduce the need for massive amounts of composition and role application\nproliferating `CPAN`.\n\nThis is in lieu of a decent dependency injection system, and is presently relying\non `Dist::Zilla` to load and construct the plugins itself, and then you just connect\nthe plugins together informally, without necessitating each plugin be specifically\ntailored to the recipient.\n\nHopefully, this may also give scope for non-`dzil` plugins being loadable into memory\nsome day, and allowing message passing of events to those plugins. ( Hence, the `plugin:` prefix )\n\nA Real World Example of what a future could look like?\n\n    [GatherDir]\n\n    [Test::Compile]\n\n    [Beam::Connector]\n    on = plugin:GatherDir#collect =\u003e plugin:Test::Compile#generate_test\n\n`GatherDir` in this example would build a mutable tree of files,\nattach them to an event `::GatherDir::Tree`, and pass that event to `Test::Compile#generate_test`,\nwhich would then add ( or remove, or mutate ) any files in that tree.\n\nTree state mutation then happens in order of prescription, in the order given\nby the various `on` declarations.\n\nThus, a single plugin can be in 2 places in the same logical stage.\n\n    [Beam::Connector]\n    on = plugin:GatherDir#collect =\u003e plugin:Test::Compile#generate_test\n    ; lots more collectors here\n    on = plugin:GatherDir#collect =\u003e plugin:Test::Compile#finalize_test\n\nWhereas presently, order of affect is either governed by:\n\n- phase - where you can add but not remove or mutate, mutate but not add or remove, remove, but not add or mutate\n- plugin order - where a single plugin cant be both early in a single phase and late\n\nIf that example is not convincing enough for you, consider all the different ways\nthere are presently for implementing `[MakeMaker]`. If you're following the standard logic\nits fine, but as soon as you set out of the box, you have a few things you're going to have to do instead:\n\n- Subclass `MakeMaker` in some way\n- Re-implement `MakeMaker` in some way\n- Fuss a lot with phase ordering and then inject code in the `File` that `MakeMaker` generates.\n\nThese approaches all work, but they're an open door to everyone re-implementing the same thing\nthousands of times over.\n\n    [MakeMaker]\n\n    [DynamicPrereqs]\n    -phases = none\n\n    [Beam::Connector]\n    on = plugin:MakeMaker#collect_augments =\u003e plugin:DynamicPrereqs#inject_augments\n\n`MakeMaker` here can just create an `event`, pass it to `DynamicPrereqs`,\n`DynamicPrereqs` can inject its desired content into the `event`,\nand then `MakeMaker` can integrate the injected events at \"wherever\" the right place for them is.\n\nThis is much superior to scraping the generated text file and injecting events\nat a given place based on a `RegEx` match.\n\n# PARAMETERS\n\n## `container`\n\nAllows loading an arbitrary `Beam::Wire` container [specification](https://metacpan.org/pod/Beam::Wire::Help::Config), initializing the\nrelevant objects lazily, and connecting them to relevant events emitted by `dzil` plugins.\n\n    [Beam::Connector]\n    container = inc/dist_beam.yml\n\nThe value can be a path to any file name that `Beam::Wire-\u003enew( file =\u003e ... )` understands, (which itself\nis any file name that `Config::Any-\u003eload_files` understands).\n\nItems in loaded container can then be referred to by their identifiers to the [`on`](#on) parameter in the form\n\n    container:${name}#${method}\n\nFor example:\n\n    [Beam::Connector]\n    container = inc/dist_beam.yml\n    on = plugin:GatherDir#gather_files =\u003e container:file_gatherer#on_gather_files\n\nThis would register the object called `file_gatherer` inside the container to be a recipient of any events called\n`gather_files` emitted by the plugin _named_ `GatherDir`\n\n## `on`\n\nDefines a connection between an event emitter and a listener.\n\nThe general syntax is:\n\n    on = emitterspec =\u003e listenerspec\n\nWhere `emitterspec` and `listenerspec` are of the form\n\n    objectnamespace:objectname#connector\n\n### `objectnamespace`\n\nThere are presently two defined object name-spaces.\n\n- `plugin`: Resolves `objectname` to a `Dist::Zilla` plugin by its `name` identifier\n- `container`: Resolves `objectname` to an explicitly named object inside an associated [`container`](#container)\n\n### `connector`\n\nFor an `emitter`, the `connector` property identifies the name of the event that is expected to be emitted by\nthat `emitter`\n\nFor a `listener`, the `connector` property identifies the name of a `method` that is expected to receive the event.\n\n# WRITING EVENT EMITTERS\n\nAdding support for hookable events in new and existing `Dist::Zilla` plugins is relatively straight-forward,\nand uses [`Beam::Emitter`](https://metacpan.org/pod/Beam::Emitter)\n\n    # Somewhere after `use Moose`\n    with \"Beam::Emitter\";\n\nAnd your class is now ready to broadcast events, and plugins are now able to hook events. Even though they don't\nexist yet.\n\nBut that's not very useful in itself. You need to find good places in your code to write events, and construct\nlittle bundles of state, \"messages\" to pass around, and perhaps, allow modifying.\n\n## Designing an Event\n\nYou want to start off designing an event class that communicates the _absolute minimum_ required to be useful.\n\nCarrying too much state, or too much indirect state is the enemy.\n\nFor instance, it would generally be unwise to design an Event that you passed to something which carried a `$zilla`\ninstance with it.\n\nYou want to make it as obscure as possible who is even sending the event, as the contents of the event should be usable\nin total isolation, because you have no idea where your events are going to get sent ( because that is outside the\nscope of your plugin ), and receivers have no solid expectations of where events are going to come from ( because that\nis dictated by the connector ).\n\n## Namespace and Indexing recommendations\n\nIt is presently recommended you define these events inline somewhere, either in the plugin that emits them,\nor in some shared container.\n\nThe **recommended namespace** scheme to follow is:\n\n    Dist::Zilla::Event::\n\nPreferably, structuring it similar to your plugin\n\n    Dist::Zilla::Plugin::Thing::Dooer\n    Dist::Zilla::Event::Thing::Dooer::BeforeDoingThing\n\nThis I'm sure you'll agree is much nicer than\n\n    Dist::Zilla::Plugin::Thing::Dooer::BeforeDoingThingEvent # O_O\n    Dist::Zilla::Plugin::BeforeDoingThingEvent               # Not a plugin\n\nIt is also recommended to _NOT_ index said Event packages at present, as that\nwould encourage people depending on the events at some point, which for this system, is\nlikely unwanted toxicity.\n\nOnly people emitting events should be caring about loading the class.\n\n## Implementing an Event\n\nEvents themselves are quite straight forward: They're just objects, objects extending\n[`Beam::Event`](https://metacpan.org/pod/Beam::Event).\n\nThis is an example event definition: It will communicate a file name it intends to prepend lines to\nand pass a mutable, empty array for the event handler to inject lines into.\n\n    package # hide from PAUSE\n      Dist::Zilla::Event::Prepender::BeforePrepend;\n\n    use Moose;  # or Moo, both work\n    extends \"Beam::Event\"\n\n    has 'filename' =\u003e (\n        is       =\u003e 'ro',\n        isa      =\u003e Str,\n        required =\u003e 1,\n    );\n    has 'lines' =\u003e (\n        is      =\u003e 'rw',\n        isa     =\u003e ArrayRef[Str],\n        lazy    =\u003e 1,\n        default =\u003e sub { [] },\n    );\n    __PACKAGE__-\u003emeta-\u003emake_immutable;\n\nSee [Using Custom Events in Beam::Emitter](https://metacpan.org/pod/Beam::Emitter#Using-Custom-Events) for details.\n\n## Emitting and Handling an Event\n\nOnce you have an Event class designed, gluing it into your code is also quite simple:\n\n    # somewhere deep in your plugin\n\n    my $event = $self-\u003eemit(\n      'before_append',                                          # the \"name\" of the event, this corresponds to the \"connector\"\n                                                                # property in Beam::Connector\n\n      class =\u003e 'Dist::Zilla::Event::Prepender::BeforePrepend',  # The class to construct an instance of\n\n      filename =\u003e 'lib/Foo.pm',                                 # attribute property of the Event object.\n    );\n\nAn instance of `class` is created with the defined name, and is passed in-order to all the objects who subscribed to the\n`before_append` event, and then returned once they're done.\n\nAnd then you can extract any of the state in the passed object and use it to do your work.\n\n# WRITING EVENT LISTENERS\n\nFortunately, the requirements for an Event Receiver is **very** low.\n\n## Receiving Events\n\nIf you're using the `Dist::Zilla::Plugin`/`plugin:` approach, all that is required is\n\n- A Valid `Dist::Zilla` plugin that registers in `$zilla-\u003eplugins`\n- Some method name of any description that can be passed an argument\n\nFor Example:\n\n    package My::Plugin;\n\n    use Moose;\n    with 'Dist::Zilla::Role::Plugin';\n\n    sub on_before_append {\n      my ( $self, $event ) = @_;\n      ...\n    }\n\nIf you're using the `Beam::Wire`/`container:` approach, all that is required is:\n\n- A named object\n- Some method name of any description that can be passed an argument\n\nFor Example:\n\n    package My::Listener;\n\n    sub new { bless {}, $_[0] }\n\n    sub on_before_append {\n      my ( $self, $event ) = @_;\n      ...\n    }\n\nThese listeners will do nothing on their own, but have events routed to them by\nrelevant `Beam` configuration.\n\n## Identifying and Handling Events\n\nYour method will be called with one argument: The event.\n\n    sub on_whatever {\n      my ( $self, $event ) = @_;\n\n    }\n\nWhat sort of events you receive of course depends on who sent them.\n\nYou can then filter them the same way as you would with any Perl Object,\nvia `-\u003eisa` etc,\n\n    sub on_whatever {\n      my ( $self, $event ) = @_;\n      if ( $event-\u003eisa('Dist::Zilla::Plugin::Prepender::AppenderEvent') ) {\n\n      }\n    }\n\nBut you can identify events by other means, via the `-\u003ename` property.\n\n    sub on_whatever {\n      my ( $self, $event ) = @_;\n      if ( q[before_append] eq $event-\u003ename ) ) {\n\n      }\n    }\n\nYou can then read the data of the event, or potentially modify it in-place, to communicate\ndata back to the sender of the event.\n\n    sub on_whatever {\n      my ( $self, $event ) = @_;\n      if ( q[before_append] eq $event-\u003ename ) ) {\n        push @{$event-\u003elines}, 'use Moose;' if $event-\u003efilename =~ /\\bMooseX\\b/; # Rediculous example I know.\n      }\n    }\n\nBut you don't need to return anything from the `sub`, return values are entirely ignored.\n\n# FOOTNOTE\n\n`Beam::Event` and `Beam::Emitter` have some tools for controlling intra-event flow,\nhowever, their usage is not 100% clear and their API may be subject to change in future.\n\nSo I have deleted the [relevant instruction on this](https://github.com/kentnl/Dist-Zilla-Plugin-Beam-Connector/compare/1c312f2...5025113)\nand it will be resurrected when I'm more sure about how it should be instructed.\n\n# AUTHOR\n\nKent Fredric \u003ckentnl@cpan.org\u003e\n\n# COPYRIGHT AND LICENSE\n\nThis software is copyright (c) 2017 by Kent Fredric \u003ckentfredric@gmail.com\u003e.\n\nThis is free software; you can redistribute it and/or modify it under\nthe same terms as the Perl 5 programming language system itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkentnl%2Fdist-zilla-plugin-beam-connector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkentnl%2Fdist-zilla-plugin-beam-connector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkentnl%2Fdist-zilla-plugin-beam-connector/lists"}