1 package MooseX::Compiler;
7 use Module::Runtime qw( module_notional_filename );
9 use Scalar::Util qw( blessed );
12 use Moose::Util::TypeConstraints;
14 my $moose_class = subtype, as 'ClassName', where {
16 && blessed $_[0]->meta()
17 && $_[0]->meta()->isa('Moose::Meta::Class');
28 isa => 'Moose::Meta::Class',
31 default => sub { $_[0]->class()->meta() },
34 has _ppi_document => (
36 isa => 'PPI::Document',
39 builder => '_build_ppi_document',
42 has _package_statement => (
44 isa => 'PPI::Statement::Package',
47 builder => '_build_package_statement',
53 $self->_modify_class_content();
54 $self->_inline_roles();
55 $self->_inline_constructor();
56 $self->_inline_attributes();
58 return $self->_ppi_document()->content();
61 sub _modify_class_content {
64 $self->_fixup_line_numbers();
65 $self->_do_not_use_moose();
66 $self->_inline_parents();
67 $self->_load_required_modules();
72 sub _fixup_line_numbers {
75 my $code = "#line 3\n";
76 $self->_package_statement()->snext_sibling()->insert_before( $_->clone() )
77 for PPI::Document->new( \$code )->children();
82 sub _do_not_use_moose {
85 my $use_nodes = $self->_ppi_document()->find(
89 if $node->isa('PPI::Statement')
90 && !$node->isa('PPI::Statement::Include');
91 return undef if $node->isa('PPI::Structure');
93 if $node->isa('PPI::Statement::Include')
94 && $node->module() =~ /^Moose/;
99 for my $node ( @{$use_nodes} ) {
100 my $replacement_code .=
101 defined $node->module_version()
104 'use', $node->module(), $node->module_version(), '()', ';',
106 : '# ' . $node->content();
108 $node->insert_before( $_->clone() )
109 for PPI::Document->new( \$replacement_code )->children();
116 sub _inline_parents {
119 my @supers = $self->_class_meta()->superclasses();
120 return unless @supers;
122 my $code = 'use parent ';
123 $code .= join ', ', map { B::perlstring($_) } @supers;
126 $self->_package_statement()->insert_after( $_->clone() )
127 for PPI::Document->new( \$code )->children();
132 sub _load_required_modules {
135 my $code = "use Scalar::Util ();\nuse Moose::Error::Util ();\nuse Carp ();\n";
137 $self->_package_statement()->insert_after( $_->clone() )
138 for PPI::Document->new( \$code )->children();
147 sub _inline_constructor {
150 my $constructor = join "\n",
153 $self->_class_meta()->_inline_new_object(),
157 $constructor .= "\n\n";
159 $self->_insert_before_end($constructor);
164 sub _inline_attributes {
168 sub _insert_before_end {
172 my $end_node = $self->_ppi_document()->find_first(
177 if $node->isa('PPI::Statement') && $node->content() =~ /^1;/;
182 die 'Cannot find the end of the class (looking for a line match /^1;/)'
185 $end_node->insert_before( $_->clone() )
186 for PPI::Document->new( \$code )->children();
191 sub _build_ppi_document {
194 my $pm_file = module_notional_filename( $self->class() );
195 my $path_to_class = $INC{$pm_file}
196 or die "Cannot find $pm_file in %INC!";
198 return PPI::Document->new( $path_to_class->stringify() )
199 or die PPI::Document->errstr();
202 sub _build_package_statement {
205 my $package_stmt = $self->_ppi_document()->find_first(
208 return 1 if $_[1]->isa('PPI::Statement::Package');
213 die 'Cannot find a package statement in this code'
214 unless $package_stmt;
216 return $package_stmt;
219 __PACKAGE__->meta()->make_immutable();