kentnl / File-ShareDir-ProjectDistDir

Simple set-and-forget using of a '/share' directory in your projects root
Other
5 stars 5 forks source link

NAME

File::ShareDir::ProjectDistDir - Simple set-and-forget using of a '/share' directory in your projects root

VERSION

version 1.000010

DETERRENT

STOP!. Before using this distribution, some warnings MUST be considered.

The primary use-case for this module is targeted at development projects that are NOT intended for CPAN.

As such, using it for CPAN is generally a bad idea, and better solutions generally involve the less fragile Test::File::ShareDir, constraining any magical behavior exclusively to where it is needed: Tests.

Why?

For these reason, it is dangerous to rely on this distribution while striving to produce quality code.

If this documentation is not sufficient to dissuade you, I must strongly implore you to choose the "strict" mechanism, because that substantially reduces the possibilities with regards to false-positive of potential dev directories.

I have in mind to find a better mechanism to deliver the same objective, but no solutions are forthcoming at this time.

SYNOPSIS

package An::Example::Package;

use File::ShareDir::ProjectDistDir;

# during development, $dir will be $projectroot/share
# but once installed, it will be wherever File::Sharedir thinks it is.
my $dir = dist_dir('An-Example')

Project layout requirements:

$project/
$project/lib/An/Example/Package.pm
$project/share/   # files for package 'An-Example' go here.

You can use a directory name other than 'share' ( Assuming you make sure when you install that, you specify the different directory there also ) as follows:

use File::ShareDir::ProjectDistDir ':all', defaults => {
  projectdir => 'templates',
};

METHODS

import

use File::ShareDir::ProjectDistDir (@args);

This uses Sub::Exporter to do the heavy lifting, so most usage of this module can be maximized by understanding that first.

Sub::Exporter tricks of note.

Make your own sharedir util

package Foo::Util;

sub import {
    my ($caller_class, $caller_file, $caller_line )  = caller();
    if ( grep { /share/ } @_ ) {
        require File::ShareDir::ProjectDistDir;
        File::ShareDir::ProjectDistDir->import(
            filename => $caller_file,
            dist_dir => { distname => 'myproject' , -as => 'share' },
            dist_dir => { distname => 'otherproject' , -as => 'other_share' , projectdir => 'share2' },
            -into => $caller_class,
        );
    }
}

....

package Foo;
use Foo::Util qw( share );

my $dir = share();
my $other_dir => other_share();

build_dist_dir

use File::ShareDir::ProjectDirDir ( : all );

#  this calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_dir(
  'dist_dir' => {},
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } }
);

use File::ShareDir::ProjectDirDir ( qw( :all ), distname => 'example-dist' );

#  this calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_dir(
  'dist_dir' => {},
  { distname => 'example-dist', defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } }
);

use File::ShareDir::ProjectDirDir
  dist_dir => { distname => 'example-dist', -as => 'mydistdir' },
  dist_dir => { distname => 'other-dist',   -as => 'otherdistdir' };

# This calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_dir(
  'dist_dir',
  { distname => 'example-dist' },
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } },
);
my $othercoderef = File::ShareDir::ProjectDistDir->build_dist_dir(
  'dist_dir',
  { distname => 'other-dist' },
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } },
);

# And leverages Sub::Exporter to create 2 subs in your package.

Generates the exported 'dist_dir' method. In development environments, the generated method will return a path to the development directories 'share' directory. In non-development environments, this simply returns File::ShareDir::dist_dir.

As a result of this, specifying the Distribution name is not required during development ( unless in strict mode ), however, it will start to matter once it is installed. This is a potential avenues for bugs if you happen to name it wrong.

In strict mode, the distribution name is ALWAYS REQUIRED, either at least at import or dist_dir() time.

build_dist_file

use File::ShareDir::ProjectDirDir ( : all );

#  this calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_file(
  'dist_file' => {},
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } }
);

use File::ShareDir::ProjectDirDir ( qw( :all ), distname => 'example-dist' );

#  this calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_file(
  'dist_file' => {},
  { distname => 'example-dist', defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } }
);

use File::ShareDir::ProjectDirDir
  dist_file => { distname => 'example-dist', -as => 'mydistfile' },
  dist_file => { distname => 'other-dist',   -as => 'otherdistfile' };

# This calls
my $coderef = File::ShareDir::ProjectDistDir->build_dist_file(
  'dist_file',
  { distname => 'example-dist' },
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } },
);
my $othercoderef = File::ShareDir::ProjectDistDir->build_dist_file(
  'dist_file',
  { distname => 'other-dist' },
  { defaults => { filename => 'path/to/yourcallingfile.pm', projectdir => 'share' } },
);

# And leverages Sub::Exporter to create 2 subs in your package.

Generates the 'dist_file' method.

In development environments, the generated method will return a path to the development directories 'share' directory. In non-development environments, this simply returns File::ShareDir::dist_file.

Caveats as a result of package-name as stated in "build_dist_dir" also apply to this method.

SIGNIFICANT CHANGES

1.000000

Strict Mode.

Using Strict Mode

use File::ShareDir::ProjectDistDir ':all', strict => 1;
use File::ShareDir::ProjectDistDir 'dist_dir' => { strict => 1 };

Why you should use strict mode

Starting with 1.000000, there is a parameter strict that changes how sharedir resolution performs.

Without strict:

lib/...
share/...

With strict

lib/...
share/dist/Dist-Name-Here/...

This technique greatly builds resilience to the long standing problem with "develop" vs "install" heuristic ambiguity.

Here at least,

dist_dir('Dist-Name')

Will instead fall back to

@INC/auto/share/dist/Dist-Name

When

share/dist/Dist-Name

Does not exist.

This means if you have a layout like this:

<DEVROOT>/inc/<a local::lib path here>
<DEVROOT>/lib/<development files here>

Then when Foo-Bar-Baz is installed as:

<DEVROOT>/inc/lib/Foo/Bar/Baz.pm
<DEVROOT>/inc/lib/auto/share/dist/Foo-Bar-Baz

Then Baz.pm will not see the DEVROOT and assume "Hey, this is development" and then proceed to try finding files in DEVROOT/share

Instead, DEVROOT must have DEVROOT/share/dist/Foo-Bar-Baz too, otherwise it reverts to DEVROOT/inc/lib/auto...

Path::Class interfaces deprecated and dependency dropped.

If you have any dependence on this function, now is the time to get yourself off it.

Minimum Changes to stay with Path::Class short term.

As the dependency has been dropped on Path::Class, if you have CPAN modules relying on Path::Class interface, you should now at a very minimum start declaring

{ requires => "Path::Class" }

This will keep your dist working, but will not be future proof against further changes.

Staying with Path::Class long term.

Recommended approach if you want to stay using the Path::Class interface:

use File::ShareDir::... etc
use Path::Class qw( dir file );

my $dir = dir( dist_dir('Dist-Name') );

This should future-proof you against anything File::ShareDir may do in the future.

Versioning Scheme arbitrary converted to float

This change is a superficial one, and should have no bearing on how significant you think this release is.

It is a significant release, but the primary reason for the version change is simply to avoid compatibility issues in versions themselves.

However, outside that, x.y.z semantics are still intended to be semi-meaningful, just with less . and more 0

dev path determination now deferred to call time instead of use

This was essentially a required change to make strict mode plausible, because strict mode _requires_ the distname to be known, even in the development environment.

This should not have any user visible effects, but please, if you have any problems, file a bug.

file component determination wrested from File::ShareDir.

dist_file('foo','bar')

Is now simply sugar syntax for

path(dist_dir('foo'))->child('bar')

This should have no side effects in your code, but please file any bugs you experience.

( return value is still undef if the file does not exist, and still croak's if the file is not a file, or unreadable, but these may both be subject to change )

0.5.0 - Heuristics and Return type changes

New devdir heuristic

Starting with 0.5.0, instead of using our simple lib/../share pattern heuristic, a more advanced heuristic is used from the new Path::FindDev and Path::IsDev.

This relies on a more "concrete" marker somewhere at the top of your development tree, and more importantly, checks for the existence of specific files that are not likely to occur outside a project root.

lib and share based heuristics were a little fragile, for a few reasons:

So instead, as of 0.5.0, the heuristic revolves around certain specific files being in the dev directory.

Which is hopefully a more fault resilient mechanism.

New Return Types

Starting with 0.5.0, the internals are now based on Path::Tiny instead of Path::Class, and as a result, there may be a few glitches in transition.

Also, previously you could get a Path::Class::* object back from dist_dir and dist_file by importing it as such:

use File::ShareDir::ProjectDistDir
    qw( dist_dir dist_file ),
    defaults => { pathclass => 1 };

Now you can also get Path::Tiny objects back, by passing:

use File::ShareDir::ProjectDistDir
    qw( dist_dir dist_file ),
    defaults => { pathtiny => 1 };

For the time being, you can still get Path::Class objects back, it is deprecated since 1.000000

( In fact, I may even make 2 specific sub-classes of PDD for people who want objects back, as it will make the API and the code much cleaner )

AUTHOR

Kent Fredric kentnl@cpan.org

COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Kent Fredric kentnl@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.