factor out compiler code
[gitmo/MooseX-Antlers.git] / lib / MooseX / Antlers / Compiler.pm
CommitLineData
5bd097e7 1package MooseX::Antlers::Compiler;
2
3use Moose;
4use IO::All qw(io);
5use String::TT qw(strip tt);
6use B qw(perlstring);
7use aliased 'MooseX::Antlers::RefTracker';
8use aliased 'MooseX::Antlers::EvalTracker';
9use aliased 'MooseX::Antlers::RefFilter';
10
11has [ qw(_target _attr_refs _attr_et _im_et _raw_source) ] => (is => 'rw');
12
13sub load_with_compiler {
14 my ($class, $target) = @_;
15 $class->new->load($target);
16}
17
18sub load {
19 my ($self, $target) = @_;
20 my %attr_refs;
21 my %attr_et;
22 my $im_et;
23 my $orig_import = Moose->can('import');
24 no warnings 'redefine';
25 local *Moose::import = sub {
26 my $targ = caller;
27 Moose->$orig_import({ into => $targ });
28 my $has = $targ->can('has');
29 {
30 no strict 'refs';
31 *{"${targ}::has"} = sub {
32 $attr_refs{$_[0]} = [
33 map RefTracker->trace_refs( $_ => \@_ ),
34 '(\@_)', '$has_args{'.perlstring($_[0]).'}'
35 ];
36 my $et = EvalTracker->new->enable;
37 $has->(@_);
38 $attr_et{$_[0]} = $et->disable;
39 return;
40 };
41 }
42 };
43 my $orig_immutable = Moose::Meta::Class->can('make_immutable');
44 local *Moose::Meta::Class::make_immutable = sub {
45 my $et = EvalTracker->new->enable;
46 $orig_immutable->(@_);
47 $im_et = $et->disable;
48 return;
49 };
50 Class::MOP::load_class($target);
51 $self->_attr_refs(\%attr_refs);
52 $self->_attr_et(\%attr_et);
53 $self->_im_et($im_et);
54 $self->_target($target);
55 (my $pm_file = $target) =~ s/::/\//g;
56 $self->_raw_source(scalar io($INC{"${pm_file}.pm"})->slurp);
57 return $self;
58}
59
60sub compiled_source {
61 my ($self) = @_;
62 my %attr_et = %{$self->_attr_et};
63 my %attr_refs = %{$self->_attr_refs};
64 my $im_et = $self->_im_et;
65 my $target = $self->_target;
66 my %attr_construct;
67 foreach my $attr_name (keys %attr_refs) {
68 $attr_construct{$attr_name}
69 = $attr_et{$attr_name}->serialized_construction(
70 $attr_refs{$attr_name}[0]
71 );
72 }
73 my $im_construct = $im_et->serialized_construction(
74 { map %{$_->[1]}, values %attr_refs }
75 );
76 my $preamble = strip tt q{
77 my %replay_has;
78 my %has_args;
79 BEGIN {
80 %replay_has = (
81 [% FOREACH a IN attr_construct_h %]
82 [% a.key %] => sub {
83 [% a.value %]
84 }
85 [% END %]
86 );
87 }
88 sub MooseX::Antlers::ImmutableHackFor::[% target %]::make_immutable {
89 [% im_construct %]
90 }
91 use MooseX::Antlers::StealImport
92 Moose => {
93 -do => sub {
94 strict->import;
95 warnings->import;
96 },
97 has => sub {
98 $has_args{$_[0]} = \@_;
99 ($replay_has{$_[0]}||die "Can't find replay for $_[0]")
100 ->(@_);
101 },
102 meta => sub { 'MooseX::Antlers::ImmutableHackFor::[% target %]' }
103 };
104 };
105
106 my $postamble = strip q{
107 no MooseX::Antlers::StealImport qw(Moose);
108 };
109
110 my $compiled = join("\n", $preamble, $self->_raw_source, $postamble);
111
112 return $compiled;
113}
114
1151;