File Coverage

blib/lib/Class/Mix.pm
Criterion Covered Total %
statement 67 67 100.0
branch 28 30 93.3
condition 25 27 92.5
subroutine 13 13 100.0
pod 2 2 100.0
total 135 139 97.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::Mix - dynamic class mixing
4              
5             =head1 SYNOPSIS
6              
7             use Class::Mix qw(mix_class);
8              
9             $foobar_object = mix_class("Foo", "Bar")->new;
10             $digest_class = mix_class("Foo", "Bar", {prefix=>"Digest::"});
11              
12             use Class::Mix qw(genpkg);
13              
14             $package = genpkg;
15             $package = genpkg("Digest::Foo::");
16              
17             =head1 DESCRIPTION
18              
19             The C<mix_class> function provided by this module dynamically generates
20             `anonymous' classes with specified inheritance.
21              
22             =cut
23              
24             package Class::Mix;
25              
26 4     4   164714 { use 5.006; }
  4         18  
27 4     4   25 use warnings;
  4         8  
  4         124  
28 4     4   20 use strict;
  4         14  
  4         144  
29              
30 4     4   18 use constant _DO_MRO => "$]" >= 5.009005;
  4         7  
  4         348  
31              
32 4     4   29 use Carp qw(croak);
  4         15  
  4         213  
33 4     4   1152 use Params::Classify 0.000 qw(is_undef is_string is_ref);
  4         8497  
  4         263  
34 4     4   1835 use if _DO_MRO, "mro";
  4         50  
  4         17  
35              
36             our $VERSION = "0.006";
37              
38 4     4   287 use parent "Exporter";
  4         7  
  4         21  
39             our @EXPORT_OK = qw(mix_class genpkg);
40              
41             BEGIN {
42 4     4   406 if(_DO_MRO) {
43 4         1691 *_get_mro = \&mro::get_mro;
44             } else {
45             *_get_mro = sub ($) { "dfs" };
46             }
47             }
48              
49             my $prefix_rx = qr/(?:[a-zA-Z_][0-9a-zA-Z_]*::(?:[0-9a-zA-Z_]+::)*)?/;
50              
51             =head1 FUNCTIONS
52              
53             =over
54              
55             =item mix_class(ITEMS ...)
56              
57             This function is used to dynamically generate `anonymous' classes by
58             mixing pre-existing classes. This is useful where an incomplete class
59             requires use of a mixin in order to become instantiable, several suitable
60             mixins are available, and it is desired to make the choice between mixins
61             at runtime.
62              
63             Each I<ITEM> in the argument list is either the name of a class to inherit
64             from (a parent class) or a reference to a hash of options. The C<@ISA>
65             list of the mixture class is set to the list of parent class names,
66             in the order supplied. The options that may be supplied are:
67              
68             =over
69              
70             =item B<mro>
71              
72             Specifies the desired method resolution order (MRO) of the mixture class.
73             See L<mro> for details of the valid values and the default determined
74             by Perl. Typically, this should be set to B<c3> if mixing into an
75             existing C3-based class hierarchy.
76              
77             =item B<prefix>
78              
79             Specifies where the resulting package will go. May be C<undef> to
80             indicate that the caller doesn't care (which is the default state).
81             Otherwise it must be either the empty string (to create a top-level
82             package) or a bareword followed by "::" (to create a package under
83             that name). For example, "Digest::" could be specified to ensure that
84             the resulting package has a name starting with "Digest::", so that C<<
85             Digest->new >> will accept it as the name of a message digest algorithm.
86              
87             =back
88              
89             The function generates a class of the form described by the arguments, and
90             returns its name. The same class will be returned by repeated invocations
91             with the same parent class list and options. The returned name may be
92             used to call a constructor or other class methods of the mixed class.
93              
94             A class name must be returned because there is no such thing as an
95             anonymous class in Perl. Classes are referenced by name. The names
96             that are generated by this function are unique and insignificant.
97             See C<genpkg> below for more information.
98              
99             If fewer than two classes to inherit from are specified, the function
100             tries to avoid generating a separate class for the mixture. If only
101             one parent class is specified then that class may be returned, and if
102             no parent classes are specified then C<UNIVERSAL> may be returned.
103             This provides the desired inheritance without creating superfluous
104             classes. These special cases only apply if the options are compatible
105             with the pre-existing class.
106              
107             This function relies on the classes it returns remaining unmodified in
108             order to be returned by future invocations. If you want to modify your
109             dynamically-generated `anonymous' classes, use C<genpkg> (below).
110              
111             =cut
112              
113             sub genpkg(;$);
114              
115             my %mixtures;
116             sub mix_class(@) {
117 62     62 1 30531 my @parents;
118             my %options;
119 62         132 foreach(@_) {
120 110 100       250 if(is_string($_)) {
    100          
121 63         134 push @parents, $_;
122             } elsif(is_ref($_, "HASH")) {
123 45         139 foreach my $k (keys %$_) {
124             croak "clashing option `$k'"
125 39 50       103 if exists $options{$k};
126 39         111 $options{$k} = $_->{$k};
127             }
128             } else {
129 2         256 croak "bad argument for mix_class";
130             }
131             }
132 60         130 foreach(keys %options) {
133 39 100       430 croak "bad option `$_' for mix_class"
134             unless /\A(?:mro|prefix)\z/;
135             }
136 58 100       191 $options{mro} = "dfs" unless exists $options{mro};
137 58 100       706 croak "bad mro value" unless is_string($options{mro});
138 52 100       119 $options{prefix} = undef unless exists $options{prefix};
139             croak "bad prefix value" unless
140             is_undef($options{prefix}) ||
141             (is_string($options{prefix}) &&
142 52 100 66     399 $options{prefix} =~ /\A$prefix_rx\z/o);
      100        
143             return "UNIVERSAL" if @parents == 0 &&
144             $options{mro} eq _get_mro("UNIVERSAL") &&
145 50 100 100     241 (is_undef($options{prefix}) || $options{prefix} eq "");
      100        
      100        
146             return $parents[0] if @parents == 1 &&
147             $options{mro} eq _get_mro($parents[0]) &&
148 43 100 100     431 (is_undef($options{prefix}) ||
      100        
      100        
149             $parents[0] =~ /\A\Q$options{prefix}\E[^:]*\z/);
150 28 100       81 $options{prefix} = "Class::Mix::" unless defined $options{prefix};
151 95         269 my $recipe = join("", map { length($_)."_".$_ }
152 28         65 $options{mro}, $options{prefix}, @parents);
153 28   66     153 return $mixtures{$recipe} ||= do {
154 16         45 my $pkg = genpkg($options{prefix});
155 4     4   25 no strict "refs";
  4         26  
  4         676  
156 16         30 @{$pkg."::ISA"} = @parents;
  16         210  
157 16 100       118 mro::set_mro($pkg, $options{mro}) if $options{mro} ne "dfs";
158 13         76 $pkg;
159             };
160             }
161              
162             =item genpkg([PREFIX])
163              
164             This function selects and returns a package name that has not been
165             previously used. The name returned is an ordinary bareword-form package
166             name, and can be used as the second argument to C<bless> and in all
167             other ways that package names are used. The package is initially empty.
168              
169             The package names returned by this function are of a type that should not
170             be used as ordinary fixed module names. However, it is not possible to
171             entirely prevent a clash. This function checks that the package name it
172             is about to return has not already been used, and will avoid returning
173             such names, but it cannot guarantee that a later-loaded module will not
174             create a clash.
175              
176             PREFIX, if present, specifies where the resulting package will go.
177             It must be either the empty string (to create a top-level package)
178             or a bareword followed by "::" (to create a package under that name).
179             For example, "Digest::" could be specified to ensure that the resulting
180             package has a name starting with "Digest::", so that C<< Digest->new >>
181             will accept it as the name of a message digest algorithm. If the PREFIX
182             is not supplied, the caller is not expressing any preference.
183              
184             =cut
185              
186             my $n = 0;
187             sub genpkg(;$) {
188 22     22 1 111 my($prefix) = @_;
189 22 100       52 $prefix = "Class::Mix::" unless defined $prefix;
190 22 50       206 croak "`$prefix' is not a valid module name prefix"
191             unless $prefix =~ /\A$prefix_rx\z/o;
192 4     4   27 no strict "refs";
  4         6  
  4         418  
193 22         42 my $pkgtail;
194             do {
195 25         54 $pkgtail = "__GP".$n++;
196 22 100       36 } while(exists ${$prefix || "::"}{$pkgtail."::"});
  25         144  
197 22         56 my $pkgname = $prefix.$pkgtail;
198 22         36 %{$pkgname."::"} = ();
  22         123  
199 22         72 return $pkgname;
200             }
201              
202             =back
203              
204             =head1 SEE ALSO
205              
206             L<Class::Generate>,
207             L<mro>
208              
209             =head1 AUTHOR
210              
211             Andrew Main (Zefram) <zefram@fysh.org>
212              
213             =head1 COPYRIGHT
214              
215             Copyright (C) 2004, 2006, 2009, 2010, 2011, 2017
216             Andrew Main (Zefram) <zefram@fysh.org>
217              
218             =head1 LICENSE
219              
220             This module is free software; you can redistribute it and/or modify it
221             under the same terms as Perl itself.
222              
223             =cut
224              
225             1;