File Coverage

blib/lib/Class/Anonymous.pm
Criterion Covered Total %
statement 42 42 100.0
branch 11 12 91.6
condition n/a
subroutine 12 12 100.0
pod 4 4 100.0
total 69 70 98.5


line stmt bran cond sub pod time code
1             package Class::Anonymous;
2              
3 4     4   99425 use strict;
  4         10  
  4         107  
4 4     4   21 use warnings;
  4         8  
  4         199  
5              
6             our $VERSION = '0.03';
7             $VERSION = eval $VERSION;
8              
9 4     4   1925 use Class::Anonymous::Instance;
  4         11  
  4         107  
10              
11 4     4   21 use Exporter 'import';
  4         5  
  4         205  
12             our @EXPORT = qw/class extend via/;
13              
14 4     4   20 use List::Util 'first';
  4         9  
  4         607  
15 4     4   30 use Scalar::Util 'refaddr';
  4         7  
  4         2687  
16              
17             my $bless = eval {
18             require Package::Anon;
19             my $stash = Package::Anon->new;
20             $stash->add_method(AUTOLOAD => \&Class::Anonymous::Instance::AUTOLOAD);
21             $stash->add_method(DESTROY => \&Class::Anonymous::Instance::DESTROY);
22             $stash->add_method(can => \&Class::Anonymous::Instance::can);
23             $stash->add_method(isa => \&Class::Anonymous::Instance::isa);
24             sub { $stash->bless($_[0]) };
25             } || sub { bless $_[0], 'Class::Anonymous::Instance' };
26              
27             our $CURRENT;
28              
29             my $new = sub {
30             my $class = shift;
31             my @isa = $class->isa();
32             push @isa, $class;
33             my $self = instance(@isa);
34             local $CURRENT = $self;
35             $_->('BUILD')->($self, @_) for @isa;
36             return $self;
37             };
38              
39             sub instance {
40 17     17 1 28 my @isa = @_;
41 17         21 my %methods;
42              
43             my $isa = sub {
44 28     28   32 my $self = shift;
45 28 100       73 return @isa unless @_;
46 15         18 my $class = shift;
47 15 100       46 return unless ref $class;
48 6         25 my $addr = refaddr $class;
49 6         49 return first { $addr == refaddr $_ } reverse @isa;
  8         36  
50 17         62 };
51              
52             return $bless->(sub {
53 95 50   95   242 return unless my $name = shift;
54 95 100       219 return $isa if $name eq 'isa';
55 67 100       158 return $new if $name eq 'new';
56 59 100       142 $methods{$name} = shift if @_;
57 59         333 return $methods{$name};
58 17         73 });
59             };
60              
61             sub class (&) {
62 9     9 1 119 my $builder = shift;
63 9         32 my $class = instance(@_);
64 9         34 $class->(BUILD => $builder);
65 9         28 return $class;
66             }
67              
68             sub extend {
69 5     5 1 10 my ($class, $extension) = @_;
70 5         20 my @isa = $class->isa();
71 5         12 return &class($extension, @isa, $class);
72             }
73              
74 5     5 1 1898 sub via (&) { $_[0] }
75              
76             1;
77              
78             =head1 NAME
79              
80             Class::Anonymous - Truly private classes with private data for Perl5
81              
82             =head1 SYNOPSIS
83              
84             use feature 'say';
85             use Class::Anonymous;
86             use Class::Anonymous::Utils ':all';
87              
88             my $lifeform = class {
89             my ($self, $name) = @_;
90             method greeting => sub { "My name is $name" };
91             };
92              
93             my $mortal = extend $lifeform => via {
94             my ($self, $name, $age) = @_;
95             around greeting => sub {
96             my $orig = shift;
97             $orig->() . " and I'm $age years old";
98             };
99             };
100              
101             my $bob = $mortal->new('Bob', 40);
102             say $bob->greeting;
103             say 'Bob is mortal' if $bob->isa($mortal);
104             say 'Bob is a lifeform' if $bob->isa($lifeform);
105              
106             =head1 DESCRIPTION
107              
108             L implements anonymous classes and private data.
109             This private data is just the lexical variables available during the builder callback(s) that are used to build the class.
110             If L is available, then no stash entry is created at all.
111             If not, then the classes will actually be blessed into L though this is to be considered an implementation detail and subject to change.
112              
113             The instance itself is a code reference which can be thought of as a meta-object.
114             Called with a single string fetches the method of that name and returns it.
115             Called with a string and a code reference attaches a new method to the object.
116             Helper functions are provided in L which provides L and method modifiers for ease of use.
117              
118             my $class = class {
119             my ($self) = @_;
120             $self->(mymethod = sub { ... });
121             my $mymethod = $self->('mymethod');
122             }
123              
124             =head1 EXPORTED FUNCTIONS
125              
126             =head2 class
127              
128             my $class = class { my ($self) = @_; ... };
129              
130             Define a new class.
131             Takes a block (or code reference) which will be called to build and instance of that class.
132             The callback is called with the new empty instance and any arguments passed to C.
133             Note that subclasses might need more initialization arguments, so you might want to plan for that.
134              
135             =head2 extend
136              
137             my $subclass = extend $class, sub { my ($self) = @_; ... };
138              
139             Define a new subclass of an existing anonymous class.
140             Takes an existing class and a code reference which will be called after the parent class builder callback(s).
141             Note that all callbacks receive the same arguments, so you might want to plan for that.
142              
143             =head2 via
144              
145             my $subclass = extend $class, via { my ($self) = @_; ... };
146              
147             Sugar for defining a code reference as a block, simply to make L look better.
148              
149             =head1 OTHER FUNCTIONS
150              
151             =head2 instance
152              
153             Builds a raw instance of a generic anonymous object.
154             All arguments are the classes to be returned by C.
155             By default it only fully implements the C, C and C methods.
156             If the instance implements the C method then it is itself a class and you can call C on it.
157              
158             =head3 Relationship to the class function
159              
160             The L function simply creates an C when it creates a class.
161             It then attaches a C function; this function creates another instance, calls all the C methods from all the C classes on it, then returns it.
162              
163             =head1 SOURCE REPOSITORY
164              
165             L
166              
167             =head1 AUTHOR
168              
169             Joel Berger, Ejoel.a.berger@gmail.comE
170              
171             =head1 COPYRIGHT AND LICENSE
172              
173             Copyright (C) 2015 by Joel Berger
174              
175             This library is free software; you can redistribute it and/or modify
176             it under the same terms as Perl itself.