File Coverage

blib/lib/MooseX/MarkAsMethods.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of MooseX-MarkAsMethods
3             #
4             # This software is Copyright (c) 2011 by Chris Weyl.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package MooseX::MarkAsMethods;
11             {
12             $MooseX::MarkAsMethods::VERSION = '0.15';
13             }
14              
15             # ABSTRACT: Mark overload code symbols as methods
16              
17 1     1   27995 use warnings;
  1         3  
  1         36  
18 1     1   5 use strict;
  1         2  
  1         38  
19              
20 1     1   851 use namespace::autoclean 0.12;
  1         387351  
  1         6  
21              
22 1     1   59 use B::Hooks::EndOfScope;
  1         2  
  1         5  
23 1     1   469 use Moose 0.94 ();
  0            
  0            
24             use Moose::Util::MetaRole;
25             use Moose::Exporter;
26              
27             # debugging
28             #use Smart::Comments '###', '####';
29              
30             {
31             package MooseX::MarkAsMethods::MetaRole::MethodMarker;
32             {
33             $MooseX::MarkAsMethods::MetaRole::MethodMarker::VERSION = '0.15';
34             }
35             use Moose::Role;
36             use namespace::autoclean;
37              
38             sub mark_as_method {
39             my $self = shift @_;
40              
41             $self->_mark_as_method($_) for @_;
42             return;
43             }
44              
45             sub _mark_as_method {
46             my ($self, $method_name) = @_;
47              
48             do { warn "$method_name is already a method!"; return }
49             if $self->has_method($method_name);
50              
51             my $code = $self->get_package_symbol({
52             name => $method_name,
53             sigil => '&',
54             type => 'CODE',
55             });
56              
57             do { warn "$method_name not found as a CODE symbol!"; return }
58             unless defined $code;
59              
60             $self->add_method($method_name =>
61             $self->wrap_method_body(
62             associated_metaclass => $self,
63             name => $method_name,
64             body => $code,
65             ),
66             );
67              
68             return;
69             }
70             }
71              
72             my ($import) = Moose::Exporter->build_import_methods(
73             install => [ qw{ init_meta unimport } ],
74             class_metaroles => {
75             class => ['MooseX::MarkAsMethods::MetaRole::MethodMarker'],
76             },
77             role_metaroles => {
78             role => ['MooseX::MarkAsMethods::MetaRole::MethodMarker'],
79             },
80             );
81              
82             sub import {
83             #my ($class, @args) = @_;
84             my $class = shift @_;
85              
86             # if someone is passing in Sub::Exporter-style initial hash, grab it
87             my $exporter_opts;
88             $exporter_opts = shift @_ if ref $_[0] && ref $_[0] eq 'HASH';
89             my %args = @_;
90              
91             #my $target = $exporter_opts->{into} if $exporter_opts;
92             #$target ||= scalar caller;
93             my $target
94             = defined $exporter_opts && defined $exporter_opts->{into}
95             ? $exporter_opts->{into}
96             : scalar caller
97             ;
98              
99             return if $target eq 'main';
100             #$class->init_meta(for_class => $target);
101              
102             my $do_autoclean = delete $args{autoclean};
103              
104             on_scope_end {
105              
106             ### $target
107             my $meta = Class::MOP::Class->initialize($target);
108              
109             ### metaclass: ref $meta
110             my %methods = map { ($_ => 1) } $meta->get_method_list;
111             my %symbols = %{ $meta->get_all_package_symbols('CODE') };
112             my @overloads = grep { /^\(/ } keys %symbols;
113              
114             ### %methods
115             ### %symbols
116             ### @overloads
117              
118             foreach my $overload_name (@overloads) {
119              
120             next if $methods{$overload_name};
121              
122             ### marking as method: $overload_name
123             $meta->mark_as_method($overload_name);
124             $methods{$overload_name} = 1;
125             delete $symbols{$overload_name};
126             }
127              
128             return;
129             };
130              
131             ### $do_autoclean
132             namespace::autoclean->import(-cleanee => $target)
133             if $do_autoclean;
134              
135             @_ = $exporter_opts ? ($exporter_opts, %args) : (%args);
136             unshift @_, $class;
137              
138             ### @_
139             goto &$import;
140             return;
141             }
142              
143             1;
144              
145              
146              
147             =pod
148              
149             =encoding utf-8
150              
151             =head1 NAME
152              
153             MooseX::MarkAsMethods - Mark overload code symbols as methods
154              
155             =head1 VERSION
156              
157             This document describes version 0.15 of MooseX::MarkAsMethods - released May 30, 2012 as part of MooseX-MarkAsMethods.
158              
159             =head1 SYNOPSIS
160              
161             package Foo;
162             use Moose;
163              
164             # mark overloads as methods and wipe other non-methods
165             use MooseX::MarkAsMethods autoclean => 1;
166              
167             # define overloads, etc as normal
168             use overload '""' => sub { shift->stringify };
169              
170             package Baz;
171             use Moose::Role;
172             use MooseX::MarkAsMethods autoclean => 1;
173              
174             # overloads defined in a role will "just work" when the role is
175             # composed into a class; they MUST use the anon-sub style invocation
176             use overload '""' => sub { shift->stringify };
177              
178             # additional methods generated outside Class::MOP/Moose can be marked, too
179             use constant foo => 'bar';
180             __PACKAGE__->meta->mark_as_method('foo');
181              
182             package Bar;
183             use Moose;
184              
185             # order is important!
186             use namespace::autoclean;
187             use MooseX::MarkAsMethods;
188              
189             # ...
190              
191             =head1 DESCRIPTION
192              
193             MooseX::MarkAsMethods allows one to easily mark certain functions as Moose
194             methods. This will allow other packages such as L<namespace::autoclean> to
195             operate without blowing away your overloads. After using
196             MooseX::MarkAsMethods your overloads will be recognized by L<Class::MOP> as
197             being methods, and class extension as well as composition from roles with
198             overloads will "just work".
199              
200             By default we check for overloads, and mark those functions as methods.
201              
202             If C<autoclean =&gt; 1> is passed to import on using this module, we will invoke
203             namespace::autoclean to clear out non-methods.
204              
205             =for Pod::Coverage init_meta
206              
207             =head1 TRAITS APPLIED
208              
209             Using this package causes a trait to be applied to your metaclass (for both
210             roles and classes), that provides a mark_as_method() method. You can use this
211             to mark newly generated methods at runtime (e.g. during class composition)
212             that some other package has created for you.
213              
214             mark_as_method() is invoked with one or more names to mark as a method. We die
215             on any error (e.g. name not in symbol table, already a method, etc). e.g.
216              
217             __PACKAGE__->meta->mark_as_method('newly_generated');
218              
219             e.g. say you have some sugar from another package that creates accessors of
220             some sort; you could mark them as methods via a method modifier:
221              
222             # called as __PACKAGE__->foo_generator('name', ...)
223             after 'foo_generator' => sub {
224              
225             shift->meta->mark_as_method(shift);
226             };
227              
228             =head1 IMPLICATIONS FOR ROLES
229              
230             Using MooseX::MarkAsMethods in a role will cause Moose to track and treat your
231             overloads like any other method defined in the role, and things will "just
232             work". That's it.
233              
234             Except... note that due to the way overloads, roles, and Moose work, you'll
235             need to use the coderef or anonymous subroutine approach to overload
236             declaration, or things will not work as you expect. Remember, we're talking
237             about _methods_ here, so we need to make it easy for L<overload> to find
238             the right method. The easiest (and supported) way to do this is to create an
239             anonymous sub to wrap the overload method.
240              
241             That is, this will work:
242              
243             # note method resolution, things will "just work"
244             use overload '""' => sub { shift->stringify };
245              
246             ...and this will not:
247              
248             use overload '""' => 'stringify';
249              
250             ...and will result in an error message like:
251              
252             # wah-wah
253             Can't resolve method "???" overloading """" in package "overload"
254              
255             =head1 CAVEATS
256              
257             =head2 Roles
258              
259             See the "IMPLICATIONS FOR ROLES" section, above.
260              
261             =head2 meta->mark_as_method()
262              
263             B<You almost certainly don't need or want to do this.> CMOP/Moose are fairly
264             good about determining what is and what isn't a method, but not perfect.
265             Before using this method, you should pause and think about why you need to.
266              
267             =head2 namespace::autoclean
268              
269             As currently implemented, we run our "method maker" at the end of the calling
270             package's compile scope (L<B::Hooks::EndOfScope>). As L<namespace::autoclean>
271             does the same thing, it's important that if namespace::autoclean is used that
272             it be used BEFORE MooseX::MarkAsMethods, so that its end_of_scope block is
273             run after ours.
274              
275             e.g.
276              
277             # yes!
278             use namespace::autoclean;
279             use MooseX::MarkAsMethods;
280              
281             # no -- overloads will be removed
282             use MooseX::MarkAsMethods;
283             use namespace::autoclean;
284              
285             The easiest way to invoke this module and clean out non-methods without having
286             to worry about ordering is:
287              
288             use MooseX::MarkAsMethods autoclean => 1;
289              
290             =head1 SEE ALSO
291              
292             Please see those modules/websites for more information related to this module.
293              
294             =over 4
295              
296             =item *
297              
298             L<L<overload>, L<B::Hooks::EndOfScope>, L<namespace::autoclean>, L<Class::MOP>,|L<overload>, L<B::Hooks::EndOfScope>, L<namespace::autoclean>, L<Class::MOP>,>
299              
300             =item *
301              
302             L<L<Moose>.|L<Moose>.>
303              
304             =item *
305              
306             L<L<MooseX::Role::WithOverloading> does allow for overload application from|L<MooseX::Role::WithOverloading> does allow for overload application from>
307              
308             =item *
309              
310             L<roles, but it does this by copying the overload symbols from the (not|roles, but it does this by copying the overload symbols from the (not>
311              
312             =item *
313              
314             L<L<namespace::autoclean>'ed role) the symbols handing overloads during class|L<namespace::autoclean>'ed role) the symbols handing overloads during class>
315              
316             =item *
317              
318             L<composition; we work by marking the overloads as methods and letting|composition; we work by marking the overloads as methods and letting>
319              
320             =item *
321              
322             L<CMOP/Moose handle them.|CMOP/Moose handle them.>
323              
324             =back
325              
326             =head1 SOURCE
327              
328             The development version is on github at L<http://github.com/RsrchBoy/moosex-markasmethods>
329             and may be cloned from L<git://github.com/RsrchBoy/moosex-markasmethods.git>
330              
331             =head1 BUGS
332              
333             Please report any bugs or feature requests on the bugtracker website
334             https://github.com/RsrchBoy/moosex-markasmethods/issues
335              
336             When submitting a bug or request, please include a test-file or a
337             patch to an existing test-file that illustrates the bug or desired
338             feature.
339              
340             =head1 AUTHOR
341              
342             Chris Weyl <cweyl@alumni.drew.edu>
343              
344             =head1 COPYRIGHT AND LICENSE
345              
346             This software is Copyright (c) 2011 by Chris Weyl.
347              
348             This is free software, licensed under:
349              
350             The GNU Lesser General Public License, Version 2.1, February 1999
351              
352             =cut
353              
354              
355             __END__
356              
357