Commits

Toby Inkster committed 1c41a1a

initial version

  • Participants
  • Tags 0.001

Comments (0)

Files changed (15)

+use inc::Module::Package 'RDF:standard';
+

File examples/simple.pl

+use 5.010;
+use lib "../lib";
+use Between;
+
+say(55 ~~ between 3, 6, 'cmp');
+
+say (between(3,5)->cmp(1));

File lib/Between.pm

+package Between;
+
+use 5.010;
+use strict;
+use warnings;
+use utf8;
+
+use constant {
+	EXCLUSIVE_START  => 0x01,
+	EXCLUSIVE_END    => 0x02,
+};
+
+use constant {
+	CMP_NUMERIC      => '<=>',
+	CMP_STRING       => 'cmp',
+	CMP_AUTO         => undef,
+};
+
+use overload (
+	q[~~]        => 'match',
+	q[""]        => 'to_string',
+	q[cmp]       => 'cmp_string',
+	q[<=>]       => 'cmp_numeric',
+	fallback     => 1,
+);
+
+BEGIN {
+	$Between::AUTHORITY = 'cpan:TOBYINK';
+	$Between::VERSION   = '0.001';
+	@Between::EXPORT    = qw(between);
+	@Between::EXPORT_OK = qw(between EXCLUSIVE_START EXCLUSIVE_END
+	                         CMP_NUMERIC CMP_STRING CMP_AUTO);
+	%Between::EXPORT_TAGS = (
+		'standard'   => \@Between::EXPORT,
+		'default'    => \@Between::EXPORT,
+		'all'        => \@Between::EXPORT_OK,
+		'constants'  => [qw(EXCLUSIVE_START EXCLUSIVE_END
+		                    CMP_NUMERIC CMP_STRING CMP_AUTO)]
+		);
+}
+
+use base 'Exporter';
+
+use Carp qw(croak confess);
+use Scalar::Util qw(looks_like_number);
+use Data::Dumper qw(Dumper);
+
+sub between ($$;$$)
+{
+	unshift @_, __PACKAGE__;
+	goto \&new;
+}
+
+sub new
+{
+	my ($class, $start, $end, $exclusive, $type) = @_;
+	bless [$start => $end, $exclusive, _type_ok($type)] => $class
+}
+
+sub _type_ok
+{
+	return unless defined(my $type = $_[0]);
+	croak "Comparison type should be 'cmp' or '<=>'"
+		unless $type ~~ [CMP_NUMERIC, CMP_STRING];
+	$type
+}
+
+sub start :lvalue
+{
+	$_[0][0]
+}
+
+sub end :lvalue
+{
+	$_[0][1]
+}
+
+sub type :lvalue
+{
+	$_[0][3] //= _type_ok($_[0]->_auto_detect_type);
+	$_[0][3]
+}
+
+sub _auto_detect_type
+{
+	my $self = shift;
+	
+	if (looks_like_number($self->start) || overload::Method($self->start, '<=>')
+	and looks_like_number($self->end)   || overload::Method($self->end,   '<=>'))
+	{
+		return CMP_NUMERIC
+	}
+	
+	return CMP_STRING
+}
+
+sub start_is_exclusive
+{
+	no warnings;
+	$_[0][2] & EXCLUSIVE_START
+}
+
+sub end_is_exclusive
+{
+	no warnings;
+	$_[0][2] & EXCLUSIVE_END
+}
+
+sub to_string
+{
+	my $self = shift;
+	sprintf(
+		'%s%s, %s%s',
+		$self->start_is_exclusive ? '(' : '[',
+		$self->_display_value($self->start),
+		$self->_display_value($self->end),
+		$self->end_is_exclusive ? ')' : ']',
+		)
+}
+
+sub _display_value
+{
+	local $Data::Dumper::Indent = 0;
+	local $Data::Dumper::Useqq  = 0;
+	local $Data::Dumper::Terse  = 1;
+	Dumper($_[1])
+}
+
+sub match
+{
+	my ($x, $y) = @_;
+	not $x->cmp($y)
+}
+
+sub cmp
+{
+	my ($self) = @_;
+	goto $self->type eq CMP_NUMERIC ? \&cmp_numeric : \&cmp_string
+}
+
+sub cmp_numeric
+{
+	my ($self, $other, $swap) = @_;
+	
+	if ( $other < $self->start
+	or  ($other == $self->start and $self->start_is_exclusive) )
+	{
+		return ($swap ? -1 : 1)
+	}
+
+	if ( $other > $self->end
+	or  ($other == $self->end and $self->end_is_exclusive) )
+	{
+		return ($swap ? 1 : -1)
+	}
+	
+	return 0
+}
+
+sub cmp_string
+{
+	my ($self, $other, $swap) = @_;
+	
+	if ( $other lt $self->start
+	or  ($other eq $self->start and $self->start_is_exclusive) )
+	{
+		return ($swap ? -1 : 1)
+	}
+
+	if ( $other gt $self->end
+	or  ($other eq $self->end and $self->end_is_exclusive) )
+	{
+		return ($swap ? 1 : -1)
+	}
+	
+	return 0
+}
+
+__PACKAGE__
+__END__
+
+=head1 NAME
+
+Between - simple range objects for smart matching
+
+=head1 SYNOPSIS
+
+ use Between;
+ use Math::Trig qw(pi);
+ 
+ if ( pi ~~between(3,4) ) {
+    say "This is to be expected.";
+ }
+
+=head1 DESCRIPTION
+
+This module is designed to make common range-testing constructions
+a little bit easier. Instead of this:
+
+ if (18 <= $person->age and $person->age <= 70) {
+    ...
+ }
+
+You would write this:
+
+ if ( $person->age ~~between(18,70) ) {
+    ...
+ }
+
+The latter is more readable, and in some cases (if the C<age> method
+call were computationally expensive) more efficient.
+
+=head2 Functional Interface
+
+=over
+
+=item C<< between($start, $end) >>
+
+This is a shortcut for the constructor (see the section below
+describing the object-oriented interface).
+
+=back
+
+=head2 Object-Oriented Interface
+
+=over
+
+=item C<< Between->new($start, $end) >>
+
+Creates a new Between object, representing a range between C<$start>
+and C<$end> (inclusive).
+
+A third argument is allowed, a number interpreted as bit flags, which
+allows C<$start> and C<$end> to be treated exclusively instead of
+inclusively.
+
+ $range = Between->new(0, 1, EXCLUSIVE_START);
+ $range = Between->new(0, 1, EXCLUSIVE_START | EXCLUSIVE_END);
+
+A fourth parameter may be provided, indicating if numeric or string
+comparisons should be used. By default, the comparison type is guessed.
+
+ $range = Between->new(0, 1, undef, '<=>');  # numeric
+ $range = Between->new(0, 1, undef, 'cmp');  # string
+
+=item C<< start >>
+
+This method returns the start of the range.
+
+=item C<< end >>
+
+This method returns the end of the range.
+
+=item C<< start_is_exclusive >>
+
+This method returns true iff the start of the range is exclusive.
+
+=item C<< end_is_exclusive >>
+
+This method returns true iff the end of the range is exclusive.
+
+=item C<< type >>
+
+The (possibly automatically detected) comparison type. Either 'cmp'
+or '<=>'.
+
+=item C<< to_string >>
+
+A printable string representation for this range.
+
+=item C<< cmp >>
+
+Equivalent to the Perl C<cmp> operator.
+
+  $range->cmp($value)
+
+will return 1 if C<$value> is less than C<< $range->start >>; return
+0 if C<$value> is within the range; and return -1 if <$value> is
+greater than C<< $range->end >>. It takes into account the
+inclusive/exclusive flags.
+
+=item C<< cmp_numeric >>
+
+Like C<cmp>, but always does numeric comparison.
+
+=item C<< cmp_string >>
+
+Like C<cmp>, but always does string comparison.
+
+=item C<< match >>
+
+Returns a boolean opposite to C<cmp>. That is, the following returns
+true if and only if C<$value> is within the range:
+
+  $range->match($value)
+
+=back
+
+=head2 Constants
+
+=over
+
+=item * B<EXCLUSIVE_START>  C<0x01>
+
+=item * B<EXCLUSIVE_END>  C<0x02>
+
+=item * B<CMP_NUMERIC>  C<< '<=>' >>
+
+=item * B<CMP_STRING>  C<< 'cmp' >>
+
+=item * B<CMP_AUTO>  C<< undef >>
+
+=back
+
+Constants are not exported by default, but can be exported using the
+':constants' or ':all' export tags.
+
+ use Between qw(:constants);
+
+=head2 Overloading
+
+The Between module overloads the following operations:
+
+=over
+
+=item * C<< ~~ >> (smart match) via C<match>.
+
+=item * C<< "" >> (stringification) via C<to_string>.
+
+=item * C<< cmp >> (string comparison), C<< lt >>, C<< gt >>, C<< le >>, C<< ge >>, C<< eq >>, C<< ne >> via C<cmp_string>.
+
+=item * C<< <=> >> (numeric comparison), C<< < >>, C<< > >>, C<< <= >>, C<< >= >>, C<< == >>, C<< != >> via C<cmp_numeric>.
+
+=back
+
+=head2 Export
+
+The following export tags are defined:
+
+=over
+
+=item * C<< :standard >> - exports the C<between> function only.
+
+=item * C<< :constants >> - exports C<EXCLUSIVE_START>, C<EXCLUSIVE_END>,
+C<CMP_NUMERIC>, C<CMP_STRING> and C<CMP_AUTO>.
+
+=item * C<< :all >> - combines C<< :standard >> and C<< :constants >>.
+
+=back
+
+By default, the C<< :standard >> tag is exported.
+
+=head1 BUGS
+
+Please report any bugs to
+L<http://rt.cpan.org/Dist/Display.html?Queue=Between>.
+
+=head1 SEE ALSO
+
+L<Object::Range>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENCE
+
+This software is copyright (c) 2012 by Toby Inkster.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=head1 DISCLAIMER OF WARRANTIES
+
+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+

File meta/changes.ttl

+# This file acts as the project's changelog.
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dcs:     <http://ontologi.es/doap-changeset#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix dist:    <http://purl.org/NET/cpan-uri/dist/Between/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+dist:project :release dist:v_0-001 .
+dist:v_0-001
+	a               :Version ;
+	dc:issued       "2012-04-23"^^xsd:date ;
+	:revision       "0.001"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/Between-0.001.tar.gz> ;
+	rdfs:label      "Initial release" .
+

File meta/doap.ttl

+# This file contains general metadata about the project.
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+<http://purl.org/NET/cpan-uri/dist/Between/project>
+	a               :Project ;
+	:programming-language "Perl" ;
+	:name           "Between" ;
+	:shortdesc      "simple range objects for smart matching" ;
+	:homepage       <https://metacpan.org/release/Between> ;
+	:download-page  <https://metacpan.org/release/Between> ;
+	:bug-database   <http://rt.cpan.org/Dist/Display.html?Queue=Between> ;
+	:created        "2012-04-23"^^xsd:date ;
+	:license        <http://dev.perl.org/licenses/> ;
+	:developer      [ a foaf:Person ; foaf:name "Toby Inkster" ; foaf:mbox <mailto:tobyink@cpan.org> ] .
+
+<http://dev.perl.org/licenses/>
+	dc:title        "the same terms as the perl 5 programming language system itself" .
+

File meta/makefile.ttl

+# This file provides instructions for packaging.
+
+@prefix : <http://purl.org/NET/cpan-uri/terms#> .
+
+<http://purl.org/NET/cpan-uri/dist/Between/project>
+	:perl_version_from _:main ;
+	:version_from _:main ;
+	:readme_from _:main ;
+	:requires
+		"strict",
+		"warnings",
+		"utf8",
+		"constant",
+		"overload",
+		"base",
+		"Exporter",
+		"Carp",
+		"Data::Dumper",
+		"Scalar::Util";
+	:test_requires "Test::More 0.61"  .
+
+_:main <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#fileName> "lib/Between.pm" .
+
+# Test module loads correctly and provides the expected API.
+
+use Test::More tests => 8;
+
+BEGIN { use_ok('Between') };
+
+isa_ok Between => qw(Exporter);
+can_ok Between => qw(between);
+can_ok Between => $_
+	for qw(EXCLUSIVE_START EXCLUSIVE_END CMP_NUMERIC CMP_STRING CMP_AUTO);

File t/02numeric.t

+# Test numeric comparisons.
+
+use 5.010;
+use strict;
+use Test::More tests => 400;
+use Between ':all';
+
+sub exercise_range_and_equiv
+{
+	my ($range, $equiv, $values) = @_;
+	my $rangestr = "$range";
+	
+	foreach my $value (@$values)
+	{
+		my $actual   = !! ($value ~~ $range);
+		my $expected = !! ($value ~~ $equiv);
+			
+		is(
+			$actual,
+			$expected,
+			sprintf("%s ~~ %s", $value, $rangestr),
+			);
+	}
+}
+
+my @values = map { $_ / 10.0 } 0 .. 98, 445;
+
+exercise_range_and_equiv(
+	between(2, 5),
+	sub { my $x = shift; return ($x >=2 && $x <= 5) },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between(2, 5, EXCLUSIVE_START),
+	sub { my $x = shift; return ($x >2 && $x <= 5) },
+	\@values,
+	);
+	
+exercise_range_and_equiv(
+	between(2, 5, EXCLUSIVE_END),
+	sub { my $x = shift; return ($x >=2 && $x < 5) },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between(2, 5, EXCLUSIVE_START | EXCLUSIVE_END),
+	sub { my $x = shift; return ($x >2 && $x < 5) },
+	\@values,
+	);

File t/03stringy.t

+# Test stringy comparisons.
+
+use 5.010;
+use strict;
+use Test::More tests => 72;
+use Between ':all';
+
+sub exercise_range_and_equiv
+{
+	my ($range, $equiv, $values) = @_;
+	my $rangestr = "$range";
+	
+	foreach my $value (@$values)
+	{
+		my $actual   = !! ($value ~~ $range);
+		my $expected = !! ($value ~~ $equiv);
+			
+		is(
+			$actual,
+			$expected,
+			sprintf("%s ~~ %s", $value, $rangestr),
+			);
+	}
+}
+
+my @values = qw( apple banana blackberry blackcurrant blueberry cherry
+	damson gooseberry grape orange pear pineapple raspberry redcurrant
+	strawberry 1 2 3);
+
+exercise_range_and_equiv(
+	between('cherry', 'grape'),
+	sub { my $x = shift; return ($x ge 'cherry' && $x le 'grape') },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between('cherry', 'grape', EXCLUSIVE_START),
+	sub { my $x = shift; return ($x gt 'cherry' && $x le 'grape') },
+	\@values,
+	);
+	
+exercise_range_and_equiv(
+	between('cherry', 'grape', EXCLUSIVE_END),
+	sub { my $x = shift; return ($x ge 'cherry' && $x lt 'grape') },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between('cherry', 'grape', EXCLUSIVE_START | EXCLUSIVE_END),
+	sub { my $x = shift; return ($x gt 'cherry' && $x lt 'grape') },
+	\@values,
+	);

File t/04objects.t

+# Test comparisons with overloaded objects.
+
+use 5.010;
+use strict;
+use Test::More;
+use Between ':all';
+
+eval { require DateTime; 1 }
+	or plan skip_all => 'need DateTime.pm to run this test file';
+
+plan tests => 28;
+
+sub exercise_range_and_equiv
+{
+	my ($range, $equiv, $values) = @_;
+	my $rangestr = "$range";
+	
+	foreach my $value (@$values)
+	{
+		my $actual   = !! ($value ~~ $range);
+		my $expected = !! ($value ~~ $equiv);
+			
+		is(
+			$actual,
+			$expected,
+			sprintf("%s ~~ %s", $value, $rangestr),
+			);
+	}
+}
+
+my @values = map
+	{ /(\d{4})-(\d{2})-(\d{2})/; DateTime->new(year => $1, month => $2, day => $3) }
+	qw( 1949-08-04 1980-06-01 1981-06-07 1983-01-09 1986-09-07 2009-02-12 2010-10-24 );
+my ($S, $E) = @values[1,4];
+
+exercise_range_and_equiv(
+	between($S, $E),
+	sub { my $x = shift; return ($x >= $S && $x <= $E) },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between($S, $E, EXCLUSIVE_START),
+	sub { my $x = shift; return ($x > $S && $x <= $E) },
+	\@values,
+	);
+	
+exercise_range_and_equiv(
+	between($S, $E, EXCLUSIVE_END),
+	sub { my $x = shift; return ($x >= $S && $x < $E) },
+	\@values,
+	);
+
+exercise_range_and_equiv(
+	between($S, $E, EXCLUSIVE_START | EXCLUSIVE_END),
+	sub { my $x = shift; return ($x > $S && $x < $E) },
+	\@values,
+	);

File t/05overloading.t

+# Test that overloading works.
+
+use 5.010;
+use strict;
+use Test::More tests => 92;
+use Between ':all';
+
+my $range = between('cherry', 'grapes', EXCLUSIVE_START);
+
+is("$range", "('cherry', 'grapes']", q(overload q("")));
+
+ok(  'banana' lt $range,  q(overload q(lt) - banana));
+ok(  'cherry' lt $range,  q(overload q(lt) - cherry));
+ok(!('damson' lt $range), q(overload q(lt) - damson));
+ok(!('grapes' lt $range), q(overload q(lt) - grapes));
+ok(!('orange' lt $range), q(overload q(lt) - orange));
+
+ok(  'banana' le $range,  q(overload q(le) - banana));
+ok(  'cherry' le $range,  q(overload q(le) - cherry));
+ok(  'damson' le $range,  q(overload q(le) - damson));
+ok(  'grapes' le $range,  q(overload q(le) - grapes));
+ok(!('orange' le $range), q(overload q(le) - orange));
+
+ok(!('banana' eq $range), q(overload q(eq) - banana));
+ok(!('cherry' eq $range), q(overload q(eq) - cherry));
+ok(  'damson' eq $range,  q(overload q(eq) - damson));
+ok(  'grapes' eq $range,  q(overload q(eq) - grapes));
+ok(!('orange' eq $range), q(overload q(eq) - orange));
+
+ok(  'banana' ne $range,  q(overload q(ne) - banana));
+ok(  'cherry' ne $range,  q(overload q(ne) - cherry));
+ok(!('damson' ne $range), q(overload q(ne) - damson));
+ok(!('grapes' ne $range), q(overload q(ne) - grapes));
+ok(  'orange' ne $range,  q(overload q(ne) - orange));
+
+ok(!('banana' gt $range), q(overload q(gt) - banana));
+ok(!('cherry' gt $range), q(overload q(gt) - cherry));
+ok(!('damson' gt $range), q(overload q(gt) - damson));
+ok(!('grapes' gt $range), q(overload q(gt) - grapes));
+ok(  'orange' gt $range,  q(overload q(gt) - orange));
+
+ok(!('banana' ge $range), q(overload q(ge) - banana));
+ok(!('cherry' ge $range), q(overload q(ge) - cherry));
+ok(  'damson' ge $range,  q(overload q(ge) - damson));
+ok(  'grapes' ge $range,  q(overload q(ge) - grapes));
+ok(  'orange' ge $range,  q(overload q(ge) - orange));
+
+ok(!('banana' ~~ $range), q(overload q(~~) - banana));
+ok(!('cherry' ~~ $range), q(overload q(~~) - cherry));
+ok(  'damson' ~~ $range,  q(overload q(~~) - damson));
+ok(  'grapes' ~~ $range,  q(overload q(~~) - grapes));
+ok(!('orange' ~~ $range), q(overload q(~~) - orange));
+
+is('banana' cmp $range, -1, q(overload q(cmp) - banana));
+is('cherry' cmp $range, -1, q(overload q(cmp) - cherry));
+is('damson' cmp $range,  0, q(overload q(cmp) - damson));
+is('grapes' cmp $range,  0, q(overload q(cmp) - grapes));
+is('orange' cmp $range, +1, q(overload q(cmp) - orange));
+
+is($range cmp 'banana', +1, q(overload q(cmp) - banana, swapped));
+is($range cmp 'cherry', +1, q(overload q(cmp) - cherry, swapped));
+is($range cmp 'damson',  0, q(overload q(cmp) - damson, swapped));
+is($range cmp 'grapes',  0, q(overload q(cmp) - grapes, swapped));
+is($range cmp 'orange', -1, q(overload q(cmp) - orange, swapped));
+
+$range = between(2, 4, EXCLUSIVE_START);
+
+is("$range", "(2, 4]", q(overload q("")));
+
+ok(   1 < $range,  q(overload q(<) -  1));
+ok(   2 < $range,  q(overload q(<) -  2));
+ok(!( 3 < $range), q(overload q(<) -  3));
+ok(!( 4 < $range), q(overload q(<) -  4));
+ok(!(20 < $range), q(overload q(<) - 20));
+
+ok(   1 <= $range,  q(overload q(<=) -  1));
+ok(   2 <= $range,  q(overload q(<=) -  2));
+ok(   3 <= $range,  q(overload q(<=) -  3));
+ok(   4 <= $range,  q(overload q(<=) -  4));
+ok(!(20 <= $range), q(overload q(<=) - 20));
+
+ok(!( 1 == $range), q(overload q(==) -  1));
+ok(!( 2 == $range), q(overload q(==) -  2));
+ok(   3 == $range,  q(overload q(==) -  3));
+ok(   4 == $range,  q(overload q(==) -  4));
+ok(!(20 == $range), q(overload q(==) - 20));
+
+ok(   1 != $range,  q(overload q(!=) -  1));
+ok(   2 != $range,  q(overload q(!=) -  2));
+ok(!( 3 != $range), q(overload q(!=) -  3));
+ok(!( 4 != $range), q(overload q(!=) -  4));
+ok(  20 != $range,  q(overload q(!=) - 20));
+
+ok(!( 1 > $range), q(overload q(>) -  1));
+ok(!( 2 > $range), q(overload q(>) -  2));
+ok(!( 3 > $range), q(overload q(>) -  3));
+ok(!( 4 > $range), q(overload q(>) -  4));
+ok(  20 > $range,  q(overload q(>) - 20));
+
+ok(!( 1 >= $range), q(overload q(>=) -  1));
+ok(!( 2 >= $range), q(overload q(>=) -  2));
+ok(   3 >= $range,  q(overload q(>=) -  3));
+ok(   4 >= $range,  q(overload q(>=) -  4));
+ok(  20 >= $range,  q(overload q(>=) - 20));
+
+ok(!( 1 ~~ $range), q(overload q(~~) -  1));
+ok(!( 2 ~~ $range), q(overload q(~~) -  2));
+ok(   3 ~~ $range,  q(overload q(~~) -  3));
+ok(   4 ~~ $range,  q(overload q(~~) -  4));
+ok(!(20 ~~ $range), q(overload q(~~) - 20));
+
+is( 1 <=> $range, -1, q(overload q(<=>) -  1));
+is( 2 <=> $range, -1, q(overload q(<=>) -  2));
+is( 3 <=> $range,  0, q(overload q(<=>) -  3));
+is( 4 <=> $range,  0, q(overload q(<=>) -  4));
+is(20 <=> $range, +1, q(overload q(<=>) - 20));
+
+is($range <=>  1, +1, q(overload q(<=>) -  1, swapped));
+is($range <=>  2, +1, q(overload q(<=>) -  2, swapped));
+is($range <=>  3,  0, q(overload q(<=>) -  3, swapped));
+is($range <=>  4,  0, q(overload q(<=>) -  4, swapped));
+is($range <=> 20, -1, q(overload q(<=>) - 20, swapped));
+use Test::More;
+eval "use Test::Pod 1.00";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+all_pod_files_ok();
+

File xt/02pod_coverage.t

+use Test::More;
+use Test::Pod::Coverage;
+
+my @modules = qw(Between);
+pod_coverage_ok($_, "$_ is covered")
+	foreach @modules;
+done_testing(scalar @modules);
+

File xt/03meta_uptodate.t

+use Test::More tests => 1;
+use Test::RDF::DOAP::Version;
+doap_version_ok('Between', 'Between');
+
+use Test::EOL;
+all_perl_files_ok();