add files
|
1 |
package Mojo::Base; |
2 | ||
3 |
use strict; |
|
4 |
use warnings; |
|
5 |
use utf8; |
|
6 |
use feature (); |
|
7 | ||
8 |
# No imports because we get subclassed, a lot! |
|
9 |
use Carp (); |
|
10 | ||
11 |
# Only Perl 5.14+ requires it on demand |
|
12 |
use IO::Handle (); |
|
13 | ||
14 |
sub import { |
|
15 |
my $class = shift; |
|
16 |
return unless my $flag = shift; |
|
17 | ||
18 |
# Base |
|
19 |
if ($flag eq '-base') { $flag = $class } |
|
20 | ||
21 |
# Strict |
|
22 |
elsif ($flag eq '-strict') { $flag = undef } |
|
23 | ||
24 |
# Module |
|
25 |
elsif ((my $file = $flag) && !$flag->can('new')) { |
|
26 |
$file =~ s!::|'!/!g; |
|
27 |
require "$file.pm"; |
|
28 |
} |
|
29 | ||
30 |
# ISA |
|
31 |
if ($flag) { |
|
32 |
my $caller = caller; |
|
33 |
no strict 'refs'; |
|
34 |
push @{"${caller}::ISA"}, $flag; |
|
35 |
*{"${caller}::has"} = sub { attr($caller, @_) }; |
|
36 |
} |
|
37 | ||
38 |
# Mojo modules are strict! |
|
39 |
strict->import; |
|
40 |
warnings->import; |
|
41 |
utf8->import; |
|
42 |
feature->import(':5.10'); |
|
43 |
} |
|
44 | ||
45 |
sub new { |
|
46 |
my $class = shift; |
|
47 |
bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class; |
|
48 |
} |
|
49 | ||
50 |
sub attr { |
|
51 |
my ($class, $attrs, $default) = @_; |
|
52 |
return unless ($class = ref $class || $class) && $attrs; |
|
53 | ||
54 |
Carp::croak 'Default has to be a code reference or constant value' |
|
55 |
if ref $default && ref $default ne 'CODE'; |
|
56 | ||
57 |
for my $attr (@{ref $attrs eq 'ARRAY' ? $attrs : [$attrs]}) { |
|
58 |
Carp::croak qq{Attribute "$attr" invalid} unless $attr =~ /^[a-zA-Z_]\w*$/; |
|
59 | ||
60 |
# Header (check arguments) |
|
61 |
my $code = "package $class;\nsub $attr {\n if (\@_ == 1) {\n"; |
|
62 | ||
63 |
# No default value (return value) |
|
64 |
unless (defined $default) { $code .= " return \$_[0]{'$attr'};" } |
|
65 | ||
66 |
# Default value |
|
67 |
else { |
|
68 | ||
69 |
# Return value |
|
70 |
$code .= " return \$_[0]{'$attr'} if exists \$_[0]{'$attr'};\n"; |
|
71 | ||
72 |
# Return default value |
|
73 |
$code .= " return \$_[0]{'$attr'} = "; |
|
74 |
$code .= ref $default eq 'CODE' ? '$default->($_[0]);' : '$default;'; |
|
75 |
} |
|
76 | ||
77 |
# Store value |
|
78 |
$code .= "\n }\n \$_[0]{'$attr'} = \$_[1];\n"; |
|
79 | ||
80 |
# Footer (return invocant) |
|
81 |
$code .= " \$_[0];\n}"; |
|
82 | ||
83 |
warn "-- Attribute $attr in $class\n$code\n\n" if $ENV{MOJO_BASE_DEBUG}; |
|
84 |
Carp::croak "Mojo::Base error: $@" unless eval "$code;1"; |
|
85 |
} |
|
86 |
} |
|
87 | ||
88 |
sub tap { |
|
89 |
my ($self, $cb) = @_; |
|
90 |
$_->$cb for $self; |
|
91 |
return $self; |
|
92 |
} |
|
93 | ||
94 |
1; |
|
95 | ||
96 |
=encoding utf8 |
|
97 | ||
98 |
=head1 NAME |
|
99 | ||
100 |
Mojo::Base - Minimal base class for Mojo projects |
|
101 | ||
102 |
=head1 SYNOPSIS |
|
103 | ||
104 |
package Cat; |
|
105 |
use Mojo::Base -base; |
|
106 | ||
107 |
has name => 'Nyan'; |
|
108 |
has [qw(birds mice)] => 2; |
|
109 | ||
110 |
package Tiger; |
|
111 |
use Mojo::Base 'Cat'; |
|
112 | ||
113 |
has friend => sub { Cat->new }; |
|
114 |
has stripes => 42; |
|
115 | ||
116 |
package main; |
|
117 |
use Mojo::Base -strict; |
|
118 | ||
119 |
my $mew = Cat->new(name => 'Longcat'); |
|
120 |
say $mew->mice; |
|
121 |
say $mew->mice(3)->birds(4)->mice; |
|
122 | ||
123 |
my $rawr = Tiger->new(stripes => 23, mice => 0); |
|
124 |
say $rawr->tap(sub { $_->friend->name('Tacgnol') })->mice; |
|
125 | ||
126 |
=head1 DESCRIPTION |
|
127 | ||
128 |
L<Mojo::Base> is a simple base class for L<Mojo> projects. |
|
129 | ||
130 |
# Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features |
|
131 |
use Mojo::Base -strict; |
|
132 |
use Mojo::Base -base; |
|
133 |
use Mojo::Base 'SomeBaseClass'; |
|
134 | ||
135 |
All three forms save a lot of typing. |
|
136 | ||
137 |
# use Mojo::Base -strict; |
|
138 |
use strict; |
|
139 |
use warnings; |
|
140 |
use utf8; |
|
141 |
use feature ':5.10'; |
|
142 |
use IO::Handle (); |
|
143 | ||
144 |
# use Mojo::Base -base; |
|
145 |
use strict; |
|
146 |
use warnings; |
|
147 |
use utf8; |
|
148 |
use feature ':5.10'; |
|
149 |
use IO::Handle (); |
|
150 |
use Mojo::Base; |
|
151 |
push @ISA, 'Mojo::Base'; |
|
152 |
sub has { Mojo::Base::attr(__PACKAGE__, @_) } |
|
153 | ||
154 |
# use Mojo::Base 'SomeBaseClass'; |
|
155 |
use strict; |
|
156 |
use warnings; |
|
157 |
use utf8; |
|
158 |
use feature ':5.10'; |
|
159 |
use IO::Handle (); |
|
160 |
require SomeBaseClass; |
|
161 |
push @ISA, 'SomeBaseClass'; |
|
162 |
use Mojo::Base; |
|
163 |
sub has { Mojo::Base::attr(__PACKAGE__, @_) } |
|
164 | ||
165 |
=head1 FUNCTIONS |
|
166 | ||
167 |
L<Mojo::Base> exports the following functions if imported with the C<-base> |
|
168 |
flag or a base class. |
|
169 | ||
170 |
=head2 has |
|
171 | ||
172 |
has 'name'; |
|
173 |
has [qw(name1 name2 name3)]; |
|
174 |
has name => 'foo'; |
|
175 |
has name => sub {...}; |
|
176 |
has [qw(name1 name2 name3)] => 'foo'; |
|
177 |
has [qw(name1 name2 name3)] => sub {...}; |
|
178 | ||
179 |
Create attributes for hash-based objects, just like the L</"attr"> method. |
|
180 | ||
181 |
=head1 METHODS |
|
182 | ||
183 |
L<Mojo::Base> implements the following methods. |
|
184 | ||
185 |
=head2 new |
|
186 | ||
187 |
my $object = BaseSubClass->new; |
|
188 |
my $object = BaseSubClass->new(name => 'value'); |
|
189 |
my $object = BaseSubClass->new({name => 'value'}); |
|
190 | ||
191 |
This base class provides a basic constructor for hash-based objects. You can |
|
192 |
pass it either a hash or a hash reference with attribute values. |
|
193 | ||
194 |
=head2 attr |
|
195 | ||
196 |
$object->attr('name'); |
|
197 |
BaseSubClass->attr('name'); |
|
198 |
BaseSubClass->attr([qw(name1 name2 name3)]); |
|
199 |
BaseSubClass->attr(name => 'foo'); |
|
200 |
BaseSubClass->attr(name => sub {...}); |
|
201 |
BaseSubClass->attr([qw(name1 name2 name3)] => 'foo'); |
|
202 |
BaseSubClass->attr([qw(name1 name2 name3)] => sub {...}); |
|
203 | ||
204 |
Create attribute accessor for hash-based objects, an array reference can be |
|
205 |
used to create more than one at a time. Pass an optional second argument to |
|
206 |
set a default value, it should be a constant or a callback. The callback will |
|
207 |
be executed at accessor read time if there's no set value. Accessors can be |
|
208 |
chained, that means they return their invocant when they are called with an |
|
209 |
argument. |
|
210 | ||
211 |
=head2 tap |
|
212 | ||
213 |
$object = $object->tap(sub {...}); |
|
214 | ||
215 |
K combinator, tap into a method chain to perform operations on an object |
|
216 |
within the chain. The object will be the first argument passed to the callback |
|
217 |
and is also available as C<$_>. |
|
218 | ||
219 |
=head1 DEBUGGING |
|
220 | ||
221 |
You can set the MOJO_BASE_DEBUG environment variable to get some advanced |
|
222 |
diagnostics information printed to C<STDERR>. |
|
223 | ||
224 |
MOJO_BASE_DEBUG=1 |
|
225 | ||
226 |
=head1 SEE ALSO |
|
227 | ||
228 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
229 | ||
230 |
=cut |