File Coverage

blib/lib/Mojolicious/Plugin/DSC.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::DSC;
2 2     2   3247 use Mojo::Base 'Mojolicious::Plugin';
  2         4  
  2         13  
3 2     2   644 use DBIx::Simple::Class;
  0            
  0            
4             use Mojo::Util qw(camelize);
5             use Carp;
6              
7             our $VERSION = '1.004';
8              
9             #some known good defaults
10             my $COMMON_ATTRIBUTES = {
11             RaiseError => 1,
12             AutoCommit => 1,
13             };
14              
15             has config => sub { {} };
16              
17             sub register {
18             my ($self, $app, $config) = @_;
19              
20             # This stuff is executed, when the plugin is loaded
21             # Config
22             $config //= {};
23             $config->{load_classes} //= [];
24             $config->{DEBUG} //= ($app->mode =~ m|^dev|);
25             $config->{dbh_attributes} //= {};
26             $config->{database} //= '';
27             croak('"load_classes" configuration directive '
28             . 'must be an ARRAY reference containing a list of classes to load.')
29             unless (ref($config->{load_classes}) eq 'ARRAY');
30             croak('"dbh_attributes" configuration directive '
31             . 'must be a HASH reference. See DBI/Database_Handle_Attributes.')
32             unless (ref($config->{dbh_attributes}) eq 'HASH');
33              
34             #prepared Data Source Name?
35             if (!$config->{dsn}) {
36             $config->{driver}
37             || croak('Please choose and set a database driver like "mysql","SQLite","Pg"!..');
38             croak('Please set "database"!') unless $config->{database} =~ m/\w+/x;
39             $config->{host} ||= 'localhost';
40             $config->{dsn} = 'dbi:'
41             . $config->{driver}
42             . ':database='
43             . $config->{database}
44             . ';host='
45             . $config->{host}
46             . ($config->{port} ? ';port=' . $config->{port} : '');
47              
48             if ($config->{database} =~ m/(\w+)/x) {
49             $config->{namespace} = camelize($1) unless $config->{namespace};
50             }
51             }
52             else {
53             my ($scheme, $driver, $attr_string, $attr_hash, $driver_dsn) =
54             DBI->parse_dsn($config->{dsn})
55             || croak("Can't parse DBI DSN! dsn=>'$config->{dsn}'");
56             $config->{driver} = $driver;
57              
58             $scheme =~ m/(database|dbname)=\W?(\w+)/x and do {
59             $config->{namespace} ||= camelize($2);
60             };
61             $config->{dbh_attributes} =
62             {%{$config->{dbh_attributes}}, ($attr_hash ? %$attr_hash : ())};
63             }
64              
65             $config->{onconnect_do} ||= [];
66              
67             #Postpone connecting to the database till the first helper call.
68             my $helper_builder = sub {
69              
70             #ready... Go!
71             my $dbix = DBIx::Simple->connect(
72             $config->{dsn},
73             $config->{user} || '',
74             $config->{password} || '',
75             {%$COMMON_ATTRIBUTES, %{$config->{dbh_attributes}}}
76             );
77             if (!ref($config->{onconnect_do})) {
78             $config->{onconnect_do} = [$config->{onconnect_do}];
79             }
80             for my $sql (@{$config->{onconnect_do}}) {
81             next unless $sql;
82             if (ref($sql) eq 'CODE') { $sql->($dbix); next; }
83             $dbix->dbh->do($sql);
84             }
85             my $DSCS = $config->{namespace};
86             my $schema = Mojo::Util::class_to_path($DSCS);
87             if (eval { require $schema; }) {
88             $DSCS->DEBUG($config->{DEBUG});
89             $DSCS->dbix($dbix);
90             }
91             else {
92             Carp::carp("($@) Trying to continue without $schema...");
93             DBIx::Simple::Class->DEBUG($config->{DEBUG});
94             DBIx::Simple::Class->dbix($dbix);
95             }
96             $self->_load_classes($app, $config);
97             return $dbix;
98             };
99              
100             #Add $dbix as attribute and helper where needed
101             my $dbix_helper = $config->{dbix_helper} ||= 'dbix';
102             $app->helper($dbix_helper, $helper_builder);
103             $self->config({%$config}); #copy
104             $app->$dbix_helper() if (!$config->{postpone_connect});
105             return $self;
106             } #end register
107              
108              
109             sub _load_classes {
110             my ($self, $app, $config) = @_;
111             state $load_error = <<"ERR";
112             You may need to create it first using the dsc_dump_schema.pl script.'
113             Try: dsc_dump_schema.pl --help'
114             ERR
115              
116             if (scalar @{$config->{load_classes}}) {
117             my @classes = @{$config->{load_classes}};
118             my $namespace = $config->{namespace};
119             $namespace .= '::' unless $namespace =~ /:{2}$/;
120             foreach my $class (@classes) {
121             if ($class =~ /^$namespace/) {
122             my $e = Mojo::Loader::load_class($class);
123             Carp::confess(ref $e ? "Exception: $e" : "$class not found: ($load_error)")
124             if $e;
125             next;
126             }
127             my $e = Mojo::Loader::load_class($namespace . $class);
128             if (ref $e) {
129             Carp::confess("Exception: $e");
130             }
131             elsif ($e) {
132             my $e2 = Mojo::Loader::load_class($class);
133             Carp::confess(ref $e2 ? "Exception: $e2" : "$class not found: ($load_error)")
134             if $e2;
135             }
136             }
137             }
138             else { #no load_classes
139             my @classes = Mojo::Loader::find_modules($config->{namespace});
140             foreach my $class (@classes) {
141             my $e = Mojo::Loader::load_class($class);
142             croak($e) if $e;
143             }
144             }
145             return;
146             }
147              
148             1;
149              
150             =pod
151              
152             =encoding utf8
153              
154             =head1 NAME
155              
156             Mojolicious::Plugin::DSC - use DBIx::Simple::Class in your application.
157              
158             =head1 SYNOPSIS
159              
160             #load
161             # Mojolicious
162             $self->plugin('DSC', $config);
163              
164             # Mojolicious::Lite
165             plugin 'DSC', $config;
166            
167             my $user = My::User->find(1234);
168             #or
169             my $user = My::User->query('SELECT * FROM users WHERE user=?','ivan');
170             #or if SQL::Abstract is isnstalled
171             my $user = My::User->select(user=>'ivan');
172            
173            
174             =head1 DESCRIPTION
175              
176             Mojolicious::Plugin::DSC is a L plugin that helps you
177             use L in your application.
178             It also adds an app attribute (C<$app-Edbix>) and controller helper (C<$c-Edbix>)
179             which is a L instance.
180              
181             =head1 CONFIGURATION
182              
183             The configuration is pretty flexible:
184              
185             # in Mojolicious startup()
186             $self->plugin('DSC', {
187             dsn => 'dbi:SQLite:database=:memory:;host=localhost'
188             });
189             #or
190             $self->plugin('DSC', {
191             driver => 'mysqlPP',
192             database => 'mydbname',
193             host => '127.0.0.1',
194             user => 'myself',
195             password => 'secret',
196             onconnect_do => [
197             'SET NAMES UTF8',
198             'SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"'
199             sub{my $dbix = shift; do_something_complicated($dbix)}
200             ],
201             dbh_attributes => {AutoCommit=>0},
202             namespace => 'My',
203            
204             #will load My::User, My::Content, My::Pages
205             load_classes =>['User', 'Content', 'My::Pages'],
206            
207             #now you can use $app->DBIX instead of $app->dbix
208             dbix_helper => 'DBIX'
209             });
210              
211             The following parameters can be provided:
212              
213             =head2 load_classes
214              
215             An ARRAYREF of classes to be loaded. If not provided,
216             all classes under L will be loaded.
217             Classes are expected to be already dumped as files using
218             C from an existing database.
219              
220             #all classes under My::Schema::Class
221             $app->plugin('DSC', {
222             namespace => My::Schema::Class,
223             });
224             #only My::Schema::Class::Groups and My::Schema::Class::Users
225             $app->plugin('DSC', {
226             namespace => My::Schema::Class,
227             load_classes => ['Groups', 'Users']
228             });
229              
230             =head2 DEBUG
231              
232             Boolean. When the current L is C this value
233             is 1.
234              
235             $app->plugin('DSC', {
236             DEBUG => 1,
237             namespace => My::Schema::Class,
238             load_classes => ['Groups', 'Users']
239             });
240              
241             =head2 dbh_attributes
242              
243             HASHREF. Attributes passed to L.
244             Default values are:
245              
246             {
247             RaiseError => 1,
248             AutoCommit => 1,
249             };
250              
251             They can be overriden:
252              
253             $app->plugin('DSC', {
254             namespace => My::Schema::Class,
255             dbh_attributes =>{ AutoCommit => 0, sqlite_unicode => 1 }
256             });
257              
258             =head2 dsn
259              
260             Connection string parsed using L and passed to L.
261              
262             From this string we guess the L, L, L, L
263             and the L which ends up as camelised form of
264             the L name.
265              
266             If L is not passed most of the configuration values above must
267             be provided so a valid connection string can be constructed.
268             If L is provided it will be preferred over the above parameters
269             (excluding namespace) because the developer should know better how
270             exactly to connect to the database.
271              
272             $app->plugin('DSC', {
273             namespace => My::Schema::Class,
274             dbh_attributes => {sqlite_unicode => 1},
275             dsn => 'dbi:SQLite:database=myfile.sqlite'
276             });
277              
278             =head2 driver
279              
280             String. One of "mysql","SQLite","Pg" etc...
281             This string is prepended with "dbi:". No default value.
282              
283             $app->plugin('DSC', {
284             driver => 'mysql',
285             dbh_attributes => {sqlite_unicode => 1},
286             dsn => 'dbi:SQLite:database=myfile.sqlite'
287             });
288              
289             =head2 database
290              
291             String - the database name. No default value.
292              
293             $app->plugin('DSC', {
294             database => app->home->rel_file('etc/ado.sqlite'),
295             dbh_attributes => {sqlite_unicode => 1},
296             driver => 'SQLite',
297             namespace => 'Ado::Model',
298             });
299              
300             =head2 host
301              
302             String. defaults to C.
303              
304             =head2 port
305              
306             String. Not added to the connection string if not provided.
307              
308             =head2 namespace
309              
310             The class name of your schema class. If not provided the value will be guessed
311             from the L or L. It is recommended to provide your
312             schema class name.
313              
314             $app->plugin('DSC', {
315             database => app->home->rel_file('etc/ado.sqlite'),
316             dbh_attributes => {sqlite_unicode => 1},
317             driver => 'SQLite',
318             namespace => 'My::Model',
319             });
320              
321              
322             =head2 user
323              
324             String. Username used to connect to the database.
325              
326             =head2 password
327              
328             String. Password used to connect to the database.
329              
330             =head2 onconnect_do
331              
332             ARRAYREF of SQL statements and callbacks which will be executed right after
333             establiching the connection.
334              
335             $app->plugin('DSC', {
336             database => app->home->rel_file('etc/ado.sqlite'),
337             dbh_attributes => {sqlite_unicode => 1},
338             driver => 'SQLite',
339             namespace => 'Ado::Model',
340             onconnect_do => [
341             'PRAGMA encoding = "UTF-8"',
342             'PRAGMA foreign_keys = ON',
343             'PRAGMA temp_store = 2', #MEMORY
344             'VACUUM',
345             sub{
346             shift->dbh->sqlite_create_function( 'now', 0, sub { return time } );
347             }
348             ],
349             });
350              
351              
352             =head2 postpone_connect
353              
354             Boolean. If set, establishing the connection to the database will
355             be postponed for the first call of C<$app-Edbix> or the method
356             name you provided for the L.
357              
358             =head2 dbix_helper
359              
360             String. The name of the helper method that can be created to invoke/use
361             directly the L instance on your controller or application.
362             Defaults to C.
363              
364             =head1 METHODS
365              
366             L inherits all methods from
367             L and implements the following new ones.
368              
369             =head2 C
370              
371             $plugin->register(Mojolicious->new);
372              
373             Register plugin in L application.
374              
375             =head2 config
376              
377             This plugin own configuration. Returns a HASHref.
378              
379             #debug
380             $app->log->debug($app->dumper($plugin->config));
381              
382             =head1 SEE ALSO
383              
384             L, L, L, L.
385              
386             =head1 LICENSE AND COPYRIGHT
387              
388             Copyright 2012 Красимир Беров (Krasimir Berov).
389              
390             This program is free software, you can redistribute it and/or
391             modify it under the terms of the Artistic License version 2.0.
392              
393             See http://dev.perl.org/licenses/ for more information.
394              
395             =cut