The process of writing a Mojolicious plugin

Tags:

I have gotten far enough into Mojolicious development to feel rather baffled again. This post is a work in progress, to be edited and filled in as I discover the answers.

According to the Rendering guide,

A skeleton for a full CPAN compatible plugin distribution can be automatically generated.

We are going to create a plugin called StaticText, so let's do:

$ mojo generate plugin StaticText

What that actually does is the following actions inside your current directory, as we can see from its output log:

  [mkdir] ./Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin
  [write] ./Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin/StaticText.pm
  [mkdir] ./Mojolicious-Plugin-StaticText/t
  [write] ./Mojolicious-Plugin-StaticText/t/basic.t
  [exist] ./Mojolicious-Plugin-StaticText
  [write] ./Mojolicious-Plugin-StaticText/Makefile.PL

Q: How exactly can I develop this inside an example application? The documentation's standard load procedure:

$self->plugin('Mojolicious::Plugin::StaticText');

fails because it doesn't look in Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin/ of course.

Q: How do I install my common personal plugins into my individual "apps" ? Presumably I should be able to "git checkout" them directly into... lib/Plugin/ ...? Obviously I'm not going to edit them into the MyApp:: namespace, right?

Q: Should use the Mojolicious::Plugin namespace for plugins that I consider to be eventual candidates for CPAN, or what is best practice for naming my personal plugins that I plan on using across multiple projects?

Q: I see how to pass parameters when loadiing a plugin, but does each plugin get loaded, or registered, just once, or can I have multiple instances of a plugin?

NOTE: Refer also to the Plugins document

Q: Where do I store "instance data" that is computed from the configuration parameters I pass when loading a plugin?

A: Plugins are simply modules, not objects. If we want to save configuration data at load-time, we could save that in an attribute of the application like this: $app->attr('some_attribute_name' => value) which we later access as just "$app->some_attribute_name".

A good example of how configuration parameters are passed, handled, and saved is http://search.cpan.org/~madcat/Mojolicious-Plugin-Database/lib/Mojolicious/Plugin/Database.pm

Behind the curtain: when you call attr, that actually builds and then eval(s) a string which defines a function by the supplied name. That function optionally sets (if called with a value), and always returns the value of the named key in the object upon which you originally called ->attr(). So in the case of doing $app->attr('foo') the function would create and use $attr->{'foo'}.

Q: The Mojolicious::Plugins guide shows the example code

$plugins->register_plugin('MyApp::Plugin::SomeThing', Mojolicious->new);

below the presumably required code:

use Mojolicious::Plugins;

my $plugins = Mojolicious::Plugins->new;
push @{$plugins->namespaces}, 'MyApp::Plugin';

Yet elsewhere, for example in http://mojolicio.us/perldoc/Mojolicious/Plugin/JSONConfig , we see a completely different style that runs with application start:

sub startup {
  my $self = shift;
  $self->plugin('Mojolicious::Plugin::StaticText');

What exactly is the difference between these two?

Q: In the version with

$plugins->register_plugin('MyApp::Plugin::SomeThing', Mojolicious->new);

what does the Mojolicious->new do? According to the documentation for Mojolicious->new(), that should "Construct a [whole other] new Mojolicious application and call 'startup'… set up logging… the renderer, static file server, default set of plugins…" We can't seriously be loading a whole new "app" nested inside of our current "app" can we? What's going on?

Q: After the name of the plugin, what happens to the remaining arguments to $self->plugin()?

A: The optional second argument must be either a reference (to a scalar, array, hash, subroutine, etc.) or a scalar. If it's just a plain scalar such as 3, like the plugin's register() subroutine will be called with a reference to a hash like this: {3, undef}. In general, you will probably want to pass a reference to a hash that contains your configuration parameters.