The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use 5.008;
use strict;
use warnings;

package any::feature;
# ABSTRACT: Backwards-compatible handling of new syntactic features

use Perl::Version;
use Carp qw(croak);
use UNIVERSAL::require;

BEGIN {
    if ($] =~ /^5\.008/) {
        require mysubs;
    }
}
our $VERSION = '0.05';
my %dispatch = (
    activate => {
        say => {
            8 => sub {
                Perl6::Say->require or die $@;

                # no need to specify the target - mysubs uses
                # Devel::Pragma::ccstash, which does the right thing if this
                # module is subclassed
                mysubs->import(say => \&Perl6::Say::say);
            },
            10 => sub {
                feature->require or die $@;
                feature->import(qw(say));
            },
        },
    },
    deactivate => {
        say => {
            8 => sub {

                # no need to specify the target - mysubs uses
                # Devel::Pragma::ccstash, which does the right thing if this
                # module is subclassed
                mysubs->unimport('say');
            },
            10 => sub {
                feature->require or die $@;
                feature->unimport(qw(say));
            },
        },
    },
);

sub get_effective_revision {
    our $perl_version ||= Perl::Version->new($]);
    $perl_version->revision;
}

sub get_effective_version {
    our $perl_version ||= Perl::Version->new($]);
    my $version = $perl_version->version;

    # assume perl 5.11 and later behave the same as perl 5.10
    $version = 10 if $version > 10;
    $version;
}

sub dispatch {
    my ($direction, $target, $feature) = @_;
    my $code = $dispatch{$direction}{$feature}{ get_effective_version() };
    unless (ref $code eq 'CODE') {
        croak "unknown feature '$feature'\n";
    }
    $code->($target);
}

sub activate {
    my ($target, $feature) = @_;
    dispatch(activate => $target, $feature);
}

sub deactivate {
    my ($target, $feature) = @_;
    dispatch(deactivate => $target, $feature);
}

sub import {
    my $pkg    = shift;
    my $target = (caller());
    $Carp::CarpInternal{__PACKAGE__}++;
    unless (get_effective_revision() == 5) {
        croak "perl 5.* required\n";
    }
    for my $feature (@_) {
        activate($target, $feature);
    }
}

sub unimport {
    my $pkg    = shift;
    my $target = (caller());
    unless (get_effective_revision() == 5) {
        croak "perl 5.* required\n";
    }
    for my $feature (@_) {
        deactivate($target, $feature);
    }
}
1;


__END__
=pod

=head1 NAME

any::feature - Backwards-compatible handling of new syntactic features

=head1 VERSION

version 1.100840

=head1 SYNOPSIS

    use any::feature 'say';
    say 'Hello, world!';

=head1 DESCRIPTION

=head2 THE PROBLEM

Perl 5.10 introduces new syntactic features which you can activate and
deactivate with the C<feature> module. You want to use the C<say> feature in a
program that's supposed to run under both Perl 5.8 and 5.10. So your program
looks like this:

    use feature 'say';
    say 'Hello, world!';

But this only works in Perl 5.10, because there is no C<feature> module in
Perl 5.8. So you write

    use Perl6::Say;
    say 'Hello, world!';

This works, but it's strange to force Perl 5.10 users to install L<Perl6::Say>
when the C<say> feature is included in Perl 5.10.

=head2 THE SOLUTION

Use C<any::feature>!

WARNING: This is just a proof-of-concept.

C<any::feature> can be used like Perl 5.10's C<feature> and will try to "do
the right thing", regardless of whether you use Perl 5.8 or Perl 5.10.

At the moment, this is just a proof-of-concept and only handles the C<say>
feature. If things work out, I plan to extend it with other Perl 5.10
features.

The following programs should work and exhibit the same behaviour both in Perl
5.8 and Perl 5.10.

This program will work:

    use any::feature 'say';
    say 'Hello, world!';

This program will fail at compile-time:

    use any::feature 'say';
    say 'Hello, world!';

    no any::feature 'say';
    say 'Oops';

The features are lexically scoped, which is how they work in Perl 5.10:

    {
        use any::feature 'say';
        say 'foo';
    }
    say 'bar';     # dies at compile-time

=head1 FUNCTIONS

=head2 dispatch

Takes as arguments a direction (C<activate> or C<deactivate>), a package name
and a feature name. Activates or deactivates the given feature for the given
package.

=head2 activate

Takes as arguments a package name and a feature name. Uses C<dispatch()> to
activate the given feature in the given package.

=head2 deactivate

Takes as arguments a package name and a feature name. Uses C<dispatch()> to
deactivate the given feature in the given package.

=head2 import

Takes the same arguments as Perl 5.10's C<use feature> pragma. Uses
C<activate()> and C<deactivate()> to do its job.

=head2 unimport

Takes the same arguments as Perl 5.10's C<no feature> pragma. Uses
C<activate()> and C<deactivate()> to do its job.

=head2 get_effective_version

Uses L<Perl::Version> to get the version number of the current perl
interpreter.  This is used to decide the course of action.

=head2 get_effective_revision

Uses L<Perl::Version> to get the revision number of the current perl
interpreter.  This is used to decide the course of action.

=head1 INSTALLATION

See perlmodinstall for information and options on installing Perl modules.

=head1 BUGS AND LIMITATIONS

No bugs have been reported.

Please report any bugs or feature requests through the web interface at
L<http://rt.cpan.org/Public/Dist/Display.html?Name=any-feature>.

=head1 AVAILABILITY

The latest version of this module is available from the Comprehensive Perl
Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
site near you, or see
L<http://search.cpan.org/dist/any-feature/>.

The development version lives at
L<http://github.com/hanekomu/any-feature/>.
Instead of sending patches, please fork this project using the standard git
and github infrastructure.

=head1 AUTHOR

  Marcel Gruenauer <marcel@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2009 by Marcel Gruenauer.

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

=cut