File Coverage

blib/lib/DBIx/Class/Schema/ResultSetNames.pm
Criterion Covered Total %
statement 57 58 98.2
branch 8 10 80.0
condition 4 6 66.6
subroutine 13 13 100.0
pod 1 1 100.0
total 83 88 94.3


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::ResultSetNames 1.03;
2 2     2   406974 use Modern::Perl;
  2         19190  
  2         14  
3 2     2   369 use base qw(DBIx::Class::Schema);
  2         4  
  2         218  
4 2     2   16 use Carp;
  2         4  
  2         102  
5 2     2   1070 use Lingua::EN::Inflect::Phrase;
  2         137464  
  2         305  
6              
7             __PACKAGE__->mk_group_accessors( inherited => 'resultset_name_methods' );
8              
9             __PACKAGE__->resultset_name_methods( {} );
10              
11             sub register_source {
12 5     5 1 221174 my ( $class, $source_name, @rest ) = @_;
13 5         30 my $source = $class->next::method( $source_name, @rest );
14 5         2048 $class->_register_resultset_name_methods($source_name);
15 4         19 return $source;
16             }
17              
18             sub _ensure_resultset_name_method {
19 8     8   19 my ( $class, $name, $sub ) = @_;
20 8 50       113 return if $class->can($name);
21             {
22 2     2   18 no strict 'refs';
  2         5  
  2         1439  
  8         17  
23 8         15 *{"${class}::${name}"} = $sub;
  8         33  
24             }
25             $class->resultset_name_methods(
26 8         14 { %{ $class->resultset_name_methods }, $name => 1 }, );
  8         199  
27 8         402 return;
28             }
29              
30             sub _register_resultset_name_methods {
31 5     5   16 my ( $class, $source_name ) = @_;
32 5         11 my $rsname_overrides = {};
33 5 100       44 if ( $class->can('override_rsnames') ) {
34 4         16 $rsname_overrides = $class->override_rsnames;
35             }
36             my $method_name = $rsname_overrides->{$source_name}->{singular}
37 5   66     67 || $class->_source_name_to_method_name($source_name);
38             my $plural_name = $rsname_overrides->{$source_name}->{plural}
39 5   66     69 || $class->_source_name_to_plural_name($source_name);
40 5 100       22 if ( $method_name eq $plural_name ) {
41 1         375 croak << "END_MESSAGE";
42             The ResultSet $source_name is the same word in both singular and
43             plural forms. Use an override to choose different words for one
44             or the other, or both. Consult the documentation for assistance
45             in doing this.
46              
47             END_MESSAGE
48             }
49             $class->_ensure_resultset_name_method(
50             $method_name => sub {
51 11     11   78792 my ( $self, @args ) = @_;
52 11 100       64 die "Can't call ${method_name} without arguments" unless @args;
53 9         46 $self->resultset($source_name)->find(@args);
54             }
55 4         39 );
56             $class->_ensure_resultset_name_method(
57             $plural_name => sub {
58 8     8   192069 my ( $self, @args ) = @_;
59 8         41 my $rs = $self->resultset($source_name);
60 8 50       3591 return $rs unless @args;
61 0         0 return $rs->search_rs(@args);
62             }
63 4         28 );
64 4         15 return;
65             }
66              
67             sub _source_name_to_method_name {
68 4     4   14 my ( $class, $source_name ) = @_;
69 4         18 my $phrase = $class->_source_name_to_phrase($source_name);
70 4         19 my $singularised = Lingua::EN::Inflect::Phrase::to_S($phrase);
71 4         325709 return join '_', split q{ }, $singularised;
72             }
73              
74             sub _source_name_to_phrase {
75 8     8   22 my ( $class, $source_name ) = @_;
76             join q{ }, map {
77 8         43 join( q{ }, map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/ )
  8         46  
  8         47  
  16         42  
78             } split /::/, $source_name;
79             }
80              
81             sub _source_name_to_plural_name {
82 4     4   14 my ( $class, $source_name ) = @_;
83 4         16 my $phrase = $class->_source_name_to_phrase($source_name);
84 4         25 my $pluralised = Lingua::EN::Inflect::Phrase::to_PL($phrase);
85 4         6748 return join '_', split q{ }, $pluralised;
86             }
87              
88             1;
89              
90             =pod
91              
92             =encoding UTF-8
93              
94             =head1 NAME
95              
96             DBIx::Class::Schema::ResultSetNames - Create resultset accessors from schema result class names
97              
98             =head1 VERSION
99              
100             version 1.03
101              
102             =head1 SYNOPSIS
103              
104             # in MyApp::Schema
105             __PACKAGE__->load_components('Schema::ResultSetNames');
106              
107             sub override_rsnames {
108             return {
109             'Widget' => { # Your schema's result class name
110             singular => 'block', # singular word you wish to use
111             plural => 'clocks' # plural word
112             }
113             };
114             }
115              
116             =head1 DESCRIPTION
117              
118             DBIx::Class::Schema::ResultSetNames adds both singular and plural method accessors for all resultsets.
119              
120             So, instead of this:
121              
122             my $schema = MyApp::Schema->connect(...);
123             my $result = $schema->resultset('Author')->search({...});
124              
125             you may choose to this:
126              
127             my $schema = MyApp::Schema->connect(...);
128             my $result = $schema->authors->search({...});
129              
130             And instead of this:
131              
132             my $schema = MyApp::Schema->connect(...);
133             my $result = $schema->resultset('Author')->find($id);
134              
135             you may choose to this:
136              
137             my $schema = MyApp::Schema->connect(...);
138             my $result = $schema->author($id)
139              
140             =head2 What is returned?
141              
142             If you call the plural form of the resultset (e.g. `authors`), you will get a L,
143             which may be empty, if no rows satisfy whatever criteria you've chained behind it.
144              
145             For the singular form (`author`), you'll get a L, or `undef`, if the selected row does not exist.
146              
147             Don't worry if your ResultSet schema class name is already plural (e.g. 'Authors'). This module will
148             Do The Right Thing, according to the behavior of L
149              
150             =head2 Optional overriding of terms
151              
152             If your schema set name is a word that is the same term in both singular and plural forms (in English), then
153             the module will C. You can create an otherwise-optional subroutine named C to give
154             the terms you wish to use to the module. You do not need to define both, as was done in the Synopsis above;
155             if one is missing, the default behavior will be used. So in the case of, for instance, a
156             L named "Moose" (no, not *that* L!), the module will C; you can do
157             something like this to overcome the problem:
158              
159             package MyApp::Schema;
160             __PACKAGE__->load_components('Schema::ResultSetNames');
161              
162             sub override_rsnames {
163             return {
164             'Moose' => {
165             plural => 'm00ses' # singular will be 'moose'
166             },
167             # other RSes that you want to override ...
168             };
169             }
170              
171             =head2 A note about `find`.
172              
173             It is perfectly permissible to use find (or the singular accessor, in this module) to locate something
174             by including a hashref of search terms:
175              
176             my $result = $schema->resultSet('Author')->find({ name => 'John Smith }); # Old way
177             my $result = $schema->author({ name => 'John Smith' }); # New way
178              
179             However, be aware that `find()` and this module will both complain if your request will return multiple
180             rows, and throw a warning. `find()` expects to return one row or undef, which is why it is best used on unique keys.
181              
182             =head2 "Let not your heart be troubled..." about relationships.
183              
184             This doesn't tamper with relationship accessors in any way. If you have a table of Authors and a table of Books,
185             the usual sort of `book($id)->author()`, and `author($id)->books()` relationship tools will still work just fine.
186              
187             =head1 SEE ALSO
188              
189             =over 4
190              
191             =item * L
192              
193             =item * L
194              
195             =back
196              
197             =head1 CREDIT WHERE CREDIT IS DUE
198              
199             Practically all of this code is the work of L. It was
200             created alongside a Dancer2 plugin that he has helped greatly with. I just tidied things up and wrote
201             documentation.
202              
203             =head1 SOURCE
204              
205             L
206              
207             =head1 HOMEPAGE
208              
209             L
210              
211             =head1 AUTHOR
212              
213             D Ruth Holloway
214              
215             =head1 COPYRIGHT AND LICENSE
216              
217             This software is copyright (c) 2021 by D Ruth Holloway.
218              
219             This is free software; you can redistribute it and/or modify it under
220             the same terms as the Perl 5 programming language system itself.
221              
222             =cut
223              
224             __END__