Newer Older
230 lines | 5.416kb
add files
Yuki Kimoto authored on 2014-03-26
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