File Coverage

blib/lib/WWW/Suffit/Model.pm
Criterion Covered Total %
statement 47 57 82.4
branch 10 42 23.8
condition 9 26 34.6
subroutine 11 16 68.7
pod 9 9 100.0
total 86 150 57.3


line stmt bran cond sub pod time code
1             package WWW::Suffit::Model;
2 2     2   96939 use strict;
  2         4  
  2         72  
3 2     2   17 use warnings;
  2         3  
  2         114  
4 2     2   584 use utf8;
  2         287  
  2         11  
5              
6             =encoding utf8
7              
8             =head1 NAME
9              
10             WWW::Suffit::Model - This library provides methods for access to WWW::Suffit models
11              
12             =head1 SYNOPSIS
13              
14             use WWW::Suffit::Model;
15              
16             my $model = WWW::Suffit::Model->new(
17             ds => "sqlite:///tmp/test.db?sqlite_unicode=1"
18             );
19              
20             $model = $model->connect_cached->init;
21              
22             =head1 DESCRIPTION
23              
24             A lightweight and extensible model layer that streamlines database interaction within the WWW::Suffit framework, providing a clean foundation for building consistent, reusable data models
25              
26             =head1 METHODS
27              
28             This class inherits all methods from L and implements the following new ones
29              
30             =head2 init
31              
32             $model = $model->connect->init($package, $schema, $prefix);
33             $model = $model->connect->init(__PACKAGE__, 'public', 'schema');
34             $model = $model->connect_cached->init;
35              
36             This method initializes the database schema prior to using the connection.
37              
38             =over 4
39              
40             =item B<$package>
41              
42             Specifies the package that holds the C<__DATA__> section containing the C blocks
43              
44             Default: your package name of the class inheriting from this one, C<__PACKAGE__>
45              
46             =item B<$schema>
47              
48             Specifies schema name. Default: C<'public'>
49              
50             =item B<$prefix>
51              
52             Specifies preffix of the DDL section name. Default: C<'schema'>
53              
54             For eg., if you specify "test" as the prefix, you will need to declare
55             your C blocks in your class as follows:
56              
57             __DATA__
58              
59             @@ test_sqlite
60              
61             ... your DDL for sqlite here ...
62              
63             @@ test_mysql
64              
65             ... your DDL for mysql here ...
66              
67             @@ test_postgresql
68              
69             ... your DDL for postgresql here ...
70              
71             =back
72              
73             =head2 is_mariadb
74              
75             print $model->is_mariadb ? "Is MariaDB" : "Is NOT MariaDB";
76              
77             Returns true if type of current database is MariaDB
78              
79             =head2 is_mysql
80              
81             print $model->is_mysql ? "Is MySQL" : "Is NOT MySQL";
82              
83             Returns true if type of current database is MySQL
84              
85             =head2 is_oracle
86              
87             print $model->is_oracle ? "Is Oracle" : "Is NOT Oracle";
88              
89             Returns true if type of current database is Oracle
90              
91             =head2 is_postgresql
92              
93             print $model->is_postgresql ? "Is PostgreSQL" : "Is NOT PostgreSQL";
94              
95             Returns true if type of current database is PostgreSQL
96              
97             =head2 is_sqlite
98              
99             print $model->is_sqlite ? "Is SQLite" : "Is NOT SQLite";
100              
101             Returns true if type of current database is SQLite
102              
103             =head2 is_initialized
104              
105             print $model->is_initialized ? "Initialized" : "Is NOT initialized";
106              
107             This method returns the initialization status of the model.
108             It returns true if the model is initialized, and false otherwise.
109              
110             =head2 initiator
111              
112             print $model->initiator; # MyModel
113              
114             This method returns the name of the class that called the init method
115              
116             =head2 schema
117              
118             print $model->schema; # public
119              
120             This method returns the schema name
121              
122             =head1 EXAMPLE
123              
124             Add the following block describing the schemas to the end of your class in the "C<__DATA__>" section:
125              
126             @@ schema_sqlite
127              
128             -- # main
129             CREATE TABLE IF NOT EXISTS "test" (
130             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
131             "comment" TEXT DEFAULT NULL -- Comment
132             );
133              
134             @@ schema_mysql
135              
136             -- # main
137             CREATE DATABASE IF NOT EXISTS `test-db` CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
138             USE `test-db`;
139             CREATE TABLE IF NOT EXISTS `test` (
140             `id` INT NOT NULL AUTO_INCREMENT,
141             `comment` LONGTEXT DEFAULT NULL, -- Comment
142             PRIMARY KEY (`id`)
143             ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
144              
145             @@ schema_postgresql
146              
147             -- # main
148             CREATE TABLE IF NOT EXISTS "test" (
149             "id" INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
150             "comment" TEXT DEFAULT NULL -- Comment
151             );
152              
153             See also F for an example of how to use this class correctly
154              
155             =head1 HISTORY
156              
157             See C file
158              
159             =head1 TO DO
160              
161             See C file
162              
163             =head1 SEE ALSO
164              
165             L, L, F
166              
167             =head1 AUTHOR
168              
169             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
170              
171             =head1 COPYRIGHT
172              
173             Copyright (C) 1998-2026 D&D Corporation
174              
175             =head1 LICENSE
176              
177             This program is distributed under the terms of the Artistic License Version 2.0
178              
179             See the C file or L for details
180              
181             =cut
182              
183             our $VERSION = '1.02';
184              
185 2     2   521 use parent 'Acrux::DBI';
  2         231  
  2         10  
186              
187 2     2   1096391 use Acrux::Util qw/touch/;
  2         6  
  2         312  
188 2     2   16 use Acrux::RefUtil qw/isnt_void/;
  2         4  
  2         384  
189              
190             use constant {
191 2         2070 SCHEMA_NAME => 'public',
192             SCHEMA_SECTION_PREFIX => 'schema',
193             SCHEMA_SECTION_FORMAT => '%s_%s',
194             DRIVERS => {
195             sqlite => 'sqlite',
196             file => 'sqlite',
197             mysql => 'mysql',
198             mariadb => 'mariadb',
199             maria => 'mariadb',
200             pg => 'postgresql',
201             pgsql => 'postgresql',
202             postgres => 'postgresql',
203             postgresql => 'postgresql',
204             oracle => 'oracle',
205             ora => 'oracle',
206             sponge => 'sponge', # For tests only
207             },
208             DEFAULT_MODEL_URI => 'sponge://',
209             DEFAULT_MODEL_DSN => 'DBI:Sponge:',
210             DEFAULT_MODEL_ATTR => {
211             RaiseError => 0,
212             PrintError => 0,
213             PrintWarn => 0,
214             },
215 2     2   16 };
  2         5  
216              
217 1 50   1 1 10 sub is_sqlite { DRIVERS->{(shift->driver)} eq 'sqlite' ? 1 : 0 }
218 0 0   0 1 0 sub is_mysql { DRIVERS->{(shift->driver)} eq 'mysql' ? 1 : 0 }
219 0 0   0 1 0 sub is_mariadb { DRIVERS->{(shift->driver)} eq 'mariadb' ? 1 : 0 }
220 0 0   0 1 0 sub is_postgresql { DRIVERS->{(shift->driver)} eq 'postgresql' ? 1 : 0 }
221 0 0   0 1 0 sub is_oracle { DRIVERS->{(shift->driver)} eq 'oracle' ? 1 : 0 }
222              
223             # Initialize schema
224             sub init {
225 1     1 1 299421 my $self = shift; # shift->connect_cached;
226 1   33     11 my $package = shift // ref($self); # __PACKAGE__
227 1   50     8 my $schema = shift // SCHEMA_NAME;
228 1   50     6 my $prefix = shift // SCHEMA_SECTION_PREFIX;
229 1 50       17 my $dbh = $self->dbh or return $self; # Skip if no connect established
230 1   50     14 my $is_inited = $self->{'_is_initialized'} || 0; # Not initialized
231 1 50       5 return $self if $is_inited; # Already initialized
232              
233             # Check SQLite
234 1 50 0     12 if ($self->is_sqlite) {
    0          
    0          
235 1         42 my $file = $dbh->sqlite_db_filename();
236 1 50 33     53 unless ($file && (-e $file) && !(-z $file)) {
      33        
237 1         14 touch($file);
238             }
239              
240             # Get table info
241 1 50       212 if (my $sth = $dbh->table_info(undef, undef, undef, 'TABLE')) {
242 1 50       947 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
243             }
244             }
245              
246             # Check MariaDB
247             elsif ($self->is_mariadb || $self->is_mysql) {
248             # Get table info
249 0 0       0 if (my $sth = $dbh->table_info('', $schema, '', 'TABLE')) {
250 0 0       0 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
251             }
252             }
253              
254             # Check PostgreSQL
255             elsif ($self->is_postgresql) {
256             # Get table info
257 0 0       0 if (my $sth = $dbh->table_info('', $schema, undef, 'TABLE')) { # schema = 'public'
258 0 0       0 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
259             }
260             }
261              
262             # Skip initialize otherwise
263             else {
264 0         0 return $self;
265             }
266              
267             # Get dump instance
268 1   50     88 my $name = sprintf(SCHEMA_SECTION_FORMAT, $prefix, DRIVERS->{($self->driver)} || 'unknown');
269 1         35 my $dump = $self->dump(name => $name)->from_data($package);
270              
271             # Import initial schema if is not inited
272 1 50       1259 unless ($is_inited) {
273 1         9 $dump->poke(); # main section (default)
274 1 50       14908 return $self if $self->error;
275             }
276              
277             # Check connect
278 1 50 0     14 return $self->error(sprintf("Can't init database \"%s\". Ping failed: %s",
279             $self->dsn, $self->errstr() || "unknown error")) unless $self->ping;
280              
281             # Ok
282 1         62 $self->{'_is_initialized'} = 1;
283 1         5 $self->{'_initiator'} = $package;
284 1         3 $self->{'_schema'} = $schema;
285 1         15 return $self;
286             }
287              
288 0 0   0 1 0 sub is_initialized { shift->{'_is_initialized'} || 0 }
289 1   50 1 1 567 sub initiator { shift->{'_initiator'} // '' }
290 1   50 1 1 8 sub schema { shift->{'_schema'} // '' }
291              
292             1;
293              
294             __END__