File Coverage

blib/lib/Data/Mapper.pm
Criterion Covered Total %
statement 24 99 24.2
branch 0 30 0.0
condition 0 6 0.0
subroutine 8 20 40.0
pod 5 12 41.6
total 37 167 22.1


line stmt bran cond sub pod time code
1             package Data::Mapper;
2 1     1   18330 use 5.008001;
  1         4  
  1         33  
3 1     1   6 use strict;
  1         2  
  1         30  
4 1     1   6 use warnings;
  1         1  
  1         31  
5 1     1   753 use parent qw(Data::Mapper::Class);
  1         280  
  1         5  
6              
7             our $VERSION = '0.06';
8              
9 1     1   45 use Carp ();
  1         3  
  1         14  
10 1     1   5 use Scalar::Util ();
  1         1  
  1         27  
11 1     1   97806 use Class::Load ();
  1         54614  
  1         31  
12              
13 1     1   638 use Data::Mapper::Data;
  1         3  
  1         1353  
14              
15             sub create {
16 0     0 1   my $self = shift;
17 0           my $name = shift;
18 0           my $data = $self->adapter->create($name => @_);
19              
20 0           $self->map_data($name, $data);
21             }
22              
23             sub find {
24 0     0 1   my $self = shift;
25 0           my $name = shift;
26 0           my $data = $self->adapter->find($name => @_);
27              
28 0 0         $data && $self->map_data($name, $data);
29             }
30              
31             sub search {
32 0     0 1   my $self = shift;
33 0           my $name = shift;
34 0           my $data = $self->adapter->search($name => @_);
35              
36 0 0         die 'results returned from search() method must be an ArrayRef'
37             if ref $data ne 'ARRAY';
38              
39 0           my @result;
40 0           push @result, $self->map_data($name, $_) for @$data;
41              
42 0           \@result;
43             }
44              
45             sub update {
46 0     0 1   my ($self, $data) = @_;
47 0           my $result;
48 0           my $has_changes = $data->isa('Data::Mapper::Data');
49              
50 0 0 0       return if $has_changes && not $data->is_changed;
51              
52 0           my $params = $self->mapped_params($data);
53 0           $result = $self->adapter->update(
54             $params->{table} => $params->{set} => $params->{where}
55             );
56              
57 0 0         $data->discard_changes if $has_changes;
58              
59 0           $result;
60             }
61              
62             sub delete {
63 0     0 1   my ($self, $data) = @_;
64 0           my $params = $self->mapped_params($data);
65 0           $self->adapter->delete($params->{table} => $params->{where});
66             }
67              
68             sub adapter {
69 0     0 0   my ($self, $adapter) = @_;
70 0 0         $self->{adapter} = $adapter if defined $adapter;
71 0 0         $self->{adapter} || die 'You must set an adapter first';
72             }
73              
74             our %DATA_CLASSES = ();
75             sub data_class {
76 0     0 0   my ($self, $name) = @_;
77              
78 0   0       $DATA_CLASSES{ref $self}{$name} ||= do {
79 0           my $data_class = join '::', (ref $self), 'Data', $self->to_class_name($name);
80              
81 0           eval { Class::Load::load_class($data_class) };
  0            
82 0 0         Carp::croak("no such data class: $data_class for $name") if $@;
83              
84 0           $data_class;
85             }
86             }
87              
88             ### PRIVATE_METHODS ###
89              
90             sub to_class_name {
91 0     0 0   my ($self, $name) = @_;
92 0 0         return $name if !$name;
93              
94 0           my @parts = split /_/, $name;
95 0           join '', (map { ucfirst } @parts);
  0            
96             }
97              
98             sub to_table_name {
99 0     0 0   my ($self, $data) = @_;
100 0           my ($table) = ((ref $data) =~ /::([^:]+)$/);
101              
102 0           $table =~ s/([A-Z])/'_' . lc $1/eg;
  0            
103 0           $table =~ s/^_//;
104 0           $table;
105             }
106              
107             sub as_serializable {
108 0     0 0   my ($self, $data) = @_;
109             +{
110 0           map { $_ => $data->{$_} } grep { !/^_/ } keys %$data
  0            
  0            
111             };
112             }
113              
114             sub map_data {
115 0     0 0   my ($self, $name, $data) = @_;
116 0           my $data_class = $self->data_class($name);
117              
118 0 0         if (Scalar::Util::blessed($data)) {
119 0 0         if ($data->can('as_serializable')) {
    0          
120 0           $data = $data->as_serializable;
121             }
122             elsif (Scalar::Util::reftype($data) eq 'HASH') {
123 0           $data = $self->as_serializable($data);
124             }
125             else {
126 0           Carp::croak('$data must be either a Hash-based object or a plain HashRef');
127             }
128             }
129              
130 0           $data_class->new($data);
131             }
132              
133             sub mapped_params {
134 0     0 0   my ($self, $data) = @_;
135 0           my $table = $self->to_table_name($data);
136 0 0         my $schema = $self->adapter->schemata->{$table}
137             or Carp::croak("no such table: $table");
138              
139 0           my $primary_keys = $schema->primary_keys;
140 0 0         die "Data::Mapper doesn't support tables which have no primary keys"
141             if !scalar @$primary_keys;
142              
143 0           my $result = { set => {}, where => {}, table => $table };
144              
145             # Data::Mapper::Data-based object
146 0 0         if ($data->isa('Data::Mapper::Data')) {
147 0           $result->{set} = $data->changes;
148              
149 0           for my $key (@$primary_keys) {
150 0           $result->{where}{$key} = $data->param($key);
151             }
152             }
153              
154             # Hash-based POPO
155             else {
156 0           $result->{set} = $self->as_serializable($data);
157              
158 0           for my $key (@$primary_keys) {
159 0           $result->{where}{$key} = $data->{$key};
160             }
161             }
162              
163 0           Carp::croak("where clause is empty")
164 0 0         if !keys %{$result->{where}};
165              
166 0           $result;
167             }
168              
169             !!1;
170              
171             __END__