File Coverage

blib/lib/MojoX/MojoDbWrap.pm
Criterion Covered Total %
statement 17 83 20.4
branch 0 12 0.0
condition 0 13 0.0
subroutine 6 16 37.5
pod 8 8 100.0
total 31 132 23.4


line stmt bran cond sub pod time code
1             package MojoX::MojoDbWrap;
2 2     2   337073 use v5.24;
  2         6  
3             { our $VERSION = '0.002' }
4              
5 2     2   2561 use Moo;
  2         12858  
  2         9  
6 2     2   2516 use warnings;
  2         2  
  2         70  
7 2     2   812 use experimental qw< signatures >;
  2         4338  
  2         10  
8 2     2   1200 use Ouch qw< :trytiny_var >;
  2         5200  
  2         8  
9              
10             sub coerce_wrappers ($x) {
11             $x //= [];
12             ouch 400, 'invalid value for wrappers, need undef or array ref'
13             unless ref($x) eq 'ARRAY';
14             return [
15             ($x // [])->@*,
16             {
17             class => 'Mojo::Pg',
18             create => sub ($class, $url) {
19             return unless $url =~ m{\A postgres (?:ql)? ://}mxs;
20             require Mojo::Pg;
21             return Mojo::Pg->new($url);
22             },
23             insert => sub ($self, $tablish, $data, $opts) {
24             my ($tbl, $idf) =
25             ref($tablish) eq 'ARRAY' ? $tablish->@* : ($tablish, 'id');
26             my $ext_opts = {
27             ($opts // {})->%*,
28             on_conflict => undef,
29             returning => $idf,
30             };
31             return $self->db->insert($tbl, $data, $ext_opts)->hash->{id};
32             },
33             },
34             {
35             class => 'Mojo::SQLite',
36             create => sub ($class, $url) {
37             require Mojo::SQLite;
38             return Mojo::SQLite->new($url);
39             },
40             insert => sub ($self, $tablish, $data, $opts) {
41             my $tbl = ref($tablish) eq 'ARRAY' ? $tablish->[0] : $tablish;
42             my $ext_opts = {
43             ($opts // {})->%*,
44             on_conflict => undef,
45             };
46             return $self->db->insert($tbl, $data, $ext_opts)->last_insert_id;
47             },
48             },
49             ];
50             }
51              
52             sub isa_wrappers ($x) {
53             for my $i (0 .. $x->$#*) {
54             my $w = $x->[$i];
55             ouch 400, "item $i lacks class name" unless defined($w->{class});
56             ouch 400, "item $i lacks create sub"
57             unless ref($w->{create}) eq 'CODE';
58             ouch 400, "item $i lacks insert sub"
59             unless ref($w->{insert}) eq 'CODE';
60             }
61             return;
62             }
63              
64 2     2   7522 use namespace::clean;
  2         39007  
  2         11  
65              
66             # actual input stuff
67             has db_url => (is => 'ro', required => 1);
68             has migrations_for => (is => 'ro', default => sub { return {} });
69             has _wrapped => (is => 'lazy', init_arg => undef);
70             has _wrappers => (
71             is => 'ro',
72             init_arg => 'wrappers',
73             default => undef,
74             coerce => \&coerce_wrappers,
75             isa => \&isa_wrappers,
76             );
77              
78 0     0     sub _build__wrapped ($self) {
  0            
  0            
79 0           my $url = $self->db_url;
80 0           for my $candidate ($self->_wrappers->@*) {
81 0           my $class = $candidate->{class};
82 0 0         my $instance = $candidate->{create}->($class, $url)
83             or next;
84             return {
85             class => $class,
86             insert => $candidate->{insert},
87 0           instance => $instance,
88             };
89             }
90             }
91              
92             # handy accessors
93 0     0 1   sub mdb ($self) { return $self->_wrapped->{instance} }
  0            
  0            
  0            
94 0     0 1   sub mdb_class ($self) { return $self->_wrapped->{class} }
  0            
  0            
  0            
95 0     0 1   sub mdb_module ($self) { return $self->_wrapped->{class} }
  0            
  0            
  0            
96 0     0 1   sub db ($self) { return $self->mdb->db }
  0            
  0            
  0            
97              
98             # real stuff
99 0     0 1   sub id_of ($self, $tablish, $cond, $opts = undef) {
  0            
  0            
  0            
  0            
  0            
100 0 0         my ($tbl, $idf) = ref($tablish) eq 'ARRAY' ? $tablish->@* : ($tablish, 'id');
101 0   0       my $hash = $self->db->select($tbl, [$idf], $cond, $opts // {})->hash;
102 0 0         return $hash ? $hash->{$idf} : undef;
103             }
104              
105 0     0     sub _inserter ($self) { return $self->_wrapped->{insert} }
  0            
  0            
  0            
106              
107 0     0 1   sub id_or_insert ($self, $tablish, $condition, $default, $opts = undef) {
  0            
  0            
  0            
  0            
  0            
  0            
108 0   0       $opts //= {};
109 0           my $inserter;
110 0           for (1 .. 3) { # paranoia for quick insert/delete
111 0           my $id = $self->id_of($tablish, $condition, $opts->{select});
112 0 0         return $id if defined($id);
113              
114             # we have to try an insertion, let's make sure we have an inserter
115 0   0       $inserter //= $self->_inserter;
116              
117 0   0       $id = $inserter->($self, $tablish, $default, $opts->{insert} // {});
118 0 0         return $id if defined($id);
119             }
120 0           ouch 500, 'cannot select nor insert, bailing out',
121             [ id_or_insert => $tablish, $condition, $default, $opts];
122             }
123              
124 0     0 1   sub select ($self, @args) {
  0            
  0            
  0            
125 0           return $self->mdb->db->select(@args);
126             }
127              
128             #sub upsert ($self, $table, $data, $opts = undef) {
129             # $opts //= {};
130             # $self->db->insert($table, $data, { $opts->%*, on_conflict => $data });
131             # return $self;
132             #}
133              
134 0     0 1   sub init ($self, $name = 'migrations') {
  0            
  0            
  0            
135 0   0       my $migrations = $self->migrations_for // {};
136 0 0 0       if (my $migration = $migrations->{$self->mdb_class} // undef) {
137 0           $self->mdb->migrations
138             ->name($name)
139             ->from_string($migration)
140             ->migrate;
141             }
142 0           return $self;
143             }
144              
145             1;