make_immutable
[gitmo/Perl-Critic-Dynamic-Moose.git] / lib / Perl / Critic / Policy / DynamicMoose.pm
CommitLineData
84a1bb62 1package Perl::Critic::Policy::DynamicMoose;
a3c86d00 2use Moose;
8f31d0d0 3use MooseX::NonMoose;
a3c86d00 4extends 'Perl::Critic::DynamicPolicy';
5
8f31d0d0 6has document => (
1e6decf8 7 is => 'rw',
8 isa => 'PPI::Document',
9 handles => [qw/ppi_document/],
8f31d0d0 10);
11
cc784ef4 12sub applies_to { 'PPI::Document' }
914f300c 13sub applies_to_metaclass { 'Class::MOP::Class', inner() }
244fe3b5 14sub default_themes { qw(moose dynamic), inner() }
8f31d0d0 15
16around violation => sub {
1e6decf8 17 my $orig = shift;
18 my $self = shift;
19 my $desc = shift;
20 my $expl = shift;
21 my $element = shift;
22
23 if (!$element) {
24 my $doc = $self->ppi_document;
0004d229 25
26 # Without this hack, Storable complains of being unable to reconstruct
27 # overloading for an unknown package (perhaps PPI::Document?). For some
28 # reason it works for PPI::Element. Anyway, this should hopefully be
29 # replaced with a more useful location, something like
30 # ( class:MyClass / attr:foo / builder:build_foo )
1e6decf8 31 $element = $doc->find('PPI::Element')->[0];
32 }
8f31d0d0 33
1e6decf8 34 return $self->$orig($desc, $expl, $element, @_);
8f31d0d0 35};
36
a3c86d00 37sub violates_dynamic {
38 my $self = shift;
39 my $doc = shift;
40
8f31d0d0 41 $self->document($doc);
8f31d0d0 42
91827b04 43 my $old_packages = $self->_find_packages;
44 $self->_compile_document;
45 my @new_packages = $self->_new_packages($old_packages);
8f31d0d0 46
47 my @violations;
34502164 48 for my $package (@new_packages) {
c140dc59 49 my $meta = Class::MOP::class_of($package)
50 or next;
51
52 grep { $meta->isa($_) } $self->applies_to_metaclass
53 or next;
8f31d0d0 54
55 push @violations, $self->violates_metaclass($meta, $doc);
56 }
57
58 return @violations;
59}
60
b971d8fa 61sub violates_metaclass { die "Your policy (" . blessed($_[0]) . ") needs to implement violates_metaclass" }
62
91827b04 63sub _compile_document {
8f31d0d0 64 my $self = shift;
65 my $doc = $self->document;
66
1ad52929 67 my $source_code = $doc->content;
68
69 eval $source_code;
70
71 die "Unable to execute " . $doc->filename . ": $@" if $@;
8f31d0d0 72}
73
91827b04 74sub _find_packages {
8f31d0d0 75 my $self = shift;
34502164 76 return [ Class::MOP::get_all_metaclass_names ];
77}
78
91827b04 79sub _new_packages {
34502164 80 my $self = shift;
81 my $old = shift;
82 my @new;
83 my %seen;
84
85 $seen{$_} = 1 for @$old;
86
91827b04 87 for (@{ $self->_find_packages }) {
34502164 88 push @new, $_ if !$seen{$_}++;
89 }
a3c86d00 90
34502164 91 return @new;
a3c86d00 92}
93
a3c86d00 94no Moose;
e9c046ee 95__PACKAGE__->meta->make_immutable;
a3c86d00 96
971;
98
e556eafb 99__END__
100
101=head1 NAME
102
103Perl::Critic::Policy::DynamicMoose
104
105=head1 DESCRIPTION
106
0fcc6d48 107This class is a base class for dynamic Moose policies. This class facilitates
108critiquing metaclasses (instead of the usual PPI documents). For example, the
109L<Perl::Critic::Policy::DynamicMoose::ProhibitPublicBuilders> policy critiques
110metaclasses by checking whether any of their attributes' builders do not start
111with an underscore. Due to the very dynamic nature of Moose and
112metaprogramming, such policies will be much more effective than static analysis
113at critiquing classes.
114
115=head1 PUBLIC METHODS
116
117=over 4
118
119=item C<applies_to_metaclass>
120
121Returns a list of metaclass names that this policy can critique. By default,
122the list is L<Class::MOP::Class>. You may use the augment modifier to add
123other kinds of metaclasses, such as L<Moose::Meta::Role> without having to
124repeat the L<Class::MOP::Class>:
125
126 augment applies_to_metaclass => sub { 'Moose::Meta::Role' };
127
128Note that only the top-level metaclass is given to you. If you want to critique
129only attributes, then you must do the Visiting yourself.
130
131=item C<applies_to_themes>
132
133Returns a list of themes for Perl::Critic so that users can run a particular
134subset of themes on their code. By default, the list contains C<moose> and
135C<dynamic>. You should use the augment modifier to add more themes instead
136of overriding the method:
137
138 augment themes => sub { 'role' };
139
140=item C<violation>
141
142This extends the regular L<Perl::Critic::Policy/violation> method by providing
143a (rather useless) default value for the C<element> parameter. For nearly all
144cases, there's no easy way to find where a metaclass violation occurred. You
145may still pass such an element if you have one. However, since you probably do
146not, you should be exact in your violation's description.
147
148=item C<violates_metaclass>
149
150This method is required to be overridden by subclasses. It takes a metaclass
151object and the L<Perl::Critic::Document> representing the entire compilation
152unit. It is expected to return a list of L<Perl::Critic::Violation> objects.
153
154=over
155
156=head1 POLICIES
157
e556eafb 158The included policies are:
159
160=over 4
161
162=item L<Perl::Critic::Policy::DynamicMoose::ProhibitPublicBuilders>
163
164Prohibit public builder methods for attributes. [Severity: 3]
165
166=back
167
ea5503e8 168=head1 WARNING
169
170B<VERY IMPORTANT:> Most L<Perl::Critic> Policies (including all the ones that
171ship with Perl::Critic> use pure static analysis -- they never compile nor
172execute any of the code that they analyze. However, this policy is very
173different. It actually attempts to compile your code and then compares the
174subroutines mentioned in your code to those found in the symbol table.
175Therefore you should B<not> use this Policy on any code that you do not trust,
176or may have undesirable side-effects at compile-time (such as connecting to the
177network or mutating files).
178
179For this Policy to work, all the modules included in your code must be
180installed locally, and must compile without error.
181
e556eafb 182=head1 AUTHOR
183
184Shawn M Moore, C<sartak@bestpractical.com>
185
186=cut
187