File Coverage

blib/lib/DBICx/Sugar.pm
Criterion Covered Total %
statement 51 77 66.2
branch 23 40 57.5
condition 0 2 0.0
subroutine 11 11 100.0
pod 3 4 75.0
total 88 134 65.6


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