Choose Your Own Tutorial
Writing Your Own Plugins
Dist::Zilla does almost nothing on its own. Everything is performed by plugins. It ships with a big pile of plugins that address a lot of needs, and many more are available on the CPAN, but sometimes you need Dist::Zilla to do something that nobody has needed before, or that's very specific to your own distribution. When that happens, you can write your own plugin. Writing and testing plugins is easy. We'll write a short one right here.
Know Your Roles
Plugins are all run as needed based on the roles they perform. When files need to be gathered to fill out the distribution, FileGatherer plugins are run. When the distribution needs to be final-checked before release, BeforeRelease plugins are run. You should read about how dists are built to get a handle on some of the basic roles, and look at the core documentation for the complete list of plugin roles. This will help you know what kind of plugin you want to write.
For our example, we'll write a really basic plugin that adds a simple little file to our distribution. That means it's a FileGatherer. Reading the docs for FileGatherer, we see we only have to supply one method, gather_files
, which can then add files with the add_file
method. add_file
is passed an object that does Dist::Zilla::Role::File and that file becomes part of our distribution. So, our first pass at this plugin looks like this:
1: | package Dist::Zilla::Plugin::Credits; |
...and that's it! To use our plugin, we just add [Credits]
to our dist.ini. When we build our dist, the CREDITS file will be added to it.
Making Plugins Configurable
Often, we'll want to make our plugin configurable. That's easy, too. We just give it some attributes. For example, we might want to allow the filename to be configurable:
1: | package Dist::Zilla::Plugin::Credits; |
Now, the user can just add [Credits]
, like before, or he can pass in configuration:
1: | [Credits] |
If you want a configuration option that takes more than one value, you'll need to mark it as multivalue arg by having its name returned by mvp_multivalue_args
.
1: | package Dist::Zilla::Plugin::Credits; |
Now the user can use this configuration:
1: | [Credits] |
Testing Your Plugin
Now that you have a plugin that does more or less what you want, you should write some tests. (Alternately, you should have written tests by now. Feel free to pretend you didn't read the first part first, in that case.)
You'll need to include a fake dist with your dist, which you'll use for building in your tests. This might become easier with future versions of Dist::Zilla, but it's not too bad even now. Create a directory in your distribution root -- I usually use corpus -- and then make a little fake distribution under it. We'll call that distribution DZT, for now. You can use dzil new
or just create a lib/DZT.pm. In fact, I suggest you make lib/DZT/Sample.pm, which will make testing easier. To prevent corpus from being indexed, you might want to use the MetaNoIndex plugin to exclude it from PAUSE indexing. Make sure your test dist doesn't have a dist.ini.
At this point, you should have the following files in your plugin distribution:
corpus/DZT/lib/DZT.pm
lib/Dist/Zilla/Plugin/Credits.pm
t/credits.t
dist.ini
Our tests for the "Credits" plugin don't require any special properties of our distribution, so we can just make a simple test like this:
1: | # t/credits.t |
The library Test::DZil includes a bunch of routines to help test your dist, starting with Builder
, which returns a Dist::Zilla::Dist::Builder object that's pre-mixed with the behavior in Dist::Zilla::Tester. This lets us pass things to its from_config
method like the add_files
, which adds files to the dist root before building starts. It also takes the source directory (corpus/DZT) and copies it to a new temporary directory so that the source material won't be altered by testing.
When $tzil->build
is called, the dist is built into another temporary directory. During subsequent testing, you can look for source files in the relative directory source and built files in the relative directory build. That's just what we do, above, when we use the slurp_file
method to get the contents of a file we expect to exist in our built distribution.