File Coverage

blib/lib/CatalystX/CRUD/Object.pm
Criterion Covered Total %
statement 36 46 78.2
branch 8 16 50.0
condition 2 6 33.3
subroutine 9 15 60.0
pod 8 8 100.0
total 63 91 69.2


line stmt bran cond sub pod time code
1             package CatalystX::CRUD::Object;
2 5     5   1246 use Moose;
  5         9  
  5         32  
3             with 'MooseX::Emulate::Class::Accessor::Fast';
4             with 'Catalyst::ClassData';
5 5     5   33198 use base 'CatalystX::CRUD';
  5         12  
  5         536  
6              
7 5     5   61 use Carp;
  5         10  
  5         341  
8 5     5   41 use Data::Dump qw( dump );
  5         64  
  5         245  
9 5     5   41 use MRO::Compat;
  5         9  
  5         118  
10 5     5   23 use mro 'c3';
  5         10  
  5         31  
11              
12             __PACKAGE__->mk_ro_accessors(qw( delegate ));
13             __PACKAGE__->mk_classdata('delegate_class');
14              
15             our $VERSION = '0.58';
16              
17             =head1 NAME
18              
19             CatalystX::CRUD::Object - an instance returned from a CatalystX::CRUD::Model
20              
21             =head1 SYNOPSIS
22              
23             package My::Object;
24             use base qw( CatalystX::CRUD::Object );
25            
26             sub create { shift->delegate->save }
27             sub read { shift->delegate->load }
28             sub update { shift->delegate->save }
29             sub delete { shift->delegate->remove }
30            
31             1;
32              
33             =head1 DESCRIPTION
34              
35             A CatalystX::CRUD::Model returns instances of CatalystX::CRUD::Object.
36              
37             The assumption is that the Object knows how to manipulate the data it represents,
38             typically by holding an instance of an ORM or other data model in the
39             C<delegate> accessor, and calling methods on that instance.
40              
41             So, for example, a CatalystX::CRUD::Object::RDBO has a Rose::DB::Object instance,
42             and calls its RDBO object's methods.
43              
44             The idea is to provide a common CRUD API for various backend storage systems.
45              
46             =head1 METHODS
47              
48             The following methods are provided.
49              
50             =cut
51              
52             =head2 new
53              
54             Generic constructor. I<args> may be a hash or hashref.
55              
56             =cut
57              
58             sub new {
59 64     64 1 754 my $class = shift;
60 64 50       282 my $arg = ref( $_[0] ) eq 'HASH' ? $_[0] : {@_};
61 64         170 return $class->next::method($arg);
62             }
63              
64             =head2 delegate
65              
66             The delegate() accessor is a holder for the object instance that the CXCO instance
67             has. A CXCO object "hasa" instance of another class in its delegate() slot. The
68             delegate is the thing that does the actual work; the CXCO object just provides a container
69             for the delegate to inhabit.
70              
71             Think of delegate as a noun, not a verb, as in "The United Nations delegate often
72             slept here."
73              
74              
75             =head1 REQUIRED METHODS
76              
77             A CXCO subclass needs to implement at least the following methods:
78              
79             =over
80              
81             =item create
82              
83             Write a new object to store.
84              
85             =item read
86              
87             Load a new object from store.
88              
89             =item update
90              
91             Write an existing object to store.
92              
93             =item delete
94              
95             Remove an existing object from store.
96              
97             =back
98              
99             =cut
100              
101 0     0 1 0 sub create { shift->throw_error("must implement create") }
102 0     0 1 0 sub read { shift->throw_error("must implement read") }
103 0     0 1 0 sub update { shift->throw_error("must implement update") }
104 0     0 1 0 sub delete { shift->throw_error("must implement delete") }
105              
106             =head2 is_new
107              
108             Return results should be boolean indicating whether the object
109             already exists or not. Expectation is code like:
110              
111             if ($object->is_new) {
112             $object->create;
113             }
114             else {
115             $object->update;
116             }
117              
118             =cut
119              
120 0     0 1 0 sub is_new { shift->throw_error("must implement is_new") }
121              
122             =head2 serialize
123              
124             Stringify the object. This class overloads the string operators
125             to call this method.
126              
127             Your delegate class should implement a serialize() method
128             or stringify to something useful.
129              
130             =cut
131              
132             sub serialize {
133 0     0 1 0 my $self = shift;
134 0 0       0 return "" unless defined $self->delegate;
135 0 0       0 return $self->delegate->can('serialize')
136             ? $self->delegate->serialize
137             : $self->delegate . "";
138             }
139              
140             =head2 AUTOLOAD
141              
142             Some black magic hackery to make Object classes act like
143             they are overloaded delegate()s.
144              
145             =cut
146              
147             sub AUTOLOAD {
148 108     108   3871 my $obj = shift;
149 108   33     283 my $obj_class = ref($obj) || $obj;
150 108   33     253 my $delegate_class = ref( $obj->delegate ) || $obj->delegate;
151 108         613 my $method = our $AUTOLOAD;
152 108         545 $method =~ s/.*://;
153 108 50       256 return if $method eq 'DESTROY';
154 108 50       207 if ( $obj->delegate->can($method) ) {
155 108         721 return $obj->delegate->$method(@_);
156             }
157              
158 0         0 $obj->throw_error( "method '$method' not implemented in class "
159             . "'$obj_class' or '$delegate_class'" );
160              
161             }
162              
163             # this overrides the basic can()
164             # to always call secondary can() on its delegate.
165             # we have to UNIVERSAL::can because we are overriding can()
166             # and would otherwise have a recursive nightmare.
167              
168             =head2 can( I<method> )
169              
170             Overrides basic can() method to call can() first on the delegate
171             and secondly (fallback) on the Object class itself.
172              
173             =cut
174              
175             sub can {
176 243     243 1 203074 my ( $obj, $method, @arg ) = @_;
177 243 50       539 if ( ref($obj) ) {
178              
179             # object method tries object_class first,
180             # then the delegate().
181 243         958 my $subref = UNIVERSAL::can( ref($obj), $method );
182 243 100       1198 return $subref if $subref;
183 63 100       231 if ( defined $obj->delegate ) {
184 62         411 return $obj->delegate->can( $method, @arg );
185             }
186 1         11 return undef;
187             }
188             else {
189              
190             # class method
191 0           return UNIVERSAL::can( $obj, $method );
192             }
193             }
194              
195             1;
196             __END__
197              
198             =head1 AUTHOR
199              
200             Peter Karman, C<< <perl at peknet.com> >>
201              
202             =head1 BUGS
203              
204             Please report any bugs or feature requests to
205             C<bug-catalystx-crud at rt.cpan.org>, or through the web interface at
206             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CatalystX-CRUD>.
207             I will be notified, and then you'll automatically be notified of progress on
208             your bug as I make changes.
209              
210             =head1 SUPPORT
211              
212             You can find documentation for this module with the perldoc command.
213              
214             perldoc CatalystX::CRUD
215              
216             You can also look for information at:
217              
218             =over 4
219              
220             =item * Mailing List
221              
222             L<https://groups.google.com/forum/#!forum/catalystxcrud>
223              
224             =item * AnnoCPAN: Annotated CPAN documentation
225              
226             L<http://annocpan.org/dist/CatalystX-CRUD>
227              
228             =item * CPAN Ratings
229              
230             L<http://cpanratings.perl.org/d/CatalystX-CRUD>
231              
232             =item * RT: CPAN's request tracker
233              
234             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=CatalystX-CRUD>
235              
236             =item * Search CPAN
237              
238             L<http://search.cpan.org/dist/CatalystX-CRUD>
239              
240             =back
241              
242             =head1 ACKNOWLEDGEMENTS
243              
244             =head1 COPYRIGHT & LICENSE
245              
246             Copyright 2007 Peter Karman, all rights reserved.
247              
248             This program is free software; you can redistribute it and/or modify it
249             under the same terms as Perl itself.
250              
251             =cut