rmtools 1.3.3 → 2.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,6 +34,8 @@ module RMTools
34
34
  file.print = !format.q
35
35
  file.out = format.out || format.log_file
36
36
  file.color_out = format.color_out || format.color_log
37
+ file.detect_comments = !!format.detect_comments
38
+ file.precede_comments = format.precede_comments || "# "
37
39
 
38
40
  file.path_format = '%'.in file.out if file.out
39
41
  file.tf = format.time.to_a
@@ -44,21 +46,30 @@ module RMTools
44
46
  end
45
47
 
46
48
  def defaults
47
- puts %{ #common options:
49
+ puts %{ # #{@c.y 'common options:'}
48
50
  :q => false, # not print
49
51
  :out => false, # output to file, may contain strftime's %H%M%Y etc for filename
50
52
  :time => ["%H:%M:%S", "%03d"], # strftime, [msecs]
51
53
  :type => :console, # or :html
52
- #console values:
54
+ # #{@c.y 'console values:'}
53
55
  :caller => "#{@c.gray('%f:%l')} #{@c.red_bold(':%m')}", # "file:line :method", %p is for fullpath
54
56
  :format => "%time %mode [%caller]: %text" # format of entire log string, %mode is {#{%w(debug log info warn).map {|i| @highlight[i.to_sym]}*', '}}
55
- :color_out => false # do not clean control characters that make output to file colorful; set to true makes more readable output of `tail' but hardly readable by gui file output
56
- #html options:
57
+ :color_out => false, # do not clean control characters that make output to file colorful; set to true makes more readable output of `tail' but hardly readable by gui file output
58
+ :detect_comments => false, # highlight and strip comments blocks
59
+ :precede_comments => "# ",
60
+ # :detect_comments with default :precede_comments allows comment blocks looking like:
61
+ $log<<<<-'#'
62
+ # ... comment string one ...
63
+ # ... comment string two ...
64
+ #
65
+ be logged like #{@c.green "\n# ... comment string one ...\n# ... comment string two ..."}
66
+ # #{@c.y 'html options:'}
57
67
  :caller => "<a class='l'>%f:%l</a> <a class='m'>:%m</a>",
58
68
  :format => "<div class='line'><a class='t'>%time</a> <a class='%mode'>%mode</m> [%caller]: <p>%text</p>%att</div>", # %att is for array of objects that should be formatted by the next option
59
69
  :att =>"<div class='att'><div class='hide'>+</div><pre>%s</pre></div>", # .hide should be scripted to work like a spoiler
60
70
  :serializer => RMTools::RMLogger::HTML # should respond to :render(obj); nil value means each object will be just #inspect'ed}
61
71
  end
72
+ alias :usage :defaults
62
73
 
63
74
  # set any needed params, the rest will be set by default
64
75
  def set_format *args
@@ -95,6 +106,8 @@ module RMTools
95
106
  text = bind.report text
96
107
  elsif !text.is String
97
108
  text = text.inspect
109
+ elsif cfg.detect_comments and text =~ /\A[ \t]*#[ \t]+\S/
110
+ text = "\n" + @c.green(text.gsub(/^([ \t]*#[ \t])?/, cfg.precede_comments).chop)
98
111
  end
99
112
  out = cfg.out
100
113
  if cfg._time or cfg.path_format
@@ -7,7 +7,12 @@ module RMTools
7
7
  quiet, mute_warn = $quiet, $log.mute_warn
8
8
  $quiet = $log.mute_warn = true
9
9
  t1 = Time.now
10
- timez.times {yield} if timez > 0
10
+ begin
11
+ timez.times {yield} if timez > 0
12
+ rescue
13
+ $quiet, $log.mute_warn = quiet, mute_warn
14
+ raise $!
15
+ end
11
16
  res = yield
12
17
  t2 = Time.now
13
18
  ts.times {}
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  RMTools::require 'dev/highlight'
3
3
  RMTools::require 'dev/logging'
4
+ require 'active_support/core_ext/class/attribute'
4
5
 
5
6
  module RMTools
6
7
 
@@ -71,16 +72,46 @@ module RMTools
71
72
  module_function :format_trace, :format_trace_to_html
72
73
  end
73
74
 
74
- class Class
75
+ # As for rmtools-1.1.0, 1.9.1 may hung up processing IO while generating traceback
76
+ # As for 1.2.10 with 1.9.3 with readline support it isn't hung up anymore
77
+
78
+ # Usage with Rails.
79
+ # Rails raise and rescue a bunch of exceptions during a first load, a reload of code (e.g. in development env) and maybe even some exceptions for each request.
80
+ # Thus, trace_format should be set only in a console environment *after* a code is loaded.
81
+ # For a web-server environment use RMTools.format_trace for inspect backtrace *after* an exception was rescued.
82
+ # Also note that Rails' autoreload of code won't rewrite SCRIPT_LINES__.
83
+ class Exception
84
+ alias :set_bt :set_backtrace
85
+ class_attribute :trace_format
75
86
 
76
- private
77
- def trace_format method
78
- if Exception.in ancestors
79
- class_attribute :__trace_format
80
- self.__trace_format = method
81
- else
82
- raise NoMethodError, "undefined method `trace_format' for class #{self}"
87
+ # If you also set (e.g. in irbrc file)
88
+ # module Readline
89
+ # alias :orig_readline :readline
90
+ # def readline(*args)
91
+ # ln = orig_readline(*args)
92
+ # SCRIPT_LINES__['(irb)'] << "#{ln}\n"
93
+ # ln
94
+ # end
95
+ # end
96
+ # it will be possible to fetch lines entered in IRB
97
+ # else format_trace would only read ordinally require'd files
98
+ def set_backtrace src
99
+ if format = self.class.trace_format
100
+ src = RMTools.__send__ format, src
83
101
  end
102
+ set_bt src
103
+ end
104
+ end
105
+
106
+ # This is the most usable setting, I think. Set it in the irbrc, config/initializers or wherever
107
+ <<-'example'
108
+ if defined? IRB
109
+ class StandardError
110
+ self.trace_format = :format_trace
84
111
  end
85
112
 
86
- end
113
+ class SystemStackError
114
+ self.trace_format = nil
115
+ end
116
+ end
117
+ example
@@ -1,10 +1,10 @@
1
1
  # encoding: utf-8
2
- class BlackHole
2
+ class Void
3
3
  __init__
4
4
 
5
- # abc = BlackHole.new
5
+ # abc = Void.new
6
6
  # (abc.first.get {|_| !_}.something << 'blah blah')[123].raise!
7
- # => #<BlackHole:0xb66367b0>
7
+ # => #<Void:0xb66367b0>
8
8
  #
9
9
  # Think twice before use it. It may devour your code!
10
10
  def method_missing(m, *args)
@@ -21,4 +21,6 @@ class BlackHole
21
21
  if RUBY_VERSION < '1.9'
22
22
  undef id
23
23
  end
24
- end
24
+ end
25
+
26
+ BlackHole = Void
@@ -61,15 +61,20 @@ class Array
61
61
  end
62
62
 
63
63
  alias diff ^
64
+
65
+ def intersects?(ary)
66
+ (self & ary).any?
67
+ end
68
+ alias :x? :intersects?
64
69
 
65
70
  # arithmetics
66
71
  def avg
67
- sum.to_f/size
72
+ empty? ? 0 : sum.to_f/size
68
73
  end
69
74
 
70
75
  # for use with iterators
71
76
  def avg_by(&b)
72
- sum(&b).to_f/size
77
+ empty? ? 0 : sum(&b).to_f/size
73
78
  end
74
79
 
75
80
  def scale(top)
@@ -252,4 +257,9 @@ class Array
252
257
  nil
253
258
  end
254
259
 
260
+ # rightmost #uniq
261
+ def runiq
262
+ reverse.uniq.reverse
263
+ end
264
+
255
265
  end
@@ -1,57 +1,300 @@
1
1
  # encoding: utf-8
2
2
  RMTools::require 'enumerable/array'
3
3
 
4
- unless defined? RMTools::Iterators
4
+ # [1, 2, 3].to_ss # => ['1', '2', '3']
5
+ # [[1,2,3], [4,5,6], [7,8,9]].to_sss
6
+ # => [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]
7
+ # [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]].subss!(/\d+/) {|m| (m.to_i % 3).to_s}
8
+ # => [["1", "2", "0"], ["1", "2", "0"], ["1", "2", "0"]]
9
+ # [[1, 2, 0], [1, 2, 0], [1, 2, 0]].sum_zeros?
10
+ # => [false, false, true, false, false, true, false, false, true]
11
+ # [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by_odds?
12
+ # => [[1, 2, 3], [3, 4, 6]]
13
+ class Array
14
+ alias :throw_no :method_missing
15
+ mattr_reader :iterators_names, :iterators_pattern
16
+ @@iterators_names = []
5
17
 
6
- # [1, 2, 3].to_ss # => ['1', '2', '3']
7
- # [[1,2,3], [4,5,6], [7,8,9]].to_sss
8
- # => [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]
9
- # [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]].subss!(/\d+/) {|m| (m.to_i % 3).to_s}
10
- # => [["1", "2", "0"], ["1", "2", "0"], ["1", "2", "0"]]
11
- # [[1, 2, 0], [1, 2, 0], [1, 2, 0]].sum_zeros?
12
- # => [false, false, true, false, false, true, false, false, true]
13
- # [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by_odds?
14
- # => [[1, 2, 3], [3, 4, 6]]
15
- class Array
16
- alias :throw_no :method_missing
17
- RMTools::Iterators = %r{^(#{(instance_methods.grep(/_by$/)+%w{every no select reject partition find_all find sum foldr})*'|'})_([\w\d\_]+[!?]?)}
18
+ class << self
18
19
 
19
- def method_missing(method, *args, &block)
20
- if match = (meth = method.to_s).match(RMTools::Iterators)
21
- iterator, meth = match[1].to_sym, match[2].to_sym
22
- begin
23
- case iterator
24
- when :every then iterator = :every?
25
- when :no then iterator = :no?
20
+ def add_iterator_name(name_or_list)
21
+ name_or_list = [name_or_list] if !name_or_list.is Array
22
+ @@iterators_names |= name_or_list
23
+ @@iterators_pattern = %r{^(#{@@iterators_names*'|'})_([\w\d\_]+[!?]?)}
24
+ end
25
+
26
+ def fallback_to_clean_iterators!
27
+ class_eval do
28
+ # Benchmark 1:
29
+ # # We take a simple methods like uniq_by (O(N)) and odd? (O(1)) to ensure that
30
+ # # penalty we would have in production would not be larger than that in bench
31
+ #
32
+ # # 1.1. Traditional calls:
33
+ # # 1.1.1: (9 x #odd? + 3 x #map + 1 x #uniq_by) * 10^6
34
+ # timer(1_000_000) { [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by {|i| i.map {|j| j.odd?}} }
35
+ # one: 0.0130ms, total: 13040.0ms
36
+ # # 1.1.2: (90_000 x #odd? + 300 x #map + 1 x #uniq_by) * 100
37
+ # timer(100) { a.uniq_by {|i| i.map {|j| j.odd?}} }
38
+ # one: 34.0000ms, total: 3400.0ms
39
+ #
40
+ # # 1.2. Meta calls:
41
+ # # 1.2.1: += (13 * 10^6 x #__send__) + (4 * 10^6 x #method_missing)
42
+ # timer(1_000_000) { [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by_odds? }
43
+ # one: 0.0354ms, total: 35440.0ms
44
+ # # += 172% of time
45
+ # a = (0...300).to_a.map {Array.rand 300};
46
+ # # 1.2.2: += (9 * 10^6 x #__send__) + (30_100 x #method_missing)
47
+ # timer(100) { a.uniq_by_odds? }
48
+ # one: 39.3000ms, total: 3930.0ms
49
+ # # += 16% of time
50
+ #
51
+ # Conclusion:
52
+ #
53
+ # 1. If we want to speed meta-calls up, we should sacrifice cleanness of Array namespace,
54
+ # I mean define missing methods inplace.
55
+ # 2. Most latency is given by #method_missing, but which are factor of #__send__?
56
+ def method_missing(method, *args, &block)
57
+ if match = (meth = method.to_s).match(@@iterators_pattern)
58
+ iterator, meth = match[1].to_sym, match[2].to_sym
59
+
60
+ begin
61
+ case iterator
62
+ when :every then iterator = :every?
63
+ when :no then iterator = :no?
64
+ end
65
+
66
+ return case iterator
67
+ when :sum, :sort_along_by; __send__(iterator, args.shift) {|i| i.__send__ meth, *args, &block}
68
+ when :find_by, :select_by, :reject_by; __send__(iterator, meth, *args)
69
+ else __send__(iterator) {|i| i.__send__ meth, *args, &block}
70
+ end
71
+ rescue NoMethodError => e
72
+ e.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
73
+ raise e
74
+ end
75
+
76
+ elsif meth.sub!(/sses([=!?]?)$/, 'ss\1') or meth.sub!(/ies([=!?]?)$/, 'y\1') or meth.sub!(/s([=!?]?)$/, '\1')
77
+ assignment = meth =~ /=$/
78
+ meth = meth.to_sym
79
+
80
+ begin
81
+ if assignment
82
+ if Array === args
83
+ each_with_index {|e,i| e.__send__ meth, args[i]}
84
+ else
85
+ each {|e| e.__send__ meth, args}
86
+ end
87
+ else map {|e| e.__send__ meth, *args, &block}
88
+ end
89
+ rescue NoMethodError => e
90
+ e.message << " (`#{method}' interpreted as map-function `#{meth}')"
91
+ raise e
92
+ end
93
+
94
+ else
95
+ throw_no method
96
+ end
97
+ end
98
+ end # class_eval
99
+ end # def fallback_to_clean_iterators!
100
+
101
+ end # << self
102
+
103
+ add_iterator_name(instance_methods.grep(/_by$/)+%w{every no select reject partition find_all find sum foldr foldl fold count rand_by})
104
+
105
+ # Benchmark 2:
106
+ #
107
+ # # 2.2. Meta calls:
108
+ # # 2.2.1: += (13 * 10^6 x #__send__)
109
+ # timer(1_000_000) { [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by_odds? }
110
+ # one: 0.0156ms, total: 15570.0ms
111
+ # # += 19% of time
112
+ # a = (0...300).to_a.map {Array.rand 300};
113
+ # # 2.2.2: += (9 * 10^6 x #__send__)
114
+ # timer(100) { a.uniq_by_odds? }
115
+ # one: 37.9000ms, total: 3790.0ms
116
+ # # += 11% of time
117
+ <<-'version 2'
118
+ def method_missing(method, *args, &block)
119
+ if match = (meth = method.to_s).match(@@iterators_pattern)
120
+ iterator, meth = match[1].to_sym, match[2].to_sym
121
+ case iterator
122
+ when :every then iterator = :every?
123
+ when :no then iterator = :no?
124
+ end
125
+
126
+ Array.class_eval do
127
+ case iterator
128
+ when :sum, :sort_along_by
129
+ define_method method do |*args, &block|
130
+ begin
131
+ # sum_posts_ids([], :all) =>
132
+ # sum([]) {|e| e.posts_ids(:all)}
133
+ __send__(iterator, args.shift) {|i| e.__send__ meth, *args, &block}
134
+ rescue NoMethodError => err
135
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
136
+ raise err
137
+ end
138
+ end
139
+ when :find_by, :select_by, :reject_by
140
+ define_method method do |*args, &block|
141
+ begin
142
+ # select_by_count(max_count) =>
143
+ # select {|e| e.count == max_count}
144
+ __send__(iterator, meth, *args)
145
+ rescue NoMethodError => err
146
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
147
+ raise err
148
+ end
26
149
  end
27
- return case iterator
28
- when :sum; sum(args.shift) {|i| i.__send__ meth, *args, &block}
29
- when :find_by, :select_by, :reject_by; __send__(iterator, meth, *args)
30
- else __send__(iterator) {|i| i.__send__ meth, *args, &block}
150
+ else
151
+ define_method method do |*args, &block|
152
+ begin
153
+ # uniq_by_sum(1) {|i| 1 / i.weight} =>
154
+ # uniq_by {|e| e.sum(1) {|i| 1 / i .weight}}
155
+ __send__(iterator) {|e| e.__send__ meth, *args, &block}
156
+ rescue NoMethodError => err
157
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
158
+ raise err
31
159
  end
32
- rescue NoMethodError => e
33
- e.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
34
- raise e
160
+ end
35
161
  end
36
- elsif meth.sub!(/sses([=!?]?)$/, 'ss\1') or meth.sub!(/ies([=!?]?)$/, 'y\1') or meth.sub!(/s([=!?]?)$/, '\1')
37
- assignment = meth =~ /=$/
38
- meth = meth.to_sym
39
- begin
40
- if assignment
41
- if Array === args
42
- each_with_index {|e,i| e.__send__ meth, args[i]}
43
- else
44
- each {|e| e.__send__ meth, args}
162
+ end
163
+
164
+ elsif meth.sub!(/sses([=!?]?)$/, 'ss\1') or meth.sub!(/ies([=!?]?)$/, 'y\1') or meth.sub!(/s([=!?]?)$/, '\1')
165
+ assignment = meth =~ /=$/
166
+ meth = meth.to_sym
167
+
168
+ Array.class_eval do
169
+ if assignment
170
+ define_method method do |value|
171
+ begin
172
+ if Array === value
173
+ # owner_ids = users_ids =>
174
+ # each_with_index {|e, i| e.owner_id = users_ids[i]}
175
+ each_with_index {|e, i| e.__send__ meth, value[i]}
176
+ else
177
+ # owner_ids = user_id =>
178
+ # each {|e, i| e.owner_id = user_id}
179
+ each {|e| e.__send__ meth, value}
180
+ end
181
+ rescue NoMethodError => e
182
+ e.message << " (`#{method}' interpreted as map-function `#{meth}')"
183
+ raise e
184
+ end
185
+ end
186
+ else
187
+ define_method method do |*args, &block|
188
+ begin
189
+ # to_is(16) =>
190
+ # map {|e| e.to_i(16)}
191
+ map {|e| e.__send__ meth, *args, &block}
192
+ rescue NoMethodError => err
193
+ err.message << " (`#{method}' interpreted as map-function `#{meth}')"
194
+ raise err
45
195
  end
46
- else map {|e| e.__send__ meth, *args, &block}
47
196
  end
48
- rescue NoMethodError => e
49
- e.message << " (`#{method}' interpreted as map-function `#{meth}')"
50
- raise e
51
197
  end
52
- else throw_no method
53
- end
54
- end
198
+ end
199
+
200
+ else
201
+ return throw_no method
202
+ end
203
+
204
+ __send__(method, *args, &block)
55
205
  end
56
-
206
+ version 2
207
+
208
+ # Benchmark 3:
209
+ #
210
+ # # 3.2. Meta calls:
211
+ # # 3.2.1: += (13 * 10^6 x #__send__)
212
+ # timer(1_000_000) { [[1, 2, 3], [3, 4, 6], [3, 8, 0]].uniq_by_odds? }
213
+ # one: 0.0145ms, total: 14520.0ms
214
+ # # += 11% of time
215
+ # a = (0...300).to_a.map {Array.rand 300};
216
+ # # 3.2.2: += (9 * 10^6 x #__send__)
217
+ # timer(100) { a.uniq_by_odds? }
218
+ # one: 36.1000ms, total: 3610.0ms
219
+ # # += 6% of time
220
+ def method_missing(method, *args, &block)
221
+ if match = (meth = method.to_s).match(@@iterators_pattern)
222
+ iterator, meth = match[1].to_sym, match[2].to_sym
223
+ case iterator
224
+ when :every then iterator = :every?
225
+ when :no then iterator = :no?
226
+ end
227
+
228
+ case iterator
229
+ when :sum, :sort_along_by
230
+ Array.class_eval %{
231
+ def #{method}(*args, &block)
232
+ # sum_posts_ids([], :all) =>
233
+ # sum([]) {|e| e.posts_ids(:all)}
234
+ #{iterator}(args.shift) {|e| e.#{meth}(*args, &block)}
235
+ rescue NoMethodError => err
236
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
237
+ raise err
238
+ end}
239
+ when :find_by, :select_by, :reject_by
240
+ Array.class_eval %{
241
+ def #{method}(val)
242
+ # select_by_count(max_count) =>
243
+ # select {|e| e.count == max_count}
244
+ #{iterator.to_s[0...-3]} {|e| e.#{meth} == val}
245
+ rescue NoMethodError => err
246
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
247
+ raise err
248
+ end}
249
+ else
250
+ Array.class_eval %{
251
+ def #{method}(*args, &block)
252
+ # uniq_by_sum(1) {|i| 1 / i.weight} =>
253
+ # uniq_by {|e| e.sum(1) {|i| 1 / i .weight}}
254
+ #{iterator} {|e| e.#{meth}(*args, &block)}
255
+ rescue NoMethodError => err
256
+ err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
257
+ raise err
258
+ end}
259
+ end
260
+
261
+ elsif meth.sub!(/sses([=!?]?)$/, 'ss\1') or meth.sub!(/ies([=!?]?)$/, 'y\1') or meth.sub!(/s([=!?]?)$/, '\1')
262
+ assignment = meth =~ /=$/
263
+ meth = meth.to_sym
264
+
265
+ if assignment
266
+ Array.class_eval %{
267
+ def #{method}(value)
268
+ if Array === value
269
+ # owner_ids = users_ids =>
270
+ # each_with_index {|e, i| e.owner_id = users_ids[i]}
271
+ each_with_index {|e, i| e.__send__ meth, value[i]}
272
+ else
273
+ # owner_ids = user_id =>
274
+ # each {|e, i| e.owner_id = user_id}
275
+ each {|e| e.__send__ meth, value}
276
+ end
277
+ rescue NoMethodError => err
278
+ err.message << " (`#{method}' interpreted as map-function `#{meth}')"
279
+ raise err
280
+ end}
281
+ else
282
+ Array.class_eval %{
283
+ def #{method}(*args, &block)
284
+ # to_is(16) =>
285
+ # map {|e| e.to_i(16)}
286
+ map {|e| e.#{meth}(*args, &block)}
287
+ rescue NoMethodError => err
288
+ err.message << " (`#{method}' interpreted as map-function `#{meth}')"
289
+ raise err
290
+ end}
291
+ end
292
+
293
+ else
294
+ return throw_no method
295
+ end
296
+
297
+ __send__(method, *args, &block)
298
+ end
299
+
57
300
  end
@@ -5,6 +5,7 @@ RMTools::require 'enumerable/traversal'
5
5
  class Hash
6
6
  include RMTools::KeyValueTraversal
7
7
  alias :>> :reverse_merge!
8
+ alias :<< :merge!
8
9
 
9
10
  def to_traversable
10
11
  RMTools::KeyValueTraversable.new(self)