stream 0.5 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3374a923bc2d44c39ea1c1957d5982b26f55a22ac958cfa119be87a978e17676
4
+ data.tar.gz: 88b9b768b08a6edf3a689f76f50627784825b15ac1ed07d6b3172bb34ce69fa3
5
+ SHA512:
6
+ metadata.gz: fd0f115fefc0969ccde444c8200d9f279b8d9736af6c91884d89e5fa9dd4a46dccb6b570da4467b7dfaa35e038644b09dbf25348289a66cf0ea919c4c21c732b
7
+ data.tar.gz: 56d49b892e3355026bb05bd98535471ebfe9e8564f9f1885e43938deda8b7f3e33551fcd15f1b492821944de5707ef296dff3f134812b1f8e904ce0ef8b4f037
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "codeclimate-test-reporter", group: :test, require: nil
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stream (0.5.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ codeclimate-test-reporter (1.0.9)
10
+ simplecov (<= 0.13)
11
+ docile (1.1.5)
12
+ json (2.1.0)
13
+ power_assert (1.1.3)
14
+ rake (12.3.2)
15
+ rdoc (6.1.1)
16
+ simplecov (0.13.0)
17
+ docile (~> 1.1.0)
18
+ json (>= 1.8, < 3)
19
+ simplecov-html (~> 0.10.0)
20
+ simplecov-html (0.10.2)
21
+ test-unit (3.2.9)
22
+ power_assert
23
+ yard (0.9.16)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ codeclimate-test-reporter
30
+ rake
31
+ rdoc
32
+ stream!
33
+ test-unit
34
+ yard
35
+
36
+ BUNDLED WITH
37
+ 2.0.1
@@ -1,3 +1,5 @@
1
+ {<img src="https://secure.travis-ci.org/monora/stream.png?branch=master" alt="Build Status" />}[https://travis-ci.org/monora/stream]
2
+
1
3
  = Extended External Iterators (forward and backward)
2
4
 
3
5
  == Description
@@ -24,7 +26,7 @@ exactly defined
24
26
  s.current_edge == [s.current, s.peek]
25
27
 
26
28
  If s a stream on [x1,...,xn]. Consider the edges [xi,xi+1] i=1,...,n and
27
- [x0,x1] and [xn,xn+1] (x0 and xn are helper elements to define the boundary
29
+ [x0,x1] and [xn,xn+1] (x0 and xn+1 are helper elements to define the boundary
28
30
  conditions). Then if s is non empty, the following conditions must be true:
29
31
 
30
32
  s.at_beginning? <=> s.current_edge == [x0,x1]
@@ -87,7 +89,7 @@ There are several concrete classes implementing the stream interface:
87
89
 
88
90
  The latest version of stream.rb can be found at
89
91
 
90
- * http://rubyforge.org/frs/?group_id=110
92
+ * http://github.com/monora/stream
91
93
 
92
94
  == Installation
93
95
 
@@ -112,7 +114,6 @@ root privileges to install.
112
114
 
113
115
  * Streams in Smalltalk: http://wiki.cs.uiuc.edu/PatternStories/FunWithStreams
114
116
  * Simon Strandgaards iterator.rb[http://aeditor.rubyforge.org/iterator/files/iterator_rb.html]
115
- * IterationStyles: http://www.rubygarden.org/ruby?IterationStyles
116
117
 
117
118
  == Examples
118
119
 
@@ -166,9 +167,6 @@ root privileges to install.
166
167
 
167
168
  == Other Stuff
168
169
 
169
- $Revision: 1.1.1.1 $
170
- $Date: 2004/05/13 23:30:03 $
171
-
172
- Author:: Horst Duchene <hd.at.clr@hduchene.de>
173
- License:: Copyright (c) 2001, 2004 Horst Duchene
170
+ Author:: Horst Duchene
171
+ License:: Copyright (c) 2001, 2013, 2016 Horst Duchene
174
172
  Released under the same license as Ruby
data/Rakefile CHANGED
@@ -1,22 +1,15 @@
1
- # Rakefile for -*- ruby -*-
1
+ # Rakefile for stream -*- ruby -*-
2
2
 
3
- begin
4
- require 'rubygems'
5
- rescue Exception
6
- nil
7
- end
8
- require 'rake/clean'
9
- require 'rake/testtask'
10
- require 'rake/gempackagetask'
11
- require 'rake/rdoctask'
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'rubygems/package_task'
12
6
 
13
- # Determine the current version of the software
7
+ require 'rake/testtask'
8
+ require 'rake/clean'
9
+ require 'yard'
14
10
 
15
- if `ruby -Ilib -rstream -e'puts STREAM_VERSION'` =~ /\S+$/
16
- PKG_VERSION = $&
17
- else
18
- PKG_VERSION = "0.0.0"
19
- end
11
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
12
+ require 'stream' # require module to STREAM_VERSION
20
13
 
21
14
  SRC_RB = FileList['lib/*.rb']
22
15
 
@@ -27,11 +20,11 @@ task :default => :test
27
20
 
28
21
  # Define a test task.
29
22
 
30
- Rake::TestTask.new { |t|
31
- #t.libs << "tests"
32
- #t.pattern = 'tests/Test*.rb'
23
+ Rake::TestTask.new do |t|
24
+ t.libs << "tests"
25
+ t.pattern = 'tests/Test*.rb'
33
26
  t.verbose = true
34
- }
27
+ end
35
28
 
36
29
  task :test
37
30
 
@@ -39,123 +32,19 @@ task :test
39
32
  desc "Run all test targets"
40
33
  task :testall => [:test ]
41
34
 
42
- # Install stream using the standard install.rb script.
35
+ # Git tagging
43
36
 
44
- desc "Install the application"
45
- task :install do
46
- ruby "install.rb"
37
+ desc "Commit all changes as a new version commit. Tag the commit with v<version> tag"
38
+ task :tag do
39
+ puts "Committing and tagging version #{STREAM_VERSION}"
40
+ `git commit -am 'Version #{STREAM_VERSION}'`
41
+ `git tag 'v#{STREAM_VERSION}'`
47
42
  end
48
43
 
49
- # Create a task to build the RDOC documentation tree.
50
-
51
- rd = Rake::RDocTask.new("rdoc") { |rdoc|
52
- rdoc.rdoc_dir = 'html'
53
- # rdoc.template = 'kilmer'
54
- # rdoc.template = 'css2'
55
- rdoc.title = "Stream - Extended External Iterators"
56
- rdoc.options << '--line-numbers' << '--inline-source'
57
- rdoc.rdoc_files.include('README')
58
- rdoc.rdoc_files.include('lib/*.rb', 'doc/**/*.rdoc')
59
- }
60
-
61
- # ====================================================================
62
- # Create a task that will package the stream software into distributable
63
- # tar, zip and gem files.
64
-
65
- PKG_FILES = FileList[
66
- 'install.rb',
67
- '[A-Z]*',
68
- 'lib/**/*.rb',
69
- 'test/**/*.rb',
70
- 'examples/**/*'
71
- ]
72
-
73
- if ! defined?(Gem)
74
- puts "Package Target requires RubyGEMs"
75
- else
76
- spec = Gem::Specification.new do |s|
77
-
78
- #### Basic information.
79
-
80
- s.name = 'stream'
81
- s.version = PKG_VERSION
82
- s.summary = "Stream - Extended External Iterators"
83
- s.description = <<-EOF
84
- Module Stream defines an interface for external iterators.
85
- EOF
86
-
87
- #### Dependencies and requirements.
88
-
89
- #s.add_dependency('log4r', '> 1.0.4')
90
- #s.requirements << ""
91
-
92
- #### Which files are to be included in this gem? Everything! (Except CVS directories.)
93
-
94
- s.files = PKG_FILES.to_a
44
+ # Documentation
95
45
 
96
- #### C code extensions.
46
+ YARD::Rake::YardocTask.new
97
47
 
98
- #s.extensions << "ext/rmagic/extconf.rb"
48
+ # Tasks for building and installing Stream gem.
99
49
 
100
- #### Load-time details: library and application (you will need one or both).
101
-
102
- s.require_path = 'lib' # Use these for libraries.
103
- s.autorequire = 'stream'
104
-
105
- #### Documentation and testing.
106
-
107
- s.has_rdoc = true
108
- #s.test_suite_file = "test/rmagic-tests.rb"
109
-
110
- #### Author and project details.
111
- s.author = "Horst Duchene"
112
- s.email = "hd.at.clr@hduchene.de"
113
- s.homepage = "http://rgl.rubyforge.org"
114
- s.rubyforge_project = "rgl"
115
- end
116
-
117
- Rake::GemPackageTask.new(spec) do |pkg|
118
- pkg.need_zip = true
119
- pkg.need_tar = true
120
- end
121
- end
122
-
123
- # Misc tasks =========================================================
124
-
125
- def count_lines(filename)
126
- lines = 0
127
- codelines = 0
128
- open(filename) { |f|
129
- f.each do |line|
130
- lines += 1
131
- next if line =~ /^\s*$/
132
- next if line =~ /^\s*#/
133
- codelines += 1
134
- end
135
- }
136
- [lines, codelines]
137
- end
138
-
139
- def show_line(msg, lines, loc)
140
- printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg
141
- end
142
-
143
- desc "Count lines in the main files"
144
- task :lines do
145
- total_lines = 0
146
- total_code = 0
147
- show_line("File Name", "LINES", "LOC")
148
- SRC_RB.each do |fn|
149
- lines, codelines = count_lines(fn)
150
- show_line(fn, lines, codelines)
151
- total_lines += lines
152
- total_code += codelines
153
- end
154
- show_line("TOTAL", total_lines, total_code)
155
- end
156
-
157
- ARCHIVEDIR = '/mnt/flash'
158
-
159
- task :archive => [:package] do
160
- cp FileList["pkg/*.tgz", "pkg/*.zip", "pkg/*.gem"], ARCHIVEDIR
161
- end
50
+ Bundler::GemHelper.install_tasks
@@ -1,4 +1,4 @@
1
- STREAM_VERSION = "0.5"
1
+ STREAM_VERSION = "0.5.2"
2
2
 
3
3
  ##
4
4
  # Module Stream defines an interface for an external Iterator which
@@ -62,25 +62,25 @@ module Stream
62
62
  # current position. #detect, which is inherited from Enumerable uses
63
63
  # #each, which implicitly calls #set_to_begin.
64
64
  def move_forward_until
65
- until at_end?
66
- element = basic_forward
67
- return element if yield(element)
68
- end
69
- nil
65
+ until at_end?
66
+ element = basic_forward
67
+ return element if yield(element)
68
+ end
69
+ nil
70
70
  end
71
71
 
72
72
  # Move backward until the boolean block is not false and returns the element
73
73
  # found. Returns nil if no object matches.
74
74
  def move_backward_until
75
- until at_beginning?
76
- element = basic_backward
77
- return element if yield(element)
78
- end
79
- nil
75
+ until at_beginning?
76
+ element = basic_backward
77
+ return element if yield(element)
78
+ end
79
+ nil
80
80
  end
81
-
82
- # Returns the element returned by the last call of #forward. If at_beginning? is
83
- # true self is returned.
81
+
82
+ # Returns the element returned by the last call of #forward. If at_beginning?
83
+ # is true self is returned.
84
84
  def current; at_beginning? ? self : basic_current; end
85
85
 
86
86
  # Returns the element returned by the last call of #backward. If at_end? is
@@ -110,9 +110,9 @@ module Stream
110
110
  yield basic_forward
111
111
  end
112
112
  end
113
-
114
- # create_stream is used for each Enumerable to create a stream for it. A Stream as
115
- # an Enumerable returns itself.
113
+
114
+ # create_stream is used for each Enumerable to create a stream for it. A
115
+ # Stream as an Enumerable returns itself.
116
116
  def create_stream; self end
117
117
 
118
118
  # A Stream::WrappedStream should return the wrapped stream unwrapped. If the
@@ -122,80 +122,83 @@ module Stream
122
122
  # The abstract super class of all concrete Classes implementing the Stream
123
123
  # interface. Only used for including module Stream.
124
124
  class BasicStream
125
- include Stream
125
+ include Stream
126
126
  end
127
127
 
128
- # A Singleton class for an empty stream. EmptyStream.instance is the sole instance
129
- # which answers true for both at_end? and at_beginning?
128
+ # A Singleton class for an empty stream. EmptyStream.instance is the sole
129
+ # instance which answers true for both at_end? and at_beginning?
130
130
  class EmptyStream < BasicStream
131
- require 'singleton'
132
- include Singleton
131
+ require 'singleton'
132
+ include Singleton
133
133
 
134
- def at_end?; true; end
135
- def at_beginning?; true; end
134
+ def at_end?; true; end
135
+ def at_beginning?; true; end
136
136
  end
137
137
 
138
- # A CollectionStream can be used as an external iterator for each interger-indexed
139
- # collection. The state of the iterator is stored in instance variable @pos.
138
+ # A CollectionStream can be used as an external iterator for each
139
+ # interger-indexed collection. The state of the iterator is stored in instance
140
+ # variable @pos.
140
141
  #
141
- # A CollectionStream for an array is created by the method Array#create_stream.
142
+ # A CollectionStream for an array is created by the method
143
+ # Array#create_stream.
142
144
  class CollectionStream < BasicStream
143
- attr_reader :pos
145
+ attr_reader :pos
144
146
 
145
- # Creates a new CollectionStream for the indexable sequence _seq_.
146
- def initialize(seq)
147
- @seq = seq
148
- set_to_begin
149
- end
147
+ # Creates a new CollectionStream for the indexable sequence _seq_.
148
+ def initialize(seq)
149
+ @seq = seq
150
+ set_to_begin
151
+ end
150
152
 
151
- def at_end?; @pos + 1 >= @seq.size; end
152
- def at_beginning?; @pos < 0; end
153
+ def at_end?; @pos + 1 >= @seq.size; end
154
+ def at_beginning?; @pos < 0; end
153
155
 
154
- # positioning
156
+ # positioning
155
157
 
156
- #
157
- def set_to_begin; @pos = -1; end
158
- def set_to_end; @pos = @seq.size - 1; end
158
+ #
159
+ def set_to_begin; @pos = -1; end
160
+ def set_to_end; @pos = @seq.size - 1; end
159
161
 
160
- def basic_forward; @pos += 1; @seq[@pos]; end
161
- def basic_backward; r = @seq[@pos]; @pos -= 1; r; end
162
+ def basic_forward; @pos += 1; @seq[@pos]; end
163
+ def basic_backward; r = @seq[@pos]; @pos -= 1; r; end
162
164
 
163
- protected
165
+ protected
164
166
 
165
- # basic_current and basic_peek can be implemented more efficiently than in
166
- # superclass
167
- def basic_current; @seq[@pos]; end
168
- def basic_peek; @seq[@pos+1]; end
167
+ # basic_current and basic_peek can be implemented more efficiently than in
168
+ # superclass
169
+ def basic_current; @seq[@pos]; end
170
+ def basic_peek; @seq[@pos+1]; end
169
171
 
170
172
  end # CollectionStream
171
173
 
172
174
  # A simple Iterator for iterating over a sequence of integers starting from
173
- # zero up to a given upper bound. Mainly used by Stream::FilteredStream. Could be
174
- # made private but if somebody needs it here it is. Is there a better name for it?
175
+ # zero up to a given upper bound. Mainly used by Stream::FilteredStream. Could
176
+ # be made private but if somebody needs it here it is. Is there a better name
177
+ # for it?
175
178
  #
176
- # The upper bound is stored in the instance variable @stop which can be incremented
177
- # dynamically by the method increment_stop.
179
+ # The upper bound is stored in the instance variable @stop which can be
180
+ # incremented dynamically by the method increment_stop.
178
181
  class IntervalStream < BasicStream
179
- attr_reader :pos
182
+ attr_reader :pos
180
183
 
181
- # Create a new IntervalStream with upper bound _stop_. stop - 1 is the last
182
- # element. By default _stop_ is zero which means that the stream is empty.
183
- def initialize (stop=0)
184
- @stop = stop - 1
185
- set_to_begin
186
- end
184
+ # Create a new IntervalStream with upper bound _stop_. stop - 1 is the last
185
+ # element. By default _stop_ is zero which means that the stream is empty.
186
+ def initialize(stop=0)
187
+ @stop = stop - 1
188
+ set_to_begin
189
+ end
187
190
 
188
- def at_beginning?; @pos < 0; end
189
- def at_end?; @pos == @stop; end
191
+ def at_beginning?; @pos < 0; end
192
+ def at_end?; @pos == @stop; end
190
193
 
191
- def set_to_end; @pos = @stop; end
192
- def set_to_begin; @pos = -1; end
194
+ def set_to_end; @pos = @stop; end
195
+ def set_to_begin; @pos = -1; end
193
196
 
194
- # Increment the upper bound by incr.
195
- def increment_stop (incr=1); @stop += incr; end
197
+ # Increment the upper bound by incr.
198
+ def increment_stop(incr=1); @stop += incr; end
196
199
 
197
- def basic_forward; @pos += 1; end
198
- def basic_backward; @pos -= 1; @pos + 1; end
200
+ def basic_forward; @pos += 1; end
201
+ def basic_backward; @pos -= 1; @pos + 1; end
199
202
  end
200
203
 
201
204
  # Class WrappedStream is the abstract superclass for stream classes that wrap
@@ -208,25 +211,25 @@ module Stream
208
211
  # arrayStream.to_a => [1,2,3]
209
212
  # Stream::WrappedStream.new(arrayStream).to_a => [1,2,3]
210
213
  class WrappedStream < BasicStream
211
- attr_reader :wrapped_stream
214
+ attr_reader :wrapped_stream
212
215
 
213
- # Create a new WrappedStream wrapping the Stream _otherStream_.
214
- def initialize (otherStream)
215
- @wrapped_stream = otherStream
216
- end
216
+ # Create a new WrappedStream wrapping the Stream _otherStream_.
217
+ def initialize(otherStream)
218
+ @wrapped_stream = otherStream
219
+ end
217
220
 
218
- def at_beginning?; @wrapped_stream.at_beginning?; end
219
- def at_end?; @wrapped_stream.at_end?; end
221
+ def at_beginning?; @wrapped_stream.at_beginning?; end
222
+ def at_end?; @wrapped_stream.at_end?; end
220
223
 
221
- def set_to_end; @wrapped_stream.set_to_end; end
222
- def set_to_begin; @wrapped_stream.set_to_begin; end
224
+ def set_to_end; @wrapped_stream.set_to_end; end
225
+ def set_to_begin; @wrapped_stream.set_to_begin; end
223
226
 
224
- # Returns the wrapped stream unwrapped.
225
- def unwrapped; @wrapped_stream.unwrapped; end
227
+ # Returns the wrapped stream unwrapped.
228
+ def unwrapped; @wrapped_stream.unwrapped; end
226
229
 
227
- public # but should be protected. Would like to have a friend concept here.
228
- def basic_forward; @wrapped_stream.basic_forward; end
229
- def basic_backward; @wrapped_stream.basic_backward; end
230
+ public # but should be protected. Would like to have a friend concept here.
231
+ def basic_forward; @wrapped_stream.basic_forward; end
232
+ def basic_backward; @wrapped_stream.basic_backward; end
230
233
  end
231
234
 
232
235
  ##
@@ -238,212 +241,215 @@ module Stream
238
241
  # (1..6).create_stream.filtered { |x| x % 2 == 0 }.to_a ==> [2, 4, 6]
239
242
  class FilteredStream < WrappedStream
240
243
 
241
- # Create a new FilteredStream wrapping _otherStream_ and selecting all its
242
- # elements which satisfy the condition defined by the block_filter_.
243
- def initialize (otherStream, &filter)
244
- super otherStream
245
- @filter = filter
246
- @positionHolder = IntervalStream.new
247
- set_to_begin
248
- end
249
-
250
- def at_beginning?; @positionHolder.at_beginning?; end
251
-
252
- # at_end? has to look ahead if there is an element satisfing the filter
253
- def at_end?
254
- @positionHolder.at_end? and
255
- begin
256
- if @peek.nil?
257
- @peek = wrapped_stream.move_forward_until( &@filter ) or return true
258
- @positionHolder.increment_stop
259
- end
260
- false
261
- end
262
- end
263
-
264
- def basic_forward
265
- result =
266
- if @peek.nil?
267
- wrapped_stream.move_forward_until(&@filter)
268
- else
269
- # Do not move!!
270
- @peek
271
- end
272
- @peek = nil
273
- @positionHolder.forward
274
- result
275
- end
276
-
277
- def basic_backward
278
- wrapped_stream.backward unless @peek.nil?
279
- @peek = nil
280
- @positionHolder.backward
281
- wrapped_stream.move_backward_until(&@filter) or self
282
- end
283
-
284
- def set_to_end
285
- # Not super which is a WrappedStream, but same behavior as in Stream
286
- until at_end?; basic_forward; end
287
- end
288
-
289
- def set_to_begin
290
- super
291
- @peek = nil
292
- @positionHolder.set_to_begin
293
- end
294
-
295
- # Returns the current position of the stream.
296
- def pos; @positionHolder.pos; end
244
+ # Create a new FilteredStream wrapping _otherStream_ and selecting all its
245
+ # elements which satisfy the condition defined by the block_filter_.
246
+ def initialize(otherStream, &filter)
247
+ super otherStream
248
+ @filter = filter
249
+ @positionHolder = IntervalStream.new
250
+ set_to_begin
251
+ end
252
+
253
+ def at_beginning?; @positionHolder.at_beginning?; end
254
+
255
+ # at_end? has to look ahead if there is an element satisfing the filter
256
+ def at_end?
257
+ @positionHolder.at_end? and
258
+ begin
259
+ if @peek.nil?
260
+ @peek = wrapped_stream.move_forward_until( &@filter ) or return true
261
+ @positionHolder.increment_stop
262
+ end
263
+ false
264
+ end
265
+ end
266
+
267
+ def basic_forward
268
+ result =
269
+ if @peek.nil?
270
+ wrapped_stream.move_forward_until(&@filter)
271
+ else
272
+ # Do not move!!
273
+ @peek
274
+ end
275
+ @peek = nil
276
+ @positionHolder.forward
277
+ result
278
+ end
279
+
280
+ def basic_backward
281
+ wrapped_stream.backward unless @peek.nil?
282
+ @peek = nil
283
+ @positionHolder.backward
284
+ wrapped_stream.move_backward_until(&@filter) or self
285
+ end
286
+
287
+ def set_to_end
288
+ # Not super which is a WrappedStream, but same behavior as in Stream
289
+ until at_end?; basic_forward; end
290
+ end
291
+
292
+ def set_to_begin
293
+ super
294
+ @peek = nil
295
+ @positionHolder.set_to_begin
296
+ end
297
+
298
+ # Returns the current position of the stream.
299
+ def pos; @positionHolder.pos; end
297
300
  end # FilteredStream
298
301
 
299
302
  ##
300
- # Each reversable stream (a stream that implements #backward and at_beginning?) can
301
- # be wrapped by a ReversedStream.
303
+ # Each reversable stream (a stream that implements #backward and
304
+ # at_beginning?) can be wrapped by a ReversedStream.
302
305
  #
303
306
  # A ReversedStream is created by the method #reverse:
304
307
  #
305
308
  # (1..6).create_stream.reverse.to_a ==> [6, 5, 4, 3, 2, 1]
306
309
  class ReversedStream < WrappedStream
307
310
 
308
- # Create a reversing wrapper for the reversable stream _otherStream_. If
309
- # _otherStream_ does not support backward moving a NotImplementedError is signaled
310
- # on the first backward move.
311
- def initialize (otherStream)
312
- super otherStream
313
- set_to_begin
314
- end
315
-
316
- # Returns true if the wrapped stream is at_end?.
317
- def at_beginning?; wrapped_stream.at_end?; end
318
- # Returns true if the wrapped stream is at_beginning?.
319
- def at_end?; wrapped_stream.at_beginning?; end
320
-
321
- # Moves the wrapped stream one step backward.
322
- def basic_forward; wrapped_stream.basic_backward; end
323
- # Moves the wrapped stream one step forward.
324
- def basic_backward; wrapped_stream.basic_forward; end
325
-
326
- # Sets the wrapped stream to the beginning.
327
- def set_to_end; wrapped_stream.set_to_begin; end
328
- # Sets the wrapped stream to the end.
329
- def set_to_begin; wrapped_stream.set_to_end; end
311
+ # Create a reversing wrapper for the reversable stream _otherStream_. If
312
+ # _otherStream_ does not support backward moving a NotImplementedError is
313
+ # signaled on the first backward move.
314
+ def initialize(otherStream)
315
+ super otherStream
316
+ set_to_begin
317
+ end
318
+
319
+ # Returns true if the wrapped stream is at_end?.
320
+ def at_beginning?; wrapped_stream.at_end?; end
321
+ # Returns true if the wrapped stream is at_beginning?.
322
+ def at_end?; wrapped_stream.at_beginning?; end
323
+
324
+ # Moves the wrapped stream one step backward.
325
+ def basic_forward; wrapped_stream.basic_backward; end
326
+ # Moves the wrapped stream one step forward.
327
+ def basic_backward; wrapped_stream.basic_forward; end
328
+
329
+ # Sets the wrapped stream to the beginning.
330
+ def set_to_end; wrapped_stream.set_to_begin; end
331
+ # Sets the wrapped stream to the end.
332
+ def set_to_begin; wrapped_stream.set_to_end; end
330
333
  end
331
334
 
332
335
  ##
333
- # The analog to Enumerable#collect for a stream is a MappedStream wrapping another
334
- # stream. A MappedStream is created by the method #collect, thus modifying
335
- # the behavior mixed in by Enumerable:
336
+ # The analog to Enumerable#collect for a stream is a MappedStream wrapping
337
+ # another stream. A MappedStream is created by the method #collect, thus
338
+ # modifying the behavior mixed in by Enumerable:
336
339
  #
337
340
  # (1..5).create_stream.collect {|x| x**2}.type ==> Stream::MappedStream
338
341
  # (1..5).collect {|x| x**2} ==> [1, 4, 9, 16, 25]
339
342
  # (1..5).create_stream.collect {|x| x**2}.to_a ==> [1, 4, 9, 16, 25]
340
343
  class MappedStream < WrappedStream
341
344
 
342
- ##
343
- # Creates a new MappedStream wrapping _otherStream_ which calls the block
344
- # _mapping_ on each move.
345
- def initialize (otherStream, &mapping)
346
- super otherStream
347
- @mapping = mapping
348
- end
349
-
350
- # Apply the stored closure for the next element in the wrapped stream and return
351
- # the result.
352
- def basic_forward; @mapping.call(super); end
353
- # Apply the stored closure for the previous element in the wrapped stream and return
354
- # the result.
355
- def basic_backward; @mapping.call(super); end
345
+ ##
346
+ # Creates a new MappedStream wrapping _otherStream_ which calls the block
347
+ # _mapping_ on each move.
348
+ def initialize(otherStream, &mapping)
349
+ super otherStream
350
+ @mapping = mapping
351
+ end
352
+
353
+ # Apply the stored closure for the next element in the wrapped stream and
354
+ # return the result.
355
+ def basic_forward; @mapping.call(super); end
356
+ # Apply the stored closure for the previous element in the wrapped stream
357
+ # and return the result.
358
+ def basic_backward; @mapping.call(super); end
356
359
  end
357
360
 
358
361
  ##
359
- # Given a stream of streams. Than a ConcatenatedStream is obtained by concatenating
360
- # these in the given order. A ConcatenatedStream is created by the methods
361
- # Stream#concatenate or Stream#concatenate_collected send to a stream of streams or
362
- # by the method + which concatenats two streams:
362
+ # Given a stream of streams. Than a ConcatenatedStream is obtained by
363
+ # concatenating these in the given order. A ConcatenatedStream is created by
364
+ # the methods Stream#concatenate or Stream#concatenate_collected send to a
365
+ # stream of streams or by the method + which concatenats two streams:
363
366
  #
364
367
  # ((1..3).create_stream + [4,5].create_stream).to_a ==> [1, 2, 3, 4, 5]
365
368
  class ConcatenatedStream < WrappedStream
366
- alias :streamOfStreams :wrapped_stream
367
- private :streamOfStreams
368
-
369
- # Creates a new ConcatenatedStream wrapping the stream of streams _streamOfStreams_.
370
- def initialize (streamOfStreams)
371
- super
372
- set_to_begin
373
- end
374
-
375
- # If the current stream is at end, than at_end? has to look ahead to find a non
376
- # empty in the stream of streams, which than gets the current stream.
377
- def at_end?
378
- @currentStream.at_end? and
379
- begin
380
- until streamOfStreams.at_end?
381
- dir, @dirOfLastMove = @dirOfLastMove, :forward
382
- s = streamOfStreams.basic_forward
383
- # if last move was backwards, then @currentStream is
384
- # equivalent to s. Move to next stream.
385
- next if dir == :backward
386
- s.set_to_begin
387
- if s.at_end? # empty stream?
388
- next # skip it
389
- else
390
- @currentStream = s
391
- return false # found non empty stream
392
- end
393
- end
394
- reachedBoundary # sets @dirOfLastMove and @currentStream
395
- end
396
- end
369
+ alias :streamOfStreams :wrapped_stream
370
+ private :streamOfStreams
371
+
372
+ # Creates a new ConcatenatedStream wrapping the stream of streams
373
+ # _streamOfStreams_.
374
+ def initialize(streamOfStreams)
375
+ super
376
+ set_to_begin
377
+ end
378
+
379
+ # If the current stream is at end, than at_end? has to look ahead to find a
380
+ # non empty in the stream of streams, which than gets the current stream.
381
+ def at_end?
382
+ if @currentStream.at_end?
383
+ return false
384
+ else
385
+ until streamOfStreams.at_end?
386
+ dir, @dirOfLastMove = @dirOfLastMove, :forward
387
+ s = streamOfStreams.basic_forward
388
+ # if last move was backwards, then @currentStream is
389
+ # equivalent to s. Move to next stream.
390
+ next if dir == :backward
391
+ s.set_to_begin
392
+ if s.at_end? # empty stream?
393
+ next # skip it
394
+ else
395
+ @currentStream = s
396
+ return false # found non empty stream
397
+ end
398
+ end # until
399
+ reachedBoundary # sets @dirOfLastMove and @currentStream
400
+ end
401
+ end
397
402
 
398
- # Same as at_end? the other way round.
399
- def at_beginning?
400
- # same algorithm as at_end? the other way round. Could we do it
401
- # with metaprogramming?
402
- @currentStream.at_beginning? and
403
- begin
404
- until streamOfStreams.at_beginning?
405
- dir, @dirOfLastMove = @dirOfLastMove, :backward
406
- s = streamOfStreams.basic_backward
407
- next if dir == :forward
408
- s.set_to_end
409
- if s.at_beginning?
410
- next
411
- else
412
- @currentStream = s
413
- return false
414
- end
403
+ # Same as at_end? the other way round.
404
+ def at_beginning?
405
+ # same algorithm as at_end? the other way round.
406
+ if @currentStream.at_beginning?
407
+ return false
408
+ else
409
+ until streamOfStreams.at_beginning?
410
+ dir, @dirOfLastMove = @dirOfLastMove, :backward
411
+ s = streamOfStreams.basic_backward
412
+ next if dir == :forward
413
+ s.set_to_end
414
+ if s.at_beginning?
415
+ next
416
+ else
417
+ @currentStream = s
418
+ return false
419
+ end
420
+ end
421
+ reachedBoundary
415
422
  end
416
- reachedBoundary
417
- end
418
423
  end
419
-
420
- def set_to_begin; super; reachedBoundary end
421
- def set_to_end; super; reachedBoundary end
422
-
423
- # Returns the next element of @currentStream. at_end? ensured that there is one.
424
- def basic_forward; @currentStream.basic_forward end
425
- # Returns the previous element of @currentStream. at_beginning? ensured that
426
- # there is one.
427
- def basic_backward; @currentStream.basic_backward end
428
-
429
- private
430
-
431
- def reachedBoundary
432
- @currentStream = EmptyStream.instance
433
- @dirOfLastMove = :none # not :forward or :backward
434
- true
435
- end
436
- # Uff, this was the hardest stream to implement.
424
+
425
+ def set_to_begin; super; reachedBoundary end
426
+ def set_to_end; super; reachedBoundary end
427
+
428
+ # Returns the next element of @currentStream. at_end? ensured that there is
429
+ # one.
430
+ def basic_forward; @currentStream.basic_forward end
431
+ # Returns the previous element of @currentStream. at_beginning? ensured that
432
+ # there is one.
433
+ def basic_backward; @currentStream.basic_backward end
434
+
435
+ private
436
+
437
+ def reachedBoundary
438
+ @currentStream = EmptyStream.instance
439
+ @dirOfLastMove = :none # not :forward or :backward
440
+ true
441
+ end
442
+ # Uff, this was the hardest stream to implement.
437
443
  end # ConcatenatedStream
438
444
 
439
- # An ImplicitStream is an easy way to create a stream on the fly without defining a
440
- # subclass of BasicStream. The basic methods required for a stream are defined with
441
- # blocks:
445
+ # An ImplicitStream is an easy way to create a stream on the fly without
446
+ # defining a subclass of BasicStream. The basic methods required for a stream
447
+ # are defined with blocks:
442
448
  #
443
449
  # s = Stream::ImplicitStream.new { |s|
444
450
  # x = 0
445
- # s.at_end_proc = proc {x == 5}
446
- # s.forward_proc = proc {x += 1 }
451
+ # s.at_end_proc = proc { x == 5 }
452
+ # s.forward_proc = proc { x += 1 }
447
453
  # }
448
454
  #
449
455
  # s.to_a ==> [1, 2, 3, 4, 5]
@@ -457,51 +463,52 @@ module Stream
457
463
  # remove the first or last element of an existing stream (see remove_first
458
464
  # and remove_last).
459
465
  class ImplicitStream < BasicStream
460
- attr_writer :at_beginning_proc, :at_end_proc, :forward_proc, :backward_proc, :set_to_begin_proc, :set_to_end_proc
461
- attr_reader :wrapped_stream
462
-
463
- # Create a new ImplicitStream which might wrap an existing stream
464
- # _otherStream_. If _otherStream_ is supplied the blocks for the basic stream
465
- # methods are initialized with closures that delegate all operations to the
466
- # wrapped stream.
467
- #
468
- # If a block is given to new, than it is called with the new ImplicitStream
469
- # stream as parameter letting the client overwriting the default blocks.
470
- def initialize (otherStream=nil)
471
- if otherStream
472
- @wrapped_stream = otherStream
473
- @at_beginning_proc = proc {otherStream.at_beginning?}
474
- @at_end_proc = proc {otherStream.at_end?}
475
- @forward_proc = proc {otherStream.basic_forward}
476
- @backward_proc = proc {otherStream.basic_backward}
477
- @set_to_end_proc = proc {otherStream.set_to_end}
478
- @set_to_begin_proc = proc {otherStream.set_to_begin}
466
+ attr_writer :at_beginning_proc, :at_end_proc, :forward_proc,
467
+ :backward_proc, :set_to_begin_proc, :set_to_end_proc
468
+ attr_reader :wrapped_stream
469
+
470
+ # Create a new ImplicitStream which might wrap an existing stream
471
+ # _otherStream_. If _otherStream_ is supplied the blocks for the basic
472
+ # stream methods are initialized with closures that delegate all operations
473
+ # to the wrapped stream.
474
+ #
475
+ # If a block is given to new, than it is called with the new ImplicitStream
476
+ # stream as parameter letting the client overwriting the default blocks.
477
+ def initialize(otherStream=nil)
478
+ if otherStream
479
+ @wrapped_stream = otherStream
480
+ @at_beginning_proc = proc {otherStream.at_beginning?}
481
+ @at_end_proc = proc {otherStream.at_end?}
482
+ @forward_proc = proc {otherStream.basic_forward}
483
+ @backward_proc = proc {otherStream.basic_backward}
484
+ @set_to_end_proc = proc {otherStream.set_to_end}
485
+ @set_to_begin_proc = proc {otherStream.set_to_begin}
486
+ end
487
+ yield self if block_given? # let client overwrite defaults
488
+
489
+ @at_beginning_proc = proc {true} unless @at_beginning_proc
490
+ @at_end_proc = proc {true} unless @at_end_proc
491
+ end
492
+
493
+ # Returns the value of @at_beginning_proc.
494
+ def at_beginning?; @at_beginning_proc.call; end
495
+ # Returns the value of @at_end_proc.
496
+ def at_end?; @at_end_proc.call; end
497
+
498
+ # Returns the value of @forward_proc.
499
+ def basic_forward; @forward_proc.call; end
500
+ # Returns the value of @backward_proc_proc.
501
+ def basic_backward; @backward_proc.call; end
502
+
503
+ # Calls set_to_end_proc or super if set_to_end_proc is undefined.
504
+ def set_to_end
505
+ @set_to_end_proc ? @set_to_end_proc.call : super
506
+ end
507
+
508
+ # Calls set_to_begin_proc or super if set_to_begin_proc is undefined.
509
+ def set_to_begin
510
+ @set_to_begin_proc ? @set_to_begin_proc.call : super
479
511
  end
480
- yield self if block_given? # let client overwrite defaults
481
-
482
- @at_beginning_proc = proc {true} unless @at_beginning_proc
483
- @at_end_proc = proc {true} unless @at_end_proc
484
- end
485
-
486
- # Returns the value of @at_beginning_proc.
487
- def at_beginning?; @at_beginning_proc.call; end
488
- # Returns the value of @at_end_proc.
489
- def at_end?; @at_end_proc.call; end
490
-
491
- # Returns the value of @forward_proc.
492
- def basic_forward; @forward_proc.call; end
493
- # Returns the value of @backward_proc_proc.
494
- def basic_backward; @backward_proc.call; end
495
-
496
- # Calls set_to_end_proc or super if set_to_end_proc is undefined.
497
- def set_to_end
498
- @set_to_end_proc ? @set_to_end_proc.call : super
499
- end
500
-
501
- # Calls set_to_begin_proc or super if set_to_begin_proc is undefined.
502
- def set_to_begin
503
- @set_to_begin_proc ? @set_to_begin_proc.call : super
504
- end
505
512
  end # ImplicitStream
506
513
 
507
514
  # Stream creation functions
@@ -509,59 +516,64 @@ module Stream
509
516
  ##
510
517
  # Return a Stream::FilteredStream which iterates over all my elements
511
518
  # satisfying the condition specified by the block.
512
- def filtered (&block); FilteredStream.new(self,&block); end
519
+ def filtered(&block); FilteredStream.new(self,&block); end
513
520
 
514
521
  # Create a Stream::ReversedStream wrapper on self.
515
522
  def reverse; ReversedStream.new self; end
516
-
517
- # Create a Stream::MappedStream wrapper on self. Instead of returning the stream
518
- # element on each move, the value of calling _mapping_ is returned instead. See
519
- # Stream::MappedStream for examples.
520
- def collect (&mapping); MappedStream.new(self, &mapping); end
521
523
 
522
- # Create a Stream::ConcatenatedStream on self, which must be a stream of streams.
524
+ # Create a Stream::MappedStream wrapper on self. Instead of returning the
525
+ # stream element on each move, the value of calling _mapping_ is returned
526
+ # instead. See Stream::MappedStream for examples.
527
+ def collect(&mapping); MappedStream.new(self, &mapping); end
528
+
529
+ # Create a Stream::ConcatenatedStream on self, which must be a stream of
530
+ # streams.
523
531
  def concatenate; ConcatenatedStream.new self; end
524
532
 
525
- # Create a Stream::ConcatenatedStream, concatenated from streams build with the
526
- # block for each element of self:
527
- #
533
+ # Create a Stream::ConcatenatedStream, concatenated from streams build with
534
+ # the block for each element of self:
535
+ #
528
536
  # s = [1, 2, 3].create_stream.concatenate_collected { |i|
529
537
  # [i,-i].create_stream
530
538
  # }.
531
539
  # s.to_a ==> [1, -1, 2, -2, 3, -3]
532
- def concatenate_collected (&mapping); self.collect(&mapping).concatenate; end
540
+ def concatenate_collected(&mapping); self.collect(&mapping).concatenate; end
533
541
 
534
- # Create a Stream::ConcatenatedStream by concatenatating the receiver and _otherStream_
542
+ # Create a Stream::ConcatenatedStream by concatenatating the receiver and
543
+ # _otherStream_
535
544
  #
536
- # (%w(a b c).create_stream + [4,5].create_stream).to_a ==> ["a", "b", "c", 4, 5]
537
- def + (otherStream)
538
- [self, otherStream].create_stream.concatenate
545
+ # (%w(a b c).create_stream + [4,5].create_stream).to_a
546
+ # ==> ["a", "b", "c", 4, 5]
547
+ def +(otherStream)
548
+ [self, otherStream].create_stream.concatenate
539
549
  end
540
550
 
541
- # Create a Stream::ImplicitStream which wraps the receiver stream by modifying one
542
- # or more basic methods of the receiver. As an example the method remove_first uses
543
- # #modify to create an ImplicitStream which filters the first element away.
544
- def modify (&block); ImplicitStream.new(self, &block); end
551
+ # Create a Stream::ImplicitStream which wraps the receiver stream by modifying
552
+ # one or more basic methods of the receiver. As an example the method
553
+ # remove_first uses #modify to create an ImplicitStream which filters the
554
+ # first element away.
555
+ def modify(&block); ImplicitStream.new(self, &block); end
545
556
 
546
557
  # Returns a Stream::ImplicitStream wrapping a Stream::FilteredStream, which
547
558
  # eliminates the first element of the receiver.
548
559
  #
549
560
  # (1..3).create_stream.remove_first.to_a ==> [2,3]
550
561
  def remove_first
551
- i = 0
552
- filter = self.filtered { | element | i += 1; i > 1 }
553
- filter.modify { |s|
554
- s.set_to_begin_proc = proc {filter.set_to_begin; i = 0}
555
- }
562
+ i = 0
563
+ filter = self.filtered { i += 1; i > 1 }
564
+ filter.modify { |s|
565
+ s.set_to_begin_proc = proc {filter.set_to_begin; i = 0}
566
+ }
556
567
  end
557
568
 
558
569
  # Returns a Stream which eliminates the first element of the receiver.
559
570
  #
560
571
  # (1..3).create_stream.remove_last.to_a ==> [1,2]
561
572
  #
562
- # <em>Take a look at the source. The implementation is inefficient but elegant.</em>
573
+ # <em>Take a look at the source. The implementation is inefficient but
574
+ # elegant.</em>
563
575
  def remove_last
564
- self.reverse.remove_first.reverse # I like this one
576
+ self.reverse.remove_first.reverse # I like this one
565
577
  end
566
578
  end
567
579
 
@@ -0,0 +1,5 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
metadata CHANGED
@@ -1,64 +1,116 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: stream
3
- version: !ruby/object:Gem::Version
4
- version: "0.5"
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.2
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Horst Duchene
8
8
  autorequire: stream
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2009-11-24 00:00:00 -07:00
13
- default_executable:
14
- dependencies: []
15
-
16
- description: " Module Stream defines an interface for external iterators.\n"
17
- email: hd.at.clr@hduchene.de
11
+ date: 2019-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Module Stream defines an interface for external iterators.
70
+ email: monora@gmail.com
18
71
  executables: []
19
-
20
72
  extensions: []
21
-
22
- extra_rdoc_files: []
23
-
24
- files:
25
- - install.rb
73
+ extra_rdoc_files:
74
+ - README.rdoc
75
+ files:
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - README.rdoc
26
79
  - Rakefile
27
- - README
80
+ - examples/examples.rb
81
+ - examples/streamtester.rb
28
82
  - lib/generator2stream.rb
29
83
  - lib/stream.rb
30
84
  - test/bm.rb
85
+ - test/test_helper.rb
31
86
  - test/testgenerator.rb
32
87
  - test/teststream.rb
33
- - examples/examples.rb
34
- - examples/streamtester.rb
35
- has_rdoc: true
36
- homepage: http://rgl.rubyforge.org
88
+ homepage: https://github.com/monora/stream
37
89
  licenses: []
38
-
90
+ metadata: {}
39
91
  post_install_message:
40
- rdoc_options: []
41
-
42
- require_paths:
92
+ rdoc_options:
93
+ - "--title"
94
+ - stream - Extended External Iterators
95
+ - "--main"
96
+ - README.rdoc
97
+ - "--line-numbers"
98
+ require_paths:
43
99
  - lib
44
- required_ruby_version: !ruby/object:Gem::Requirement
45
- requirements:
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
46
102
  - - ">="
47
- - !ruby/object:Gem::Version
48
- version: "0"
49
- version:
50
- required_rubygems_version: !ruby/object:Gem::Requirement
51
- requirements:
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
52
107
  - - ">="
53
- - !ruby/object:Gem::Version
54
- version: "0"
55
- version:
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
56
110
  requirements: []
57
-
58
- rubyforge_project: rgl
59
- rubygems_version: 1.3.5
111
+ rubyforge_project:
112
+ rubygems_version: 2.7.7
60
113
  signing_key:
61
- specification_version: 3
62
- summary: Stream - Extended External Iterators
114
+ specification_version: 4
115
+ summary: stream - Extended External Iterators
63
116
  test_files: []
64
-
data/install.rb DELETED
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'rbconfig'
4
- require 'ftools'
5
- require 'find'
6
-
7
- SRC_BASE = 'lib'
8
- INSTDIR = File.join Config::CONFIG['sitedir']
9
-
10
- def install
11
- begin
12
- pwd = Dir.pwd
13
- Dir.chdir(SRC_BASE)
14
- Dir['*.rb'].each do |file|
15
- dst = File.join( INSTDIR, file )
16
- File.install(file, dst, 0644, true)
17
- end
18
- Dir.chdir(pwd)
19
- rescue
20
- puts $!
21
- end
22
- end
23
-
24
- install