rmtools 1.0.0 → 1.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.
- data/History.txt +5 -0
- data/Manifest.txt +84 -26
- data/Rakefile +7 -4
- data/ext/extconf.rb +1 -1
- data/ext/rmtools.cpp +27 -12
- data/ext/rmtools.h +6 -5
- data/lib/rmtools/b.rb +18 -0
- data/lib/rmtools/console/coloring.rb +72 -0
- data/lib/rmtools/console/highlight.rb +13 -0
- data/lib/rmtools/{printing.rb → console/printing.rb} +17 -2
- data/lib/rmtools/console.rb +1 -0
- data/lib/rmtools/conversions/enum.rb +23 -0
- data/lib/rmtools/conversions/int.rb +47 -0
- data/lib/rmtools/conversions/string.rb +26 -0
- data/lib/rmtools/conversions.rb +1 -0
- data/lib/rmtools/core/arguments.rb +52 -0
- data/lib/rmtools/{boolean.rb → core/boolean.rb} +0 -13
- data/lib/rmtools/core/class.rb +41 -0
- data/lib/rmtools/core/js.rb +45 -0
- data/lib/rmtools/core/kernel.rb +28 -0
- data/lib/rmtools/{module.rb → core/module.rb} +0 -41
- data/lib/rmtools/core/numeric.rb +35 -0
- data/lib/rmtools/{object.rb → core/object.rb} +2 -23
- data/lib/rmtools/core/proc.rb +18 -0
- data/lib/rmtools/core/regexp.rb +11 -0
- data/lib/rmtools/core/string_compliance.rb +31 -0
- data/lib/rmtools/core.rb +1 -0
- data/lib/rmtools/db/active_record.rb +54 -0
- data/lib/rmtools/db.rb +7 -0
- data/lib/rmtools/debug/binding.rb +56 -0
- data/lib/rmtools/debug/highlight.rb +23 -0
- data/lib/rmtools/debug/logging.rb +176 -0
- data/lib/rmtools/debug/present.rb +38 -0
- data/lib/rmtools/debug/timer.rb +19 -0
- data/lib/rmtools/debug/traceback.rb +92 -0
- data/lib/rmtools/debug.rb +1 -0
- data/lib/rmtools/debug_notrace.rb +1 -0
- data/lib/rmtools/enumerable/array.rb +134 -0
- data/lib/rmtools/enumerable/array_iterators.rb +33 -0
- data/lib/rmtools/enumerable/common.rb +49 -0
- data/lib/rmtools/{hash.rb → enumerable/hash.rb} +8 -8
- data/lib/rmtools/enumerable/object_space.rb +19 -0
- data/lib/rmtools/enumerable/range.rb +201 -0
- data/lib/rmtools/enumerable.rb +1 -0
- data/lib/rmtools/experimental/blackhole.rb +12 -0
- data/lib/rmtools/experimental/deprecation.rb +36 -0
- data/lib/rmtools/experimental/dumps.rb +28 -0
- data/lib/rmtools/{numeric.rb → experimental/numeric.rb} +22 -51
- data/lib/rmtools/experimental/rails_backtrace.rb +29 -0
- data/lib/rmtools/experimental/string.rb +56 -0
- data/lib/rmtools/{tree.rb → experimental/tree.rb} +0 -0
- data/lib/rmtools/experimental.rb +1 -0
- data/lib/rmtools/fs/dir.rb +89 -0
- data/lib/rmtools/fs/file.rb +104 -0
- data/lib/rmtools/fs/io.rb +58 -0
- data/lib/rmtools/fs/tools.rb +49 -0
- data/lib/rmtools/fs.rb +1 -0
- data/lib/rmtools/functional/fold.rb +32 -0
- data/lib/rmtools/{string_to_proc.rb → functional/string_to_proc.rb} +2 -22
- data/lib/rmtools/functional/unfold.rb +16 -0
- data/lib/rmtools/functional.rb +1 -0
- data/lib/rmtools/ip/numeric.rb +35 -0
- data/lib/rmtools/ip/string.rb +45 -0
- data/lib/rmtools/ip.rb +1 -0
- data/lib/rmtools/lang/ansi.rb +17 -0
- data/lib/rmtools/lang/cyrillic.rb +106 -0
- data/lib/rmtools/lang/regexp.rb +8 -0
- data/lib/rmtools/lang/shortcuts.rb +20 -0
- data/lib/rmtools/lang.rb +1 -0
- data/lib/rmtools/rand/array.rb +39 -0
- data/lib/rmtools/rand/enum.rb +26 -0
- data/lib/rmtools/rand/range.rb +13 -0
- data/lib/rmtools/{random.rb → rand/string.rb} +13 -107
- data/lib/rmtools/rand.rb +1 -0
- data/lib/rmtools/require.rb +13 -0
- data/lib/rmtools/setup.rb +6 -5
- data/lib/rmtools/text/string_parse.rb +60 -0
- data/lib/rmtools/{stringscanner.rb → text/string_scanner.rb} +3 -2
- data/lib/rmtools/text/string_simple.rb +75 -0
- data/lib/rmtools/text/string_split.rb +148 -0
- data/lib/rmtools/text/textilize.rb +44 -0
- data/lib/rmtools/text.rb +1 -0
- data/lib/rmtools/time/global.rb +17 -0
- data/lib/rmtools/time/russian.rb +47 -0
- data/lib/rmtools/time.rb +1 -32
- data/lib/rmtools/xml/document.rb +28 -0
- data/lib/rmtools/xml/finders.rb +84 -0
- data/lib/rmtools/xml/libxml.rb +11 -0
- data/lib/rmtools/xml/node.rb +196 -0
- data/lib/rmtools/xml/string.rb +43 -0
- data/lib/rmtools/xml/xpath.rb +32 -0
- data/lib/rmtools/xml.rb +7 -0
- data/lib/rmtools.rb +8 -44
- metadata +97 -72
- data/lib/rmtools/arguments.rb +0 -24
- data/lib/rmtools/array.rb +0 -189
- data/lib/rmtools/binding.rb +0 -23
- data/lib/rmtools/coloring.rb +0 -82
- data/lib/rmtools/cyr-time.rb +0 -49
- data/lib/rmtools/cyrilic.rb +0 -124
- data/lib/rmtools/dumps.rb +0 -192
- data/lib/rmtools/enum.rb +0 -90
- data/lib/rmtools/io.rb +0 -303
- data/lib/rmtools/js.rb +0 -25
- data/lib/rmtools/limited_string.rb +0 -17
- data/lib/rmtools/logging.rb +0 -158
- data/lib/rmtools/proc.rb +0 -25
- data/lib/rmtools/range.rb +0 -100
- data/lib/rmtools/string.rb +0 -276
- data/lib/rmtools/traceback.rb +0 -106
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_with_path 'lang/ansi'
|
|
2
|
+
|
|
3
|
+
class String
|
|
4
|
+
# Actually, for short strings and 1251<->65001 it's much faster to use predefined ANSI2UTF and UTF2ANSI procs
|
|
5
|
+
def utf(from_encoding='WINDOWS-1251')
|
|
6
|
+
Iconv.new('UTF-8', from_encoding).iconv(self)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def utf!(from_encoding='WINDOWS-1251')
|
|
10
|
+
replace utf from_encoding
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ansi(from_encoding='UTF-8')
|
|
14
|
+
Iconv.new('WINDOWS-1251', from_encoding).iconv(self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ansi!(from_encoding='UTF-8')
|
|
18
|
+
replace ansi from_encoding
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/rmtools/lang.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_with_path __FILE__, '*'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_with_path 'rand/enum'
|
|
2
|
+
|
|
3
|
+
module RMTools
|
|
4
|
+
|
|
5
|
+
def randarr(len, &b)
|
|
6
|
+
a = (0...len).to_a.shuffle
|
|
7
|
+
block_given? ? a.map!(&b) : a
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module_function :randarr
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Array
|
|
14
|
+
|
|
15
|
+
def self.rand(len)
|
|
16
|
+
RMTools.randarr(len)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def rand!
|
|
20
|
+
delete_at Kernel.rand size
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def randdiv(int)
|
|
24
|
+
dup.randdiv!(int)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def randdiv!(int)
|
|
28
|
+
len = 2*int.to_i+1
|
|
29
|
+
return [self] if len <= 1
|
|
30
|
+
newarr = []
|
|
31
|
+
while size > 0
|
|
32
|
+
lenn = Kernel.rand(len)
|
|
33
|
+
next if lenn < 1
|
|
34
|
+
newarr << slice!(0, lenn)
|
|
35
|
+
end
|
|
36
|
+
newarr
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Enumerable
|
|
2
|
+
|
|
3
|
+
def rand
|
|
4
|
+
if block_given?
|
|
5
|
+
h, ua = {}, to_a.uniq
|
|
6
|
+
size = ua.size
|
|
7
|
+
loop {
|
|
8
|
+
i = Kernel.rand size
|
|
9
|
+
if h[i]
|
|
10
|
+
return if h.size == s
|
|
11
|
+
elsif yield(e = ua[i])
|
|
12
|
+
return e
|
|
13
|
+
else h[i] = true
|
|
14
|
+
end
|
|
15
|
+
}
|
|
16
|
+
else to_a[Kernel.rand(size)]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def randsample(qty=Kernel.rand(size))
|
|
21
|
+
a, b = [], to_a.dup
|
|
22
|
+
qty.times {a << b.rand!}
|
|
23
|
+
a
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
require_with_path 'lang/ansi'
|
|
3
|
+
|
|
2
4
|
module RMTools
|
|
3
5
|
begin
|
|
4
6
|
require 'securerandom'
|
|
@@ -11,8 +13,8 @@ module RMTools
|
|
|
11
13
|
Chars = 'abcdefghijklmnopqrstuvwxyz'
|
|
12
14
|
Alphanum = Chars + Chars.upcase + Numbers
|
|
13
15
|
ASCII_readable = '!"#$%&\'()*+,-./[\]^_`{|}~:;<=>?@ ' + Alphanum
|
|
14
|
-
RuChars = UTF2ANSI[RU_LETTERS[0]]
|
|
15
|
-
RuAlphanum = UTF2ANSI[RU_LETTERS.join] + Numbers
|
|
16
|
+
RuChars = UTF2ANSI[Cyrillic::RU_LETTERS[0]]
|
|
17
|
+
RuAlphanum = UTF2ANSI[Cyrillic::RU_LETTERS.join] + Numbers
|
|
16
18
|
|
|
17
19
|
def randstr(len=8, what=:alphanum)
|
|
18
20
|
s = ''
|
|
@@ -75,111 +77,7 @@ module RMTools
|
|
|
75
77
|
res[0...len]
|
|
76
78
|
end
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
def randarr(len, &b)
|
|
80
|
-
a = (0...len).to_a.shuffle
|
|
81
|
-
block_given? ? a.map!(&b) : a
|
|
82
|
-
end
|
|
83
|
-
else
|
|
84
|
-
def randarr(len, &b)
|
|
85
|
-
d = (0...len).to_a
|
|
86
|
-
a = Array.new(len) {d.rand!}
|
|
87
|
-
block_given? ? a.map!(&b) : a
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
module_function :randstr, :randarr
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
module Enumerable
|
|
95
|
-
|
|
96
|
-
def randsample(qty=Kernel.rand(size))
|
|
97
|
-
a, b = [], to_a.dup
|
|
98
|
-
qty.times {a << b.rand!}
|
|
99
|
-
a
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def rand(&cond)
|
|
103
|
-
if cond
|
|
104
|
-
a, b, s = Set.new, to_a, size
|
|
105
|
-
loop {
|
|
106
|
-
i = Kernel.rand s
|
|
107
|
-
if i.in a
|
|
108
|
-
return if a.size == s
|
|
109
|
-
elsif !cond[e = b[i]]
|
|
110
|
-
a << i
|
|
111
|
-
else return e
|
|
112
|
-
end
|
|
113
|
-
}
|
|
114
|
-
else to_a[Kernel.rand(size)]
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
class Range
|
|
121
|
-
|
|
122
|
-
def rand
|
|
123
|
-
self.begin + Kernel.rand(size)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def randseg
|
|
127
|
-
(a = rand) > (b = rand) ? b..a : a..b
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
class Array
|
|
133
|
-
|
|
134
|
-
def self.rand(len)
|
|
135
|
-
RMTools.randarr(len)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def rand(&cond)
|
|
139
|
-
if cond
|
|
140
|
-
a, s = [], size
|
|
141
|
-
loop {
|
|
142
|
-
i = Kernel.rand s
|
|
143
|
-
if i.in a
|
|
144
|
-
return if a.size == s
|
|
145
|
-
elsif !cond[e = self[i]]
|
|
146
|
-
a << i
|
|
147
|
-
else return e
|
|
148
|
-
end
|
|
149
|
-
}
|
|
150
|
-
else self[Kernel.rand(size)]
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def rand!
|
|
155
|
-
delete_at Kernel.rand size
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def randdiv(int)
|
|
159
|
-
len = 2*int.to_i+1
|
|
160
|
-
return [self] if len <= 1
|
|
161
|
-
newarr = []
|
|
162
|
-
arr = dup
|
|
163
|
-
while arr.size > 0
|
|
164
|
-
lenn = Kernel.rand(len)
|
|
165
|
-
next if lenn < 1
|
|
166
|
-
newarr << arr.slice!(0, lenn)
|
|
167
|
-
end
|
|
168
|
-
newarr
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def randdiv!(int)
|
|
172
|
-
len = 2*int.to_i+1
|
|
173
|
-
return [self] if len <= 1
|
|
174
|
-
newarr = []
|
|
175
|
-
while size > 0
|
|
176
|
-
lenn = Kernel.rand(len)
|
|
177
|
-
next if lenn < 1
|
|
178
|
-
newarr << slice!(0, lenn)
|
|
179
|
-
end
|
|
180
|
-
newarr
|
|
181
|
-
end
|
|
182
|
-
|
|
80
|
+
module_function :randstr
|
|
183
81
|
end
|
|
184
82
|
|
|
185
83
|
class String
|
|
@@ -191,5 +89,13 @@ class String
|
|
|
191
89
|
def rand(chsize=1)
|
|
192
90
|
self[Kernel.rand(size*chsize), chsize]
|
|
193
91
|
end
|
|
92
|
+
|
|
93
|
+
def randsubstr(chsize=1)
|
|
94
|
+
(a = Kernel.rand(size*chsize)) > (b = Kernel.rand(size*chsize)) ? self[b..a] : self[a..b]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def randsample(qty=Kernel.rand(size))
|
|
98
|
+
split('').randsample(qty)
|
|
99
|
+
end
|
|
194
100
|
|
|
195
101
|
end
|
data/lib/rmtools/rand.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_with_path __FILE__, '*'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
# ` require_with_path __FILE__, "*" ' requires all ruby files from dir named as file
|
|
3
|
+
# ` require_with_path "folder", "file" ' requires file.rb from dir `folder' in working directory
|
|
4
|
+
# `require_with_path "file" ' requires 'file.rb' from working directory
|
|
5
|
+
def require_with_path(location, mask=nil)
|
|
6
|
+
if !mask
|
|
7
|
+
location, mask = File.dirname(__FILE__), location # /path/to/gems/rmtools
|
|
8
|
+
end
|
|
9
|
+
mask += '.rb' unless mask['.']
|
|
10
|
+
location = File.expand_path(location).chomp('.rb')
|
|
11
|
+
Dir.glob(File.join location, mask) {|file| require file}
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/rmtools/setup.rb
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
|
|
2
|
+
require File.expand_path('require', File.dirname(__FILE__))
|
|
3
|
+
require_with_path 'fs'
|
|
3
4
|
require 'digest/md5'
|
|
4
5
|
|
|
5
|
-
def ext_files_not_modified(ext_name='rmtools')
|
|
6
|
+
def ext_files_not_modified(ext_name='rmtools', version='\d')
|
|
6
7
|
return unless name = Gem.source_index.gems.keys.find {|n|
|
|
7
|
-
n =~ /^#{ext_name}
|
|
8
|
+
n =~ /^#{ext_name}-#{version}/
|
|
8
9
|
}
|
|
9
10
|
gemspec = Gem.source_index.gems[name]
|
|
10
11
|
full_path = gemspec.full_gem_path
|
|
11
12
|
ext_files = gemspec.files.grep(/^ext\//)
|
|
12
13
|
ext_files.each {|f|
|
|
13
|
-
|
|
14
|
-
return unless File.file?
|
|
14
|
+
installed = File.join(full_path, f)
|
|
15
|
+
return unless File.file? installed and Digest::SHA256.file(f) == Digest::SHA256.file(installed)
|
|
15
16
|
}
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require_with_path 'conversions/string'
|
|
2
|
+
|
|
3
|
+
class String
|
|
4
|
+
CALLER_RE = %r{^\(?(.*?([^/\\]+?))#{ # ( path ( file ) )
|
|
5
|
+
}\)?:(\d+)(?::in #{ # :( line )[ :in
|
|
6
|
+
}[`<]([^'>]+)[>']#{ # `( closure )' ]
|
|
7
|
+
})?$}
|
|
8
|
+
URL_RE = %r{^((?:([^:]+)://)#{ # ( protocol
|
|
9
|
+
}([^/:]*(?::(\d+))?))?#{ # root[:port] )
|
|
10
|
+
}((/[^?#]*?(?:\.(\w+))?)#{ # ( path[.( fileext )]
|
|
11
|
+
}(?:\?(.*?))?)?#{ # [?( query params )] )
|
|
12
|
+
}(?:#(.+))?#{ # [ #( anchor ) ]
|
|
13
|
+
}$}
|
|
14
|
+
IP_RE = /\d+\.\d+\.\d+\.\d+(?::\d+)?/
|
|
15
|
+
IP_RANGE_RE = /(\d+\.\d+\.\d+\.\d+)\s*-\s*(\d+\.\d+\.\d+\.\d+)/
|
|
16
|
+
|
|
17
|
+
def parse(as)
|
|
18
|
+
case as
|
|
19
|
+
when :uri
|
|
20
|
+
m = match URL_RE
|
|
21
|
+
!m || m[0].empty? ?
|
|
22
|
+
{ 'href' => self } :
|
|
23
|
+
{ 'href' => self,
|
|
24
|
+
'root' => m[1],
|
|
25
|
+
'protocol' => m[2],
|
|
26
|
+
'host' => m[3],
|
|
27
|
+
'port' => m[4] ? m[4].to_i : 80,
|
|
28
|
+
'fullpath' => m[5] || '/',
|
|
29
|
+
'pathname' => m[5] || '/',
|
|
30
|
+
'path' => m[6] || '',
|
|
31
|
+
'ext' => m[7],
|
|
32
|
+
'query' => m[8] && m[8].to_hash(false),
|
|
33
|
+
'anchor' => m[9] }
|
|
34
|
+
when :caller
|
|
35
|
+
m = match CALLER_RE
|
|
36
|
+
m || m[0].empty? ? nil :
|
|
37
|
+
{ 'path' => m[1],
|
|
38
|
+
'file' => m[2],
|
|
39
|
+
'line' => m[3].to_i,
|
|
40
|
+
'func' => m[4],
|
|
41
|
+
'fullpath' =>
|
|
42
|
+
m[1]['('] ? m[1] :
|
|
43
|
+
File.expand_path(m[1]) }
|
|
44
|
+
when :ip; self[IP_RE]
|
|
45
|
+
when :ip_range; (m = match IP_RANGE_RE) && m[1]..m[2]
|
|
46
|
+
else raise ArgumentError, "Incorrect flag. Correct flags: :uri, :caller, :ip, :ip_range"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parseuri
|
|
51
|
+
deprecation "Use String#parse(:uri) instead."
|
|
52
|
+
parse :uri
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def parseip(range=nil)
|
|
56
|
+
deprecation "Use String#parse(:ip#{'_range' if range}) instead."
|
|
57
|
+
parse :"ip#{'_range' if range}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
require 'strscan'
|
|
2
3
|
|
|
3
4
|
class StringScanner
|
|
4
5
|
attr_reader :last
|
|
@@ -7,7 +8,7 @@ class StringScanner
|
|
|
7
8
|
@last = 0
|
|
8
9
|
res = scan_until re
|
|
9
10
|
if cbs
|
|
10
|
-
if cbs[0].
|
|
11
|
+
if cbs.keys.compact[0].is Fixnum
|
|
11
12
|
while res
|
|
12
13
|
if cb = cbs[matched.ord]
|
|
13
14
|
cb[self]
|
|
@@ -18,7 +19,7 @@ class StringScanner
|
|
|
18
19
|
end
|
|
19
20
|
else
|
|
20
21
|
while res
|
|
21
|
-
if cb = cbs.find {|pattern, proc| pattern.in matched}
|
|
22
|
+
if cb = cbs.find {|pattern, proc| pattern and pattern.in matched}
|
|
22
23
|
# patterns must be as explicit as possible
|
|
23
24
|
cb[1][self]
|
|
24
25
|
@last = pos
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
class String
|
|
2
|
+
if !method_defined? :/
|
|
3
|
+
alias :/ :split
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def inline
|
|
7
|
+
count("\n") == 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def lchomp(match)
|
|
11
|
+
if index(match) == 0
|
|
12
|
+
self[match.size..-1]
|
|
13
|
+
else
|
|
14
|
+
self.dup
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def lchomp!(match)
|
|
19
|
+
if index(match) == 0
|
|
20
|
+
self[0...match.size] = ''
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def until(splitter=$/)
|
|
26
|
+
split(splitter, 2)[0]
|
|
27
|
+
end
|
|
28
|
+
alias :till :until
|
|
29
|
+
|
|
30
|
+
def after(splitter=$/)
|
|
31
|
+
split(splitter, 2)[1]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# %{blah blah
|
|
35
|
+
# wall of text in the interpreter
|
|
36
|
+
# oh it's too bulky; may be we should
|
|
37
|
+
# save this text into variable
|
|
38
|
+
# blah blah} >> (str='')
|
|
39
|
+
# saved!
|
|
40
|
+
def >>(str)
|
|
41
|
+
str.replace(self + str)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Fast search for highlighting purposes
|
|
45
|
+
def find_with_offsets text, offset
|
|
46
|
+
index = index(text)
|
|
47
|
+
start = [0, index - offset].max
|
|
48
|
+
_end = index + text.size
|
|
49
|
+
[self[start...index], text, self[_end, offset]]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# 'filename.txt'.bump!.bump!
|
|
53
|
+
# => "filename.txt.2"
|
|
54
|
+
# 'filename.txt'.bump!.bump!.bump!('_')
|
|
55
|
+
# => "filename.txt.2_1"
|
|
56
|
+
# 'filename.txt'.bump!.bump!.bump!('_').bump!
|
|
57
|
+
# => "filename.txt.2_1.1"
|
|
58
|
+
def bump!(splt='.')
|
|
59
|
+
replace bump_version splt
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def bump_version(splt='.')
|
|
63
|
+
re = /(?:(\d*)#{Regexp.escape splt})?/
|
|
64
|
+
s = File.split self
|
|
65
|
+
s[0] == '.' ?
|
|
66
|
+
s[1].reverse.sub(re) {$1?"#{$1.to_i+1}#{splt}":"1#{splt}"}.reverse :
|
|
67
|
+
File.join(s[0], s[1].reverse.sub(re) {$1?"#{$1.to_i+1}#{splt}":"1#{splt}"}.reverse)
|
|
68
|
+
end
|
|
69
|
+
alias :next_version :bump_version
|
|
70
|
+
|
|
71
|
+
def to_re(esc=false)
|
|
72
|
+
Regexp.new(esc ? Regexp.escape(self) : self)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require_with_path 'text/string_simple'
|
|
2
|
+
require_with_path 'enumerable/array_iterators'
|
|
3
|
+
|
|
4
|
+
class String
|
|
5
|
+
|
|
6
|
+
def rsplit(splitter=$/, qty=0)
|
|
7
|
+
reverse.split(splitter, qty).reverse.reverses
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Same as split, but without :reject_splitter option keeps splitters on the left of parts
|
|
11
|
+
# with :report_headers option collects all regexp'ed splitters along with result array
|
|
12
|
+
def sharp_split(splitter, *args)
|
|
13
|
+
count, opts = args.fetch_opts [0, :flags], :include_splitter => true
|
|
14
|
+
if !opts[:report_headers] and opts[:include_splitter] and splitter.is Regexp
|
|
15
|
+
return split(/(?=#{splitter.source})/, count)
|
|
16
|
+
end
|
|
17
|
+
a = split(splitter, count)
|
|
18
|
+
return a if !opts[:include_splitter] and !opts[:report_headers]
|
|
19
|
+
skan = nil
|
|
20
|
+
case splitter
|
|
21
|
+
when String
|
|
22
|
+
skan = ([splitter]*a.size).unshift ''
|
|
23
|
+
a = (1...a.size).map {|i| splitter+a[i]}.unshift a[0] if opts[:include_splitter]
|
|
24
|
+
when Regexp
|
|
25
|
+
skan = scan(splitter).unshift ''
|
|
26
|
+
a = (0...a.size).map {|i| skan[i].to_s+a[i]} if opts[:include_splitter]
|
|
27
|
+
end
|
|
28
|
+
opts[:report_headers] ? [a, skan] : a
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Same as sharp_split, but without keeps splitters on the *right* of parts
|
|
32
|
+
def sharp_splitr(splitter, *args)
|
|
33
|
+
count, opts = args.fetch_opts [0, :flags], :include_splitter => true
|
|
34
|
+
a = split(splitter, count)
|
|
35
|
+
return a if !opts[:include_splitter] and !opts[:report_headers]
|
|
36
|
+
skan = nil
|
|
37
|
+
case splitter
|
|
38
|
+
when String
|
|
39
|
+
skan = [splitter]*a.size << ''
|
|
40
|
+
a = (0...a.size-1).map {|i| a[i]+splitter} << a[i] if opts[:include_splitter]
|
|
41
|
+
when Regexp
|
|
42
|
+
skan = scan(splitter) << ''
|
|
43
|
+
a = (0...a.size).map {|i| a[i]+skan[i]} if opts[:include_splitter]
|
|
44
|
+
end
|
|
45
|
+
opts[:report_headers] ? [a, skan] : a
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def div(len, *)
|
|
49
|
+
if !len.is Fixnum
|
|
50
|
+
deprecation "Use #sharp_split instead."
|
|
51
|
+
return sharp_split len
|
|
52
|
+
end
|
|
53
|
+
return [self] if len <= 0
|
|
54
|
+
str = dup
|
|
55
|
+
arr = []
|
|
56
|
+
while str.b
|
|
57
|
+
arr << str.slice!(0, len)
|
|
58
|
+
end
|
|
59
|
+
arr
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
def split_by_syntax(str, maxlen, buflen=0)
|
|
64
|
+
len, add = maxlen - buflen, nil
|
|
65
|
+
[/[^.?!]+\z/, /[^;]+\z/, /[^\n]+/, /\S+\z/, /[^。]+z/, /[^、]+z/].each {|t|
|
|
66
|
+
if !(add = str[t]) or add.size <= len
|
|
67
|
+
return add
|
|
68
|
+
end
|
|
69
|
+
}
|
|
70
|
+
add
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def sanitize_blocks!(blocks, opts)
|
|
74
|
+
blocks.reject! {|b| b.empty?} if opts[:no_blanks]
|
|
75
|
+
blocks.strips! if opts[:strips]
|
|
76
|
+
blocks.each {|b| raise Exception, "can't split string by #{terminator} to blocks with max length = #{maxlen}" if b.size > maxlen} if opts[:strict_overhead]
|
|
77
|
+
blocks
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
public
|
|
81
|
+
# Base smart-split method
|
|
82
|
+
# Keep in mind that cyrrilic in 1.8 is 2-byte long as method doesn't use cyrillic lib to not decrease speed.
|
|
83
|
+
def split_to_blocks(maxlen, *opts)
|
|
84
|
+
raise Exception, "Can't split text with maxlen = #{maxlen}" if maxlen < 1
|
|
85
|
+
return [self] if size <= maxlen
|
|
86
|
+
terminator, opts = opts.fetch_opts [nil, :flags], :strict_overhead => true, :no_blanks => true
|
|
87
|
+
blocks = []
|
|
88
|
+
term_re = /[^#{terminator}]+\z/ if terminator and terminator != :syntax
|
|
89
|
+
words, buf = split(opts[:strips] ? ' ' : / /), nil
|
|
90
|
+
while words.b or buf.b
|
|
91
|
+
if terminator and blocks.b and (
|
|
92
|
+
buf_add = if terminator == :syntax
|
|
93
|
+
split_by_syntax blocks[-1], maxlen, buf.size
|
|
94
|
+
else blocks[-1][term_re]
|
|
95
|
+
end.b)
|
|
96
|
+
if buf_add == blocks[-1]
|
|
97
|
+
blocks.pop
|
|
98
|
+
else blocks[-1] = blocks[-1][0...-buf_add.size]
|
|
99
|
+
end
|
|
100
|
+
buf = buf_add + buf
|
|
101
|
+
end
|
|
102
|
+
if blocks.size == opts[:lines]
|
|
103
|
+
return sanitize_blocks! blocks, opts
|
|
104
|
+
end
|
|
105
|
+
blocks << ''
|
|
106
|
+
if buf
|
|
107
|
+
blocks[-1] << buf
|
|
108
|
+
buf = nil
|
|
109
|
+
end
|
|
110
|
+
while words.b
|
|
111
|
+
buf = words.shift + ' '
|
|
112
|
+
break if blocks[-1].size + buf.size - 1 > maxlen
|
|
113
|
+
blocks[-1] << buf
|
|
114
|
+
buf = nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
sanitize_blocks! blocks, opts
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# 'An elegant way to factor duplication out of options passed to a series of method calls. Each method called in the block, with the block variable as the receiver, will have its options merged with the default options hash provided. '.cut_line 100
|
|
121
|
+
# => "An elegant way to factor duplication out of options passed to a series of method calls..."
|
|
122
|
+
def cut_line(maxlen, *opts)
|
|
123
|
+
terminator, opts = opts.fetch_opts [:syntax, :flags]
|
|
124
|
+
opts[:charsize] ||= RUBY_VERSION < '1.9' && a[0].cyr? ? 2 : 1
|
|
125
|
+
return self if size <= maxlen
|
|
126
|
+
maxlen -= 3
|
|
127
|
+
split_to_blocks(maxlen*opts[:charsize], terminator, :strips => true, :strict_overhead => false, :lines => 1)[0][0, maxlen].chomp('.') + '...'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# puts 'An elegant way to factor duplication out of options passed to a series of method calls. Each method called in the block, with the block variable as the receiver, will have its options merged with the default options hash provided. '.split_to_lines 50
|
|
131
|
+
# produces:
|
|
132
|
+
# An elegant way to factor duplication out of
|
|
133
|
+
# options passed to a series of method calls. Each
|
|
134
|
+
# method called in the block, with the block
|
|
135
|
+
# variable as the receiver, will have its options
|
|
136
|
+
# merged with the default options hash provided.
|
|
137
|
+
#
|
|
138
|
+
# This method is use cyrillic lib only to detect char byte-length
|
|
139
|
+
# options: charsize, no_blanks, strips
|
|
140
|
+
def split_to_lines(maxlen, *opts)
|
|
141
|
+
raise Exception, "Can't break text with maxlen = #{maxlen}" if maxlen < 1
|
|
142
|
+
opts = opts.fetch_opts :flags, :strips=>true
|
|
143
|
+
a = split("\n")
|
|
144
|
+
opts[:charsize] ||= RUBY_VERSION < '1.9' && a[0].cyr? ? 2 : 1
|
|
145
|
+
a.map {|string| string.strip.split_to_blocks(maxlen*opts[:charsize], opts.merge(:strict_overhead => false))}.flatten*"\n"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module RMTools
|
|
2
|
+
|
|
3
|
+
def dump_recurse(obj, depth, maxdepth)
|
|
4
|
+
res = ''
|
|
5
|
+
case obj
|
|
6
|
+
when Hash
|
|
7
|
+
if depth <= maxdepth
|
|
8
|
+
res = "{\n"
|
|
9
|
+
obj.each { |i, j|
|
|
10
|
+
i = i.inspect unless i.is_a? String
|
|
11
|
+
childinfo = dump_recurse(j,depth+1,maxdepth)
|
|
12
|
+
res << "%s %s => %s、\n"%[(" "*depth), i, childinfo]
|
|
13
|
+
}
|
|
14
|
+
res << " "*depth+" }"
|
|
15
|
+
else
|
|
16
|
+
res = obj.inspect
|
|
17
|
+
end
|
|
18
|
+
res
|
|
19
|
+
when Array
|
|
20
|
+
if depth <= maxdepth
|
|
21
|
+
res = "[\n"
|
|
22
|
+
obj.each_with_index { |j, i|
|
|
23
|
+
childinfo = dump_recurse(j,depth+1,maxdepth)
|
|
24
|
+
res << "%s %0*d: %s、\n"%[(" "*depth), (obj.size-1).to_s.size, i, childinfo]
|
|
25
|
+
}
|
|
26
|
+
res << " "*depth+" ]"
|
|
27
|
+
else
|
|
28
|
+
res = obj.inspect
|
|
29
|
+
end
|
|
30
|
+
res
|
|
31
|
+
when String then obj
|
|
32
|
+
else obj.inspect
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Enumerable
|
|
39
|
+
|
|
40
|
+
def dump(depth=5)
|
|
41
|
+
RMTools.dump_recurse self, 0, depth
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
data/lib/rmtools/text.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_with_path __FILE__, '*'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module RMTools
|
|
2
|
+
|
|
3
|
+
def puttime(ms=nil)
|
|
4
|
+
t = Time.now
|
|
5
|
+
if ms
|
|
6
|
+
t.strftime("%H:%M:%S")+sprintf(".%03d ", t.usec/1000)
|
|
7
|
+
else
|
|
8
|
+
t.strftime("%d.%m.%y %H:%M:%S ")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def putdate
|
|
13
|
+
Time.now.strftime("%d.%m.%y")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module_function :puttime, :putdate
|
|
17
|
+
end
|