array_scanner 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +78 -0
- data/Rakefile +6 -0
- data/array_scanner.gemspec +24 -0
- data/lib/array_scanner/version.rb +3 -0
- data/lib/array_scanner.rb +224 -0
- data/lib/hist.rb +21 -0
- data/spec/lib/array_scanner_spec.rb +481 -0
- data/spec/lib/hist_spec.rb +55 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZWQ4MTcwNjcxNTljZWZjYWFlOGU0OTQxODg0ZmIxMDA3NzhhYTcwYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZjY5ODhkMzQzYTBhNzg5NTUwYjQ2MGNkY2IyODg3YWU1Yjc4ZWUwMQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZTc0ZjRlNTYwNDIzMzJjYjZlOTI4MmJmMWE3ODliNmIzN2EwYzMzN2ZmNDE4
|
10
|
+
MTk3YjU4YjgyZDFhYzkwYjU4MmQ0ZGIyZjgwYzA4ZmZmMmFlMGZlMGE0ZmRi
|
11
|
+
ZDIwYTBlMDU2ZTg2ZGRmMzQwNGMyYjFkZjFlMTlmODlmZmE2YjE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZDlhMGY0YTg3ZDNhZjQ3Mjc2NzNjMTM3MmJjYzU2YTQ4NTdkMTIyOTYwOGNk
|
14
|
+
ODY1ZmY4MWViZTM3ZWQ0ZTMzYjkzYjEwMGUxM2U2MDRjZjE0NWJhZjcxZGM3
|
15
|
+
OWMzMTY1NWJjNTNjNDVlZmMxYWE4MTFkZGVjYWUzMWJlNjA0Yzk=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 LFDM
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# ArrayScanner
|
2
|
+
|
3
|
+
Class for traversing an array, remembering the position of a pointer and
|
4
|
+
recent scan operations, very much like Ruby's standard library
|
5
|
+
StringScanner.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'array_scanner'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install array_scanner
|
20
|
+
|
21
|
+
## Example Usage
|
22
|
+
|
23
|
+
ArrayScanner.new([:a, :b, :c, :d, :e, :f, :g])
|
24
|
+
|
25
|
+
inspect => "Pointer at 0/6. Current element: :a"
|
26
|
+
|
27
|
+
forward(2) => 2
|
28
|
+
o-------2-------> *
|
29
|
+
|
30
|
+
current_element => :c
|
31
|
+
*
|
32
|
+
|
33
|
+
scan { |el| el == :c } => :c
|
34
|
+
o---1--> *
|
35
|
+
|
36
|
+
position => 3
|
37
|
+
*
|
38
|
+
|
39
|
+
rest => [:d, :e, :f, :g]
|
40
|
+
*
|
41
|
+
|
42
|
+
rest_size => 4
|
43
|
+
*
|
44
|
+
|
45
|
+
scan_until { |el| el == :f } => [:d, :e]
|
46
|
+
o-------2-------> *
|
47
|
+
|
48
|
+
unscan => 3
|
49
|
+
* <-------2-------o
|
50
|
+
|
51
|
+
scan_until(true) { |el| el == :f } => [:d, :e, :f]
|
52
|
+
o------------3-----------> *
|
53
|
+
|
54
|
+
eoa? => true
|
55
|
+
*
|
56
|
+
|
57
|
+
rewind_to { |el| el == :b } => :b
|
58
|
+
* <--------------------5--------------------- *
|
59
|
+
|
60
|
+
surroundings => [:a, :c]
|
61
|
+
*
|
62
|
+
|
63
|
+
last_positions => [6, 3, 5, 3, 2, 0]
|
64
|
+
*
|
65
|
+
|
66
|
+
last_results => [[:d, :e, :f], [:d, :e], :c]
|
67
|
+
*
|
68
|
+
|
69
|
+
Check tests for more behaviour.
|
70
|
+
|
71
|
+
|
72
|
+
## Contributing
|
73
|
+
|
74
|
+
1. Fork it
|
75
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
76
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
77
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
78
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'array_scanner/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "array_scanner"
|
8
|
+
spec.version = ArrayScanner::VERSION
|
9
|
+
spec.authors = ["LFDM"]
|
10
|
+
spec.email = ["1986gh@gmail.com"]
|
11
|
+
spec.description = %q{ArrayScanner that mimics ruby's std lib StringScanner}
|
12
|
+
spec.summary = %q{Class for traversing an array, remembering the position of a pointer and recent scan operations.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require "array_scanner/version"
|
2
|
+
require_relative "hist"
|
3
|
+
|
4
|
+
class ArrayScanner
|
5
|
+
attr_reader :position, :pos_hist, :res_hist
|
6
|
+
alias :pos :position
|
7
|
+
alias :pointer :position
|
8
|
+
|
9
|
+
def initialize(arr, history_size = 10)
|
10
|
+
raise TypeError.new("Argument is not an Array.") unless arr.is_a?(Array)
|
11
|
+
raise TypeError.new("History size is not a Fixnum.") unless history_size.is_a?(Fixnum)
|
12
|
+
|
13
|
+
@arr = arr
|
14
|
+
@position = 0
|
15
|
+
|
16
|
+
@pos_hist = Hist.new(history_size)
|
17
|
+
@res_hist = Hist.new(history_size)
|
18
|
+
end
|
19
|
+
|
20
|
+
def size
|
21
|
+
@arr.size
|
22
|
+
end
|
23
|
+
|
24
|
+
alias :length :size
|
25
|
+
|
26
|
+
def eoa
|
27
|
+
size - 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def eoa?
|
31
|
+
@position == eoa
|
32
|
+
end
|
33
|
+
|
34
|
+
def current_element
|
35
|
+
@arr[@position]
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :points_at :current_element
|
39
|
+
alias :current :current_element
|
40
|
+
|
41
|
+
def position=(new)
|
42
|
+
raise TypeError.new("New position not a Fixnum.") unless new.is_a? Fixnum
|
43
|
+
raise ArgumentError.new("New position #{new} outside of range 0..#{eoa}.") unless new.between?(0, eoa)
|
44
|
+
|
45
|
+
@pos_hist.push(@position)
|
46
|
+
@position = new
|
47
|
+
end
|
48
|
+
|
49
|
+
alias :pos= :position=
|
50
|
+
alias :pointer= :position=
|
51
|
+
|
52
|
+
def surroundings
|
53
|
+
[scanned.last, rest[1]]
|
54
|
+
end
|
55
|
+
|
56
|
+
def last_position
|
57
|
+
@pos_hist.recent
|
58
|
+
end
|
59
|
+
|
60
|
+
def last_positions(n = nil)
|
61
|
+
n ? @pos_hist.recent(n) : @pos_hist.stack
|
62
|
+
end
|
63
|
+
|
64
|
+
def unscan(steps = 1)
|
65
|
+
return nil if last_positions.empty?
|
66
|
+
self.position = last_positions[steps - 1] || last_positions.last
|
67
|
+
end
|
68
|
+
|
69
|
+
def last_result(valid = false)
|
70
|
+
valid ? @res_hist.stack.find { |x| x } : @res_hist.recent
|
71
|
+
end
|
72
|
+
|
73
|
+
def last_results(n = nil)
|
74
|
+
n ? @res_hist.recent(n) : @res_hist.stack
|
75
|
+
end
|
76
|
+
|
77
|
+
def forward(fixnum)
|
78
|
+
new = @position + fixnum
|
79
|
+
self.position = (new > eoa ? eoa : new)
|
80
|
+
end
|
81
|
+
|
82
|
+
def forward_to
|
83
|
+
if block_given?
|
84
|
+
f = rest.find { |x| yield(x) }
|
85
|
+
f ? self.position = @arr.index(f) : nil
|
86
|
+
else
|
87
|
+
raise needs_block
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def rewind(fixnum)
|
92
|
+
new = @position - fixnum
|
93
|
+
self.position = (new < 0 ? 0 : new)
|
94
|
+
end
|
95
|
+
|
96
|
+
def rewind_to
|
97
|
+
if block_given?
|
98
|
+
b = scanned.find { |x| yield(x) }
|
99
|
+
b ? self.position = @arr.index(b) : nil
|
100
|
+
else
|
101
|
+
raise needs_block
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def reset
|
106
|
+
self.position = 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def terminate
|
110
|
+
self.position = eoa
|
111
|
+
end
|
112
|
+
|
113
|
+
alias :clear :terminate
|
114
|
+
|
115
|
+
def scanned
|
116
|
+
@arr[0...@position]
|
117
|
+
end
|
118
|
+
|
119
|
+
def scanned_size
|
120
|
+
@position
|
121
|
+
end
|
122
|
+
|
123
|
+
def rest
|
124
|
+
@arr[@position..-1]
|
125
|
+
end
|
126
|
+
|
127
|
+
def rest_size
|
128
|
+
size - @position
|
129
|
+
end
|
130
|
+
|
131
|
+
def scan(forward = true)
|
132
|
+
res = @arr[@position]
|
133
|
+
|
134
|
+
if block_given? and not yield(res)
|
135
|
+
rr(false)
|
136
|
+
else
|
137
|
+
forward(1) if forward &! eoa?
|
138
|
+
rr(res)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def scan_until(include_true_element = false)
|
143
|
+
if block_given?
|
144
|
+
if e = rest.find { |el| yield(el) }
|
145
|
+
i = @arr.index(e)
|
146
|
+
i += 1 if include_true_element
|
147
|
+
|
148
|
+
self.position = (i > eoa ? eoa : i)
|
149
|
+
rr(@arr[last_position...i])
|
150
|
+
else
|
151
|
+
rr(false)
|
152
|
+
end
|
153
|
+
else
|
154
|
+
raise needs_block
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def peek(n = nil)
|
159
|
+
n ? rest.take(n) : rest.first
|
160
|
+
end
|
161
|
+
|
162
|
+
def peek_until
|
163
|
+
if block_given?
|
164
|
+
i = rest.index { |el| yield(el) }
|
165
|
+
i ? rest[0...i] : []
|
166
|
+
else
|
167
|
+
raise needs_block
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def look_behind(n = nil)
|
172
|
+
n ? scanned.reverse.take(n) : scanned.last
|
173
|
+
end
|
174
|
+
|
175
|
+
def look_behind_until
|
176
|
+
if block_given?
|
177
|
+
rev = scanned.reverse
|
178
|
+
i = rev.index { |el| yield(el) }
|
179
|
+
i ? rev[0...i] : []
|
180
|
+
else
|
181
|
+
raise needs_block
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def find
|
186
|
+
if block_given?
|
187
|
+
rest.find { |e| yield e }
|
188
|
+
else
|
189
|
+
raise needs_block
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
alias :next :find
|
194
|
+
|
195
|
+
def previous
|
196
|
+
if block_given?
|
197
|
+
scanned.reverse.find { |e| yield e }
|
198
|
+
else
|
199
|
+
raise needs_block
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def rr(obj)
|
204
|
+
# means return result
|
205
|
+
@res_hist.push(obj)
|
206
|
+
obj
|
207
|
+
end
|
208
|
+
|
209
|
+
def needs_block
|
210
|
+
ArgumentError.new("Method needs block.")
|
211
|
+
end
|
212
|
+
|
213
|
+
def to_a
|
214
|
+
@arr
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_s
|
218
|
+
@arr.to_s
|
219
|
+
end
|
220
|
+
|
221
|
+
def inspect
|
222
|
+
"Pointer at #{@position}/#{eoa}. Current element: #{":" if current.is_a?(Symbol)}#{current}"
|
223
|
+
end
|
224
|
+
end
|
data/lib/hist.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hist
|
2
|
+
attr_reader :stack, :max_size
|
3
|
+
|
4
|
+
def initialize(max_size = 10)
|
5
|
+
@stack = []
|
6
|
+
@max_size = max_size
|
7
|
+
end
|
8
|
+
|
9
|
+
def push(obj)
|
10
|
+
@stack.pop if @stack.size == @max_size
|
11
|
+
@stack.unshift(obj)
|
12
|
+
end
|
13
|
+
|
14
|
+
def recent(n = nil)
|
15
|
+
n ? @stack.take(n) : @stack.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](i)
|
19
|
+
@stack[i]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,481 @@
|
|
1
|
+
require_relative "../../lib/array_scanner"
|
2
|
+
|
3
|
+
describe ArrayScanner do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "should raise type errors for wrong arguments" do
|
6
|
+
expect { ArrayScanner.new({}) } .to raise_error(TypeError)
|
7
|
+
expect { ArrayScanner.new([], nil) }.to raise_error(TypeError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
@a = ArrayScanner.new([1,2,3,4,5])
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#size" do
|
16
|
+
it "should return the arrays size" do
|
17
|
+
@a.size.should be 5
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#length" do
|
22
|
+
it "is an alias for #size" do
|
23
|
+
@a.length.should == @a.size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#eoa" do
|
28
|
+
it "should return last valid position, i.d. end of array" do
|
29
|
+
@a.eoa.should be 4
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#eoa?" do
|
34
|
+
it "should return false if not at end of array" do
|
35
|
+
@a.eoa?.should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return true if at end of array" do
|
39
|
+
@a.position = 4
|
40
|
+
@a.eoa?.should be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#current_element" do
|
45
|
+
it "should return element at the pointers position" do
|
46
|
+
@a.position = 2
|
47
|
+
@a.current_element.should be 3
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#current" do
|
52
|
+
it "is an alias for #current_element" do
|
53
|
+
@a.position = 2
|
54
|
+
@a.current.should be 3
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#points_at" do
|
59
|
+
it "is an alias for #current_element" do
|
60
|
+
@a.position = 2
|
61
|
+
@a.points_at.should be 3
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#position" do
|
66
|
+
it "should return current position" do
|
67
|
+
@a.position.should be 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#pos" do
|
72
|
+
it "is an alias for #position" do
|
73
|
+
@a.pos.should be 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#pointer" do
|
78
|
+
it "is an alias for #position" do
|
79
|
+
@a.pointer.should be 0
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#position=" do
|
84
|
+
it "should set new position" do
|
85
|
+
@a.position = 4
|
86
|
+
@a.position.should be 4
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise error when new position is invalid type" do
|
90
|
+
expect { @a.position = "" }.to raise_error(TypeError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise error when new position is outside of arrays range" do
|
94
|
+
expect { @a.position = 6 }.to raise_error(ArgumentError)
|
95
|
+
expect { @a.position = 5 }.to raise_error(ArgumentError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should add to positions history stack" do
|
99
|
+
[1,2,3].each { |n| @a.position = n }
|
100
|
+
@a.pos_hist.stack.should == [2,1,0]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#pos=" do
|
105
|
+
it "is an alias for #position=" do
|
106
|
+
@a.pos = 4
|
107
|
+
@a.position.should be 4
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#pointer=" do
|
112
|
+
it "is an alias for #position=" do
|
113
|
+
@a.pointer = 4
|
114
|
+
@a.position.should be 4
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#surroundings" do
|
119
|
+
it "should return element in front and element behind pointer" do
|
120
|
+
@a.pos = 2
|
121
|
+
@a.surroundings.should == [2,4]
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return nil elements for edge positions" do
|
125
|
+
@a.surroundings.should == [nil, 2]
|
126
|
+
@a.terminate
|
127
|
+
@a.surroundings.should == [4, nil]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#last_position" do
|
132
|
+
it "should return last position after recent movement" do
|
133
|
+
@a.position = 1 and @a.position = 2
|
134
|
+
@a.last_position.should be 1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#last_positions" do
|
139
|
+
it "should return array of all pointer movements when called without arg" do
|
140
|
+
@a.position = 3 and @a.position = 1 and @a.position = 4
|
141
|
+
@a.last_positions.should == [1,3,0]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should return array of all pointer movements according to argument number" do
|
145
|
+
@a.position = 3 and @a.position = 1 and @a.forward(1)
|
146
|
+
@a.last_positions(2).should == [1,3]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "#unscan" do
|
151
|
+
before :each do
|
152
|
+
[1,2,3,4].each { |n| @a.pos_hist.push(n) }
|
153
|
+
end
|
154
|
+
|
155
|
+
context "without argument" do
|
156
|
+
it "should reset pointer to last position and return new value" do
|
157
|
+
@a.unscan.should be 4
|
158
|
+
@a.position.should be 4
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "with argument" do
|
163
|
+
it "should reset pointer by n steps and return new value" do
|
164
|
+
@a.unscan(3).should be 2
|
165
|
+
@a.position.should be 2
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should return nil when position history is empty" do
|
170
|
+
@a.pos_hist.stack.clear
|
171
|
+
@a.unscan.should be_nil
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#last_result" do
|
177
|
+
it "should return last result" do
|
178
|
+
@a.scan
|
179
|
+
@a.last_result.should == 1
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should return last valid result with true argument" do
|
183
|
+
@a.scan and @a.scan { |x| x == 100 }
|
184
|
+
@a.last_result.should be_false
|
185
|
+
@a.last_result(true).should == 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "#forward" do
|
190
|
+
it "should forward pointer by argument" do
|
191
|
+
@a.forward(2)
|
192
|
+
@a.position.should be 2
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should foward pointer to eoa if new position is outside of range" do
|
196
|
+
@a.forward(10)
|
197
|
+
@a.position.should be 4
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe "#forward_to" do
|
202
|
+
it "should forward pointer and return new position if block is true" do
|
203
|
+
@a.forward_to { |el| el == 3 }.should be 2
|
204
|
+
@a.position.should be 2
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should not forward pointer and return nil if block is false" do
|
208
|
+
@a.forward_to { |el| el == 7 }.should be_nil
|
209
|
+
@a.position.should be 0
|
210
|
+
end
|
211
|
+
|
212
|
+
it "raises ArgumentError without block" do
|
213
|
+
expect { @a.look_behind_until }.to raise_error(ArgumentError)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "#rewind" do
|
218
|
+
it "should rewind pointer by argument" do
|
219
|
+
@a.position = 3
|
220
|
+
@a.rewind(2)
|
221
|
+
@a.position.should be 1
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should rewind to zero if new position is outside of range" do
|
225
|
+
@a.position = 2
|
226
|
+
@a.rewind(4)
|
227
|
+
@a.position.should be 0
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "#rewind_to" do
|
232
|
+
it "should rewind pointer to new position if block is true" do
|
233
|
+
@a.position = 4
|
234
|
+
@a.rewind_to { |el| el == 2 }.should be 1
|
235
|
+
@a.position.should be 1
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should not rewind pointer and return nil if block is false" do
|
239
|
+
@a.position = 4
|
240
|
+
@a.rewind_to { |el| el == 5 }.should be_nil
|
241
|
+
@a.position.should be 4
|
242
|
+
end
|
243
|
+
|
244
|
+
it "raises ArgumentError without block" do
|
245
|
+
expect { @a.look_behind_until }.to raise_error(ArgumentError)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#reset" do
|
250
|
+
it "should reset pointer to 0" do
|
251
|
+
@a.position = 4
|
252
|
+
@a.reset
|
253
|
+
@a.position.should be 0
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#terminate" do
|
258
|
+
it "should set pointer to eoa position" do
|
259
|
+
@a.terminate
|
260
|
+
@a.position.should be 4
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "#clear" do
|
265
|
+
it "is an alias for #terminate" do
|
266
|
+
@a.clear.should be @a.terminate
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe "#scanned" do
|
271
|
+
it "should return already scanned elements" do
|
272
|
+
@a.position = 3
|
273
|
+
@a.scanned.should == [1,2,3]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe "#scanned_size" do
|
278
|
+
it "should return number of scanned elements" do
|
279
|
+
@a.position = 3
|
280
|
+
@a.scanned_size.should be 3
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe "#rest" do
|
285
|
+
it "should return remaining elements" do
|
286
|
+
@a.position = 1
|
287
|
+
@a.rest.should == [2,3,4,5]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe "#rest_size" do
|
292
|
+
it "should return number of remaining elements" do
|
293
|
+
@a.position = 1
|
294
|
+
@a.rest_size.should be 4
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "#scan" do
|
299
|
+
context "without blk" do
|
300
|
+
it "should return array element at current position and advance pointer" do
|
301
|
+
@a.position = 2
|
302
|
+
@a.scan.should be 3
|
303
|
+
@a.position.should be 3
|
304
|
+
end
|
305
|
+
|
306
|
+
it "should not advance pointer with false argument" do
|
307
|
+
@a.scan(false)
|
308
|
+
@a.position.should be 0
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should not advance pointer if at eoa" do
|
312
|
+
@a.position = 4
|
313
|
+
@a.scan
|
314
|
+
@a.position.should be 4
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
context "with blk" do
|
319
|
+
it "should return false for false block" do
|
320
|
+
@a.scan { |el| el == 6 }.should be_false
|
321
|
+
end
|
322
|
+
|
323
|
+
it "should return array element at current position for true block and advance position by 1" do
|
324
|
+
@a.position = 2
|
325
|
+
@a.scan { |el| el == 3 }.should be 3
|
326
|
+
@a.position.should be 3
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should not advance pointer with false argument" do
|
330
|
+
@a.scan(false) { |el| el == 3 }.should be false
|
331
|
+
@a.position.should be 0
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe "#scan_until" do
|
337
|
+
it "should return array from current position to position before the block evaluated to true and move pointer" do
|
338
|
+
@a.scan_until { |el| el == 3 }.should == [1,2]
|
339
|
+
@a.position.should be 2
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should return false when block is false and not move pointer." do
|
343
|
+
@a.scan_until { |el| el == 8 }.should be_false
|
344
|
+
@a.position.should be 0
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should include the element for which the block evaluated true when true argument is given." do
|
348
|
+
@a.scan_until(true) { |el| el == 3 }.should == [1,2,3]
|
349
|
+
@a.position.should be 3
|
350
|
+
end
|
351
|
+
|
352
|
+
it "should rest pointer at eoa when true argument is given and the truthy result is at eoa." do
|
353
|
+
@a.scan_until(true) { |el| el == 5 }.should == [1,2,3,4,5]
|
354
|
+
@a.position.should == @a.eoa
|
355
|
+
end
|
356
|
+
|
357
|
+
it "raises ArgumentError without block" do
|
358
|
+
expect { @a.look_behind_until }.to raise_error(ArgumentError)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "#peek" do
|
363
|
+
it "should return next element without argument" do
|
364
|
+
@a.position = 1
|
365
|
+
@a.peek.should == 2
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should array of next elements by argument number" do
|
369
|
+
@a.peek(1).should == [1]
|
370
|
+
@a.peek(2).should == [1,2]
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
describe "#peek_until" do
|
375
|
+
it "should return array of next elements between current position and the element for which the block is true" do
|
376
|
+
@a.position = 1
|
377
|
+
@a.peek_until { |el| el == 5 }.should == [2,3,4]
|
378
|
+
end
|
379
|
+
|
380
|
+
it "should return empty array if block is false" do
|
381
|
+
@a.peek_until { |el| el == 6 }.should == []
|
382
|
+
end
|
383
|
+
|
384
|
+
it "raises ArgumentError without block" do
|
385
|
+
expect { @a.look_behind_until }.to raise_error(ArgumentError)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
describe "#look_behind" do
|
390
|
+
it "should return element behind pointer (i.e. pointer position - 1), when no arguemnt is given" do
|
391
|
+
@a.pos = 2
|
392
|
+
@a.look_behind.should be 2
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should array of previous elements by argument number (reverse order)" do
|
396
|
+
@a.pos = 4
|
397
|
+
@a.look_behind(1).should == [4]
|
398
|
+
@a.look_behind(2).should == [4,3]
|
399
|
+
end
|
400
|
+
|
401
|
+
it "should return nil when there is nothing behind without argument" do
|
402
|
+
@a.look_behind.should be_nil
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should return empty array when there is nothing behind with argument" do
|
406
|
+
@a.look_behind(3).should == []
|
407
|
+
end
|
408
|
+
|
409
|
+
it "should return everything behind when argument number is larger than number of elements behind" do
|
410
|
+
@a.pos = 2
|
411
|
+
@a.look_behind(4).should == [2,1]
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe "#look_behind_until" do
|
416
|
+
it "should return arguemnt of previous element between current position and the element for which the block is true - in reverse order" do
|
417
|
+
@a.pos = 4
|
418
|
+
@a.look_behind_until { |el| el == 2 }.should == [4,3]
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should return empty array if block is false" do
|
422
|
+
@a.look_behind_until { |el| el == 1 }.should == []
|
423
|
+
end
|
424
|
+
|
425
|
+
it "raises ArgumentError without block" do
|
426
|
+
expect { @a.look_behind_until }.to raise_error(ArgumentError)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe "#find" do
|
431
|
+
it "should return next element after pointer for true block" do
|
432
|
+
@a.position = 1
|
433
|
+
@a.find { |x| x == 3 }.should be 3
|
434
|
+
end
|
435
|
+
|
436
|
+
it "should return nil for no find - e.g. pointer is already behind the looked up element" do
|
437
|
+
@a.position = 3
|
438
|
+
@a.find { |x| x == 0 }.should be_nil
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
describe "#next" do
|
443
|
+
it "is an alias for #find" do
|
444
|
+
@a.position = 1
|
445
|
+
@a.next { |x| x == 3 }.should be 3
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
describe "#previous" do
|
450
|
+
it "should return previous element in front of pointer for true block" do
|
451
|
+
@a.position = 3
|
452
|
+
@a.previous { |x| x == 2 }.should be 2
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
describe "#rr" do
|
457
|
+
it "should add to results history and return the argument" do
|
458
|
+
@a.rr(1).should be 1
|
459
|
+
@a.res_hist.stack.should == [1]
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe "#to_a" do
|
464
|
+
it "should return simple array" do
|
465
|
+
@a.to_a.should == [1,2,3,4,5]
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe "#to_s" do
|
470
|
+
it "should return simple array to_s" do
|
471
|
+
@a.to_s.should == "[1, 2, 3, 4, 5]"
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
describe "#inspect" do
|
476
|
+
it "should return pointer information" do
|
477
|
+
@a.pos = 2
|
478
|
+
@a.inspect.should == "Pointer at 2/4. Current element: 3"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative "../../lib/hist"
|
2
|
+
|
3
|
+
describe Hist do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "should come with default stack size 10" do
|
6
|
+
Hist.new.max_size.should be 10
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should take max sizes as argument" do
|
10
|
+
Hist.new(11).max_size.should be 11
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#[]" do
|
15
|
+
it "delegates [] to stack" do
|
16
|
+
a = Hist.new
|
17
|
+
a.push(1) and a.push(2)
|
18
|
+
a[1].should be 1
|
19
|
+
a[0].should be 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#push" do
|
24
|
+
before :each do
|
25
|
+
@a = Hist.new(3)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should push new element to stack" do
|
29
|
+
@a.push(1)
|
30
|
+
@a.stack.should == [1]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not take more elements than max size" do
|
34
|
+
@a.instance_variable_set(:@stack, [1,2,3])
|
35
|
+
@a.push(4)
|
36
|
+
@a.stack.should == [4,1,2]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#recent" do
|
41
|
+
before :each do
|
42
|
+
@a = Hist.new(3)
|
43
|
+
@a.instance_variable_set(:@stack, [1,2,3])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return recently added element to stack without argument" do
|
47
|
+
@a.recent.should be 1
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return array of recently added elements according to argument number" do
|
51
|
+
@a.recent(2).should == [1,2]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: array_scanner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- LFDM
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
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: rspec
|
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
|
+
description: ArrayScanner that mimics ruby's std lib StringScanner
|
56
|
+
email:
|
57
|
+
- 1986gh@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- array_scanner.gemspec
|
68
|
+
- lib/array_scanner.rb
|
69
|
+
- lib/array_scanner/version.rb
|
70
|
+
- lib/hist.rb
|
71
|
+
- spec/lib/array_scanner_spec.rb
|
72
|
+
- spec/lib/hist_spec.rb
|
73
|
+
homepage: ''
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.0.3
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Class for traversing an array, remembering the position of a pointer and
|
97
|
+
recent scan operations.
|
98
|
+
test_files:
|
99
|
+
- spec/lib/array_scanner_spec.rb
|
100
|
+
- spec/lib/hist_spec.rb
|