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.
- data/.gitignore +22 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -14
- data/README.md +75 -24
- data/Rakefile +1 -37
- data/lib/rmtools/{db/active_record.rb → active_record/base.rb} +7 -3
- data/lib/rmtools/active_record/declarative.rb +153 -0
- data/lib/rmtools/conversions/enum.rb +14 -2
- data/lib/rmtools/{b.rb → core/b.rb} +9 -2
- data/lib/rmtools/core/class.rb +7 -0
- data/lib/rmtools/core/js.rb +13 -10
- data/lib/rmtools/core/kernel.rb +5 -3
- data/lib/rmtools/core/proc.rb +12 -3
- data/lib/rmtools/core/symbol.rb +22 -0
- data/lib/rmtools/db.rb +1 -1
- data/lib/rmtools/dev/highlight.rb +1 -1
- data/lib/rmtools/dev/logging.rb +17 -4
- data/lib/rmtools/dev/timer.rb +6 -1
- data/lib/rmtools/dev/trace_format.rb +40 -9
- data/lib/rmtools/dev/{blackhole.rb → void.rb} +6 -4
- data/lib/rmtools/enumerable/array.rb +12 -2
- data/lib/rmtools/enumerable/array_iterators.rb +287 -44
- data/lib/rmtools/enumerable/hash.rb +1 -0
- data/lib/rmtools/enumerable/range.rb +128 -77
- data/lib/rmtools/enumerable/traversal.rb +2 -2
- data/lib/rmtools/functional/unfold.rb +6 -8
- data/lib/rmtools/rand/array.rb +16 -14
- data/lib/rmtools/time/{global.rb → helpers.rb} +0 -0
- data/lib/rmtools/version.rb +3 -0
- data/lib/rmtools.rb +10 -2
- data/lib/rmtools_dev.rb +3 -6
- data/rmtools.gemspec +28 -0
- metadata +103 -120
- data/Manifest.txt +0 -98
- data/lib/rmtools/dev/observing.rb +0 -115
- data/lib/rmtools/dev/traceback.rb +0 -41
- data/lib/rmtools/dev_min.rb +0 -2
- data/lib/rmtools/init.rb +0 -13
- data/lib/rmtools/ip.rb +0 -2
data/lib/rmtools/dev/logging.rb
CHANGED
@@ -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
|
-
#
|
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
|
data/lib/rmtools/dev/timer.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
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
|
2
|
+
class Void
|
3
3
|
__init__
|
4
4
|
|
5
|
-
# abc =
|
5
|
+
# abc = Void.new
|
6
6
|
# (abc.first.get {|_| !_}.something << 'blah blah')[123].raise!
|
7
|
-
# => #<
|
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
|
-
|
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
|
-
|
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
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
e.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
|
34
|
-
raise e
|
160
|
+
end
|
35
161
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|