mustermann-strscan 0.4.0
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 +7 -0
- data/README.md +38 -0
- data/lib/mustermann/string_scanner.rb +313 -0
- data/lib/mustermann/strscan.rb +1 -0
- data/mustermann-strscan.gemspec +18 -0
- data/spec/string_scanner_spec.rb +271 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 06f8fd306c3c984eba6afc9ca05e88efe2e03f78
|
4
|
+
data.tar.gz: 9f3b1baf5062f5f6ef3d1ff10607cddb4d48a8c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 930c1721880ad0c61a8110181f5f14be48f6a8d0b35891c3234da4f0a20b7d7c3ebdf464d9899283a064b3b353b96fe2b358c4be765720406c3b470e3d312037
|
7
|
+
data.tar.gz: 31b9fefd04dba3e49f9231653476adb4744c4879327004f390a56404c5c31ccf23152a31afe5017d1064e2af4094fabf8f043487729a6a290cf4624993f3f239
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# String Scanner for Mustermann
|
2
|
+
|
3
|
+
This gem implements `Mustermann::StringScanner`, a tool inspired by Ruby's [`StringScanner`]() class.
|
4
|
+
|
5
|
+
``` ruby
|
6
|
+
require 'mustermann/string_scanner'
|
7
|
+
scanner = Mustermann::StringScanner.new("here is our example string")
|
8
|
+
|
9
|
+
scanner.scan("here") # => "here"
|
10
|
+
scanner.getch # => " "
|
11
|
+
|
12
|
+
if scanner.scan(":verb our")
|
13
|
+
scanner.scan(:noun, capture: :word)
|
14
|
+
scanner[:verb] # => "is"
|
15
|
+
scanner[:nound] # => "example"
|
16
|
+
end
|
17
|
+
|
18
|
+
scanner.rest # => "string"
|
19
|
+
```
|
20
|
+
|
21
|
+
You can pass it pattern objects directly:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
pattern = Mustermann.new(':name')
|
25
|
+
scanner.check(pattern)
|
26
|
+
```
|
27
|
+
|
28
|
+
Or have `#scan` (and other methods) check these for you.
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
scanner.check('{name}', type: :template)
|
32
|
+
```
|
33
|
+
|
34
|
+
You can also pass in default options for ad hoc patterns when creating the scanner:
|
35
|
+
|
36
|
+
``` ruby
|
37
|
+
scanner = Mustermann::StringScanner.new(input, type: :shell)
|
38
|
+
```
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
require 'mustermann/pattern_cache'
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module Mustermann
|
6
|
+
# Class inspired by Ruby's StringScanner to scan an input string using multiple patterns.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require 'mustermann/string_scanner'
|
10
|
+
# scanner = Mustermann::StringScanner.new("here is our example string")
|
11
|
+
#
|
12
|
+
# scanner.scan("here") # => "here"
|
13
|
+
# scanner.getch # => " "
|
14
|
+
#
|
15
|
+
# if scanner.scan(":verb our")
|
16
|
+
# scanner.scan(:noun, capture: :word)
|
17
|
+
# scanner[:verb] # => "is"
|
18
|
+
# scanner[:nound] # => "example"
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# scanner.rest # => "string"
|
22
|
+
#
|
23
|
+
# @note
|
24
|
+
# This structure is not thread-safe, you should not scan on the same StringScanner instance concurrently.
|
25
|
+
# Even if it was thread-safe, scanning concurrently would probably lead to unwanted behaviour.
|
26
|
+
class StringScanner
|
27
|
+
# Exception raised if scan/unscan operation cannot be performed.
|
28
|
+
ScanError = Class.new(::ScanError)
|
29
|
+
PATTERN_CACHE = PatternCache.new
|
30
|
+
private_constant :PATTERN_CACHE
|
31
|
+
|
32
|
+
# Patterns created by {#scan} will be globally cached, since we assume that there is a finite number
|
33
|
+
# of different patterns used and that they are more likely to be reused than not.
|
34
|
+
# This method allows clearing the cache.
|
35
|
+
#
|
36
|
+
# @see Mustermann::PatternCache
|
37
|
+
def self.clear_cache
|
38
|
+
PATTERN_CACHE.clear
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Integer] number of cached patterns
|
42
|
+
# @see clear_cache
|
43
|
+
# @api private
|
44
|
+
def self.cache_size
|
45
|
+
PATTERN_CACHE.size
|
46
|
+
end
|
47
|
+
|
48
|
+
# Encapsulates return values for {StringScanner#scan}, {StringScanner#check}, and friends.
|
49
|
+
# Behaves like a String (the substring which matched the pattern), but also exposes its position
|
50
|
+
# in the main string and any params parsed from it.
|
51
|
+
class ScanResult < DelegateClass(String)
|
52
|
+
# The scanner this result came from.
|
53
|
+
# @example
|
54
|
+
# require 'mustermann/string_scanner'
|
55
|
+
# scanner = Mustermann::StringScanner.new('foo/bar')
|
56
|
+
# scanner.scan(:name).scanner == scanner # => true
|
57
|
+
attr_reader :scanner
|
58
|
+
|
59
|
+
# @example
|
60
|
+
# require 'mustermann/string_scanner'
|
61
|
+
# scanner = Mustermann::StringScanner.new('foo/bar')
|
62
|
+
# scanner.scan(:name).position # => 0
|
63
|
+
# scanner.getch.position # => 3
|
64
|
+
# scanner.scan(:name).position # => 4
|
65
|
+
#
|
66
|
+
# @return [Integer] position the substring starts at
|
67
|
+
attr_reader :position
|
68
|
+
alias_method :pos, :position
|
69
|
+
|
70
|
+
# @example
|
71
|
+
# require 'mustermann/string_scanner'
|
72
|
+
# scanner = Mustermann::StringScanner.new('foo/bar')
|
73
|
+
# scanner.scan(:name).length # => 3
|
74
|
+
# scanner.getch.length # => 1
|
75
|
+
# scanner.scan(:name).length # => 3
|
76
|
+
#
|
77
|
+
# @return [Integer] length of the substring
|
78
|
+
attr_reader :length
|
79
|
+
|
80
|
+
# Params parsed from the substring.
|
81
|
+
# Will not include params from previous scan results.
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# require 'mustermann/string_scanner'
|
85
|
+
# scanner = Mustermann::StringScanner.new('foo/bar')
|
86
|
+
# scanner.scan(:name).params # => { "name" => "foo" }
|
87
|
+
# scanner.getch.params # => {}
|
88
|
+
# scanner.scan(:name).params # => { "name" => "bar" }
|
89
|
+
#
|
90
|
+
# @see Mustermann::StringScanner#params
|
91
|
+
# @see Mustermann::StringScanner#[]
|
92
|
+
#
|
93
|
+
# @return [Hash] params parsed from the substring
|
94
|
+
attr_reader :params
|
95
|
+
|
96
|
+
# @api private
|
97
|
+
def initialize(scanner, position, length, params = {})
|
98
|
+
@scanner, @position, @length, @params = scanner, position, length, params
|
99
|
+
end
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
# @!visibility private
|
103
|
+
def __getobj__
|
104
|
+
@__getobj__ ||= scanner.to_s[position, length]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Hash] default pattern options used for {#scan} and similar methods
|
109
|
+
# @see #initialize
|
110
|
+
attr_reader :pattern_options
|
111
|
+
|
112
|
+
# Params from all previous matches from {#scan} and {#scan_until},
|
113
|
+
# but not from {#check} and {#check_until}. Changes can be reverted
|
114
|
+
# with {#unscan} and it can be completely cleared via {#reset}.
|
115
|
+
#
|
116
|
+
# @return [Hash] current params
|
117
|
+
attr_reader :params
|
118
|
+
|
119
|
+
# @return [Integer] current scan position on the input string
|
120
|
+
attr_accessor :position
|
121
|
+
alias_method :pos, :position
|
122
|
+
alias_method :pos=, :position=
|
123
|
+
|
124
|
+
# @example with different default type
|
125
|
+
# require 'mustermann/string_scanner'
|
126
|
+
# scanner = Mustermann::StringScanner.new("foo/bar/baz", type: :shell)
|
127
|
+
# scanner.scan('*') # => "foo"
|
128
|
+
# scanner.scan('**/*') # => "/bar/baz"
|
129
|
+
#
|
130
|
+
# @param [String] string the string to scan
|
131
|
+
# @param [Hash] pattern_options default options used for {#scan}
|
132
|
+
def initialize(string = "", **pattern_options)
|
133
|
+
@pattern_options = pattern_options
|
134
|
+
@string = String(string).dup
|
135
|
+
reset
|
136
|
+
end
|
137
|
+
|
138
|
+
# Resets the {#position} to the start and clears all {#params}.
|
139
|
+
# @return [Mustermann::StringScanner] the scanner itself
|
140
|
+
def reset
|
141
|
+
@position = 0
|
142
|
+
@params = {}
|
143
|
+
@history = []
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
# Moves the position to the end of the input string.
|
148
|
+
# @return [Mustermann::StringScanner] the scanner itself
|
149
|
+
def terminate
|
150
|
+
track_result ScanResult.new(self, @position, size - @position)
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
# Checks if the given pattern matches any substring starting at the current position.
|
155
|
+
#
|
156
|
+
# If it does, it will advance the current {#position} to the end of the substring and merges any params parsed
|
157
|
+
# from the substring into {#params}.
|
158
|
+
#
|
159
|
+
# @param (see Mustermann.new)
|
160
|
+
# @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
|
161
|
+
def scan(pattern, **options)
|
162
|
+
track_result check(pattern, **options)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Checks if the given pattern matches any substring starting at any position after the current position.
|
166
|
+
#
|
167
|
+
# If it does, it will advance the current {#position} to the end of the substring and merges any params parsed
|
168
|
+
# from the substring into {#params}.
|
169
|
+
#
|
170
|
+
# @param (see Mustermann.new)
|
171
|
+
# @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
|
172
|
+
def scan_until(pattern, **options)
|
173
|
+
result, prefix = check_until_with_prefix(pattern, **options)
|
174
|
+
track_result(prefix, result)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Reverts the last operation that advanced the position.
|
178
|
+
#
|
179
|
+
# Operations advancing the position: {#terminate}, {#scan}, {#scan_until}, {#getch}.
|
180
|
+
# @return [Mustermann::StringScanner] the scanner itself
|
181
|
+
def unscan
|
182
|
+
raise ScanError, 'unscan failed: previous match record not exist' if @history.empty?
|
183
|
+
previous = @history[0..-2]
|
184
|
+
reset
|
185
|
+
previous.each { |r| track_result(*r) }
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
# Checks if the given pattern matches any substring starting at the current position.
|
190
|
+
#
|
191
|
+
# Does not affect {#position} or {#params}.
|
192
|
+
#
|
193
|
+
# @param (see Mustermann.new)
|
194
|
+
# @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
|
195
|
+
def check(pattern, **options)
|
196
|
+
params, length = create_pattern(pattern, **options).peek_params(rest)
|
197
|
+
ScanResult.new(self, @position, length, params) if params
|
198
|
+
end
|
199
|
+
|
200
|
+
# Checks if the given pattern matches any substring starting at any position after the current position.
|
201
|
+
#
|
202
|
+
# Does not affect {#position} or {#params}.
|
203
|
+
#
|
204
|
+
# @param (see Mustermann.new)
|
205
|
+
# @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
|
206
|
+
def check_until(pattern, **options)
|
207
|
+
check_until_with_prefix(pattern, **options).first
|
208
|
+
end
|
209
|
+
|
210
|
+
def check_until_with_prefix(pattern, **options)
|
211
|
+
start = @position
|
212
|
+
@position += 1 until eos? or result = check(pattern, **options)
|
213
|
+
prefix = ScanResult.new(self, start, @position - start) if result
|
214
|
+
[result, prefix]
|
215
|
+
ensure
|
216
|
+
@position = start
|
217
|
+
end
|
218
|
+
|
219
|
+
# Reads a single character and advances the {#position} by one.
|
220
|
+
# @return [Mustermann::StringScanner::ScanResult, nil] the character, nil if at end of string
|
221
|
+
def getch
|
222
|
+
track_result ScanResult.new(self, @position, 1) unless eos?
|
223
|
+
end
|
224
|
+
|
225
|
+
# Appends the given string to the string being scanned
|
226
|
+
#
|
227
|
+
# @example
|
228
|
+
# require 'mustermann/string_scanner'
|
229
|
+
# scanner = Mustermann::StringScanner.new
|
230
|
+
# scanner << "foo"
|
231
|
+
# scanner.scan(/.+/) # => "foo"
|
232
|
+
#
|
233
|
+
# @param [String] string will be appended
|
234
|
+
# @return [Mustermann::StringScanner] the scanner itself
|
235
|
+
def <<(string)
|
236
|
+
@string << string
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
# @return [true, false] whether or not the end of the string has been reached
|
241
|
+
def eos?
|
242
|
+
@position >= @string.size
|
243
|
+
end
|
244
|
+
|
245
|
+
# @return [true, false] whether or not the current position is at the start of a line
|
246
|
+
def beginning_of_line?
|
247
|
+
@position == 0 or @string[@position - 1] == "\n"
|
248
|
+
end
|
249
|
+
|
250
|
+
# @return [String] outstanding string not yet matched, empty string at end of input string
|
251
|
+
def rest
|
252
|
+
@string[@position..-1] || ""
|
253
|
+
end
|
254
|
+
|
255
|
+
# @return [Integer] number of character remaining to be scanned
|
256
|
+
def rest_size
|
257
|
+
@position > size ? 0 : size - @position
|
258
|
+
end
|
259
|
+
|
260
|
+
# Allows to peek at a number of still unscanned characters without advacing the {#position}.
|
261
|
+
#
|
262
|
+
# @param [Integer] length how many characters to look at
|
263
|
+
# @return [String] the substring
|
264
|
+
def peek(length = 1)
|
265
|
+
@string[@position, length]
|
266
|
+
end
|
267
|
+
|
268
|
+
# Shorthand for accessing {#params}. Accepts symbols as keys.
|
269
|
+
def [](key)
|
270
|
+
params[key.to_s]
|
271
|
+
end
|
272
|
+
|
273
|
+
# (see #params)
|
274
|
+
def to_h
|
275
|
+
params.dup
|
276
|
+
end
|
277
|
+
|
278
|
+
# @return [String] the input string
|
279
|
+
# @see #initialize
|
280
|
+
# @see #<<
|
281
|
+
def to_s
|
282
|
+
@string.dup
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [Integer] size of the input string
|
286
|
+
def size
|
287
|
+
@string.size
|
288
|
+
end
|
289
|
+
|
290
|
+
# @!visibility private
|
291
|
+
def inspect
|
292
|
+
"#<%p %d/%d @ %p>" % [ self.class, @position, @string.size, @string ]
|
293
|
+
end
|
294
|
+
|
295
|
+
# @!visibility private
|
296
|
+
def create_pattern(pattern, **options)
|
297
|
+
PATTERN_CACHE.create_pattern(pattern, **options, **pattern_options)
|
298
|
+
end
|
299
|
+
|
300
|
+
# @!visibility private
|
301
|
+
def track_result(*results)
|
302
|
+
results.compact!
|
303
|
+
@history << results if results.any?
|
304
|
+
results.each do |result|
|
305
|
+
@params.merge! result.params
|
306
|
+
@position += result.length
|
307
|
+
end
|
308
|
+
results.last
|
309
|
+
end
|
310
|
+
|
311
|
+
private :create_pattern, :track_result, :check_until_with_prefix
|
312
|
+
end
|
313
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'mustermann/string_scanner'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift File.expand_path("../../mustermann/lib", __FILE__)
|
2
|
+
require "mustermann/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mustermann-strscan"
|
6
|
+
s.version = Mustermann::VERSION
|
7
|
+
s.author = "Konstantin Haase"
|
8
|
+
s.email = "konstantin.mailinglists@googlemail.com"
|
9
|
+
s.homepage = "https://github.com/rkh/mustermann"
|
10
|
+
s.summary = %q{StringScanner for Mustermann}
|
11
|
+
s.description = %q{Implements a version of Ruby's StringScanner that works with Mustermann patterns}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.required_ruby_version = '>= 2.1.0'
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.add_dependency 'mustermann', Mustermann::VERSION
|
18
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/string_scanner'
|
3
|
+
|
4
|
+
describe Mustermann::StringScanner do
|
5
|
+
include Support::ScanMatcher
|
6
|
+
|
7
|
+
subject(:scanner) { Mustermann::StringScanner.new(example_string) }
|
8
|
+
let(:example_string) { "foo bar" }
|
9
|
+
|
10
|
+
describe :scan do
|
11
|
+
it { should scan("foo") }
|
12
|
+
it { should scan(/foo/) }
|
13
|
+
it { should scan(:name) }
|
14
|
+
it { should scan(":name") }
|
15
|
+
|
16
|
+
it { should_not scan(" ") }
|
17
|
+
it { should_not scan("bar") }
|
18
|
+
|
19
|
+
example do
|
20
|
+
should scan("foo")
|
21
|
+
should scan(" ")
|
22
|
+
should scan("bar")
|
23
|
+
end
|
24
|
+
|
25
|
+
example do
|
26
|
+
scanner.position = 4
|
27
|
+
should scan("bar")
|
28
|
+
end
|
29
|
+
|
30
|
+
example do
|
31
|
+
should scan("foo")
|
32
|
+
scanner.reset
|
33
|
+
should scan("foo")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe :check do
|
38
|
+
it { should check("foo") }
|
39
|
+
it { should check(/foo/) }
|
40
|
+
it { should check(:name) }
|
41
|
+
it { should check(":name") }
|
42
|
+
|
43
|
+
it { should_not check(" ") }
|
44
|
+
it { should_not check("bar") }
|
45
|
+
|
46
|
+
example do
|
47
|
+
should check("foo")
|
48
|
+
should_not check(" ")
|
49
|
+
should_not check("bar")
|
50
|
+
should check("foo")
|
51
|
+
end
|
52
|
+
|
53
|
+
example do
|
54
|
+
scanner.position = 4
|
55
|
+
should check("bar")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe :scan_until do
|
60
|
+
it { should scan_until("foo") }
|
61
|
+
it { should scan_until(":name") }
|
62
|
+
it { should scan_until(" ") }
|
63
|
+
it { should scan_until("bar") }
|
64
|
+
it { should_not scan_until("baz") }
|
65
|
+
|
66
|
+
example do
|
67
|
+
should scan_until(" ")
|
68
|
+
should check("bar")
|
69
|
+
end
|
70
|
+
|
71
|
+
example do
|
72
|
+
should scan_until(" ")
|
73
|
+
scanner.reset
|
74
|
+
should scan("foo")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe :check_until do
|
79
|
+
it { should check_until("foo") }
|
80
|
+
it { should check_until(":name") }
|
81
|
+
it { should check_until(" ") }
|
82
|
+
it { should check_until("bar") }
|
83
|
+
it { should_not check_until("baz") }
|
84
|
+
|
85
|
+
example do
|
86
|
+
should check_until(" ")
|
87
|
+
should_not check("bar")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe :getch do
|
92
|
+
example { scanner.getch.should be == "f" }
|
93
|
+
|
94
|
+
example do
|
95
|
+
scanner.scan("foo")
|
96
|
+
scanner.getch.should be == " "
|
97
|
+
should scan("bar")
|
98
|
+
end
|
99
|
+
|
100
|
+
example do
|
101
|
+
scanner.getch
|
102
|
+
scanner.reset
|
103
|
+
should scan("foo")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe :<< do
|
108
|
+
example do
|
109
|
+
should_not scan_until("baz")
|
110
|
+
scanner << " baz"
|
111
|
+
scanner.to_s.should be == "foo bar baz"
|
112
|
+
should scan_until("baz")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe :eos? do
|
117
|
+
it { should_not be_eos }
|
118
|
+
example do
|
119
|
+
scanner.position = 7
|
120
|
+
should be_eos
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe :beginning_of_line? do
|
125
|
+
let(:example_string) { "foo\nbar" }
|
126
|
+
it { should be_beginning_of_line }
|
127
|
+
|
128
|
+
example do
|
129
|
+
scanner.position = 2
|
130
|
+
should_not be_beginning_of_line
|
131
|
+
end
|
132
|
+
|
133
|
+
example do
|
134
|
+
scanner.position = 3
|
135
|
+
should_not be_beginning_of_line
|
136
|
+
end
|
137
|
+
|
138
|
+
example do
|
139
|
+
scanner.position = 4
|
140
|
+
should be_beginning_of_line
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe :rest do
|
145
|
+
example { scanner.rest.should be == "foo bar" }
|
146
|
+
example do
|
147
|
+
scanner.position = 4
|
148
|
+
scanner.rest.should be == "bar"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe :rest_size do
|
153
|
+
example { scanner.rest_size.should be == 7 }
|
154
|
+
example do
|
155
|
+
scanner.position = 4
|
156
|
+
scanner.rest_size.should be == 3
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe :peek do
|
161
|
+
example { scanner.peek(3).should be == "foo" }
|
162
|
+
|
163
|
+
example do
|
164
|
+
scanner.peek(3).should be == "foo"
|
165
|
+
scanner.peek(3).should be == "foo"
|
166
|
+
end
|
167
|
+
|
168
|
+
example do
|
169
|
+
scanner.position = 4
|
170
|
+
scanner.peek(3).should be == "bar"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe :inspect do
|
175
|
+
example { scanner.inspect.should be == '#<Mustermann::StringScanner 0/7 @ "foo bar">' }
|
176
|
+
example do
|
177
|
+
scanner.position = 4
|
178
|
+
scanner.inspect.should be == '#<Mustermann::StringScanner 4/7 @ "foo bar">'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe :[] do
|
183
|
+
example do
|
184
|
+
should scan(:name)
|
185
|
+
scanner['name'].should be == "foo bar"
|
186
|
+
end
|
187
|
+
|
188
|
+
example do
|
189
|
+
should scan(:name, capture: /\S+/)
|
190
|
+
scanner['name'].should be == "foo"
|
191
|
+
should scan(" :name", capture: /\S+/)
|
192
|
+
scanner['name'].should be == "bar"
|
193
|
+
end
|
194
|
+
|
195
|
+
example do
|
196
|
+
should scan(":a", capture: /\S+/)
|
197
|
+
should scan(" :b", capture: /\S+/)
|
198
|
+
scanner['a'].should be == "foo"
|
199
|
+
scanner['b'].should be == "bar"
|
200
|
+
end
|
201
|
+
|
202
|
+
example do
|
203
|
+
a = scanner.scan(":a", capture: /\S+/)
|
204
|
+
b = scanner.scan(" :b", capture: /\S+/)
|
205
|
+
a.params['a'].should be == 'foo'
|
206
|
+
b.params['b'].should be == 'bar'
|
207
|
+
a.params['b'].should be_nil
|
208
|
+
b.params['a'].should be_nil
|
209
|
+
end
|
210
|
+
|
211
|
+
example do
|
212
|
+
result = scanner.check(":a", capture: /\S+/)
|
213
|
+
result.params['a'].should be == 'foo'
|
214
|
+
scanner['a'].should be_nil
|
215
|
+
end
|
216
|
+
|
217
|
+
example do
|
218
|
+
should scan(:name)
|
219
|
+
scanner.reset
|
220
|
+
scanner['name'].should be_nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe :unscan do
|
225
|
+
example do
|
226
|
+
should scan(:name, capture: /\S+/)
|
227
|
+
scanner['name'].should be == "foo"
|
228
|
+
should scan(" :name", capture: /\S+/)
|
229
|
+
scanner['name'].should be == "bar"
|
230
|
+
scanner.unscan
|
231
|
+
scanner['name'].should be == "foo"
|
232
|
+
scanner.rest.should be == " bar"
|
233
|
+
end
|
234
|
+
|
235
|
+
example do
|
236
|
+
should scan_until(" ")
|
237
|
+
scanner.unscan
|
238
|
+
scanner.rest.should be == "foo bar"
|
239
|
+
end
|
240
|
+
|
241
|
+
example do
|
242
|
+
expect { scanner.unscan }.to raise_error(Mustermann::StringScanner::ScanError,
|
243
|
+
'unscan failed: previous match record not exist')
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe :terminate do
|
248
|
+
example do
|
249
|
+
scanner.terminate
|
250
|
+
scanner.should be_eos
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe :to_h do
|
255
|
+
example { scanner.to_h.should be == {} }
|
256
|
+
example do
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe :to_s do
|
261
|
+
example { scanner.to_s.should be == "foo bar" }
|
262
|
+
end
|
263
|
+
|
264
|
+
describe :clear_cache do
|
265
|
+
example do
|
266
|
+
scanner.scan("foo")
|
267
|
+
Mustermann::StringScanner.clear_cache
|
268
|
+
Mustermann::StringScanner.cache_size.should be == 0
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mustermann-strscan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Konstantin Haase
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mustermann
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.0
|
27
|
+
description: Implements a version of Ruby's StringScanner that works with Mustermann
|
28
|
+
patterns
|
29
|
+
email: konstantin.mailinglists@googlemail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- lib/mustermann/string_scanner.rb
|
36
|
+
- lib/mustermann/strscan.rb
|
37
|
+
- mustermann-strscan.gemspec
|
38
|
+
- spec/string_scanner_spec.rb
|
39
|
+
homepage: https://github.com/rkh/mustermann
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 2.1.0
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.4.3
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: StringScanner for Mustermann
|
63
|
+
test_files:
|
64
|
+
- spec/string_scanner_spec.rb
|
65
|
+
has_rdoc:
|