File Coverage

lib/Momo.pm
Criterion Covered Total %
statement 113 132 85.6
branch 35 60 58.3
condition 5 12 41.6
subroutine 26 27 96.3
pod 3 3 100.0
total 182 234 77.7


line stmt bran cond sub pod time code
1             package Momo;
2              
3             # ABSTRACT: a simple oop module inspired from Mojo::Base and Moo
4 1     1   22769 use utf8;
  1         10  
  1         5  
5 1     1   30 use strict;
  1         2  
  1         29  
6 1     1   4 use warnings;
  1         6  
  1         25  
7 1     1   5 use Carp;
  1         1  
  1         46  
8 1     1   880 use Role::Tiny ();
  1         3562  
  1         24  
9 1     1   7 use Scalar::Util qw(set_prototype);
  1         2  
  1         85  
10 1     1   749 use Class::Method::Modifiers;
  1         1401  
  1         77  
11              
12             our $VERSION = 1.2;
13              
14             sub import {
15 3     3   3298 my $class = shift;
16              
17             {
18 1     1   5 no strict 'refs';
  1         2  
  1         22  
  3         5  
19 1     1   3 no warnings 'redefine';
  1         2  
  1         650  
20              
21 3         5 my $package = caller;
22 3         4 push @{ $package . '::ISA' }, 'Momo';
  3         38  
23             # install methodes into package,make the package get magic feature
24             # install class method modifiers
25 3 50       5 if( not defined &{ $package."::has" } ){
  3         18  
26 3     7   18 *{ $package . '::has' } = sub { attr( $package, @_ ) };
  3         11  
  7         613  
27             }
28 3 50       5 if( not defined &{ $package."::extends" } ) {
  3         14  
29 3         10 *{ $package . '::extends' } = sub {
30 2     2   16 for (@_) {
31 2         11 ( my $file = $_ ) =~ s!::|'!/!g;
32 2         4 eval { require "$file.pm" };
  2         1281  
33 2         12452 push @{ $package . '::ISA' }, $_;
  2         40  
34             }
35 3         25 };
36             }
37 3 50       4 if( not defined &{ $package."::with" } ){
  3         12  
38 3         10 *{ $package . '::with' } = sub {
39 1     1   8 Role::Tiny->apply_roles_to_package( $package, @_ );
40 3         9 };
41             }
42 3         5 for my $method (qw(before after around)) {
43 9         33 *{ $package . '::' . $method } = sub {
44 5     5   774 Class::Method::Modifiers::install_modifier( $package, $method,
        5      
45             @_ );
46 9         19 };
47             }
48 3         4 set_prototype \&{ $package . '::extends' }, '@';
  3         13  
49 3         5 set_prototype \&{ $package . '::with' }, '@';
  3         11  
50             }
51 3         34 strict->import;
52 3         42 warnings->import;
53 3         14 utf8->import;
54 3         111 Carp->import;
55 3 50       10 if ( $] >= 5.010 ) {
56 3         16 require 'feature.pm';
57 3         2805 feature->import( ':' . substr("$^V",1,4) );
58             }
59              
60             }
61              
62             sub new {
63 1     1 1 38 my $class = shift;
64 1 50 33     13 bless @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {}, ref $class || $class;
  0 50       0  
65             }
66              
67             sub attr {
68 8     8 1 15 my ( $class, $attrs, $default ) = @_;
69 8 50 33     55 return unless ( $class = ref $class || $class ) && $attrs;
      33        
70              
71 8 50 66     22 Carp::croak 'Default has to be a code reference or constant value'
72             if ref $default && ref $default ne 'CODE';
73              
74             # Compile attributes
75 8 50       8 for my $attr ( @{ ref $attrs eq 'ARRAY' ? $attrs : [$attrs] } ) {
  8         26  
76 8 50       31 Carp::croak qq{Attribute "$attr" invalid}
77             unless $attr =~ /^[a-zA-Z_]\w*$/;
78              
79 8         125 my $code = "package $class;\nsub $attr {\n if (\@_ == 1) {\n";
80 8 50       16 unless ( defined $default ) { $code .= " return \$_[0]{'$attr'};" }
  0         0  
81             else {
82 8         15 $code .= " return \$_[0]{'$attr'} if exists \$_[0]{'$attr'};\n";
83 8         11 $code .= " return \$_[0]{'$attr'} = ";
84 8 100       18 $code .=
85             ref $default eq 'CODE' ? '$default->($_[0]);' : '$default;';
86             }
87              
88 8         13 $code .= "\n }\n \$_[0]{'$attr'} = \$_[1];\n";
89 8         20 $code .= " \$_[0];\n}";
90              
91 1     1   4 no strict 'refs';
  1         2  
  1         184  
92 8 50       20 warn "-- Attribute $attr in $class\n$code\n\n" if $ENV{MOMO_DEBUG};
93 8 50   4   942 Carp::croak "Momo error: $@" unless eval "$code;1";
  4 50   2   59  
  3 100   1   27  
  0 100   1   0  
  1 50   1   11  
  1 50   2   3  
  2 50   1   46  
  2 50   11   10  
  1 50       10  
  0 50       0  
  0 50       0  
  1 50       606  
  1 50       6  
  1 50       5  
  0 50       0  
  0 100       0  
  1 100       585  
  1         5  
  1         7  
  0         0  
  0         0  
  1         5  
  1         4  
  1         6  
  0         0  
  0         0  
  2         35  
  2         17  
  0         0  
  0         0  
  0         0  
  1         565  
  1         5  
  1         4  
  0         0  
  0         0  
  11         339  
  6         30  
  1         6  
  5         13  
  5         107  
94             }
95             }
96              
97             sub tap {
98 0     0 1   my ( $self, $cb ) = @_;
99 0           $_->$cb for $self;
100 0           return $self;
101             }
102              
103             1;
104              
105             =encoding utf8
106              
107             =head1 NAME
108              
109             Momo,a simple oop module inspired from Mojo::Base and Moo.
110              
111             =head1 SYNOPSIS
112              
113            
114             package Person;
115              
116             use Momo;
117            
118             has name => 'james'; # if not set,default set james
119             has [qw(age sex)];
120              
121             sub new{
122             say "I'm a Person";
123             }
124              
125             1;
126              
127             =head1 DESCRIPTION
128              
129             Why I want to write this module? If you heard about Moose or Moo,you know they
130             are pretty module for perl object-oriented program.
131             Compare with old style of perl5 object-oriented program:
132              
133            
134             package Foo;
135              
136             BEGIN{
137             push @ISA,"Some::SuperClass";
138             } # or
139             use base 'Some::SuperClass';
140              
141             sub new{
142             my $self = bless {} shift;
143             $self->{name} = shift;
144             $self->{age} = shift;
145             return $self;
146             }
147              
148             sub some_method{
149             say "I'm some method";
150             }
151              
152             1;
153              
154             # invoke method as oop style
155             my $obj = Foo->new;
156             $obj->some_method;
157              
158             In moose or moo,write like this:
159              
160             package Foo;
161              
162             use Moose; # or use Moo
163            
164             extends 'SomeBaseClass' with 'role1','role2';
165              
166             has x => ( is => 'rw',default => sub { {} },lazy => 1);
167             has y => ( is => 'ro',default => ref {} );
168             has z => ( is => 'rw',default => sub { { xx => 'yy'} },required => 1);
169              
170             before method1 => sub {
171             .....
172             };
173              
174             after method1 => sub {
175             };
176              
177             around method1 => sub {
178             };
179              
180             1;
181              
182             It looks so amazing,everything works fine,L can give you vast powerful feature
183             of perl Object-Oriented,the syntax sugars like 'extends','has','with'...etc are magic.
184             On the other hand,you can override the attribute with C,and also,with the role
185             make it more inconceivable.
186              
187             But,I still find some problem:
188              
189             =over 4
190              
191             =item the cumbersome syntax in C defined
192            
193             Every time when I type the C blabla,does this below is really I want?
194              
195             has ua=> ( is => 'rw',isa => 'LWP::UserAgent',
196             default => sub { LWP::UserAgent->new },lazy => 1);
197             # is => 'rw', over and over,lazy => 1, over and over,default,over and
198             # over....
199             # It makes me creazy!
200              
201             You know perl is a dynamically typed language,not like java ,c++,I think I don't need
202             the feature,even I almost do not used this feature,I also write ruby and python,but I
203             never see this.
204              
205             =item inherit some class of non Moose modules
206              
207             In L or L,if you want to inherit a calss of non Moose style,all I know is
208             to use L;
209              
210             package Term::VT102::NBased;
211             use Moose;
212             use MooseX::NonMoose;
213             extends 'Term::VT102';
214              
215             has [qw/x_base y_base/] => (
216             is => 'ro',
217             isa => 'Int',
218             default => 1,
219             );
220              
221             around x => sub {
222             my $orig = shift;
223             my $self = shift;
224             $self->$orig(@_) + $self->x_base - 1;
225             };
226              
227             # ... (wrap other methods)
228              
229             no Moose;
230             # no need to fiddle with inline_constructor here
231             __PACKAGE__->meta->make_immutable;
232              
233             my $vt = Term::VT102::NBased->new(x_base => 0, y_base => 0);
234              
235             or:
236              
237             package Crawler::Event;
238              
239             use Moo;
240              
241             extends 'Object::Event','Moo::Object';
242              
243             # explicit constructor
244             sub new {
245             my $class = shift;
246              
247             # call Mojo::UserAgent's constructor
248             my $obj = $class->SUPER::new(@_);
249             return $class->meta->new_object(
250             # pass in the constructed object
251             # using the special key __INSTANCE__
252             __INSTANCE__ => $obj,
253             @_, # pass in the normal args
254             );
255             }
256              
257             1;
258              
259             It looks so weird,this is just a simple object inherit,why I need to type so many
260             code and install a Moosex module.
261             Also,there is another way to fix this,use C
262              
263             package Website;
264              
265             use Moose;
266              
267             has 'uri' => (
268             is => 'ro',
269             isa => 'URI',
270             handles => {
271             hostname => 'host',
272             path => 'path',
273             },
274             );
275              
276             But I think this is more complex,if you lost some method of handles,when you run
277             your code which just need to inherit LWP,then throw a error like:
278              
279             Can't find this method
280              
281             Oh my god,why I must do this?
282              
283             =item speed of runtime slowly
284              
285             Although,Moo have make moose looks tidy and simple,at runtime,it still cost more
286             time than old style of perl Object-Oriented program.
287              
288             Here is the benchmark of Momo,Moose,Moo,hashref,bless hashref,each test create a
289             object and access the attr,set the attr:
290              
291             Benchmark: timing 1000000 iterations of blessed_hashref, hashref, momo, moo, moose...
292             blessed_hashref: 1.60316 wallclock secs ( 1.60 usr + 0.00 sys = 1.60 CPU) @ 625000.00/s (n=1000000)
293             hashref: 1.34393 wallclock secs ( 1.35 usr + 0.00 sys = 1.35 CPU) @ 740740.74/s (n=1000000)
294             momo: 3.97532 wallclock secs ( 3.97 usr + 0.00 sys = 3.97 CPU) @ 251889.17/s (n=1000000)
295             moo: 5.36459 wallclock secs ( 5.37 usr + 0.00 sys = 5.37 CPU) @ 186219.74/s (n=1000000)
296             moose: 7.81556 wallclock secs ( 7.81 usr + 0.00 sys = 7.81 CPU) @ 128040.97/s (n=1000000)
297             Rate moose moo momo blessed_hashref hashref
298             moose 128041/s -- -31% -49% -80% -83%
299             moo 186220/s 45% -- -26% -70% -75%
300             momo 251889/s 97% 35% -- -60% -66%
301             blessed_hashref 625000/s 388% 236% 148% -- -16%
302             hashref 740741/s 479% 298% 194% 19% --
303              
304             The result shows that Momo is faster than Moo,Moose.
305              
306             =back
307              
308             When I develop mojo app,I found C is so simple and light,fast speed,
309             and I add some features like : role,method modifiers,everything works fine for me.
310              
311             Anyway,if you hate all of these,try L,or you can keep working on L or L.
312              
313             It's so simple:
314              
315             package MomoStyle;
316              
317             use Momo;
318             extends 'LWP::UserAgent'; #inherit LWP::UserAgent so easy
319             with 'Logger'; # does a logging role,same as moose's role
320              
321             has name => 'momo';
322             has city => 'beijing';
323             has check => sub {
324             my $self = shift;
325             if( $name eq 'momo' ){
326             # do some stuff here
327             }
328             };
329             # if you need do some other thing,you can override new
330             sub new{
331             my $self = shift->SUPER::new(@_);
332             $self->agent("momo");
333             $self;
334             }
335              
336             1;
337              
338              
339             =head1 FUNCTIONS
340              
341             L exports the following functions
342              
343             =head2 has
344              
345             has 'name';
346             has [qw(name1 name2 name3)];
347             has name => 'foo';
348             has name => sub {...};
349             has [qw(name1 name2 name3)] => 'foo';
350             has [qw(name1 name2 name3)] => sub {...};
351              
352             Create attributes for hash-based objects, just like the L has or L,
353             but ignore the option of C,C...
354              
355             =head1 METHODS
356              
357             L implements the following methods.
358              
359             =head2 new
360              
361             my $object = BaseSubClass->new;
362             my $object = BaseSubClass->new(name => 'value');
363             my $object = BaseSubClass->new({name => 'value'});
364              
365             This base class provides a basic constructor for hash-based objects. You can
366             pass it either a hash or a hash reference with attribute values.
367              
368             =head2 attr
369              
370             $object->attr('name');
371             BaseSubClass->attr('name');
372             BaseSubClass->attr([qw(name1 name2 name3)]);
373             BaseSubClass->attr(name => 'foo');
374             BaseSubClass->attr(name => sub {...});
375             BaseSubClass->attr([qw(name1 name2 name3)] => 'foo');
376             BaseSubClass->attr([qw(name1 name2 name3)] => sub {...});
377              
378             Create attribute accessor for hash-based objects, an array reference can be
379             used to create more than one at a time. Pass an optional second argument to
380             set a default value, it should be a constant or a callback. The callback will
381             be executed at accessor read time if there's no set value. Accessors can be
382             chained, that means they return their invocant when they are called with an
383             argument.
384              
385             =head2 extends
386              
387             use this to inherit a class,different with Moose's extends,you can extends any
388             other module,what if they are blessed style module,
389              
390             extends 'LWP::UserAgent';
391             extends 'BaseClass1','BaseClass2';
392              
393             =head2 with
394              
395             use this to does a role,about role you can check L
396              
397             extends 'Mojo::Lite';
398             with 'Some::Role';
399              
400             1;
401              
402             =head2 method_modifiers
403              
404             Same as Moo and Moose,Momo support method modifiers in Class or Role:
405            
406             package Role1;
407              
408             use Momo::Role;
409              
410             before some_method => sub { print "i'm before method" };
411             after some_method => sub { print "i'm after method" };
412             around some_method => sub { $_[1]->$_[0](@_) };
413              
414             in class package:
415              
416             package Cat;
417              
418             use Momo;
419              
420             before feed => sub { print "I want to eat some water" };
421             after feed => sub { print "after feed,I'm full" };
422             around feed => sub { print "I should eat what" };
423             sub feed { print "I want to feed ..." };
424              
425             =head2 tap
426              
427             $object = $object->tap(sub {...});
428              
429             K combinator, tap into a method chain to perform operations on an object
430             within the chain. The object will be the first argument passed to the callback
431             and is also available as C<$_>.
432              
433              
434             =head1 DEBUGGING
435              
436             You can set the C environment variable to get some advanced
437             diagnostics information printed to C.
438              
439             MOMO_DEBUG=1
440              
441             =head1 SEE ALSO
442              
443             L,L,L,L
444              
445             =head1 TODO
446              
447             =over 4
448              
449             =item support MOP
450              
451             =item write C with XS
452              
453             =back
454              
455             =head1 BUGS
456              
457             Any bugs just email C,or commit a issue on github:
458             L
459              
460             =head1 AUTHOR
461              
462             舌尖上的牛氓 C
463              
464             QQ: 492003149
465              
466             QQ-Group: 211685345
467              
468             Site: L
469              
470             =head1 Copyright
471              
472             Copyright (C) <2013>, <舌尖上的牛氓>.
473              
474             This module is free software; you
475             can redistribute it and/or modify it under the same terms
476             as Perl 5.10.0. For more details, see the full text of the
477             licenses in the directory LICENSES.
478              
479             This program is distributed in the hope that it will be
480             useful, but without any warranty; without even the implied
481             warranty of merchantability or fitness for a particular purpose.
482              
483             =cut
484              
485             # niumang // vim: ts=4 sw=4 expandtab
486             # TODO - Edit.
487