summaryrefslogtreecommitdiffstats
path: root/lib/Exporter/Lexical.pm
blob: 48e4d729c1bb034bcc7ff9d438b8a95f117aab35 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package Exporter::Lexical;
use strict;
use warnings;
use 5.018;
# ABSTRACT: exporter for lexical subs

=head1 SYNOPSIS

  package My::Exporter;
  use Exporter::Lexical -exports => [ 'foo' ]
  sub foo { "FOO" }

  package MyApp;

  {
      use My::Exporter;
      warn foo(); # FOO
  }
  warn foo(); # Undefined subroutine &main::foo called

=head1 DESCRIPTION

This module allows you to export lexical subs from your exporter module. It is
implemented using the new C<lexical_subs> feature in perl 5.18, so the
functions truly are lexical (unlike some of the previous attempts).

This module is quite experimental, and may change a lot in the future as I
figure out how it should work. It is very much a proof of concept for the
moment.

=cut

use XSLoader;
XSLoader::load(
    __PACKAGE__,
    # we need to be careful not to touch $VERSION at compile time, otherwise
    # DynaLoader will assume it's set and check against it, which will cause
    # fail when being run in the checkout without dzil having set the actual
    # $VERSION
    exists $Exporter::Lexical::{VERSION}
        ? ${ $Exporter::Lexical::{VERSION} } : (),
);

sub import {
    my $package = shift;
    my %opts = @_;

    my $caller = caller;

    my $import = build_exporter(\%opts, $caller);

    {
        no strict 'refs';
        *{ $caller . '::import' } = $import;
    }
}

=func build_exporter(\%opts[, $caller])

  my $import Exporter::Lexical::build_exporter({
      -exports => ['foo'],
  });

This function just creates the method that it would install as your package's
C<import> method, without actually installing it. This lets you write your own
import method that does whatever you want it to do, while still being able to
export from it.

=cut

sub build_exporter {
    my ($opts, $caller) = @_;
    $caller //= caller;

    return sub {
        my $caller_stash = do {
            no strict 'refs';
            \%{ $caller . '::' };
        };
        my @exports = @{ $opts->{'-exports'} };
        my %exports = map { $_ => \&{ $caller_stash->{$_} } } @exports;

        for my $export (keys %exports) {
            lexical_import($export, $exports{$export});
        }

        # XXX there is a bug with lexical_import where the pad entry sequence
        # numbers are incorrect when used with 'use', so the first statement
        # after the 'use' statement doesn't see the lexical. hack around this
        # for now by injecting a dummy statement right after the 'use'.
        _lex_stuff(";1;");
    };
}

=func lexical_import($name, $sub)

Installs C<$sub> as a lexical subroutine into the currently compiling lexical
scope. Throws an error if there is no currently compiling lexical scope (for
instance, if this is called at runtime).

=cut

=head1 BUGS

No known bugs.

Please report any bugs through RT: email
C<bug-exporter-lexical at rt.cpan.org>, or browse to
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Exporter-Lexical>.

=head1 SEE ALSO

L<Sub::Exporter::Lexical>

L<Lexical::Import>

L<feature/The 'lexical_subs' feature>

=head1 SUPPORT

You can find this documentation for this module with the perldoc command.

    perldoc Exporter::Lexical

You can also look for information at:

=over 4

=item * MetaCPAN

L<https://metacpan.org/release/Exporter-Lexical>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Exporter-Lexical>

=item * Github

L<https://github.com/doy/exporter-lexical>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Exporter-Lexical>

=back

=cut

1;