File Coverage

blib/lib/Kelp/Base.pm
Criterion Covered Total %
statement 52 53 98.1
branch 13 14 92.8
condition 7 9 77.7
subroutine 13 13 100.0
pod 0 2 0.0
total 85 91 93.4


line stmt bran cond sub pod time code
1             package Kelp::Base;
2              
3 56     56   276408 use strict;
  56         120  
  56         2189  
4 56     56   320 use warnings;
  56         207  
  56         3320  
5 56     56   346 use feature ();
  56         119  
  56         1232  
6 56     56   281 use Carp;
  56         164  
  56         11611  
7              
8             require namespace::autoclean;
9             require Kelp::Util;
10              
11             sub import
12             {
13 870     870   221251 my $class = shift;
14 870         2595 my $caller = caller;
15              
16             # Do not import into inherited classes
17 870 100       48578 return if $class ne __PACKAGE__;
18              
19 703   66     3119 my $base = shift || $class;
20              
21 703 100       2109 if ($base ne '-strict') {
22 56     56   413 no strict 'refs';
  56         160  
  56         3077  
23 56     56   313 no warnings 'redefine';
  56         97  
  56         22117  
24              
25 621 100       5528 if ($base ne '-attr') {
26 620         4762 Kelp::Util::load_package($base);
27 620         1206 push @{"${caller}::ISA"}, $base;
  620         8678  
28             }
29              
30 621     2681   5159 *{"${caller}::attr"} = sub { attr($caller, @_) };
  621         4926  
  2681         479235  
31              
32 621         6839 namespace::autoclean->import(
33             -cleanee => $caller
34             );
35             }
36              
37 703         36469 strict->import;
38 703         22412 warnings->import;
39 703         225994 feature->import(':5.10');
40             }
41              
42             sub new
43             {
44 1252     1252 0 1514431 my $self = shift;
45 1252         7007 return bless {@_}, $self;
46             }
47              
48             sub attr
49             {
50 2683     2683 0 5796 my ($class, $name, $default) = @_;
51              
52 2683 50 66     9977 if (ref $default && ref $default ne 'CODE') {
53 0         0 croak "Default value for '$name' can not be a reference.";
54             }
55              
56             # Readonly attributes are marked with '-'
57 2683         5945 my $readonly = $name =~ s/^\-//;
58              
59             # Remember if default is a function
60 2683         14106 my $default_sub = ref $default eq 'CODE';
61              
62             {
63 56     56   468 no strict 'refs';
  56         122  
  56         2775  
  2683         3557  
64 56     56   491 no warnings 'redefine';
  56         162  
  56         12456  
65              
66 2683         13965 *{"${class}::$name"} = sub {
67 30629 100 100 30629   272739 return $_[0]->{$name} = $_[1] if @_ > 1 && !$readonly;
68 26722 100       133084 return $_[0]->{$name} if exists $_[0]->{$name};
69 3242 100       16076 return $_[0]->{$name} = $default_sub ? $default->($_[0]) : $default;
70 2683         10580 };
71             }
72             }
73              
74             1;
75              
76             __END__
77              
78             =pod
79              
80             =head1 NAME
81              
82             Kelp::Base - Simple lazy attributes
83              
84             =head1 SYNOPSIS
85              
86             use Kelp::Base;
87              
88             attr source => 'dbi:mysql:users';
89             attr user => 'test';
90             attr pass => 'secret';
91             attr opts => sub { { PrintError => 1, RaiseError => 1 } };
92              
93             attr dbh => sub {
94             my $self = shift;
95             DBI->connect( $self->sourse, $self->user, $self->pass, $self->opts );
96             };
97              
98             # Later ...
99             sub do_stuff {
100             my $self = shift;
101             $self->dbh->do('DELETE FROM accounts');
102             }
103              
104             or
105              
106             use Kelp::Base 'Module::Name'; # Extend Module::Name
107              
108             or
109              
110             use Kelp::Base -strict; # Only use strict, warnings and v5.10
111             # No magic
112              
113             =head1 DESCRIPTION
114              
115             This module provides simple lazy attributes.
116              
117             =head1 WHY?
118              
119             Some users will naturally want to ask F<"Why not use Moose/Mouse/Moo/Mo?">. The
120             answer is that the Kelp web framework needs lazy attributes, but the author
121             wanted to keep the code light and object manager agnostic. This allows the
122             users of the framework to choose an object manager to their liking. As a nice
123             addition, our getters and constructors are quite a bit faster than any non-XS
124             variant of L<Moose>, which makes the core code very fast.
125              
126             There is nothing more annoying than a module that forces you to use L<Moose>
127             when you are perfectly fine with L<Moo> or L<Mo>, for example. Since this
128             module is so minimal, you should probably switch to a full-blown OO system of
129             your choice when writing your application. Kelp::Base should be compatible with
130             it as long as it uses blessed hashes under the hood.
131              
132             =head1 USAGE
133              
134             use Kelp::Base;
135              
136             The above will automatically include C<strict>, C<warnings> and C<v5.10>. It will
137             also inject a new sub in the current class called C<attr>.
138              
139             attr name1 => 1; # Fixed value
140             attr name2 => sub { [ 1, 2, 3 ] }; # Array
141             attr name3 => sub {
142             $_[0]->other;
143             }
144              
145             ...
146              
147             say $self->name1; # 1
148             $self->name2( [ 6, 7, 8 ] ); # Set new value
149              
150             All those attributes will be available for reading and writing in each instance
151             of the current class. If you want to create a read-only attribute, prefix its
152             name with a dash.
153              
154             attr -readonly => "something";
155              
156             # Later
157             say $self->readonly; # something
158             $self->readonly("nothing"); # no change
159              
160             Kelp::Base can also be imported without turning an object into a class:
161              
162             # imports strict, warnings and :5.10
163             use Kelp::Base -strict;
164              
165             # imports all of the above plus attr
166             use Kelp::Base -attr;
167              
168             The former is useful for less boilerplate in scripts on older perls. The latter
169             is useful when using C<attr> with L<Role::Tiny>.
170              
171             =head1 SEE ALSO
172              
173             L<Kelp>, L<Moose>, L<Moo>, L<Mo>, L<Any::Moose>
174              
175             =cut
176