rmtools 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/History.txt +5 -0
  2. data/Manifest.txt +84 -26
  3. data/Rakefile +7 -4
  4. data/ext/extconf.rb +1 -1
  5. data/ext/rmtools.cpp +27 -12
  6. data/ext/rmtools.h +6 -5
  7. data/lib/rmtools/b.rb +18 -0
  8. data/lib/rmtools/console/coloring.rb +72 -0
  9. data/lib/rmtools/console/highlight.rb +13 -0
  10. data/lib/rmtools/{printing.rb → console/printing.rb} +17 -2
  11. data/lib/rmtools/console.rb +1 -0
  12. data/lib/rmtools/conversions/enum.rb +23 -0
  13. data/lib/rmtools/conversions/int.rb +47 -0
  14. data/lib/rmtools/conversions/string.rb +26 -0
  15. data/lib/rmtools/conversions.rb +1 -0
  16. data/lib/rmtools/core/arguments.rb +52 -0
  17. data/lib/rmtools/{boolean.rb → core/boolean.rb} +0 -13
  18. data/lib/rmtools/core/class.rb +41 -0
  19. data/lib/rmtools/core/js.rb +45 -0
  20. data/lib/rmtools/core/kernel.rb +28 -0
  21. data/lib/rmtools/{module.rb → core/module.rb} +0 -41
  22. data/lib/rmtools/core/numeric.rb +35 -0
  23. data/lib/rmtools/{object.rb → core/object.rb} +2 -23
  24. data/lib/rmtools/core/proc.rb +18 -0
  25. data/lib/rmtools/core/regexp.rb +11 -0
  26. data/lib/rmtools/core/string_compliance.rb +31 -0
  27. data/lib/rmtools/core.rb +1 -0
  28. data/lib/rmtools/db/active_record.rb +54 -0
  29. data/lib/rmtools/db.rb +7 -0
  30. data/lib/rmtools/debug/binding.rb +56 -0
  31. data/lib/rmtools/debug/highlight.rb +23 -0
  32. data/lib/rmtools/debug/logging.rb +176 -0
  33. data/lib/rmtools/debug/present.rb +38 -0
  34. data/lib/rmtools/debug/timer.rb +19 -0
  35. data/lib/rmtools/debug/traceback.rb +92 -0
  36. data/lib/rmtools/debug.rb +1 -0
  37. data/lib/rmtools/debug_notrace.rb +1 -0
  38. data/lib/rmtools/enumerable/array.rb +134 -0
  39. data/lib/rmtools/enumerable/array_iterators.rb +33 -0
  40. data/lib/rmtools/enumerable/common.rb +49 -0
  41. data/lib/rmtools/{hash.rb → enumerable/hash.rb} +8 -8
  42. data/lib/rmtools/enumerable/object_space.rb +19 -0
  43. data/lib/rmtools/enumerable/range.rb +201 -0
  44. data/lib/rmtools/enumerable.rb +1 -0
  45. data/lib/rmtools/experimental/blackhole.rb +12 -0
  46. data/lib/rmtools/experimental/deprecation.rb +36 -0
  47. data/lib/rmtools/experimental/dumps.rb +28 -0
  48. data/lib/rmtools/{numeric.rb → experimental/numeric.rb} +22 -51
  49. data/lib/rmtools/experimental/rails_backtrace.rb +29 -0
  50. data/lib/rmtools/experimental/string.rb +56 -0
  51. data/lib/rmtools/{tree.rb → experimental/tree.rb} +0 -0
  52. data/lib/rmtools/experimental.rb +1 -0
  53. data/lib/rmtools/fs/dir.rb +89 -0
  54. data/lib/rmtools/fs/file.rb +104 -0
  55. data/lib/rmtools/fs/io.rb +58 -0
  56. data/lib/rmtools/fs/tools.rb +49 -0
  57. data/lib/rmtools/fs.rb +1 -0
  58. data/lib/rmtools/functional/fold.rb +32 -0
  59. data/lib/rmtools/{string_to_proc.rb → functional/string_to_proc.rb} +2 -22
  60. data/lib/rmtools/functional/unfold.rb +16 -0
  61. data/lib/rmtools/functional.rb +1 -0
  62. data/lib/rmtools/ip/numeric.rb +35 -0
  63. data/lib/rmtools/ip/string.rb +45 -0
  64. data/lib/rmtools/ip.rb +1 -0
  65. data/lib/rmtools/lang/ansi.rb +17 -0
  66. data/lib/rmtools/lang/cyrillic.rb +106 -0
  67. data/lib/rmtools/lang/regexp.rb +8 -0
  68. data/lib/rmtools/lang/shortcuts.rb +20 -0
  69. data/lib/rmtools/lang.rb +1 -0
  70. data/lib/rmtools/rand/array.rb +39 -0
  71. data/lib/rmtools/rand/enum.rb +26 -0
  72. data/lib/rmtools/rand/range.rb +13 -0
  73. data/lib/rmtools/{random.rb → rand/string.rb} +13 -107
  74. data/lib/rmtools/rand.rb +1 -0
  75. data/lib/rmtools/require.rb +13 -0
  76. data/lib/rmtools/setup.rb +6 -5
  77. data/lib/rmtools/text/string_parse.rb +60 -0
  78. data/lib/rmtools/{stringscanner.rb → text/string_scanner.rb} +3 -2
  79. data/lib/rmtools/text/string_simple.rb +75 -0
  80. data/lib/rmtools/text/string_split.rb +148 -0
  81. data/lib/rmtools/text/textilize.rb +44 -0
  82. data/lib/rmtools/text.rb +1 -0
  83. data/lib/rmtools/time/global.rb +17 -0
  84. data/lib/rmtools/time/russian.rb +47 -0
  85. data/lib/rmtools/time.rb +1 -32
  86. data/lib/rmtools/xml/document.rb +28 -0
  87. data/lib/rmtools/xml/finders.rb +84 -0
  88. data/lib/rmtools/xml/libxml.rb +11 -0
  89. data/lib/rmtools/xml/node.rb +196 -0
  90. data/lib/rmtools/xml/string.rb +43 -0
  91. data/lib/rmtools/xml/xpath.rb +32 -0
  92. data/lib/rmtools/xml.rb +7 -0
  93. data/lib/rmtools.rb +8 -44
  94. metadata +97 -72
  95. data/lib/rmtools/arguments.rb +0 -24
  96. data/lib/rmtools/array.rb +0 -189
  97. data/lib/rmtools/binding.rb +0 -23
  98. data/lib/rmtools/coloring.rb +0 -82
  99. data/lib/rmtools/cyr-time.rb +0 -49
  100. data/lib/rmtools/cyrilic.rb +0 -124
  101. data/lib/rmtools/dumps.rb +0 -192
  102. data/lib/rmtools/enum.rb +0 -90
  103. data/lib/rmtools/io.rb +0 -303
  104. data/lib/rmtools/js.rb +0 -25
  105. data/lib/rmtools/limited_string.rb +0 -17
  106. data/lib/rmtools/logging.rb +0 -158
  107. data/lib/rmtools/proc.rb +0 -25
  108. data/lib/rmtools/range.rb +0 -100
  109. data/lib/rmtools/string.rb +0 -276
  110. 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
@@ -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
@@ -0,0 +1,13 @@
1
+ require_with_path 'rand/enum'
2
+
3
+ class Range
4
+
5
+ def rand
6
+ self.begin + Kernel.rand(size)
7
+ end
8
+
9
+ def randseg
10
+ (a = rand) > (b = rand) ? b..a : a..b
11
+ end
12
+
13
+ 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
- if RUBY_VERSION >= "1.8.7"
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
@@ -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
- Dir.chdir(File.expand_path File.dirname __FILE__) {require 'io'; require 'boolean'}
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}-\d/
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
- insalled = File.join(full_path, f)
14
- return unless File.file? insalled and Digest::SHA256.file(f) == Digest::SHA256.file(insalled)
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].kinda Integer
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
@@ -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