~percona-toolkit-dev/percona-toolkit/pt-mysql-summary-Blank-InnoDB-Section-for-5.6-1254233

« back to all changes in this revision

Viewing changes to lib/Lmo.pm

  • Committer: Daniel Nichter
  • Date: 2013-06-19 21:23:55 UTC
  • mfrom: (582.1.5 release-2.2.3)
  • Revision ID: daniel@percona.com-20130619212355-nf6bmx23j3b76afe
Tags: 2.2.3
Merge release-2.2.3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# This program is copyright 2007-2011 Baron Schwartz, 2012 Percona Ireland Ltd.
2
 
# Feedback and improvements are welcome.
3
 
#
4
 
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5
 
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6
 
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7
 
#
8
 
# This program is free software; you can redistribute it and/or modify it under
9
 
# the terms of the GNU General Public License as published by the Free Software
10
 
# Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
11
 
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12
 
# licenses.
13
 
#
14
 
# You should have received a copy of the GNU General Public License along with
15
 
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16
 
# Place, Suite 330, Boston, MA  02111-1307  USA.
17
 
# ###########################################################################
18
 
# Lmo package
19
 
# ###########################################################################
20
 
# Package: Lmo
21
 
# Lmo provides a miniature object system in the style of Moose and Moo.
22
 
BEGIN {
23
 
$INC{"Lmo.pm"} = __FILE__;
24
 
package Lmo;
25
 
our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo.
26
 
 
27
 
 
28
 
use strict;
29
 
use warnings qw( FATAL all );
30
 
 
31
 
use Carp ();
32
 
use Scalar::Util qw(looks_like_number blessed);
33
 
 
34
 
use Lmo::Meta;
35
 
use Lmo::Object;
36
 
use Lmo::Types;
37
 
 
38
 
use Lmo::Utils;
39
 
 
40
 
my %export_for;
41
 
sub import {
42
 
   # Set warnings and strict for the caller.
43
 
   warnings->import(qw(FATAL all));
44
 
   strict->import();
45
 
 
46
 
   my $caller     = scalar caller(); # Caller's package
47
 
   my %exports = (
48
 
      extends  => \&extends,
49
 
      has      => \&has,
50
 
      with     => \&with,
51
 
      override => \&override,
52
 
      confess  => \&Carp::confess,
53
 
   );
54
 
 
55
 
   # We keep this so code doing 'no Mo;' actually does a cleanup.
56
 
   $export_for{$caller} = \%exports;
57
 
 
58
 
   # Export has, extends and sosuch.
59
 
   for my $keyword ( keys %exports ) {
60
 
      _install_coderef "${caller}::$keyword" => $exports{$keyword};
61
 
   }
62
 
 
63
 
   # Set up our caller's ISA, unless they already set it manually themselves,
64
 
   # in which case we assume they know what they are doing.
65
 
   # XXX weird syntax here because we want to call the classes' extends at
66
 
   # least once, to avoid warnings.
67
 
   if ( !@{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] } ) {
68
 
      @_ = "Lmo::Object";
69
 
      goto *{ _glob_for "${caller}::extends" }{CODE};
70
 
   }
71
 
}
72
 
 
73
 
sub extends {
74
 
   my $caller = scalar caller();
75
 
   for my $class ( @_ ) {
76
 
      _load_module($class);
77
 
   }
78
 
   _set_package_isa($caller, @_);
79
 
   _set_inherited_metadata($caller);
80
 
}
81
 
 
82
 
sub _load_module {
83
 
   my ($class) = @_;
84
 
   
85
 
   # Try loading the class, but don't croak if we fail.
86
 
   (my $file = $class) =~ s{::|'}{/}g;
87
 
   $file .= '.pm';
88
 
   { local $@; eval { require "$file" } } # or warn $@;
89
 
   return;
90
 
}
91
 
 
92
 
sub with {
93
 
   my $package = scalar caller();
94
 
   require Role::Tiny;
95
 
   for my $role ( @_ ) {
96
 
      _load_module($role);
97
 
      _role_attribute_metadata($package, $role);
98
 
   }
99
 
   Role::Tiny->apply_roles_to_package($package, @_);
100
 
}
101
 
 
102
 
sub _role_attribute_metadata {
103
 
   my ($package, $role) = @_;
104
 
 
105
 
   my $package_meta = Lmo::Meta->metadata_for($package);
106
 
   my $role_meta    = Lmo::Meta->metadata_for($role);
107
 
 
108
 
   # The role metadata always comes first, since it shouldn't redefine
109
 
   # metadata defined in the class itself.
110
 
   %$package_meta = (%$role_meta, %$package_meta);
111
 
}
112
 
 
113
 
sub has {
114
 
   my $names  = shift;
115
 
   my $caller = scalar caller();
116
 
 
117
 
   my $class_metadata = Lmo::Meta->metadata_for($caller);
118
 
   
119
 
   for my $attribute ( ref $names ? @$names : $names ) {
120
 
      my %args   = @_;
121
 
      my $method = ($args{is} || '') eq 'ro'
122
 
         ? sub {
123
 
            Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller}::${attribute}")
124
 
               if $#_;
125
 
            return $_[0]{$attribute};
126
 
         }
127
 
         : sub {
128
 
            return $#_
129
 
                  ? $_[0]{$attribute} = $_[1]
130
 
                  : $_[0]{$attribute};
131
 
         };
132
 
 
133
 
      $class_metadata->{$attribute} = ();
134
 
 
135
 
      # isa => Constaint,
136
 
      if ( my $type_check = $args{isa} ) {
137
 
         my $check_name = $type_check;
138
 
         
139
 
         if ( my ($aggregate_type, $inner_type) = $type_check =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) {
140
 
            $type_check = Lmo::Types::_nested_constraints($attribute, $aggregate_type, $inner_type);
141
 
         }
142
 
         
143
 
         my $check_sub = sub {
144
 
            my ($new_val) = @_;
145
 
            Lmo::Types::check_type_constaints($attribute, $type_check, $check_name, $new_val);
146
 
         };
147
 
         
148
 
         $class_metadata->{$attribute}{isa} = [$check_name, $check_sub];
149
 
         my $orig_method = $method;
150
 
         $method = sub {
151
 
            $check_sub->($_[1]) if $#_;
152
 
            goto &$orig_method;
153
 
         };
154
 
      }
155
 
 
156
 
      # XXX TODO: Inline builder and default into the actual method, for speed.
157
 
      # builder => '_builder_method',
158
 
      if ( my $builder = $args{builder} ) {
159
 
         my $original_method = $method;
160
 
         $method = sub {
161
 
               $#_
162
 
                  ? goto &$original_method
163
 
                  : ! exists $_[0]{$attribute}
164
 
                     ? $_[0]{$attribute} = $_[0]->$builder
165
 
                     : goto &$original_method
166
 
         };
167
 
      }
168
 
 
169
 
      # default => CodeRef,
170
 
      if ( my $code = $args{default} ) {
171
 
         Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef")
172
 
               unless ref($code) eq 'CODE';
173
 
         my $original_method = $method;
174
 
         $method = sub {
175
 
               $#_
176
 
                  ? goto &$original_method
177
 
                  : ! exists $_[0]{$attribute}
178
 
                     ? $_[0]{$attribute} = $_[0]->$code
179
 
                     : goto &$original_method
180
 
         };
181
 
      }
182
 
 
183
 
      # does => 'Role',
184
 
      if ( my $role = $args{does} ) {
185
 
         my $original_method = $method;
186
 
         $method = sub {
187
 
            if ( $#_ ) {
188
 
               Carp::confess(qq<Attribute ($attribute) doesn't consume a '$role' role">)
189
 
                  unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) }
190
 
            }
191
 
            goto &$original_method
192
 
         };
193
 
      }
194
 
 
195
 
      # coerce => CodeRef,
196
 
      if ( my $coercion = $args{coerce} ) {
197
 
         $class_metadata->{$attribute}{coerce} = $coercion;
198
 
         my $original_method = $method;
199
 
         $method = sub {
200
 
            if ( $#_ ) {
201
 
               return $original_method->($_[0], $coercion->($_[1]))
202
 
            }
203
 
            goto &$original_method;
204
 
         }
205
 
      }
206
 
 
207
 
      # Actually put the attribute's accessor in the class
208
 
      _install_coderef "${caller}::$attribute" => $method;
209
 
 
210
 
      if ( $args{required} ) {
211
 
         $class_metadata->{$attribute}{required} = 1;
212
 
      }
213
 
 
214
 
      if ($args{clearer}) {
215
 
         _install_coderef "${caller}::$args{clearer}"
216
 
            => sub { delete shift->{$attribute} }
217
 
      }
218
 
 
219
 
      if ($args{predicate}) {
220
 
         _install_coderef "${caller}::$args{predicate}"
221
 
            => sub { exists shift->{$attribute} }
222
 
      }
223
 
 
224
 
      if ($args{handles}) {
225
 
         _has_handles($caller, $attribute, \%args);
226
 
      }
227
 
 
228
 
      if (exists $args{init_arg}) {
229
 
         $class_metadata->{$attribute}{init_arg} = $args{init_arg};
230
 
      }
231
 
   }
232
 
}
233
 
 
234
 
# handles handles
235
 
sub _has_handles {
236
 
   my ($caller, $attribute, $args) = @_;
237
 
   my $handles = $args->{handles};
238
 
 
239
 
   my $ref = ref $handles;
240
 
   my $kv;
241
 
   if ( $ref eq ref [] ) {
242
 
         # handles => [ ... list of methods ... ],
243
 
         $kv = { map { $_,$_ } @{$handles} };
244
 
   }
245
 
   elsif ( $ref eq ref {} ) {
246
 
         # handles => { 'method_to_install' => 'original_method' | [ 'original_method', ... curried arguments ... ], },
247
 
         $kv = $handles;
248
 
   }
249
 
   elsif ( $ref eq ref qr// ) {
250
 
         # handles => qr/PAT/,
251
 
         Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)")
252
 
            unless $args->{isa};
253
 
         my $target_class = $args->{isa};
254
 
         $kv = {
255
 
            map   { $_, $_     }
256
 
            grep  { $_ =~ $handles }
257
 
            grep  { !exists $Lmo::Object::{$_} && $target_class->can($_) }
258
 
            grep  { !$export_for{$target_class}->{$_} }
259
 
            keys %{ _stash_for $target_class }
260
 
         };
261
 
   }
262
 
   else {
263
 
         Carp::confess("handles for $ref not yet implemented");
264
 
   }
265
 
 
266
 
   while ( my ($method, $target) = each %{$kv} ) {
267
 
         my $name = _glob_for "${caller}::$method";
268
 
         Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation")
269
 
            if defined &$name;
270
 
 
271
 
         # If we have an arrayref, they are currying some arguments.
272
 
         my ($target, @curried_args) = ref($target) ? @$target : $target;
273
 
         *$name = sub {
274
 
            my $self        = shift;
275
 
            my $delegate_to = $self->$attribute();
276
 
            my $error = "Cannot delegate $method to $target because the value of $attribute";
277
 
            Carp::confess("$error is not defined") unless $delegate_to;
278
 
            Carp::confess("$error is not an object (got '$delegate_to')")
279
 
               unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target));
280
 
            return $delegate_to->$target(@curried_args, @_);
281
 
         }
282
 
   }
283
 
}
284
 
 
285
 
# Sets a package's @ISA to the list passed in. Overwrites any previous values.
286
 
sub _set_package_isa {
287
 
   my ($package, @new_isa) = @_;
288
 
   my $package_isa  = \*{ _glob_for "${package}::ISA" };
289
 
   # This somewhat weirder syntax is here to work around a Perl 5.10.0 bug;
290
 
   # For whatever reason, some other variants weren't setting ISA.
291
 
   @{*$package_isa} = @new_isa;
292
 
}
293
 
 
294
 
# Each class has its own metadata. When a class inhyerits attributes,
295
 
# it should also inherit the attribute metadata.
296
 
sub _set_inherited_metadata {
297
 
   my $class = shift;
298
 
   my $class_metadata = Lmo::Meta->metadata_for($class);
299
 
   my $linearized_isa = mro::get_linear_isa($class);
300
 
   my %new_metadata;
301
 
 
302
 
   # Walk @ISA in reverse, grabbing the metadata for each
303
 
   # class. Attributes with the same name defined in more
304
 
   # specific classes override their parent's attributes.
305
 
   for my $isa_class (reverse @$linearized_isa) {
306
 
      my $isa_metadata = Lmo::Meta->metadata_for($isa_class);
307
 
      %new_metadata = (
308
 
         %new_metadata,
309
 
         %$isa_metadata,
310
 
      );
311
 
   }
312
 
   %$class_metadata = %new_metadata;
313
 
}
314
 
 
315
 
sub unimport {
316
 
   my $caller = scalar caller();
317
 
   my $target = caller;
318
 
  _unimport_coderefs($target, keys %{$export_for{$caller}});
319
 
}
320
 
 
321
 
sub Dumper {
322
 
   require Data::Dumper;
323
 
   local $Data::Dumper::Indent    = 0;
324
 
   local $Data::Dumper::Sortkeys  = 0;
325
 
   local $Data::Dumper::Quotekeys = 0;
326
 
   local $Data::Dumper::Terse     = 1;
327
 
 
328
 
   Data::Dumper::Dumper(@_)
329
 
}
330
 
 
331
 
BEGIN {
332
 
   # mro is the method resolution order. The module itself is core in
333
 
   # recent Perls; In older Perls it's available from MRO::Compat from
334
 
   # CPAN, and in case that isn't available to us, we inline the barest
335
 
   # funcionality.
336
 
   if ($] >= 5.010) {
337
 
      { local $@; require mro; }
338
 
   }
339
 
   else {
340
 
      local $@;
341
 
      eval {
342
 
         require MRO::Compat;
343
 
      } or do {
344
 
         *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub {
345
 
            no strict 'refs';
346
 
 
347
 
            my $classname = shift;
348
 
 
349
 
            my @lin = ($classname);
350
 
            my %stored;
351
 
            foreach my $parent (@{"$classname\::ISA"}) {
352
 
               my $plin = mro::get_linear_isa_dfs($parent);
353
 
               foreach (@$plin) {
354
 
                     next if exists $stored{$_};
355
 
                     push(@lin, $_);
356
 
                     $stored{$_} = 1;
357
 
               }
358
 
            }
359
 
            return \@lin;
360
 
         };
361
 
      }
362
 
   }
363
 
}
364
 
 
365
 
sub override {
366
 
   my ($methods, $code) = @_;
367
 
   my $caller          = scalar caller;
368
 
 
369
 
   for my $method ( ref($methods) ? @$methods : $methods ) {
370
 
      my $full_method     = "${caller}::${method}";
371
 
      *{_glob_for $full_method} = $code;
372
 
   }
373
 
}
374
 
 
375
 
}
376
 
1;
377
 
# ###########################################################################
378
 
# End Lmo package
379
 
# ###########################################################################