Commits

Toby Inkster committed 9c2c79b

lots and lots of changes. probably should have split this into multiple commits

Comments (0)

Files changed (13)

 use Scalar::Util qw[blessed];
 
 my $object = to_jom(from_json(<<'JSON'));
-{ "store": {
-    "book": [ 
-      { "category": "reference",
-        "author": "Nigel Rees",
-        "title": "Sayings of the Century",
-        "price": 8.95
-      },
-      { "category": "fiction",
-        "author": "Evelyn Waugh",
-        "title": "Sword of Honour",
-        "price": 12.99
-      },
-      { "category": "fiction",
-        "author": "Herman Melville",
-        "title": "Moby Dick",
-        "isbn": "0-553-21311-3",
-        "price": 8.99
-      },
-      { "category": "fiction",
-        "author": "J. R. R. Tolkien",
-        "title": "The Lord of the Rings",
-        "isbn": "0-395-19395-8",
-        "price": 22.99
-      }
-    ],
-    "bicycle": {
-      "color": "red",
-      "price": 19.95
-    }
-  }
+{
+	"store": {
+		"book": [
+			{
+				"category": "reference",
+				"author":   "Nigel Rees",
+				"title":    "Sayings of the Century",
+				"price":    8.95
+			},
+			{
+				"category": "fiction",
+				"author":   "Evelyn Waugh",
+				"title":    "Sword of Honour",
+				"price":    12.99
+			},
+			{
+				"category": "fiction",
+				"author":   "Herman Melville",
+				"title":    "Moby Dick",
+				"isbn":     "0-553-21311-3",
+				"price":    8.99
+			},
+			{
+				"category": "fiction",
+				"author":   "J. R. R. Tolkien",
+				"title":    "The Lord of the Rings",
+				"isbn":     "0-395-19395-8",
+				"price":    22.99
+			}
+		],
+		"bicycle": {
+			"color": "red",
+			"price": 19.95
+		}
+	}
 }
 JSON
 
 	say to_json([$jpath->paths($object)], {pretty=>1});
 	say [$jpath->values($object)]->[0]->nodePath
 		if blessed([$jpath->values($object)]->[0]);
-	say '-' x 40; 
+	say '-' x 40;
 }
 use overload '""' => \&to_string;
 no warnings;
 
-our $VERSION = '0.200';
-our $Safe    = 1;
+our $AUTHORITY = 'cpan:TOBYINK';
+our $VERSION   = '0.202';
+our $Safe      = 1;
 
 use Carp;
 use JSON qw[from_json];
+use Scalar::Util qw[blessed];
+use lvalue ();
+
+use Sub::Exporter -setup => {
+	exports => [qw/ jpath jpath1 jpath_map /],
+};
+
+sub jpath
+{
+	my ($object, $expression) = @_;
+	my @return = __PACKAGE__->new($expression)->values($object);
+}
+
+sub jpath1 :lvalue
+{
+	my ($object, $expression) = @_;
+	__PACKAGE__->new($expression)->value($object);
+}
+
+sub jpath_map (&$$)
+{
+	my ($coderef, $object, $expression) = @_;
+	return __PACKAGE__->new($expression)->map($object, $coderef);
+}
 
 sub new
 {
 	my ($class, $expression) = @_;
+	return $expression
+		if blessed($expression) && $expression->isa(__PACKAGE__);
 	return bless \$expression, $class;
 }
 
 	return;
 }
 
-sub values
+sub _dive :lvalue
 {
-	my ($self, $object) = @_;
-	return $self->_get($object, 'VALUE');
+	my ($obj, $path) = @_;
+	
+	$path = [
+		$path =~ /\[(.+?)\]/g
+	] unless ref $path;
+	
+	while (@$path > 1) {
+		my $chunk = shift @$path;
+		if (JSON::Path::Helper::isObject($obj))
+			{ $obj = $obj->{$chunk} }
+		elsif (JSON::Path::Helper::isArray($obj))
+			{ $obj = $obj->[$chunk] }
+		else
+			{ print "Huh?" }
+	}
+	
+	my $chunk = shift @$path;
+	lvalue::get {
+		if (JSON::Path::Helper::isObject($obj))
+			{ $obj = $obj->{$chunk} }
+		elsif (JSON::Path::Helper::isArray($obj))
+			{ $obj = $obj->[$chunk] }
+		else
+			{ print "hUh?" }
+	}
+	lvalue::set {
+		if (JSON::Path::Helper::isObject($obj))
+			{ $obj->{$chunk} = shift }
+		elsif (JSON::Path::Helper::isArray($obj))
+			{ $obj->[$chunk] = shift }
+		else
+			{ print "huH?" }
+	}
 }
 
 sub paths
 	return $self->_get($object, 'PATH');
 }
 
-1;
-
-package JSON::Path::Helper;
-
-use 5.008;
-use strict qw(vars refs);
-no warnings;
-
-use Carp;
-use Scalar::Util qw[blessed];
-
-our $VERSION = '0.200';
-
-sub new
+sub get
 {
-	return bless {
-		obj        => undef,
-		resultType => 'VALUE',
-		result     => [],
-		subx       => [],
-		}, $_[0];
+	my ($self, $object) = @_;
+	return $self->_get($object, 'VALUE');
 }
 
-sub normalize
+sub set
 {
-	my ($self, $x) = @_;
-	$x =~ s/[\['](\??\(.*?\))[\]']/_callback_01($self,$1)/eg;
-	$x =~ s/'?\.'?|\['?/;/g;
-	$x =~ s/;;;|;;/;..;/g;
-	$x =~ s/;\$|'?\]|'$//g;
-	$x =~ s/#([0-9]+)/_callback_02($self,$1)/eg;
-	$self->{'result'} = [];   # result array was temporarily used as a buffer
-	return $x;
+	my ($self, $object, $value, $limit) = @_;
+	my $count = 0;
+	foreach my $path ( $self->_get($object, 'PATH') )
+	{
+		_dive($object, $path) = $value;
+		++$count;
+		last if $limit && ($count >= $limit);
+	}
+	return $count;
 }
 
-sub _callback_01
+sub value :lvalue
 {
-	my ($self, $m1) = @_;
-	push @{ $self->{'result'} }, $m1;
-	my $last_index = scalar @{ $self->{'result'} } - 1;
-	return "[#${last_index}]";
-}
-
-sub _callback_02
-{
-	my ($self, $m1) = @_;
-	return $self->{'result'}->[$m1];
-}
-
-sub asPath
-{
-	my ($self, $path) = @_;
-	my @x = split /\;/, $path;
-	my $p = '$';
-	my $n = scalar(@x);
-	for (my $i=1; $i<$n; $i++)
-	{
-		$p .= /^[0-9*]+$/ ? ("[".$x[$i]."]") : ("['".$x[$i]."']");
+	my ($self, $object) = @_;
+	lvalue::get {
+		my ($value) = $self->get($object);
+		return $value;
 	}
-	return $p;
-}
-
-sub store
-{
-	my ($self, $p, $v) = @_;
-	push @{ $self->{'result'} }, ( $self->{'resultType'} eq "PATH" ? $self->asPath($p) : $v )
-		if $p;
-	return !!$p;
-}
-
-sub trace
-{
-	my ($self, $expr, $val, $path) = @_;
-	
-	return $self->store($path, $val) if "$expr" eq '';
-	#return $self->store($path, $val) unless $expr;
-	
-	my ($loc, $x);
-	{
-		my @x = split /\;/, $expr;
-		$loc  = shift @x;
-		$x    = join ';', @x;
-	}
-	
-	# in Perl need to distinguish between arrays and hashes.
-	if (isArray($val)
-	and $loc =~ /^\-?[0-9]+$/
-	and exists $val->[$loc])
-	{
-		$self->trace($x, $val->[$loc], sprintf('%s;%s', $path, $loc));
-	}
-	elsif (isObject($val)
-	and exists $val->{$loc})
-	{
-		$self->trace($x, $val->{$loc}, sprintf('%s;%s', $path, $loc));
-	}
-	elsif ($loc eq '*')
-	{
-		$self->walk($loc, $x, $val, $path, \&_callback_03);
-	}
-	elsif ($loc eq '..')
-	{
-		$self->trace($x, $val, $path);
-		$self->walk($loc, $x, $val, $path, \&_callback_04);
-	}
-	elsif ($loc =~ /\,/)  # [name1,name2,...]
-	{
-		$self->trace($_.';'.$x, $val, $path)
-			foreach split /\,/, $loc;
-	}
-	elsif ($loc =~ /^\(.*?\)$/) # [(expr)]
-	{
-		my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
-		$self->trace($evalx.';'.$x, $val, $path);
-	}
-	elsif ($loc =~ /^\?\(.*?\)$/) # [?(expr)]
-	{
-		# my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
-		$self->walk($loc, $x, $val, $path, \&_callback_05);
-	}
-	elsif ($loc =~ /^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/) # [start:end:step]  python slice syntax
-	{
-		$self->slice($loc, $x, $val, $path);
+	lvalue::set {
+		my $value = shift;
+		$self->set($object, $value, 1);
 	}
 }
 
-sub _callback_03
+sub values
 {
-	my ($self, $m, $l, $x, $v, $p) = @_;
-	$self->trace($m.";".$x,$v,$p);
+	my ($self, $object) = @_;
+	my @values = $self->get($object);
+	wantarray ? @values : scalar @values;
 }
 
-sub _callback_04
+sub map
 {
-	my ($self, $m, $l, $x, $v, $p) = @_;
-
-	if (isArray($v)
-	and isArray($v->[$m]) || isObject($v->[$m]))
+	my ($self, $object, $coderef) = @_;
+	my $count;
+	foreach my $path ( $self->_get($object, 'PATH') )
 	{
-		$self->trace("..;".$x, $v->[$m], $p.";".$m);
+		++$count;
+		my $value = do {
+			local $_ = _dive($object, $path);
+			local $. = $path;
+			scalar $coderef->();
+		};
+		_dive($object, $path) = $value;
 	}
-	elsif (isObject($v)
-	and isArray($v->{$m}) || isObject($v->{$m}))
-	{
-		$self->trace("..;".$x, $v->{$m}, $p.";".$m);
-	}
+	return $count;
 }
 
-sub _callback_05
-{
-	my ($self, $m, $l, $x, $v, $p) = @_;
+BEGIN {
+	package JSON::Path::Helper;
 	
-	$l =~ s/^\?\((.*?)\)$/$1/g;
+	use 5.008;
+	use strict qw(vars refs);
+	no warnings;
 	
-	my $evalx;
-	if (isArray($v))
+	our $AUTHORITY = 'cpan:TOBYINK';
+	our $VERSION   = '0.202';
+	
+	use Carp;
+	use Scalar::Util qw[blessed];
+	
+	sub new
 	{
-		$evalx = $self->evalx($l, $v->[$m]);
-	}
-	elsif (isObject($v))
-	{
-		$evalx = $self->evalx($l, $v->{$m});
+		return bless {
+			obj        => undef,
+			resultType => 'VALUE',
+			result     => [],
+			subx       => [],
+			}, $_[0];
 	}
 	
-	$self->trace($m.";".$x, $v, $p)
-		if $evalx;
-}
-
-sub walk
-{
-	my ($self, $loc, $expr, $val, $path, $f) = @_;
-
-	if (isArray($val))
+	sub normalize
 	{
-		map {
-			$f->($self, $_, $loc, $expr, $val, $path);
-		} 0..scalar @$val;
-	}
-
-	elsif (isObject($val))
-	{
-		map {
-			$f->($self, $_, $loc, $expr, $val, $path);
-		} keys %$val;
+		my ($self, $x) = @_;
+		$x =~ s/[\['](\??\(.*?\))[\]']/_callback_01($self,$1)/eg;
+		$x =~ s/'?\.'?|\['?/;/g;
+		$x =~ s/;;;|;;/;..;/g;
+		$x =~ s/;\$|'?\]|'$//g;
+		$x =~ s/#([0-9]+)/_callback_02($self,$1)/eg;
+		$self->{'result'} = [];   # result array was temporarily used as a buffer
+		return $x;
 	}
 	
-	else
+	sub _callback_01
 	{
-		croak('walk called on non hashref/arrayref value, died');
-	}
-}
-
-sub slice
-{
-	my ($self, $loc, $expr, $v, $path) = @_;
-	
-	$loc =~ s/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/$1:$2:$3/;
-	my @s   = split /\:/, $loc;
-	my $len = scalar @$v;
-
-	my $start = $s[0]+0 ? $s[0]+0 : 0;
-	my $end   = $s[1]+0 ? $s[1]+0 : $len;
-	my $step  = $s[2]+0 ? $s[2]+0 : 1;
-
-	$start = ($start < 0) ? max(0,$start+$len) : min($len,$start);
-	$end   = ($end < 0)   ? max(0,$end+$len)   : min($len,$end);
-
-	for (my $i=$start; $i<$end; $i+=$step)
-	{
-		$self->trace($i.";".$expr, $v, $path);
-	}
-}
-
-sub max
-{
-	return $_[0] > $_[1] ? $_[0] : $_[1];
-}
-
-sub min
-{
-	return $_[0] < $_[1] ? $_[0] : $_[1];
-}
-
-sub evalx
-{
-	my ($self, $x, $v, $vname) = @_;
-	
-	croak('non-safe evaluation, died') if $JSON::Path::Safe;
-		
-	my $expr = $x;
-	$expr =~ s/\$root/\$self->{'obj'}/g;
-	$expr =~ s/\$_/\$v/g;
-
-	local $@ = undef;
-	my $res = eval $expr;
-	
-	if ($@)
-	{
-		croak("eval failed: `$expr`, died");
+		my ($self, $m1) = @_;
+		push @{ $self->{'result'} }, $m1;
+		my $last_index = scalar @{ $self->{'result'} } - 1;
+		return "[#${last_index}]";
 	}
 	
-	return $res;
-}
-
-sub isObject
-{
-	my $obj = shift;
-	return 1 if ref($obj) eq 'HASH';
-	return 1 if blessed($obj) && $obj->can('typeof') && $obj->typeof eq 'HASH';
-	return;
-}
-
-sub isArray
-{
-	my $obj = shift;
-	return 1 if ref($obj) eq 'ARRAY';
-	return 1 if blessed($obj) && $obj->can('typeof') && $obj->typeof eq 'ARRAY';
-	return;
-}
+	sub _callback_02
+	{
+		my ($self, $m1) = @_;
+		return $self->{'result'}->[$m1];
+	}
+	
+	sub asPath
+	{
+		my ($self, $path) = @_;
+		my @x = split /\;/, $path;
+		my $p = '$';
+		my $n = scalar(@x);
+		for (my $i=1; $i<$n; $i++)
+		{
+			$p .= /^[0-9]+$/ ? ("[".$x[$i]."]") : ("['".$x[$i]."']");
+		}
+		return $p;
+	}
+	
+	sub store
+	{
+		my ($self, $p, $v) = @_;
+		push @{ $self->{'result'} }, ( $self->{'resultType'} eq "PATH" ? $self->asPath($p) : $v )
+			if $p;
+		return !!$p;
+	}
+	
+	sub trace
+	{
+		my ($self, $expr, $val, $path) = @_;
+		
+		return $self->store($path, $val) if "$expr" eq '';
+		#return $self->store($path, $val) unless $expr;
+		
+		my ($loc, $x);
+		{
+			my @x = split /\;/, $expr;
+			$loc  = shift @x;
+			$x    = join ';', @x;
+		}
+		
+		# in Perl need to distinguish between arrays and hashes.
+		if (isArray($val)
+		and $loc =~ /^\-?[0-9]+$/
+		and exists $val->[$loc])
+		{
+			$self->trace($x, $val->[$loc], sprintf('%s;%s', $path, $loc));
+		}
+		elsif (isObject($val)
+		and exists $val->{$loc})
+		{
+			$self->trace($x, $val->{$loc}, sprintf('%s;%s', $path, $loc));
+		}
+		elsif ($loc eq '*')
+		{
+			$self->walk($loc, $x, $val, $path, \&_callback_03);
+		}
+		elsif ($loc eq '..')
+		{
+			$self->trace($x, $val, $path);
+			$self->walk($loc, $x, $val, $path, \&_callback_04);
+		}
+		elsif ($loc =~ /\,/)  # [name1,name2,...]
+		{
+			$self->trace($_.';'.$x, $val, $path)
+				foreach split /\,/, $loc;
+		}
+		elsif ($loc =~ /^\(.*?\)$/) # [(expr)]
+		{
+			my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
+			$self->trace($evalx.';'.$x, $val, $path);
+		}
+		elsif ($loc =~ /^\?\(.*?\)$/) # [?(expr)]
+		{
+			# my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
+			$self->walk($loc, $x, $val, $path, \&_callback_05);
+		}
+		elsif ($loc =~ /^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/) # [start:end:step]  python slice syntax
+		{
+			$self->slice($loc, $x, $val, $path);
+		}
+	}
+	
+	sub _callback_03
+	{
+		my ($self, $m, $l, $x, $v, $p) = @_;
+		$self->trace($m.";".$x,$v,$p);
+	}
+	
+	sub _callback_04
+	{
+		my ($self, $m, $l, $x, $v, $p) = @_;
+		
+		if (isArray($v)
+		and isArray($v->[$m]) || isObject($v->[$m]))
+		{
+			$self->trace("..;".$x, $v->[$m], $p.";".$m);
+		}
+		elsif (isObject($v)
+		and isArray($v->{$m}) || isObject($v->{$m}))
+		{
+			$self->trace("..;".$x, $v->{$m}, $p.";".$m);
+		}
+	}
+	
+	sub _callback_05
+	{
+		my ($self, $m, $l, $x, $v, $p) = @_;
+		
+		$l =~ s/^\?\((.*?)\)$/$1/g;
+		
+		my $evalx;
+		if (isArray($v))
+		{
+			$evalx = $self->evalx($l, $v->[$m]);
+		}
+		elsif (isObject($v))
+		{
+			$evalx = $self->evalx($l, $v->{$m});
+		}
+		
+		$self->trace($m.";".$x, $v, $p)
+			if $evalx;
+	}
+	
+	sub walk
+	{
+		my ($self, $loc, $expr, $val, $path, $f) = @_;
+		
+		if (isArray($val))
+		{
+			map {
+				$f->($self, $_, $loc, $expr, $val, $path);
+			} 0..scalar @$val;
+		}
+		
+		elsif (isObject($val))
+		{
+			map {
+				$f->($self, $_, $loc, $expr, $val, $path);
+			} keys %$val;
+		}
+		
+		else
+		{
+			croak('walk called on non hashref/arrayref value, died');
+		}
+	}
+	
+	sub slice
+	{
+		my ($self, $loc, $expr, $v, $path) = @_;
+		
+		$loc =~ s/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/$1:$2:$3/;
+		my @s   = split /\:/, $loc;
+		my $len = scalar @$v;
+		
+		my $start = $s[0]+0 ? $s[0]+0 : 0;
+		my $end   = $s[1]+0 ? $s[1]+0 : $len;
+		my $step  = $s[2]+0 ? $s[2]+0 : 1;
+		
+		$start = ($start < 0) ? max(0,$start+$len) : min($len,$start);
+		$end   = ($end < 0)   ? max(0,$end+$len)   : min($len,$end);
+		
+		for (my $i=$start; $i<$end; $i+=$step)
+		{
+			$self->trace($i.";".$expr, $v, $path);
+		}
+	}
+	
+	sub max
+	{
+		return $_[0] > $_[1] ? $_[0] : $_[1];
+	}
+	
+	sub min
+	{
+		return $_[0] < $_[1] ? $_[0] : $_[1];
+	}
+	
+	sub evalx
+	{
+		my ($self, $x, $v, $vname) = @_;
+		
+		croak('non-safe evaluation, died') if $JSON::Path::Safe;
+			
+		my $expr = $x;
+		$expr =~ s/\$root/\$self->{'obj'}/g;
+		$expr =~ s/\$_/\$v/g;
+		
+		local $@ = undef;
+		my $res = eval $expr;
+		
+		if ($@)
+		{
+			croak("eval failed: `$expr`, died");
+		}
+		
+		return $res;
+	}
+	
+	sub isObject
+	{
+		my $obj = shift;
+		return 1 if ref($obj) eq 'HASH';
+		return 1 if blessed($obj) && $obj->can('typeof') && $obj->typeof eq 'HASH';
+		return;
+	}
+	
+	sub isArray
+	{
+		my $obj = shift;
+		return 1 if ref($obj) eq 'ARRAY';
+		return 1 if blessed($obj) && $obj->can('typeof') && $obj->typeof eq 'ARRAY';
+		return;
+	}
+};
 
 1;
 
  my @authors = $jpath->values($data);
  
  # The author of the last (by order) book
- my $jpath     = JSON::Path->new('$..book[-1:]');
- my ($tolkien) = $jpath->values($data);
+ my $jpath   = JSON::Path->new('$..book[-1:]');
+ my $tolkien = $jpath->value($data);
 
 =head1 DESCRIPTION
 
 
 JSONPath is described at L<http://goessner.net/articles/JsonPath/>.
 
-This module is JSON::JOM-compatible.
-
 =head2 Constructor
 
 =over 4
 capable of being decoded by JSON::from_json.
 
 Returns a list of structures from within $object which match against the
-JSONPath expression.
+JSONPath expression. In scalar context, returns the number of matches.
+
+=item C<<  value($object)  >>
+
+Like C<values>, but returns just the first value. This method is an lvalue
+sub, which means you can assign to it:
+
+  $path->values('$.name') = 'Bob';
 
 =item C<<  paths($object)  >>
 
-As per C<values> but instead of returning structures which match
-the expression, returns paths that point towards those structures.
+As per C<values> but instead of returning structures which match the
+expression, returns canonical JSONPaths that point towards those structures.
+
+=item C<<  get($object)  >>
+
+In list context, identical to C<< values >>, but in scalar context returns
+the first result.
+
+=item C<<  set($object, $value, $limit)  >>
+
+Alters C<< $object >>, setting the paths to C<< $value >>. If set, then
+C<< $limit >> limits the number of changes made.
+
+Returns the number of changes made.
+
+=item C<<  map($object, $coderef)  >>
+
+Conceptually similar to Perl's C<map> keyword. Executes the coderef
+(in scalar context!) for each match of the path within the object,
+and sets a new value from the coderef's return value. Within the
+coderef, C<< $_ >> may be used to access the old value, and C<< $. >>
+may be used to access the curent canonical JSONPath.
 
 =item C<<  to_string  >>
 
 
 =back
 
+=head2 Functions
+
+The following functions are available for export, but are not exported
+by default:
+
+=over
+
+=item C<< jpath($object, $path_string) >>
+
+Shortcut for C<< JSON::Path->new($path_string)->values($object) >>. That is,
+it can be used as an lvalue.
+
+=item C<< jpath1($object, $path_string) >>
+
+Shortcut for C<< JSON::Path->new($path_string)->value($object) >>. That is,
+it can be used as an lvalue.
+
+=item C<< jpath_map { CODE } $object, $path_string >>
+
+Shortcut for C<< JSON::Path->new($path_string)->map($object, $code) >>. 
+
+=back
+
 =head1 PERL SPECIFICS
 
 JSONPath is intended as a cross-programming-language method of
 
 Copyright 2007 Stefan Goessner.
 
-Copyright 2010-2011 Toby Inkster.
+Copyright 2010-2012 Toby Inkster.
 
 This module is tri-licensed. It is available under the X11 (a.k.a. MIT)
 licence; you can also redistribute it and/or modify it under the same

meta/changes.pret

 		item "Modernize."^^Packaging;
 	].
 
+`JSON-Path 0.201 cpan:TOBYINK`
+	issued  2012-09-12;
+	changeset [
+		item "Remove remaining `use Error` in test suite."^^Bugfix;
+	].
+
+`JSON-Path 0.202 cpan:TOBYINK`
+	issued  2012-10-12;
+	changeset [
+		item "'value' method."^^Addition;
+		item "'map' method."^^Addition;
+		item "'get' and 'set' methods."^^Addition;
+		item "'jpath', 'jpath1' and 'jpath_map' functions."^^Addition;
+	].
+

meta/makefile.pret

 	version_from      m`JSON::Path`;
 	readme_from       m`JSON::Path`;
 	requires          p`JSON 2.00`;
+	requires          p`Sub::Exporter`;
+	requires          p`lvalue`;
 	test_requires     p`Test::More 0.61`;
 .
 
-use Test::More tests => 9;
+use Test::More tests => 10;
 BEGIN { use_ok('JSON::Path') };
 
-use Error qw[:try];
-
 use JSON;
 my $object = from_json(<<'JSON');
-{ "store": {
-    "book": [ 
-      { "category": "reference",
-        "author": "Nigel Rees",
-        "title": "Sayings of the Century",
-        "price": 8.95
-      },
-      { "category": "fiction",
-        "author": "Evelyn Waugh",
-        "title": "Sword of Honour",
-        "price": 12.99
-      },
-      { "category": "fiction",
-        "author": "Herman Melville",
-        "title": "Moby Dick",
-        "isbn": "0-553-21311-3",
-        "price": 8.99
-      },
-      { "category": "fiction",
-        "author": "J. R. R. Tolkien",
-        "title": "The Lord of the Rings",
-        "isbn": "0-395-19395-8",
-        "price": 22.99
-      }
-    ],
-    "bicycle": {
-      "color": "red",
-      "price": 19.95
-    }
-  }
+{
+	"store": {
+		"book": [
+			{
+				"category": "reference",
+				"author":   "Nigel Rees",
+				"title":    "Sayings of the Century",
+				"price":    8.95
+			},
+			{
+				"category": "fiction",
+				"author":   "Evelyn Waugh",
+				"title":    "Sword of Honour",
+				"price":    12.99
+			},
+			{
+				"category": "fiction",
+				"author":   "Herman Melville",
+				"title":    "Moby Dick",
+				"isbn":     "0-553-21311-3",
+				"price":    8.99
+			},
+			{
+				"category": "fiction",
+				"author":   "J. R. R. Tolkien",
+				"title":    "The Lord of the Rings",
+				"isbn":     "0-395-19395-8",
+				"price":    22.99
+			}
+		],
+		"bicycle": {
+			"color": "red",
+			"price": 19.95
+		}
+	}
 }
 JSON
 
 
 ok($JSON::Path::Safe, "safe by default");
 
-try {
+ok(!eval {
 	my $path3 = JSON::Path->new('$..book[?($_->{author} =~ /tolkien/i)]');
 	my $results3 = $path3->values($object);
-}
-catch Error::Simple with {
-	ok(1, "disallow dangerous eval");
-}
+	1;
+}, "eval disabled by default");
 
 $JSON::Path::Safe = 0;
 
 			'bar' => 1,
 		},
 		{
-			'bar' => 2,     
+			'bar' => 2,
 		},
 		{
 			'bar' => 3,
-		},        
+		},
 	]
 };
 
+use Test::More;
+use JSON::Path -all;
+
+use JSON;
+my $object = from_json(<<'JSON');
+{
+	"store": {
+		"book": [
+			{
+				"category": "reference",
+				"author":   "Nigel Rees",
+				"title":    "Sayings of the Century",
+				"price":    8.95
+			},
+			{
+				"category": "fiction",
+				"author":   "Evelyn Waugh",
+				"title":    "Sword of Honour",
+				"price":    12.99
+			},
+			{
+				"category": "fiction",
+				"author":   "Herman Melville",
+				"title":    "Moby Dick",
+				"isbn":     "0-553-21311-3",
+				"price":    8.99
+			},
+			{
+				"category": "fiction",
+				"author":   "J. R. R. Tolkien",
+				"title":    "The Lord of the Rings",
+				"isbn":     "0-395-19395-8",
+				"price":    22.99
+			}
+		],
+		"bicycle": {
+			"color": "red",
+			"price": 19.95
+		}
+	}
+}
+JSON
+
+my $path1 = '$.store.book[*].title';
+
+is_deeply(
+	[ jpath1($object, $path1) ],
+	[ 'Sayings of the Century' ],
+);
+
+is_deeply(
+	[ jpath($object, $path1) ],
+	[ 'Sayings of the Century', 'Sword of Honour', 'Moby Dick', 'The Lord of the Rings' ],
+);
+
+done_testing();
+use Test::More;
+use JSON::Path -all;
+
+use JSON;
+my $object = from_json(<<'JSON');
+{
+	"store": {
+		"book": [
+			{
+				"category": "reference",
+				"author":   "Nigel Rees",
+				"title":    "Sayings of the Century",
+				"price":    8.95
+			},
+			{
+				"category": "fiction",
+				"author":   "Evelyn Waugh",
+				"title":    "Sword of Honour",
+				"price":    12.99
+			},
+			{
+				"category": "fiction",
+				"author":   "Herman Melville",
+				"title":    "Moby Dick",
+				"isbn":     "0-553-21311-3",
+				"price":    8.99
+			},
+			{
+				"category": "fiction",
+				"author":   "J. R. R. Tolkien",
+				"title":    "The Lord of the Rings",
+				"isbn":     "0-395-19395-8",
+				"price":    22.99
+			}
+		],
+		"bicycle": {
+			"color": "red",
+			"price": 19.95
+		}
+	}
+}
+JSON
+
+my $path1 = '$.store.book[*].title';
+
+jpath_map { uc $_ } $object, '$.store.book[*].title';
+
+is_deeply(
+	[ jpath1($object, $path1) ],
+	[ map uc,'Sayings of the Century' ],
+);
+
+is_deeply(
+	[ jpath($object, $path1) ],
+	[ map uc, 'Sayings of the Century', 'Sword of Honour', 'Moby Dick', 'The Lord of the Rings' ],
+);
+
+is(
+	JSON::Path->new('$.store.book[*].author')->set($object => 'Anon', 2),
+	2,
+);
+
+is_deeply(
+	[ jpath($object, '$.store.book[*].author') ],
+	[ 'Anon', 'Anon', 'Herman Melville', 'J. R. R. Tolkien' ],
+);
+
+done_testing();

xt/02pod_coverage.t

+use XT::Util;
 use Test::More;
 use Test::Pod::Coverage;
 
-my @modules = qw(JSON::Path);
-pod_coverage_ok($_, "$_ is covered") for @modules;
-done_testing(scalar @modules);
+plan skip_all => __CONFIG__->{skip_all}
+	if __CONFIG__->{skip_all};
 
+if ( __CONFIG__->{modules} )
+{
+	my @modules = @{ __CONFIG__->{modules} };
+	pod_coverage_ok($_, "$_ is covered") for @modules;
+	done_testing(scalar @modules);
+}
+else
+{
+	all_pod_coverage_ok();
+}
+

xt/03meta_uptodate.config

+{"package":"JSON-Path"}

xt/03meta_uptodate.t

+use XT::Util;
 use Test::More tests => 1;
 use Test::RDF::DOAP::Version;
-doap_version_ok('JSON-Path', 'JSON::Path');
+doap_version_ok(__CONFIG__->{package}, __CONFIG__->{version_from});
 
+use Test::Tabs;
+all_perl_files_ok();
+use XT::Util;
+use Test::More;
+use Test::HasVersion;
+
+plan skip_all => __CONFIG__->{skip_all}
+	if __CONFIG__->{skip_all};
+
+if ( __CONFIG__->{modules} )
+{
+	my @modules = @{ __CONFIG__->{modules} };
+	pm_version_ok($_, "$_ is covered") for @modules;
+	done_testing(scalar @modules);
+}
+else
+{
+	all_pm_version_ok();
+}
+