File Coverage

blib/lib/Catalyst/TraitFor/Model/DBIC/Schema/ResultRoles.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Catalyst::TraitFor::Model::DBIC::Schema::ResultRoles;
2              
3             #
4             # copyright (c) 2011 Lukas Thiemeier
5             #
6             # this module is free software
7             #
8             # see "LICENCE AND COPYRIGHT" below for more information
9             #
10              
11 1     1   28867 use namespace::autoclean;
  1         26099  
  1         7  
12 1     1   520 use Moose::Role;
  0            
  0            
13             use Moose::Util qw/apply_all_roles/;
14             use Module::Find qw/findallmod/;
15              
16             our $VERSION = 0.0110;
17              
18             requires qw/BUILD schema schema_class/;
19              
20             # where to look for Result-Roles
21             has role_location => (
22             is => 'ro',
23             isa => 'Str',
24             required => 1,
25             lazy => 1,
26             init_arg => 'rr_role_location',
27             builder => "_build_role_location",
28             # transforming "/" to "::" for Module::Find
29             trigger => sub {
30             my ($self, $value) =@_;
31             $value =~ s/\//::/g;
32             $self->{role_location} = $value;
33             },
34             );
35              
36             has quiet => (
37             is => 'ro',
38             isa => 'Bool',
39             required => 1,
40             init_arg => 'rr_quiet',
41             default => 0,
42             );
43              
44             has debug => (
45             is => 'ro',
46             isa => 'Bool',
47             required => 1,
48             init_arg => 'rr_debug',
49             default => 0,
50             );
51              
52             has die => (
53             is => 'ro',
54             isa => 'Bool',
55             required => 1,
56             init_arg => 'rr_die',
57             default => 0,
58             );
59              
60             # apply roles at BUILD time
61             after BUILD => sub {
62              
63             my $self = shift;
64              
65             # stop here if role_location is not set
66             unless($self->role_location){
67             $self->_reaction_on(error => "unable to read \"role_location\", NOT PROCEEDING");
68             return;
69             }
70              
71             my @messages; # stores status messages
72             my @errors; # stores error messages
73              
74             #loop over all result sources
75             foreach my $sourcename ($self->schema->sources){
76              
77             # store result_class
78             my $source = $self->schema->source_registrations->{$sourcename}->result_class;
79              
80             # check if the current resultclass is a Moose-class
81             if( $source->can("meta")){
82             push @messages, "searching roles for $sourcename";
83              
84             # find roles
85             my $roles = $self->_find_roles_for_source($sourcename);
86             if ($roles){
87              
88             # try to apply the roles
89             eval {apply_all_roles($source->meta, @$roles)};
90              
91             if($@){
92             # die if roles could not be applied
93             die $@;
94             }
95             else{
96             # prepare status messages
97             push @messages, "Roles applied to \"$sourcename\": " . join ", ", @$roles;
98             }
99             }
100             else{
101             # prepare status messages
102             push @messages, "Could not find any roles for \"$sourcename\"";
103             }
104             }
105             else{
106             # prepare error messages
107             push @errors, "Resultclass \"$sourcename\" does not provide a meta-class";
108             }
109             }
110              
111             # print status and error messages
112             my $error_msg = join "\n", @errors if @errors;
113             my $status_msg = join "\n", @messages if @messages;
114             $self->_reaction_on(
115             status => $status_msg,
116             error => $error_msg,
117             );
118              
119              
120             };
121              
122             # returns possible Roles for a given source
123             sub _find_roles_for_source{
124             my ($self,$source) = @_;
125             my @roles = findallmod $self->role_location . "::$source";
126             return \@roles if @roles;
127             return undef;
128             }
129              
130             # builds default role_location
131             sub _build_role_location{
132             my ($self) = @_;
133             return $self->schema_class . "::ResultRole";
134             }
135              
136             # expects error and status message as named parameters
137             # and prints, warns or dies, depending on the configuration
138             sub _reaction_on{
139             #my ($self) = @_;
140             my $self = shift;
141             my %args = (@_);
142             my $status = "ResultRole [status] :\n". $args{status} if $args{status};
143             my $error = "ResultRole [error] :\n". $args{error} if $args{error};
144             if($self->die && $error){
145             die $error;
146             }
147             elsif($self->debug){
148             warn $error if $error;
149             warn $status if $status;
150             }
151             if(not $self->quiet || $self->debug){
152             print "$error\n" if $error and not $self->debug;
153             print "$status\n" if $status;
154             }
155             return 0;
156             }
157              
158             1;
159              
160             __END__
161              
162             =head1 NAME
163              
164             Catalyst::TraitFor::Model::DBIC::Schema::ResultRoles - Automatically applying Moose Roles to Result-Classes at BUILD time
165              
166             =head1 VERSION
167              
168             Version 0.0110
169              
170             =head1 SYNOPSIS
171              
172             In your Catalyst Model (lib/YourApp/Model/YourModel.pm):
173              
174             __PACKAGE__->config(
175             ...
176              
177             traits => "ResultRoles",
178            
179             ...
180             );
181              
182             OR in your Application main file (lib/YourApp.pm):
183              
184             __PACKAGE__->config(
185             ...
186              
187             "Model::YourModel" => (
188             ...
189              
190             traits => "ResultRoles",
191              
192             ...
193             ),
194             );
195              
196             and then, in an appropriate location (lib/YourApp/Schema/ResultRole/YourResult/YourRole.pm):
197              
198             package YourApp::Schema::ResultRole::YourResult::YourRole;
199              
200             use namespace::autoclean;
201             use Moose::Role;
202              
203             YourApp::Schema::Result::YourResult->many_to_many(...);
204             YourApp::Schema::Result::YourResult->add_column(...);
205              
206             sub your_result_sub{
207             # do something result specific
208             }
209             1;
210              
211             =head1 DESCRIPTION
212              
213             This module is a trait for DBIC based Catalyst models.
214             It hooks to the models BUILD process and appies
215             a set of Moose Roles to each loaded resultclass.
216             This makes it possible to customize the resultclasses
217             without changing the automaticaly created DBIx::Class::Core files.
218             Resultclasses can be customized by creating one or more roles per resultclass.
219             Customized code and automatically created code are clearly seperated.
220              
221             Because applying roles depends on the presence of a meta-class,
222             this trait only works with "moosified" resultclasses. "Non-moosified"
223             resultclasses are ignored, which makes it possible to use a mixed set
224             of moosified and non-moosified resultclasses.
225              
226             =head1 CONFIGURATION
227              
228             =head2 enabling the traits
229              
230             See L</SYNOPSIS> above or L<Catalyst::Model::DBIC::Schema/traits>
231              
232             =head2 creating roles for result classes
233              
234             =head3 Example:
235              
236             Assumed the application name is "MyApp", and the schema class is
237             "MyApp::Schema". If you want to create a role for "MyApp::Schema::Book",
238              
239             create lib/MyApp/Schema/ResultRole/Book.pm with the following content:
240              
241             package MyApp::Schema::ResultRole::Book::Author;
242              
243             use namespace::autoclean;
244             use Moose::Role;
245              
246             1;
247              
248             Within this package, MyApp::Schema::Book can be customized with all
249             features provided by L<Moose::Role>.
250             Result-class methods, like "many_to_many" and "has_many" have to be called with the
251             full result-class name.
252              
253             Assumed there is another result-class named "Author" and a corresponding BookAuthor
254             relation, a many_to_many relation could be defined for MyApp::Schema::Result::Book by
255             editing the role and adding:
256              
257             requires qw/book_authors/;
258             MyApp::Schema::Result::Book->many_to_many(authors => 'book_authors', 'author');
259              
260             to MyApp::Schema::ResultRole::Book::Author, after "use Moose::Role", but before "1;"
261              
262             =head3 How does it work:
263              
264             Without further configuration, the trait will guess the role_location attribute
265             by calling $self->schema_class and appending "::ResultRole".
266              
267             Example: Assumed the application name is "MyApp", and the schema class is
268             "MyApp::Schema": The result_location will be set to "MyApp::Schema::ResultRole"
269              
270             Catalyst::TraitFor::Model::DBIC::Schema::Result uses L<Module::Find/find_all_modules> to
271             find possible roles for each defined result source. The roles namespace is expected to be:
272              
273             $self->role_location . "::". $souce_name
274              
275             Example: Assumed the application name is "MyApp", the schema class is
276             "MyApp::Schema" and the current source name is "Book": All packages in
277             "MyApp::Schema::ResultRole::Book" are expected to be roles for
278             MyApp::Schema::Result::Book;
279              
280             Possible roles are applied to the schema class with L<Moose::Util/apply_all_roles>.
281              
282             =head2 setting attributes
283              
284             All attributes can be configured by setting their "config args"
285             within the applications configuration, either in the the applications
286             main file, or in the applications schema class.
287              
288             Example: Assumed the application name is "MyApp", and the model class is
289             "MyApp::Model::DB": To enable the "debug" flag, either add
290              
291             __PACKAGE__->config(
292             rr_debug => 1,
293             );
294              
295             to lib/MyApp/Model/DB.pm, or add
296              
297             __PACKAGE__->config(
298             'Model::DB' =>{
299             rr_debug => 1,
300             },
301             );
302              
303             to lib/MyApp.pm.
304              
305              
306             =head1 ATTRIBUTES
307              
308             The following attributes can be customized:
309              
310             =over 2
311              
312             =item * role_location
313              
314             A String specifying where the trait should look for ResultRoles.
315             Shoud either be something like "YourApp::Schema::ResultRoles"
316             or like "YourApp/Schema/ResultRoles"
317              
318             default: $SCHEMA_CLASS::ResultRoles, where $SCHEMA_CLASS is your
319             applications schema class.
320              
321             config arg: rr_role_location
322              
323             =item * die
324              
325             A Boolean. If set to 1, the trait will die when it encounters
326             non-moose result classes.
327              
328             When set to 0, the trait will only die
329             on errors concerning user-generated ResultRoles.
330             Non-moose result classes are ignored.
331              
332             default: 0
333              
334             config arg: rr_die
335              
336             =item * debug
337              
338             A Boolean. If set to 1, the trait will print status and
339             error messages to STERR (unless it has died before)
340              
341             default: 0
342              
343             config arg: rr_debug
344              
345             =item * quiet
346              
347             A Boolean. If set to 0, the trait will print status and
348             error messages to STDOUT (unless it has died or reported to STDERR before)
349              
350             default: 0
351              
352             config arg: rr_quiet
353              
354             =back
355              
356             =head1 BUGS
357              
358             Please report any bugs or feature requests to C<bug-catalyst-traitfor-model-dbic-schema-resultroles at rt.cpan.org>, or through
359             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-TraitFor-Model-DBIC-Schema-ResultRoles>. I will be notified, and then you'll
360             automatically be notified of progress on your bug as I make changes.
361              
362             =head1 TODO
363              
364             =over 2
365              
366             =item * applying roles to ResultSets
367              
368             =item * manually loading roles from other locations than $self->role_location
369              
370             =item * moosify result classes on demand
371              
372             =back
373              
374             =head1 AUTHOR
375              
376             Lukas Thiemeier, C<< <lukast at cpan.org> >>
377              
378             =head1 SUPPORT
379              
380             You can find documentation for this module with the perldoc command.
381              
382             perldoc Catalyst::TraitFor::Model::DBIC::Schema::ResultRoles
383              
384             A public subversion repository is available at:
385             http://svn.thiemeier.net/public/ResultRole
386              
387             WebSVN is available at L<http://svn.thiemeier.net/>
388              
389             =head2 SEE ALSO
390              
391             =over 2
392              
393             =item * L<Catalyst::Model::DBIC::Schema/traits>
394              
395             =item * L<DBIx::Class::Schema>
396              
397             =item * L<Module::Find>
398              
399             =item * L<Moose::Role>
400              
401             =item * L<Moose::Util>
402              
403             =item * L<MooseX::NonMoose>
404              
405             =back
406              
407             =head1 ACKNOWLEDGEMENTS
408              
409             =over 2
410              
411             =item * Larry Marso - thanks for the suggestion
412              
413             =back
414              
415              
416             =head1 LICENSE AND COPYRIGHT
417              
418             Copyright 2011 Lukas Thiemeier.
419              
420             This program is free software; you can redistribute it and/or modify it
421             under the terms of either: the GNU General Public License as published
422             by the Free Software Foundation; or the Artistic License.
423              
424             See L<http://dev.perl.org/licenses/> for more information.
425