move collection documentation to main module
Dan Book [Mon, 9 Nov 2015 05:27:12 +0000 (00:27 -0500)]
META.json
README.pod
lib/DOM/Tiny.pm
lib/DOM/Tiny/Collection.pm [deleted file]
lib/DOM/Tiny/_Collection.pm [new file with mode: 0644]
t/collection.t

index d523916..2c6a671 100644 (file)
--- a/META.json
+++ b/META.json
          "file" : "lib/DOM/Tiny/CSS.pm",
          "version" : "0.001"
       },
-      "DOM::Tiny::Collection" : {
-         "file" : "lib/DOM/Tiny/Collection.pm",
-         "version" : "0.001"
-      },
       "DOM::Tiny::Entities" : {
          "file" : "lib/DOM/Tiny/Entities.pm",
          "version" : "0.001"
index 3c86b61..01d617a 100644 (file)
@@ -2,6 +2,8 @@
 
 =encoding utf8
 
+=for Pod::Coverage TO_JSON
+
 =head1 NAME
 
 DOM::Tiny - Minimalistic HTML/XML DOM parser with CSS selectors
@@ -675,6 +677,217 @@ Alias for L</"attr">.
 
 Alias for L</"to_string">.
 
+=head1 COLLECTION METHODS
+
+Some L<DOM::Tiny> methods return an array-based collection object, which can
+either be accessed directly as an array reference, or with the following
+methods.
+
+  # Chain methods
+  $collection->map(sub { ucfirst })->shuffle->each(sub {
+    my ($word, $num) = @_;
+    say "$num: $word";
+  });
+
+  # Access array directly to manipulate collection
+  $collection->[23] += 100;
+  say for @$collection;
+
+=head2 compact
+
+  my $new = $collection->compact;
+
+Create a new collection with all elements that are defined and not an empty
+string.
+
+  # $collection contains (0, 1, undef, 2, '', 3)
+  $collection->compact->join(', '); # "0, 1, 2, 3"
+
+=head2 each
+
+  my @elements = $collection->each;
+  $collection  = $collection->each(sub {...});
+
+Evaluate callback for each element in collection or return all elements as a
+list if none has been provided. The element will be the first argument passed
+to the callback and is also available as C<$_>.
+
+  # Make a numbered list
+  $collection->each(sub {
+    my ($e, $num) = @_;
+    say "$num: $e";
+  });
+
+=head2 first
+
+  my $first = $collection->first;
+  my $first = $collection->first(qr/foo/);
+  my $first = $collection->first(sub {...});
+  my $first = $collection->first($method);
+  my $first = $collection->first($method, @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and return the first one that matched the regular expression, or for
+which the callback/method returned true. The element will be the first argument
+passed to the callback and is also available as C<$_>.
+
+  # Longer version
+  my $first = $collection->first(sub { $_->$method(@args) });
+
+  # Find first value that contains the word "dom"
+  my $interesting = $collection->first(qr/dom/i);
+
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
+
+=head2 flatten
+
+  my $new = $collection->flatten;
+
+Flatten nested collections/arrays recursively and create a new collection with
+all elements.
+
+  # $collection contains (1, [2, [3, 4], 5, [6]], 7)
+  $collection->flatten->join(', '); # "1, 2, 3, 4, 5, 6, 7"
+
+=head2 grep
+
+  my $new = $collection->grep(qr/foo/);
+  my $new = $collection->grep(sub {...});
+  my $new = $collection->grep($method);
+  my $new = $collection->grep($method, @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and create a new collection with all elements that matched the
+regular expression, or for which the callback/method returned true. The element
+will be the first argument passed to the callback and is also available as
+C<$_>.
+
+  # Longer version
+  my $new = $collection->grep(sub { $_->$method(@args) });
+
+  # Find all values that contain the word "dom"
+  my $interesting = $collection->grep(qr/dom/i);
+
+  # Find all values that are greater than 5
+  my $greater = $collection->grep(sub { $_ > 5 });
+
+=head2 join
+
+  my $stream = $collection->join;
+  my $stream = $collection->join("\n");
+
+Turn collection into string.
+
+  # Join all values with commas
+  $collection->join(', ');
+
+=head2 last
+
+  my $last = $collection->last;
+
+Return the last element in collection.
+
+=head2 map
+
+  my $new = $collection->map(sub {...});
+  my $new = $collection->map($method);
+  my $new = $collection->map($method, @args);
+
+Evaluate callback for, or call method on, each element in collection and create
+a new collection from the results. The element will be the first argument
+passed to the callback and is also available as C<$_>.
+
+  # Longer version
+  my $new = $collection->map(sub { $_->$method(@args) });
+
+  # Append the word "dom" to all values
+  my $domified = $collection->map(sub { $_ . 'dom' });
+
+=head2 reduce
+
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
+
+Reduce elements in collection with callback, the first element will be used as
+initial value if none has been provided.
+
+  # Calculate the sum of all values
+  my $sum = $collection->reduce(sub { $a + $b });
+
+  # Count how often each value occurs in collection
+  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
+
+=head2 reverse
+
+  my $new = $collection->reverse;
+
+Create a new collection with all elements in reverse order.
+
+=head2 slice
+
+  my $new = $collection->slice(4 .. 7);
+
+Create a new collection with all selected elements.
+
+  # $collection contains ('A', 'B', 'C', 'D', 'E')
+  $collection->slice(1, 2, 4)->join(' '); # "B C E"
+
+=head2 shuffle
+
+  my $new = $collection->shuffle;
+
+Create a new collection with all elements in random order.
+
+=head2 size
+
+  my $size = $collection->size;
+
+Number of elements in collection.
+
+=head2 sort
+
+  my $new = $collection->sort;
+  my $new = $collection->sort(sub {...});
+
+Sort elements based on return value of callback and create a new collection
+from the results.
+
+  # Sort values case-insensitive
+  my $case_insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
+
+=head2 tap
+
+  $collection = $collection->tap(sub {...});
+
+Equivalent to L<Mojo::Base/"tap">.
+
+=head2 to_array
+
+  my $array = $collection->to_array;
+
+Turn collection into array reference.
+
+=head2 uniq
+
+  my $new = $collection->uniq;
+  my $new = $collection->uniq(sub {...});
+  my $new = $collection->uniq($method);
+  my $new = $collection->uniq($method, @args);
+
+Create a new collection without duplicate elements, using the string
+representation of either the elements or the return value of the
+callback/method.
+
+  # Longer version
+  my $new = $collection->uniq(sub { $_->$method(@args) });
+
+  # $collection contains ('foo', 'bar', 'bar', 'baz')
+  $collection->uniq->join(' '); # "foo bar baz"
+
+  # $collection contains ([1, 2], [2, 1], [3, 2])
+  $collection->uniq(sub{ $_->[1] })->to_array; # "[[1, 2], [2, 1]]"
+
 =head1 BUGS
 
 Report any issues on the public bugtracker.
index 699ffd9..bcaeb87 100644 (file)
@@ -11,7 +11,7 @@ use overload
   fallback => 1;
 
 use Carp 'croak';
-use DOM::Tiny::Collection;
+use DOM::Tiny::_Collection;
 use DOM::Tiny::CSS;
 use DOM::Tiny::HTML;
 use Scalar::Util qw(blessed weaken);
@@ -24,6 +24,8 @@ sub new {
   return @_ ? $self->parse(@_) : $self;
 }
 
+sub TO_JSON { shift->_delegate('render') }
+
 sub all_text { shift->_all_text(1, @_) }
 
 sub ancestors { _select($_[0]->_collect($_[0]->_ancestors), $_[1]) }
@@ -151,7 +153,7 @@ sub tag {
   return $self;
 }
 
-sub tap { shift->DOM::Tiny::Collection::tap(@_) }
+sub tap { shift->DOM::Tiny::_Collection::tap(@_) }
 
 sub text { shift->_all_text(0, @_) }
 
@@ -223,7 +225,7 @@ sub _build { shift->new->tree(shift)->xml(shift) }
 sub _collect {
   my $self = shift;
   my $xml  = $self->xml;
-  return DOM::Tiny::Collection->new(map { $self->_build($_, $xml) } @_);
+  return DOM::Tiny::_Collection->new(map { $self->_build($_, $xml) } @_);
 }
 
 sub _content {
@@ -387,6 +389,8 @@ sub _wrap {
 
 =encoding utf8
 
+=for Pod::Coverage TO_JSON
+
 =head1 NAME
 
 DOM::Tiny - Minimalistic HTML/XML DOM parser with CSS selectors
@@ -1060,6 +1064,217 @@ Alias for L</"attr">.
 
 Alias for L</"to_string">.
 
+=head1 COLLECTION METHODS
+
+Some L<DOM::Tiny> methods return an array-based collection object, which can
+either be accessed directly as an array reference, or with the following
+methods.
+
+  # Chain methods
+  $collection->map(sub { ucfirst })->shuffle->each(sub {
+    my ($word, $num) = @_;
+    say "$num: $word";
+  });
+
+  # Access array directly to manipulate collection
+  $collection->[23] += 100;
+  say for @$collection;
+
+=head2 compact
+
+  my $new = $collection->compact;
+
+Create a new collection with all elements that are defined and not an empty
+string.
+
+  # $collection contains (0, 1, undef, 2, '', 3)
+  $collection->compact->join(', '); # "0, 1, 2, 3"
+
+=head2 each
+
+  my @elements = $collection->each;
+  $collection  = $collection->each(sub {...});
+
+Evaluate callback for each element in collection or return all elements as a
+list if none has been provided. The element will be the first argument passed
+to the callback and is also available as C<$_>.
+
+  # Make a numbered list
+  $collection->each(sub {
+    my ($e, $num) = @_;
+    say "$num: $e";
+  });
+
+=head2 first
+
+  my $first = $collection->first;
+  my $first = $collection->first(qr/foo/);
+  my $first = $collection->first(sub {...});
+  my $first = $collection->first($method);
+  my $first = $collection->first($method, @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and return the first one that matched the regular expression, or for
+which the callback/method returned true. The element will be the first argument
+passed to the callback and is also available as C<$_>.
+
+  # Longer version
+  my $first = $collection->first(sub { $_->$method(@args) });
+
+  # Find first value that contains the word "dom"
+  my $interesting = $collection->first(qr/dom/i);
+
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
+
+=head2 flatten
+
+  my $new = $collection->flatten;
+
+Flatten nested collections/arrays recursively and create a new collection with
+all elements.
+
+  # $collection contains (1, [2, [3, 4], 5, [6]], 7)
+  $collection->flatten->join(', '); # "1, 2, 3, 4, 5, 6, 7"
+
+=head2 grep
+
+  my $new = $collection->grep(qr/foo/);
+  my $new = $collection->grep(sub {...});
+  my $new = $collection->grep($method);
+  my $new = $collection->grep($method, @args);
+
+Evaluate regular expression/callback for, or call method on, each element in
+collection and create a new collection with all elements that matched the
+regular expression, or for which the callback/method returned true. The element
+will be the first argument passed to the callback and is also available as
+C<$_>.
+
+  # Longer version
+  my $new = $collection->grep(sub { $_->$method(@args) });
+
+  # Find all values that contain the word "dom"
+  my $interesting = $collection->grep(qr/dom/i);
+
+  # Find all values that are greater than 5
+  my $greater = $collection->grep(sub { $_ > 5 });
+
+=head2 join
+
+  my $stream = $collection->join;
+  my $stream = $collection->join("\n");
+
+Turn collection into string.
+
+  # Join all values with commas
+  $collection->join(', ');
+
+=head2 last
+
+  my $last = $collection->last;
+
+Return the last element in collection.
+
+=head2 map
+
+  my $new = $collection->map(sub {...});
+  my $new = $collection->map($method);
+  my $new = $collection->map($method, @args);
+
+Evaluate callback for, or call method on, each element in collection and create
+a new collection from the results. The element will be the first argument
+passed to the callback and is also available as C<$_>.
+
+  # Longer version
+  my $new = $collection->map(sub { $_->$method(@args) });
+
+  # Append the word "dom" to all values
+  my $domified = $collection->map(sub { $_ . 'dom' });
+
+=head2 reduce
+
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
+
+Reduce elements in collection with callback, the first element will be used as
+initial value if none has been provided.
+
+  # Calculate the sum of all values
+  my $sum = $collection->reduce(sub { $a + $b });
+
+  # Count how often each value occurs in collection
+  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
+
+=head2 reverse
+
+  my $new = $collection->reverse;
+
+Create a new collection with all elements in reverse order.
+
+=head2 slice
+
+  my $new = $collection->slice(4 .. 7);
+
+Create a new collection with all selected elements.
+
+  # $collection contains ('A', 'B', 'C', 'D', 'E')
+  $collection->slice(1, 2, 4)->join(' '); # "B C E"
+
+=head2 shuffle
+
+  my $new = $collection->shuffle;
+
+Create a new collection with all elements in random order.
+
+=head2 size
+
+  my $size = $collection->size;
+
+Number of elements in collection.
+
+=head2 sort
+
+  my $new = $collection->sort;
+  my $new = $collection->sort(sub {...});
+
+Sort elements based on return value of callback and create a new collection
+from the results.
+
+  # Sort values case-insensitive
+  my $case_insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
+
+=head2 tap
+
+  $collection = $collection->tap(sub {...});
+
+Equivalent to L<Mojo::Base/"tap">.
+
+=head2 to_array
+
+  my $array = $collection->to_array;
+
+Turn collection into array reference.
+
+=head2 uniq
+
+  my $new = $collection->uniq;
+  my $new = $collection->uniq(sub {...});
+  my $new = $collection->uniq($method);
+  my $new = $collection->uniq($method, @args);
+
+Create a new collection without duplicate elements, using the string
+representation of either the elements or the return value of the
+callback/method.
+
+  # Longer version
+  my $new = $collection->uniq(sub { $_->$method(@args) });
+
+  # $collection contains ('foo', 'bar', 'bar', 'baz')
+  $collection->uniq->join(' '); # "foo bar baz"
+
+  # $collection contains ([1, 2], [2, 1], [3, 2])
+  $collection->uniq(sub{ $_->[1] })->to_array; # "[[1, 2], [2, 1]]"
+
 =head1 BUGS
 
 Report any issues on the public bugtracker.
diff --git a/lib/DOM/Tiny/Collection.pm b/lib/DOM/Tiny/Collection.pm
deleted file mode 100644 (file)
index 0f678c2..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-package DOM::Tiny::Collection;
-
-use strict;
-use warnings;
-use Carp 'croak';
-use List::Util;
-use Scalar::Util 'blessed';
-
-our $VERSION = '0.001';
-
-sub new {
-  my $class = shift;
-  return bless [@_], ref $class || $class;
-}
-
-sub TO_JSON { [@{shift()}] }
-
-sub compact {
-  my $self = shift;
-  return $self->new(grep { defined && (ref || length) } @$self);
-}
-
-sub each {
-  my ($self, $cb) = @_;
-  return @$self unless $cb;
-  my $i = 1;
-  $_->$cb($i++) for @$self;
-  return $self;
-}
-
-sub first {
-  my ($self, $cb) = (shift, shift);
-  return $self->[0] unless $cb;
-  return List::Util::first { $_ =~ $cb } @$self if ref $cb eq 'Regexp';
-  return List::Util::first { $_->$cb(@_) } @$self;
-}
-
-sub flatten { $_[0]->new(_flatten(@{$_[0]})) }
-
-sub grep {
-  my ($self, $cb) = (shift, shift);
-  return $self->new(grep { $_ =~ $cb } @$self) if ref $cb eq 'Regexp';
-  return $self->new(grep { $_->$cb(@_) } @$self);
-}
-
-sub join {
-  join $_[1] // '', map {"$_"} @{$_[0]};
-}
-
-sub last { shift->[-1] }
-
-sub map {
-  my ($self, $cb) = (shift, shift);
-  return $self->new(map { $_->$cb(@_) } @$self);
-}
-
-sub reduce {
-  my $self = shift;
-  @_ = (@_, @$self);
-  goto &List::Util::reduce;
-}
-
-sub reverse { $_[0]->new(reverse @{$_[0]}) }
-
-sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) }
-
-sub size { scalar @{$_[0]} }
-
-sub slice {
-  my $self = shift;
-  return $self->new(@$self[@_]);
-}
-
-sub sort {
-  my ($self, $cb) = @_;
-
-  return $self->new(sort @$self) unless $cb;
-
-  my $caller = caller;
-  no strict 'refs';
-  my @sorted = sort {
-    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
-    $a->$cb($b);
-  } @$self;
-  return $self->new(@sorted);
-}
-
-sub tap {
-  my ($self, $cb) = (shift, shift);
-  $_->$cb(@_) for $self;
-  return $self;
-}
-
-sub to_array { [@{shift()}] }
-
-sub uniq {
-  my ($self, $cb) = (shift, shift);
-  my %seen;
-  return $self->new(grep { !$seen{$_->$cb(@_)}++ } @$self) if $cb;
-  return $self->new(grep { !$seen{$_}++ } @$self);
-}
-
-sub _flatten {
-  map { _ref($_) ? _flatten(@$_) : $_ } @_;
-}
-
-sub _ref { ref $_[0] eq 'ARRAY' || blessed $_[0] && $_[0]->isa(__PACKAGE__) }
-
-1;
-
-=encoding utf8
-
-=head1 NAME
-
-DOM::Tiny::Collection - Collection
-
-=head1 SYNOPSIS
-
-  use DOM::Tiny::Collection;
-
-  # Manipulate collection
-  my $collection = DOM::Tiny::Collection->new(qw(just works));
-  unshift @$collection, 'it';
-  say $collection->join("\n");
-
-  # Chain methods
-  $collection->map(sub { ucfirst })->shuffle->each(sub {
-    my ($word, $num) = @_;
-    say "$num: $word";
-  });
-
-=head1 DESCRIPTION
-
-L<DOM::Tiny::Collection> is an array-based container for collections of
-L<DOM::Tiny> nodes or other items based on L<Mojo::Collection>.
-
-  # Access array directly to manipulate collection
-  my $collection = DOM::Tiny::Collection->new(1 .. 25);
-  $collection->[23] += 100;
-  say for @$collection;
-
-=head1 METHODS
-
-L<DOM::Tiny::Collection> implements the following methods.
-
-=head2 new
-
-  my $collection = DOM::Tiny::Collection->new(1, 2, 3);
-
-Construct a new array-based L<DOM::Tiny::Collection> object.
-
-=head2 TO_JSON
-
-  my $array = $collection->TO_JSON;
-
-Alias for L</"to_array">.
-
-=head2 compact
-
-  my $new = $collection->compact;
-
-Create a new collection with all elements that are defined and not an empty
-string.
-
-  # "0, 1, 2, 3"
-  DOM::Tiny::Collection->new(0, 1, undef, 2, '', 3)->compact->join(', ');
-
-=head2 each
-
-  my @elements = $collection->each;
-  $collection  = $collection->each(sub {...});
-
-Evaluate callback for each element in collection or return all elements as a
-list if none has been provided. The element will be the first argument passed
-to the callback and is also available as C<$_>.
-
-  # Make a numbered list
-  $collection->each(sub {
-    my ($e, $num) = @_;
-    say "$num: $e";
-  });
-
-=head2 first
-
-  my $first = $collection->first;
-  my $first = $collection->first(qr/foo/);
-  my $first = $collection->first(sub {...});
-  my $first = $collection->first($method);
-  my $first = $collection->first($method, @args);
-
-Evaluate regular expression/callback for, or call method on, each element in
-collection and return the first one that matched the regular expression, or for
-which the callback/method returned true. The element will be the first argument
-passed to the callback and is also available as C<$_>.
-
-  # Longer version
-  my $first = $collection->first(sub { $_->$method(@args) });
-
-  # Find first value that contains the word "dom"
-  my $interesting = $collection->first(qr/dom/i);
-
-  # Find first value that is greater than 5
-  my $greater = $collection->first(sub { $_ > 5 });
-
-=head2 flatten
-
-  my $new = $collection->flatten;
-
-Flatten nested collections/arrays recursively and create a new collection with
-all elements.
-
-  # "1, 2, 3, 4, 5, 6, 7"
-  DOM::Tiny::Collection->new(1, [2, [3, 4], 5, [6]], 7)->flatten->join(', ');
-
-=head2 grep
-
-  my $new = $collection->grep(qr/foo/);
-  my $new = $collection->grep(sub {...});
-  my $new = $collection->grep($method);
-  my $new = $collection->grep($method, @args);
-
-Evaluate regular expression/callback for, or call method on, each element in
-collection and create a new collection with all elements that matched the
-regular expression, or for which the callback/method returned true. The element
-will be the first argument passed to the callback and is also available as
-C<$_>.
-
-  # Longer version
-  my $new = $collection->grep(sub { $_->$method(@args) });
-
-  # Find all values that contain the word "dom"
-  my $interesting = $collection->grep(qr/dom/i);
-
-  # Find all values that are greater than 5
-  my $greater = $collection->grep(sub { $_ > 5 });
-
-=head2 join
-
-  my $stream = $collection->join;
-  my $stream = $collection->join("\n");
-
-Turn collection into string.
-
-  # Join all values with commas
-  $collection->join(', ')->say;
-
-=head2 last
-
-  my $last = $collection->last;
-
-Return the last element in collection.
-
-=head2 map
-
-  my $new = $collection->map(sub {...});
-  my $new = $collection->map($method);
-  my $new = $collection->map($method, @args);
-
-Evaluate callback for, or call method on, each element in collection and create
-a new collection from the results. The element will be the first argument
-passed to the callback and is also available as C<$_>.
-
-  # Longer version
-  my $new = $collection->map(sub { $_->$method(@args) });
-
-  # Append the word "dom" to all values
-  my $domified = $collection->map(sub { $_ . 'dom' });
-
-=head2 reduce
-
-  my $result = $collection->reduce(sub {...});
-  my $result = $collection->reduce(sub {...}, $initial);
-
-Reduce elements in collection with callback, the first element will be used as
-initial value if none has been provided.
-
-  # Calculate the sum of all values
-  my $sum = $collection->reduce(sub { $a + $b });
-
-  # Count how often each value occurs in collection
-  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
-
-=head2 reverse
-
-  my $new = $collection->reverse;
-
-Create a new collection with all elements in reverse order.
-
-=head2 slice
-
-  my $new = $collection->slice(4 .. 7);
-
-Create a new collection with all selected elements.
-
-  # "B C E"
-  DOM::Tiny::Collection->new('A', 'B', 'C', 'D', 'E')->slice(1, 2, 4)->join(' ');
-
-=head2 shuffle
-
-  my $new = $collection->shuffle;
-
-Create a new collection with all elements in random order.
-
-=head2 size
-
-  my $size = $collection->size;
-
-Number of elements in collection.
-
-=head2 sort
-
-  my $new = $collection->sort;
-  my $new = $collection->sort(sub {...});
-
-Sort elements based on return value of callback and create a new collection
-from the results.
-
-  # Sort values case-insensitive
-  my $case_insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
-
-=head2 tap
-
-  $collection = $collection->tap(sub {...});
-
-Equivalent to L<Mojo::Base/"tap">.
-
-=head2 to_array
-
-  my $array = $collection->to_array;
-
-Turn collection into array reference.
-
-=head2 uniq
-
-  my $new = $collection->uniq;
-  my $new = $collection->uniq(sub {...});
-  my $new = $collection->uniq($method);
-  my $new = $collection->uniq($method, @args);
-
-Create a new collection without duplicate elements, using the string
-representation of either the elements or the return value of the
-callback/method.
-
-  # Longer version
-  my $new = $collection->uniq(sub { $_->$method(@args) });
-
-  # "foo bar baz"
-  DOM::Tiny::Collection->new('foo', 'bar', 'bar', 'baz')->uniq->join(' ');
-
-  # "[[1, 2], [2, 1]]"
-  DOM::Tiny::Collection->new([1, 2], [2, 1], [3, 2])->uniq(sub{ $_->[1] })->to_array;
-
-=head1 BUGS
-
-Report any issues on the public bugtracker.
-
-=head1 AUTHOR
-
-Dan Book <dbook@cpan.org>
-
-=head1 COPYRIGHT AND LICENSE
-
-This software is Copyright (c) 2015 by Dan Book.
-
-This is free software, licensed under:
-
-  The Artistic License 2.0 (GPL Compatible)
-
-=head1 SEE ALSO
-
-L<Mojo::Collection>
diff --git a/lib/DOM/Tiny/_Collection.pm b/lib/DOM/Tiny/_Collection.pm
new file mode 100644 (file)
index 0000000..a053563
--- /dev/null
@@ -0,0 +1,113 @@
+package DOM::Tiny::_Collection;
+
+use strict;
+use warnings;
+use Carp 'croak';
+use List::Util;
+use Scalar::Util 'blessed';
+
+our $VERSION = '0.001';
+
+=for Pod::Coverage *EVERYTHING*
+
+=cut
+
+sub new {
+  my $class = shift;
+  return bless [@_], ref $class || $class;
+}
+
+sub TO_JSON { [@{shift()}] }
+
+sub compact {
+  my $self = shift;
+  return $self->new(grep { defined && (ref || length) } @$self);
+}
+
+sub each {
+  my ($self, $cb) = @_;
+  return @$self unless $cb;
+  my $i = 1;
+  $_->$cb($i++) for @$self;
+  return $self;
+}
+
+sub first {
+  my ($self, $cb) = (shift, shift);
+  return $self->[0] unless $cb;
+  return List::Util::first { $_ =~ $cb } @$self if ref $cb eq 'Regexp';
+  return List::Util::first { $_->$cb(@_) } @$self;
+}
+
+sub flatten { $_[0]->new(_flatten(@{$_[0]})) }
+
+sub grep {
+  my ($self, $cb) = (shift, shift);
+  return $self->new(grep { $_ =~ $cb } @$self) if ref $cb eq 'Regexp';
+  return $self->new(grep { $_->$cb(@_) } @$self);
+}
+
+sub join {
+  join $_[1] // '', map {"$_"} @{$_[0]};
+}
+
+sub last { shift->[-1] }
+
+sub map {
+  my ($self, $cb) = (shift, shift);
+  return $self->new(map { $_->$cb(@_) } @$self);
+}
+
+sub reduce {
+  my $self = shift;
+  @_ = (@_, @$self);
+  goto &List::Util::reduce;
+}
+
+sub reverse { $_[0]->new(reverse @{$_[0]}) }
+
+sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) }
+
+sub size { scalar @{$_[0]} }
+
+sub slice {
+  my $self = shift;
+  return $self->new(@$self[@_]);
+}
+
+sub sort {
+  my ($self, $cb) = @_;
+
+  return $self->new(sort @$self) unless $cb;
+
+  my $caller = caller;
+  no strict 'refs';
+  my @sorted = sort {
+    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
+    $a->$cb($b);
+  } @$self;
+  return $self->new(@sorted);
+}
+
+sub tap {
+  my ($self, $cb) = (shift, shift);
+  $_->$cb(@_) for $self;
+  return $self;
+}
+
+sub to_array { [@{shift()}] }
+
+sub uniq {
+  my ($self, $cb) = (shift, shift);
+  my %seen;
+  return $self->new(grep { !$seen{$_->$cb(@_)}++ } @$self) if $cb;
+  return $self->new(grep { !$seen{$_}++ } @$self);
+}
+
+sub _flatten {
+  map { _ref($_) ? _flatten(@$_) : $_ } @_;
+}
+
+sub _ref { ref $_[0] eq 'ARRAY' || blessed $_[0] && $_[0]->isa(__PACKAGE__) }
+
+1;
index 0c6332f..b455ba1 100644 (file)
@@ -1,10 +1,10 @@
 use strict;
 use warnings;
 use Test::More;
-use DOM::Tiny::Collection;
+use DOM::Tiny::_Collection;
 use JSON::PP ();
 
-sub c { DOM::Tiny::Collection->new(@_) }
+sub c { DOM::Tiny::_Collection->new(@_) }
 
 # Array
 is c(1, 2, 3)->[1], 2, 'right result';