sub_string 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c328fe148dea22de40b0b91fcb890ecad51ec98b52579bd963ec3595a5c5d312
4
+ data.tar.gz: 2490b15f6bab5c743d02db40e5857acf9fd6b3b38b75fa7d155869e7dce957e3
5
+ SHA512:
6
+ metadata.gz: 573037a06377750a49a90d0ed95ca0ab59deb693eec7410b9a618352f334927e229a10a621e0c7a380f8deb4f201d5925b112871442b0fbb4d32ef86bc4dafb3
7
+ data.tar.gz: bab762511ada2c4e0c5b959c243605ad503d7e38f11ec1eebab0acbfa84eb95e6b3bbd9b9a3d0b6a609001521d455fde94489436dcbbfd0ebd31ea014adb1449
@@ -0,0 +1,51 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+ /vendor/bundle
10
+
11
+ # Ignore all logfiles and tempfiles.
12
+ /log/*
13
+ /tmp/*
14
+ !/log/.keep
15
+ !/tmp/.keep
16
+
17
+ .rbenv-version
18
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
19
+ .rvmrc
20
+
21
+ /node_modules
22
+ /yarn-error.log
23
+
24
+ .byebug_history
25
+
26
+ *.[oa]
27
+ *.so
28
+ *~
29
+ *.nogem
30
+ *nogem.*
31
+ *.bak
32
+ *.BAK
33
+ *.backup
34
+ *.org
35
+ *.orig
36
+ *.elc
37
+ *.pyc
38
+ \#*\#
39
+
40
+ # Elastic Beanstalk Files
41
+ .elasticbeanstalk/*
42
+ !.elasticbeanstalk/*.cfg.yml
43
+ !.elasticbeanstalk/*.global.yml
44
+
45
+ # yard
46
+ *.yardoc
47
+
48
+ # Ruby Gem doc
49
+ *.gem
50
+ doc/*
51
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012-2018 Scott Chacon and others
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ ALL =
2
+
3
+ objs =
4
+
5
+ .SUFFIXES: .so .o .c .f
6
+
7
+ #.o.so:
8
+ # ${LD} ${LFLAGS} -o $@ $< ${LINK_LIB}
9
+
10
+ all: ${ALL}
11
+
12
+
13
+ .PHONY: clean test doc
14
+ clean:
15
+ $(RM) bin/*~
16
+
17
+ ## You may need RUBYLIB=`pwd`/lib:$RUBYLIB
18
+ test:
19
+ rake test
20
+
21
+ doc:
22
+ yard doc; [[ -x ".github" && ( "README.en.rdoc" -nt ".github/README.md" ) ]] && ( ruby -r rdoc -e 'puts RDoc::Markup::ToMarkdown.new.convert ARGF.read' < README.en.rdoc > .github/README.md ; echo ".github/README.md is updated." ) || exit 0
23
+
@@ -0,0 +1,173 @@
1
+
2
+ = SubString - Duck-typed String class with negligible memory use
3
+
4
+ == Summary
5
+
6
+ Class {SubString} that expresses Ruby sub-String but taking up negligible memory space, as its instance holds the positional information only. It behaves exactly like String (duck-typing), except destructive modification is prohibited. If the original string is destructively modified, warning is issued.
7
+
8
+ For the detailed background concept (and algorithm), see the reference page of the
9
+ generalized parent class, SubObject, either at the
10
+ {Ruby Gems page}[http://rubygems.org/gems/sub_object]
11
+ or in
12
+ {Github}[https://github.com/masasakano/sub_object] .
13
+
14
+ The full package of this class is found also in
15
+ {SubString Ruby Gems page}[http://rubygems.org/gems/sub_string] and
16
+ in {Github}[https://github.com/masasakano/sub_string]
17
+
18
+
19
+ == Description
20
+
21
+ This class takes three parameters in the initialization: *source*, *pos*
22
+ (starting positional character-index), and *size* (of the substring of the original String *source*.
23
+
24
+ SubObject.new( source, position, size )
25
+
26
+ The constructed instance of this class keeps only these three pieces
27
+ of information (plus a hash value, strictly speaking), and hence uses negligible internal memory space on its own.
28
+ Note that only the information this instance needs for *source* is
29
+ +source.object_id+ or equivalent, which is really negligible in size.
30
+ In other words, this class does not create the copy of sub-Object from
31
+ the original source object as the Ruby default +String#[i,j]+ does.
32
+
33
+ Then, whenever it is referred to, it reconstructs the original
34
+ sub-String object:
35
+
36
+ source[pos, size]
37
+
38
+ and works exactly like *source*
39
+ ({duck-typing}[https://en.wikipedia.org/wiki/Duck_typing]).
40
+
41
+ As an example, the child class {SubString}[http://rubygems.org/gems/sub_string] (provided as a different Gem)
42
+ works as:
43
+
44
+ src = "abcdef"
45
+ ss = SubString.new(src, -4, 3) # => Similar to "abcdef"[-4,3]
46
+ print ss # => "cde" (STDOUT)
47
+ ss+'3p' # => "cde3p"
48
+ ss.upcase # => "CDE"
49
+ ss.sub(/^./, 'Q') # => "Qde"
50
+ ss.is_a?(String) # => true
51
+ "xy_"+ss # => "xy_cde"
52
+ "cde" == ss # => true
53
+
54
+ Internally the instance holds the source object. Therefore, as long
55
+ as the instance is alive, the source object is never garbage-collected (GC).
56
+
57
+ === Instance methods
58
+
59
+ The following is the instance methods of {SubObject}[http://rubygems.org/gems/sub_object] unique to this class.
60
+
61
+ +#source()+:: Returns the first argument (+source+ String) given in initialization. The returned value is dup-ped and **frozen**.
62
+ +#pos()+:: Returns the second argument (positional index) given in initialization.
63
+ +#subsize()+:: Returns the third argument (size) given in initialization. Equivalent to the method +#size+.
64
+ +#pos_size()+:: Returns the two-component array of +[pos, subsize]+
65
+ +#to_source()+:: Returns the instance projected with +to_src+
66
+
67
+ In addition, +#inspect+ is redefined.
68
+
69
+ Any public methods but destructive ones that are defined for the +source+ can
70
+ be applied to the instance of this class.
71
+
72
+ === Potential use
73
+
74
+ Each sub-String in Ruby like +String[i, j]+ or +String[i..j]+ takes up memory
75
+ according to the length of the sub-String. Consider an example:
76
+
77
+ src = "Some very extremely lengthy string.... (snipped)".
78
+ sub = src[1..-1]
79
+
80
+ The variable +sub+ uses up about the same memory of +src+.
81
+ If a great number of +sub+ is created and held alive, the total memory
82
+ used by the process can become quicly multifold, even by orders of magnitude.
83
+
84
+ This is where this class comes in handy. For example, a parsing
85
+ program applied to a huge text document with a complex grammar may
86
+ hold a number of such String variables. By using this class instead
87
+ of String, it can save some valuable memory.
88
+
89
+ === Warning about destructive methods to this instance or worse, to the source
90
+
91
+ If the source object has been destructively altered, such as with the
92
+ destructive method +clear+, the corresponding object of this class
93
+ is likely not to make sense any more. This class can detect such destructive
94
+ modification (using the method +Object#hash+) and issues a warning
95
+ whenever it is accessed after the source object has been destructively
96
+ altered, unless the appropriate global settings (see the next section)
97
+ are set to suppress it.
98
+
99
+ Similarly, because this class supplies an object with the filter +String#to_str+
100
+ when it receives any message (i.e., method),
101
+ it does not make sense to apply a destructive change on the instance of this class.
102
+ Therefore, whenever a destructive method (such as, +String#sub!+ and
103
+ +String#replace+ or even +String#force_encoding+) is applied to an
104
+ instance of this class, it
105
+ raises NoMethodError exception (or tries to with the best effort). The routine to identify the
106
+ destructive method relies thoroughly on the method name.
107
+ The methods ending with "!" are regarded as destructive and all the
108
+ built-in destructive methods of String (as of Ruby 2.6.5).
109
+
110
+ If a user library adds destructive methods with the name not ending
111
+ with "!" in the String class, you can register it in the inherited class constant
112
+ DESTRUCTIVE_METHODS so the instances of this class will
113
+ recognize them (Note that it is recommended to use a destructive
114
+ method for it, perhaps with one of +Array#<<+, +Array#append+, +Array#push+, +Array#concat+ etc).
115
+ The reference of the parent
116
+ {SubObject}[http://rubygems.org/gems/sub_object] class describes the
117
+ detail of the mechanism of how it works (if you are interested).
118
+
119
+ Note that if a (user-defined) desturctive method of String passes the
120
+ check by this class objects,
121
+ the result is most likely to be different from intended. It certainly
122
+ never alters this instance destructively, and
123
+ the returned value may be not like the expected value.
124
+
125
+ === Suppressing the warning
126
+
127
+ This class objects issues a warning in default every time it detects the *source*
128
+ String object has been destructively modified. My best advice is,
129
+ never alter the *source* object destructively! It makes no sense to
130
+ use this object in such a case.
131
+
132
+ However if you want to suppress the warning message, set the Ruby
133
+ global variable +$VERBOSE+ to nil. Alternatively, you can control it with
134
+ a class instance variable as
135
+
136
+ SubObject.verbose # => getter
137
+ SubObject.verbose=true # => setter
138
+
139
+ If it is set either TRUE or FALSE, this verbosity level has a
140
+ priority, regardless of the value of +$VERBOSE+.
141
+ In default it is nil and $VERBOSE is referred to.
142
+
143
+
144
+ == Install
145
+
146
+ This script requires {Ruby}[http://www.ruby-lang.org] Version 2.0
147
+ or above.
148
+
149
+ You can install it from the usual Ruby gem command.
150
+ Or, alternatively, download it and put the library file in one of your Ruby library search paths.
151
+
152
+ == Developer's note
153
+
154
+ The master of this README file is found in
155
+ {RubyGems/sub_object}[https://rubygems.org/gems/sub_object]
156
+
157
+ === Tests
158
+
159
+ Ruby codes under the directory <tt>test/</tt> are the test scripts.
160
+ You can run them from the top directory as <tt>ruby test/test_****.rb</tt>
161
+ or simply run <tt>make test</tt>.
162
+
163
+ == Known bugs and Todo items
164
+
165
+ * This class ignores any optional (keyword) parameters for the methods. It is due to the fact Ruby {BasicObject#method_missing}[https://ruby-doc.org/core-2.6.5/BasicObject.html#method-i-method_missing] does not take them into account as of Ruby-2.6.5. It may change in future versions of Ruby. As far as the Ruby built-in methods of String are concerned, it does not matter because none of them uses one. However, if String uses such user-defined methods, it may encounter a trouble.
166
+
167
+
168
+ == Copyright
169
+
170
+ Author:: Masa Sakano < info a_t wisebabel dot com >
171
+ Versions:: The versions of this package follow Semantic Versioning (2.0.0) http://semver.org/
172
+ License:: MIT
173
+
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
9
+
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'sub_object' # Gem: {SubObject}[http://rubygems.org/gems/sub_object]
4
+
5
+ # Child class of SubObject for String: SubString
6
+ #
7
+ # See for detail the full reference in the top page
8
+ # {SubString}[http://rubygems.org/gems/sub_string]
9
+ # and in {Github}[https://github.com/masasakano/sub_string]
10
+ # and also
11
+ # {SubObject}[http://rubygems.org/gems/sub_object]
12
+ # and that in {Github}[https://github.com/masasakano/sub_object]
13
+ #
14
+ # @author Masa Sakano (Wise Babel Ltd)
15
+ #
16
+ class SubString < SubObject
17
+ # Symbol of the method that projects to (returns) the original-like instance;
18
+ # e.g., :to_str for String. The value should be overwritten in the child class of SubObject.
19
+ TO_SOURCE_METHOD = :to_str
20
+ alias_method TO_SOURCE_METHOD, :to_source
21
+ end
22
+
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'rake'
4
+ require 'date'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sub_string}.sub(/.*/){|c| (c == File.basename(Dir.pwd)) ? c : raise("ERROR: s.name=(#{c}) in gemspec seems wrong!")}
8
+ s.version = "1.0"
9
+ # s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ # s.bindir = 'bin'
11
+ # %w(sub_string).each do |f|
12
+ # path = s.bindir+'/'+f
13
+ # File.executable?(path) ? s.executables << f : raise("ERROR: Executable (#{path}) is not executable!")
14
+ # end
15
+ s.authors = ["Masa Sakano"]
16
+ s.date = %q{2019-11-05}.sub(/.*/){|c| (Date.parse(c) == Date.today) ? c : raise("ERROR: s.date=(#{c}) is not today!")}
17
+ s.summary = %q{Duck-typed String class with negligible memory use}
18
+ s.description = %q{Class SubString that expresses Ruby sub-String but taking up negligible memory space, as its instance holds the positional information only. It behaves exactly like String (duck-typing), except destructive modification is prohibited. If the original string is destructively altered, warning is issued.}
19
+ # s.email = %q{abc@example.com}
20
+ s.extra_rdoc_files = [
21
+ #"LICENSE.txt",
22
+ "README.en.rdoc",
23
+ ]
24
+ s.license = 'MIT'
25
+ s.files = FileList['.gitignore','lib/**/*.rb','[A-Z]*','test/**/*.rb', '*.gemspec'].to_a.delete_if{ |f|
26
+ ret = false
27
+ arignore = IO.readlines('.gitignore')
28
+ arignore.map{|i| i.chomp}.each do |suffix|
29
+ if File.fnmatch(suffix, File.basename(f))
30
+ ret = true
31
+ break
32
+ end
33
+ end
34
+ ret
35
+ }
36
+ s.files.reject! { |fn| File.symlink? fn }
37
+
38
+ s.add_runtime_dependency 'sub_object', '>= 1.0'
39
+ # s.add_development_dependency "bourne", [">= 0"]
40
+ s.homepage = %q{https://www.wisebabel.com}
41
+ s.rdoc_options = ["--charset=UTF-8"]
42
+
43
+ # s.require_paths = ["lib"] # Default "lib"
44
+ s.required_ruby_version = '>= 2.0' # respond_to_missing?
45
+ s.test_files = Dir['test/**/*.rb']
46
+ s.test_files.reject! { |fn| File.symlink? fn }
47
+ # s.requirements << 'libmagick, v6.0' # Simply, info to users.
48
+ # s.rubygems_version = %q{1.3.5} # This is always set automatically!!
49
+
50
+ s.metadata["yard.run"] = "yri" # use "yard" to build full HTML docs.
51
+ end
52
+
@@ -0,0 +1,124 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # @author Masa Sakano (Wise Babel Ltd)
4
+
5
+ #require 'open3'
6
+ require 'sub_object'
7
+ require 'sub_string'
8
+
9
+ $stdout.sync=true
10
+ $stderr.sync=true
11
+ # print '$LOAD_PATH=';p $LOAD_PATH
12
+
13
+ #################################################
14
+ # Unit Test
15
+ #################################################
16
+
17
+ gem "minitest"
18
+ # require 'minitest/unit'
19
+ require 'minitest/autorun'
20
+
21
+ class TestUnitSubString < MiniTest::Test
22
+ T = true
23
+ F = false
24
+ SCFNAME = File.basename(__FILE__)
25
+ EXE = "%s/../bin/%s" % [File.dirname(__FILE__), File.basename(__FILE__).sub(/^test_(.+)\.rb/, '\1')]
26
+
27
+ def setup
28
+ end
29
+
30
+ def teardown
31
+ end
32
+
33
+ def test_sub_string01
34
+ assert_raises(TypeError){ SubString.new [9,8], -3, 2 }
35
+
36
+ str = 'abcdefghijklm'
37
+ obj = SubString.new str, -5, 2
38
+ assert_raises(TypeError){ SubString.new str, -3, :a }
39
+ assert_equal obj, "ij"
40
+ assert_equal "ij", obj
41
+ assert( "ij"==obj )
42
+ assert_operator obj, ">", "ii"
43
+ assert_operator "ii", "<", obj
44
+ assert( SubString.method_defined?(:to_str), "obj=#{obj.inspect}" )
45
+ assert( obj.methods.include? :to_str )
46
+ assert_equal 'ijKL', obj+"KL"
47
+ assert_equal "IJ", obj.upcase
48
+ assert obj.instance_of?(SubString)
49
+ refute obj.instance_of?(SubObject)
50
+ assert obj.respond_to?(:upcase), "upcase: to_source = (#{obj.to_source.inspect}); methods: #{obj.methods.sort.inspect}"
51
+ assert obj.respond_to?(:gsub), "to_source = (#{obj.to_source.inspect}); methods: #{obj.methods.sort.inspect}"
52
+ refute obj.respond_to?(:gsub!)
53
+ refute obj.respond_to?(:naiyo)
54
+ assert_raises(NoMethodError){ obj.push 5 }
55
+ assert_raises(NoMethodError){ obj.keep_if{} }
56
+ assert_raises(NoMethodError){ obj.sub! }
57
+ assert_raises(NoMethodError){ obj.replace }
58
+
59
+ mutex = Mutex.new
60
+ exclu = Thread.new {
61
+ mutex.synchronize {
62
+ org_verbose = $VERBOSE
63
+ assert_output('', ''){ _ = SubString.verbose }
64
+ begin
65
+ $VERBOSE = true
66
+ assert_nil SubString.verbose
67
+ SubString.verbose=nil;
68
+ assert_nil SubString.verbose
69
+ str.sub!(/^./){|c| c.upcase}
70
+ assert_output('', /destructively/){ obj.source }
71
+ $VERBOSE = false
72
+ assert_output('', /destructively/){ obj.source }
73
+ $VERBOSE = nil
74
+ assert_output('', ''){ obj.source }
75
+
76
+ SubString.verbose = true
77
+ assert_equal true, SubString.verbose
78
+ assert_equal true, SubString.instance_variable_get(:@verbosity)
79
+ assert_output('', /destructively/){ obj.source }
80
+ SubString.verbose = false
81
+ assert_equal false, SubString.verbose
82
+ assert_output('', ''){ obj.source }
83
+ $VERBOSE = true
84
+ assert_output('', ''){ obj.source }
85
+ SubString.verbose=nil;
86
+ assert_output('', /destructively/){ obj.source }
87
+
88
+ # Original String recovered, hence its hash value.
89
+ str.sub!(/^./){|c| c.downcase}
90
+ assert_output('', ''){ obj.source }
91
+ ensure
92
+ $VERBOSE = org_verbose
93
+ SubString.verbose=nil;
94
+ end
95
+ }
96
+ }
97
+ exclu.join
98
+ end
99
+
100
+ def test_sub_string02
101
+ str = 'abcdefghijklm'*20
102
+ obj = SubString.new str, 0, 120
103
+ str.upcase!
104
+ siz = nil
105
+ _, err = capture_io { siz = obj.size }
106
+ assert_equal '..."'+"\n", err[-5..-1]
107
+ assert_equal 60, (err.split(']')[-1].size-4)/10.0.round*10
108
+ assert_equal 120, siz
109
+ end
110
+
111
+ # As in README.en.rdoc
112
+ def test_sub_string03
113
+ src = "abcdef"
114
+ ss = SubString.new(src, -4, 3) # => Similar to "abcdef"[-4,3]
115
+ assert_operator "cde", '==', ss
116
+ assert_equal "cde", ss.to_s
117
+ assert_equal "cde3p", ss+'3p'
118
+ assert_equal "CDE", ss.upcase
119
+ assert_equal "Qde", ss.sub(/^./, 'Q')
120
+ assert ss.is_a?(String)
121
+ assert_equal "xy_cde", "xy_"+ss
122
+ end
123
+ end # class TestUnitSubString < MiniTest::Test
124
+
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sub_string
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Masa Sakano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sub_object
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ description: Class SubString that expresses Ruby sub-String but taking up negligible
28
+ memory space, as its instance holds the positional information only. It behaves
29
+ exactly like String (duck-typing), except destructive modification is prohibited. If
30
+ the original string is destructively altered, warning is issued.
31
+ email:
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - README.en.rdoc
36
+ files:
37
+ - ".gitignore"
38
+ - LICENSE.txt
39
+ - Makefile
40
+ - README.en.rdoc
41
+ - Rakefile
42
+ - lib/sub_string.rb
43
+ - sub_string.gemspec
44
+ - test/test_sub_string.rb
45
+ homepage: https://www.wisebabel.com
46
+ licenses:
47
+ - MIT
48
+ metadata:
49
+ yard.run: yri
50
+ post_install_message:
51
+ rdoc_options:
52
+ - "--charset=UTF-8"
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '2.0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubygems_version: 3.0.3
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: Duck-typed String class with negligible memory use
70
+ test_files:
71
+ - test/test_sub_string.rb