File Coverage

blib/lib/Homer.pm
Criterion Covered Total %
statement 51 51 100.0
branch 8 10 80.0
condition 5 12 41.6
subroutine 12 12 100.0
pod 1 1 100.0
total 77 86 89.5


line stmt bran cond sub pod time code
1             package Homer;
2              
3             # ABSTRACT: Simple prototype-based object system
4              
5 2     2   51926 use warnings;
  2         4  
  2         67  
6 2     2   10 use strict;
  2         4  
  2         63  
7              
8 2     2   11 use Carp;
  2         7  
  2         503  
9              
10             our $VERSION = "1.000000";
11             $VERSION = eval $VERSION;
12              
13             =head1 NAME
14              
15             Homer - Simple prototype-based object system
16              
17             =head1 SYNOPSIS
18              
19             use Homer;
20              
21             # create a prototype object
22             my $person = Homer->new(
23             first_name => 'Generic',
24             last_name => 'Person',
25             say_hi => sub {
26             my $self = shift;
27             print "Hi, my name is ", $self->first_name, ' ', $self->last_name, "\n";
28             }
29             );
30              
31             # create a new object based on it
32             my $homer = $person->extend(
33             first_name => 'Homer',
34             last_name => 'Simpson'
35             );
36              
37             $homer->say_hi; # prints 'Hi, my name is Homer Simpson'
38              
39             # let's extend even more
40             my $bart = $homer->extend(
41             first_name => 'Bart',
42             father => sub { print "My father's name is ", $_[0]->prot->first_name, "\n" }
43             );
44              
45             $bart->say_hi; # prints 'Hi, my name is Bart Simpson'
46             $bart->father; # prints "My father's name is Homer"
47              
48             =head1 DESCRIPTION
49              
50             C is a very simple B, similar to JavaScript.
51             In a prototype based object system there are no classes. Objects are either directly created
52             with some attributes and methods, or cloned from existing objects, in which case the object
53             being cloned becomes the prototype of the new object. The new object inherits all attributes
54             and methods from the prototype. Attributes and methods can be overridden, and new ones can be
55             added. The new object can be cloned as well, becoming the prototype of yet another new object,
56             thus creating a possibly endless chain of prototypes.
57              
58             Prototype-based objects can be very powerful and useful in certain cases. They can provide a
59             quick way of solving problems. Plus, sometimes you just really need an object, but don't need
60             a class. I like to think of prototype-based OO versus class-based OO as being similar to
61             schema-less database systems versus relational database systems.
62              
63             C is a quick and dirty implementation of such a system in Perl. As Perl is a class-based
64             language, this is merely a hack. When an object is created, C creates a specific class just
65             for it behind the scenes. When an object is cloned, a new class is created for the clone, with the
66             parent object's class pushed to the new one's C<@ISA> variable, thus providing inheritance.
67              
68             I can't say this implementation is particularly smart or efficient, but it gives me what I need
69             and is very lightweight (C has no non-core dependencies). If you need a more robust
70             solution, L might fit your need.
71              
72             =head1 HOMER AT A GLANCE
73              
74             =over
75              
76             =item * Prototypes are created by calling C on the C class with a hash, holding
77             attributes and methods:
78              
79             my $prototype = Homer->new(
80             attr1 => 'value1',
81             attr2 => 'value2',
82             meth1 => sub { print "meth1" }
83             );
84              
85             $prototype->attr1; # value1
86             $prototype->attr2; # value2
87             $prototype->meth1; # prints "meth1"
88              
89             =item * A list of all pure-attributes of an object (i.e. not methods) can be received by
90             calling C on the object.
91              
92             $prototype->attributes; # ('attr1', 'attr2')
93              
94             =item * Every object created by Homer can be cloned using C. The hash can
95             contain new attributes and methods, and can override existing ones.
96              
97             my $clone = $prototype->extend(
98             attr2 => 'value3',
99             meth2 => sub { print "meth2" }
100             );
101              
102             $clone->attr1; # value1
103             $clone->attr2; # value3
104             $clone->meth1; # prints "meth1"
105             $clone->meth2; # prints "meth2"
106              
107             =item * Objects based on a prototype can refer to their prototype using the C method:
108              
109             $clone->prot->attr2; # value2
110              
111             =item * All attributes are read-write:
112              
113             $clone->attr1('value4');
114             $clone->attr1; # value4
115             $clone->prot->attr1; # still value1
116              
117             =item * New methods can be added to an object after its construction. If the object is a
118             prototype of other objects, they will immediately receive the new methods too.
119              
120             $prototype->add_method('meth3' => sub { print "meth3" });
121             $clone->can('meth3'); # true
122              
123             =item * New attributes can't be added after construction (for now).
124              
125             =item * Cloned objects can be cloned too, creating a chain of prototypes:
126              
127             my $clone2 = $clone->extend;
128             my $clone3 = $clone2->extend;
129             $clone3->prot->prot->prot; # the original $prototype
130              
131             =back
132              
133             =head1 CONSTRUCTOR
134              
135             =head2 new( [ %attrs ] )
136              
137             Creates a new prototype object with the provided attributes and methods (if any).
138              
139             =cut
140              
141             sub new {
142 1     1 1 25 my ($this_class, %attrs) = @_;
143              
144 1         6 my $new_class = $this_class->_generate_class;
145              
146 1         8 return $this_class->_generate_object($new_class, %attrs);
147             }
148              
149             sub _generate_class {
150 3     3   8 my $this_class = shift;
151              
152 3         33 my @caller = caller(1);
153              
154 3         21 return join('::', $this_class, @caller[3,2]);
155             }
156              
157             sub _generate_object {
158 3     3   12 my ($this_class, $new_class, %attrs) = @_;
159              
160 2     2   14 no strict 'refs';
  2         4  
  2         1069  
161 3         12 foreach my $a (keys %attrs) {
162 10 100 66     57 if (ref $attrs{$a} && ref $attrs{$a} eq 'CODE') {
163             # method
164 4         9 *{"${new_class}::$a"} = delete($attrs{$a});
  4         40  
165             } else {
166 6         33 *{"${new_class}::$a"} = sub {
167 13     13   1945 my ($self, $newval) = @_;
168              
169 13 100       33 $self->{$a} = $newval
170             if $newval;
171              
172 13         62 return $self->{$a};
173 6         28 };
174             }
175             }
176              
177 3     2   14 *{"${new_class}::attributes"} = sub { keys %attrs };
  3         16  
  2         8  
178              
179 3         17 *{"${new_class}::extend"} = sub {
180 2     2   1627 my ($prot, %attrs) = @_;
181              
182 2         14 foreach ($prot->attributes) {
183 3 100       14 $attrs{$_} = $prot->$_
184             unless exists $attrs{$_};
185             }
186              
187 2         8 my $new_class = $this_class->_generate_class;
188 2         6 @{"${new_class}::ISA"} = (ref($prot));
  2         54  
189              
190 2     2   9 *{"${new_class}::prot"} = sub { $prot };
  2         14  
  2         10  
191              
192 2         27 return $this_class->_generate_object($new_class, %attrs);
193 3         16 };
194              
195 3         16 *{"${new_class}::add_method"} = sub {
196 1     1   502 my ($self, $name, $code) = @_;
197              
198 1 50 33     9 croak "You must provide the name of the method"
199             unless $name && !ref $name;
200 1 50 33     13 croak "You must provide an anonymous subroutine"
      33        
201             unless $code && ref $code && ref $code eq 'CODE';
202              
203 1         2 *{"${new_class}::$name"} = $code;
  1         11  
204 3         22 };
205              
206 3         18 return bless \%attrs, $new_class;
207             }
208              
209             =head1 CONFIGURATION AND ENVIRONMENT
210            
211             C requires no configuration files or environment variables.
212              
213             =head1 DEPENDENCIES
214              
215             None other than L.
216              
217             =head1 BUGS AND LIMITATIONS
218              
219             Please report any bugs or feature requests to
220             C, or through the web interface at
221             L.
222              
223             =head1 SUPPORT
224              
225             You can find documentation for this module with the perldoc command.
226              
227             perldoc Homer
228              
229             You can also look for information at:
230              
231             =over 4
232            
233             =item * RT: CPAN's request tracker
234            
235             L
236            
237             =item * AnnoCPAN: Annotated CPAN documentation
238            
239             L
240            
241             =item * CPAN Ratings
242            
243             L
244            
245             =item * Search CPAN
246            
247             L
248            
249             =back
250            
251             =head1 AUTHOR
252            
253             Ido Perlmuter
254            
255             =head1 LICENSE AND COPYRIGHT
256            
257             Copyright (c) 2014, Ido Perlmuter C<< ido@ido50.net >>.
258            
259             This module is free software; you can redistribute it and/or
260             modify it under the same terms as Perl itself, either version
261             5.8.1 or any later version. See L
262             and L.
263            
264             The full text of the license can be found in the
265             LICENSE file included with this module.
266            
267             =head1 DISCLAIMER OF WARRANTY
268            
269             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
270             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
271             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
272             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
273             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
274             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
275             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
276             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
277             NECESSARY SERVICING, REPAIR, OR CORRECTION.
278            
279             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
280             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
281             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
282             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
283             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
284             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
285             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
286             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
287             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
288             SUCH DAMAGES.
289              
290             =cut
291              
292             1;
293             __END__