Imported Upstream version 0.082820
gregor herrmann
8 years ago
14 | 14 | Alexander Keusch <cpan@keusch.at> |
15 | 15 | alexrj: Alessandro Ranellucci <aar@cpan.org> |
16 | 16 | alnewkirk: Al Newkirk <github@alnewkirk.com> |
17 | Altreus: Alastair McGowan-Douglas <alastair.mcgowan@opusvl.com> | |
17 | 18 | amiri: Amiri Barksdale <amiribarksdale@gmail.com> |
18 | 19 | amoore: Andrew Moore <amoore@cpan.org> |
19 | 20 | Andrew Mehta <Andrew@unitedgames.co.uk> |
37 | 38 | caldrin: Maik Hentsche <maik.hentsche@amd.com> |
38 | 39 | castaway: Jess Robinson <castaway@desert-island.me.uk> |
39 | 40 | chorny: Alexandr Ciornii <alexchorny@gmail.com> |
41 | cj: C.J. Adams-Collier <cjcollier@cpan.org> | |
40 | 42 | claco: Christopher H. Laco <claco@cpan.org> |
41 | 43 | clkao: CL Kao <clkao@clkao.org> |
42 | 44 | Ctrl-O http://ctrlo.com/ |
53 | 55 | dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com> |
54 | 56 | dnm: Justin Wheeler <jwheeler@datademons.com> |
55 | 57 | dpetrov: Dimitar Petrov <mitakaa@gmail.com> |
58 | Dr^ZigMan: Robert Stone <drzigman@drzigman.com> | |
56 | 59 | dsteinbrunner: David Steinbrunner <dsteinbrunner@pobox.com> |
57 | 60 | duncan_dmg: Duncan Garland <Duncan.Garland@motortrak.com> |
58 | 61 | dwc: Daniel Westermann-Clark <danieltwc@cpan.org> |
78 | 81 | Ian Wells <ijw@cack.org.uk> |
79 | 82 | idn: Ian Norton <i.norton@shadowcat.co.uk> |
80 | 83 | ilmari: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> |
84 | ingy: Ingy döt Net <ingy@ingy.net> | |
81 | 85 | initself: Mike Baas <mike@initselftech.com> |
82 | 86 | ironcamel: Naveed Massjouni <naveedm9@gmail.com> |
83 | 87 | jasonmay: Jason May <jason.a.may@gmail.com> |
98 | 102 | jshirley: J. Shirley <jshirley@gmail.com> |
99 | 103 | kaare: Kaare Rasmussen |
100 | 104 | kd: Kieren Diment <diment@gmail.com> |
105 | kkane: Kevin L. Kane <kevin.kane@gmail.com> | |
101 | 106 | konobi: Scott McWhirter <konobi@cpan.org> |
102 | 107 | lejeunerenard: Sean Zellmer <sean@lejeunerenard.com> |
103 | 108 | littlesavage: Alexey Illarionov <littlesavage@orionet.ru> |
193 | 198 | typester: Daisuke Murase <typester@cpan.org> |
194 | 199 | uree: Oriol Soriano <oriol.soriano@capside.com> |
195 | 200 | uwe: Uwe Voelker <uwe@uwevoelker.de> |
201 | vanstyn: Henry Van Styn <vanstyn@cpan.org> | |
196 | 202 | victori: Victor Igumnov <victori@cpan.org> |
197 | 203 | wdh: Will Hawes <wdhawes@gmail.com> |
198 | 204 | wesm: Wes Malone <wes@mitsi.com> |
200 | 206 | wintermute: Toby Corkindale <tjc@cpan.org> |
201 | 207 | wreis: Wallace Reis <wreis@cpan.org> |
202 | 208 | xenoterracide: Caleb Cushing <xenoterracide@gmail.com> |
209 | xmikew: Mike Wisener <xmikew@32ths.com> | |
203 | 210 | yrlnry: Mark Jason Dominus <mjd@plover.com> |
204 | 211 | zamolxes: Bogdan Lucaciu <bogdan@wiz.ro> |
205 | 212 | Zefram: Andrew Main <zefram@fysh.org> |
0 | 0 | Revision history for DBIx::Class |
1 | ||
2 | 0.082820 2015-03-20 20:35 (UTC) | |
3 | * Fixes | |
4 | - Protect destructors from rare but possible double execution, and | |
5 | loudly warn the user whenever the problem is encountered (GH#63) | |
6 | - Relax the 'self_result_object' argument check in the relationship | |
7 | resolution codepath, restoring exotic uses of inflate_result | |
8 | http://lists.scsys.co.uk/pipermail/dbix-class/2015-January/011876.html | |
9 | - Fix updating multiple CLOB/BLOB columns on Oracle | |
10 | - Fix exception on complex update/delete under a replicated setup | |
11 | http://lists.scsys.co.uk/pipermail/dbix-class/2015-January/011903.html | |
12 | - Fix uninitialized warnings on empty hashes passed to join/prefetch | |
13 | https://github.com/vanstyn/RapidApp/commit/6f41f6e48 and | |
14 | http://lists.scsys.co.uk/pipermail/dbix-class/2015-February/011921.html | |
15 | - Fix hang in t/72pg.t when run against DBD::Pg 3.5.0. The ping() | |
16 | implementation changes due to RT#100648 made an alarm() based | |
17 | timeout lock-prone. | |
18 | ||
19 | * Misc | |
20 | - Remove warning about potential side effects of RT#79576 (scheduled) | |
21 | - Various doc improvements (GH#35, GH#62, GH#66, GH#70, GH#71, GH#72) | |
22 | - Depend on newer Moo, to benefit from a safer runtime (RT#93004) | |
23 | - Fix intermittent failures in the LeakTracer on 5.18+ | |
24 | - Fix failures of t/54taint.t on Windows with spaces in the $^X | |
25 | executable path (RT#101615) | |
1 | 26 | |
2 | 27 | 0.082810 2014-10-25 13:58 (UTC) |
3 | 28 | * Fixes |
0 | DBIx::Class is Copyright (c) 2005-2014 by mst, castaway, ribasushi, and others. | |
0 | DBIx::Class is Copyright (c) 2005-2015 by mst, castaway, ribasushi, and others. | |
1 | 1 | See AUTHORS and LICENSE included with this distribution. All rights reserved. |
2 | 2 | |
3 | 3 | This is free software; you can redistribute it and/or modify it under the |
225 | 225 | META.yml |
226 | 226 | README |
227 | 227 | script/dbicadmin |
228 | t/00describe_environment.t | |
228 | 229 | t/04_c3_mro.t |
229 | 230 | t/05components.t |
230 | 231 | t/100extra_source.t |
688 | 689 | t/update/all.t |
689 | 690 | t/update/ident_cond.t |
690 | 691 | t/update/type_aware.t |
692 | t/zzzzzzz_authors.t | |
691 | 693 | t/zzzzzzz_perl_perf_bug.t |
692 | 694 | t/zzzzzzz_sqlite_deadlock.t |
693 | 695 | xt/authors.t |
51 | 51 | List::Util: 1.16 |
52 | 52 | MRO::Compat: 0.12 |
53 | 53 | Module::Find: 0.07 |
54 | Moo: 1.006001 | |
54 | Moo: 2.000 | |
55 | 55 | Path::Class: 0.18 |
56 | 56 | SQL::Abstract: 1.81 |
57 | 57 | Scope::Guard: 0.03 |
67 | 67 | homepage: http://www.dbix-class.org/ |
68 | 68 | license: http://dev.perl.org/licenses/ |
69 | 69 | repository: https://github.com/dbsrgits/DBIx-Class |
70 | version: 0.082810 | |
70 | version: 0.082820 | |
71 | 71 | x_authority: cpan:RIBASUSHI |
72 | 72 | x_contributors: |
73 | 73 | - 'abraxxa: Alexander Hartmaier <abraxxa@cpan.org>' |
76 | 76 | - 'Alexander Keusch <cpan@keusch.at>' |
77 | 77 | - 'alexrj: Alessandro Ranellucci <aar@cpan.org>' |
78 | 78 | - 'alnewkirk: Al Newkirk <github@alnewkirk.com>' |
79 | - 'Altreus: Alastair McGowan-Douglas <alastair.mcgowan@opusvl.com>' | |
79 | 80 | - 'amiri: Amiri Barksdale <amiribarksdale@gmail.com>' |
80 | 81 | - 'amoore: Andrew Moore <amoore@cpan.org>' |
81 | 82 | - 'Andrew Mehta <Andrew@unitedgames.co.uk>' |
99 | 100 | - 'caldrin: Maik Hentsche <maik.hentsche@amd.com>' |
100 | 101 | - 'castaway: Jess Robinson <castaway@desert-island.me.uk>' |
101 | 102 | - 'chorny: Alexandr Ciornii <alexchorny@gmail.com>' |
103 | - 'cj: C.J. Adams-Collier <cjcollier@cpan.org>' | |
102 | 104 | - 'claco: Christopher H. Laco <claco@cpan.org>' |
103 | 105 | - 'clkao: CL Kao <clkao@clkao.org>' |
104 | 106 | - 'Ctrl-O http://ctrlo.com/' |
115 | 117 | - 'dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com>' |
116 | 118 | - 'dnm: Justin Wheeler <jwheeler@datademons.com>' |
117 | 119 | - 'dpetrov: Dimitar Petrov <mitakaa@gmail.com>' |
120 | - 'Dr^ZigMan: Robert Stone <drzigman@drzigman.com>' | |
118 | 121 | - 'dsteinbrunner: David Steinbrunner <dsteinbrunner@pobox.com>' |
119 | 122 | - 'duncan_dmg: Duncan Garland <Duncan.Garland@motortrak.com>' |
120 | 123 | - 'dwc: Daniel Westermann-Clark <danieltwc@cpan.org>' |
140 | 143 | - 'Ian Wells <ijw@cack.org.uk>' |
141 | 144 | - 'idn: Ian Norton <i.norton@shadowcat.co.uk>' |
142 | 145 | - 'ilmari: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>' |
146 | - 'ingy: Ingy döt Net <ingy@ingy.net>' | |
143 | 147 | - 'initself: Mike Baas <mike@initselftech.com>' |
144 | 148 | - 'ironcamel: Naveed Massjouni <naveedm9@gmail.com>' |
145 | 149 | - 'jasonmay: Jason May <jason.a.may@gmail.com>' |
160 | 164 | - 'jshirley: J. Shirley <jshirley@gmail.com>' |
161 | 165 | - 'kaare: Kaare Rasmussen' |
162 | 166 | - 'kd: Kieren Diment <diment@gmail.com>' |
167 | - 'kkane: Kevin L. Kane <kevin.kane@gmail.com>' | |
163 | 168 | - 'konobi: Scott McWhirter <konobi@cpan.org>' |
164 | 169 | - 'lejeunerenard: Sean Zellmer <sean@lejeunerenard.com>' |
165 | 170 | - 'littlesavage: Alexey Illarionov <littlesavage@orionet.ru>' |
255 | 260 | - 'typester: Daisuke Murase <typester@cpan.org>' |
256 | 261 | - 'uree: Oriol Soriano <oriol.soriano@capside.com>' |
257 | 262 | - 'uwe: Uwe Voelker <uwe@uwevoelker.de>' |
263 | - 'vanstyn: Henry Van Styn <vanstyn@cpan.org>' | |
258 | 264 | - 'victori: Victor Igumnov <victori@cpan.org>' |
259 | 265 | - 'wdh: Will Hawes <wdhawes@gmail.com>' |
260 | 266 | - 'wesm: Wes Malone <wes@mitsi.com>' |
262 | 268 | - 'wintermute: Toby Corkindale <tjc@cpan.org>' |
263 | 269 | - 'wreis: Wallace Reis <wreis@cpan.org>' |
264 | 270 | - 'xenoterracide: Caleb Cushing <xenoterracide@gmail.com>' |
271 | - 'xmikew: Mike Wisener <xmikew@32ths.com>' | |
265 | 272 | - 'yrlnry: Mark Jason Dominus <mjd@plover.com>' |
266 | 273 | - 'zamolxes: Bogdan Lucaciu <bogdan@wiz.ro>' |
267 | 274 | - 'Zefram: Andrew Main <zefram@fysh.org>' |
55 | 55 | 'Data::Page' => '2.00', |
56 | 56 | 'Devel::GlobalDestruction' => '0.09', |
57 | 57 | 'Hash::Merge' => '0.12', |
58 | 'Moo' => '1.006001', | |
58 | 'Moo' => '2.000', | |
59 | 59 | 'MRO::Compat' => '0.12', |
60 | 60 | 'Module::Find' => '0.07', |
61 | 61 | 'namespace::clean' => '0.24', |
0 | DBIx::Class is Copyright (c) 2005-2014 by mst, castaway, ribasushi, and others. | |
0 | DBIx::Class is Copyright (c) 2005-2015 by mst, castaway, ribasushi, and others. | |
1 | 1 | See AUTHORS and LICENSE included with this distribution. All rights reserved. |
2 | 2 | |
3 | 3 | NAME |
203 | 203 | questions and suggestions have been shown to catalyze monumental |
204 | 204 | improvements in consistency, accuracy and performance. |
205 | 205 | |
206 | List of the awesome contributors who made DBIC v0.082810 possible | |
207 | ||
208 | abraxxa:Alexander Hartmaier <abraxxa@cpan.org> | |
209 | ||
210 | acca:Alexander Kuznetsov <acca@cpan.org> | |
211 | ||
212 | aherzog:Adam Herzog <adam@herzogdesigns.com> | |
206 | List of the awesome contributors who made DBIC v0.082820 possible | |
207 | ||
208 | abraxxa: Alexander Hartmaier <abraxxa@cpan.org> | |
209 | ||
210 | acca: Alexander Kuznetsov <acca@cpan.org> | |
211 | ||
212 | aherzog: Adam Herzog <adam@herzogdesigns.com> | |
213 | 213 | |
214 | 214 | Alexander Keusch <cpan@keusch.at> |
215 | 215 | |
216 | alexrj:Alessandro Ranellucci <aar@cpan.org> | |
217 | ||
218 | alnewkirk:Al Newkirk <github@alnewkirk.com> | |
219 | ||
220 | amiri:Amiri Barksdale <amiribarksdale@gmail.com> | |
221 | ||
222 | amoore:Andrew Moore <amoore@cpan.org> | |
216 | alexrj: Alessandro Ranellucci <aar@cpan.org> | |
217 | ||
218 | alnewkirk: Al Newkirk <github@alnewkirk.com> | |
219 | ||
220 | Altreus: Alastair McGowan-Douglas <alastair.mcgowan@opusvl.com> | |
221 | ||
222 | amiri: Amiri Barksdale <amiribarksdale@gmail.com> | |
223 | ||
224 | amoore: Andrew Moore <amoore@cpan.org> | |
223 | 225 | |
224 | 226 | Andrew Mehta <Andrew@unitedgames.co.uk> |
225 | 227 | |
226 | andrewalker:Andre Walker <andre@andrewalker.net> | |
227 | ||
228 | andyg:Andy Grundman <andy@hybridized.org> | |
229 | ||
230 | ank:Andres Kievsky <ank@ank.com.ar> | |
231 | ||
232 | arc:Aaron Crane <arc@cpan.org> | |
233 | ||
234 | arcanez:Justin Hunter <justin.d.hunter@gmail.com> | |
235 | ||
236 | ash:Ash Berlin <ash@cpan.org> | |
237 | ||
238 | bert:Norbert Csongrádi <bert@cpan.org> | |
239 | ||
240 | bfwg:Colin Newell <colin.newell@gmail.com> | |
241 | ||
242 | blblack:Brandon L. Black <blblack@gmail.com> | |
243 | ||
244 | bluefeet:Aran Deltac <bluefeet@cpan.org> | |
245 | ||
246 | boghead:Bryan Beeley <cpan@beeley.org> | |
247 | ||
248 | bphillips:Brian Phillips <bphillips@cpan.org> | |
249 | ||
250 | brd:Brad Davis <brd@FreeBSD.org> | |
228 | andrewalker: Andre Walker <andre@andrewalker.net> | |
229 | ||
230 | andyg: Andy Grundman <andy@hybridized.org> | |
231 | ||
232 | ank: Andres Kievsky <ank@ank.com.ar> | |
233 | ||
234 | arc: Aaron Crane <arc@cpan.org> | |
235 | ||
236 | arcanez: Justin Hunter <justin.d.hunter@gmail.com> | |
237 | ||
238 | ash: Ash Berlin <ash@cpan.org> | |
239 | ||
240 | bert: Norbert Csongrádi <bert@cpan.org> | |
241 | ||
242 | bfwg: Colin Newell <colin.newell@gmail.com> | |
243 | ||
244 | blblack: Brandon L. Black <blblack@gmail.com> | |
245 | ||
246 | bluefeet: Aran Deltac <bluefeet@cpan.org> | |
247 | ||
248 | boghead: Bryan Beeley <cpan@beeley.org> | |
249 | ||
250 | bphillips: Brian Phillips <bphillips@cpan.org> | |
251 | ||
252 | brd: Brad Davis <brd@FreeBSD.org> | |
251 | 253 | |
252 | 254 | Brian Kirkbride <brian.kirkbride@deeperbydesign.com> |
253 | 255 | |
254 | bricas:Brian Cassidy <bricas@cpan.org> | |
255 | ||
256 | brunov:Bruno Vecchi <vecchi.b@gmail.com> | |
257 | ||
258 | caelum:Rafael Kitover <rkitover@cpan.org> | |
259 | ||
260 | caldrin:Maik Hentsche <maik.hentsche@amd.com> | |
261 | ||
262 | castaway:Jess Robinson <castaway@desert-island.me.uk> | |
263 | ||
264 | chorny:Alexandr Ciornii <alexchorny@gmail.com> | |
265 | ||
266 | claco:Christopher H. Laco <claco@cpan.org> | |
267 | ||
268 | clkao:CL Kao <clkao@clkao.org> | |
256 | bricas: Brian Cassidy <bricas@cpan.org> | |
257 | ||
258 | brunov: Bruno Vecchi <vecchi.b@gmail.com> | |
259 | ||
260 | caelum: Rafael Kitover <rkitover@cpan.org> | |
261 | ||
262 | caldrin: Maik Hentsche <maik.hentsche@amd.com> | |
263 | ||
264 | castaway: Jess Robinson <castaway@desert-island.me.uk> | |
265 | ||
266 | chorny: Alexandr Ciornii <alexchorny@gmail.com> | |
267 | ||
268 | cj: C.J. Adams-Collier <cjcollier@cpan.org> | |
269 | ||
270 | claco: Christopher H. Laco <claco@cpan.org> | |
271 | ||
272 | clkao: CL Kao <clkao@clkao.org> | |
269 | 273 | |
270 | 274 | Ctrl-O <http://ctrlo.com/> |
271 | 275 | |
272 | da5id:David Jack Olrik <david@olrik.dk> | |
273 | ||
274 | dams:Damien Krotkine <dams@cpan.org> | |
275 | ||
276 | dandv:Dan Dascalescu <ddascalescu+github@gmail.com> | |
277 | ||
278 | dariusj:Darius Jokilehto <dariusjokilehto@yahoo.co.uk> | |
279 | ||
280 | davewood:David Schmidt <mail@davidschmidt.at> | |
281 | ||
282 | daxim:Lars Dɪᴇᴄᴋᴏᴡ 迪拉斯 <daxim@cpan.org> | |
283 | ||
284 | dduncan:Darren Duncan <darren@darrenduncan.net> | |
285 | ||
286 | debolaz:Anders Nor Berle <berle@cpan.org> | |
287 | ||
288 | dew:Dan Thomas <dan@godders.org> | |
289 | ||
290 | dim0xff:Dmitry Latin <dim0xff@gmail.com> | |
291 | ||
292 | dkubb:Dan Kubb <dan.kubb-cpan@onautopilot.com> | |
293 | ||
294 | dnm:Justin Wheeler <jwheeler@datademons.com> | |
295 | ||
296 | dpetrov:Dimitar Petrov <mitakaa@gmail.com> | |
297 | ||
298 | dsteinbrunner:David Steinbrunner <dsteinbrunner@pobox.com> | |
299 | ||
300 | duncan_dmg:Duncan Garland <Duncan.Garland@motortrak.com> | |
301 | ||
302 | dwc:Daniel Westermann-Clark <danieltwc@cpan.org> | |
303 | ||
304 | dyfrgi:Michael Leuchtenburg <michael@slashhome.org> | |
305 | ||
306 | edenc:Eden Cardim <edencardim@gmail.com> | |
276 | da5id: David Jack Olrik <david@olrik.dk> | |
277 | ||
278 | dams: Damien Krotkine <dams@cpan.org> | |
279 | ||
280 | dandv: Dan Dascalescu <ddascalescu+github@gmail.com> | |
281 | ||
282 | dariusj: Darius Jokilehto <dariusjokilehto@yahoo.co.uk> | |
283 | ||
284 | davewood: David Schmidt <mail@davidschmidt.at> | |
285 | ||
286 | daxim: Lars Dɪᴇᴄᴋᴏᴡ 迪拉斯 <daxim@cpan.org> | |
287 | ||
288 | dduncan: Darren Duncan <darren@darrenduncan.net> | |
289 | ||
290 | debolaz: Anders Nor Berle <berle@cpan.org> | |
291 | ||
292 | dew: Dan Thomas <dan@godders.org> | |
293 | ||
294 | dim0xff: Dmitry Latin <dim0xff@gmail.com> | |
295 | ||
296 | dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com> | |
297 | ||
298 | dnm: Justin Wheeler <jwheeler@datademons.com> | |
299 | ||
300 | dpetrov: Dimitar Petrov <mitakaa@gmail.com> | |
301 | ||
302 | Dr^ZigMan: Robert Stone <drzigman@drzigman.com> | |
303 | ||
304 | dsteinbrunner: David Steinbrunner <dsteinbrunner@pobox.com> | |
305 | ||
306 | duncan_dmg: Duncan Garland <Duncan.Garland@motortrak.com> | |
307 | ||
308 | dwc: Daniel Westermann-Clark <danieltwc@cpan.org> | |
309 | ||
310 | dyfrgi: Michael Leuchtenburg <michael@slashhome.org> | |
311 | ||
312 | edenc: Eden Cardim <edencardim@gmail.com> | |
307 | 313 | |
308 | 314 | Eligo <http://eligo.co.uk/> |
309 | 315 | |
310 | ether:Karen Etheridge <ether@cpan.org> | |
311 | ||
312 | evdb:Edmund von der Burg <evdb@ecclestoad.co.uk> | |
313 | ||
314 | faxm0dem:Fabien Wernli <cpan@faxm0dem.org> | |
315 | ||
316 | felliott:Fitz Elliott <fitz.elliott@gmail.com> | |
317 | ||
318 | freetime:Bill Moseley <moseley@hank.org> | |
319 | ||
320 | frew:Arthur Axel "fREW" Schmidt <frioux@gmail.com> | |
321 | ||
322 | gbjk:Gareth Kirwan <gbjk@thermeon.com> | |
323 | ||
324 | Getty:Torsten Raudssus <torsten@raudss.us> | |
325 | ||
326 | goraxe:Gordon Irving <goraxe@cpan.org> | |
327 | ||
328 | gphat:Cory G Watson <gphat@cpan.org> | |
316 | ether: Karen Etheridge <ether@cpan.org> | |
317 | ||
318 | evdb: Edmund von der Burg <evdb@ecclestoad.co.uk> | |
319 | ||
320 | faxm0dem: Fabien Wernli <cpan@faxm0dem.org> | |
321 | ||
322 | felliott: Fitz Elliott <fitz.elliott@gmail.com> | |
323 | ||
324 | freetime: Bill Moseley <moseley@hank.org> | |
325 | ||
326 | frew: Arthur Axel "fREW" Schmidt <frioux@gmail.com> | |
327 | ||
328 | gbjk: Gareth Kirwan <gbjk@thermeon.com> | |
329 | ||
330 | Getty: Torsten Raudssus <torsten@raudss.us> | |
331 | ||
332 | goraxe: Gordon Irving <goraxe@cpan.org> | |
333 | ||
334 | gphat: Cory G Watson <gphat@cpan.org> | |
329 | 335 | |
330 | 336 | Grant Street Group <http://www.grantstreet.com/> |
331 | 337 | |
332 | groditi:Guillermo Roditi <groditi@cpan.org> | |
333 | ||
334 | gshank:Gerda Shank <gshank@cpan.org> | |
335 | ||
336 | guacamole:Fred Steinberg <fred.steinberg@gmail.com> | |
337 | ||
338 | Haarg:Graham Knop <haarg@haarg.org> | |
339 | ||
340 | hobbs:Andrew Rodland <andrew@cleverdomain.org> | |
338 | groditi: Guillermo Roditi <groditi@cpan.org> | |
339 | ||
340 | gshank: Gerda Shank <gshank@cpan.org> | |
341 | ||
342 | guacamole: Fred Steinberg <fred.steinberg@gmail.com> | |
343 | ||
344 | Haarg: Graham Knop <haarg@haarg.org> | |
345 | ||
346 | hobbs: Andrew Rodland <andrew@cleverdomain.org> | |
341 | 347 | |
342 | 348 | Ian Wells <ijw@cack.org.uk> |
343 | 349 | |
344 | idn:Ian Norton <i.norton@shadowcat.co.uk> | |
345 | ||
346 | ilmari:Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | |
347 | ||
348 | initself:Mike Baas <mike@initselftech.com> | |
349 | ||
350 | ironcamel:Naveed Massjouni <naveedm9@gmail.com> | |
351 | ||
352 | jasonmay:Jason May <jason.a.may@gmail.com> | |
353 | ||
354 | jawnsy:Jonathan Yu <jawnsy@cpan.org> | |
355 | ||
356 | jegade:Jens Gassmann <jens.gassmann@atomix.de> | |
357 | ||
358 | jeneric:Eric A. Miller <emiller@cpan.org> | |
359 | ||
360 | jesper:Jesper Krogh <jesper@krogh.cc> | |
350 | idn: Ian Norton <i.norton@shadowcat.co.uk> | |
351 | ||
352 | ilmari: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | |
353 | ||
354 | ingy: Ingy döt Net <ingy@ingy.net> | |
355 | ||
356 | initself: Mike Baas <mike@initselftech.com> | |
357 | ||
358 | ironcamel: Naveed Massjouni <naveedm9@gmail.com> | |
359 | ||
360 | jasonmay: Jason May <jason.a.may@gmail.com> | |
361 | ||
362 | jawnsy: Jonathan Yu <jawnsy@cpan.org> | |
363 | ||
364 | jegade: Jens Gassmann <jens.gassmann@atomix.de> | |
365 | ||
366 | jeneric: Eric A. Miller <emiller@cpan.org> | |
367 | ||
368 | jesper: Jesper Krogh <jesper@krogh.cc> | |
361 | 369 | |
362 | 370 | Jesse Sheidlower <jester@panix.com> |
363 | 371 | |
364 | jgoulah:John Goulah <jgoulah@cpan.org> | |
365 | ||
366 | jguenther:Justin Guenther <jguenther@cpan.org> | |
367 | ||
368 | jhannah:Jay Hannah <jay@jays.net> | |
369 | ||
370 | jmac:Jason McIntosh <jmac@appleseed-sc.com> | |
371 | ||
372 | jmmills:Jason M. Mills <jmmills@cpan.org> | |
373 | ||
374 | jnapiorkowski:John Napiorkowski <jjn1056@yahoo.com> | |
372 | jgoulah: John Goulah <jgoulah@cpan.org> | |
373 | ||
374 | jguenther: Justin Guenther <jguenther@cpan.org> | |
375 | ||
376 | jhannah: Jay Hannah <jay@jays.net> | |
377 | ||
378 | jmac: Jason McIntosh <jmac@appleseed-sc.com> | |
379 | ||
380 | jmmills: Jason M. Mills <jmmills@cpan.org> | |
381 | ||
382 | jnapiorkowski: John Napiorkowski <jjn1056@yahoo.com> | |
375 | 383 | |
376 | 384 | Joe Carlson <jwcarlson@lbl.gov> |
377 | 385 | |
378 | jon:Jon Schutz <jjschutz@cpan.org> | |
386 | jon: Jon Schutz <jjschutz@cpan.org> | |
379 | 387 | |
380 | 388 | Jordan Metzmeier <jmetzmeier@magazines.com> |
381 | 389 | |
382 | jshirley:J. Shirley <jshirley@gmail.com> | |
383 | ||
384 | kaare:Kaare Rasmussen | |
385 | ||
386 | kd:Kieren Diment <diment@gmail.com> | |
387 | ||
388 | konobi:Scott McWhirter <konobi@cpan.org> | |
389 | ||
390 | lejeunerenard:Sean Zellmer <sean@lejeunerenard.com> | |
391 | ||
392 | littlesavage:Alexey Illarionov <littlesavage@orionet.ru> | |
393 | ||
394 | lukes:Luke Saunders <luke.saunders@gmail.com> | |
395 | ||
396 | marcus:Marcus Ramberg <mramberg@cpan.org> | |
397 | ||
398 | mateu:Mateu X. Hunter <hunter@missoula.org> | |
390 | jshirley: J. Shirley <jshirley@gmail.com> | |
391 | ||
392 | kaare: Kaare Rasmussen | |
393 | ||
394 | kd: Kieren Diment <diment@gmail.com> | |
395 | ||
396 | kkane: Kevin L. Kane <kevin.kane@gmail.com> | |
397 | ||
398 | konobi: Scott McWhirter <konobi@cpan.org> | |
399 | ||
400 | lejeunerenard: Sean Zellmer <sean@lejeunerenard.com> | |
401 | ||
402 | littlesavage: Alexey Illarionov <littlesavage@orionet.ru> | |
403 | ||
404 | lukes: Luke Saunders <luke.saunders@gmail.com> | |
405 | ||
406 | marcus: Marcus Ramberg <mramberg@cpan.org> | |
407 | ||
408 | mateu: Mateu X. Hunter <hunter@missoula.org> | |
399 | 409 | |
400 | 410 | Matt LeBlanc <antirice@gmail.com> |
401 | 411 | |
402 | 412 | Matt Sickler <imMute@msk4.com> |
403 | 413 | |
404 | mattlaw:Matt Lawrence | |
405 | ||
406 | mattp:Matt Phillips <mattp@cpan.org> | |
407 | ||
408 | mdk:Mark Keating <m.keating@shadowcat.co.uk> | |
409 | ||
410 | melo:Pedro Melo <melo@simplicidade.org> | |
411 | ||
412 | metaperl:Terrence Brannon <metaperl@gmail.com> | |
413 | ||
414 | michaelr:Michael Reddick <michael.reddick@gmail.com> | |
415 | ||
416 | milki:Jonathan Chu <milki@rescomp.berkeley.edu> | |
417 | ||
418 | minty:Murray Walker <perl@minty.org> | |
419 | ||
420 | mithaldu:Christian Walde <walde.christian@gmail.com> | |
421 | ||
422 | mjemmeson:Michael Jemmeson <michael.jemmeson@gmail.com> | |
423 | ||
424 | mna:Maya | |
425 | ||
426 | mo:Moritz Onken <onken@netcubed.de> | |
427 | ||
428 | moltar:Roman Filippov <romanf@cpan.org> | |
429 | ||
430 | moritz:Moritz Lenz <moritz@faui2k3.org> | |
431 | ||
432 | mrf:Mike Francis <ungrim97@gmail.com> | |
433 | ||
434 | mst:Matt S. Trout <mst@shadowcat.co.uk> | |
435 | ||
436 | mstratman:Mark A. Stratman <stratman@gmail.com> | |
437 | ||
438 | ned:Neil de Carteret <n3dst4@gmail.com> | |
439 | ||
440 | nigel:Nigel Metheringham <nigelm@cpan.org> | |
441 | ||
442 | ningu:David Kamholz <dkamholz@cpan.org> | |
443 | ||
444 | Nniuq:Ron "Quinn" Straight" <quinnfazigu@gmail.org> | |
445 | ||
446 | norbi:Norbert Buchmuller <norbi@nix.hu> | |
447 | ||
448 | nothingmuch:Yuval Kogman <nothingmuch@woobling.org> | |
449 | ||
450 | nuba:Nuba Princigalli <nuba@cpan.org> | |
451 | ||
452 | Numa:Dan Sully <daniel@cpan.org> | |
453 | ||
454 | oalders:Olaf Alders <olaf@wundersolutions.com> | |
414 | mattlaw: Matt Lawrence | |
415 | ||
416 | mattp: Matt Phillips <mattp@cpan.org> | |
417 | ||
418 | mdk: Mark Keating <m.keating@shadowcat.co.uk> | |
419 | ||
420 | melo: Pedro Melo <melo@simplicidade.org> | |
421 | ||
422 | metaperl: Terrence Brannon <metaperl@gmail.com> | |
423 | ||
424 | michaelr: Michael Reddick <michael.reddick@gmail.com> | |
425 | ||
426 | milki: Jonathan Chu <milki@rescomp.berkeley.edu> | |
427 | ||
428 | minty: Murray Walker <perl@minty.org> | |
429 | ||
430 | mithaldu: Christian Walde <walde.christian@gmail.com> | |
431 | ||
432 | mjemmeson: Michael Jemmeson <michael.jemmeson@gmail.com> | |
433 | ||
434 | mna: Maya | |
435 | ||
436 | mo: Moritz Onken <onken@netcubed.de> | |
437 | ||
438 | moltar: Roman Filippov <romanf@cpan.org> | |
439 | ||
440 | moritz: Moritz Lenz <moritz@faui2k3.org> | |
441 | ||
442 | mrf: Mike Francis <ungrim97@gmail.com> | |
443 | ||
444 | mst: Matt S. Trout <mst@shadowcat.co.uk> | |
445 | ||
446 | mstratman: Mark A. Stratman <stratman@gmail.com> | |
447 | ||
448 | ned: Neil de Carteret <n3dst4@gmail.com> | |
449 | ||
450 | nigel: Nigel Metheringham <nigelm@cpan.org> | |
451 | ||
452 | ningu: David Kamholz <dkamholz@cpan.org> | |
453 | ||
454 | Nniuq: Ron "Quinn" Straight" <quinnfazigu@gmail.org> | |
455 | ||
456 | norbi: Norbert Buchmuller <norbi@nix.hu> | |
457 | ||
458 | nothingmuch: Yuval Kogman <nothingmuch@woobling.org> | |
459 | ||
460 | nuba: Nuba Princigalli <nuba@cpan.org> | |
461 | ||
462 | Numa: Dan Sully <daniel@cpan.org> | |
463 | ||
464 | oalders: Olaf Alders <olaf@wundersolutions.com> | |
455 | 465 | |
456 | 466 | Olly Betts <olly@survex.com> |
457 | 467 | |
458 | osfameron:Hakim Cassimally <osfameron@cpan.org> | |
459 | ||
460 | ovid:Curtis "Ovid" Poe <ovid@cpan.org> | |
461 | ||
462 | oyse:Øystein Torget <oystein.torget@dnv.com> | |
463 | ||
464 | paulm:Paul Makepeace <paulm+pause@paulm.com> | |
465 | ||
466 | penguin:K J Cheetham <jamie@shadowcatsystems.co.uk> | |
467 | ||
468 | perigrin:Chris Prather <chris@prather.org> | |
468 | osfameron: Hakim Cassimally <osfameron@cpan.org> | |
469 | ||
470 | ovid: Curtis "Ovid" Poe <ovid@cpan.org> | |
471 | ||
472 | oyse: Øystein Torget <oystein.torget@dnv.com> | |
473 | ||
474 | paulm: Paul Makepeace <paulm+pause@paulm.com> | |
475 | ||
476 | penguin: K J Cheetham <jamie@shadowcatsystems.co.uk> | |
477 | ||
478 | perigrin: Chris Prather <chris@prather.org> | |
469 | 479 | |
470 | 480 | Peter Siklósi <einon@einon.hu> |
471 | 481 | |
472 | 482 | Peter Valdemar Mørch <peter@morch.com> |
473 | 483 | |
474 | peter:Peter Collingbourne <peter@pcc.me.uk> | |
475 | ||
476 | phaylon:Robert Sedlacek <phaylon@dunkelheit.at> | |
477 | ||
478 | plu:Johannes Plunien <plu@cpan.org> | |
479 | ||
480 | Possum:Daniel LeWarne <possum@cpan.org> | |
481 | ||
482 | pplu:Jose Luis Martinez <jlmartinez@capside.com> | |
483 | ||
484 | quicksilver:Jules Bean <jules@jellybean.co.uk> | |
485 | ||
486 | racke:Stefan Hornburg <racke@linuxia.de> | |
487 | ||
488 | rafl:Florian Ragwitz <rafl@debian.org> | |
489 | ||
490 | rainboxx:Matthias Dietrich <perl@rb.ly> | |
491 | ||
492 | rbo:Robert Bohne <rbo@cpan.org> | |
493 | ||
494 | rbuels:Robert Buels <rmb32@cornell.edu> | |
495 | ||
496 | rdj:Ryan D Johnson <ryan@innerfence.com> | |
497 | ||
498 | Relequestual:Ben Hutton <relequestual@gmail.com> | |
499 | ||
500 | renormalist:Steffen Schwigon <schwigon@cpan.org> | |
501 | ||
502 | ribasushi:Peter Rabbitson <ribasushi@cpan.org> | |
503 | ||
504 | rjbs:Ricardo Signes <rjbs@cpan.org> | |
484 | peter: Peter Collingbourne <peter@pcc.me.uk> | |
485 | ||
486 | phaylon: Robert Sedlacek <phaylon@dunkelheit.at> | |
487 | ||
488 | plu: Johannes Plunien <plu@cpan.org> | |
489 | ||
490 | Possum: Daniel LeWarne <possum@cpan.org> | |
491 | ||
492 | pplu: Jose Luis Martinez <jlmartinez@capside.com> | |
493 | ||
494 | quicksilver: Jules Bean <jules@jellybean.co.uk> | |
495 | ||
496 | racke: Stefan Hornburg <racke@linuxia.de> | |
497 | ||
498 | rafl: Florian Ragwitz <rafl@debian.org> | |
499 | ||
500 | rainboxx: Matthias Dietrich <perl@rb.ly> | |
501 | ||
502 | rbo: Robert Bohne <rbo@cpan.org> | |
503 | ||
504 | rbuels: Robert Buels <rmb32@cornell.edu> | |
505 | ||
506 | rdj: Ryan D Johnson <ryan@innerfence.com> | |
507 | ||
508 | Relequestual: Ben Hutton <relequestual@gmail.com> | |
509 | ||
510 | renormalist: Steffen Schwigon <schwigon@cpan.org> | |
511 | ||
512 | ribasushi: Peter Rabbitson <ribasushi@cpan.org> | |
513 | ||
514 | rjbs: Ricardo Signes <rjbs@cpan.org> | |
505 | 515 | |
506 | 516 | Robert Krimen <rkrimen@cpan.org> |
507 | 517 | |
508 | 518 | Robert Olson <bob@rdolson.org> |
509 | 519 | |
510 | robkinyon:Rob Kinyon <rkinyon@cpan.org> | |
520 | robkinyon: Rob Kinyon <rkinyon@cpan.org> | |
511 | 521 | |
512 | 522 | Roman Ardern-Corris <spam_in@3legs.com> |
513 | 523 | |
514 | ruoso:Daniel Ruoso <daniel@ruoso.com> | |
515 | ||
516 | Sadrak:Felix Antonius Wilhelm Ostmann <sadrak@cpan.org> | |
517 | ||
518 | sc_:Just Another Perl Hacker | |
519 | ||
520 | schwern:Michael G Schwern <mschwern@cpan.org> | |
524 | ruoso: Daniel Ruoso <daniel@ruoso.com> | |
525 | ||
526 | Sadrak: Felix Antonius Wilhelm Ostmann <sadrak@cpan.org> | |
527 | ||
528 | sc_: Just Another Perl Hacker | |
529 | ||
530 | schwern: Michael G Schwern <mschwern@cpan.org> | |
521 | 531 | |
522 | 532 | Scott R. Godin <webdragon.net@gmail.com> |
523 | 533 | |
524 | scotty:Scotty Allen <scotty@scottyallen.com> | |
525 | ||
526 | semifor:Marc Mims <marc@questright.com> | |
534 | scotty: Scotty Allen <scotty@scottyallen.com> | |
535 | ||
536 | semifor: Marc Mims <marc@questright.com> | |
527 | 537 | |
528 | 538 | Simon Elliott <cpan@browsing.co.uk> |
529 | 539 | |
530 | SineSwiper:Brendan Byrd <perl@resonatorsoft.org> | |
531 | ||
532 | skaufman:Samuel Kaufman <sam@socialflow.com> | |
533 | ||
534 | solomon:Jared Johnson <jaredj@nmgi.com> | |
535 | ||
536 | spb:Stephen Bennett <stephen@freenode.net> | |
540 | SineSwiper: Brendan Byrd <perl@resonatorsoft.org> | |
541 | ||
542 | skaufman: Samuel Kaufman <sam@socialflow.com> | |
543 | ||
544 | solomon: Jared Johnson <jaredj@nmgi.com> | |
545 | ||
546 | spb: Stephen Bennett <stephen@freenode.net> | |
537 | 547 | |
538 | 548 | Squeeks <squeek@cpan.org> |
539 | 549 | |
540 | srezic:Slaven Rezic <slaven@rezic.de> | |
541 | ||
542 | sszabo:Stephan Szabo <sszabo@bigpanda.com> | |
550 | srezic: Slaven Rezic <slaven@rezic.de> | |
551 | ||
552 | sszabo: Stephan Szabo <sszabo@bigpanda.com> | |
543 | 553 | |
544 | 554 | Stephen Peters <steve@stephenpeters.me> |
545 | 555 | |
546 | stonecolddevin:Devin Austin <dhoss@cpan.org> | |
547 | ||
548 | talexb:Alex Beamish <talexb@gmail.com> | |
549 | ||
550 | tamias:Ronald J Kimball <rjk@tamias.net> | |
551 | ||
552 | TBSliver:Tom Bloor <t.bloor@shadowcat.co.uk> | |
553 | ||
554 | teejay:Aaron Trevena <teejay@cpan.org> | |
555 | ||
556 | theorbtwo:James Mastros <james@mastros.biz> | |
556 | stonecolddevin: Devin Austin <dhoss@cpan.org> | |
557 | ||
558 | talexb: Alex Beamish <talexb@gmail.com> | |
559 | ||
560 | tamias: Ronald J Kimball <rjk@tamias.net> | |
561 | ||
562 | TBSliver: Tom Bloor <t.bloor@shadowcat.co.uk> | |
563 | ||
564 | teejay: Aaron Trevena <teejay@cpan.org> | |
565 | ||
566 | theorbtwo: James Mastros <james@mastros.biz> | |
557 | 567 | |
558 | 568 | Thomas Kratz <tomk@cpan.org> |
559 | 569 | |
560 | timbunce:Tim Bunce <tim.bunce@pobox.com> | |
570 | timbunce: Tim Bunce <tim.bunce@pobox.com> | |
561 | 571 | |
562 | 572 | Todd Lipcon |
563 | 573 | |
564 | 574 | Tom Hukins <tom@eborcom.com> |
565 | 575 | |
566 | tommy:Tommy Butler <tbutler.cpan.org@internetalias.net> | |
567 | ||
568 | tonvoon:Ton Voon <ton.voon@opsview.com> | |
569 | ||
570 | triode:Pete Gamache <gamache@cpan.org> | |
571 | ||
572 | typester:Daisuke Murase <typester@cpan.org> | |
573 | ||
574 | uree:Oriol Soriano <oriol.soriano@capside.com> | |
575 | ||
576 | uwe:Uwe Voelker <uwe@uwevoelker.de> | |
577 | ||
578 | victori:Victor Igumnov <victori@cpan.org> | |
579 | ||
580 | wdh:Will Hawes <wdhawes@gmail.com> | |
581 | ||
582 | wesm:Wes Malone <wes@mitsi.com> | |
583 | ||
584 | willert:Sebastian Willert <willert@cpan.org> | |
585 | ||
586 | wintermute:Toby Corkindale <tjc@cpan.org> | |
587 | ||
588 | wreis:Wallace Reis <wreis@cpan.org> | |
589 | ||
590 | xenoterracide:Caleb Cushing <xenoterracide@gmail.com> | |
591 | ||
592 | yrlnry:Mark Jason Dominus <mjd@plover.com> | |
593 | ||
594 | zamolxes:Bogdan Lucaciu <bogdan@wiz.ro> | |
595 | ||
596 | Zefram:Andrew Main <zefram@fysh.org> | |
576 | tommy: Tommy Butler <tbutler.cpan.org@internetalias.net> | |
577 | ||
578 | tonvoon: Ton Voon <ton.voon@opsview.com> | |
579 | ||
580 | triode: Pete Gamache <gamache@cpan.org> | |
581 | ||
582 | typester: Daisuke Murase <typester@cpan.org> | |
583 | ||
584 | uree: Oriol Soriano <oriol.soriano@capside.com> | |
585 | ||
586 | uwe: Uwe Voelker <uwe@uwevoelker.de> | |
587 | ||
588 | vanstyn: Henry Van Styn <vanstyn@cpan.org> | |
589 | ||
590 | victori: Victor Igumnov <victori@cpan.org> | |
591 | ||
592 | wdh: Will Hawes <wdhawes@gmail.com> | |
593 | ||
594 | wesm: Wes Malone <wes@mitsi.com> | |
595 | ||
596 | willert: Sebastian Willert <willert@cpan.org> | |
597 | ||
598 | wintermute: Toby Corkindale <tjc@cpan.org> | |
599 | ||
600 | wreis: Wallace Reis <wreis@cpan.org> | |
601 | ||
602 | xenoterracide: Caleb Cushing <xenoterracide@gmail.com> | |
603 | ||
604 | xmikew: Mike Wisener <xmikew@32ths.com> | |
605 | ||
606 | yrlnry: Mark Jason Dominus <mjd@plover.com> | |
607 | ||
608 | zamolxes: Bogdan Lucaciu <bogdan@wiz.ro> | |
609 | ||
610 | Zefram: Andrew Main <zefram@fysh.org> | |
597 | 611 | |
598 | 612 | The canonical source of authors and their details is the AUTHORS file at |
599 | 613 | the root of this distribution (or repository). The canonical source of |
20 | 20 | |
21 | 21 | __PACKAGE__->add_unique_constraint([qw( name )]); |
22 | 22 | |
23 | __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd'); | |
23 | __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd', 'artistid'); | |
24 | 24 | |
25 | 25 | 1; |
26 | 26 |
11 | 11 | data_type => 'integer', |
12 | 12 | is_auto_increment => 1 |
13 | 13 | }, |
14 | artist => { | |
14 | artistid => { | |
15 | 15 | data_type => 'integer', |
16 | 16 | }, |
17 | 17 | title => { |
25 | 25 | |
26 | 26 | __PACKAGE__->set_primary_key('cdid'); |
27 | 27 | |
28 | __PACKAGE__->add_unique_constraint([qw( title artist )]); | |
28 | __PACKAGE__->add_unique_constraint([qw( title artistid )]); | |
29 | 29 | |
30 | __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist'); | |
31 | __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track'); | |
30 | __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist', 'artistid'); | |
31 | __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track', 'cdid'); | |
32 | 32 | |
33 | 33 | 1; |
11 | 11 | data_type => 'integer', |
12 | 12 | is_auto_increment => 1 |
13 | 13 | }, |
14 | cd => { | |
14 | cdid => { | |
15 | 15 | data_type => 'integer', |
16 | 16 | }, |
17 | 17 | title => { |
21 | 21 | |
22 | 22 | __PACKAGE__->set_primary_key('trackid'); |
23 | 23 | |
24 | __PACKAGE__->add_unique_constraint([qw( title cd )]); | |
24 | __PACKAGE__->add_unique_constraint([qw( title cdid )]); | |
25 | 25 | |
26 | __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd'); | |
26 | __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd', 'cdid'); | |
27 | 27 | |
28 | 28 | 1; |
Binary diff not shown
6 | 6 | |
7 | 7 | CREATE TABLE "cd" ( |
8 | 8 | "cdid" INTEGER PRIMARY KEY NOT NULL, |
9 | "artist" integer NOT NULL, | |
9 | "artistid" integer NOT NULL, | |
10 | 10 | "title" text NOT NULL, |
11 | 11 | "year" datetime, |
12 | FOREIGN KEY ("artist") REFERENCES "artist"("artistid") ON DELETE CASCADE ON UPDATE CASCADE | |
12 | FOREIGN KEY ("artistid") REFERENCES "artist"("artistid") ON DELETE CASCADE ON UPDATE CASCADE | |
13 | 13 | ); |
14 | 14 | |
15 | CREATE INDEX "cd_idx_artist" ON "cd" ("artist"); | |
15 | CREATE INDEX "cd_idx_artistid" ON "cd" ("artistid"); | |
16 | 16 | |
17 | CREATE UNIQUE INDEX "cd_title_artist" ON "cd" ("title", "artist"); | |
17 | CREATE UNIQUE INDEX "cd_title_artistid" ON "cd" ("title", "artistid"); | |
18 | 18 | |
19 | 19 | CREATE TABLE "track" ( |
20 | 20 | "trackid" INTEGER PRIMARY KEY NOT NULL, |
21 | "cd" integer NOT NULL, | |
21 | "cdid" integer NOT NULL, | |
22 | 22 | "title" text NOT NULL, |
23 | FOREIGN KEY ("cd") REFERENCES "cd"("cdid") ON DELETE CASCADE ON UPDATE CASCADE | |
23 | FOREIGN KEY ("cdid") REFERENCES "cd"("cdid") ON DELETE CASCADE ON UPDATE CASCADE | |
24 | 24 | ); |
25 | 25 | |
26 | CREATE INDEX "track_idx_cd" ON "track" ("cd"); | |
26 | CREATE INDEX "track_idx_cdid" ON "track" ("cdid"); | |
27 | 27 | |
28 | CREATE UNIQUE INDEX "track_title_cd" ON "track" ("title", "cd"); | |
28 | CREATE UNIQUE INDEX "track_title_cdid" ON "track" ("title", "cdid"); |
30 | 30 | } |
31 | 31 | |
32 | 32 | $schema->populate('Cd', [ |
33 | [qw/title artist/], | |
33 | [qw/title artistid/], | |
34 | 34 | @cds, |
35 | 35 | ]); |
36 | 36 | |
54 | 54 | } |
55 | 55 | |
56 | 56 | $schema->populate('Track',[ |
57 | [qw/cd title/], | |
57 | [qw/cdid title/], | |
58 | 58 | @tracks, |
59 | 59 | ]); |
52 | 52 | } |
53 | 53 | ); |
54 | 54 | while (my $track = $rs->next) { |
55 | print $track->title . "\n"; | |
55 | print $track->title . " (from the CD '" . $track->cd->title | |
56 | . "')\n"; | |
56 | 57 | } |
57 | 58 | print "\n"; |
58 | 59 | } |
69 | 70 | } |
70 | 71 | ); |
71 | 72 | my $cd = $rs->first; |
72 | print $cd->title . "\n\n"; | |
73 | print $cd->title . " has the track '$tracktitle'.\n\n"; | |
73 | 74 | } |
74 | 75 | |
75 | 76 | sub get_cds_by_artist { |
103 | 104 | } |
104 | 105 | ); |
105 | 106 | my $artist = $rs->first; |
106 | print $artist->name . "\n\n"; | |
107 | print $artist->name . " recorded the track '$tracktitle'.\n\n"; | |
107 | 108 | } |
108 | 109 | |
109 | 110 | sub get_artist_by_cd { |
118 | 119 | } |
119 | 120 | ); |
120 | 121 | my $artist = $rs->first; |
121 | print $artist->name . "\n\n"; | |
122 | print $artist->name . " recorded the CD '$cdtitle'.\n\n"; | |
122 | 123 | } |
2 | 2 | |
3 | 3 | use strict; |
4 | 4 | use warnings; |
5 | use DBIx::Class::_Util 'detected_reinvoked_destructor'; | |
6 | use namespace::clean; | |
5 | 7 | |
6 | 8 | sub DESTROY { |
9 | return if &detected_reinvoked_destructor; | |
10 | ||
7 | 11 | my ($self) = @_; |
8 | 12 | my $class = ref $self; |
9 | 13 | warn "$class $self destroyed without saving changes to " |
1769 | 1769 | =head2 Formatting DateTime objects in queries |
1770 | 1770 | |
1771 | 1771 | To ensure C<WHERE> conditions containing L<DateTime> arguments are properly |
1772 | formatted to be understood by your RDBMS, you must use the C<DateTime> | |
1772 | formatted to be understood by your RDBMS, you must use the L<DateTime> | |
1773 | 1773 | formatter returned by L<DBIx::Class::Storage::DBI/datetime_parser> to format |
1774 | 1774 | any L<DateTime> objects you pass to L<search|DBIx::Class::ResultSet/search> |
1775 | 1775 | conditions. Any L<Storage|DBIx::Class::Storage> object attached to your |
1776 | L<Schema|DBIx::Class::Schema> provides a correct C<DateTime> formatter, so | |
1776 | L<Schema|DBIx::Class::Schema> provides a correct L<DateTime> formatter, so | |
1777 | 1777 | all you have to do is: |
1778 | 1778 | |
1779 | 1779 | my $dtf = $schema->storage->datetime_parser; |
1792 | 1792 | C<DateTime> object, which almost never matches the RDBMS expectations. |
1793 | 1793 | |
1794 | 1794 | This kludge is necessary only for conditions passed to |
1795 | L<DBIx::Class::ResultSet/search>, whereas | |
1796 | L<create|DBIx::Class::ResultSet/create>, | |
1797 | L<find|DBIx::Class::ResultSet/find>, | |
1798 | L<DBIx::Class::Row/update> (but not L<DBIx::Class::ResultSet/update>) are all | |
1795 | L<search|DBIx::Class::ResultSet/search> and L<DBIx::Class::ResultSet/find>, | |
1796 | whereas L<create|DBIx::Class::ResultSet/create> and | |
1797 | L<DBIx::Class::Row/update> (but not L<DBIx::Class::ResultSet/update>) are | |
1799 | 1798 | L<DBIx::Class::InflateColumn>-aware and will do the right thing when supplied |
1800 | an inflated C<DateTime> object. | |
1799 | an inflated L<DateTime> object. | |
1801 | 1800 | |
1802 | 1801 | =head2 Using Unicode |
1803 | 1802 |
7 | 7 | testing a very basic CD database using SQLite, with DBIx::Class::Schema |
8 | 8 | as the database frontend. |
9 | 9 | |
10 | The database consists of the following: | |
10 | The database structure is based on the following rules: | |
11 | ||
12 | An artist can have many cds, and each cd belongs to just one artist. | |
13 | A cd can have many tracks, and each track belongs to just one cd. | |
14 | ||
15 | The database is implemented with the following: | |
11 | 16 | |
12 | 17 | table 'artist' with columns: artistid, name |
13 | table 'cd' with columns: cdid, artist, title, year | |
14 | table 'track' with columns: trackid, cd, title | |
18 | table 'cd' with columns: cdid, artistid, title, year | |
19 | table 'track' with columns: trackid, cdid, title | |
15 | 20 | |
16 | ||
17 | And these rules exists: | |
18 | ||
19 | one artist can have many cds | |
20 | one cd belongs to one artist | |
21 | one cd can have many tracks | |
22 | one track belongs to one cd | |
23 | ||
21 | Each of the table's first columns is the primary key; any subsequent | |
22 | keys are foreign keys. | |
24 | 23 | |
25 | 24 | =head2 Installation |
26 | 25 | |
27 | Install DBIx::Class via CPAN should be sufficient. | |
26 | You'll need to install DBIx::Class via CPAN, and you'll also need to | |
27 | install sqlite3 (not sqlite) if it's not already intalled. | |
28 | 28 | |
29 | =head3 Create the database/tables | |
29 | =head3 The database/tables/data | |
30 | 30 | |
31 | First make and change the directory: | |
31 | Your distribution already comes with a pre-filled SQLite database | |
32 | F<examples/Schema/db/example.db>. You can see it by e.g. | |
32 | 33 | |
33 | mkdir app | |
34 | cd app | |
35 | mkdir db | |
36 | cd db | |
34 | cpanm --look DBIx::Class | |
37 | 35 | |
38 | This example uses SQLite which is a dependency of DBIx::Class, so you | |
39 | shouldn't have to install extra software. | |
36 | If for some reason the file is unreadable on your system, you can | |
37 | recreate it as follows: | |
40 | 38 | |
41 | Save the following into a example.sql in the directory db | |
39 | cp -a <unpacked-DBIC-tarball>/examples/Schema dbicapp | |
40 | cd dbicapp | |
41 | rm db/example.db | |
42 | sqlite3 db/example.db < db/example.sql | |
43 | perl insertdb.pl | |
42 | 44 | |
43 | CREATE TABLE artist ( | |
44 | artistid INTEGER PRIMARY KEY, | |
45 | name TEXT NOT NULL | |
46 | ); | |
45 | =head3 Testing the database | |
47 | 46 | |
48 | CREATE TABLE cd ( | |
49 | cdid INTEGER PRIMARY KEY, | |
50 | artist INTEGER NOT NULL REFERENCES artist(artistid), | |
51 | title TEXT NOT NULL | |
52 | ); | |
47 | Enter the example Schema directory | |
53 | 48 | |
54 | CREATE TABLE track ( | |
55 | trackid INTEGER PRIMARY KEY, | |
56 | cd INTEGER NOT NULL REFERENCES cd(cdid), | |
57 | title TEXT NOT NULL | |
58 | ); | |
49 | cd <unpacked-DBIC-tarball>/examples/Schema | |
59 | 50 | |
60 | and create the SQLite database file: | |
51 | Run the script testdb.pl, which will test that the database has | |
52 | successfully been filled. | |
61 | 53 | |
62 | sqlite3 example.db < example.sql | |
54 | When this script is run, it should output the following: | |
63 | 55 | |
64 | =head3 Set up DBIx::Class::Schema | |
56 | get_tracks_by_cd(Bad): | |
57 | Leave Me Alone | |
58 | Smooth Criminal | |
59 | Dirty Diana | |
65 | 60 | |
66 | Change directory back from db to the directory app: | |
61 | get_tracks_by_artist(Michael Jackson): | |
62 | Billie Jean (from the CD 'Thriller') | |
63 | Beat It (from the CD 'Thriller') | |
64 | Leave Me Alone (from the CD 'Bad') | |
65 | Smooth Criminal (from the CD 'Bad') | |
66 | Dirty Diana (from the CD 'Bad') | |
67 | 67 | |
68 | cd ../ | |
68 | get_cd_by_track(Stan): | |
69 | The Marshall Mathers LP has the track 'Stan'. | |
69 | 70 | |
70 | Now create some more directories: | |
71 | get_cds_by_artist(Michael Jackson): | |
72 | Thriller | |
73 | Bad | |
71 | 74 | |
72 | mkdir MyApp | |
73 | mkdir MyApp/Schema | |
74 | mkdir MyApp/Schema/Result | |
75 | mkdir MyApp/Schema/ResultSet | |
75 | get_artist_by_track(Dirty Diana): | |
76 | Michael Jackson recorded the track 'Dirty Diana'. | |
76 | 77 | |
77 | Then, create the following DBIx::Class::Schema classes: | |
78 | ||
79 | MyApp/Schema.pm: | |
80 | ||
81 | package MyApp::Schema; | |
82 | use base qw/DBIx::Class::Schema/; | |
83 | __PACKAGE__->load_namespaces; | |
84 | ||
85 | 1; | |
78 | get_artist_by_cd(The Marshall Mathers LP): | |
79 | Eminem recorded the CD 'The Marshall Mathers LP'. | |
86 | 80 | |
87 | 81 | |
88 | MyApp/Schema/Result/Artist.pm: | |
82 | =head3 Discussion about the results | |
89 | 83 | |
90 | package MyApp::Schema::Result::Artist; | |
91 | use base qw/DBIx::Class::Core/; | |
92 | __PACKAGE__->table('artist'); | |
93 | __PACKAGE__->add_columns(qw/ artistid name /); | |
94 | __PACKAGE__->set_primary_key('artistid'); | |
95 | __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd'); | |
84 | The data model defined in this example has an artist with multiple CDs, | |
85 | and a CD with multiple tracks; thus, it's simple to traverse from a | |
86 | track back to a CD, and from there back to an artist. This is | |
87 | demonstrated in the get_tracks_by_artist routine, where we easily walk | |
88 | from the individual track back to the title of the CD that the track | |
89 | came from ($track->cd->title). | |
96 | 90 | |
97 | 1; | |
98 | ||
99 | ||
100 | MyApp/Schema/Result/Cd.pm: | |
101 | ||
102 | package MyApp::Schema::Result::Cd; | |
103 | use base qw/DBIx::Class::Core/; | |
104 | __PACKAGE__->load_components(qw/InflateColumn::DateTime/); | |
105 | __PACKAGE__->table('cd'); | |
106 | __PACKAGE__->add_columns(qw/ cdid artist title year/); | |
107 | __PACKAGE__->set_primary_key('cdid'); | |
108 | __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist'); | |
109 | __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track'); | |
110 | ||
111 | 1; | |
112 | ||
113 | ||
114 | MyApp/Schema/Result/Track.pm: | |
115 | ||
116 | package MyApp::Schema::Result::Track; | |
117 | use base qw/DBIx::Class::Core/; | |
118 | __PACKAGE__->table('track'); | |
119 | __PACKAGE__->add_columns(qw/ trackid cd title /); | |
120 | __PACKAGE__->set_primary_key('trackid'); | |
121 | __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd'); | |
122 | ||
123 | 1; | |
124 | ||
125 | ||
126 | =head3 Write a script to insert some records | |
127 | ||
128 | insertdb.pl | |
129 | ||
130 | #!/usr/bin/perl | |
131 | ||
132 | use strict; | |
133 | use warnings; | |
134 | ||
135 | use MyApp::Schema; | |
136 | ||
137 | my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db'); | |
138 | ||
139 | my @artists = (['Michael Jackson'], ['Eminem']); | |
140 | $schema->populate('Artist', [ | |
141 | [qw/name/], | |
142 | @artists, | |
143 | ]); | |
144 | ||
145 | my %albums = ( | |
146 | 'Thriller' => 'Michael Jackson', | |
147 | 'Bad' => 'Michael Jackson', | |
148 | 'The Marshall Mathers LP' => 'Eminem', | |
149 | ); | |
150 | ||
151 | my @cds; | |
152 | foreach my $lp (keys %albums) { | |
153 | my $artist = $schema->resultset('Artist')->find({ | |
154 | name => $albums{$lp} | |
155 | }); | |
156 | push @cds, [$lp, $artist->id]; | |
157 | } | |
158 | ||
159 | $schema->populate('Cd', [ | |
160 | [qw/title artist/], | |
161 | @cds, | |
162 | ]); | |
163 | ||
164 | ||
165 | my %tracks = ( | |
166 | 'Beat It' => 'Thriller', | |
167 | 'Billie Jean' => 'Thriller', | |
168 | 'Dirty Diana' => 'Bad', | |
169 | 'Smooth Criminal' => 'Bad', | |
170 | 'Leave Me Alone' => 'Bad', | |
171 | 'Stan' => 'The Marshall Mathers LP', | |
172 | 'The Way I Am' => 'The Marshall Mathers LP', | |
173 | ); | |
174 | ||
175 | my @tracks; | |
176 | foreach my $track (keys %tracks) { | |
177 | my $cdname = $schema->resultset('Cd')->find({ | |
178 | title => $tracks{$track}, | |
179 | }); | |
180 | push @tracks, [$cdname->id, $track]; | |
181 | } | |
182 | ||
183 | $schema->populate('Track',[ | |
184 | [qw/cd title/], | |
185 | @tracks, | |
186 | ]); | |
187 | ||
188 | =head3 Create and run the test scripts | |
189 | ||
190 | testdb.pl: | |
191 | ||
192 | #!/usr/bin/perl | |
193 | ||
194 | use strict; | |
195 | use warnings; | |
196 | ||
197 | use MyApp::Schema; | |
198 | ||
199 | my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db'); | |
200 | # for other DSNs, e.g. MySQL, see the perldoc for the relevant dbd | |
201 | # driver, e.g perldoc L<DBD::mysql>. | |
202 | ||
203 | get_tracks_by_cd('Bad'); | |
204 | get_tracks_by_artist('Michael Jackson'); | |
205 | ||
206 | get_cd_by_track('Stan'); | |
207 | get_cds_by_artist('Michael Jackson'); | |
208 | ||
209 | get_artist_by_track('Dirty Diana'); | |
210 | get_artist_by_cd('The Marshall Mathers LP'); | |
211 | ||
212 | ||
213 | sub get_tracks_by_cd { | |
214 | my $cdtitle = shift; | |
215 | print "get_tracks_by_cd($cdtitle):\n"; | |
216 | my $rs = $schema->resultset('Track')->search( | |
217 | { | |
218 | 'cd.title' => $cdtitle | |
219 | }, | |
220 | { | |
221 | join => [qw/ cd /], | |
222 | } | |
223 | ); | |
224 | while (my $track = $rs->next) { | |
225 | print $track->title . "\n"; | |
226 | } | |
227 | print "\n"; | |
228 | } | |
229 | ||
230 | sub get_tracks_by_artist { | |
231 | my $artistname = shift; | |
232 | print "get_tracks_by_artist($artistname):\n"; | |
233 | my $rs = $schema->resultset('Track')->search( | |
234 | { | |
235 | 'artist.name' => $artistname | |
236 | }, | |
237 | { | |
238 | join => { | |
239 | 'cd' => 'artist' | |
240 | }, | |
241 | } | |
242 | ); | |
243 | while (my $track = $rs->next) { | |
244 | print $track->title . "\n"; | |
245 | } | |
246 | print "\n"; | |
247 | } | |
248 | ||
249 | ||
250 | sub get_cd_by_track { | |
251 | my $tracktitle = shift; | |
252 | print "get_cd_by_track($tracktitle):\n"; | |
253 | my $rs = $schema->resultset('Cd')->search( | |
254 | { | |
255 | 'tracks.title' => $tracktitle | |
256 | }, | |
257 | { | |
258 | join => [qw/ tracks /], | |
259 | } | |
260 | ); | |
261 | my $cd = $rs->first; | |
262 | print $cd->title . "\n\n"; | |
263 | } | |
264 | ||
265 | sub get_cds_by_artist { | |
266 | my $artistname = shift; | |
267 | print "get_cds_by_artist($artistname):\n"; | |
268 | my $rs = $schema->resultset('Cd')->search( | |
269 | { | |
270 | 'artist.name' => $artistname | |
271 | }, | |
272 | { | |
273 | join => [qw/ artist /], | |
274 | } | |
275 | ); | |
276 | while (my $cd = $rs->next) { | |
277 | print $cd->title . "\n"; | |
278 | } | |
279 | print "\n"; | |
280 | } | |
281 | ||
282 | ||
283 | ||
284 | sub get_artist_by_track { | |
285 | my $tracktitle = shift; | |
286 | print "get_artist_by_track($tracktitle):\n"; | |
287 | my $rs = $schema->resultset('Artist')->search( | |
288 | { | |
289 | 'tracks.title' => $tracktitle | |
290 | }, | |
291 | { | |
292 | join => { | |
293 | 'cds' => 'tracks' | |
294 | } | |
295 | } | |
296 | ); | |
297 | my $artist = $rs->first; | |
298 | print $artist->name . "\n\n"; | |
299 | } | |
300 | ||
301 | sub get_artist_by_cd { | |
302 | my $cdtitle = shift; | |
303 | print "get_artist_by_cd($cdtitle):\n"; | |
304 | my $rs = $schema->resultset('Artist')->search( | |
305 | { | |
306 | 'cds.title' => $cdtitle | |
307 | }, | |
308 | { | |
309 | join => [qw/ cds /], | |
310 | } | |
311 | ); | |
312 | my $artist = $rs->first; | |
313 | print $artist->name . "\n\n"; | |
314 | } | |
315 | ||
316 | ||
317 | ||
318 | It should output: | |
319 | ||
320 | get_tracks_by_cd(Bad): | |
321 | Dirty Diana | |
322 | Smooth Criminal | |
323 | Leave Me Alone | |
324 | ||
325 | get_tracks_by_artist(Michael Jackson): | |
326 | Beat it | |
327 | Billie Jean | |
328 | Dirty Diana | |
329 | Smooth Criminal | |
330 | Leave Me Alone | |
331 | ||
332 | get_cd_by_track(Stan): | |
333 | The Marshall Mathers LP | |
334 | ||
335 | get_cds_by_artist(Michael Jackson): | |
336 | Thriller | |
337 | Bad | |
338 | ||
339 | get_artist_by_track(Dirty Diana): | |
340 | Michael Jackson | |
341 | ||
342 | get_artist_by_cd(The Marshall Mathers LP): | |
343 | Eminem | |
344 | ||
345 | =head1 Notes | |
346 | ||
347 | A reference implementation of the database and scripts in this example | |
348 | are available in the main distribution for DBIx::Class under the | |
349 | directory F<examples/Schema>. | |
350 | ||
351 | With these scripts we're relying on @INC looking in the current | |
352 | working directory. You may want to add the MyApp namespaces to | |
353 | @INC in a different way when it comes to deployment. | |
354 | ||
355 | The F<testdb.pl> script is an excellent start for testing your database | |
356 | model. | |
91 | Note also that in the get_tracks_by_cd and get_tracks_by_artist | |
92 | routines, the result set is called multiple times with the 'next' | |
93 | iterator. In contrast, get_cd_by_track uses the 'first' result set | |
94 | method, since only one CD is expected to have a specific track. | |
357 | 95 | |
358 | 96 | This example uses L<DBIx::Class::Schema/load_namespaces> to load in the |
359 | 97 | appropriate L<Result|DBIx::Class::Manual::ResultClass> classes from the |
360 | 98 | C<MyApp::Schema::Result> namespace, and any required |
361 | 99 | L<ResultSet|DBIx::Class::ResultSet> classes from the |
362 | C<MyApp::Schema::ResultSet> namespace (although we created the directory | |
363 | in the directions above we did not add, or need to add, any resultset | |
364 | classes). | |
100 | C<MyApp::Schema::ResultSet> namespace (although we did not add, nor needed | |
101 | any such classes in this example). | |
365 | 102 | |
366 | 103 | =head1 FURTHER QUESTIONS? |
367 | 104 |
81 | 81 | |
82 | 82 | __PACKAGE__->table('mydb.mytablename'); |
83 | 83 | |
84 | And load all the Result classes for both / all databases using one | |
85 | L<DBIx::Class::Schema/load_namespaces> call. | |
84 | And load all the Result classes for both / all databases by calling | |
85 | L<DBIx::Class::Schema/load_namespaces>. | |
86 | 86 | |
87 | 87 | =item .. use DBIx::Class across PostgreSQL/DB2/Oracle schemas? |
88 | 88 | |
261 | 261 | ->on_connect_do("ALTER SESSION SET NLS_SORT = 'BINARY_CI'"); |
262 | 262 | ->on_connect_do("ALTER SESSION SET NLS_SORT = 'GERMAN_CI'"); |
263 | 263 | |
264 | =item .. format a DateTime object for searching? | |
265 | ||
266 | L<search|DBIx::Class::ResultSet/search> and L<find|DBIx::Class::ResultSet/find> | |
267 | do not take L<DBIx::Class::InflateColumn> into account, and so your L<DateTime> | |
268 | object will not be correctly deflated into a format your RDBMS expects. | |
269 | ||
270 | The L<datetime_parser|DBIx::Class::Storage::DBI/datetime_parser> method on your | |
271 | storage object can be used to return the object that would normally do this, so | |
272 | it's easy to do it manually: | |
273 | ||
274 | my $dtf = $schema->storage->datetime_parser; | |
275 | my $rs = $schema->resultset('users')->search( | |
276 | { | |
277 | signup_date => { | |
278 | -between => [ | |
279 | $dtf->format_datetime($dt_start), | |
280 | $dtf->format_datetime($dt_end), | |
281 | ], | |
282 | } | |
283 | }, | |
284 | ); | |
285 | ||
286 | With in a Result Class method, you can get this from the | |
287 | L<C<result_source>|DBIx::Class::Row/result_source>. | |
288 | ||
289 | my $dtf = $self->result_source->storage->datetime_parser; | |
290 | ||
291 | This kludge is necessary only for conditions passed to | |
292 | L<search|DBIx::Class::ResultSet/search> and L<DBIx::Class::ResultSet/find>, | |
293 | whereas L<create|DBIx::Class::ResultSet/create> and L<DBIx::Class::Row/update> | |
294 | (but not L<DBIx::Class::ResultSet/update>) are | |
295 | L<DBIx::Class::InflateColumn>-aware and will do the right thing when supplied | |
296 | an inflated L<DateTime> object. | |
264 | 297 | |
265 | 298 | =back |
266 | 299 |
17 | 17 | |
18 | 18 | ... |
19 | 19 | |
20 | configure_requires 'DBIx::Class' => '0.082810'; | |
20 | configure_requires 'DBIx::Class' => '0.082820'; | |
21 | 21 | |
22 | 22 | require DBIx::Class::Optional::Dependencies; |
23 | 23 |
3322 | 3322 | }); |
3323 | 3323 | } |
3324 | 3324 | |
3325 | The alias of L<newly created resultsets|/search> can be altered by the | |
3326 | L<alias attribute|/alias>. | |
3327 | ||
3325 | 3328 | =cut |
3326 | 3329 | |
3327 | 3330 | sub current_source_alias { |
3815 | 3818 | |
3816 | 3819 | if (ref $b eq 'HASH') { |
3817 | 3820 | my ($b_key) = keys %{$b}; |
3821 | $b_key = '' if ! defined $b_key; | |
3818 | 3822 | if (ref $a eq 'HASH') { |
3819 | 3823 | my ($a_key) = keys %{$a}; |
3824 | $a_key = '' if ! defined $a_key; | |
3820 | 3825 | if ($a_key eq $b_key) { |
3821 | 3826 | return (1 + $self->_calculate_score( $a->{$a_key}, $b->{$b_key} )); |
3822 | 3827 | } else { |
4083 | 4088 | as => [qw(some_column dbic_slot)] |
4084 | 4089 | |
4085 | 4090 | If you want to individually retrieve related columns (in essence perform |
4086 | manual prefetch) you have to make sure to specify the correct inflation slot | |
4091 | manual L</prefetch>) you have to make sure to specify the correct inflation slot | |
4087 | 4092 | chain such that it matches existing relationships: |
4088 | 4093 | |
4089 | 4094 | my $rs = $schema->resultset('Artist')->search({}, { |
4090 | 4095 | # required to tell DBIC to collapse has_many relationships |
4091 | 4096 | collapse => 1, |
4092 | join => { cds => 'tracks'}, | |
4097 | join => { cds => 'tracks' }, | |
4093 | 4098 | '+columns' => { |
4094 | 4099 | 'cds.cdid' => 'cds.cdid', |
4095 | 4100 | 'cds.tracks.title' => 'tracks.title', |
209 | 209 | The length of your column, if it is a column type that can have a size |
210 | 210 | restriction. This is currently only used to create tables from your |
211 | 211 | schema, see L<DBIx::Class::Schema/deploy>. |
212 | ||
213 | { size => [ 9, 6 ] } | |
214 | ||
215 | For decimal or float values you can specify an ArrayRef in order to | |
216 | control precision, assuming your database's | |
217 | L<SQL::Translator::Producer> supports it. | |
212 | 218 | |
213 | 219 | =item is_nullable |
214 | 220 | |
1903 | 1909 | |
1904 | 1910 | $args->{condition} ||= $rel_info->{cond}; |
1905 | 1911 | |
1906 | $self->throw_exception( "Argument 'self_result_object' must be an object of class '@{[ $self->result_class ]}'" ) | |
1912 | $self->throw_exception( "Argument 'self_result_object' must be an object inheriting from DBIx::Class::Row" ) | |
1907 | 1913 | if ( |
1908 | 1914 | exists $args->{self_result_object} |
1909 | 1915 | and |
1910 | ( ! defined blessed $args->{self_result_object} or ! $args->{self_result_object}->isa($self->result_class) ) | |
1916 | ( ! defined blessed $args->{self_result_object} or ! $args->{self_result_object}->isa('DBIx::Class::Row') ) | |
1911 | 1917 | ) |
1912 | 1918 | ; |
1913 | 1919 | |
2305 | 2311 | |
2306 | 2312 | my $global_phase_destroy; |
2307 | 2313 | sub DESTROY { |
2314 | ### NO detected_reinvoked_destructor check | |
2315 | ### This code very much relies on being called multuple times | |
2316 | ||
2308 | 2317 | return if $global_phase_destroy ||= in_global_destruction; |
2309 | 2318 | |
2310 | 2319 | ###### |
1212 | 1212 | sub thaw { |
1213 | 1213 | my ($self, $obj) = @_; |
1214 | 1214 | local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; |
1215 | require Storable; | |
1216 | 1215 | return Storable::thaw($obj); |
1217 | 1216 | } |
1218 | 1217 | |
1224 | 1223 | =cut |
1225 | 1224 | |
1226 | 1225 | sub freeze { |
1227 | require Storable; | |
1228 | 1226 | return Storable::nfreeze($_[1]); |
1229 | 1227 | } |
1230 | 1228 | |
1247 | 1245 | sub dclone { |
1248 | 1246 | my ($self, $obj) = @_; |
1249 | 1247 | local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; |
1250 | require Storable; | |
1251 | 1248 | return Storable::dclone($obj); |
1252 | 1249 | } |
1253 | 1250 | |
1387 | 1384 | |
1388 | 1385 | my $global_phase_destroy; |
1389 | 1386 | sub DESTROY { |
1387 | ### NO detected_reinvoked_destructor check | |
1388 | ### This code very much relies on being called multuple times | |
1389 | ||
1390 | 1390 | return if $global_phase_destroy ||= in_global_destruction; |
1391 | 1391 | |
1392 | 1392 | my $self = shift; |
9 | 9 | use DBIx::Class::_Util qw(is_exception qsub); |
10 | 10 | use Scalar::Util qw(weaken blessed reftype); |
11 | 11 | use Try::Tiny; |
12 | ||
13 | # DO NOT edit away without talking to riba first, he will just put it back | |
14 | # BEGIN pre-Moo2 import block | |
15 | BEGIN { | |
16 | my $initial_fatal_bits = (${^WARNING_BITS}||'') & $warnings::DeadBits{all}; | |
17 | ||
18 | local $ENV{PERL_STRICTURES_EXTRA} = 0; | |
19 | # load all of these now, so that lazy-loading does not escape | |
20 | # the current PERL_STRICTURES_EXTRA setting | |
21 | require Sub::Quote; | |
22 | require Sub::Defer; | |
23 | require Moo; | |
24 | require Moo::Object; | |
25 | require Method::Generate::Accessor; | |
26 | require Method::Generate::Constructor; | |
27 | ||
28 | Moo->import; | |
29 | ${^WARNING_BITS} &= ( $initial_fatal_bits | ~ $warnings::DeadBits{all} ); | |
30 | } | |
31 | # END pre-Moo2 import block | |
32 | ||
12 | use Moo; | |
33 | 13 | use namespace::clean; |
34 | 14 | |
35 | 15 | =head1 NAME |
7 | 7 | use Try::Tiny; |
8 | 8 | use Scalar::Util qw(refaddr weaken); |
9 | 9 | use List::Util 'shuffle'; |
10 | use DBIx::Class::_Util 'detected_reinvoked_destructor'; | |
10 | 11 | use namespace::clean; |
11 | 12 | |
12 | 13 | __PACKAGE__->mk_group_accessors('simple' => |
232 | 233 | |
233 | 234 | |
234 | 235 | sub DESTROY { |
236 | return if &detected_reinvoked_destructor; | |
237 | ||
235 | 238 | $_[0]->__finish_sth if $_[0]->{sth}; |
236 | 239 | } |
237 | 240 |
418 | 418 | |
419 | 419 | my $attrs = $self->next::method($ident, $bind); |
420 | 420 | |
421 | for my $i (0 .. $#$attrs) { | |
422 | if (keys %{$attrs->[$i]||{}} and my $col = $bind->[$i][0]{dbic_colname}) { | |
423 | $attrs->[$i]{ora_field} = $col; | |
424 | } | |
425 | } | |
421 | # Push the column name into all bind attrs, make sure to *NOT* write into | |
422 | # the existing $attrs->[$idx]{..} hashref, as it is cached by the call to | |
423 | # next::method above. | |
424 | $attrs->[$_] | |
425 | and | |
426 | keys %{ $attrs->[$_] } | |
427 | and | |
428 | $bind->[$_][0]{dbic_colname} | |
429 | and | |
430 | $attrs->[$_] = { %{$attrs->[$_]}, ora_field => $bind->[$_][0]{dbic_colname} } | |
431 | for 0 .. $#$attrs; | |
426 | 432 | |
427 | 433 | $attrs; |
428 | 434 | } |
306 | 306 | _parse_connect_do |
307 | 307 | savepoints |
308 | 308 | _sql_maker_opts |
309 | _use_multicolumn_in | |
309 | 310 | _conn_pid |
310 | 311 | _dbh_autocommit |
311 | 312 | _native_data_type |
362 | 363 | # the capability framework |
363 | 364 | # not sure if CMOP->initialize does evil things to DBIC::S::DBI, fix if a problem |
364 | 365 | grep |
365 | { $_ =~ /^ _ (?: use | supports | determine_supports ) _ /x } | |
366 | { $_ =~ /^ _ (?: use | supports | determine_supports ) _ /x and $_ ne '_use_multicolumn_in' } | |
366 | 367 | ( Class::MOP::Class->initialize('DBIx::Class::Storage::DBI')->get_all_method_names ) |
367 | 368 | )], |
368 | 369 | }; |
60 | 60 | |
61 | 61 | Even if you upgrade DBIx::Class (which works around the bug starting from |
62 | 62 | version 0.08210) you may still have corrupted/incorrect data in your database. |
63 | DBIx::Class will currently detect when this condition (more than one | |
64 | stringifiable object in one CRUD call) is encountered and will issue a warning | |
65 | pointing to this section. This warning will be removed 2 years from now, | |
66 | around April 2015, You can disable it after you've audited your data by | |
67 | setting the C<DBIC_RT79576_NOWARN> environment variable. Note - the warning | |
68 | is emitted only once per callsite per process and only when the condition in | |
69 | question is encountered. Thus it is very unlikely that your logsystem will be | |
70 | flooded as a result of this. | |
63 | DBIx::Class warned about this condition for several years, hoping to give | |
64 | anyone affected sufficient notice of the potential issues. The warning was | |
65 | removed in version 0.082900. | |
71 | 66 | |
72 | 67 | =back |
73 | 68 | |
316 | 311 | = modver_gt_or_eq('DBD::SQLite', '1.37') ? 1 : 0; |
317 | 312 | } |
318 | 313 | |
319 | # an attempt to detect former effects of RT#79576, bug itself present between | |
320 | # 0.08191 and 0.08209 inclusive (fixed in 0.08210 and higher) | |
321 | my $stringifiable = 0; | |
322 | ||
323 | 314 | for my $i (0.. $#$bindattrs) { |
324 | ||
325 | $stringifiable++ if ( length ref $bind->[$i][1] and is_plain_value($bind->[$i][1]) ); | |
326 | ||
327 | 315 | if ( |
328 | 316 | defined $bindattrs->[$i] |
329 | 317 | and |
366 | 354 | } |
367 | 355 | } |
368 | 356 | |
369 | carp_unique( | |
370 | 'POSSIBLE *PAST* DATA CORRUPTION detected - see ' | |
371 | . 'DBIx::Class::Storage::DBI::SQLite/RT79576 or ' | |
372 | . 'http://v.gd/DBIC_SQLite_RT79576 for further details or set ' | |
373 | . '$ENV{DBIC_RT79576_NOWARN} to disable this warning. Trigger ' | |
374 | . 'condition encountered' | |
375 | ) if (!$ENV{DBIC_RT79576_NOWARN} and $stringifiable > 1); | |
376 | ||
377 | 357 | return $bindattrs; |
378 | 358 | } |
379 | 359 |
12 | 12 | use Context::Preserve 'preserve_context'; |
13 | 13 | use Try::Tiny; |
14 | 14 | use SQL::Abstract qw(is_plain_value is_literal_value); |
15 | use DBIx::Class::_Util qw(quote_sub perlstring serialize); | |
15 | use DBIx::Class::_Util qw(quote_sub perlstring serialize detected_reinvoked_destructor); | |
16 | 16 | use namespace::clean; |
17 | 17 | |
18 | 18 | # default cursor class, overridable in connect_info attributes |
252 | 252 | } |
253 | 253 | |
254 | 254 | sub DESTROY { |
255 | return if &detected_reinvoked_destructor; | |
256 | ||
255 | 257 | $_[0]->_verify_pid unless DBIx::Class::_ENV_::BROKEN_FORK; |
256 | 258 | # some databases spew warnings on implicit disconnect |
257 | 259 | local $SIG{__WARN__} = sub {}; |
1666 | 1668 | ) { |
1667 | 1669 | carp_unique 'DateTime objects passed to search() are not supported ' |
1668 | 1670 | . 'properly (InflateColumn::DateTime formats and settings are not ' |
1669 | . 'respected.) See "Formatting DateTime objects in queries" in ' | |
1670 | . 'DBIx::Class::Manual::Cookbook. To disable this warning for good ' | |
1671 | . 'respected.) See ".. format a DateTime object for searching?" in ' | |
1672 | . 'DBIx::Class::Manual::FAQ. To disable this warning for good ' | |
1671 | 1673 | . 'set $ENV{DBIC_DT_SEARCH_OK} to true' |
1672 | 1674 | } |
1673 | 1675 |
4 | 4 | |
5 | 5 | use DBIx::Class::_Util qw(sigwarn_silencer qsub); |
6 | 6 | use IO::Handle (); |
7 | ||
8 | # DO NOT edit away without talking to riba first, he will just put it back | |
9 | # BEGIN pre-Moo2 import block | |
10 | BEGIN { | |
11 | my $initial_fatal_bits = (${^WARNING_BITS}||'') & $warnings::DeadBits{all}; | |
12 | ||
13 | local $ENV{PERL_STRICTURES_EXTRA} = 0; | |
14 | # load all of these now, so that lazy-loading does not escape | |
15 | # the current PERL_STRICTURES_EXTRA setting | |
16 | require Sub::Quote; | |
17 | require Sub::Defer; | |
18 | require Moo; | |
19 | require Moo::Object; | |
20 | require Method::Generate::Accessor; | |
21 | require Method::Generate::Constructor; | |
22 | ||
23 | Moo->import; | |
24 | ${^WARNING_BITS} &= ( $initial_fatal_bits | ~ $warnings::DeadBits{all} ); | |
25 | } | |
26 | # END pre-Moo2 import block | |
27 | ||
7 | use Moo; | |
28 | 8 | extends 'DBIx::Class'; |
29 | 9 | use namespace::clean; |
30 | 10 |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | use Try::Tiny; |
5 | use Scalar::Util qw/weaken blessed refaddr/; | |
5 | use Scalar::Util qw(weaken blessed refaddr); | |
6 | 6 | use DBIx::Class; |
7 | use DBIx::Class::_Util 'is_exception'; | |
7 | use DBIx::Class::_Util qw(is_exception detected_reinvoked_destructor); | |
8 | 8 | use DBIx::Class::Carp; |
9 | 9 | use namespace::clean; |
10 | 10 | |
49 | 49 | } |
50 | 50 | |
51 | 51 | sub DESTROY { |
52 | return if &detected_reinvoked_destructor; | |
53 | ||
52 | 54 | my $self = shift; |
53 | 55 | |
54 | 56 | return if $self->{inactivated}; |
54 | 54 | # Carp::Skip to the rescue soon |
55 | 55 | use DBIx::Class::Carp '^DBIx::Class|^DBICTest'; |
56 | 56 | |
57 | use B (); | |
57 | 58 | use Carp 'croak'; |
58 | use Scalar::Util qw(weaken blessed reftype); | |
59 | use Storable 'nfreeze'; | |
60 | use Scalar::Util qw(weaken blessed reftype refaddr); | |
59 | 61 | use List::Util qw(first); |
60 | ||
61 | # DO NOT edit away without talking to riba first, he will just put it back | |
62 | # BEGIN pre-Moo2 import block | |
63 | BEGIN { | |
64 | my $initial_fatal_bits = (${^WARNING_BITS}||'') & $warnings::DeadBits{all}; | |
65 | ||
66 | local $ENV{PERL_STRICTURES_EXTRA} = 0; | |
67 | # load all of these now, so that lazy-loading does not escape | |
68 | # the current PERL_STRICTURES_EXTRA setting | |
69 | require Sub::Quote; | |
70 | require Sub::Defer; | |
71 | ||
72 | Sub::Quote->import('quote_sub'); | |
73 | ${^WARNING_BITS} &= ( $initial_fatal_bits | ~ $warnings::DeadBits{all} ); | |
74 | } | |
75 | sub qsub ($) { goto "e_sub } # no point depping on new Moo just for this | |
76 | # END pre-Moo2 import block | |
62 | use Sub::Quote qw(qsub quote_sub); | |
77 | 63 | |
78 | 64 | use base 'Exporter'; |
79 | 65 | our @EXPORT_OK = qw( |
80 | sigwarn_silencer modver_gt_or_eq | |
66 | sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt | |
81 | 67 | fail_on_internal_wantarray fail_on_internal_call |
82 | refdesc refcount hrefaddr is_exception | |
68 | refdesc refcount hrefaddr is_exception detected_reinvoked_destructor | |
83 | 69 | quote_sub qsub perlstring serialize |
84 | 70 | UNRESOLVABLE_CONDITION |
85 | 71 | ); |
98 | 84 | |
99 | 85 | sub perlstring ($) { q{"}. quotemeta( shift ). q{"} }; |
100 | 86 | |
101 | sub hrefaddr ($) { sprintf '0x%x', &Scalar::Util::refaddr||0 } | |
87 | sub hrefaddr ($) { sprintf '0x%x', &refaddr||0 } | |
102 | 88 | |
103 | 89 | sub refdesc ($) { |
104 | 90 | croak "Expecting a reference" if ! length ref $_[0]; |
108 | 94 | sprintf '%s%s(0x%x)', |
109 | 95 | ( defined( $_[1] = blessed $_[0]) ? "$_[1]=" : '' ), |
110 | 96 | reftype $_[0], |
111 | Scalar::Util::refaddr($_[0]), | |
97 | refaddr($_[0]), | |
112 | 98 | ; |
113 | 99 | } |
114 | 100 | |
115 | 101 | sub refcount ($) { |
116 | 102 | croak "Expecting a reference" if ! length ref $_[0]; |
117 | 103 | |
118 | require B; | |
119 | 104 | # No tempvars - must operate on $_[0], otherwise the pad |
120 | 105 | # will count as an extra ref |
121 | 106 | B::svref_2object($_[0])->REFCNT; |
122 | 107 | } |
123 | 108 | |
124 | 109 | sub serialize ($) { |
125 | require Storable; | |
126 | 110 | local $Storable::canonical = 1; |
127 | Storable::nfreeze($_[0]); | |
111 | nfreeze($_[0]); | |
128 | 112 | } |
129 | 113 | |
130 | 114 | sub is_exception ($) { |
179 | 163 | return $not_blank; |
180 | 164 | } |
181 | 165 | |
166 | { | |
167 | my $destruction_registry = {}; | |
168 | ||
169 | sub CLONE { | |
170 | $destruction_registry = { map | |
171 | { defined $_ ? ( refaddr($_) => $_ ) : () } | |
172 | values %$destruction_registry | |
173 | }; | |
174 | } | |
175 | ||
176 | # This is almost invariably invoked from within DESTROY | |
177 | # throwing exceptions won't work | |
178 | sub detected_reinvoked_destructor { | |
179 | ||
180 | # quick "garbage collection" pass - prevents the registry | |
181 | # from slowly growing with a bunch of undef-valued keys | |
182 | defined $destruction_registry->{$_} or delete $destruction_registry->{$_} | |
183 | for keys %$destruction_registry; | |
184 | ||
185 | if (! length ref $_[0]) { | |
186 | printf STDERR '%s() expects a blessed reference %s', | |
187 | (caller(0))[3], | |
188 | Carp::longmess, | |
189 | ; | |
190 | return undef; # don't know wtf to do | |
191 | } | |
192 | elsif (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) { | |
193 | weaken( $destruction_registry->{$addr} = $_[0] ); | |
194 | return 0; | |
195 | } | |
196 | else { | |
197 | carp_unique ( sprintf ( | |
198 | 'Preventing *MULTIPLE* DESTROY() invocations on %s - an *EXTREMELY ' | |
199 | . 'DANGEROUS* condition which is *ALMOST CERTAINLY GLOBAL* within your ' | |
200 | . 'application, affecting *ALL* classes without active protection against ' | |
201 | . 'this. Diagnose and fix the root cause ASAP!!!%s', | |
202 | refdesc $_[0], | |
203 | ( ( $INC{'Devel/StackTrace.pm'} and ! do { local $@; eval { Devel::StackTrace->VERSION(2) } } ) | |
204 | ? " (likely culprit Devel::StackTrace\@@{[ Devel::StackTrace->VERSION ]} found in %INC, http://is.gd/D_ST_refcap)" | |
205 | : '' | |
206 | ) | |
207 | )); | |
208 | ||
209 | return 1; | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
182 | 214 | sub modver_gt_or_eq ($$) { |
183 | 215 | my ($mod, $ver) = @_; |
184 | 216 | |
196 | 228 | |
197 | 229 | local $@; |
198 | 230 | eval { $mod->VERSION($ver) } ? 1 : 0; |
231 | } | |
232 | ||
233 | sub modver_gt_or_eq_and_lt ($$$) { | |
234 | my ($mod, $v_ge, $v_lt) = @_; | |
235 | ||
236 | croak "Nonsensical maximum version supplied" | |
237 | if ! defined $v_lt or $v_lt =~ /[^0-9\.\_]/; | |
238 | ||
239 | return ( | |
240 | modver_gt_or_eq($mod, $v_ge) | |
241 | and | |
242 | ! modver_gt_or_eq($mod, $v_lt) | |
243 | ) ? 1 : 0; | |
199 | 244 | } |
200 | 245 | |
201 | 246 | { |
10 | 10 | # $VERSION declaration must stay up here, ahead of any other package |
11 | 11 | # declarations, as to not confuse various modules attempting to determine |
12 | 12 | # this ones version, whether that be s.c.o. or Module::Metadata, etc |
13 | $VERSION = '0.082810'; | |
13 | $VERSION = '0.082820'; | |
14 | 14 | |
15 | 15 | $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases |
16 | 16 | |
25 | 25 | |
26 | 26 | __PACKAGE__->mk_group_accessors(inherited => '_skip_namespace_frames'); |
27 | 27 | __PACKAGE__->_skip_namespace_frames('^DBIx::Class|^SQL::Abstract|^Try::Tiny|^Class::Accessor::Grouped|^Context::Preserve'); |
28 | ||
29 | # FIXME - this is not really necessary, and is in | |
30 | # fact going to slow things down a bit | |
31 | # However it is the right thing to do in order to get | |
32 | # various install bases to highlight their brokenness | |
33 | # Remove at some unknown point in the future | |
34 | sub DESTROY { &DBIx::Class::_Util::detected_reinvoked_destructor } | |
28 | 35 | |
29 | 36 | sub mk_classdata { |
30 | 37 | shift->mk_classaccessor(@_); |
278 | 285 | =item * Travis-CI log: L<https://travis-ci.org/dbsrgits/dbix-class/builds> |
279 | 286 | |
280 | 287 | =for html |
281 | ↪ Stable branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=master"></img> | |
288 | ↪ Main dev branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=current/blead"></img> | |
289 | ↪ Release branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=current/for_cpan_index"></img> | |
282 | 290 | |
283 | 291 | =back |
284 | 292 |
210 | 210 | =item * Travis-CI log: L<https://travis-ci.org/dbsrgits/dbix-class/builds> |
211 | 211 | |
212 | 212 | =for html |
213 | ↪ Stable branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=master"></img> | |
213 | ↪ Main dev branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=current/blead"></img> | |
214 | ↪ Release branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=current/for_cpan_index"></img> | |
214 | 215 | |
215 | 216 | =back |
216 | 217 | |
224 | 225 | the seemingly most insignificant questions and suggestions have been shown |
225 | 226 | to catalyze monumental improvements in consistency, accuracy and performance. |
226 | 227 | |
227 | List of the awesome contributors who made DBIC v0.082810 possible | |
228 | List of the awesome contributors who made DBIC v0.082820 possible | |
228 | 229 | |
229 | 230 | =encoding utf8 |
230 | 231 | |
231 | 232 | =over |
232 | 233 | |
233 | B<abraxxa>:Alexander Hartmaier <abraxxa@cpan.org> | |
234 | ||
235 | B<acca>:Alexander Kuznetsov <acca@cpan.org> | |
236 | ||
237 | B<aherzog>:Adam Herzog <adam@herzogdesigns.com> | |
234 | B<abraxxa>: Alexander Hartmaier <abraxxa@cpan.org> | |
235 | ||
236 | B<acca>: Alexander Kuznetsov <acca@cpan.org> | |
237 | ||
238 | B<aherzog>: Adam Herzog <adam@herzogdesigns.com> | |
238 | 239 | |
239 | 240 | Alexander Keusch <cpan@keusch.at> |
240 | 241 | |
241 | B<alexrj>:Alessandro Ranellucci <aar@cpan.org> | |
242 | ||
243 | B<alnewkirk>:Al Newkirk <github@alnewkirk.com> | |
244 | ||
245 | B<amiri>:Amiri Barksdale <amiribarksdale@gmail.com> | |
246 | ||
247 | B<amoore>:Andrew Moore <amoore@cpan.org> | |
242 | B<alexrj>: Alessandro Ranellucci <aar@cpan.org> | |
243 | ||
244 | B<alnewkirk>: Al Newkirk <github@alnewkirk.com> | |
245 | ||
246 | B<Altreus>: Alastair McGowan-Douglas <alastair.mcgowan@opusvl.com> | |
247 | ||
248 | B<amiri>: Amiri Barksdale <amiribarksdale@gmail.com> | |
249 | ||
250 | B<amoore>: Andrew Moore <amoore@cpan.org> | |
248 | 251 | |
249 | 252 | Andrew Mehta <Andrew@unitedgames.co.uk> |
250 | 253 | |
251 | B<andrewalker>:Andre Walker <andre@andrewalker.net> | |
252 | ||
253 | B<andyg>:Andy Grundman <andy@hybridized.org> | |
254 | ||
255 | B<ank>:Andres Kievsky <ank@ank.com.ar> | |
256 | ||
257 | B<arc>:Aaron Crane <arc@cpan.org> | |
258 | ||
259 | B<arcanez>:Justin Hunter <justin.d.hunter@gmail.com> | |
260 | ||
261 | B<ash>:Ash Berlin <ash@cpan.org> | |
262 | ||
263 | B<bert>:Norbert Csongrádi <bert@cpan.org> | |
264 | ||
265 | B<bfwg>:Colin Newell <colin.newell@gmail.com> | |
266 | ||
267 | B<blblack>:Brandon L. Black <blblack@gmail.com> | |
268 | ||
269 | B<bluefeet>:Aran Deltac <bluefeet@cpan.org> | |
270 | ||
271 | B<boghead>:Bryan Beeley <cpan@beeley.org> | |
272 | ||
273 | B<bphillips>:Brian Phillips <bphillips@cpan.org> | |
274 | ||
275 | B<brd>:Brad Davis <brd@FreeBSD.org> | |
254 | B<andrewalker>: Andre Walker <andre@andrewalker.net> | |
255 | ||
256 | B<andyg>: Andy Grundman <andy@hybridized.org> | |
257 | ||
258 | B<ank>: Andres Kievsky <ank@ank.com.ar> | |
259 | ||
260 | B<arc>: Aaron Crane <arc@cpan.org> | |
261 | ||
262 | B<arcanez>: Justin Hunter <justin.d.hunter@gmail.com> | |
263 | ||
264 | B<ash>: Ash Berlin <ash@cpan.org> | |
265 | ||
266 | B<bert>: Norbert Csongrádi <bert@cpan.org> | |
267 | ||
268 | B<bfwg>: Colin Newell <colin.newell@gmail.com> | |
269 | ||
270 | B<blblack>: Brandon L. Black <blblack@gmail.com> | |
271 | ||
272 | B<bluefeet>: Aran Deltac <bluefeet@cpan.org> | |
273 | ||
274 | B<boghead>: Bryan Beeley <cpan@beeley.org> | |
275 | ||
276 | B<bphillips>: Brian Phillips <bphillips@cpan.org> | |
277 | ||
278 | B<brd>: Brad Davis <brd@FreeBSD.org> | |
276 | 279 | |
277 | 280 | Brian Kirkbride <brian.kirkbride@deeperbydesign.com> |
278 | 281 | |
279 | B<bricas>:Brian Cassidy <bricas@cpan.org> | |
280 | ||
281 | B<brunov>:Bruno Vecchi <vecchi.b@gmail.com> | |
282 | ||
283 | B<caelum>:Rafael Kitover <rkitover@cpan.org> | |
284 | ||
285 | B<caldrin>:Maik Hentsche <maik.hentsche@amd.com> | |
286 | ||
287 | B<castaway>:Jess Robinson <castaway@desert-island.me.uk> | |
288 | ||
289 | B<chorny>:Alexandr Ciornii <alexchorny@gmail.com> | |
290 | ||
291 | B<claco>:Christopher H. Laco <claco@cpan.org> | |
292 | ||
293 | B<clkao>:CL Kao <clkao@clkao.org> | |
282 | B<bricas>: Brian Cassidy <bricas@cpan.org> | |
283 | ||
284 | B<brunov>: Bruno Vecchi <vecchi.b@gmail.com> | |
285 | ||
286 | B<caelum>: Rafael Kitover <rkitover@cpan.org> | |
287 | ||
288 | B<caldrin>: Maik Hentsche <maik.hentsche@amd.com> | |
289 | ||
290 | B<castaway>: Jess Robinson <castaway@desert-island.me.uk> | |
291 | ||
292 | B<chorny>: Alexandr Ciornii <alexchorny@gmail.com> | |
293 | ||
294 | B<cj>: C.J. Adams-Collier <cjcollier@cpan.org> | |
295 | ||
296 | B<claco>: Christopher H. Laco <claco@cpan.org> | |
297 | ||
298 | B<clkao>: CL Kao <clkao@clkao.org> | |
294 | 299 | |
295 | 300 | Ctrl-O L<http://ctrlo.com/|http://ctrlo.com/> |
296 | 301 | |
297 | B<da5id>:David Jack Olrik <david@olrik.dk> | |
298 | ||
299 | B<dams>:Damien Krotkine <dams@cpan.org> | |
300 | ||
301 | B<dandv>:Dan Dascalescu <ddascalescu+github@gmail.com> | |
302 | ||
303 | B<dariusj>:Darius Jokilehto <dariusjokilehto@yahoo.co.uk> | |
304 | ||
305 | B<davewood>:David Schmidt <mail@davidschmidt.at> | |
306 | ||
307 | B<daxim>:Lars Dɪᴇᴄᴋᴏᴡ 迪拉斯 <daxim@cpan.org> | |
308 | ||
309 | B<dduncan>:Darren Duncan <darren@darrenduncan.net> | |
310 | ||
311 | B<debolaz>:Anders Nor Berle <berle@cpan.org> | |
312 | ||
313 | B<dew>:Dan Thomas <dan@godders.org> | |
314 | ||
315 | B<dim0xff>:Dmitry Latin <dim0xff@gmail.com> | |
316 | ||
317 | B<dkubb>:Dan Kubb <dan.kubb-cpan@onautopilot.com> | |
318 | ||
319 | B<dnm>:Justin Wheeler <jwheeler@datademons.com> | |
320 | ||
321 | B<dpetrov>:Dimitar Petrov <mitakaa@gmail.com> | |
322 | ||
323 | B<dsteinbrunner>:David Steinbrunner <dsteinbrunner@pobox.com> | |
324 | ||
325 | B<duncan_dmg>:Duncan Garland <Duncan.Garland@motortrak.com> | |
326 | ||
327 | B<dwc>:Daniel Westermann-Clark <danieltwc@cpan.org> | |
328 | ||
329 | B<dyfrgi>:Michael Leuchtenburg <michael@slashhome.org> | |
330 | ||
331 | B<edenc>:Eden Cardim <edencardim@gmail.com> | |
302 | B<da5id>: David Jack Olrik <david@olrik.dk> | |
303 | ||
304 | B<dams>: Damien Krotkine <dams@cpan.org> | |
305 | ||
306 | B<dandv>: Dan Dascalescu <ddascalescu+github@gmail.com> | |
307 | ||
308 | B<dariusj>: Darius Jokilehto <dariusjokilehto@yahoo.co.uk> | |
309 | ||
310 | B<davewood>: David Schmidt <mail@davidschmidt.at> | |
311 | ||
312 | B<daxim>: Lars Dɪᴇᴄᴋᴏᴡ 迪拉斯 <daxim@cpan.org> | |
313 | ||
314 | B<dduncan>: Darren Duncan <darren@darrenduncan.net> | |
315 | ||
316 | B<debolaz>: Anders Nor Berle <berle@cpan.org> | |
317 | ||
318 | B<dew>: Dan Thomas <dan@godders.org> | |
319 | ||
320 | B<dim0xff>: Dmitry Latin <dim0xff@gmail.com> | |
321 | ||
322 | B<dkubb>: Dan Kubb <dan.kubb-cpan@onautopilot.com> | |
323 | ||
324 | B<dnm>: Justin Wheeler <jwheeler@datademons.com> | |
325 | ||
326 | B<dpetrov>: Dimitar Petrov <mitakaa@gmail.com> | |
327 | ||
328 | B<Dr^ZigMan>: Robert Stone <drzigman@drzigman.com> | |
329 | ||
330 | B<dsteinbrunner>: David Steinbrunner <dsteinbrunner@pobox.com> | |
331 | ||
332 | B<duncan_dmg>: Duncan Garland <Duncan.Garland@motortrak.com> | |
333 | ||
334 | B<dwc>: Daniel Westermann-Clark <danieltwc@cpan.org> | |
335 | ||
336 | B<dyfrgi>: Michael Leuchtenburg <michael@slashhome.org> | |
337 | ||
338 | B<edenc>: Eden Cardim <edencardim@gmail.com> | |
332 | 339 | |
333 | 340 | Eligo L<http://eligo.co.uk/|http://eligo.co.uk/> |
334 | 341 | |
335 | B<ether>:Karen Etheridge <ether@cpan.org> | |
336 | ||
337 | B<evdb>:Edmund von der Burg <evdb@ecclestoad.co.uk> | |
338 | ||
339 | B<faxm0dem>:Fabien Wernli <cpan@faxm0dem.org> | |
340 | ||
341 | B<felliott>:Fitz Elliott <fitz.elliott@gmail.com> | |
342 | ||
343 | B<freetime>:Bill Moseley <moseley@hank.org> | |
344 | ||
345 | B<frew>:Arthur Axel "fREW" Schmidt <frioux@gmail.com> | |
346 | ||
347 | B<gbjk>:Gareth Kirwan <gbjk@thermeon.com> | |
348 | ||
349 | B<Getty>:Torsten Raudssus <torsten@raudss.us> | |
350 | ||
351 | B<goraxe>:Gordon Irving <goraxe@cpan.org> | |
352 | ||
353 | B<gphat>:Cory G Watson <gphat@cpan.org> | |
342 | B<ether>: Karen Etheridge <ether@cpan.org> | |
343 | ||
344 | B<evdb>: Edmund von der Burg <evdb@ecclestoad.co.uk> | |
345 | ||
346 | B<faxm0dem>: Fabien Wernli <cpan@faxm0dem.org> | |
347 | ||
348 | B<felliott>: Fitz Elliott <fitz.elliott@gmail.com> | |
349 | ||
350 | B<freetime>: Bill Moseley <moseley@hank.org> | |
351 | ||
352 | B<frew>: Arthur Axel "fREW" Schmidt <frioux@gmail.com> | |
353 | ||
354 | B<gbjk>: Gareth Kirwan <gbjk@thermeon.com> | |
355 | ||
356 | B<Getty>: Torsten Raudssus <torsten@raudss.us> | |
357 | ||
358 | B<goraxe>: Gordon Irving <goraxe@cpan.org> | |
359 | ||
360 | B<gphat>: Cory G Watson <gphat@cpan.org> | |
354 | 361 | |
355 | 362 | Grant Street Group L<http://www.grantstreet.com/|http://www.grantstreet.com/> |
356 | 363 | |
357 | B<groditi>:Guillermo Roditi <groditi@cpan.org> | |
358 | ||
359 | B<gshank>:Gerda Shank <gshank@cpan.org> | |
360 | ||
361 | B<guacamole>:Fred Steinberg <fred.steinberg@gmail.com> | |
362 | ||
363 | B<Haarg>:Graham Knop <haarg@haarg.org> | |
364 | ||
365 | B<hobbs>:Andrew Rodland <andrew@cleverdomain.org> | |
364 | B<groditi>: Guillermo Roditi <groditi@cpan.org> | |
365 | ||
366 | B<gshank>: Gerda Shank <gshank@cpan.org> | |
367 | ||
368 | B<guacamole>: Fred Steinberg <fred.steinberg@gmail.com> | |
369 | ||
370 | B<Haarg>: Graham Knop <haarg@haarg.org> | |
371 | ||
372 | B<hobbs>: Andrew Rodland <andrew@cleverdomain.org> | |
366 | 373 | |
367 | 374 | Ian Wells <ijw@cack.org.uk> |
368 | 375 | |
369 | B<idn>:Ian Norton <i.norton@shadowcat.co.uk> | |
370 | ||
371 | B<ilmari>:Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | |
372 | ||
373 | B<initself>:Mike Baas <mike@initselftech.com> | |
374 | ||
375 | B<ironcamel>:Naveed Massjouni <naveedm9@gmail.com> | |
376 | ||
377 | B<jasonmay>:Jason May <jason.a.may@gmail.com> | |
378 | ||
379 | B<jawnsy>:Jonathan Yu <jawnsy@cpan.org> | |
380 | ||
381 | B<jegade>:Jens Gassmann <jens.gassmann@atomix.de> | |
382 | ||
383 | B<jeneric>:Eric A. Miller <emiller@cpan.org> | |
384 | ||
385 | B<jesper>:Jesper Krogh <jesper@krogh.cc> | |
376 | B<idn>: Ian Norton <i.norton@shadowcat.co.uk> | |
377 | ||
378 | B<ilmari>: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | |
379 | ||
380 | B<ingy>: Ingy döt Net <ingy@ingy.net> | |
381 | ||
382 | B<initself>: Mike Baas <mike@initselftech.com> | |
383 | ||
384 | B<ironcamel>: Naveed Massjouni <naveedm9@gmail.com> | |
385 | ||
386 | B<jasonmay>: Jason May <jason.a.may@gmail.com> | |
387 | ||
388 | B<jawnsy>: Jonathan Yu <jawnsy@cpan.org> | |
389 | ||
390 | B<jegade>: Jens Gassmann <jens.gassmann@atomix.de> | |
391 | ||
392 | B<jeneric>: Eric A. Miller <emiller@cpan.org> | |
393 | ||
394 | B<jesper>: Jesper Krogh <jesper@krogh.cc> | |
386 | 395 | |
387 | 396 | Jesse Sheidlower <jester@panix.com> |
388 | 397 | |
389 | B<jgoulah>:John Goulah <jgoulah@cpan.org> | |
390 | ||
391 | B<jguenther>:Justin Guenther <jguenther@cpan.org> | |
392 | ||
393 | B<jhannah>:Jay Hannah <jay@jays.net> | |
394 | ||
395 | B<jmac>:Jason McIntosh <jmac@appleseed-sc.com> | |
396 | ||
397 | B<jmmills>:Jason M. Mills <jmmills@cpan.org> | |
398 | ||
399 | B<jnapiorkowski>:John Napiorkowski <jjn1056@yahoo.com> | |
398 | B<jgoulah>: John Goulah <jgoulah@cpan.org> | |
399 | ||
400 | B<jguenther>: Justin Guenther <jguenther@cpan.org> | |
401 | ||
402 | B<jhannah>: Jay Hannah <jay@jays.net> | |
403 | ||
404 | B<jmac>: Jason McIntosh <jmac@appleseed-sc.com> | |
405 | ||
406 | B<jmmills>: Jason M. Mills <jmmills@cpan.org> | |
407 | ||
408 | B<jnapiorkowski>: John Napiorkowski <jjn1056@yahoo.com> | |
400 | 409 | |
401 | 410 | Joe Carlson <jwcarlson@lbl.gov> |
402 | 411 | |
403 | B<jon>:Jon Schutz <jjschutz@cpan.org> | |
412 | B<jon>: Jon Schutz <jjschutz@cpan.org> | |
404 | 413 | |
405 | 414 | Jordan Metzmeier <jmetzmeier@magazines.com> |
406 | 415 | |
407 | B<jshirley>:J. Shirley <jshirley@gmail.com> | |
408 | ||
409 | B<kaare>:Kaare Rasmussen | |
410 | ||
411 | B<kd>:Kieren Diment <diment@gmail.com> | |
412 | ||
413 | B<konobi>:Scott McWhirter <konobi@cpan.org> | |
414 | ||
415 | B<lejeunerenard>:Sean Zellmer <sean@lejeunerenard.com> | |
416 | ||
417 | B<littlesavage>:Alexey Illarionov <littlesavage@orionet.ru> | |
418 | ||
419 | B<lukes>:Luke Saunders <luke.saunders@gmail.com> | |
420 | ||
421 | B<marcus>:Marcus Ramberg <mramberg@cpan.org> | |
422 | ||
423 | B<mateu>:Mateu X. Hunter <hunter@missoula.org> | |
416 | B<jshirley>: J. Shirley <jshirley@gmail.com> | |
417 | ||
418 | B<kaare>: Kaare Rasmussen | |
419 | ||
420 | B<kd>: Kieren Diment <diment@gmail.com> | |
421 | ||
422 | B<kkane>: Kevin L. Kane <kevin.kane@gmail.com> | |
423 | ||
424 | B<konobi>: Scott McWhirter <konobi@cpan.org> | |
425 | ||
426 | B<lejeunerenard>: Sean Zellmer <sean@lejeunerenard.com> | |
427 | ||
428 | B<littlesavage>: Alexey Illarionov <littlesavage@orionet.ru> | |
429 | ||
430 | B<lukes>: Luke Saunders <luke.saunders@gmail.com> | |
431 | ||
432 | B<marcus>: Marcus Ramberg <mramberg@cpan.org> | |
433 | ||
434 | B<mateu>: Mateu X. Hunter <hunter@missoula.org> | |
424 | 435 | |
425 | 436 | Matt LeBlanc <antirice@gmail.com> |
426 | 437 | |
427 | 438 | Matt Sickler <imMute@msk4.com> |
428 | 439 | |
429 | B<mattlaw>:Matt Lawrence | |
430 | ||
431 | B<mattp>:Matt Phillips <mattp@cpan.org> | |
432 | ||
433 | B<mdk>:Mark Keating <m.keating@shadowcat.co.uk> | |
434 | ||
435 | B<melo>:Pedro Melo <melo@simplicidade.org> | |
436 | ||
437 | B<metaperl>:Terrence Brannon <metaperl@gmail.com> | |
438 | ||
439 | B<michaelr>:Michael Reddick <michael.reddick@gmail.com> | |
440 | ||
441 | B<milki>:Jonathan Chu <milki@rescomp.berkeley.edu> | |
442 | ||
443 | B<minty>:Murray Walker <perl@minty.org> | |
444 | ||
445 | B<mithaldu>:Christian Walde <walde.christian@gmail.com> | |
446 | ||
447 | B<mjemmeson>:Michael Jemmeson <michael.jemmeson@gmail.com> | |
448 | ||
449 | B<mna>:Maya | |
450 | ||
451 | B<mo>:Moritz Onken <onken@netcubed.de> | |
452 | ||
453 | B<moltar>:Roman Filippov <romanf@cpan.org> | |
454 | ||
455 | B<moritz>:Moritz Lenz <moritz@faui2k3.org> | |
456 | ||
457 | B<mrf>:Mike Francis <ungrim97@gmail.com> | |
458 | ||
459 | B<mst>:Matt S. Trout <mst@shadowcat.co.uk> | |
460 | ||
461 | B<mstratman>:Mark A. Stratman <stratman@gmail.com> | |
462 | ||
463 | B<ned>:Neil de Carteret <n3dst4@gmail.com> | |
464 | ||
465 | B<nigel>:Nigel Metheringham <nigelm@cpan.org> | |
466 | ||
467 | B<ningu>:David Kamholz <dkamholz@cpan.org> | |
468 | ||
469 | B<Nniuq>:Ron "Quinn" Straight" <quinnfazigu@gmail.org> | |
470 | ||
471 | B<norbi>:Norbert Buchmuller <norbi@nix.hu> | |
472 | ||
473 | B<nothingmuch>:Yuval Kogman <nothingmuch@woobling.org> | |
474 | ||
475 | B<nuba>:Nuba Princigalli <nuba@cpan.org> | |
476 | ||
477 | B<Numa>:Dan Sully <daniel@cpan.org> | |
478 | ||
479 | B<oalders>:Olaf Alders <olaf@wundersolutions.com> | |
440 | B<mattlaw>: Matt Lawrence | |
441 | ||
442 | B<mattp>: Matt Phillips <mattp@cpan.org> | |
443 | ||
444 | B<mdk>: Mark Keating <m.keating@shadowcat.co.uk> | |
445 | ||
446 | B<melo>: Pedro Melo <melo@simplicidade.org> | |
447 | ||
448 | B<metaperl>: Terrence Brannon <metaperl@gmail.com> | |
449 | ||
450 | B<michaelr>: Michael Reddick <michael.reddick@gmail.com> | |
451 | ||
452 | B<milki>: Jonathan Chu <milki@rescomp.berkeley.edu> | |
453 | ||
454 | B<minty>: Murray Walker <perl@minty.org> | |
455 | ||
456 | B<mithaldu>: Christian Walde <walde.christian@gmail.com> | |
457 | ||
458 | B<mjemmeson>: Michael Jemmeson <michael.jemmeson@gmail.com> | |
459 | ||
460 | B<mna>: Maya | |
461 | ||
462 | B<mo>: Moritz Onken <onken@netcubed.de> | |
463 | ||
464 | B<moltar>: Roman Filippov <romanf@cpan.org> | |
465 | ||
466 | B<moritz>: Moritz Lenz <moritz@faui2k3.org> | |
467 | ||
468 | B<mrf>: Mike Francis <ungrim97@gmail.com> | |
469 | ||
470 | B<mst>: Matt S. Trout <mst@shadowcat.co.uk> | |
471 | ||
472 | B<mstratman>: Mark A. Stratman <stratman@gmail.com> | |
473 | ||
474 | B<ned>: Neil de Carteret <n3dst4@gmail.com> | |
475 | ||
476 | B<nigel>: Nigel Metheringham <nigelm@cpan.org> | |
477 | ||
478 | B<ningu>: David Kamholz <dkamholz@cpan.org> | |
479 | ||
480 | B<Nniuq>: Ron "Quinn" Straight" <quinnfazigu@gmail.org> | |
481 | ||
482 | B<norbi>: Norbert Buchmuller <norbi@nix.hu> | |
483 | ||
484 | B<nothingmuch>: Yuval Kogman <nothingmuch@woobling.org> | |
485 | ||
486 | B<nuba>: Nuba Princigalli <nuba@cpan.org> | |
487 | ||
488 | B<Numa>: Dan Sully <daniel@cpan.org> | |
489 | ||
490 | B<oalders>: Olaf Alders <olaf@wundersolutions.com> | |
480 | 491 | |
481 | 492 | Olly Betts <olly@survex.com> |
482 | 493 | |
483 | B<osfameron>:Hakim Cassimally <osfameron@cpan.org> | |
484 | ||
485 | B<ovid>:Curtis "Ovid" Poe <ovid@cpan.org> | |
486 | ||
487 | B<oyse>:Øystein Torget <oystein.torget@dnv.com> | |
488 | ||
489 | B<paulm>:Paul Makepeace <paulm+pause@paulm.com> | |
490 | ||
491 | B<penguin>:K J Cheetham <jamie@shadowcatsystems.co.uk> | |
492 | ||
493 | B<perigrin>:Chris Prather <chris@prather.org> | |
494 | B<osfameron>: Hakim Cassimally <osfameron@cpan.org> | |
495 | ||
496 | B<ovid>: Curtis "Ovid" Poe <ovid@cpan.org> | |
497 | ||
498 | B<oyse>: Øystein Torget <oystein.torget@dnv.com> | |
499 | ||
500 | B<paulm>: Paul Makepeace <paulm+pause@paulm.com> | |
501 | ||
502 | B<penguin>: K J Cheetham <jamie@shadowcatsystems.co.uk> | |
503 | ||
504 | B<perigrin>: Chris Prather <chris@prather.org> | |
494 | 505 | |
495 | 506 | Peter Siklósi <einon@einon.hu> |
496 | 507 | |
497 | 508 | Peter Valdemar Mørch <peter@morch.com> |
498 | 509 | |
499 | B<peter>:Peter Collingbourne <peter@pcc.me.uk> | |
500 | ||
501 | B<phaylon>:Robert Sedlacek <phaylon@dunkelheit.at> | |
502 | ||
503 | B<plu>:Johannes Plunien <plu@cpan.org> | |
504 | ||
505 | B<Possum>:Daniel LeWarne <possum@cpan.org> | |
506 | ||
507 | B<pplu>:Jose Luis Martinez <jlmartinez@capside.com> | |
508 | ||
509 | B<quicksilver>:Jules Bean <jules@jellybean.co.uk> | |
510 | ||
511 | B<racke>:Stefan Hornburg <racke@linuxia.de> | |
512 | ||
513 | B<rafl>:Florian Ragwitz <rafl@debian.org> | |
514 | ||
515 | B<rainboxx>:Matthias Dietrich <perl@rb.ly> | |
516 | ||
517 | B<rbo>:Robert Bohne <rbo@cpan.org> | |
518 | ||
519 | B<rbuels>:Robert Buels <rmb32@cornell.edu> | |
520 | ||
521 | B<rdj>:Ryan D Johnson <ryan@innerfence.com> | |
522 | ||
523 | B<Relequestual>:Ben Hutton <relequestual@gmail.com> | |
524 | ||
525 | B<renormalist>:Steffen Schwigon <schwigon@cpan.org> | |
526 | ||
527 | B<ribasushi>:Peter Rabbitson <ribasushi@cpan.org> | |
528 | ||
529 | B<rjbs>:Ricardo Signes <rjbs@cpan.org> | |
510 | B<peter>: Peter Collingbourne <peter@pcc.me.uk> | |
511 | ||
512 | B<phaylon>: Robert Sedlacek <phaylon@dunkelheit.at> | |
513 | ||
514 | B<plu>: Johannes Plunien <plu@cpan.org> | |
515 | ||
516 | B<Possum>: Daniel LeWarne <possum@cpan.org> | |
517 | ||
518 | B<pplu>: Jose Luis Martinez <jlmartinez@capside.com> | |
519 | ||
520 | B<quicksilver>: Jules Bean <jules@jellybean.co.uk> | |
521 | ||
522 | B<racke>: Stefan Hornburg <racke@linuxia.de> | |
523 | ||
524 | B<rafl>: Florian Ragwitz <rafl@debian.org> | |
525 | ||
526 | B<rainboxx>: Matthias Dietrich <perl@rb.ly> | |
527 | ||
528 | B<rbo>: Robert Bohne <rbo@cpan.org> | |
529 | ||
530 | B<rbuels>: Robert Buels <rmb32@cornell.edu> | |
531 | ||
532 | B<rdj>: Ryan D Johnson <ryan@innerfence.com> | |
533 | ||
534 | B<Relequestual>: Ben Hutton <relequestual@gmail.com> | |
535 | ||
536 | B<renormalist>: Steffen Schwigon <schwigon@cpan.org> | |
537 | ||
538 | B<ribasushi>: Peter Rabbitson <ribasushi@cpan.org> | |
539 | ||
540 | B<rjbs>: Ricardo Signes <rjbs@cpan.org> | |
530 | 541 | |
531 | 542 | Robert Krimen <rkrimen@cpan.org> |
532 | 543 | |
533 | 544 | Robert Olson <bob@rdolson.org> |
534 | 545 | |
535 | B<robkinyon>:Rob Kinyon <rkinyon@cpan.org> | |
546 | B<robkinyon>: Rob Kinyon <rkinyon@cpan.org> | |
536 | 547 | |
537 | 548 | Roman Ardern-Corris <spam_in@3legs.com> |
538 | 549 | |
539 | B<ruoso>:Daniel Ruoso <daniel@ruoso.com> | |
540 | ||
541 | B<Sadrak>:Felix Antonius Wilhelm Ostmann <sadrak@cpan.org> | |
542 | ||
543 | B<sc_>:Just Another Perl Hacker | |
544 | ||
545 | B<schwern>:Michael G Schwern <mschwern@cpan.org> | |
550 | B<ruoso>: Daniel Ruoso <daniel@ruoso.com> | |
551 | ||
552 | B<Sadrak>: Felix Antonius Wilhelm Ostmann <sadrak@cpan.org> | |
553 | ||
554 | B<sc_>: Just Another Perl Hacker | |
555 | ||
556 | B<schwern>: Michael G Schwern <mschwern@cpan.org> | |
546 | 557 | |
547 | 558 | Scott R. Godin <webdragon.net@gmail.com> |
548 | 559 | |
549 | B<scotty>:Scotty Allen <scotty@scottyallen.com> | |
550 | ||
551 | B<semifor>:Marc Mims <marc@questright.com> | |
560 | B<scotty>: Scotty Allen <scotty@scottyallen.com> | |
561 | ||
562 | B<semifor>: Marc Mims <marc@questright.com> | |
552 | 563 | |
553 | 564 | Simon Elliott <cpan@browsing.co.uk> |
554 | 565 | |
555 | B<SineSwiper>:Brendan Byrd <perl@resonatorsoft.org> | |
556 | ||
557 | B<skaufman>:Samuel Kaufman <sam@socialflow.com> | |
558 | ||
559 | B<solomon>:Jared Johnson <jaredj@nmgi.com> | |
560 | ||
561 | B<spb>:Stephen Bennett <stephen@freenode.net> | |
566 | B<SineSwiper>: Brendan Byrd <perl@resonatorsoft.org> | |
567 | ||
568 | B<skaufman>: Samuel Kaufman <sam@socialflow.com> | |
569 | ||
570 | B<solomon>: Jared Johnson <jaredj@nmgi.com> | |
571 | ||
572 | B<spb>: Stephen Bennett <stephen@freenode.net> | |
562 | 573 | |
563 | 574 | Squeeks <squeek@cpan.org> |
564 | 575 | |
565 | B<srezic>:Slaven Rezic <slaven@rezic.de> | |
566 | ||
567 | B<sszabo>:Stephan Szabo <sszabo@bigpanda.com> | |
576 | B<srezic>: Slaven Rezic <slaven@rezic.de> | |
577 | ||
578 | B<sszabo>: Stephan Szabo <sszabo@bigpanda.com> | |
568 | 579 | |
569 | 580 | Stephen Peters <steve@stephenpeters.me> |
570 | 581 | |
571 | B<stonecolddevin>:Devin Austin <dhoss@cpan.org> | |
572 | ||
573 | B<talexb>:Alex Beamish <talexb@gmail.com> | |
574 | ||
575 | B<tamias>:Ronald J Kimball <rjk@tamias.net> | |
576 | ||
577 | B<TBSliver>:Tom Bloor <t.bloor@shadowcat.co.uk> | |
578 | ||
579 | B<teejay>:Aaron Trevena <teejay@cpan.org> | |
580 | ||
581 | B<theorbtwo>:James Mastros <james@mastros.biz> | |
582 | B<stonecolddevin>: Devin Austin <dhoss@cpan.org> | |
583 | ||
584 | B<talexb>: Alex Beamish <talexb@gmail.com> | |
585 | ||
586 | B<tamias>: Ronald J Kimball <rjk@tamias.net> | |
587 | ||
588 | B<TBSliver>: Tom Bloor <t.bloor@shadowcat.co.uk> | |
589 | ||
590 | B<teejay>: Aaron Trevena <teejay@cpan.org> | |
591 | ||
592 | B<theorbtwo>: James Mastros <james@mastros.biz> | |
582 | 593 | |
583 | 594 | Thomas Kratz <tomk@cpan.org> |
584 | 595 | |
585 | B<timbunce>:Tim Bunce <tim.bunce@pobox.com> | |
596 | B<timbunce>: Tim Bunce <tim.bunce@pobox.com> | |
586 | 597 | |
587 | 598 | Todd Lipcon |
588 | 599 | |
589 | 600 | Tom Hukins <tom@eborcom.com> |
590 | 601 | |
591 | B<tommy>:Tommy Butler <tbutler.cpan.org@internetalias.net> | |
592 | ||
593 | B<tonvoon>:Ton Voon <ton.voon@opsview.com> | |
594 | ||
595 | B<triode>:Pete Gamache <gamache@cpan.org> | |
596 | ||
597 | B<typester>:Daisuke Murase <typester@cpan.org> | |
598 | ||
599 | B<uree>:Oriol Soriano <oriol.soriano@capside.com> | |
600 | ||
601 | B<uwe>:Uwe Voelker <uwe@uwevoelker.de> | |
602 | ||
603 | B<victori>:Victor Igumnov <victori@cpan.org> | |
604 | ||
605 | B<wdh>:Will Hawes <wdhawes@gmail.com> | |
606 | ||
607 | B<wesm>:Wes Malone <wes@mitsi.com> | |
608 | ||
609 | B<willert>:Sebastian Willert <willert@cpan.org> | |
610 | ||
611 | B<wintermute>:Toby Corkindale <tjc@cpan.org> | |
612 | ||
613 | B<wreis>:Wallace Reis <wreis@cpan.org> | |
614 | ||
615 | B<xenoterracide>:Caleb Cushing <xenoterracide@gmail.com> | |
616 | ||
617 | B<yrlnry>:Mark Jason Dominus <mjd@plover.com> | |
618 | ||
619 | B<zamolxes>:Bogdan Lucaciu <bogdan@wiz.ro> | |
620 | ||
621 | B<Zefram>:Andrew Main <zefram@fysh.org> | |
602 | B<tommy>: Tommy Butler <tbutler.cpan.org@internetalias.net> | |
603 | ||
604 | B<tonvoon>: Ton Voon <ton.voon@opsview.com> | |
605 | ||
606 | B<triode>: Pete Gamache <gamache@cpan.org> | |
607 | ||
608 | B<typester>: Daisuke Murase <typester@cpan.org> | |
609 | ||
610 | B<uree>: Oriol Soriano <oriol.soriano@capside.com> | |
611 | ||
612 | B<uwe>: Uwe Voelker <uwe@uwevoelker.de> | |
613 | ||
614 | B<vanstyn>: Henry Van Styn <vanstyn@cpan.org> | |
615 | ||
616 | B<victori>: Victor Igumnov <victori@cpan.org> | |
617 | ||
618 | B<wdh>: Will Hawes <wdhawes@gmail.com> | |
619 | ||
620 | B<wesm>: Wes Malone <wes@mitsi.com> | |
621 | ||
622 | B<willert>: Sebastian Willert <willert@cpan.org> | |
623 | ||
624 | B<wintermute>: Toby Corkindale <tjc@cpan.org> | |
625 | ||
626 | B<wreis>: Wallace Reis <wreis@cpan.org> | |
627 | ||
628 | B<xenoterracide>: Caleb Cushing <xenoterracide@gmail.com> | |
629 | ||
630 | B<xmikew>: Mike Wisener <xmikew@32ths.com> | |
631 | ||
632 | B<yrlnry>: Mark Jason Dominus <mjd@plover.com> | |
633 | ||
634 | B<zamolxes>: Bogdan Lucaciu <bogdan@wiz.ro> | |
635 | ||
636 | B<Zefram>: Andrew Main <zefram@fysh.org> | |
622 | 637 | |
623 | 638 | =back |
624 | 639 |
9 | 9 | map { chomp; ( ( ! $_ or $_ =~ /^\s*\#/ ) ? () : $_ ) } <$fh>; |
10 | 10 | } or die "Known AUTHORS file seems empty... can't happen..."; |
11 | 11 | |
12 | $_ =~ s!^ ( [^\:]+ ) : \s !B<$1>:!x | |
12 | $_ =~ s!^ ( [^\:]+ ) : \s !B<$1>: !x | |
13 | 13 | for @known_authors; |
14 | 14 | |
15 | 15 | $_ =~ s!( \b https? :// [^\s\>]+ )!L<$1|$1>!x |
0 | ### | |
1 | ### This version is rather 5.8-centric, because DBIC itself is 5.8 | |
2 | ### It certainly can be rewritten to degrade well on 5.6 | |
3 | ### | |
4 | ||
5 | ||
6 | BEGIN { | |
7 | if ($] < 5.010) { | |
8 | ||
9 | # Pre-5.10 perls pollute %INC on unsuccesfull module | |
10 | # require, making it appear as if the module is already | |
11 | # loaded on subsequent require()s | |
12 | # Can't seem to find the exact RT/perldelta entry | |
13 | # | |
14 | # The reason we can't just use a sane, clean loader, is because | |
15 | # if a Module require()s another module the %INC will still | |
16 | # get filled with crap and we are back to square one. A global | |
17 | # fix is really the only way for this test, as we try to load | |
18 | # each available module separately, and have no control (nor | |
19 | # knowledge) over their common dependencies. | |
20 | # | |
21 | # we want to do this here, in the very beginning, before even | |
22 | # warnings/strict are loaded | |
23 | ||
24 | unshift @INC, 't/lib'; | |
25 | require DBICTest::Util::OverrideRequire; | |
26 | ||
27 | DBICTest::Util::OverrideRequire::override_global_require( sub { | |
28 | my $res = eval { $_[0]->() }; | |
29 | if ($@ ne '') { | |
30 | delete $INC{$_[1]}; | |
31 | die $@; | |
32 | } | |
33 | return $res; | |
34 | } ); | |
35 | } | |
36 | } | |
37 | ||
38 | # Explicitly add 'lib' to the front of INC - this way we will | |
39 | # know without ambiguity what was loaded from the local untar | |
40 | # and what came from elsewhere | |
41 | use lib qw(lib t/lib); | |
42 | ||
43 | use strict; | |
44 | use warnings; | |
45 | ||
46 | use Test::More 'no_plan'; | |
47 | use Config; | |
48 | use File::Find 'find'; | |
49 | use Module::Runtime 'module_notional_filename'; | |
50 | use List::Util qw(max min); | |
51 | use ExtUtils::MakeMaker; | |
52 | use DBICTest::Util 'visit_namespaces'; | |
53 | ||
54 | # load these two to pull in the t/lib armada | |
55 | use DBICTest; | |
56 | use DBICTest::Schema; | |
57 | DBICTest->init_schema; | |
58 | ||
59 | # do !!!NOT!!! use Module::Runtime's require_module - it breaks CORE::require | |
60 | sub req_mod ($) { | |
61 | # trap deprecation warnings and whatnot | |
62 | local $SIG{__WARN__} = sub {}; | |
63 | local $@; | |
64 | eval "require $_[0]"; | |
65 | } | |
66 | ||
67 | sub say_err { | |
68 | print STDERR "\n", @_, "\n"; | |
69 | } | |
70 | ||
71 | # needed for WeirdOS | |
72 | sub fixup_path ($) { | |
73 | return $_[0] unless ( $^O eq 'MSWin32' and $_[0] ); | |
74 | ||
75 | # sometimes we can get a short/longname mix, normalize everything to longnames | |
76 | my $fn = Win32::GetLongPathName($_[0]); | |
77 | ||
78 | # Fixup (native) slashes in Config not matching (unixy) slashes in INC | |
79 | $fn =~ s|\\|/|g; | |
80 | ||
81 | $fn; | |
82 | } | |
83 | ||
84 | my @lib_display_order = qw( | |
85 | sitearch | |
86 | sitelib | |
87 | vendorarch | |
88 | vendorlib | |
89 | archlib | |
90 | privlib | |
91 | ); | |
92 | my $lib_paths = { | |
93 | (map | |
94 | { $Config{$_} | |
95 | ? ( $_ => fixup_path( $Config{"${_}exp"} || $Config{$_} ) ) | |
96 | : () | |
97 | } | |
98 | @lib_display_order | |
99 | ), | |
100 | ||
101 | # synthetic, for display | |
102 | './lib' => 'lib', | |
103 | }; | |
104 | ||
105 | sub describe_fn { | |
106 | my $fn = shift; | |
107 | ||
108 | return '' if !defined $fn; | |
109 | ||
110 | $fn = fixup_path( $fn ); | |
111 | ||
112 | $lib_paths->{$_} and $fn =~ s/^\Q$lib_paths->{$_}/<<$_>>/ and last | |
113 | for @lib_display_order; | |
114 | ||
115 | $fn; | |
116 | } | |
117 | ||
118 | sub md5_of_fn { | |
119 | # we already checked for -r/-f, just bail if can't open | |
120 | open my $fh, '<:raw', $_[0] or return ''; | |
121 | require Digest::MD5; | |
122 | Digest::MD5->new->addfile($fh)->hexdigest; | |
123 | } | |
124 | ||
125 | # first run through lib and *try* to load anything we can find | |
126 | # within our own project | |
127 | find({ | |
128 | wanted => sub { | |
129 | -f $_ or return; | |
130 | ||
131 | # can't just `require $fn`, as we need %INC to be | |
132 | # populated properly | |
133 | my ($mod) = $_ =~ /^ lib [\/\\] (.+) \.pm $/x | |
134 | or return; | |
135 | ||
136 | req_mod join ('::', File::Spec->splitdir($mod)); | |
137 | }, | |
138 | no_chdir => 1, | |
139 | }, 'lib' ); | |
140 | ||
141 | # now run through OptDeps and attempt loading everything else | |
142 | # | |
143 | # some things needs to be sorted before other things | |
144 | # positive - load first | |
145 | # negative - load last | |
146 | my $load_weights = { | |
147 | # Make sure oracle is tried last - some clients (e.g. 10.2) have symbol | |
148 | # clashes with libssl, and will segfault everything coming after them | |
149 | "DBD::Oracle" => -999, | |
150 | }; | |
151 | ||
152 | my $optdeps = { | |
153 | map | |
154 | { $_ => 1 } | |
155 | map | |
156 | { keys %{DBIx::Class::Optional::Dependencies->req_list_for($_)} } | |
157 | grep | |
158 | { $_ !~ /rdbms/ } | |
159 | keys %{DBIx::Class::Optional::Dependencies->req_group_list} | |
160 | }; | |
161 | req_mod $_ for sort | |
162 | { ($load_weights->{$b}||0) <=> ($load_weights->{$a}||0) } | |
163 | keys %$optdeps | |
164 | ; | |
165 | ||
166 | my $has_versionpm = eval { require version }; | |
167 | ||
168 | # at this point we've loaded everything we ever could, let's drill through | |
169 | # the *ENTIRE* symtable and build a map of versions | |
170 | my $version_list = { perl => $] }; | |
171 | visit_namespaces( action => sub { | |
172 | no strict 'refs'; | |
173 | my $pkg = shift; | |
174 | ||
175 | # keep going, but nothing to see here | |
176 | return 1 if $pkg eq 'main'; | |
177 | ||
178 | # private - not interested, including no further descent | |
179 | return 0 if $pkg =~ / (?: ^ | :: ) _ /x; | |
180 | ||
181 | # not interested in no-VERSION-containing modules, nor synthetic classes | |
182 | return 1 if ( | |
183 | ! defined ${"${pkg}::VERSION"} | |
184 | or | |
185 | ${"${pkg}::VERSION"} =~ /\Qset by base.pm/ | |
186 | ); | |
187 | ||
188 | # make sure a version can be extracted, be noisy when it doesn't work | |
189 | # do this even if we are throwing away the result below in lieu of EUMM | |
190 | my $mod_ver = eval { $pkg->VERSION }; | |
191 | if (my $err = $@) { | |
192 | $err =~ s/^/ /mg; | |
193 | say_err | |
194 | "Calling `$pkg->VERSION` resulted in an exception, which should never " | |
195 | . "happen - please file a bug with the distribution containing $pkg. " | |
196 | . "Complete exception text below:\n\n$err" | |
197 | ; | |
198 | } | |
199 | elsif( ! defined $mod_ver or ! length $mod_ver ) { | |
200 | my $ret = defined $mod_ver | |
201 | ? "the empty string ''" | |
202 | : "'undef'" | |
203 | ; | |
204 | ||
205 | say_err | |
206 | "Calling `$pkg->VERSION` returned $ret, even though \$${pkg}::VERSION " | |
207 | . "is defined, which should never happen - please file a bug with the " | |
208 | . "distribution containing $pkg." | |
209 | ; | |
210 | ||
211 | undef $mod_ver; | |
212 | } | |
213 | ||
214 | # if this is a real file - extract the version via EUMM whenever possible | |
215 | my $fn = $INC{module_notional_filename($pkg)}; | |
216 | ||
217 | my $eumm_ver = ( | |
218 | $fn | |
219 | and | |
220 | -f $fn | |
221 | and | |
222 | -r $fn | |
223 | and | |
224 | eval { MM->parse_version( $fn ) } | |
225 | ) || undef; | |
226 | ||
227 | if ( | |
228 | $has_versionpm | |
229 | and | |
230 | defined $eumm_ver | |
231 | and | |
232 | defined $mod_ver | |
233 | and | |
234 | $eumm_ver ne $mod_ver | |
235 | and | |
236 | ( | |
237 | ( eval { version->parse( do { (my $v = $eumm_ver) =~ s/_//g; $v } ) } || 0 ) | |
238 | != | |
239 | ( eval { version->parse( do { (my $v = $mod_ver) =~ s/_//g; $v } ) } || 0 ) | |
240 | ) | |
241 | ) { | |
242 | say_err | |
243 | "Mismatch of versions '$mod_ver' and '$eumm_ver', obtained respectively " | |
244 | . "via `$pkg->VERSION` and parsing the version out of @{[ describe_fn $fn ]} " | |
245 | . "with ExtUtils::MakeMaker\@@{[ ExtUtils::MakeMaker->VERSION ]}. " | |
246 | . "This should never happen - please check whether this is still present " | |
247 | . "in the latest version, and then file a bug with the distribution " | |
248 | . "containing $pkg." | |
249 | ; | |
250 | } | |
251 | ||
252 | if( defined $eumm_ver ) { | |
253 | $version_list->{$pkg} = $eumm_ver; | |
254 | } | |
255 | elsif( defined $mod_ver ) { | |
256 | $version_list->{$pkg} = $mod_ver; | |
257 | } | |
258 | ||
259 | 1; | |
260 | }); | |
261 | ||
262 | # In retrospect it makes little sense to omit this information - just | |
263 | # show everything at all times. | |
264 | # Nevertheless leave the dead code, in case it turns out to be a bad idea... | |
265 | my $show_all = 1; | |
266 | #my $show_all = $ENV{PERL_DESCRIBE_ALL_DEPS} || !DBICTest::RunMode->is_plain; | |
267 | ||
268 | # compress identical versions as close to the root as we can | |
269 | # unless we are dealing with a smoker - in which case we want | |
270 | # to see every MD5 there is | |
271 | unless ($show_all) { | |
272 | for my $mod ( sort { length($b) <=> length($a) } keys %$version_list ) { | |
273 | my $parent = $mod; | |
274 | ||
275 | while ( $parent =~ s/ :: (?: . (?! :: ) )+ $ //x ) { | |
276 | $version_list->{$parent} | |
277 | and | |
278 | $version_list->{$parent} eq $version_list->{$mod} | |
279 | and | |
280 | ( ( delete $version_list->{$mod} ) or 1 ) | |
281 | and | |
282 | last | |
283 | } | |
284 | } | |
285 | } | |
286 | ||
287 | ok 1, (scalar keys %$version_list) . " distinctly versioned modules"; | |
288 | ||
289 | exit if ($ENV{TRAVIS}||'') eq 'true'; | |
290 | ||
291 | # sort stuff into @INC segments | |
292 | my $segments; | |
293 | ||
294 | MODULE: | |
295 | for my $mod ( sort { lc($a) cmp lc($b) } keys %$version_list ) { | |
296 | my $fn = $INC{module_notional_filename($mod)}; | |
297 | ||
298 | my $tuple = [ $mod ]; | |
299 | ||
300 | if ( defined $fn && -f $fn && -r $fn ) { | |
301 | push @$tuple, ( $fn = fixup_path($fn) ); | |
302 | ||
303 | for my $lib (@lib_display_order, './lib') { | |
304 | if ( $lib_paths->{$lib} and index($fn, $lib_paths->{$lib}) == 0 ) { | |
305 | push @{$segments->{$lib}}, $tuple; | |
306 | next MODULE; | |
307 | } | |
308 | } | |
309 | } | |
310 | ||
311 | # fallthrough for anything without a physical filename, or unknown lib | |
312 | push @{$segments->{''}}, $tuple; | |
313 | } | |
314 | ||
315 | # diag the result out | |
316 | my $max_ver_len = max map | |
317 | { length $_ } | |
318 | ( values %$version_list, 'xxx.yyyzzz_bbb' ) | |
319 | ; | |
320 | my $max_mod_len = max map { length $_ } keys %$version_list; | |
321 | ||
322 | my $discl = <<'EOD'; | |
323 | ||
324 | Versions of all loadable modules within both the core and *OPTIONAL* dependency chains present on this system | |
325 | Note that *MANY* of these modules will *NEVER* be loaded during normal operation of DBIx::Class | |
326 | EOD | |
327 | ||
328 | $discl .= "(modules with versions identical to their parent namespace were omitted - set PERL_DESCRIBE_ALL_DEPS to see them)\n" | |
329 | unless $show_all; | |
330 | ||
331 | diag $discl; | |
332 | ||
333 | diag "\n"; | |
334 | ||
335 | for my $seg ( '', @lib_display_order, './lib' ) { | |
336 | next unless $segments->{$seg}; | |
337 | ||
338 | diag sprintf "=== %s ===\n\n", | |
339 | $seg | |
340 | ? "Modules found in " . ( $Config{$seg} ? "\$Config{$seg}" : $seg ) | |
341 | : 'Misc versions' | |
342 | ; | |
343 | ||
344 | diag sprintf ( | |
345 | "%*s %*s%s\n", | |
346 | $max_ver_len => $version_list->{$_->[0]}, | |
347 | -$max_mod_len => $_->[0], | |
348 | ($_->[1] | |
349 | ? ' ' x (80 - min(78, $max_mod_len)) . "[ MD5: @{[ md5_of_fn( $_->[1] ) ]} ]" | |
350 | : '' | |
351 | ), | |
352 | ) for @{$segments->{$seg}}; | |
353 | ||
354 | diag "\n\n" | |
355 | } | |
356 | ||
357 | diag "$discl\n"; |
384 | 384 | # test all kinds of population with stringified objects |
385 | 385 | # or with empty sets |
386 | 386 | warnings_like { |
387 | local $ENV{DBIC_RT79576_NOWARN}; | |
388 | ||
389 | 387 | my $rs = $schema->resultset('Artist')->search({}, { columns => [qw(name rank)], order_by => 'artistid' }); |
390 | 388 | |
391 | 389 | # the stringification has nothing to do with the artist name |
506 | 504 | ); |
507 | 505 | |
508 | 506 | $rs->delete; |
509 | } [ | |
510 | # warning to be removed around Apr 1st 2015 | |
511 | # smokers start failing a month before that | |
512 | ( | |
513 | ( DBICTest::RunMode->is_author and ( time() > 1427846400 ) ) | |
514 | or | |
515 | ( DBICTest::RunMode->is_smoker and ( time() > 1425168000 ) ) | |
516 | ) | |
517 | ? () | |
518 | # one unique for populate() and create() each | |
519 | : (qr/\QPOSSIBLE *PAST* DATA CORRUPTION detected \E.+\QTrigger condition encountered at @{[ __FILE__ ]} line\E \d/) x 4 | |
520 | ], 'Data integrity warnings as planned'; | |
507 | } [], 'Data integrity warnings gone as planned'; | |
521 | 508 | |
522 | 509 | $schema->is_executed_sql_bind( |
523 | 510 | sub { |
15 | 15 | |
16 | 16 | cmp_ok(DBICTest->resultset('Artist')->count, '>', 0, 'count is valid'); |
17 | 17 | |
18 | # cleanup globaly cached handle so we do not trigger the leaktest | |
19 | DBICTest->schema->storage->disconnect; | |
20 | ||
21 | 18 | done_testing; |
22 | 22 | |
23 | 23 | use lib qw(t/lib); |
24 | 24 | use DBICTest::RunMode; |
25 | ||
26 | plan skip_all => "Temporarily no smoke testing of Test::More 1.3xx alphas" if ( | |
27 | DBICTest::RunMode->is_smoker | |
28 | and | |
29 | eval { Test::More->VERSION("1.300") } | |
30 | and | |
31 | require ExtUtils::MakeMaker | |
32 | and | |
33 | MM->parse_version($INC{"Test/Builder.pm"}) =~ / ^ 1 \. 3.. ... \_ /x | |
34 | ); | |
35 | ||
36 | my $TB = Test::More->builder; | |
37 | if ($ENV{DBICTEST_IN_PERSISTENT_ENV}) { | |
38 | # without this explicit close older TBs warn in END after a ->reset | |
39 | if ($TB->VERSION < 1.005) { | |
40 | close ($TB->$_) for (qw/output failure_output todo_output/); | |
41 | } | |
42 | ||
43 | # if I do not do this, I get happy sigpipes on new TB, no idea why | |
44 | # (the above close-and-forget doesn't work - new TB does *not* reopen | |
45 | # its handles automatically anymore) | |
46 | else { | |
47 | for (qw/failure_output todo_output/) { | |
48 | close $TB->$_; | |
49 | open ($TB->$_, '>&', *STDERR); | |
50 | } | |
51 | ||
52 | close $TB->output; | |
53 | open ($TB->output, '>&', *STDOUT); | |
54 | } | |
55 | ||
56 | # so done_testing can work on every persistent pass | |
57 | $TB->reset; | |
58 | } | |
59 | ||
60 | 25 | use DBICTest::Util::LeakTracer qw(populate_weakregistry assert_empty_weakregistry visit_refs); |
61 | 26 | use Scalar::Util qw(weaken blessed reftype); |
62 | use DBIx::Class; | |
63 | use DBIx::Class::_Util qw(hrefaddr sigwarn_silencer); | |
27 | use DBIx::Class::_Util qw(hrefaddr sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt); | |
64 | 28 | BEGIN { |
65 | 29 | plan skip_all => "Your perl version $] appears to leak like a sieve - skipping test" |
66 | 30 | if DBIx::Class::_ENV_::PEEPEENESS; |
31 | } | |
32 | ||
33 | ||
34 | my $TB = Test::More->builder; | |
35 | if ($ENV{DBICTEST_IN_PERSISTENT_ENV}) { | |
36 | # without this explicit close TB warns in END after a ->reset | |
37 | close ($TB->$_) for qw(output failure_output todo_output); | |
38 | ||
39 | # newer TB does not auto-reopen handles | |
40 | if ( modver_gt_or_eq( 'Test::More', '1.200' ) ) { | |
41 | open ($TB->$_, '>&', *STDERR) | |
42 | for qw( failure_output todo_output ); | |
43 | open ($TB->output, '>&', *STDOUT); | |
44 | } | |
45 | ||
46 | # so done_testing can work on every persistent pass | |
47 | $TB->reset; | |
67 | 48 | } |
68 | 49 | |
69 | 50 | # this is what holds all weakened refs to be checked for leakage |
97 | 78 | # Test Builder is now making a new object for every pass/fail (que bloat?) |
98 | 79 | # and as such we can't really store any of its objects (since it will |
99 | 80 | # re-populate the registry while checking it, ewwww!) |
100 | return $obj if (ref $obj) =~ /^TB2::/; | |
81 | return $obj if (ref $obj) =~ /^TB2::|^Test::Stream/; | |
101 | 82 | |
102 | 83 | # populate immediately to avoid weird side effects |
103 | 84 | return populate_weakregistry ($weak_registry, $obj ); |
338 | 319 | ! DBICTest::RunMode->is_plain |
339 | 320 | and |
340 | 321 | ! $ENV{DBICTEST_IN_PERSISTENT_ENV} |
341 | and | |
342 | # FIXME - investigate wtf is going on with 5.18 | |
343 | ! ( $] > 5.017 and $ENV{DBIC_TRACE_PROFILE} ) | |
344 | 322 | ) { |
345 | 323 | |
346 | 324 | # FIXME - ideally we should be able to just populate an alternative |
468 | 446 | delete $weak_registry->{$addr} |
469 | 447 | unless $cleared->{hash_merge_singleton}{$weak_registry->{$addr}{weakref}{behavior}}++; |
470 | 448 | } |
449 | elsif ($names =~ /^DateTime::TimeZone::UTC/m) { | |
450 | # DT is going through a refactor it seems - let it leak zones for now | |
451 | delete $weak_registry->{$addr}; | |
452 | } | |
471 | 453 | elsif ( |
472 | 454 | # # if we can look at closed over pieces - we will register it as a global |
473 | 455 | # !DBICTest::Util::LeakTracer::CV_TRACING |
535 | 517 | ($ENV{PATH}) = $ENV{PATH} =~ /(.+)/; |
536 | 518 | |
537 | 519 | |
538 | my $persistence_tests = { | |
539 | PPerl => { | |
540 | cmd => [qw/pperl --prefork=1/, __FILE__], | |
541 | }, | |
542 | 'CGI::SpeedyCGI' => { | |
543 | cmd => [qw/speedy -- -t5/, __FILE__], | |
544 | }, | |
545 | }; | |
546 | ||
547 | # scgi is smart and will auto-reap after -t amount of seconds | |
548 | # pperl needs an actual killer :( | |
549 | $persistence_tests->{PPerl}{termcmd} = [ | |
550 | $persistence_tests->{PPerl}{cmd}[0], | |
551 | '--kill', | |
552 | @{$persistence_tests->{PPerl}{cmd}}[ 1 .. $#{$persistence_tests->{PPerl}{cmd}} ], | |
553 | ]; | |
554 | ||
520 | my $persistence_tests; | |
555 | 521 | SKIP: { |
556 | 522 | skip 'Test already in a persistent loop', 1 |
557 | 523 | if $ENV{DBICTEST_IN_PERSISTENT_ENV}; |
559 | 525 | skip 'Main test failed - skipping persistent env tests', 1 |
560 | 526 | unless $TB->is_passing; |
561 | 527 | |
528 | skip "Test::Builder\@@{[ Test::Builder->VERSION ]} known to break persistence tests", 1 | |
529 | if modver_gt_or_eq_and_lt( 'Test::More', '1.200', '1.301001_099' ); | |
530 | ||
562 | 531 | local $ENV{DBICTEST_IN_PERSISTENT_ENV} = 1; |
532 | ||
533 | $persistence_tests = { | |
534 | PPerl => { | |
535 | cmd => [qw/pperl --prefork=1/, __FILE__], | |
536 | }, | |
537 | 'CGI::SpeedyCGI' => { | |
538 | cmd => [qw/speedy -- -t5/, __FILE__], | |
539 | }, | |
540 | }; | |
541 | ||
542 | # scgi is smart and will auto-reap after -t amount of seconds | |
543 | # pperl needs an actual killer :( | |
544 | $persistence_tests->{PPerl}{termcmd} = [ | |
545 | $persistence_tests->{PPerl}{cmd}[0], | |
546 | '--kill', | |
547 | @{$persistence_tests->{PPerl}{cmd}}[ 1 .. $#{$persistence_tests->{PPerl}{cmd}} ], | |
548 | ]; | |
563 | 549 | |
564 | 550 | require IPC::Open2; |
565 | 551 | |
609 | 595 | # just an extra precaution in case we blew away from the SKIP - since there are no |
610 | 596 | # PID files to go by (man does pperl really suck :( |
611 | 597 | END { |
612 | unless ($ENV{DBICTEST_IN_PERSISTENT_ENV}) { | |
613 | close $_ for (*STDIN, *STDOUT, *STDERR); | |
598 | if ($persistence_tests->{PPerl}{termcmd}) { | |
614 | 599 | local $?; # otherwise test will inherit $? of the system() |
615 | system (@{$persistence_tests->{PPerl}{termcmd}}) | |
616 | if $persistence_tests->{PPerl}{termcmd}; | |
617 | } | |
618 | } | |
600 | require IPC::Open3; | |
601 | open my $null, ">", File::Spec->devnull; | |
602 | waitpid( | |
603 | IPC::Open3::open3(undef, $null, $null, @{$persistence_tests->{PPerl}{termcmd}}), | |
604 | 0, | |
605 | ); | |
606 | } | |
607 | } |
101 | 101 | namespace::clean |
102 | 102 | Try::Tiny |
103 | 103 | Sub::Name |
104 | strictures | |
105 | 104 | Sub::Defer |
106 | 105 | Sub::Quote |
107 | 106 | |
108 | 107 | Scalar::Util |
109 | 108 | List::Util |
109 | Storable | |
110 | 110 | |
111 | 111 | Class::Accessor::Grouped |
112 | 112 | Class::C3::Componentised |
167 | 167 | my $s = DBICTest->init_schema; |
168 | 168 | is ($s->resultset('Artist')->find(1)->name, 'Caterwauler McCrae'); |
169 | 169 | assert_no_missing_expected_requires(); |
170 | } | |
171 | ||
172 | # make sure we never loaded any of the strictures XS bullshit | |
173 | { | |
174 | ok( ! exists $INC{ Module::Runtime::module_notional_filename($_) }, "$_ load never attempted" ) | |
175 | for qw(indirect multidimensional bareword::filehandles); | |
176 | 170 | } |
177 | 171 | |
178 | 172 | done_testing; |
4 | 4 | # there is talk of possible perl compilations where -T is fatal or just |
5 | 5 | # doesn't work. We don't want to have the user deal with that. |
6 | 6 | BEGIN { unless ($INC{'t/lib/DBICTest/WithTaint.pm'}) { |
7 | ||
8 | if ( $^O eq 'MSWin32' and $^X =~ /\x20/ ) { | |
9 | print "1..0 # SKIP Running this test on Windows with spaces within the perl executable path (\$^X) is not possible due to https://rt.perl.org/Ticket/Display.html?id=123907\n"; | |
10 | exit 0; | |
11 | } | |
7 | 12 | |
8 | 13 | # it is possible the test itself is initially invoked in taint mode |
9 | 14 | # and with relative paths *and* with a relative $^X and some other |
391 | 391 | sub { die "DBICTestTimeout" }, |
392 | 392 | )); |
393 | 393 | |
394 | alarm(2); | |
395 | 394 | $artist2 = $schema2->resultset('Artist')->find(1); |
396 | 395 | $artist2->name('fooey'); |
396 | ||
397 | # FIXME - this needs to go away in lieu of a non-retrying runner | |
398 | # ( i.e. after solving RT#47005 ) | |
399 | local *DBIx::Class::Storage::DBI::_ping = sub { 1 }, DBIx::Class::_ENV_::OLD_MRO && Class::C3->reinitialize() | |
400 | if DBIx::Class::_Util::modver_gt_or_eq( 'DBD::Pg' => '3.5.0' ); | |
401 | ||
402 | alarm(1); | |
397 | 403 | $artist2->update; |
398 | 404 | }; |
399 | 405 |
7 | 7 | use DBIx::Class::Optional::Dependencies (); |
8 | 8 | |
9 | 9 | use lib qw(t/lib); |
10 | ||
11 | use DBICTest::Schema::BindType; | |
12 | BEGIN { | |
13 | DBICTest::Schema::BindType->add_columns( | |
14 | 'blb2' => { | |
15 | data_type => 'blob', | |
16 | is_nullable => 1, | |
17 | }, | |
18 | 'clb2' => { | |
19 | data_type => 'clob', | |
20 | is_nullable => 1, | |
21 | } | |
22 | ); | |
23 | } | |
24 | ||
10 | 25 | use DBICTest; |
11 | 26 | |
12 | 27 | my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_ORA_${_}" } qw/DSN USER PASS/}; |
85 | 100 | |
86 | 101 | my $str = $binstr{$size}; |
87 | 102 | lives_ok { |
88 | $rs->create( { 'id' => $id, blob => "blob:$str", clob => "clob:$str" } ) | |
103 | $rs->create( { 'id' => $id, blob => "blob:$str", clob => "clob:$str", blb2 => "blb2:$str", clb2 => "clb2:$str" } ) | |
89 | 104 | } "inserted $size without dying"; |
90 | 105 | |
91 | 106 | my %kids = %{$schema->storage->_dbh->{CachedKids}}; |
98 | 113 | is @objs, 1, 'One row found matching on both LOBs'; |
99 | 114 | ok (try { $objs[0]->blob }||'' eq "blob:$str", 'blob inserted/retrieved correctly'); |
100 | 115 | ok (try { $objs[0]->clob }||'' eq "clob:$str", 'clob inserted/retrieved correctly'); |
116 | ok (try { $objs[0]->clb2 }||'' eq "clb2:$str", "clb2 inserted correctly"); | |
117 | ok (try { $objs[0]->blb2 }||'' eq "blb2:$str", "blb2 inserted correctly"); | |
101 | 118 | |
102 | 119 | { |
103 | 120 | local $TODO = '-like comparison on blobs not tested before ora 10 (fails on 8i)' |
122 | 139 | |
123 | 140 | lives_ok { |
124 | 141 | $rs->search({ id => $id, blob => "blob:$str", clob => "clob:$str" }) |
125 | ->update({ blob => 'updated blob', clob => 'updated clob' }); | |
142 | ->update({ blob => 'updated blob', clob => 'updated clob', clb2 => 'updated clb2', blb2 => 'updated blb2' }); | |
126 | 143 | } 'blob UPDATE with blobs in WHERE clause survived'; |
127 | 144 | |
128 | 145 | @objs = $rs->search({ blob => "updated blob", clob => 'updated clob' })->all; |
129 | 146 | is @objs, 1, 'found updated row'; |
130 | 147 | ok (try { $objs[0]->blob }||'' eq "updated blob", 'blob updated/retrieved correctly'); |
131 | 148 | ok (try { $objs[0]->clob }||'' eq "updated clob", 'clob updated/retrieved correctly'); |
149 | ok (try { $objs[0]->clb2 }||'' eq "updated clb2", "clb2 updated correctly"); | |
150 | ok (try { $objs[0]->blb2 }||'' eq "updated blb2", "blb2 updated correctly"); | |
132 | 151 | |
133 | 152 | lives_ok { |
134 | 153 | $rs->search({ id => $id }) |
159 | 178 | |
160 | 179 | do_clean($dbh); |
161 | 180 | |
162 | $dbh->do("CREATE TABLE ${q}bindtype_test${q} (${q}id${q} integer NOT NULL PRIMARY KEY, ${q}bytea${q} integer NULL, ${q}blob${q} blob NULL, ${q}blob2${q} blob NULL, ${q}clob${q} clob NULL, ${q}clob2${q} clob NULL, ${q}a_memo${q} integer NULL)"); | |
181 | $dbh->do("CREATE TABLE ${q}bindtype_test${q} (${q}id${q} integer NOT NULL PRIMARY KEY, ${q}bytea${q} integer NULL, ${q}blob${q} blob NULL, ${q}blb2${q} blob NULL, ${q}clob${q} clob NULL, ${q}clb2${q} clob NULL, ${q}a_memo${q} integer NULL)"); | |
163 | 182 | } |
164 | 183 | |
165 | 184 | # clean up our mess |
8 | 8 | |
9 | 9 | use lib qw(t/lib); |
10 | 10 | use DBICTest; |
11 | use DBIx::Class::_Util qw(sigwarn_silencer modver_gt_or_eq); | |
11 | use DBIx::Class::_Util qw( sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt ); | |
12 | 12 | |
13 | 13 | # check that we work somewhat OK with braindead SQLite transaction handling |
14 | 14 | # |
159 | 159 | $_[1]->do('ALTER TABLE artist ADD COLUMN bigint BIGINT'); |
160 | 160 | }); |
161 | 161 | |
162 | my $sqlite_broken_bigint = ( | |
163 | modver_gt_or_eq('DBD::SQLite', '1.34') and ! modver_gt_or_eq('DBD::SQLite', '1.37') | |
164 | ); | |
162 | my $sqlite_broken_bigint = modver_gt_or_eq_and_lt( 'DBD::SQLite', '1.34', '1.37' ); | |
165 | 163 | |
166 | 164 | # 63 bit integer |
167 | 165 | my $many_bits = (Math::BigInt->new(2) ** 62); |
4 | 4 | use lib qw(t/lib); |
5 | 5 | use DBICTest; |
6 | 6 | use Test::More; |
7 | ||
8 | plan tests => 15; | |
9 | 7 | |
10 | 8 | my $schema = DBICTest->init_schema(); |
11 | 9 | my $rs = $schema->resultset( 'CD' ); |
130 | 128 | is_deeply( $result, $expected ); |
131 | 129 | } |
132 | 130 | |
131 | { | |
132 | my $a = [ { 'artist' => { 'manager' => {} } }, 'cd' ]; | |
133 | my $b = [ 'artist', { 'artist' => { 'manager' => {} } } ]; | |
134 | my $expected = [ { 'artist' => { 'manager' => {} } }, 'cd', { 'artist' => { 'manager' => {} } } ]; | |
135 | my $result = $rs->_merge_joinpref_attr($a, $b); | |
136 | is_deeply( $result, $expected ); | |
137 | } | |
133 | 138 | |
134 | 1; | |
139 | { | |
140 | my $a = [ { 'artist' => { 'manager' => undef } }, 'cd' ]; | |
141 | my $b = [ 'artist', { 'artist' => { 'manager' => undef } } ]; | |
142 | my $expected = [ { 'artist' => { 'manager' => undef } }, 'cd', { 'artist' => { 'manager' => undef } } ]; | |
143 | my $result = $rs->_merge_joinpref_attr($a, $b); | |
144 | is_deeply( $result, $expected ); | |
145 | } | |
146 | ||
147 | done_testing; |
60 | 60 | ); |
61 | 61 | is( $it->count, 1, "complex abstract count ok" ); |
62 | 62 | |
63 | # cleanup globals so we do not trigger the leaktest | |
64 | for ( map { DBICTest->schema->class($_) } DBICTest->schema->sources ) { | |
65 | $_->class_resolver(undef); | |
66 | $_->resultset_instance(undef); | |
67 | $_->result_source_instance(undef); | |
68 | } | |
69 | { | |
70 | no warnings qw/redefine once/; | |
71 | *DBICTest::schema = sub {}; | |
72 | } | |
73 | ||
74 | 63 | done_testing; |
6 | 6 | use lib qw(t/lib); |
7 | 7 | use DBICTest; |
8 | 8 | |
9 | plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_rdbms_oracle') | |
10 | unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_oracle'); | |
11 | ||
12 | 9 | my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_ORA_${_}" } qw/DSN USER PASS/}; |
13 | 10 | |
14 | 11 | if (not ($dsn && $user && $pass)) { |
15 | 12 | plan skip_all => 'Set $ENV{DBICTEST_ORA_DSN}, _USER and _PASS to run this test. ' . |
16 | 13 | 'Warning: This test drops and creates a table called \'event\''; |
17 | 14 | } |
15 | ||
16 | plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_rdbms_oracle') | |
17 | unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_oracle'); | |
18 | ||
18 | 19 | |
19 | 20 | # DateTime::Format::Oracle needs this set |
20 | 21 | $ENV{NLS_DATE_FORMAT} = 'DD-MON-YY'; |
2 | 2 | |
3 | 3 | use Test::More; |
4 | 4 | |
5 | use DBIx::Class::_Util 'modver_gt_or_eq'; | |
5 | use DBIx::Class::_Util 'modver_gt_or_eq_and_lt'; | |
6 | 6 | use base(); |
7 | 7 | BEGIN { |
8 | 8 | plan skip_all => 'base.pm 2.20 (only present in perl 5.19.7) is known to break this test' |
9 | if modver_gt_or_eq(base => '2.19_01') and ! modver_gt_or_eq(base => '2.21'); | |
9 | if modver_gt_or_eq_and_lt( 'base', '2.19_01', '2.21' ); | |
10 | 10 | } |
11 | 11 | |
12 | 12 | use Test::Exception; |
197 | 197 | |
198 | 198 | sub is_smoker { |
199 | 199 | return |
200 | ( ($ENV{TRAVIS}||'') eq 'true' ) | |
200 | ( ($ENV{TRAVIS}||'') eq 'true' and ($ENV{TRAVIS_REPO_SLUG}||'') eq 'dbsrgits/dbix-class' ) | |
201 | 201 | || |
202 | 202 | ( $ENV{AUTOMATED_TESTING} && ! $ENV{PERL5_CPANM_IS_RUNNING} && ! $ENV{RELEASE_TESTING} ) |
203 | 203 | ; |
7 | 7 | use DBIx::Class::_Util qw(refcount hrefaddr refdesc); |
8 | 8 | use DBIx::Class::Optional::Dependencies; |
9 | 9 | use Data::Dumper::Concise; |
10 | use DBICTest::Util 'stacktrace'; | |
10 | use DBICTest::Util qw( stacktrace visit_namespaces ); | |
11 | 11 | use constant { |
12 | 12 | CV_TRACING => DBIx::Class::Optional::Dependencies->req_ok_for ('test_leaks_heavy'), |
13 | SKIP_SCALAR_REFS => ( $] > 5.017 ) ? 1 : 0, | |
14 | 13 | }; |
15 | 14 | |
16 | 15 | use base 'Exporter'; |
41 | 40 | (defined $reg->{$_}{weakref}) or delete $reg->{$_} |
42 | 41 | for keys %$reg; |
43 | 42 | } |
44 | ||
45 | # FIXME/INVESTIGATE - something fishy is going on with refs to plain | |
46 | # strings, perhaps something to do with the CoW work etc... | |
47 | return $target if SKIP_SCALAR_REFS and reftype($target) eq 'SCALAR'; | |
48 | 43 | |
49 | 44 | if (! defined $weak_registry->{$refaddr}{weakref}) { |
50 | 45 | $weak_registry->{$refaddr} = { |
140 | 135 | elsif (CV_TRACING and $type eq 'CODE') { |
141 | 136 | $visited_cnt += visit_refs({ %$args, refs => [ map { |
142 | 137 | ( !isweak($_) ) ? $_ : () |
143 | } scalar PadWalker::closed_over($r) ] }); # scalar due to RT#92269 | |
138 | } values %{ scalar PadWalker::closed_over($r) } ] }); # scalar due to RT#92269 | |
144 | 139 | } |
145 | 140 | 1; |
146 | 141 | } or warn "Could not descend into @{[ refdesc $r ]}: $@\n"; |
147 | 142 | } |
148 | 143 | $visited_cnt; |
149 | } | |
150 | ||
151 | sub visit_namespaces { | |
152 | my $args = { (ref $_[0]) ? %{$_[0]} : @_ }; | |
153 | ||
154 | my $visited = 1; | |
155 | ||
156 | $args->{package} ||= '::'; | |
157 | $args->{package} = '::' if $args->{package} eq 'main'; | |
158 | ||
159 | if ( $args->{action}->($args->{package}) ) { | |
160 | ||
161 | my $base = $args->{package}; | |
162 | $base = '' if $base eq '::'; | |
163 | ||
164 | ||
165 | $visited += visit_namespaces({ %$args, package => $_ }) for map | |
166 | { $_ =~ /(.+?)::$/ ? "${base}::$1" : () } | |
167 | grep | |
168 | { $_ =~ /(?<!^main)::$/ } | |
169 | do { no strict 'refs'; keys %{ $base . '::'} } | |
170 | } | |
171 | ||
172 | return $visited; | |
173 | 144 | } |
174 | 145 | |
175 | 146 | # compiles a list of addresses stored as globals (possibly even catching |
178 | 149 | |
179 | 150 | my $refs_per_pkg; |
180 | 151 | |
181 | my $dummy_addresslist; | |
182 | ||
183 | 152 | my $seen_refs = {}; |
184 | 153 | visit_namespaces( |
185 | 154 | action => sub { |
187 | 156 | no strict 'refs'; |
188 | 157 | |
189 | 158 | my $pkg = shift; |
190 | $pkg = '' if $pkg eq '::'; | |
191 | $pkg .= '::'; | |
192 | 159 | |
193 | 160 | # the unless regex at the end skips some dangerous namespaces outright |
194 | 161 | # (but does not prevent descent) |
195 | 162 | $refs_per_pkg->{$pkg} += visit_refs ( |
196 | 163 | seen_refs => $seen_refs, |
197 | 164 | |
198 | # FIXME FIXME FIXME | |
199 | # This is so damn odd - if we feed a constsub {1} (or in fact almost | |
200 | # anything other than the actionsub below, any scalarref will show | |
201 | # up as a leak, trapped by... something... | |
202 | # Ideally we should be able to const this to sub{1} and just return | |
203 | # $seen_refs (in fact it is identical to the dummy list at the end of | |
204 | # a run here). Alas this doesn't seem to work, so punt for now... | |
205 | action => sub { ++$dummy_addresslist->{ hrefaddr $_[0] } }, | |
165 | action => sub { 1 }, | |
206 | 166 | |
207 | 167 | refs => [ map { my $sym = $_; |
208 | # *{"$pkg$sym"}{CODE} won't simply work - MRO-cached CVs are invisible there | |
209 | ( CV_TRACING ? Class::MethodCache::get_cv("${pkg}$sym") : () ), | |
210 | ||
211 | ( defined *{"$pkg$sym"}{SCALAR} and length ref ${"$pkg$sym"} and ! isweak( ${"$pkg$sym"} ) ) | |
212 | ? ${"$pkg$sym"} : () | |
168 | # *{"${pkg}::$sym"}{CODE} won't simply work - MRO-cached CVs are invisible there | |
169 | ( CV_TRACING ? Class::MethodCache::get_cv("${pkg}::$sym") : () ), | |
170 | ||
171 | ( defined *{"${pkg}::$sym"}{SCALAR} and length ref ${"${pkg}::$sym"} and ! isweak( ${"${pkg}::$sym"} ) ) | |
172 | ? ${"${pkg}::$sym"} : () | |
213 | 173 | , |
214 | 174 | |
215 | 175 | ( map { |
216 | ( defined *{"$pkg$sym"}{$_} and ! isweak(defined *{"$pkg$sym"}{$_}) ) | |
217 | ? *{"$pkg$sym"}{$_} | |
176 | ( defined *{"${pkg}::$sym"}{$_} and ! isweak(defined *{"${pkg}::$sym"}{$_}) ) | |
177 | ? *{"${pkg}::$sym"}{$_} | |
218 | 178 | : () |
219 | 179 | } qw(HASH ARRAY IO GLOB) ), |
220 | 180 | |
221 | } keys %$pkg ], | |
222 | ) unless $pkg =~ /^ :: (?: | |
181 | } keys %{"${pkg}::"} ], | |
182 | ) unless $pkg =~ /^ (?: | |
223 | 183 | DB | next | B | .+? ::::ISA (?: ::CACHE ) | Class::C3 |
224 | ) :: $/x; | |
184 | ) $/x; | |
225 | 185 | } |
226 | 186 | ); |
227 | 187 | |
238 | 198 | |
239 | 199 | sub assert_empty_weakregistry { |
240 | 200 | my ($weak_registry, $quiet) = @_; |
241 | ||
242 | Sub::Defer::undefer_all(); | |
243 | 201 | |
244 | 202 | # in case we hooked bless any extra object creation will wreak |
245 | 203 | # havoc during the assert phase |
365 | 323 | $tb->note("Auto checked $refs_traced references for leaks - none detected"); |
366 | 324 | } |
367 | 325 | |
368 | # Disable this until better times - SQLT and probably other things | |
369 | # still load strictures. Let's just wait until Moo2.0 and go from there | |
370 | =begin for tears | |
371 | 326 | # also while we are here and not in plain runmode: make sure we never |
372 | 327 | # loaded any of the strictures XS bullshit (it's a leak in a sense) |
373 | unless (DBICTest::RunMode->is_plain) { | |
328 | unless ( | |
329 | $ENV{MOO_FATAL_WARNINGS} | |
330 | or | |
331 | # FIXME - SQLT loads strictures explicitly, /facedesk | |
332 | # remove this INC check when 0fb58589 and 45287c815 are rectified | |
333 | $INC{'SQL/Translator.pm'} | |
334 | or | |
335 | DBICTest::RunMode->is_plain | |
336 | ) { | |
374 | 337 | for (qw(indirect multidimensional bareword::filehandles)) { |
375 | 338 | exists $INC{ Module::Runtime::module_notional_filename($_) } |
376 | 339 | and |
377 | 340 | $tb->ok(0, "$_ load should not have been attempted!!!" ) |
378 | 341 | } |
379 | 342 | } |
380 | =cut | |
381 | ||
382 | 343 | } |
383 | 344 | } |
384 | 345 |
16 | 16 | } |
17 | 17 | } |
18 | 18 | |
19 | use Module::Runtime 'module_notional_filename'; | |
20 | BEGIN { | |
21 | for my $mod (qw( SQL::Abstract::Test SQL::Abstract )) { | |
22 | if ( $INC{ module_notional_filename($mod) } ) { | |
23 | # FIXME this does not seem to work in BEGIN - why?! | |
24 | #require Carp; | |
25 | #$Carp::Internal{ (__PACKAGE__) }++; | |
26 | #Carp::croak( __PACKAGE__ . " must be loaded before $mod" ); | |
27 | ||
28 | my ($fr, @frame) = 1; | |
29 | while (@frame = caller($fr++)) { | |
30 | last if $frame[1] !~ m|^t/lib/DBICTest|; | |
31 | } | |
32 | ||
33 | die __PACKAGE__ . " must be loaded before $mod (or modules using $mod) at $frame[1] line $frame[2]\n"; | |
34 | } | |
35 | } | |
36 | } | |
37 | ||
38 | 19 | use Config; |
39 | 20 | use Carp 'confess'; |
40 | 21 | use Scalar::Util qw(blessed refaddr); |
22 | use DBIx::Class::_Util; | |
41 | 23 | |
42 | 24 | use base 'Exporter'; |
43 | our @EXPORT_OK = qw(local_umask stacktrace check_customcond_args); | |
25 | our @EXPORT_OK = qw(local_umask stacktrace check_customcond_args visit_namespaces); | |
44 | 26 | |
45 | 27 | sub local_umask { |
46 | 28 | return unless defined $Config{d_umask}; |
56 | 38 | { |
57 | 39 | package DBICTest::Util::UmaskGuard; |
58 | 40 | sub DESTROY { |
41 | &DBIx::Class::_Util::detected_reinvoked_destructor; | |
42 | ||
59 | 43 | local ($@, $!); |
60 | 44 | eval { defined (umask ${$_[0]}) or die }; |
61 | 45 | warn ( "Unable to reset old umask ${$_[0]}: " . ($!||'Unknown error') ) |
124 | 108 | $args; |
125 | 109 | } |
126 | 110 | |
111 | sub visit_namespaces { | |
112 | my $args = { (ref $_[0]) ? %{$_[0]} : @_ }; | |
113 | ||
114 | my $visited_count = 1; | |
115 | ||
116 | # A package and a namespace are subtly different things | |
117 | $args->{package} ||= 'main'; | |
118 | $args->{package} = 'main' if $args->{package} =~ /^ :: (?: main )? $/x; | |
119 | $args->{package} =~ s/^:://; | |
120 | ||
121 | if ( $args->{action}->($args->{package}) ) { | |
122 | my $ns = | |
123 | ( ($args->{package} eq 'main') ? '' : $args->{package} ) | |
124 | . | |
125 | '::' | |
126 | ; | |
127 | ||
128 | $visited_count += visit_namespaces( %$args, package => $_ ) for | |
129 | grep | |
130 | # this happens sometimes on %:: traversal | |
131 | { $_ ne '::main' } | |
132 | map | |
133 | { $_ =~ /^(.+?)::$/ ? "$ns$1" : () } | |
134 | do { no strict 'refs'; keys %$ns } | |
135 | ; | |
136 | } | |
137 | ||
138 | return $visited_count; | |
139 | } | |
140 | ||
127 | 141 | 1; |
6 | 6 | use DBICTest::Util 'local_umask'; |
7 | 7 | use DBICTest::Schema; |
8 | 8 | use DBICTest::Util::LeakTracer qw/populate_weakregistry assert_empty_weakregistry/; |
9 | use DBIx::Class::_Util 'detected_reinvoked_destructor'; | |
9 | 10 | use Carp; |
10 | 11 | use Path::Class::File (); |
11 | 12 | use File::Spec; |
141 | 142 | |
142 | 143 | $SIG{INT} = sub { _cleanup_dbfile(); exit 1 }; |
143 | 144 | |
145 | my $need_global_cleanup; | |
144 | 146 | sub _cleanup_dbfile { |
145 | 147 | # cleanup if this is us |
146 | 148 | if ( |
150 | 152 | or |
151 | 153 | $ENV{DBICTEST_LOCK_HOLDER} == $$ |
152 | 154 | ) { |
155 | if ($need_global_cleanup and my $dbh = DBICTest->schema->storage->_dbh) { | |
156 | $dbh->disconnect; | |
157 | } | |
158 | ||
153 | 159 | my $db_file = _sqlite_dbfilename(); |
154 | 160 | unlink $_ for ($db_file, "${db_file}-journal"); |
155 | 161 | } |
216 | 222 | $dbh->{Callbacks} = { |
217 | 223 | connect => sub { $guard_cb->('connect') }, |
218 | 224 | disconnect => sub { $guard_cb->('disconnect') }, |
219 | DESTROY => sub { $guard_cb->('DESTROY') }, | |
225 | DESTROY => sub { &detected_reinvoked_destructor; $guard_cb->('DESTROY') }, | |
220 | 226 | }; |
221 | 227 | } |
222 | 228 | }, |
314 | 320 | my $schema; |
315 | 321 | |
316 | 322 | if ($args{compose_connection}) { |
323 | $need_global_cleanup = 1; | |
317 | 324 | $schema = DBICTest::Schema->compose_connection( |
318 | 325 | 'DBICTest', $self->_database(%args) |
319 | 326 | ); |
127 | 127 | ], |
128 | 128 | 'Expected SQL on correlated realiased subquery' |
129 | 129 | ); |
130 | ||
131 | $schema->storage->disconnect; | |
130 | 132 | |
131 | 133 | # test for subselect identifier leakage |
132 | 134 | # NOTE - the hodge-podge mix of literal and regular identifuers is *deliberate* |
145 | 145 | ); |
146 | 146 | } |
147 | 147 | |
148 | $schema->storage->_dbh->disconnect; | |
149 | ||
148 | 150 | # make sure connection-less storages do not throw on _determine_driver |
149 | 151 | # but work with ENV at the same time |
150 | 152 | SKIP: for my $env_dsn (undef, (DBICTest->_database)[0] ) { |
216 | 216 | is(scalar @w, 0, 'no warnings \o/'); |
217 | 217 | } |
218 | 218 | |
219 | # ensure Devel::StackTrace-refcapture-like effects are countered | |
220 | { | |
221 | my $s = DBICTest::Schema->connect('dbi:SQLite::memory:'); | |
222 | my $g = $s->txn_scope_guard; | |
223 | ||
224 | my @arg_capture; | |
225 | { | |
226 | local $SIG{__WARN__} = sub { | |
227 | package DB; | |
228 | my $frnum; | |
229 | while (my @f = caller(++$frnum) ) { | |
230 | push @arg_capture, @DB::args; | |
231 | } | |
232 | }; | |
233 | ||
234 | undef $g; | |
235 | 1; | |
236 | } | |
237 | ||
238 | warnings_exist | |
239 | { @arg_capture = () } | |
240 | qr/\QPreventing *MULTIPLE* DESTROY() invocations on DBIx::Class::Storage::TxnScopeGuard/ | |
241 | ; | |
242 | } | |
243 | ||
219 | 244 | done_testing; |
0 | use warnings; | |
1 | use strict; | |
2 | ||
3 | use Test::More 'no_plan'; | |
4 | ||
5 | my $authorcount = scalar do { | |
6 | open (my $fh, '<', 'AUTHORS') or die "Unable to open AUTHORS - can't happen: $!\n"; | |
7 | map { chomp; ( ( ! $_ or $_ =~ /^\s*\#/ ) ? () : $_ ) } <$fh>; | |
8 | } or die "Known AUTHORS file seems empty... can't happen..."; | |
9 | ||
10 | # do not announce anything under travis - we are watching for STDERR silence | |
11 | diag "\n\n$authorcount contributors made this library what it is today\n\n" | |
12 | unless ($ENV{TRAVIS}||'') eq 'true'; | |
13 | ||
14 | ok 1; |
37 | 37 | |
38 | 38 | my $email_re = qr/( \< [^\<\>]+ \> ) $/x; |
39 | 39 | |
40 | my (%known_authors, $count); | |
40 | my %known_authors; | |
41 | 41 | for (@known_authors) { |
42 | 42 | my ($name_email) = m/ ^ (?: [^\:]+ \: \s )? (.+) /x; |
43 | 43 | my ($email) = $name_email =~ $email_re; |
44 | 44 | |
45 | if ( | |
45 | fail "Duplicate found: $name_email" if ( | |
46 | 46 | $known_authors{$name_email}++ |
47 | 47 | or |
48 | 48 | ( $email and $known_authors{$email}++ ) |
49 | ) { | |
50 | fail "Duplicate found: $name_email"; | |
51 | } | |
52 | else { | |
53 | $count++; | |
54 | } | |
49 | ); | |
55 | 50 | } |
56 | ||
57 | # do not announce anything under travis - we are watching for STDERR silence | |
58 | diag "\n\n$count contributors made this library what it is today\n\n" | |
59 | unless ($ENV{TRAVIS}||'') eq 'true'; | |
60 | 51 | |
61 | 52 | # augh taint mode |
62 | 53 | if (length $ENV{PATH}) { |