File Coverage

blib/lib/DBICx/Sugar.pm
Criterion Covered Total %
statement 48 79 60.7
branch 23 44 52.2
condition 0 2 0.0
subroutine 10 12 83.3
pod 5 6 83.3
total 86 143 60.1


line stmt bran cond sub pod time code
1             package DBICx::Sugar;
2              
3 5     5   266071 use strict;
  5         6  
  5         114  
4 5     5   15 use warnings;
  5         5  
  5         104  
5 5     5   13 use Carp qw(croak);
  5         5  
  5         166  
6 5     5   17 use Exporter qw(import);
  5         5  
  5         88  
7 5     5   2009 use Module::Load;
  5         3691  
  5         27  
8 5     5   1864 use YAML qw(LoadFile);
  5         22976  
  5         2845  
9              
10             our $VERSION = '0.0100'; # VERSION
11              
12             our @EXPORT_OK = qw(config get_config add_schema_to_config rset resultset schema);
13              
14             my $_config;
15             my $_schemas = {};
16              
17             sub config {
18 25     25 0 2515 my ($data) = @_;
19 25 100       60 if ($data) {
20 4 50       13 croak 'config data must be a hashref' unless 'HASH' eq ref $data;
21 4         7 $_config = $data;
22             }
23 25 50       69 return $_config if $_config;
24 0         0 my $config_path;
25 0 0       0 if (-f 'config.yaml') {
    0          
26 0         0 $config_path = 'config.yaml'
27             } elsif (-f 'config.yml') {
28 0         0 $config_path = 'config.yml';
29             } else {
30 0         0 croak "could not find a config.yml or config.yaml file";
31             }
32 0         0 return $_config = LoadFile($config_path)->{dbicx_sugar};
33             }
34              
35 0     0 1 0 sub get_config { return $_config; }
36              
37             sub add_schema_to_config {
38 0     0 1 0 my ($schema_name, $schema_data) = @_;
39             croak "Schema name $schema_name already exists"
40 0 0       0 if exists $_config->{$schema_name};
41 0 0       0 croak "Schema data must be a hashref (schema name: $schema_name)"
42             unless 'HASH' eq ref $schema_data;
43 0         0 $_config->{$schema_name} = $schema_data;
44             }
45              
46             sub schema {
47 21     21 1 260953 my ($name) = @_;
48 21         40 my $cfg = config();
49              
50 21 100       41 if (not defined $name) {
51 10 100       35 if (keys %$cfg == 1) {
    50          
52 5         9 ($name) = keys %$cfg;
53             } elsif (keys %$cfg) {
54 5         8 $name = "default";
55             } else {
56 0         0 die "No schemas are configured";
57             }
58             }
59              
60 21 100       111 return $_schemas->{$name} if $_schemas->{$name};
61              
62 10 100       60 my $options = $cfg->{$name} or die "The schema $name is not configured";
63 7 100       17 if ( my $alias = $options->{alias} ) {
64 2 100       15 $options = $cfg->{$alias}
65             or die "The schema alias $alias does not exist in the config";
66 1 50       7 return $_schemas->{$alias} if $_schemas->{$alias};
67             }
68              
69             my @conn_info = $options->{connect_info}
70 0         0 ? @{$options->{connect_info}}
71 5 50       25 : @$options{qw(dsn user password options)};
72 5 50       10 if ( exists $options->{pass} ) {
73 0         0 warn "The pass option is deprecated. Use password instead.";
74 0         0 $conn_info[2] = $options->{pass};
75             }
76              
77 5         7 my $schema;
78              
79 5 50       12 if ( my $schema_class = $options->{schema_class} ) {
80 5         11 $schema_class =~ s/-/::/g;
81 5         6 eval { load $schema_class };
  5         18  
82 5 50       314524 die "Could not load schema_class $schema_class: $@" if $@;
83 5 50       23 if ( my $replicated = $options->{replicated} ) {
84 0         0 $schema = $schema_class->clone;
85 0         0 my %storage_options;
86 0         0 my @params = qw( balancer_type balancer_args pool_type pool_args );
87 0         0 for my $p ( @params ) {
88 0         0 my $value = $replicated->{$p};
89 0 0       0 $storage_options{$p} = $value if defined $value;
90             }
91 0         0 $schema->storage_type([ '::DBI::Replicated', \%storage_options ]);
92 0         0 $schema->connection( @conn_info );
93 0         0 $schema->storage->connect_replicants( @{$replicated->{replicants}});
  0         0  
94             } else {
95 5         36 $schema = $schema_class->connect( @conn_info );
96             }
97             } else {
98 0         0 my $dbic_loader = 'DBIx::Class::Schema::Loader';
99 0         0 eval { load $dbic_loader };
  0         0  
100 0 0       0 die "You must provide a schema_class option or install $dbic_loader."
101             if $@;
102 0   0     0 $dbic_loader->naming( $options->{schema_loader_naming} || 'v7' );
103 0         0 $schema = DBIx::Class::Schema::Loader->connect(@conn_info);
104             }
105              
106 5         133924 return $_schemas->{$name} = $schema;
107             };
108              
109             sub resultset {
110 5     5 1 2554 my ($rset_name) = @_;
111 5         12 return schema()->resultset($rset_name);
112             }
113              
114 4     4 1 5079 sub rset { goto &resultset };
115              
116             # ABSTRACT: Just some syntax sugar for DBIx::Class
117              
118              
119             1;
120              
121             __END__
122              
123             =pod
124              
125             =encoding UTF-8
126              
127             =head1 NAME
128              
129             DBICx::Sugar - Just some syntax sugar for DBIx::Class
130              
131             =head1 VERSION
132              
133             version 0.0100
134              
135             =head1 SYNOPSIS
136              
137             use DBICx::Sugar qw(schema resultset rset);
138              
139             # all of the following are equivalent:
140              
141             $user = schema('default')->resultset('User')->find('bob');
142             $user = schema->resultset('User')->find('bob');
143             $user = resultset('User')->find('bob');
144             $user = rset('User')->find('bob');
145              
146             =head1 DESCRIPTION
147              
148             Just some syntax sugar for your DBIx::Class applications.
149             This was originally created to remove code duplication between
150             L<Dancer::Plugin::DBIC> and L<Dancer2::Plugin::DBIC>.
151              
152             =head1 CONFIGURATION
153              
154             Configuration can be automatically parsed from a `config.yaml` or `config.yml`
155             file in the current working directory, or it can be explicitly set with the
156             C<config> function:
157              
158             DBICx::Sugar::config({ default => { dsn => ... } });
159              
160             If you want the config to be autoloaded from a yaml config file, just make sure
161             to put your config data under a top level C<dbicx_sugar> key.
162              
163             =head2 simple example
164              
165             Here is a simple example. It defines one database named C<default>:
166              
167             dbicx_sugar:
168             default:
169             dsn: dbi:SQLite:dbname=myapp.db
170             schema_class: MyApp::Schema
171              
172             =head2 multiple schemas
173              
174             In this example, there are 2 databases configured named C<default> and C<foo>:
175              
176             dbicx_sugar:
177             default:
178             dsn: dbi:SQLite:dbname=myapp.db
179             schema_class: MyApp::Schema
180             foo:
181             dsn: dbi:Pg:dbname=foo
182             schema_class: Foo::Schema
183             user: bob
184             password: secret
185             options:
186             RaiseError: 1
187             PrintError: 1
188              
189             Each database configured must at least have a dsn option.
190             The dsn option should be the L<DBI> driver connection string.
191             All other options are optional.
192              
193             If you only have one schema configured, or one of them is named
194             C<default>, you can call C<schema> without an argument to get the only
195             or C<default> schema, respectively.
196              
197             If a schema_class option is not provided, then L<DBIx::Class::Schema::Loader>
198             will be used to dynamically load the schema by introspecting the database
199             corresponding to the dsn value.
200             You need L<DBIx::Class::Schema::Loader> installed for this to work.
201              
202             WARNING: Dynamic loading is not recommended for production environments.
203             It is almost always better to provide a schema_class option.
204              
205             The schema_class option should be the name of your L<DBIx::Class::Schema> class.
206             See L</"SCHEMA GENERATION">
207             Optionally, a database configuration may have user, password, and options
208             parameters as described in the documentation for C<connect()> in L<DBI>.
209              
210             =head2 connect_info
211              
212             Alternatively, you may also declare your connection information inside an
213             array named C<connect_info>:
214              
215             dbicx_sugar:
216             default:
217             schema_class: MyApp::Schema
218             connect_info:
219             - dbi:Pg:dbname=foo
220             - bob
221             - secret
222             -
223             RaiseError: 1
224             PrintError: 1
225              
226             =head2 replicated
227              
228             You can also add database read slaves to your configuration with the
229             C<replicated> config option.
230             This will automatically make your read queries go to a slave and your write
231             queries go to the master.
232             Keep in mind that this will require additional dependencies:
233             L<DBIx::Class::Optional::Dependencies#Storage::Replicated>
234             See L<DBIx::Class::Storage::DBI::Replicated> for more details.
235             Here is an example configuration that adds two read slaves:
236              
237             dbicx_sugar:
238             default:
239             schema_class: MyApp::Schema
240             dsn: dbi:Pg:dbname=master
241             replicated:
242             balancer_type: ::Random # optional
243             balancer_args: # optional
244             auto_validate_every: 5 # optional
245             master_read_weight:1 # optional
246             # pool_type and pool_args are also allowed and are also optional
247             replicants:
248             -
249             - dbi:Pg:dbname=slave1
250             - user1
251             - password1
252             -
253             quote_names: 1
254             pg_enable_utf8: 1
255             -
256             - dbi:Pg:dbname=slave2
257             - user2
258             - password2
259             -
260             quote_names: 1
261             pg_enable_utf8: 1
262              
263             =head2 alias
264              
265             Schema aliases allow you to reference the same underlying database by multiple
266             names.
267             For example:
268              
269             dbicx_sugar:
270             default:
271             dsn: dbi:Pg:dbname=master
272             schema_class: MyApp::Schema
273             slave1:
274             alias: default
275              
276             Now you can access the default schema with C<schema()>, C<schema('default')>,
277             or C<schema('slave1')>.
278             This can come in handy if, for example, you have master/slave replication in
279             your production environment but only a single database in your development
280             environment.
281             You can continue to reference C<schema('slave1')> in your code in both
282             environments by simply creating a schema alias in your development.yml config
283             file, as shown above.
284              
285             =head1 FUNCTIONS
286              
287             =head2 schema
288              
289             my $user = schema->resultset('User')->find('bob');
290              
291             Returns a L<DBIx::Class::Schema> object ready for you to use.
292             For performance, schema objects are cached in memory and are lazy loaded the
293             first time they are accessed.
294             If you have configured only one database, then you can simply call C<schema>
295             with no arguments.
296             If you have configured multiple databases,
297             you can still call C<schema> with no arguments if there is a database
298             named C<default> in the configuration.
299             With no argument, the C<default> schema is returned.
300             Otherwise, you B<must> provide C<schema()> with the name of the database:
301              
302             my $user = schema('foo')->resultset('User')->find('bob');
303              
304             =head2 resultset
305              
306             This is a convenience method that will save you some typing.
307             Use this B<only> when accessing the C<default> schema.
308              
309             my $user = resultset('User')->find('bob');
310              
311             is equivalent to:
312              
313             my $user = schema->resultset('User')->find('bob');
314              
315             =head2 rset
316              
317             my $user = rset('User')->find('bob');
318              
319             This is simply an alias for C<resultset>.
320              
321             =head2 get_config
322              
323             Returns the current configuration, like config does,
324             but does not look for a config file.
325              
326             Use this for introspection, eg:
327              
328             my $dbix_sugar_is_configured = get_config ? 1 : 0 ;
329              
330             =head2 add_schema_to_config
331              
332             This function does not touch the existing config.
333             It can be used if some other part of your app
334             has configured DBICx::Sugar but did not know about
335             the part that uses an extra schema.
336              
337             add_schema_to_config('schema_name', { dsn => ... });
338              
339             =head1 SCHEMA GENERATION
340              
341             Setting the schema_class option and having proper DBIx::Class classes
342             is the recommended approach for performance and stability.
343             You can use the L<dbicdump> command line tool provided by
344             L<DBIx::Class::Schema::Loader> to help you.
345             For example, if your app were named Foo, then you could run the following
346             from the root of your project directory:
347              
348             dbicdump -o dump_directory=./lib Foo::Schema dbi:SQLite:/path/to/foo.db
349              
350             For this example, your C<schema_class> setting would be C<'Foo::Schema'>.
351              
352             =head1 CONTRIBUTORS
353              
354             =over
355              
356             =item *
357              
358             Henk van Oers <L<https://github.com/hvoers>>
359              
360             =back
361              
362             =head1 AUTHOR
363              
364             Naveed Massjouni <naveed@vt.edu>
365              
366             =head1 COPYRIGHT AND LICENSE
367              
368             This software is copyright (c) 2015 by Naveed Massjouni.
369              
370             This is free software; you can redistribute it and/or modify it under
371             the same terms as the Perl 5 programming language system itself.
372              
373             =cut