From 66af6c678e9b03b9d13eb013befc171a8770614e Mon Sep 17 00:00:00 2001 From: Horst Duchene Date: Sun, 10 May 2026 13:44:06 +0000 Subject: [PATCH] fix: convert README to Markdown, add copyright, fix badges, clean up - Convert README.rdoc to README.md (working Markdown badges) - Add project copyright notice to LICENSE (Horst Duchene, 2002-2026) - Remove deprecated rdoc_options and extra_rdoc_files from gemspec - Remove yard and rdoc dev dependencies (rubydoc.info handles docs) - Remove obsolete :tag task, YARD rake task and unused requires from Rakefile --- LICENSE | 4 + README.md | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ README.rdoc | 181 ------------------------------------------ Rakefile | 29 ------- stream.gemspec | 13 +-- 5 files changed, 214 insertions(+), 222 deletions(-) create mode 100644 README.md delete mode 100644 README.rdoc diff --git a/LICENSE b/LICENSE index 426810a..b0a02c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,7 @@ +Copyright (c) 2002-2026 Horst Duchene + +stream is distributed under the same license as Ruby. + Ruby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6e6e74 --- /dev/null +++ b/README.md @@ -0,0 +1,209 @@ +[![Build Status](https://github.com/monora/stream/actions/workflows/ruby.yml/badge.svg)](https://github.com/monora/stream/actions/workflows/ruby.yml) +[![Version](https://img.shields.io/gem/v/stream.svg)](https://rubygems.org/gems/stream) + +# Extended External Iterators (forward and backward) + +## Description + +Module `Stream` defines an interface for [external +iterators](https://wiki.c2.com/?ExternalIterator). A stream can be seen +as an iterator on a sequence of objects `x1,...,xn`. The state of the +stream is uniquely determined by the following methods: + +* `at_beginning?` +* `at_end?` +* `current` +* `peek` + +State changes are done with the following operations: + +* `set_to_begin` +* `set_to_end` +* `forward` +* `backward` + +With the help of the method `current_edge` the state of a stream `s` can be +exactly defined + +``` +s.current_edge == [s.current, s.peek] +``` + +If `s` a stream on `[x1,...,xn]`. Consider the edges `[xi,xi+1]` i=1,...,n +and `[x0,x1]` and `[xn,xn+1]` (x0 and xn+1 are helper elements to define +the boundary conditions). Then if `s` is non empty, the following +conditions must be true: + +``` +s.at_beginning? <=> s.current_edge == [x0,x1] +s.at_end? <=> s.current_edge == [xn,xn+1] +s.empty? <=> s.at_beginning? && s.at_end? <=> s.current_edge == [x0,x1] <=> n = 0 +s.set_to_end => s.at_end? +s.set_to_begin => s.at_beginning? +``` + +If `0 <= i < n` and `s.current_edge == [xi, xi+1]`, then: + +``` +[s.forward, s.current_edge] == [xi+1, [xi+1, xi+2]] +``` + +If `1 <= i < n` and `s.current_edge == [xi, xi+1]`, then: + +``` +[s.backward, s.current_edge] == [xi, [xi-1, xi]] +``` + +The result of `peek` is the same as of `forward` without changing state. The result of +`current` is the same as of `backward` without changing state. + +Module `Stream` includes `Enumerable` implementing `each` in the obvious way. + +Not every stream needs to implement `backward` and `at_beginning?` +thus being not reversable. If they are reversable `peek` can easily be +implemented using `forward` and `backward`, as is done in module +`Stream`. If a stream is not reversable all derived streams provided +by the stream module (filter, mapping, concatenation) can be used +anyway. Explicit or implicit (via `peek` or `current`) uses of `backward` +would throw a `NotImplementedError`. + +Classes implementing the stream interface must implement the following +methods: + +* `basic_forward` +* `basic_backward` +* `at_end?` +* `at_beginning?` + +The methods `set_to_end` and `set_to_begin` are by default implemented as: + +``` +set_to_end : until at_end?; do basic_forward end +set_to_begin : until at_beginning?; do basic_backward end +``` + +The methods `forward` and `backward` are by default implemented as: + +``` +forward: raise EndOfStreamException if at_end?; basic_forward. +backward: raise EndOfStreamException if at_beginning?; basic_backward +``` + +Thus subclasses must only implement **four** methods. Efficiency sometimes +demands better implementations. + +There are several concrete classes implementing the stream interface: + +* `Stream::EmptyStream` (boring) +* `Stream::CollectionStream` created by the method `Array#create_stream` +* `Stream::FilteredStream` created by the method `Stream#filtered` +* `Stream::ReversedStream` created by the method `Stream#reverse` +* `Stream::ConcatenatedStream` created by the method `Stream#concatenate` +* `Stream::ImplicitStream` using closures for the basic methods to implement + +## Installation + +```bash +gem install stream +``` + +or download the latest sources from the git repository +. + +## Examples + +### Iterate over three streams + +```ruby +g = ('a'..'f').create_stream +h = (1..10).create_stream +i = (10..20).create_stream + +until g.at_end? || h.at_end? || i.at_end? + p [g.forward, h.forward, i.forward] +end +``` + +Output: + +``` +["a", 1, 10] +["b", 2, 11] +["c", 3, 12] +["d", 4, 13] +["e", 5, 14] +["f", 6, 15] +``` + +### Concatenate file streams + +```ruby +def filestream fname + Stream::ImplicitStream.new { |s| + f = open(fname) + s.at_end_proc = proc {f.eof?} + s.forward_proc = proc {f.readline} + # Need not implement backward moving to use the framework + } +end + +(filestream("/etc/passwd") + ('a'..'f').create_stream + filestream("/etc/group")).each do |l| + puts l +end +``` + +### Two filtered collection streams concatenated and reversed + +```ruby +def newstream; (1..6).create_stream; end +s = newstream.filtered { |x| x % 2 == 0 } + newstream.filtered { |x| x % 2 != 0 } +s = s.reverse +puts "Contents : #{s.to_a.join ' '}" +puts "At end? : #{s.at_end?}" +puts "At beginning? : #{s.at_beginning?}" +puts "2xBackwards : #{s.backward} #{s.backward}" +puts "Forward : #{s.forward}" +puts "Peek : #{s.peek}" +puts "Current : #{s.current}" +puts "set_to_begin : Peek=#{s.set_to_begin;s.peek}" +``` + +Output: + +``` +Contents : 5 3 1 6 4 2 +At end? : true +At beginning? : false +2xBackwards : 2 4 +Forward : 4 +Peek : 2 +Current : 4 +set_to_begin : Peek=5 +``` + +### An infinite stream (do not use `set_to_end`!) + +```ruby +def randomStream + Stream::ImplicitStream.new { |s| + s.set_to_begin_proc = proc {srand 1234} + s.at_end_proc = proc {false} + s.forward_proc = proc {rand} + } +end +s = randomStream.filtered { |x| x >= 0.5 }.collect { |x| sprintf("%5.2f",x*100) } +puts "5 random numbers: #{(1..5).collect {|x| s.forward}}\n" +``` + +Output: + +``` +5 random numbers: ["62.21", "78.54", "78.00", "80.19", "95.81"] +``` + +## Copying + +stream is Copyright (c) 2002-2026 by Horst Duchene. + +It is free software, and may be redistributed under the terms specified +in the [LICENSE](LICENSE) file. diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 911af77..0000000 --- a/README.rdoc +++ /dev/null @@ -1,181 +0,0 @@ -{Build Status}[https://github.com/monora/stream/actions/workflows/ruby.yml] -{Version}[https://rubygems.org/gems/stream] - -= Extended External Iterators (forward and backward) - -== Description - -Module +Stream+ defines an interface for {external -iterators}[https://wiki.c2.com/?ExternalIterator]. A stream can be seen -as an iterator on a sequence of objects +x1,...,xn+. The state of the -stream is uniquely determined by the following methods: - -* at_beginning? -* at_end? -* current -* peek - -State changes are done with the following operations: - -* set_to_begin -* set_to_end -* forward -* backward - -With the help of the method +current_edge+ the state of a stream +s+ can be -exactly defined - - s.current_edge == [s.current, s.peek] - -If +s+ a stream on [x1,...,xn]. Consider the edges [xi,xi+1] i=1,...,n -and [x0,x1] and [xn,xn+1] (x0 and xn+1 are helper elements to define -the boundary conditions). Then if +s+ is non empty, the following -conditions must be true: - - s.at_beginning? <=> s.current_edge == [x0,x1] - s.at_end? <=> s.current_edge == [xn,xn+1] - s.empty? <=> s.at_beginning? && s.at_end? <=> s.current_edge == [x0,x1] <=> n = 0 - s.set_to_end => s.at_end? - s.set_to_begin => s.at_beginning? - -If 0 <= i < n and s.current_edge == [xi, xi+1] , then: - - [s.forward, s.current_edge] == [xi+1, [xi+1, xi+2]] - -If 1 <= i < n and s.current_edge == [xi, xi+1] , then: - - [s.backward, s.current_edge] == [xi, [xi-1, xi]] - -The result of peek is the same as of forward without changing state. The result of -current is the same as of backward without changing state. - -Module +Stream+ includes +Enumerable+ implementing +each+ in the obvious way. - -Not every stream needs to implement +backward+ and +at_beginning?+ -thus being not reversable. If they are reversable peek can easily be -implemented using +forward+ and +backward+, as is done in module -+Stream+. If a stream is not reversable all derived streams provided -by the stream module (filter, mapping, concatenation) can be used -anyway. Explicit or implicit (via peek or current) uses of backward -would throw a +NotImplementedError+. - -Classes implementing the stream interface must implement the following -methods: - -* basic_forward -* basic_backward - -* at_end? -* at_beginning? - -The methods +set_to_end+ and +set_to_begin+ are by default implemented -as: - - set_to_end : until at_end?; do basic_forward end - set_to_begin : until at_beginning?; do basic_backward end - -The methods +forward+ and +backward+ are by default implemented as: - - forward: raise EndOfStreamException if at_end?; basic_forward. - backward: raise EndOfStreamException if at_beginning?; basic_backward - -Thus subclasses must only implement *four* methods. Efficiency sometimes -demands better implementations. - -There are several concrete classes implementing the stream interface: - -* +Stream::EmptyStream+ (boring) -* +Stream::CollectionStream+ created by the method +Array#create_stream+ -* +Stream::FilteredStream+ created by the method +Stream#filtered+ -* +Stream::ReversedStream+ created by the method +Stream#reverse+ -* +Stream::ConcatenatedStream+ created by the method +Stream#concatenate+ -* +Stream::ImplicitStream+ using closures for the basic methods to implement - -== Installation - - gem install stream - -or download the latest sources from the git repository -https://github.com/monora/stream. - -== Examples - -=== Iterate over three streams - - g = ('a'..'f').create_stream - h = (1..10).create_stream - i = (10..20).create_stream - - until g.at_end? || h.at_end? || i.at_end? - p [g.forward, h.forward, i.forward] - end - -Output: - - ["a", 1, 10] - ["b", 2, 11] - ["c", 3, 12] - ["d", 4, 13] - ["e", 5, 14] - ["f", 6, 15] - -=== Concatenate file streams - - def filestream fname - Stream::ImplicitStream.new { |s| - f = open(fname) - s.at_end_proc = proc {f.eof?} - s.forward_proc = proc {f.readline} - # Need not implement backward moving to use the framework - } - end - - (filestream("/etc/passwd") + ('a'..'f').create_stream + filestream("/etc/group")).each do |l| - puts l - end - -=== Two filtered collection streams concatenated and reversed - - def newstream; (1..6).create_stream; end - s = newstream.filtered { |x| x % 2 == 0 } + newstream.filtered { |x| x % 2 != 0 } - s = s.reverse - puts "Contents : #{s.to_a.join ' '}" - puts "At end? : #{s.at_end?}" - puts "At beginning? : #{s.at_beginning?}" - puts "2xBackwards : #{s.backward} #{s.backward}" - puts "Forward : #{s.forward}" - puts "Peek : #{s.peek}" - puts "Current : #{s.current}" - puts "set_to_begin : Peek=#{s.set_to_begin;s.peek}" - -Output: - - Contents : 5 3 1 6 4 2 - At end? : true - At beginning? : false - 2xBackwards : 2 4 - Forward : 4 - Peek : 2 - Current : 4 - set_to_begin : Peek=5 - -=== An infinite stream (do not use +set_to_end+!) - - def randomStream - Stream::ImplicitStream.new { |s| - s.set_to_begin_proc = proc {srand 1234} - s.at_end_proc = proc {false} - s.forward_proc = proc {rand} - } - end - s = randomStream.filtered { |x| x >= 0.5 }.collect { |x| sprintf("%5.2f",x*100) } - puts "5 random numbers: #{(1..5).collect {|x| s.forward}}\n" - -Output: - - 5 random numbers: ["62.21", "78.54", "78.00", "80.19", "95.81"] - -== License - -Author:: Horst Duchene -License:: Copyright (c) 2001, 2013, 2016, 2020, 2022 Horst Duchene (Released under the same license as Ruby (see LICENSE)) diff --git a/Rakefile b/Rakefile index ee8c4e1..c430ad7 100644 --- a/Rakefile +++ b/Rakefile @@ -1,22 +1,12 @@ # Rakefile for stream -*- ruby -*- require 'bundler/setup' -require 'rubygems/package_task' require 'rake/testtask' -require 'rake/clean' -require 'yard' - - -SRC_RB = FileList['lib/*.rb'] - -# The default task is run if rake is given no explicit arguments. desc "Default Task" task :default => :test -# Define a test task. - Rake::TestTask.new do |t| t.libs << 'test' t.pattern = 'test/test*.rb' @@ -25,23 +15,4 @@ end task :test -# Define a test that will run all the test targets. -desc "Run all test targets" -task :testall => [:test ] - -# Git tagging - -desc "Commit all changes as a new version commit. Tag the commit with v tag" -task :tag do - puts "Committing and tagging version #{STREAM_VERSION}" - `git commit -am 'Version #{STREAM_VERSION}'` - `git tag 'v#{STREAM_VERSION}'` -end - -# Documentation - -YARD::Rake::YardocTask.new - -# Tasks for building and installing Stream gem. - Bundler::GemHelper.install_tasks diff --git a/stream.gemspec b/stream.gemspec index 69913a5..51da4de 100644 --- a/stream.gemspec +++ b/stream.gemspec @@ -9,8 +9,6 @@ Gem::Specification.new do |s| #### Dependencies and requirements. s.add_development_dependency 'rake' - s.add_development_dependency 'yard' - s.add_development_dependency 'rdoc' s.add_development_dependency 'test-unit' #### Which files are to be included in this gem? @@ -21,7 +19,7 @@ Gem::Specification.new do |s| 'test/**/*.rb', 'examples/**/*', 'Gemfile', - 'README.rdoc', + 'README.md', 'LICENSE', 'Rakefile' ] @@ -31,15 +29,6 @@ Gem::Specification.new do |s| s.require_paths = ['lib'] s.license = 'ruby' - #### Documentation and testing. - - s.extra_rdoc_files = ['README.rdoc'] - s.rdoc_options += [ - '--title', s.summary, - '--main', 'README.rdoc', - '--line-numbers' - ] - #### Author and project details. s.authors = [ "Horst Duchene" ]