line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# vim: set sw=2 ft=perl: |
2
|
|
|
|
|
|
|
package DBIx::Class::Sims; |
3
|
|
|
|
|
|
|
|
4
|
26
|
|
|
26
|
|
2501144
|
use 5.010_001; |
|
26
|
|
|
|
|
164
|
|
5
|
|
|
|
|
|
|
|
6
|
26
|
|
|
26
|
|
368
|
use strictures 2; |
|
26
|
|
|
|
|
1364
|
|
|
26
|
|
|
|
|
901
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.300900'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
{ |
11
|
|
|
|
|
|
|
# Do **NOT** import a clone() function into the DBIx::Class::Schema namespace |
12
|
|
|
|
|
|
|
# because that will override DBIC's clone() method and break all the things. |
13
|
|
|
|
|
|
|
package MyCloner; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub clone { |
16
|
0
|
|
|
0
|
|
|
my ($data) = @_; |
17
|
|
|
|
|
|
|
|
18
|
0
|
0
|
|
|
|
|
if (ref($data) eq 'HASH') { |
|
|
0
|
|
|
|
|
|
19
|
|
|
|
|
|
|
return { |
20
|
0
|
|
|
|
|
|
map { $_ => clone($data->{$_}) } |
|
0
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
keys %$data |
22
|
|
|
|
|
|
|
}; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
elsif (ref($data) eq 'ARRAY') { |
25
|
|
|
|
|
|
|
return [ |
26
|
0
|
|
|
|
|
|
map { clone($_) } |
|
0
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
@$data |
28
|
|
|
|
|
|
|
]; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
0
|
|
|
|
|
|
return $data; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
26
|
|
|
26
|
|
9782
|
use DDP; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
use Data::Walk qw( walk ); |
38
|
|
|
|
|
|
|
use DateTime; |
39
|
|
|
|
|
|
|
use DBIx::Class::TopoSort (); |
40
|
|
|
|
|
|
|
use Hash::Merge qw( merge ); |
41
|
|
|
|
|
|
|
use List::Util qw( first ); |
42
|
|
|
|
|
|
|
use List::MoreUtils qw( natatime ); |
43
|
|
|
|
|
|
|
use Scalar::Util qw( blessed reftype ); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
{ |
46
|
|
|
|
|
|
|
# The aliases in this block are done at BEGIN time so that the ::Types class |
47
|
|
|
|
|
|
|
# can use them when it is loaded through `use`. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my @sim_names; |
50
|
|
|
|
|
|
|
my %sim_types; |
51
|
|
|
|
|
|
|
my @sim_matchers; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub set_sim_type { |
54
|
|
|
|
|
|
|
shift; |
55
|
|
|
|
|
|
|
my $types = shift // ''; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
if (ref($types) eq 'HASH') { |
58
|
|
|
|
|
|
|
while ( my ($name, $meth) = each(%$types) ) { |
59
|
|
|
|
|
|
|
next unless ref($meth) eq 'CODE'; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
$sim_types{$name} = $meth; |
62
|
|
|
|
|
|
|
push @sim_names, $name; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
elsif (ref($types) eq 'ARRAY') { |
66
|
|
|
|
|
|
|
foreach my $item (@$types) { |
67
|
|
|
|
|
|
|
next unless ref($item->[2]) eq 'CODE'; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
push @sim_names, $item->[0]; |
70
|
|
|
|
|
|
|
push @sim_matchers, [ qr/^$item->[1]$/, $item->[2] ]; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
return; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
BEGIN { *set_sim_types = \&set_sim_type; } |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub __find_sim_type { |
79
|
|
|
|
|
|
|
my ($str) = @_; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
unless (exists $sim_types{$str}) { |
82
|
|
|
|
|
|
|
my $item = first { $str =~ $_->[0] } @sim_matchers; |
83
|
|
|
|
|
|
|
if ($item) { |
84
|
|
|
|
|
|
|
$sim_types{$str} = $item->[1]; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
return $sim_types{$str}; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub sim_type { |
92
|
|
|
|
|
|
|
shift; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# If no specific type requested, then return the complete list of all |
95
|
|
|
|
|
|
|
# registered types. |
96
|
|
|
|
|
|
|
return sort @sim_names if @_ == 0; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
return __find_sim_type($_[0]) if @_ == 1; |
99
|
|
|
|
|
|
|
return map { __find_sim_type($_) } @_; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
BEGIN { *sim_types = \&sim_type; } |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
use DBIx::Class::Sims::Types; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
use DBIx::Class::Sims::Runner; |
106
|
|
|
|
|
|
|
use DBIx::Class::Sims::Util; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub add_sims { |
109
|
|
|
|
|
|
|
my $class = shift; |
110
|
|
|
|
|
|
|
my ($schema, $source, @remainder) = @_; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $rsrc = $schema->source($source); |
113
|
|
|
|
|
|
|
my $it = natatime(2, @remainder); |
114
|
|
|
|
|
|
|
while (my ($column, $sim_info) = $it->()) { |
115
|
|
|
|
|
|
|
my $col_info = $schema->source($source)->column_info($column) // next; |
116
|
|
|
|
|
|
|
$col_info->{sim} = merge( |
117
|
|
|
|
|
|
|
$col_info->{sim} // {}, |
118
|
|
|
|
|
|
|
$sim_info // {}, |
119
|
|
|
|
|
|
|
); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
return; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
*add_sim = \&add_sims; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub load_sims { |
127
|
|
|
|
|
|
|
my $self = shift; |
128
|
|
|
|
|
|
|
my $schema; |
129
|
|
|
|
|
|
|
if (ref($self) && $self->isa('DBIx::Class::Schema')) { |
130
|
|
|
|
|
|
|
$schema = $self; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
else { |
133
|
|
|
|
|
|
|
$schema = shift(@_); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
my ($spec_proto, $opts_proto) = @_; |
136
|
|
|
|
|
|
|
$spec_proto = MyCloner::clone($spec_proto // {}); |
137
|
|
|
|
|
|
|
$opts_proto = MyCloner::clone($opts_proto // {}); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my $spec = massage_input($schema, normalize_input($spec_proto)); |
140
|
|
|
|
|
|
|
my $opts = normalize_input($opts_proto); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# 1. Ensure the belongs_to relationships are in $reqs |
143
|
|
|
|
|
|
|
# 2. Set the rel_info as the leaf in $reqs |
144
|
|
|
|
|
|
|
my $reqs = normalize_input($opts->{constraints} // {}); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# 2: Create the rows in toposorted order |
147
|
|
|
|
|
|
|
my $hooks = $opts->{hooks} // {}; |
148
|
|
|
|
|
|
|
$hooks->{preprocess} //= sub {}; |
149
|
|
|
|
|
|
|
$hooks->{postprocess} //= sub {}; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Create a lookup of the items passed in so we can return them back. |
152
|
|
|
|
|
|
|
my $initial_spec = {}; |
153
|
|
|
|
|
|
|
foreach my $name (keys %$spec) { |
154
|
|
|
|
|
|
|
my $normalized = DBIx::Class::Sims::Util->normalize_aoh($spec->{$name}); |
155
|
|
|
|
|
|
|
unless ($normalized) { |
156
|
|
|
|
|
|
|
warn "Skipping $name - I don't know what to do!\n"; |
157
|
|
|
|
|
|
|
delete $spec->{$name}; |
158
|
|
|
|
|
|
|
next; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
$spec->{$name} = $normalized; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
foreach my $item (@{$spec->{$name}}) { |
163
|
|
|
|
|
|
|
$initial_spec->{$name}{$item} = 1; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my ($rows, $additional) = ({}, {}); |
168
|
|
|
|
|
|
|
if (keys %{$spec}) { |
169
|
|
|
|
|
|
|
# Yes, this invokes srand() twice, once in implicitly in rand() and once |
170
|
|
|
|
|
|
|
# again right after. But, that's okay. We don't care what the seed is and |
171
|
|
|
|
|
|
|
# this allows DBIC to be called multiple times in the same process in the |
172
|
|
|
|
|
|
|
# same second without problems. |
173
|
|
|
|
|
|
|
$additional->{seed} = $opts->{seed} //= rand(time & $$); |
174
|
|
|
|
|
|
|
srand($opts->{seed}); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
my @toposort = DBIx::Class::TopoSort->toposort( |
177
|
|
|
|
|
|
|
$schema, |
178
|
|
|
|
|
|
|
%{$opts->{toposort} // {}}, |
179
|
|
|
|
|
|
|
); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
my $runner = DBIx::Class::Sims::Runner->new( |
182
|
|
|
|
|
|
|
parent => $self, |
183
|
|
|
|
|
|
|
schema => $schema, |
184
|
|
|
|
|
|
|
toposort => \@toposort, |
185
|
|
|
|
|
|
|
initial_spec => $initial_spec, |
186
|
|
|
|
|
|
|
spec => $spec, |
187
|
|
|
|
|
|
|
hooks => $hooks, |
188
|
|
|
|
|
|
|
reqs => $reqs, |
189
|
|
|
|
|
|
|
); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
$rows = eval { |
192
|
|
|
|
|
|
|
$runner->run(); |
193
|
|
|
|
|
|
|
}; if ($@) { |
194
|
|
|
|
|
|
|
$additional->{error} = $@; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
if ($opts->{die_on_failure} // 1) { |
197
|
|
|
|
|
|
|
warn "SEED: $opts->{seed}\n"; |
198
|
|
|
|
|
|
|
die $@; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$additional->{created} = $runner->{created}; |
203
|
|
|
|
|
|
|
$additional->{duplicates} = $runner->{duplicates}; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Force a reload from the database of every row we're returning. |
206
|
|
|
|
|
|
|
foreach my $item (values %$rows) { |
207
|
|
|
|
|
|
|
$_->discard_changes for @$item; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
if (wantarray) { |
212
|
|
|
|
|
|
|
return ($rows, $additional); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
else { |
215
|
|
|
|
|
|
|
return $rows; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
use YAML::Any qw( LoadFile Load ); |
220
|
|
|
|
|
|
|
sub normalize_input { |
221
|
|
|
|
|
|
|
my ($proto) = @_; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
if ( ref($proto) ) { |
224
|
|
|
|
|
|
|
return $proto; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Doing a stat on a filename with a newline throws an error. |
228
|
|
|
|
|
|
|
my $x = eval { |
229
|
|
|
|
|
|
|
no warnings; |
230
|
|
|
|
|
|
|
if ( -e $proto ) { |
231
|
|
|
|
|
|
|
return LoadFile($proto); |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
}; |
234
|
|
|
|
|
|
|
return $x if $x; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
return Load($proto); |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub massage_input { |
240
|
|
|
|
|
|
|
my ($schema, $struct) = @_; |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
my $dtp = $schema->storage->datetime_parser; |
243
|
|
|
|
|
|
|
walk({ |
244
|
|
|
|
|
|
|
preprocess => sub { |
245
|
|
|
|
|
|
|
# Don't descend into the weeds. Only do the things we care about. |
246
|
|
|
|
|
|
|
return if grep { blessed($_) } @_; |
247
|
|
|
|
|
|
|
return unless grep { reftype($_) } @_; |
248
|
|
|
|
|
|
|
return @_; |
249
|
|
|
|
|
|
|
}, |
250
|
|
|
|
|
|
|
wanted => sub { |
251
|
|
|
|
|
|
|
return unless (reftype($_)//'') eq 'HASH' && !blessed($_); |
252
|
|
|
|
|
|
|
foreach my $k ( keys %$_ ) { |
253
|
|
|
|
|
|
|
my $t = $_; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Expand the dot-naming convention. |
256
|
|
|
|
|
|
|
while ( $k =~ /([^.]*)\.(.*)/ ) { |
257
|
|
|
|
|
|
|
$t->{$1} = { $2 => delete($t->{$k}) }; |
258
|
|
|
|
|
|
|
$t = $t->{$1}; $k = $2; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Handle DateTime values passed to us. |
262
|
|
|
|
|
|
|
if (defined $t->{$k}) { |
263
|
|
|
|
|
|
|
if ( $t->{$k} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)$/ ) { |
264
|
|
|
|
|
|
|
# format_datetime() requires a DateTime object. This may be a |
265
|
|
|
|
|
|
|
# string, therefore hoist it if need-be. |
266
|
|
|
|
|
|
|
unless (blessed($t->{$k})) { |
267
|
|
|
|
|
|
|
$t->{$k} = DateTime->new( |
268
|
|
|
|
|
|
|
year => $1, month => $2, day => $3, |
269
|
|
|
|
|
|
|
hour => $4, minute => $5, second => $6, |
270
|
|
|
|
|
|
|
); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
$t->{$k} = $dtp->format_datetime($t->{$k}); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
}, |
277
|
|
|
|
|
|
|
}, $struct); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
return $struct; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |
283
|
|
|
|
|
|
|
__END__ |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head1 NAME |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
DBIx::Class::Sims - The addition of simulating data to DBIx::Class |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head1 SYNOPSIS (CLASS VERSION) |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
DBIx::Class::Sims->add_sims( |
292
|
|
|
|
|
|
|
$schema, 'source_name', |
293
|
|
|
|
|
|
|
address => { type => 'us_address' }, |
294
|
|
|
|
|
|
|
zip_code => { type => 'us_zipcode' }, |
295
|
|
|
|
|
|
|
# ... |
296
|
|
|
|
|
|
|
); |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $rows = DBIx::Class::Sims->load_sims($schema, { |
299
|
|
|
|
|
|
|
Table1 => [ |
300
|
|
|
|
|
|
|
{}, # Take sims or default values for everything |
301
|
|
|
|
|
|
|
{ # Override some values, take sim values for others |
302
|
|
|
|
|
|
|
column1 => 20, |
303
|
|
|
|
|
|
|
column2 => 'something', |
304
|
|
|
|
|
|
|
}, |
305
|
|
|
|
|
|
|
], |
306
|
|
|
|
|
|
|
}); |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=head1 SYNOPSIS (COMPONENT VERSION) |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Within your schema class: |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
__PACKAGE__->load_components('Sims'); |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Within your resultsources, specify the sims generation rules for columns that |
315
|
|
|
|
|
|
|
need specified. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
__PACKAGE__->add_columns( |
318
|
|
|
|
|
|
|
... |
319
|
|
|
|
|
|
|
address => { |
320
|
|
|
|
|
|
|
data_type => 'varchar', |
321
|
|
|
|
|
|
|
is_nullable => 1, |
322
|
|
|
|
|
|
|
data_length => 10, |
323
|
|
|
|
|
|
|
sim => { type => 'us_address' }, |
324
|
|
|
|
|
|
|
}, |
325
|
|
|
|
|
|
|
zipcode => { |
326
|
|
|
|
|
|
|
data_type => 'varchar', |
327
|
|
|
|
|
|
|
is_nullable => 1, |
328
|
|
|
|
|
|
|
data_length => 10, |
329
|
|
|
|
|
|
|
sim => { type => 'us_zipcode' }, |
330
|
|
|
|
|
|
|
}, |
331
|
|
|
|
|
|
|
column1 => { |
332
|
|
|
|
|
|
|
data_type => 'int', |
333
|
|
|
|
|
|
|
is_nullable => 0, |
334
|
|
|
|
|
|
|
sim => { |
335
|
|
|
|
|
|
|
min => 10, |
336
|
|
|
|
|
|
|
max => 20, |
337
|
|
|
|
|
|
|
}, |
338
|
|
|
|
|
|
|
}, |
339
|
|
|
|
|
|
|
column2 => { |
340
|
|
|
|
|
|
|
data_type => 'varchar', |
341
|
|
|
|
|
|
|
is_nullable => 1, |
342
|
|
|
|
|
|
|
data_length => 10, |
343
|
|
|
|
|
|
|
default_value => 'foobar', |
344
|
|
|
|
|
|
|
}, |
345
|
|
|
|
|
|
|
... |
346
|
|
|
|
|
|
|
); |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
Later: |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
$schema->deploy({ |
351
|
|
|
|
|
|
|
add_drop_table => 1, |
352
|
|
|
|
|
|
|
}); |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
my $rows = $schema->load_sims({ |
355
|
|
|
|
|
|
|
Table1 => [ |
356
|
|
|
|
|
|
|
{}, # Take sims or default values for everything |
357
|
|
|
|
|
|
|
{ # Override some values, take sim values for others |
358
|
|
|
|
|
|
|
column1 => 20, |
359
|
|
|
|
|
|
|
column2 => 'something', |
360
|
|
|
|
|
|
|
}, |
361
|
|
|
|
|
|
|
], |
362
|
|
|
|
|
|
|
}); |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=head1 PURPOSE |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Generating test data for non-simplistic databases is extremely hard, especially |
367
|
|
|
|
|
|
|
as the schema grows and changes. Designing scenarios B<should> be doable by only |
368
|
|
|
|
|
|
|
specifying the minimal elements actually used in the test with the test being |
369
|
|
|
|
|
|
|
resilient to any changes in the schema that don't affect the elements specified. |
370
|
|
|
|
|
|
|
This includes changes like adding a new parent table, new required child tables, |
371
|
|
|
|
|
|
|
and new non-NULL columns to the table being tested. |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
With Sims, you specify only what you care about. Any required parent rows are |
374
|
|
|
|
|
|
|
automatically generated. If a row requires a certain number of child rows (all |
375
|
|
|
|
|
|
|
artists must have one or more albums), that can be set as well. If a column must |
376
|
|
|
|
|
|
|
have specific data in it (a US zipcode or a range of numbers), you can specify |
377
|
|
|
|
|
|
|
that in the table definition. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
And, in all cases, you can override anything. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 DESCRIPTION |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
This is a L<DBIx::Class> component that adds a few methods to your |
384
|
|
|
|
|
|
|
L<DBIx::Class::Schema> object. These methods make it much easier to create data |
385
|
|
|
|
|
|
|
for testing purposes (though, obviously, it's not limited to just test data). |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Alternately, it can be used as a class method vs. a component, if that fits your |
388
|
|
|
|
|
|
|
needs better. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=head1 METHODS |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=head2 load_sims |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
C<< $rv, $addl? = $schema->load_sims( $spec, ?$opts ) >> |
395
|
|
|
|
|
|
|
C<< $rv, $addl? = DBIx::Class::Sims->load_sims( $schema, $spec, ?$opts ) >> |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
This method will load the rows requested in C<$spec>, plus any additional rows |
398
|
|
|
|
|
|
|
necessary to make those rows work. This includes any parent rows (as defined by |
399
|
|
|
|
|
|
|
C<belongs_to>) and per any constraints defined in C<$opts->{constraints}>. If |
400
|
|
|
|
|
|
|
need-be, you can pass in hooks (as described below) to manipulate the data. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
load_sims does all of its work within a call to L<DBIx::Class::Schema/txn_do>. |
403
|
|
|
|
|
|
|
If anything goes wrong, load_sims will rethrow the error after the transaction |
404
|
|
|
|
|
|
|
is rolled back. |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
This, of course, assumes that the tables you are working with support |
407
|
|
|
|
|
|
|
transactions. (I'm looking at you, MyISAM!) If they do not, that is on you. |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=head3 Return value |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
This returns one or two values, depending on if you call load_sims in a scalar |
412
|
|
|
|
|
|
|
or array context. |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
The first value is a hash of arrays of hashes. This will match the C<$spec>, |
415
|
|
|
|
|
|
|
except that where the C<$spec> has a requested set of things to make, the return |
416
|
|
|
|
|
|
|
will have the DBIx::Class::Row objects that were created. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Note that you do not get back the objects for anything other than the objects |
419
|
|
|
|
|
|
|
specified at the top level. |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
This second value is a hashref with additional items that may be useful. It may |
422
|
|
|
|
|
|
|
contain: |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=over 4 |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=item * error |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
This will contain any error that happened while trying to create the rows. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
This is most useful when C<< die_on_failure >> is set to 0. |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=item * seed |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
This is the random seed that was used in this run. If you set the seed in the |
435
|
|
|
|
|
|
|
opts parameter in the load_sims call, it will be that value. Otherwise, it will |
436
|
|
|
|
|
|
|
be set to a usefully random value for you. It will be different every time even |
437
|
|
|
|
|
|
|
if you call load_sims multiple times within the same process in the same second. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=item * created |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
This is a hashref containing a count of each source that was created. This is |
442
|
|
|
|
|
|
|
different from the first return value in that this lists everything created, not |
443
|
|
|
|
|
|
|
just what was requested. It also only has counts, not the actual rows. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=item * duplicates |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
This is a hashref containing a list for each source of all the duplicates that |
448
|
|
|
|
|
|
|
were found when creating rows for that source. For each duplicate found, there |
449
|
|
|
|
|
|
|
will be an entry that specifies the criteria used to find that duplicate and the |
450
|
|
|
|
|
|
|
row in the database that was found. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
The list will be ordered by when the duplicate was found, but that ordering will |
453
|
|
|
|
|
|
|
B<NOT> be stable across different runs unless the same C<< seed >> is used. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=back |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head2 set_sim_type |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
C<< $class_or_obj->set_sim_type({ $name => $handler, ... }); >> |
460
|
|
|
|
|
|
|
C<< $class_or_obj->set_sim_type([ [ $name, $regex, $handler ], ... ]); >> |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This method will set the handler for the C<$name> sim type. The C<$handler> must |
463
|
|
|
|
|
|
|
be a reference to a subroutine. You may pass in as many name/handler pairs as you |
464
|
|
|
|
|
|
|
like. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
You may alternately pass in an arrayref of triplets. This allows you to use a |
467
|
|
|
|
|
|
|
regex to match the provided type. C<$name> will be returned when the user |
468
|
|
|
|
|
|
|
introspects the list of loaded sim types. C<$regex> will be used when finding the |
469
|
|
|
|
|
|
|
type to handle this column. C<$handler> must be a reference to a subroutine. |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
You cannot set pairs and triplets in the same invocation. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
This method may be called as a class or object method. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
This method returns nothing. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
C<set_sim_types()> is an alias to this method. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=head2 sim_types |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
C<< $class_or_obj->sim_types(); >> |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
This method will return a sorted list of all registered sim types. |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
This method may be called as a class or object method. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=head1 SPECIFICATION |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
The specification can be passed along as a filename that contains YAML or JSON, |
490
|
|
|
|
|
|
|
a string that contains YAML or JSON, or as a hash of arrays of hashes. The |
491
|
|
|
|
|
|
|
structure should look like: |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
{ |
494
|
|
|
|
|
|
|
ResultSourceName => [ |
495
|
|
|
|
|
|
|
{ |
496
|
|
|
|
|
|
|
column => $value, |
497
|
|
|
|
|
|
|
column => $value, |
498
|
|
|
|
|
|
|
relationship => $parent_object, |
499
|
|
|
|
|
|
|
relationship => { |
500
|
|
|
|
|
|
|
column => $value, |
501
|
|
|
|
|
|
|
}, |
502
|
|
|
|
|
|
|
'relationship.column' => $value, |
503
|
|
|
|
|
|
|
'rel1.rel2.rel3.column' => $value, |
504
|
|
|
|
|
|
|
}, |
505
|
|
|
|
|
|
|
], |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
If a column is a belongs_to relationship name, then the row associated with that |
509
|
|
|
|
|
|
|
relationship specifier will be used. This is how you would specify a specific |
510
|
|
|
|
|
|
|
parent-child relationship. (Otherwise, a random choice will be made as to which |
511
|
|
|
|
|
|
|
parent to use, creating one as necessary if possible.) The dots will be followed |
512
|
|
|
|
|
|
|
as far as necessary. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
If a column's value is a hashref, then that will be treated as a sim entry. |
515
|
|
|
|
|
|
|
Example: |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
{ |
518
|
|
|
|
|
|
|
Artist => [ |
519
|
|
|
|
|
|
|
{ |
520
|
|
|
|
|
|
|
name => { type => 'us_name' }, |
521
|
|
|
|
|
|
|
}, |
522
|
|
|
|
|
|
|
], |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
That will use the provided sim type 'us_name'. This will override any sim entry |
526
|
|
|
|
|
|
|
specified on the column. See L</SIM ENTRY> for more information. |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Note: Before 0.300800, this behavior was triggered by a reference to a hashref. |
529
|
|
|
|
|
|
|
That will still work, but is deprecated, throws a warning, and will be removed |
530
|
|
|
|
|
|
|
in a future release. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Columns that have not been specified will be populated in one of two ways. The |
533
|
|
|
|
|
|
|
first is if the database has a default value for it. Otherwise, you can specify |
534
|
|
|
|
|
|
|
the C<sim> key in the column_info for that column. This is a new key that is not |
535
|
|
|
|
|
|
|
used by any other component. See L</SIM ENTRY> for more information. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
(Please see L<DBIx::Class::ResultSource/add_columns> for details on column_info) |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
B<NOTE>: The keys of the outermost hash are resultsource names. The keys within |
540
|
|
|
|
|
|
|
the row-specific hashes are either columns or relationships. Not resultsources. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head2 Reuse wherever possible |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
The Sims's normal behavior is to attempt to reuse whenever possible. The theory |
545
|
|
|
|
|
|
|
is that if you didn't say you cared about something, you do B<NOT> care about |
546
|
|
|
|
|
|
|
that thing. |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head3 Unique constraints |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
If a source has unique constraints defined, the Sims will use them to determine |
551
|
|
|
|
|
|
|
if a new row with these values I<can> be created or not. If a row already |
552
|
|
|
|
|
|
|
exists with these values for the unique constraints, then that row will be used |
553
|
|
|
|
|
|
|
instead of creating a new one. |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
This is B<REGARDLESS> of the values for the non-unique-constraint rows. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head3 Forcing creation of a parent |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
If you do not specify values for a parent (i.e., belongs_to), then the first row |
560
|
|
|
|
|
|
|
for that parent will be be used. If you don't care what values the parent has, |
561
|
|
|
|
|
|
|
but you care that a different parent is used, then you can set the __META__ key |
562
|
|
|
|
|
|
|
as follows: |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
$schema->load_sims({ |
565
|
|
|
|
|
|
|
Album => { |
566
|
|
|
|
|
|
|
artist => { __META__ => { create => 1 } }, |
567
|
|
|
|
|
|
|
name => 'Some name', |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
}) |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
This will force the creation of a parent instead of reusing the parent. |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
B<NOTE>: If the simmed values within the parent's class would result in values |
574
|
|
|
|
|
|
|
that are the same across a unique constraint with an existing row, then that |
575
|
|
|
|
|
|
|
row will be used. This just bypasses the "attempt to use the first parent". |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=head2 Alternatives |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=head3 Hard-coded number of things |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
If you only want N of a thing, not really caring just what the column values end |
582
|
|
|
|
|
|
|
up being, you can take a shortcut: |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
{ |
585
|
|
|
|
|
|
|
ResultSourceName => 3, |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
That will create 3 of that thing, taking all the defaults and sim'ed options as |
589
|
|
|
|
|
|
|
exist. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
This will also work if you want 3 of a child via a has_many relationship. For |
592
|
|
|
|
|
|
|
example, you can do: |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
{ |
595
|
|
|
|
|
|
|
Artist => { |
596
|
|
|
|
|
|
|
name => 'Someone Famous', |
597
|
|
|
|
|
|
|
albums => 240, |
598
|
|
|
|
|
|
|
}, |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
That will create 240 different albums for that artist, all with the defaults. |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=head3 Just one thing |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
If you are creating one of a thing and setting some of the values, you can skip |
606
|
|
|
|
|
|
|
the arrayref and pass the hashref directly. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
{ |
609
|
|
|
|
|
|
|
ResultSourceName => { |
610
|
|
|
|
|
|
|
column => $value, |
611
|
|
|
|
|
|
|
column => $value, |
612
|
|
|
|
|
|
|
relationship => { |
613
|
|
|
|
|
|
|
column => $value, |
614
|
|
|
|
|
|
|
}, |
615
|
|
|
|
|
|
|
'relationship.column' => $value, |
616
|
|
|
|
|
|
|
'rel1.rel2.rel3.column' => $value, |
617
|
|
|
|
|
|
|
}, |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
And that will work exactly as expected. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head3 References |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Let's say you have a table that's a child of two other tables. You can specify |
625
|
|
|
|
|
|
|
that relationship as follows: |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
{ |
628
|
|
|
|
|
|
|
Parent1 => 1, |
629
|
|
|
|
|
|
|
Parent2 => { |
630
|
|
|
|
|
|
|
Child => { |
631
|
|
|
|
|
|
|
parent1 => \"Parent1[0]", |
632
|
|
|
|
|
|
|
}, |
633
|
|
|
|
|
|
|
}, |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
That's a reference to a string with the tablename as a pseudo-array, then the |
637
|
|
|
|
|
|
|
index into that array. This only works for rows that you are going to return |
638
|
|
|
|
|
|
|
back from the C<< load_sims() >> call. |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
This also only works for belongs_to relationships. Since all parents are created |
641
|
|
|
|
|
|
|
before all children, the Sims cannot back-reference into children. |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=head2 Notes |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=over 4 |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item * Multiply-specified children |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
Sometimes, you will have a table with more than one parent (q.v. t/t5.t for an |
650
|
|
|
|
|
|
|
example of this). If you specify a row for each parent and, in each parent, |
651
|
|
|
|
|
|
|
specify a child with the same characteristics, only one child will be created. |
652
|
|
|
|
|
|
|
The assumption is that you meant the same row. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
This does B<not> apply to creating multiple rows with the same characteristics |
655
|
|
|
|
|
|
|
as children of the same parent. The assumption is that you meant to do that. |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=back |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=head1 OPTS |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
There are several possible options. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=head2 constraints |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
The constraints can be passed along as a filename that contains YAML or JSON, a |
666
|
|
|
|
|
|
|
string that contains YAML or JSON, or as a hash of arrays of hashes. The |
667
|
|
|
|
|
|
|
structure should look like: |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
{ |
670
|
|
|
|
|
|
|
Person => { |
671
|
|
|
|
|
|
|
addresses => 2, |
672
|
|
|
|
|
|
|
}, |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
All the C<belongs_to> relationships are automatically added to the constraints. |
676
|
|
|
|
|
|
|
You can add additional constraints, as needed. The most common use for this will |
677
|
|
|
|
|
|
|
be to add required child rows. For example, C<< Person->has_many('addresses') >> |
678
|
|
|
|
|
|
|
would normally mean that if you create a Person, no Address rows would be |
679
|
|
|
|
|
|
|
created. But, we could specify a constraint that says "Every person must have |
680
|
|
|
|
|
|
|
at least 2 addresses." Now, whenever a Person is created, two Addresses will be |
681
|
|
|
|
|
|
|
added along as well, if they weren't already created through some other |
682
|
|
|
|
|
|
|
specification. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=head2 die_on_failure |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
If set to 0, this will prevent a die when creating a row. Instead, you will be |
687
|
|
|
|
|
|
|
responsible for checking C<< $additional->{error} >> yourself. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
This defaults to 1. |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=head2 seed |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
If set, this will be the srand() seed used for this invocation. |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=head2 toposort |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
This is passed directly to the call to C<< DBIx::Class::TopoSort->toposort >>. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=head2 hooks |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
Most people will never need to use this. But, some schema definitions may have |
702
|
|
|
|
|
|
|
reasons that prevent a clean simulating with this module. For example, there may |
703
|
|
|
|
|
|
|
be application-managed sequences. To that end, you may specify the following |
704
|
|
|
|
|
|
|
hooks: |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=over 4 |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=item * preprocess |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
This receives C<$name, $source, $spec> and expects nothing in return. C<$spec> |
711
|
|
|
|
|
|
|
is the hashref that will be passed to C<<$schema->resultset($name)->create()>>. |
712
|
|
|
|
|
|
|
This hook is expected to modify C<$spec> as needed. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=item * postprocess |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
This receives C<$name, $source, $row> and expects nothing in return. This hook |
717
|
|
|
|
|
|
|
is expected to modify the newly-created row object as needed. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=back |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=head1 SIM ENTRY |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
To control how a column's values are simulated, add a "sim" entry in the |
724
|
|
|
|
|
|
|
column_info for that column. The sim entry is a hash that can have the followingkeys: |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=over 4 |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=item * value / values |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
This behaves just like default_value would behave, but doesn't require setting a |
731
|
|
|
|
|
|
|
default value on the column. |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
sim => { |
734
|
|
|
|
|
|
|
value => 'The value to always use', |
735
|
|
|
|
|
|
|
}, |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
This can be either a string, number, or an arrayref of strings or numbers. If it |
738
|
|
|
|
|
|
|
is an arrayref, then a random choice from that array will be selected. |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=item * type |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
This labels the column as having a certain type. A type is registered using |
743
|
|
|
|
|
|
|
L</set_sim_type>. The type acts as a name for a function that's used to generate |
744
|
|
|
|
|
|
|
the value. See L</Types> for more information. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=item * min / max |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
If the column is numeric, then the min and max bound the random value generated. |
749
|
|
|
|
|
|
|
If the column is a string, then the min and max are the length of the random |
750
|
|
|
|
|
|
|
value generated. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=item * func |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
This is a function that is provided the column info. Its return value is used to |
755
|
|
|
|
|
|
|
populate the column. |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=item * null_chance |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
If the column is nullable I<and> this is set I<and> it is a number between 0 and |
760
|
|
|
|
|
|
|
1, then if C<rand()> is less than that number, the column will be set to null. |
761
|
|
|
|
|
|
|
Otherwise, the standard behaviors will apply. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
If the column is B<not> nullable, this setting is ignored. |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=back |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
(Please see L<DBIx::Class::ResultSource/add_columns> for details on column_info) |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=head2 Types |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
The handler for a sim type will receive the column info (as defined in |
772
|
|
|
|
|
|
|
L<DBIx::Class::ResultSource/add_columns>). From that, the handler returns the |
773
|
|
|
|
|
|
|
value that will be used for this column. |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
Please see L<DBIx::Class::Sims::Types> for the list of included sim types. |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
=head1 SEQUENCE OF EVENTS |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
When an item is created, the following actions are taken (in this order): |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=over 4 |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=item 1 The columns are fixed up. |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
This is where generated values are generated. After this is done, all the values |
786
|
|
|
|
|
|
|
that will be inserted into the database are now available. |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
q.v. L</SIM ENTRY> for more information. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=item 1 The preprocess hook fires. |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
You can modify the hashref as necessary. This includes potentially changing what |
793
|
|
|
|
|
|
|
parent and/or child rows to associate with this row. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=item 1 All foreign keys are resolved. |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
If it's a parent relationship, the parent row will be found or created. All |
798
|
|
|
|
|
|
|
parent rows will go through the same sequence of events as described here. |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
If it's a child relationship, creation of the child rows will be deferred until |
801
|
|
|
|
|
|
|
later. |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
=item 1 The row is found or created. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
It might be found by unique constraint or created. |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=item 1 All child relationships are handled |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
Because they're a child relationship, they are deferred until the time that |
810
|
|
|
|
|
|
|
model is handled in the toposorted graph. They are not created now because they |
811
|
|
|
|
|
|
|
might associate with a different parent that has not been created yet. |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
=item 1 The postprocess hook fires. |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
Note that any child rows are not guaranteed to exist yet. |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=back |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=head1 TODO |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=head2 Multi-column types |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
In some applications, columns like "state" and "zipcode" are correlated. Values |
824
|
|
|
|
|
|
|
for one must be legal for the value in the other. The Sims currently has no way |
825
|
|
|
|
|
|
|
of generating correlated columns like this. |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
This is most useful for saying "These 6 columns should be a coherent address". |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=head2 Allow a column to reference other columns |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Sometimes, a column should alter its behavior based on other columns. A fullname |
832
|
|
|
|
|
|
|
column may have the firstname and lastname columns concatenated, with other |
833
|
|
|
|
|
|
|
things thrown in. Or, a zipcode column should only generate a zipcode that're |
834
|
|
|
|
|
|
|
legal for the state. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=head1 BUGS/SUGGESTIONS |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
This module is hosted on Github at |
839
|
|
|
|
|
|
|
L<https://github.com/robkinyon/dbix-class-sims>. Pull requests are strongly |
840
|
|
|
|
|
|
|
encouraged. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=head1 DBIx::Class::Fixtures |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
L<DBIx::Class::Fixtures> is another way to load data into a database. Unlike |
845
|
|
|
|
|
|
|
this module, L<DBIx::Class::Fixtures> approaches the problem by loading the same |
846
|
|
|
|
|
|
|
data every time. This is complementary because some tables (such as lookup |
847
|
|
|
|
|
|
|
tables of countries) want to be seeded with the same data every time. The ideal |
848
|
|
|
|
|
|
|
solution would be to have a set of tables loaded with fixtures and another set |
849
|
|
|
|
|
|
|
of tables loaded with sims. |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=head1 SEE ALSO |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
L<DBIx::Class>, L<DBIx::Class::Fixtures> |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=head1 AUTHOR |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
Rob Kinyon <rob.kinyon@gmail.com> |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=head1 LICENSE |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
Copyright (c) 2013 Rob Kinyon. All Rights Reserved. |
862
|
|
|
|
|
|
|
This is free software, you may use it and distribute it under the same terms |
863
|
|
|
|
|
|
|
as Perl itself. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=cut |