NAME
    portable::loader - load classes and roles which can be moved around your
    namespace

SYNOPSIS
    Define some classes:

      ## Nature.portable
      ##
      version = 1.0
      toolkit = "Moo"
  
      [class:Tree.has]
      leaf = { is = "lazy", type = "ArrayRef[Leaf]" }
  
      [class:Tree.can]
      add_leaf = {{{
        my $self = shift;
        push @{ $self->leaf }, @_;
        return $self; # for chaining
      }}}
      _build_leaf = {{{
        return [];
      }}}
  
      [class:Leaf.has]
      colour = { type = "Str", default = "green" }
  
      [class:Maple]
      extends = "Tree"

    Use the classes:

      ## script.pl
      ##
      use portable::lib '/var/lib/portable-libs';
      use portable::alias 'Nature';
  
      my $tree  = Nature->new_maple;
      $tree->add_leaf( Nature->new_leaf );
  
      # 'Nature' isn't really a Perl package.
      # It's just a sub that returns a string.

DESCRIPTION
    The intent of portable::loader is for classes and roles to be portable
    around your namespace. The idea is for classes and roles to not know their
    package names and not care about their package names. And for them to also
    not know or care about the package names of their "friends".

    (When I say their friends, I'm talking about a user-agent object which
    needs to be able to consume HTTP request objects and return HTTP response
    objects, maybe write to a cookie jar object, etc.)

    Typically in Perl code, package names are the one thing that is hard-coded
    everywhere and this can make things like dependency injection, and API
    versioning really difficult to do. Like if you need to make some major
    changes to your class's API, do you create an entirely new package with a
    different namespace, then wait for your consumers to update? Or do you
    keep the old namespace and deal with breakages.

    What if instead of doing this:

      use YourAPI::Tree;
      use YourAPI::Leaf;
  
      my $tree = YourAPI::Tree->new;
      $tree->add_leaf(YourAPI::Leaf->new);

    People could do this?

      use portable::loader;
      my $api = portable::loader->load("YourAPI");
  
      my $tree = $api->new_tree;
      $tree->add_leaf($api->new_leaf);

    The class names are not hard-coded anywhere. They are not even hard-coded
    in the definitions of the Leaf and Tree classes.

    And there's very little runtime overhead in doing this!

  Writing a portable library
   Syntax
    Portable libraries are conceptually any hashref suitable for passing to
    MooX::Press. A structure something like this:

      {
        version => 1.0,
        toolkit => "Moo",
        "class:Tree" => {
          has => [
            "leaf" => { is => "lazy", type => "ArrayRef[Leaf]" },
          ],
          can => [
            "add_leaf" => sub {
              my $self = shift;
              push @{ $self->leaf }, @_;
              return $self; # for chaining
            },
            "_build_leaf" => sub {
              return [];
            },
          ],
        },
        "class:Leaf" => {
          has => [
            "colour" => { type => "Str", default => "green" },
          ],
        },
        "class:Maple" => {
          extends => "Tree",
        },
      }

    You could save that as "Nature.portable.pl" and portable::loader would be
    able to load it.

    But although a library is conceptually a hashref, it can be written in
    other syntaxes. It could be written in JSON, if JSON::Eval is used to
    inflate coderefs in the JSON:

      {
        "version": 1.0,
        "toolkit": "Moo",
        "class:Tree": {
          "has": [
            "leaf": { "is": "lazy", "type": "ArrayRef[Leaf]" }
          ],
          "can": [
            "add_leaf": {
              "$eval": "sub { my $self = shift; push @{ $self->leaf }, @_; return $self; }" 
            },
            "_build_leaf: {
              "$eval": "sub { return []; }"
            }
          ]
        },
        "class:Leaf": {
          "has": [
            "colour": { "type": "Str", "default": "green" }
          ],
        },
        "class:Maple": {
          "extends": "Tree"
        }
      }

    If this is saved at "Nature.portable.json", portable::loader should be
    able to load it.

    The default format that portable::loader uses though, is TOML, an INI-like
    file format. portable::loader adds an extension to TOML allowing `{{{ ...
    }}}` to represent a coderef with Perl code inside. (The parsing is kind of
    naive, so don't expect nested coderefs to work and that kind of thing!

      version = 1.0
      toolkit = "Moo"
  
      [class:Tree.has]
      leaf = { is = "lazy", type = "ArrayRef[Leaf]" }
  
      [class:Tree.can]
      add_leaf = {{{
        my $self = shift;
        push @{ $self->leaf }, @_;
        return $self; # for chaining
      }}}
      _build_leaf = {{{
        return [];
      }}}
  
      [class:Leaf.has]
      colour = { type = "Str", default = "green" }
  
      [class:Maple]
      extends = "Tree"

   Design considerations
    When writing a library, the key thing to remember is that you don't know
    the final package names of any of your classes and roles.

    You can refer to other classes and roles from your library in type
    constraints, and that should "just work".

    Also, you can instantiate other classes in your methods using:

      [class:Maple.can]
      grow_red_leaf = {{{
        my $self = shift;
        my $leaf = $self->FACTORY->new_leaf(colour => "red");
        push @{ $self->leaf }, $leaf;
        return $self;
      }}}

    The `$self->FACTORY` method gives you something with a bunch of `new_*`
    methods for instantiating other objects from your library.

    You could even do this when defining the Leaf class:

      [class:Leaf.factory]
      new_leaf = {{{
        my ($factory, $class) = (shift, shift);
        return $class->new(@_);
      }}}
      new_red_leaf = {{{
        my ($factory, $class) = (shift, shift);
        return $class->new(colour => "red", @_);
      }}}

    And then your Maple class can do this:

      [class:Maple.can]
      grow_red_leaf = {{{
        my $self = shift;
        my $leaf = $self->FACTORY->new_red_leaf;
        push @{ $self->leaf }, $leaf;
        return $self;
      }}}

    The aim being for your Maple class to know as little as possible about how
    to build a leaf other than "I can get one from the factory".

    This makes it easy to override behaviour using Class::Method::Modifiers to
    wrap the `new_red_leaf` method of the factory.

  Loading a library
    portable::loader maintains its own version of @INC to locate libraries
    from: @portable::INC.

    You can use portable::lib to push directories onto it:

      use portable::lib '/var/lib/portable-libs';

    Or you can manipulate @portable::INC directly; it's just an array of
    strings. You should `use portable::lib` first though because portable::lib
    will push some default directories onto @portable::INC before it loads.

    Once you've set your search paths, you can load a library like this:

      use portable::loader;
      my $lib = portable::loader->load($libname);

    portable::loader will search for "$libname.portable.pl",
    "$libname.portable.json", "$libname.portable.toml", or "$libname.portable"
    (which will be assumed to be TOML). Other formats can be supported through
    plugins. (API will eventually be documented.)

    It will be parsed, loaded, classes built, etc, and a string will be
    returned which can be used

    If more than one is found, only one will be loaded. The order in which
    they are checked is currently not guaranteed, but the precedence of
    directories in @portable::INC will be respected.

    There are also `load_from_filename` and `load_from_hashref` methods if you
    already know the exact filename you want to load, or already have a
    hashref.

   Using portable::alias
    This:

      use portable::alias "Foo";

    Is roughly equivalent to this:

      use portable::loader;
      use constant "Foo" => portable::loader->load("Foo");

    This:

      use portable::alias "VeryLongName" => "ShortName";

    Means this:

      use constant "ShortName" => portable::loader->load("VeryLongName");

    So you can do:

      my $thing = Foo->new_someclass(%args);

    The "constant" exported by portable::alias isn't really a constant though.
    It accepts arguments. You can do:

      my $thing = Foo("SomeClass")->new(%args);
  
      my $type_constraint = Foo("SomeClass");
      my $type_constraint = Foo("SomeRole");

    Using portable::alias is a cleaner-looking alternative to using
    portable::loader in a lot of cases.

  Using a library
    Use the factory returned by portable::loader to create objects, then use
    the objects according to the library's documentation.

BUGS
    Please report any bugs to
    <http://rt.cpan.org/Dist/Display.html?Queue=portable-loader>.

SEE ALSO
    MooX::Press, JSON::Eval, TOML, Type::Tiny, Moo, Moose.

AUTHOR
    Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE
    This software is copyright (c) 2019 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.

DISCLAIMER OF WARRANTIES
    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.

