rmtools 1.3.3 → 2.0.0.rc5
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/.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
|