bless({ _code => $args->{code} }, $class);
}
-sub peek {
- my ($self) = @_;
- if (exists $self->{_peeked}) {
- return ($self->{_peeked});
- }
- if (my ($peeked) = $self->next) {
- return ($self->{_peeked} = $peeked);
- }
- return;
-}
-
sub next {
my ($self) = @_;
HTML::Zoom::CodeStream->from_array(@_)
}
+sub _stream_from_proto {
+ my ($self, $proto) = @_;
+ my $ref = ref $proto;
+ if (not $ref) {
+ return $self->_stream_from_array({ type => 'TEXT', raw => $proto });
+ } elsif ($ref eq 'ARRAY') {
+ return $self->_stream_from_array(@$proto);
+ } elsif ($ref eq 'CODE') {
+ return $proto->();
+ } elsif ($ref eq 'SCALAR') {
+ require HTML::Zoom::Parser::BuiltIn;
+ return HTML::Zoom::Parser::BuiltIn->html_to_stream($$proto);
+ }
+ die "What the hell is $proto and how should I turn a $ref into a stream?";
+}
+
sub _stream_concat {
- shift; # lose $self
- my @streams = @_;
- my $cur_stream = shift(@streams) or die "No streams passed";
- HTML::Zoom::CodeStream->new({
- code => sub {
- return unless $cur_stream;
- my $evt;
- until (($evt) = $cur_stream->next) {
- return unless $cur_stream = shift(@streams);
- }
- return $evt;
- }
- });
+ shift->_stream_from_array(@_)->flatten;
}
sub set_attribute {
}
sub replace {
- my ($self, $events, $options) = @_;
+ my ($self, $replace_with, $options) = @_;
my $coll_proto = $self->collect($options);
sub {
my ($evt, $stream) = @_;
- my $emit = $self->_stream_from_array(@$events);
+ my $emit = $self->_stream_from_proto($replace_with);
my $coll = &$coll_proto;
# For a straightforward replace operation we can, in fact, do the emit
# -before- the collect, and my first cut did so. However in order to
};
}
+sub repeat {
+ my ($self, $repeat_for, $options) = @_;
+ $options->{into} = \my @into;
+ my $map_repeat = sub {
+ local $_ = $self->_stream_from_array(@into);
+ $_[0]->($_)
+ };
+ my $repeater = sub {
+ $self->_stream_from_proto($repeat_for)
+ ->map($map_repeat)
+ ->flatten
+ };
+ $self->replace($repeater, $options);
+}
+
1;
);
}
-sub peek {
- my ($self) = @_;
- if (exists $self->{_peeked}) {
- return ($self->{_peeked});
- }
- if (my ($peeked) = $self->next) {
- return ($self->{_peeked} = $peeked);
- }
- return;
-}
-
sub next {
my ($self) = @_;
use strict;
use warnings FATAL => 'all';
+sub peek {
+ my ($self) = @_;
+ if (exists $self->{_peeked}) {
+ return ($self->{_peeked});
+ }
+ if (my ($peeked) = $self->next) {
+ return ($self->{_peeked} = $peeked);
+ }
+ return;
+}
+
+sub flatten {
+ my $source_stream = shift;
+ require HTML::Zoom::CodeStream;
+ my $cur_stream;
+ HTML::Zoom::CodeStream->new({
+ code => sub {
+ return unless $source_stream;
+ my $next;
+ until (($next) = ($cur_stream ? $cur_stream->next : ())) {
+#::Dwarn $source_stream;
+ unless (($cur_stream) = $source_stream->next) {
+ undef $source_stream; return;
+ }
+ }
+#::Dwarn $next;
+ return $next;
+ }
+ });
+}
+
+sub map {
+ my ($source_stream, $map_func) = @_;
+ require HTML::Zoom::CodeStream;
+ HTML::Zoom::CodeStream->new({
+ code => sub {
+ return unless $source_stream;
+ # If we were aiming for a "true" perl-like map then we should
+ # elegantly handle the case where the map function returns 0 events
+ # and the case where it returns >1 - if you're reading this comment
+ # because you wanted it to do that, now would be the time to fix it :)
+ if (my ($next) = $source_stream->next) {
+ #### XXXX collapsing this into a return doesn't work. what the
+ #### flying fornication ... -- mst
+ my $mapped = do { local $_ = $next; $map_func->($next) };
+ return $mapped;
+ }
+ undef $source_stream; return;
+ }
+ });
+}
+
1;
use warnings FATAL => 'all';
use Test::More;
+use Devel::Dwarn;
+
use HTML::Zoom::Parser::BuiltIn;
use HTML::Zoom::Producer::BuiltIn;
use HTML::Zoom::SelectorParser;
'append inside ok'
);
+if (1) {
+
+warn "\n\n----\n\n";
+
+my $r_inside = sub { my $r = shift; sub { $_->replace($r, { inside => 1 }) } };
+
+is(
+ run_for {
+ $_->repeat(
+ [
+ sub {
+ filter
+ filter($_ => '.name' => $r_inside->('mst'))
+ => '.career' => $r_inside->('Chainsaw Wielder')
+ },
+ sub {
+ filter
+ filter($_ => '.name' => $r_inside->('mdk'))
+ => '.career' => $r_inside->('Adminion')
+ },
+ ]
+ )
+ },
+ q{<body>
+ <div class="main">
+ <span class="hilight name">mst</span>
+ <span class="career">Chainsaw Wielder</span>
+ <hr />
+ </div><div class="main">
+ <span class="hilight name">mdk</span>
+ <span class="career">Adminion</span>
+ <hr />
+ </div>
+</body>
+},
+ 'repeat ok'
+);
+
+}
+
done_testing;