match_skeleton 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ee807aa1f8e0b7050849503c16ac08f4ef6bc3f
4
+ data.tar.gz: 36b5a97d0a8e12c5d74924373d4d6accf3198987
5
+ SHA512:
6
+ metadata.gz: 13d436fe8338c8ccd41b6a503b5daa033dab55e6bb66dfab71fb8211a59270acac9cf60b576ed455b36f413e23ad99bd26775ac6b6425b39997668181684c231
7
+ data.tar.gz: 66d95d123d62cc28348e681f987e94b664761e73c4ae8cacb578c848270fa17e9cc28c8b22876f71faa495d92191e79046ca2c8943e4d305a9eff028aceabb12
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.[oa]
2
+ *.so
3
+ *~
4
+ *.nogem
5
+ *nogem.*
6
+ *.bak
7
+ *.BAK
8
+ *.backup
9
+ *.org
10
+ *.orig
data/ChangeLog ADDED
@@ -0,0 +1,3 @@
1
+ -----
2
+ (Version: 0.1.0) 2016-05-19
3
+ * First creation.
data/News ADDED
@@ -0,0 +1,3 @@
1
+ -----
2
+ (Version: 0.1.0) 2016-05-19
3
+ * First creation.
data/README.ja.rdoc ADDED
@@ -0,0 +1,188 @@
1
+
2
+ = MatchSkeleton - Equivalent to MatchData with less memory use
3
+
4
+ This package provides the class {MatchSkeleton}.
5
+ It behaves almost exactly like {MatchData}, except it uses a greatly
6
+ less and practically negligible internal memory.
7
+
8
+ The built-in {MatchData} class holds a copy of the original
9
+ {String}, from which the matched data are derived, in a "freeze"
10
+ state. It is useful because no one can by chance destructively modify
11
+ the original string. However, if is memory-savvy. Even if
12
+ {MatchData} is the match of the first few characters of a 1-GB string,
13
+ it internally holds the entire 1-GB of the original string. If a code
14
+ running holds 1000 of them, it needs a memory use of 1TB(!).
15
+
16
+ This class holds the same information of the matched data as
17
+ {MatchData} but as positional indices, and the original String object only.
18
+ Therefore, as long as the original String is not destructively
19
+ modified by an external process, any method of this class returns the
20
+ equal result as {MatchData}. Note that while the instance of this
21
+ class is kept, the original string is not garbage-collected by the
22
+ Ruby interpreter.
23
+
24
+ One additional function this class offers is the instance variable and
25
+ method <tt>pos_begin</tt>. It can be set by a user and hold the
26
+ information ({Integer}) at which position the {Regexp} match has started to
27
+ generate the matched data.
28
+
29
+ Note this library, when required, also modifies the operator {MatchData#==}
30
+ so that it can be compared with {MatchSkeleton} objects.
31
+ Now the operator is simply based on duck-typing of
32
+ {#string}, {#regexp}, and {#pre_match}.
33
+ {MatchData#eql?} is not affected, and therefore any comparison between
34
+ {MatchData} and {MatchSkeleton} objects with {MatchData#eql?} or
35
+ {MatchSkeleton#eql?} returns false.
36
+
37
+ == Install
38
+
39
+ The standard procedure to install a gem should work:
40
+ gem install match_skeleton
41
+ Or, get it from
42
+ {https://rubygems.org/gems/match_skeleton}
43
+
44
+ Then just require as
45
+ require 'match_skeleton'
46
+
47
+ Have fun!
48
+
49
+
50
+ == Simple Examples
51
+
52
+ Here are some simple examples.
53
+
54
+ require 'match_skeleton'
55
+
56
+ str = "Something_Big."
57
+ pos = 3
58
+ mtch = str.match(/(.)big/i, pos)
59
+ mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
60
+
61
+ mtch == mskn # => true
62
+ mskn.string # => "Something_Big."
63
+ mskn[1] # => "_"
64
+ mskn.pos_begin # => 3
65
+
66
+ mskn.post_match # => "."
67
+ mtch.post_match # => "."
68
+ str[-1,1] = '%'
69
+ mskn.post_match # => "%" # The original string has been destructively modified. Do not do that...
70
+ mtch.post_match # => "."
71
+ mtch == mskn # => false
72
+
73
+ == Known bugs
74
+
75
+ * None.
76
+
77
+ This library has been tested in Ruby 2.0 or later only.
78
+ It may work in Ruby 1.8, but has not been tested.
79
+
80
+ Extensive tests have been performed, as included in the package.
81
+
82
+ == Acknowledgement
83
+
84
+ This work is supported by {Wise Babel Ltd}[http://www.wisebabel.com/].
85
+ {http://www.wisebabel.com/}
86
+
87
+ == Copyright etc
88
+
89
+ Author:: Masa Sakano < imagine a_t sakano dot co dot uk >
90
+ License:: MIT.
91
+ Warranty:: No warranty whatsoever.
92
+ Versions:: The versions of this package follow Semantic Versioning (2.0.0) http://semver.org/
93
+
94
+
95
+
96
+ = MatchSkeleton - MatchDataに等価でメモリを節約するクラス
97
+
98
+ クラス {MatchSkeleton} のGemです。
99
+ {MatchData}とほぼ完全に等価な動作をします。ただし、メモリ使用量は
100
+ 比較してはるかに少なくなり、ほぼ無視できます。
101
+
102
+ 組込{MatchData}クラスは、正規表現マッチした元の文字列(String)のコピーを
103
+ "freeze"した状態で保持します。おかげで、外部のプロセスが元文字列を
104
+ 破壊的に変更して{MatchData}のオブジェクトも影響を受ける、ということは
105
+ ありません。しかし、それはメモリ使用量が巨大になり得ます。たとえば、
106
+ {MatchData}オブジェクトが1GBの文字列の最初の数文字だけのマッチだったと
107
+ しても、同オブジェクトは、その1GBの文字列を内部的に保持します。
108
+ もし動作中のコードがそんなオブジェクトを1000個保持していたら、
109
+ それだけで 1TB(!)のメモリを消費することになります。
110
+
111
+ このクラスは、{MatchData}と同じ情報を保持しますが、それは内部的に、
112
+ インデックスと(コピーではない)原文字列そのものだけです。したがって、
113
+ 原文字列が外部のプロセスで破壊的に変更されない限り、このクラスの
114
+ メソッドの返り値は、{MatchData}と同じです。念のため、このクラスの
115
+ インスタンスが生きている限り、原文字列がガーベージ・コレクトされる
116
+ ことはありません。
117
+
118
+ 一点、このクラス独自のパラメーターとして、インスタンス変数かつ
119
+ メソッドの<tt>pos_begin</tt>が導入されています。正規表現マッチの
120
+ 開始ポジション({Integer})を(ユーザーが設定することで)保持します。
121
+
122
+ なお、このライブラリを require した時、演算子 {MatchData#==}
123
+ が変更され、{MatchSkeleton} と比較できるようになります。
124
+ 結果、同演算子は、ダック・タイピングとして、
125
+ {#string}, {#regexp}, および {#pre_match}に依存するようになります。
126
+ 一方、{MatchData#eql?} は影響を受けません。すなわち、
127
+ {MatchData#eql?} あるいは {MatchSkeleton#eql?}によって、
128
+ {MatchData} と {MatchSkeleton} オブジェクトを比較すると常に
129
+ 偽(false)を返します。
130
+
131
+
132
+ == インストール
133
+
134
+ gem を使ってインストールできます。
135
+ gem install match_skeleton
136
+ もしくは以下から入手して下さい。
137
+ {https://rubygems.org/gems/match_skeleton}
138
+
139
+ あとは、以下で一発です。
140
+ require 'match_skeleton'
141
+
142
+ お楽しみあれ!
143
+
144
+
145
+ == 単純な使用例
146
+
147
+ 以下に幾つかの基本的な使用例を列挙します。
148
+
149
+ require 'match_skeleton'
150
+
151
+ str = "Something_Big."
152
+ pos = 3
153
+ mtch = str.match(/(.)big/i, pos)
154
+ mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
155
+
156
+ mtch == mskn # => true
157
+ mskn.string # => "Something_Big."
158
+ mskn[1] # => "_"
159
+ mskn.pos_begin # => 3
160
+
161
+ mskn.post_match # => "."
162
+ mtch.post_match # => "."
163
+ str[-1,1] = '%'
164
+ mskn.post_match # => "%" # 元文字列が破壊的に変更された結果。そんなことはしないように……。
165
+ mtch.post_match # => "."
166
+ mtch == mskn # => false
167
+
168
+ == 既知のバグ
169
+
170
+ * なし。
171
+
172
+ このライブラリは Ruby 2.0 以上でのみ挙動試験されています。
173
+ 1.8以前でも動くかもしれませんが、テストされていません。
174
+
175
+ パッケージに含まれている通り、網羅的なテストが実行されています。
176
+
177
+ == 謝辞
178
+
179
+ この開発は、ワイズバベル社({Wise Babel Ltd}[http://www.wisebabel.com/en])に支援されています。
180
+ http://www.wisebabel.com/ja
181
+
182
+ == 著作権他情報
183
+
184
+ 著者:: Masa Sakano < imagine a_t sakano dot co dot uk >
185
+ 利用許諾条項:: MIT.
186
+ 保証:: 一切無し。
187
+ バージョン:: Semantic Versioning (2.0.0) http://semver.org/
188
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.pattern = 'test/**/test_*.rb'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
10
+
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # =Class MatchData
4
+ #
5
+ # Modifies the operator {#==} so it can be compared with {MatchSkeleton}.
6
+ #
7
+ class MatchData
8
+
9
+ # Backup alias for {MatchData#==}
10
+ alias_method :equal_before_match_skeleton, :== if ! self.method_defined?(:equal_before_match_skeleton)
11
+
12
+ # Compares with {MatchSkeleton}, in addition to {MatchData}
13
+ #
14
+ # @param [#string, #regexp, #pre_match] All the methods have to be there. Practically, {MatchSkeleton} and {MatchData}
15
+ # @return [Boolean]
16
+ def ==(obj)
17
+ (string == obj.string) &&
18
+ (regexp == obj.regexp) &&
19
+ (pre_match == obj.pre_match)
20
+ end
21
+
22
+ end
@@ -0,0 +1,218 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'match_skeleton/match_data'
4
+
5
+ # =Class MatchSkeleton
6
+ #
7
+ # To represent {MatchData} with much less memory use
8
+ #
9
+ class MatchSkeleton
10
+
11
+ # The same as {MatchData#string} but it is identical to the original string.
12
+ # If the original string is modified destructively, this too is modified.
13
+ attr_reader :string
14
+
15
+ # The same as {MatchData#regexp}.
16
+ attr_reader :regexp
17
+
18
+ # The position {Regexp} match has started.
19
+ # For example, both
20
+ # /x/.match('0000x', 0)
21
+ # /x/.match('0000x', 3)
22
+ # give the same (equal) {MatchData}. This instance variable {#pos_begin}
23
+ # holds the position (0 or 3 in the cases above), if set explicitly.
24
+ attr_accessor :pos_begin
25
+
26
+
27
+ # @param md [MatchData]
28
+ # @param string [String] If not specified, it is taken from the first argument (however, that would spoil the whole point of using this class!)
29
+ # @param pos_begin: [Integer] The position {Regexp} match has started.
30
+ def initialize(md, string=nil, pos_begin: nil)
31
+ size_str = md.string.size
32
+ if string && string.size != size_str
33
+ raise ArgumentError, 'The first parameter is obligatory.'
34
+ end
35
+ @string = (string || md.string)
36
+ @regexp = md.regexp
37
+ @pre_match = (0..(md.pre_match.size-1)) # {Range}
38
+ @post_match = ((size_str-md.post_match.size)..(size_str-1)) # {Range}
39
+
40
+ # @offsets is Hash with the keys of both Integer and possibly Symbol
41
+ # if names exist.
42
+ names = md.names
43
+ ar_off = (0..(md.size-1)).map do |n|
44
+ ar = md.offset(n)
45
+ #if names[0]=='foo'
46
+ #print "DEBUG: ar=#{ar.inspect}\n"
47
+ #printf "DEBUG: n=(%d)range=%s\n", n, (ar.first...ar.last).inspect
48
+ #end
49
+ (ar.first...ar.last)
50
+ end
51
+ @offsets = {}
52
+ ar_off.each_with_index do |ev, ei|
53
+ @offsets[ei] = ev
54
+ ej = ei - 1
55
+ @offsets[names[ej]] = ev if (ej >= 0 && names[ej])
56
+ #print "DEBUG: names=#{names[ei].inspect}\n"
57
+ #p names[ei], ev
58
+ #p md.offset(:foo)
59
+ end
60
+ #printf "DEBUG: offsets=%s\n", @offsets.inspect if !names.empty?
61
+
62
+
63
+ @pos_begin = pos_begin
64
+ end
65
+
66
+ # Comparable with {MatchSkeleton} and {MatchData}
67
+ #
68
+ # A difference in {#pos_begin} is not taken into account.
69
+ #
70
+ # @param [Object] The methods of {#string}, {#regexp}, {#pre_match} have to be defined and return the same to be true. Practically, only {MatchSkeleton} and {MatchData} may return true.
71
+ # @return [Boolean]
72
+ # @see #eql?
73
+ def ==(obj)
74
+ (string == obj.string) &&
75
+ (regexp == obj.regexp) &&
76
+ (pre_match == obj.pre_match)
77
+ end
78
+
79
+ # The same as {MatchData#[]}
80
+ #
81
+ # @param i [Integer, Range, Symbol, String]
82
+ # @param j [Integer, NilClass]
83
+ # @return [String, Array, NilClass]
84
+ # @raise [IndexError] if an invalid argument(s) is given.
85
+ def [](i, j=nil)
86
+ if j
87
+ to_a[i, j]
88
+ elsif defined?(i.to_sym)
89
+ values_at(i)[0]
90
+ else
91
+ to_a[i]
92
+ end
93
+ end
94
+
95
+ # The same as {MatchData#begin}
96
+ #
97
+ # @param n [Integer]
98
+ # @return [Integer]
99
+ def begin(n)
100
+ offset(n)[0]
101
+ end
102
+
103
+ # The same as {MatchData#captures}
104
+ #
105
+ # @return [Array]
106
+ def captures
107
+ to_a[1..-1]
108
+ end
109
+
110
+ # The same as {MatchData#end}
111
+ #
112
+ # @param n [Integer]
113
+ # @return [Integer]
114
+ def end(n)
115
+ offset(n)[1]
116
+ end
117
+
118
+ # The same as {#==} but stricter comparison.
119
+ #
120
+ # The comparison between {MatchSkeleton} and {MatchData} returns false and
121
+ # that between {MatchSkeleton} with different {#pos_begin} or
122
+ # those with strings of different Object-IDs also return false.
123
+ #
124
+ # @param [Object]
125
+ # @return [Boolean]
126
+ # @see #==
127
+ def eql?(obj)
128
+ return false if self != obj
129
+ return false if !defined?(obj.pos_begin)
130
+ return false if (string.object_id != obj.string.object_id)
131
+ return false if pos_begin != obj.pos_begin
132
+ return true
133
+ end
134
+
135
+ # Similar to {MatchData#inspect}
136
+ #
137
+ # @return [String]
138
+ def inspect
139
+ core = ''
140
+ ar = (names.empty? ? captures : names)
141
+ ar.each_with_index do |ev, ei|
142
+ core << sprintf(" %d:%s", ei, ev.inspect)
143
+ end
144
+ sprintf("#<%s %s%s>", self.class.to_s, self[0].inspect, core)
145
+ end
146
+
147
+ # The same as {MatchData#names} and {Regexp#names}
148
+ #
149
+ # @return [Array<String>]
150
+ def names
151
+ regexp.names
152
+ end
153
+
154
+ # The same as {MatchData#offset}
155
+ #
156
+ # @param n [Integer]
157
+ # @return [Array<integer>]
158
+ def offset(n)
159
+ if defined?(n.to_sym)
160
+ n = n.to_s
161
+ raise IndexError, sprintf("undefined group name reference: %s", n) if !names.include?(n)
162
+ end
163
+ [@offsets[n].first,
164
+ @offsets[n].last]
165
+ end
166
+
167
+ # The same as {MatchData#pre_match}
168
+ #
169
+ # @return [String]
170
+ def pre_match
171
+ @string[@pre_match]
172
+ end
173
+
174
+ # The same as {MatchData#post_match}
175
+ #
176
+ # @return [String]
177
+ def post_match
178
+ @string[@post_match]
179
+ end
180
+
181
+ # The same as {MatchData#size}
182
+ #
183
+ # @return [Integer]
184
+ def size
185
+ to_a.size
186
+ end
187
+ alias_method :length, :size if ! self.method_defined?(:length)
188
+
189
+ # The same as {MatchData#to_a}
190
+ #
191
+ # @return [Array]
192
+ def to_a
193
+ #print 'DEBUG: '; p @offsets
194
+ indices = @offsets.keys.sort
195
+ indices.delete_if { |i| !defined?(i.divmod) }
196
+ indices.map { |i| string[@offsets[i]] }
197
+ end
198
+
199
+ # The same as {MatchData#to_s}
200
+ #
201
+ # @return [String]
202
+ def to_s
203
+ self[0]
204
+ end
205
+
206
+ # The same as {MatchData#values_at}
207
+ #
208
+ # @param *rest [Integer, Symbol, String]
209
+ # @return [Array]
210
+ def values_at(*rest)
211
+ rest.map do |i|
212
+ key = @offsets[i.to_s]
213
+ # printf "DEBUG(%s): offsets=%s string=%s i=%s key=%s r=%s\n", __method__, @offsets.inspect,string.inspect,i.inspect,key.inspect,(string[key].inspect rescue 'nil') if !names.empty?
214
+ raise IndexError, sprintf("undefined group name reference: %s", i) if !key
215
+ string[key]
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,353 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $stdout.sync=true
4
+ $stderr.sync=true
5
+ # print '$LOAD_PATH=';p $LOAD_PATH
6
+
7
+ arlibrelpath = []
8
+ arlibdir = %w(match_skeleton)
9
+ arlibbase = [''] # match_skeleton.rb is read.
10
+
11
+ arlibbase.each do |elibbase|
12
+ arlibdir.each do |elibdir|
13
+
14
+ arAllPaths = []
15
+ er=nil
16
+ pathnow = nil
17
+ # (['../lib/', 'lib/', ''].map{|i| i+elibbase+'/'} + ['']).each do |dir|
18
+ ['../lib', 'lib', ''].each do |dirroot|
19
+ begin
20
+ s = [dirroot, elibdir, elibbase].join('/').sub(%r@^/@, '').sub(%r@/$@, '')
21
+ # eg., %w(../lib/rangeary lib/rangeary rangeary)
22
+ next if s.empty?
23
+ arAllPaths.push(s)
24
+ require s
25
+ pathnow = s
26
+ break
27
+ rescue LoadError => er
28
+ end
29
+ end # (['../lib/', 'lib/', ''].map{|i| i+elibbase+'/'} + '').each do |dir|
30
+
31
+ if pathnow.nil?
32
+ warn "Warning: All the attempts to load the following files have failed. Abort..."
33
+ warn arAllPaths.inspect
34
+ warn " NOTE: It may be because a require statement in that file failed,
35
+ rather than requiring the file itself.
36
+ Check with % ruby -r#{File.basename(elibbase)} -e p
37
+ or maybe add env RUBYLIB=$RUBYLIB:`pwd`"
38
+ # p $LOADED_FEATURES.grep(/#{Regexp.quote(File.basename(elibbase)+'.rb')}$/)
39
+ raise er
40
+ else
41
+ arlibrelpath.push pathnow
42
+ end
43
+ end # arlibdir.each do |elibdir|
44
+ end # arlibbase.each do |elibbase|
45
+
46
+ print "NOTE: Library relative paths: "; p arlibrelpath
47
+ print "NOTE: Library full paths:\n"
48
+ arlibbase.each do |elibbase|
49
+ fname = (elibbase.empty? ? arlibdir[0] : elibbase) # Not complete.
50
+ p $LOADED_FEATURES.grep(/#{Regexp.quote(File.basename(fname)+'.rb')}$/)
51
+ end
52
+
53
+
54
+ #################################################
55
+ # Unit Test
56
+ #################################################
57
+
58
+ require 'minitest/autorun'
59
+
60
+ class TestMatchSkeleton < Minitest::Test
61
+ T = true
62
+ F = false
63
+ Inf = Float::INFINITY
64
+ BS ||= "\u005c"
65
+ BS_QUOTE ||= Regexp.quote(BS)
66
+
67
+ def setup
68
+ # @ib = 1
69
+ end
70
+
71
+ def teardown # teardown is not often used.
72
+ # @foo = nil
73
+ end
74
+
75
+ # # User method
76
+ # def conjRE(r1, r2)
77
+ # Escape_Character.class_eval{ conjunctionRangeExtd(r1, r2) }
78
+ # end
79
+
80
+ def test_readme_example01
81
+ str = "Something_Big."
82
+ pos = 3
83
+ mtch = str.match(/(.)big/i, pos)
84
+ mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
85
+
86
+ assert_equal true, (mtch == mskn)
87
+ assert_equal "Something_Big.", mskn.string
88
+ assert_equal "_", mskn[1]
89
+ assert_equal 3, mskn.pos_begin
90
+
91
+ assert_equal ".", mskn.post_match
92
+ assert_equal ".", mtch.post_match
93
+ str[-1,1] = '%'
94
+ assert_equal '%', mskn.post_match
95
+ assert_equal ".", mtch.post_match
96
+ assert_equal false, (mtch == mskn)
97
+ end
98
+
99
+ def test_string01
100
+ # string(), regexp()
101
+ s = 'abcdefghij'
102
+ id = s.object_id
103
+ re = /./i
104
+ md = s.match(re)
105
+ ms1 = MatchSkeleton.new(md, s)
106
+
107
+ assert_equal s, ms1.string
108
+ assert_equal id, ms1.string.object_id
109
+ assert_equal re, ms1.regexp
110
+ end
111
+
112
+ def test_pos_begin01
113
+ s = 'abcdefghij'
114
+ re = /g/i
115
+ pos = 3
116
+ md = s.match(re, pos)
117
+ ms1 = MatchSkeleton.new(md, s, pos_begin: pos)
118
+
119
+ assert_equal pos, ms1.pos_begin
120
+ ms1.pos_begin = 5
121
+ assert_equal 5, ms1.pos_begin
122
+ end
123
+
124
+ def test_equal01
125
+ s = 'abcdefghij'
126
+ re1 = /g/
127
+ pos = 3
128
+
129
+ md1 = s.match(re1, pos)
130
+ ms1 = MatchSkeleton.new(md1, s, pos_begin: pos)
131
+ assert_equal true, (ms1 == md1)
132
+ assert_equal true, (md1 == ms1)
133
+ assert_equal false, (ms1.eql?(md1))
134
+
135
+ # pos_begin does not count.
136
+ ms1p = ms1.dup
137
+ assert_equal ms1.string.object_id, ms1p.string.object_id
138
+ assert_equal true, (ms1 == ms1p)
139
+ assert_equal true, (ms1.eql?(ms1p))
140
+ ms1p.pos_begin = 6
141
+ assert_equal true, (ms1 == ms1p)
142
+ assert_equal false, (ms1.eql?(ms1p))
143
+
144
+ # Different Object-ID
145
+ s2 = s.dup
146
+ md2 = s2.match(re1, pos)
147
+ ms21= MatchSkeleton.new(md2, s, pos_begin: pos) # Equal but non-identical MatchData, but identical string s (with the identical Object-IDs)
148
+ assert_equal true, (ms1 == ms21)
149
+ assert_equal true, (ms1 == md2)
150
+ assert_equal true, (md2 == ms1)
151
+ assert_equal true, (ms21== md2)
152
+ assert_equal true, (md2 == ms21)
153
+ assert_equal true, (ms21.eql?(ms1))
154
+
155
+ ms2 = MatchSkeleton.new(md2, s2, pos_begin: pos) # Equal MatchData, but non-identical string s (with the different Object-IDs)
156
+ assert_equal true, (ms1 == ms2)
157
+ assert_equal true, (ms1 == md2)
158
+ assert_equal true, (ms2 == md2)
159
+ assert_equal true, (md2 == ms2)
160
+ assert_equal false, (ms2.eql?(ms1))
161
+
162
+ # Slightly different Regexp
163
+ re3 = /g/i
164
+ md3 = s.match(re3, pos)
165
+ ms3 = MatchSkeleton.new(md3, s, pos_begin: pos)
166
+ assert_equal false, (ms3 == ms1)
167
+
168
+ # Different pre_match
169
+ re4 = /./
170
+ pos4 = 2
171
+ md4 = s.match(re4, pos4)
172
+ ms4 = MatchSkeleton.new(md4, s)
173
+ pos5 = 6
174
+ md5 = s.match(re4, pos5)
175
+ ms5 = MatchSkeleton.new(md5, s)
176
+ assert_equal false, (ms4 == ms5)
177
+ end
178
+
179
+ def test_squarebracket_single
180
+ s = "THX1138."
181
+ md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
182
+ ms1 = MatchSkeleton.new(md1, s)
183
+ assert_equal md1[0], ms1[0]
184
+ assert_equal "HX1138", ms1[0]
185
+ assert_equal md1[1], ms1[1]
186
+ assert_equal "H", ms1[1]
187
+ assert_equal md1[3], ms1[3]
188
+ assert_equal "113", ms1[3]
189
+ end
190
+
191
+ def test_squarebracket_double
192
+ s = "THX1138."
193
+ md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
194
+ ms1 = MatchSkeleton.new(md1, s)
195
+ assert_equal md1[1,2], ms1[1, 2]
196
+ assert_equal ["H", "X"], ms1[1, 2]
197
+ assert_equal md1[-3, 2], ms1[-3, 2]
198
+ assert_equal ["X", "113"], ms1[-3, 2]
199
+ end
200
+
201
+ def test_squarebracket_range
202
+ s = "THX1138."
203
+ md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
204
+ ms1 = MatchSkeleton.new(md1, s)
205
+ assert_equal md1[1..3], ms1[1..3]
206
+ assert_equal ["H", "X", "113"], ms1[1..3]
207
+ end
208
+
209
+ def test_squarebracket_name
210
+ s = "ccaaab"
211
+ md1 = /(?<foo>a+)b/.match(s) #=> #<MatchData "aaab" foo:"aaa">
212
+ ms1 = MatchSkeleton.new(md1, s)
213
+ assert_equal md1["foo"], ms1["foo"]
214
+ assert_equal "aaa", ms1["foo"]
215
+ assert_equal md1[:foo], ms1[:foo]
216
+ assert_equal "aaa", ms1[:foo]
217
+ assert_raises(IndexError) { md1.begin(:naiyo) }
218
+ assert_raises(IndexError) { ms1.begin(:naiyo) }
219
+ end
220
+
221
+ def test_begin_end_int01
222
+ s ="THX1138."
223
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
224
+ ms1 = MatchSkeleton.new(md1, s)
225
+ assert_equal md1.begin(0), ms1.begin(0)
226
+ assert_equal md1.begin(2), ms1.begin(2)
227
+ assert_equal md1.end(0), ms1.end(0)
228
+ assert_equal md1.end(2), ms1.end(2)
229
+ assert_equal 1, ms1.begin(0)
230
+ assert_equal 2, ms1.begin(2)
231
+ assert_equal 7, ms1.end(0)
232
+ assert_equal 3, ms1.end(2)
233
+ end
234
+
235
+ def test_begin_end_int02
236
+ s ="hoge"
237
+ md1 = /(?<foo>.)(.)(?<bar>.)/.match(s)
238
+ ms1 = MatchSkeleton.new(md1, s)
239
+ assert_equal md1.begin(:foo), ms1.begin(:foo)
240
+ assert_equal md1.begin(:bar), ms1.begin(:bar)
241
+ assert_equal md1.end(:bar), ms1.end(:bar)
242
+ assert_equal md1.end(:foo), ms1.end(:foo)
243
+ assert_equal 0, ms1.begin(:foo)
244
+ assert_equal 2, ms1.begin(:bar)
245
+ assert_equal 1, ms1.end(:foo)
246
+ assert_equal 3, ms1.end(:bar)
247
+ assert_raises(IndexError) { ms1.begin(:naiyo) }
248
+ assert_raises(IndexError) { ms1.end(:naiyo) }
249
+ end
250
+
251
+ def test_captures01
252
+ s ="THX1138."
253
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
254
+ ms1 = MatchSkeleton.new(md1, s)
255
+ assert_equal md1.captures.size, ms1.captures.size
256
+ assert_equal md1.captures, ms1.captures
257
+ assert_equal md1.captures[0], ms1.captures[0]
258
+ assert_equal ms1.captures[3], ms1.captures[3]
259
+ assert_equal 4, ms1.captures.size
260
+ assert_equal ms1.to_a[1..-1], ms1.captures
261
+ assert_equal 'H', ms1.captures[0]
262
+ assert_equal '8', ms1.captures[3]
263
+ end
264
+
265
+ def test_inspect01
266
+ s ="foo"
267
+ md1 = /.$/.match(s)
268
+ ms1 = MatchSkeleton.new(md1, s)
269
+ assert_equal '#<MatchSkeleton "o">', ms1.inspect
270
+ end
271
+
272
+ def test_names01
273
+ s = "hoge"
274
+ md1 = /(?<foo>.)(?<bar>.)(?<baz>.)/.match(s)
275
+ ms1 = MatchSkeleton.new(md1, s)
276
+ assert_equal md1.names, ms1.names
277
+ assert_equal ["foo", "bar", "baz"], ms1.names
278
+
279
+ s = "a"
280
+ md1 = /(?<x>.)(?<y>.)?/.match(s) #=> #<MatchData "a" x:"a" y:nil>
281
+ ms1 = MatchSkeleton.new(md1, s)
282
+ assert_equal md1.names, ms1.names
283
+ assert_equal ["x", "y"], ms1.names
284
+ end
285
+
286
+ def test_offset_int01
287
+ s = "THX1138."
288
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
289
+ ms1 = MatchSkeleton.new(md1, s)
290
+ assert_equal md1.offset(0), ms1.offset(0)
291
+ assert_equal md1.offset(4), ms1.offset(4)
292
+ assert_equal [1, 7], ms1.offset(0)
293
+ assert_equal [6, 7], ms1.offset(4)
294
+ end
295
+
296
+ def test_offset_name01
297
+ s = "hoge"
298
+ md1 = /(?<foo>.)(.)(?<bar>.)/.match(s)
299
+ ms1 = MatchSkeleton.new(md1, s)
300
+ assert_equal md1.offset(:foo), ms1.offset(:foo)
301
+ assert_equal md1.offset(:bar), ms1.offset(:bar)
302
+ assert_equal [0, 1], ms1.offset(:foo)
303
+ assert_equal [2, 3], ms1.offset(:bar)
304
+ assert_raises(IndexError) { ms1.offset(:naiyo) }
305
+ end
306
+
307
+ def test_post_match01
308
+ s = "THX1138: The Movie"
309
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
310
+ ms1 = MatchSkeleton.new(md1, s)
311
+ assert_equal md1.post_match, ms1.post_match
312
+ assert_equal ": The Movie", ms1.post_match
313
+ end
314
+
315
+ def test_pre_match01
316
+ s = "THX1138."
317
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
318
+ ms1 = MatchSkeleton.new(md1, s)
319
+ assert_equal md1.pre_match, ms1.pre_match
320
+ assert_equal "T", ms1.pre_match
321
+ end
322
+
323
+ def test_size01
324
+ s = "THX1138."
325
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
326
+ ms1 = MatchSkeleton.new(md1, s)
327
+ assert_equal md1.size, ms1.size
328
+ assert_equal md1.length, ms1.length
329
+ assert_equal 5, ms1.size
330
+ assert_equal 5, ms1.length
331
+ end
332
+
333
+ def test_to_a01
334
+ s = "THX1138."
335
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
336
+ ms1 = MatchSkeleton.new(md1, s)
337
+ assert_equal md1.to_a, ms1.to_a
338
+ assert_equal ["HX1138", "H", "X", "113", "8"], ms1.to_a
339
+ end
340
+
341
+ def test_to_s01
342
+ s = "THX1138."
343
+ md1 = /(.)(.)(\d+)(\d)/.match(s)
344
+ ms1 = MatchSkeleton.new(md1, s)
345
+ assert_equal md1.to_s, ms1.to_s
346
+ assert_equal "HX1138", ms1.to_s
347
+ assert_equal ms1.to_a[0], ms1.to_s
348
+ assert_equal ms1[0], ms1.to_s
349
+ end
350
+
351
+ end # class TestMatchSkeleton < Minitest::Test
352
+
353
+
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: match_skeleton
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Masa Sakano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: MatchSkeleton is a class equivalent to MatchData with negligible memory
14
+ use. As long as the original string is not destructively modified, it behaves almost
15
+ icentical to MatchData.
16
+ email: info@wisebabel.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files:
20
+ - README.ja.rdoc
21
+ files:
22
+ - ".gitignore"
23
+ - ChangeLog
24
+ - News
25
+ - README.ja.rdoc
26
+ - Rakefile
27
+ - lib/match_skeleton.rb
28
+ - lib/match_skeleton/match_data.rb
29
+ - test/test_match_skeleton.rb
30
+ homepage: https://www.wisebabel.com/
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options:
36
+ - "--charset=UTF-8"
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.5.1
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: MatchSkeleton - class equivalent to MatchData with negligible memory use
55
+ test_files:
56
+ - test/test_match_skeleton.rb
57
+ has_rdoc: