Skip to content

Instantly share code, notes, and snippets.

/json.diff Secret

Created December 31, 2014 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/0812a33be995f2a1852f to your computer and use it in GitHub Desktop.
Save anonymous/0812a33be995f2a1852f to your computer and use it in GitHub Desktop.
diff --git a/lib/Mojo/Pg/Database.pm b/lib/Mojo/Pg/Database.pm
index 64932b3..d3b17a2 100644
--- a/lib/Mojo/Pg/Database.pm
+++ b/lib/Mojo/Pg/Database.pm
@@ -4,6 +4,7 @@ use Mojo::Base 'Mojo::EventEmitter';
use DBD::Pg ':async';
use IO::Handle;
use Mojo::IOLoop;
+use Mojo::JSON 'encode_json';
use Mojo::Pg::Results;
use Mojo::Pg::Transaction;
use Scalar::Util 'weaken';
@@ -44,6 +45,10 @@ sub dollar_only { ++$_[0]->{dollar_only} and return $_[0] }
sub is_listening { !!keys %{shift->{listen} || {}} }
+sub json {
+ shift->val(map { encode_json $_} @_);
+}
+
sub listen {
my ($self, $name) = @_;
@@ -70,6 +75,8 @@ sub query {
my ($self, $query) = (shift, shift);
my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
+ my @values = $self->{values} ? (@{delete $self->{values}}, @_) : @_;
+
# Dollar only
my $dbh = $self->dbh;
local $dbh->{pg_placeholder_dollaronly} = 1 if delete $self->{dollar_only};
@@ -77,14 +84,14 @@ sub query {
# Blocking
unless ($cb) {
my $sth = $dbh->prepare($query);
- $sth->execute(@_);
+ $sth->execute(@values);
$self->_notifications;
return Mojo::Pg::Results->new(sth => $sth);
}
# Non-blocking
my $sth = $dbh->prepare($query, {pg_async => PG_ASYNC});
- push @{$self->{waiting}}, {args => [@_], cb => $cb, sth => $sth};
+ push @{$self->{waiting}}, {args => \@values, cb => $cb, sth => $sth};
$self->$_ for qw(_next _watch);
}
@@ -100,6 +107,12 @@ sub unlisten {
return $self;
}
+sub val {
+ my $self = shift;
+ push @{$self->{values}}, @_;
+ return $self;
+}
+
sub _next {
return unless my $next = shift->{waiting}[0];
$next->{sth}->execute(@{$next->{args}}) unless $next->{executed}++;
@@ -251,9 +264,8 @@ Execute a statement and discard its result.
Activate C<pg_placeholder_dollaronly> for next L</"query"> call and allow C<?>
to be used as an operator.
- use Mojo::JSON 'decode_json';
$db->dollar_only->query('select * from foo where bar ? $1', 'baz')
- ->hashes->map(sub { decode_json($_->{bar})->{baz} })->join("\n")->say;
+ ->hashes->map(sub { $_->{bar}{baz} })->join("\n")->say;
=head2 is_listening
@@ -261,6 +273,12 @@ to be used as an operator.
Check if L</"dbh"> is listening for notifications.
+=head2 json
+
+ $db = $db->json(@values);
+
+Enqueue JSON placeholder values for next L</"query"> call.
+
=head2 listen
$db = $db->listen('foo');
@@ -308,6 +326,12 @@ results. You can also append a callback to perform operation non-blocking.
Stop listening for notifications.
+=head2 val
+
+ $db = $db->val(@values);
+
+Enqueue placeholder values for next L</"query"> call.
+
=head1 SEE ALSO
L<Mojo::Pg>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
diff --git a/lib/Mojo/Pg/Results.pm b/lib/Mojo/Pg/Results.pm
index bc594cd..ff8c3c6 100644
--- a/lib/Mojo/Pg/Results.pm
+++ b/lib/Mojo/Pg/Results.pm
@@ -2,24 +2,56 @@ package Mojo::Pg::Results;
use Mojo::Base -base;
use Mojo::Collection;
+use Mojo::JSON 'decode_json';
use Mojo::Util 'tablify';
has 'sth';
-sub array { shift->sth->fetchrow_arrayref }
+sub array { ($_[0]->_decode($_[0]->sth->fetchrow_arrayref))[0] }
-sub arrays { Mojo::Collection->new(@{shift->sth->fetchall_arrayref}) }
+sub arrays {
+ Mojo::Collection->new($_[0]->_decode(@{$_[0]->sth->fetchall_arrayref}));
+}
sub columns { shift->sth->{NAME} }
-sub hash { shift->sth->fetchrow_hashref }
+sub hash { ($_[0]->_decode($_[0]->sth->fetchrow_hashref))[0] }
-sub hashes { Mojo::Collection->new(@{shift->sth->fetchall_arrayref({})}) }
+sub hashes {
+ Mojo::Collection->new($_[0]->_decode(@{$_[0]->sth->fetchall_arrayref({})}));
+}
+
+sub no_json { ++$_[0]->{no_json} and return $_[0] }
sub rows { shift->sth->rows }
sub text { tablify shift->arrays }
+sub _decode {
+ my ($self, @data) = @_;
+
+ return @data if $self->{no_json};
+ my ($idx, $name) = @$self{qw(idx name)};
+ unless ($idx) {
+ my $types = $self->sth->{pg_type};
+ my @idx = grep { $types->[$_] eq 'json' || $types->[$_] eq 'jsonb' }
+ 0 .. $#$types;
+ ($idx, $name) = @$self{qw(idx name)} = (\@idx, [@{$self->columns}[@idx]]);
+ }
+ return @data unless @$idx && @data;
+
+ for my $data (@data) {
+ if (ref $data eq 'HASH') {
+ map { $data->{$_} = decode_json $data->{$_} } @$name;
+ }
+ else {
+ map { $data->[$_] = decode_json $data->[$_] } @$idx;
+ }
+ }
+
+ return @data;
+}
+
1;
=encoding utf8
@@ -104,6 +136,12 @@ containing hash references.
# Process all rows at once
say $results->hashes->reduce(sub { $a->{money} + $b->{money} });
+=head2 no_json
+
+ $results = $results->no_json;
+
+Do not decode JSON values.
+
=head2 rows
my $num = $results->rows;
diff --git a/t/database.t b/t/database.t
index 020feec..5d0e9b4 100644
--- a/t/database.t
+++ b/t/database.t
@@ -148,6 +148,19 @@ eval { $db->dollar_only->query('select ? as test', 23) };
like $@, qr/called with 1 bind variables when 0 are needed/, 'right error';
is $db->query('select ? as test', 23)->hash->{test}, 23, 'right result';
+# JSON
+$db = $pg->db;
+is_deeply $db->json({bar => 'baz'})->query('select ?::json as foo')->hash,
+ {foo => {bar => 'baz'}}, 'right structure';
+is_deeply $db->json({bar => 'baz'})->query('select ?::json as foo')->array,
+ [{bar => 'baz'}], 'right structure';
+is_deeply $db->json({bar => 'baz'})->query('select ?::json as foo')
+ ->hashes->first, {foo => {bar => 'baz'}}, 'right structure';
+is_deeply $db->json({bar => 'baz'})->query('select ?::json as foo')
+ ->arrays->first, [{bar => 'baz'}], 'right structure';
+is_deeply $db->json({bar => 'baz'})->query('select ?::json as foo')
+ ->no_json->hash, {foo => '{"bar":"baz"}'}, 'right structure';
+
# Fork safety
$dbh = $pg->db->dbh;
my ($connections, $current) = @_;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment