File Coverage

blib/lib/TreePath/Backend/DBIx.pm
Criterion Covered Total %
statement 79 82 96.3
branch 14 22 63.6
condition 7 9 77.7
subroutine 12 12 100.0
pod n/a
total 112 125 89.6


line stmt bran cond sub pod time code
1             package TreePath::Backend::DBIx;
2             $TreePath::Backend::DBIx::VERSION = '0.14';
3 5     5   13849 use Moose::Role;
  5         9  
  5         45  
4 5     5   25613 use base 'DBIx::Class::Schema';
  5         11  
  5         3675  
5 5     5   219967 use Carp qw/croak/;
  5         12  
  5         271  
6 5     5   26 use Path::Class;
  5         10  
  5         257  
7 5     5   3012 use Hash::Merge;
  5         12552  
  5         245  
8              
9 5     5   2534 use FindBin '$Bin';
  5         4707  
  5         4709  
10             require UNIVERSAL::require;
11              
12              
13             my $attrs = {
14             # starting with v3.3, SQLite supports the "IF EXISTS" clause to "DROP TABLE",
15             # even though SQL::Translator::Producer::SQLite 1.59 isn't passed along this option
16             # see https://rt.cpan.org/Ticket/Display.html?id=48688
17             sqlite_version => 3.3,
18             add_drop_table => 0,
19             no_comments => 0,
20             RaiseError => 1,
21             PrintError => 0,
22             };
23              
24             has dsn => (
25             is => 'rw',
26             default => sub {
27             my $self = shift;
28             return $self->model_config->{'connect_info'}->{dsn};
29             }
30             );
31              
32             has model_config => (
33             is => 'rw',
34             lazy_build => 1,
35             );
36              
37             has 'schema' => (
38             is => 'rw',
39             predicate => 'has_schema',
40             # lazy_build => 1,
41             );
42              
43             has '_source_name' => (
44             is => 'rw',
45             isa => 'Str',
46             );
47              
48             has '_populate_backend' => (
49             is => 'rw',
50             isa => 'Int',
51             );
52              
53              
54             sub _build_model_config {
55 6     6   9 my $self = shift;
56 6         204 my $config = $self->conf;
57              
58 6 50       173 my $model_config = $config->{$self->config->{backend}->{args}->{model}}
59             or croak "'backend/args/model' is not defined in conf file !";
60 6         168 return $model_config
61             }
62              
63             sub _connect_info {
64 7     7   14 my $self = shift;
65              
66 7         206 my $model_config = $self->model_config;
67              
68 7         13 my ($dsn, $user, $password, $unicode_option, $db_type);
69 7         13 eval {
70 7 50       21 if (!$dsn)
71             {
72 7 50       26 if (ref $model_config->{'connect_info'}) {
73              
74 7         20 $dsn = $model_config->{'connect_info'}->{dsn};
75 7         13 $user = $model_config->{'connect_info'}->{user};
76 7         14 $password = $model_config->{'connect_info'}->{password};
77              
78             # Determine database type amongst: SQLite, Pg or MySQL
79 7         35 $dsn =~ m/^dbi:(\w+)/;
80 7         57 $db_type = lc($1);
81 7         61 my %unicode_connection_for_db = (
82             'sqlite' => { sqlite_unicode => 1 },
83             'pg' => { pg_enable_utf8 => 1 },
84             'mysql' => { mysql_enable_utf8 => 1 },
85              
86             );
87 7         39 $unicode_option = $unicode_connection_for_db{$db_type};
88             }
89             else {
90 0         0 $dsn = $model_config->{'connect_info'};
91             }
92             }
93             };
94              
95 7 50       19 if ($@) {
96 0         0 die "Your DSN line in " . $self->conf . " doesn't look like a valid DSN.";
97             }
98 7 50       19 die "No valid Data Source Name (DSN).\n" if !$dsn;
99 7         23 $dsn =~ s/__HOME__/$FindBin::Bin\/\.\./g;
100              
101 7 50       26 if ( $db_type eq 'sqlite' ){
102 7         27 $dsn =~ m/.*:(.*)$/;
103 7         45 my $dir = dir($1)->parent;
104 7         1814 $dir->mkpath;
105             }
106              
107 7         634 my $merge = Hash::Merge->new( 'LEFT_PRECEDENT' );
108 7         116 my $allattrs = $merge->merge( $unicode_option, $attrs );
109              
110 7         521 return $dsn, $user, $password, $allattrs;
111             }
112              
113              
114             sub _load {
115 7     7   11 my $self = shift;
116              
117 7         38 $self->_log("Loading tree from dbix");
118              
119 7         30 my($dsn, $user, $password, $allattrs) = $self->_connect_info;
120              
121 7 100 66     316 $self->_populate_backend($self->config->{backend}->{args}->{'populate_backend'})
      100        
122             if ( $self->can('_populate_backend') && ! defined $self->_populate_backend && defined $self->config->{backend}->{args}->{'populate_backend'} );
123              
124 7         204 my $schema_class = $self->model_config->{schema_class};
125 7         602 eval "require $schema_class";
126 7 50       977558 if( $@ ){
127 0         0 die("Cannot load $schema_class : $@");
128             }
129 7         96 my $schema = $schema_class->connect($dsn,$user,$password,$allattrs);
130 7         267092 my $source_name = $self->config->{backend}->{args}->{source_name};
131 7         233 $self->_source_name($source_name);
132 7         12 eval { $schema->resultset($source_name)->count };
  7         48  
133              
134 7 100       260684 if ( $@ ) {
135 5 50       361 print "Deploy and populate $dsn\n" if $self->debug;
136 5         43 $schema->deploy;
137 5 100 66     2682776 $schema->_populate if ( $schema->can('_populate') && $self->_populate_backend);
138             }
139 7         137625 $self->schema($schema);
140              
141 7         751 my @rs = $self->schema->resultset($source_name)->search();
142              
143 7         16521 my $search_field = $self->_search_field;
144 7         236 my $parent_field = $self->_parent_field;
145              
146 7         114 return { map { $_->id => { name => $_->$search_field, parent => $_->$parent_field } } @rs};
  45         3243  
147             }
148              
149              
150             sub _create {
151 15     15   29 my $self = shift;
152 15         32 my $node = shift;
153              
154 15         81 my $clone = $self->_clone_node($node);
155 15         558 $self->schema->resultset($self->_source_name)->create($clone);
156             }
157              
158             sub _update {
159 9     9   20 my $self = shift;
160 9         17 my $node = shift;
161              
162 9         35 my $clone = $self->_clone_node($node);
163 9         377 $self->schema->resultset($self->_source_name)->update_or_create($clone);
164             }
165              
166             sub _delete {
167 4     4   9 my $self = shift;
168 4         8 my $nodes = shift;
169              
170 4         10 foreach my $node (@$nodes) {
171 11         331952 $self->schema->resultset($self->_source_name)->find($node->{id})->delete;
172             }
173             }
174              
175              
176              
177             =head1 NAME
178              
179             TreePath::Backend::DBIx - Backend 'DBIx' for TreePath
180              
181             =head1 VERSION
182              
183             version 0.14
184              
185             =head1 CONFIGURATION
186              
187             $tp = TreePath->new( conf => 't/conf/treefromdbix.yml' );
188              
189             # t/conf/treefromdbix.yml
190             Model::TPath:
191             schema_class: Schema::TPath
192             connect_info:
193             dsn: 'dbi:SQLite:dbname=:memory:'
194              
195             TreePath:
196             debug: 0
197             backend:
198             name: DBIx
199             args:
200             model: Model::TPath
201             source_name: Page
202             search_field: name
203             parent_field: parent_id
204              
205              
206             =head2 REQUIRED SCHEMA
207              
208             See t/lib/Schema/TPath.pm
209              
210             =head1 AUTHOR
211              
212             Daniel Brosseau, C<< <dab at catapulse.org> >>
213              
214             =head1 LICENSE AND COPYRIGHT
215              
216             Copyright 2014 Daniel Brosseau.
217              
218             This program is free software; you can redistribute it and/or modify it
219             under the terms of either: the GNU General Public License as published
220             by the Free Software Foundation; or the Artistic License.
221              
222             See http://dev.perl.org/licenses/ for more information.
223              
224              
225             =cut
226              
227             1;