File Coverage

blib/lib/DBIx/CSSQuery/Collection.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package DBIx::CSSQuery::Collection;
2 4     4   27 use strict;
  4         9  
  4         136  
3 4     4   6119 use DBI ":sql_types";
  4         24254  
  4         2031  
4 4     4   2500 use DBIx::CSSQuery::DB;
  0            
  0            
5              
6             use self;
7              
8             sub new {
9             return bless {
10             db => DBIx::CSSQuery::DB->new(),
11             selector => undef,
12             sql_params => {
13             order => "ORDER BY id ASC",
14             limit => undef
15             }
16             }, $self;
17             }
18              
19             sub get {
20             my ($index) = @args;
21             my $record;
22              
23             $self->each(
24             sql_params => {
25             limit => "$index,1"
26             },
27             callback => sub {
28             $record = $_[0]
29             }
30             );
31              
32             return $record
33             }
34              
35             sub insert {
36             my %fields = @args;
37             my $parsed = _parse_css_selector($self->{selector});
38             my $p = $parsed->[0];
39              
40             my @fields = keys %fields;
41              
42             local $"= ",";
43             my $sql = "INSERT INTO $p->{type} (@fields) VALUES (@{[ map {'?'} @fields ]})";
44             my $dbh = $self->{db}->attr("dbh");
45             my $sth = $dbh->prepare($sql);
46             my $rv = $sth->execute(map { $fields{$_} } @fields);
47              
48             return $self;
49             }
50              
51             sub size {
52             my $parsed = _parse_css_selector($self->{selector});
53             my ($sql, $values) = _build_select_sql_statement($parsed, {
54             select => "count(*)"
55             });
56             my $dbh = $self->{db}->attr("dbh");
57              
58             my $sth = $dbh->prepare( $sql );
59             for my $i (0 .. $#{$values} ) {
60             my $v = $values->[$i];
61             $sth->bind_param($i+1, $v->[0], $v->[1]);
62             }
63              
64             $sth->execute;
65             my $record = $sth->fetchrow_hashref;
66             return $record->{'count(*)'};
67             }
68              
69             sub last {
70             $self->{sql_params}{order} =~ s/ASC/DESC/;
71             $self->{sql_params}{limit} = "0,1";
72             return $self;
73             }
74              
75             sub each {
76             my %params;
77             if (@args == 0) {
78             return $self;
79             }
80              
81             if (ref($args[0]) eq 'CODE') {
82             $params{callback}= $args[0];
83             }
84             else {
85             %params = @args;
86             }
87              
88             my $cb = $params{callback};
89             return $self unless defined $cb;
90              
91             my $parsed = _parse_css_selector($self->{selector});
92              
93             for(keys %{$self->{sql_params}}) {
94             $params{sql_params}{$_} = $self->{sql_params}{$_};
95             }
96              
97             my ($sql, $values) = _build_select_sql_statement($parsed, $params{sql_params});
98              
99             my $dbh = $self->{db}->attr("dbh");
100              
101             my $sth = $dbh->prepare( $sql );
102              
103             for my $i (0 .. $#{$values} ) {
104             my $v = $values->[$i];
105             $sth->bind_param($i+1, $v->[0], $v->[1]);
106             }
107              
108             $sth->execute;
109             while (my $record = $sth->fetchrow_hashref) {
110             $cb->($record);
111             }
112              
113             return $self;
114             }
115              
116              
117             sub _build_select_sql_statement {
118             my ($parsed, $params) = @_;
119              
120             my $p = $parsed->[0];
121              
122             my @values = ();
123              
124             my $from = " FROM $p->{type} ";
125             my $where = "";
126             if ($p->{attribute} =~ m/ \[ (.+) = (.+) \] /x ) {
127             $where = " WHERE ";
128             my $field = $1;
129             my $val = $2;
130              
131             $where .= "$field = ?";
132             my $type = SQL_INTEGER;
133             if ($val =~ /^'(.+)'$/) {
134             $val = $1;
135             $type = SQL_VARCHAR;
136             }
137             push @values, [$val, SQL_INTEGER];
138             }
139              
140             $params = {} if !defined($params);
141             my $limit = defined($params->{limit}) ? " LIMIT $params->{limit}" : "";
142              
143             my $select = "SELECT * ";
144             $select = "SELECT $params->{'select'} " if $params->{'select'};
145              
146             my $order = "ORDER BY id ASC";
147             $order = " " . $params->{'order'} if $params->{'order'};
148              
149             return "${select}${from} ${where} ${order} ${limit}", \@values;
150             }
151              
152             sub _parse_css_selector {
153             my $selector = shift;
154             my @sel =
155             map { _parse_simple_css_selector($_) }
156             split(/ /, $selector);
157             return \@sel;
158             }
159              
160             sub _parse_simple_css_selector {
161             my $selector = shift;
162             my $word = qr/[_a-z0-9]+/o;
163             my $parsed = {
164             type => "",
165             class => "",
166             id => "",
167             attribute => "",
168             special => ""
169             };
170              
171             $selector =~ m{^(\*|$word)};
172             $parsed->{type} = $1;
173              
174             while ( $selector =~ m{
175             \G
176             ( \#$word ) | # ID
177             ( (?:\[ .+ \] )+ ) | # attribute
178             ( (?:\.$word )+ ) | # class names
179             ( (?::$word(?: \(.+\))?)+ ) # special
180             }gx) {
181             $parsed->{id} .= $1 ||"";
182             $parsed->{attribute} .= $2 ||"";
183             $parsed->{class} .= $3 ||"";
184             $parsed->{special} .= $4 ||"";
185             }
186              
187             return $parsed;
188             }
189              
190             1;
191             __END__