File Coverage

blib/lib/DBIx/Class/Schema/PopulateMore.pm
Criterion Covered Total %
statement 27 29 93.1
branch 6 8 75.0
condition 4 6 66.6
subroutine 5 6 83.3
pod 1 1 100.0
total 43 50 86.0


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::PopulateMore;
2              
3 3     3   1571 use warnings;
  3         4  
  3         82  
4 3     3   11 use strict;
  3         3  
  3         67  
5              
6 3     3   71 use 5.008005;
  3         10  
  3         75  
7 3     3   1285 use DBIx::Class::Schema::PopulateMore::Command;
  3         9  
  3         47  
8              
9             =head1 NAME
10              
11             DBIx::Class::Schema::PopulateMore - An enhanced populate method
12              
13             =head1 VERSION
14              
15             Version 0.19
16              
17             =cut
18              
19             our $VERSION = '0.19';
20              
21             =head1 SYNOPSIS
22              
23             The following is example usage for this component.
24              
25             package Myapp::Schema;
26             use base qw/DBIx::Class::Schema/;
27            
28             __PACKAGE__->load_components(qw/Schema::PopulateMore/);
29             __PACKAGE__->load_namespaces();
30            
31             ## All the rest of your setup
32              
33             Then assuming you have ResultSources of Gender, Person and FriendList:
34              
35             my $setup_rows = [
36            
37             {Gender => {
38             fields => 'label',
39             data => {
40             male => 'male',
41             female => 'female',
42             }}},
43            
44             {Person => {
45             fields => ['name', 'age', 'gender'],
46             data => {
47             john => ['john', 38, "!Index:Gender.male"],
48             jane => ['jane', 40, '!Index:Gender.female'],
49             }}},
50            
51             {FriendList => {
52             fields => ['person', 'friend', 'created_date'],
53             data => {
54             john_jane => [
55             '!Index:Person.john',
56             '!Index:Person.jane'
57             '!Date: March 30, 1996',
58             ],
59             }}},
60             ];
61            
62             $schema->populate_more($setup_rows);
63              
64             Please see the test cases for more detailed examples.
65              
66             =head1 DESCRIPTION
67              
68             This is a L<DBIx::Class::Schema> component that provides an enhanced version
69             of the builtin method L<DBIx::Class::Schema/populate>. What it does is make it
70             easier when you are doing a first time setup and need to insert a bunch of
71             rows, like the first time you deploy a new database, or after you update it.
72              
73             It's not as full featured as L<DBIx::Class::Fixtures> but is targeted more
74             directly at making it easier to just take a prewritten perl structure --or one
75             loaded from a configuration file-- and setup your database.
76              
77             Most of us using L<DBIx::CLass> have written a version of this at one time or
78             another. What is special to this component is the fact that unlike the normal
79             populate method you can insert to multiple result_sources in one go. While
80             doing this, we index the created rows so as to make it easy to reference them
81             in relationships. I did this because I think it's very ugly to have to type in
82             all the primary keys by hand, particularly if your PK is multi column, or is
83             using some lengthy format such as uuid. Also, we can embed expansion commands
84             in the row values to do inflation for us. For example, any value starting with
85             "!Index:" will substitute it's value for that of the relating fields in the
86             named row.
87              
88             This distribution supplies three expansion commands:
89              
90             =over 4
91              
92             =item Index
93              
94             Use for creating relationships. This is a string in the form of "Source.Label"
95             where the Source is the name of the result source that you are creating rows in
96             and Label is a key name from the key part of the data hash.
97              
98             =item Env
99              
100             Get's it's value from %ENV. Typically this will be setup in your shell or at
101             application runtime. This is a string in the form of "!Env:MY_ENV_VAR"
102              
103             =item Date
104              
105             converts it's value to a L<DateTime> object. Will use a various methods to try
106             and coerce a string, like "today", or "January 6, 1974". Makes it easier to
107             insert dates into your database without knowing or caring about the expected
108             format. For this to work correctly, you need to use the class component
109             L<DBIx::Class::InflateColumn::DateTime> and mark your column data type as
110             'datetime' or similar.
111              
112             =item Find
113              
114             Used for when you want the value of something that you expect already exists
115             in the database (but for which you didn't just populatemore for, use 'Index'
116             for that case.) Use cases for this include lookup style tables, like 'Status'
117             or 'Gender', 'State', etc. which you may already have installed. This is a
118             string in the form of '!Find:Source.[key1=val1,key2=val2,...'.
119              
120             If your find doesn't return a single result, expect an error.
121              
122             It's trivial to write more; please feel free to post me your contributions.
123              
124             =back
125              
126             Please note the when inserting rows, we are actually calling "create_or_update"
127             on each data item, so this will not be as fast as using $schema->bulk_insert.
128              
129              
130             =head1 METHODS
131              
132             This module defines the following methods.
133              
134             =head2 populate_more ($ArrayRef||@Array)
135              
136             Given an arrayref formatted as in the L</SYNOPSIS> example, populate a rows in
137             a database. Confesses on errors.
138              
139             We allow a few different inputs to make it less verbose to use under different
140             situations, as well as format nicely using your configuration format of choice.
141              
142             The $ArrayRef contains one or more elements in the following pattern;
143              
144             $schema->populate_more([
145             {Source1 => {
146             fields => [qw/ column belongs_to has_many/],
147             data => {
148             key_1 => ['value', $row, \@rows ],
149             }}},
150             {Source2 => {
151             fields => [qw/ column belongs_to has_many/],
152             data => {
153             key_1 => ['value', $row, \@rows ],
154             }}},
155             ]);
156              
157             The @Array version can be one of the following:
158              
159             ## Option One
160             $schema->populate_more(
161             {Source1 => {
162             fields => [qw/ column belongs_to has_many/],
163             data => {
164             key_1 => ['value', $row, \@rows ],
165             }}},
166             {Source2 => {
167             fields => [qw/ column belongs_to has_many/],
168             data => {
169             key_1 => ['value', $row, \@rows ],
170             }}},
171             );
172              
173             ## Option Two
174             $schema->populate_more(
175             Source1 => {
176             fields => [qw/ column belongs_to has_many/],
177             data => {
178             key_1 => ['value', $row, \@rows ],
179             }
180             },
181             Source2 => {
182             fields => [qw/ column belongs_to has_many/],
183             data => {
184             key_1 => ['value', $row, \@rows ],
185             }
186             },
187             );
188              
189             The last option is probably your choice if you are building a Perl structure
190             directly, since it's the least verbose.
191              
192             'SourceX' is the name of a DBIC source (as in $schema->resultset($Source)->...)
193             while fields is an arrayref of either columns or named relationships and data
194             is a hashref of rows that you will insert into the Source.
195              
196             See L</SYNOPSIS> for more.
197              
198             =cut
199              
200             sub populate_more {
201 5     5 1 86809 my ($self, $arg, @rest) = @_;
202              
203 5 50       23 $self->throw_exception("Argument is required.")
204             unless $arg;
205              
206 5 100 66     53 my @args = (ref $arg && ref $arg eq 'ARRAY') ? @$arg : ($arg, @rest);
207              
208 5         8 my @definitions;
209 5         18 while(@args) {
210 16         17 my $next = shift(@args);
211 16 100 66     65 if( (ref $next) && (ref $next eq 'HASH') ) {
212 14         30 push @definitions, $next;
213             } else {
214 2         4 my $value = shift(@args);
215 2         10 push @definitions, {$next => $value};
216             }
217             }
218              
219 5         11 my $command;
220 5         8 eval {
221             $command = DBIx::Class::Schema::PopulateMore::Command->new(
222             definitions=>[@definitions],
223             schema=>$self,
224             exception_cb=>sub {
225 0     0   0 $self->throw_exception(@_);
226             },
227 5         160 );
228             };
229              
230 5 50       252 if($@) {
231 0         0 $self->throw_exception("Can't create Command: $@");
232             } else {
233 5         26 $command->execute;
234             }
235             }
236              
237              
238             =head1 ARGUMENT NOTES
239              
240             The perl structure used in L</populate_more> was designed to be reasonable
241             friendly to type in most of the popular configuration formats. For example,
242             the above serialized to YAML would look like:
243              
244             - Gender:
245             fields: label
246             data:
247             female: female
248             male: male
249             - Person:
250             fields:
251             - name
252             - age
253             - gender
254             data:
255             jane:
256             - jane
257             - 40
258             - '!Index:Gender.female'
259             john:
260             - john
261             - 38
262             - !Index:Gender.male'
263             - FriendList:
264             fields:
265             - person
266             - friend
267             - created_date
268             data:
269             john_jane:
270             - '!Index:Person.john'
271             - '!Index:Person.jane'
272             - '!Date: March 30, 1996'
273              
274             Since the argument is an arrayref or an array, the same base result source can
275             appear as many times as you like. This could be useful when a second insert
276             to a given source requires completion of other inserts. The insert order
277             follows the index of the arrayref you create.
278              
279             =head1 AUTHOR
280              
281             John Napiorkowski, C<< <jjnapiork@cpan.org> >>
282              
283             =head1 BUGS
284              
285             Please report any bugs or feature requests to:
286              
287             C<bug-DBIx-Class-Schema-PopulateMore at rt.cpan.org>
288              
289             or through the web interface at:
290              
291             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=DBIx-Class-Schema-PopulateMore>
292              
293             I will be notified, and then you'll automatically be notified of progress on
294             your bug as I make changes.
295              
296             =head1 SUPPORT
297              
298             You can find documentation for this module with the perldoc command.
299              
300             perldoc DBIx::Class::Schema::PopulateMore
301              
302             You can also look for information at:
303              
304             =over 4
305              
306             =item * RT: CPAN's request tracker
307              
308             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=DBIx-Class-Schema-PopulateMore>
309              
310             =item * AnnoCPAN: Annotated CPAN documentation
311              
312             L<http://annocpan.org/dist/DBIx-Class-Schema-PopulateMore>
313              
314             =item * CPAN Ratings
315              
316             L<http://cpanratings.perl.org/d/DBIx-Class-Schema-PopulateMore>
317              
318             =item * Search CPAN
319              
320             L<http://search.cpan.org/dist/DBIx-Class-Schema-PopulateMore>
321              
322             =back
323              
324             =head1 ACKNOWLEDGEMENTS
325              
326             Thanks to the entire L<DBIx::Class> team for providing such a useful and
327             extensible ORM. Also thanks to the L<Moose> developers for making it fun and
328             easy to write beautiful Perl.
329              
330             =head1 COPYRIGHT & LICENSE
331              
332             Copyright 2011, John Napiorkowski
333              
334             This program is free software; you can redistribute it and/or modify it under
335             the same terms as Perl itself.
336              
337             =cut
338              
339             1;