utilrb 2.0.2.b2 → 2.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.boring +40 -0
- data/.gitignore +13 -0
- data/.travis.yml +5 -0
- data/CMakeLists.txt +18 -0
- data/Gemfile +3 -0
- data/Makefile +8 -0
- data/Manifest.txt +0 -8
- data/{README.rd → README.md} +11 -7
- data/Rakefile +16 -63
- data/benchmarks/validate_options.rb +79 -0
- data/ext/utilrb/extconf.rb +1 -17
- data/ext/utilrb/utilrb.cc +0 -23
- data/lib/utilrb/column_formatter.rb +8 -5
- data/lib/utilrb/common.rb +1 -6
- data/lib/utilrb/enumerable/uniq.rb +2 -8
- data/lib/utilrb/event_loop.rb +5 -10
- data/lib/utilrb/kernel/load_dsl_file.rb +1 -2
- data/lib/utilrb/kernel/options.rb +25 -29
- data/lib/utilrb/logger/hierarchy.rb +0 -1
- data/lib/utilrb/logger/io.rb +3 -3
- data/lib/utilrb/logger/root.rb +12 -6
- data/lib/utilrb/module/ancestor_p.rb +0 -12
- data/lib/utilrb/module/is_singleton.rb +6 -0
- data/lib/utilrb/module/singleton_class_p.rb +14 -0
- data/lib/utilrb/object/attribute.rb +33 -65
- data/lib/utilrb/object/singleton_class.rb +1 -20
- data/lib/utilrb/pkgconfig.rb +21 -10
- data/lib/utilrb/socket/tcp_server.rb +2 -2
- data/lib/utilrb/spawn.rb +1 -1
- data/lib/utilrb/test.rb +65 -0
- data/lib/utilrb/thread_pool.rb +11 -13
- data/lib/utilrb/timepoints.rb +15 -0
- data/lib/utilrb/value_set.rb +10 -1
- data/lib/utilrb/version.rb +4 -0
- data/lib/utilrb/weakref.rb +11 -12
- data/lib/utilrb/yard.rb +0 -111
- data/lib/utilrb.rb +6 -1
- data/lib/yard-utilrb.rb +1 -0
- data/manifest.xml +19 -0
- data/package.xml +29 -0
- data/utilrb.gemspec +27 -0
- metadata +56 -107
- data/ext/utilrb/proc.c +0 -39
- data/ext/utilrb/readline.c +0 -52
- data/ext/utilrb/weakref.cc +0 -143
- data/lib/utilrb/models/inherited_enumerable.rb +0 -341
- data/lib/utilrb/models/registration.rb +0 -115
- data/lib/utilrb/module/inherited_enumerable.rb +0 -6
- data/lib/utilrb/objectstats.rb +0 -193
- data/lib/utilrb/ruby_object_graph.rb +0 -384
- data/test/data/test_pkgconfig.pc +0 -9
- data/test/data/test_pkgconfig_empty.pc +0 -10
- data/test/test_array.rb +0 -15
- data/test/test_config.rb +0 -4
- data/test/test_dir.rb +0 -22
- data/test/test_enumerable.rb +0 -119
- data/test/test_event_loop.rb +0 -407
- data/test/test_exception.rb +0 -38
- data/test/test_gc.rb +0 -34
- data/test/test_hash.rb +0 -102
- data/test/test_kernel.rb +0 -300
- data/test/test_logger.rb +0 -204
- data/test/test_misc.rb +0 -42
- data/test/test_models.rb +0 -212
- data/test/test_module.rb +0 -126
- data/test/test_object.rb +0 -77
- data/test/test_objectstats.rb +0 -26
- data/test/test_pkgconfig.rb +0 -84
- data/test/test_proc.rb +0 -31
- data/test/test_set.rb +0 -19
- data/test/test_thread_pool.rb +0 -409
- data/test/test_time.rb +0 -47
- data/test/test_unbound_method.rb +0 -23
- data/test/test_weakref.rb +0 -81
@@ -1,341 +0,0 @@
|
|
1
|
-
require 'utilrb/object/attribute'
|
2
|
-
require 'utilrb/object/singleton_class'
|
3
|
-
require 'utilrb/enumerable/uniq'
|
4
|
-
require 'utilrb/module/include'
|
5
|
-
|
6
|
-
module Utilrb
|
7
|
-
module Models
|
8
|
-
# Helper method for inherited_enumerable
|
9
|
-
#
|
10
|
-
# It is called in the context of the singleton class of the module/class on
|
11
|
-
# which inherited_enumerable is called
|
12
|
-
def define_inherited_enumerable(name, attribute_name = name, options = Hash.new, &init) # :nodoc:
|
13
|
-
# Set up the attribute accessor
|
14
|
-
attribute(attribute_name, &init)
|
15
|
-
class_eval { private "#{attribute_name}=" }
|
16
|
-
|
17
|
-
promote = method_defined?("promote_#{name}")
|
18
|
-
options[:enum_with] ||= :each
|
19
|
-
|
20
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
21
|
-
def all_#{name}; each_#{name}.to_a end
|
22
|
-
def self_#{name}; @#{attribute_name} end
|
23
|
-
EOF
|
24
|
-
|
25
|
-
if options[:map]
|
26
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
27
|
-
def find_#{name}(key)
|
28
|
-
raise ArgumentError, "nil cannot be used as a key in find_#{name}" if !key
|
29
|
-
each_#{name}(key, true) do |value|
|
30
|
-
return value
|
31
|
-
end
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
def has_#{name}?(key)
|
35
|
-
ancestors = self.ancestors
|
36
|
-
if ancestors.first != self
|
37
|
-
ancestors.unshift self
|
38
|
-
end
|
39
|
-
for klass in ancestors
|
40
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
41
|
-
return true if klass.#{attribute_name}.has_key?(key)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
false
|
45
|
-
end
|
46
|
-
EOF
|
47
|
-
end
|
48
|
-
|
49
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
50
|
-
def clear_#{attribute_name}
|
51
|
-
#{attribute_name}.clear
|
52
|
-
for klass in ancestors
|
53
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
54
|
-
klass.#{attribute_name}.clear
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
EOF
|
59
|
-
|
60
|
-
if !promote
|
61
|
-
if options[:map]
|
62
|
-
define_inherited_enumerable_map_without_promotion(name, attribute_name, options)
|
63
|
-
else
|
64
|
-
define_inherited_enumerable_nomap_without_promotion(name, attribute_name, options)
|
65
|
-
end
|
66
|
-
else
|
67
|
-
if options[:map]
|
68
|
-
define_inherited_enumerable_map_with_promotion(name, attribute_name, options)
|
69
|
-
else
|
70
|
-
define_inherited_enumerable_nomap_with_promotion(name, attribute_name, options)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def define_inherited_enumerable_map_without_promotion(name, attribute_name, options)
|
76
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
77
|
-
def each_#{name}(key = nil, uniq = true)
|
78
|
-
if !block_given?
|
79
|
-
return enum_for(:each_#{name}, key, uniq)
|
80
|
-
end
|
81
|
-
|
82
|
-
ancestors = self.ancestors
|
83
|
-
if ancestors.first != self
|
84
|
-
ancestors.unshift self
|
85
|
-
end
|
86
|
-
if key
|
87
|
-
for klass in ancestors
|
88
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
89
|
-
if klass.#{attribute_name}.has_key?(key)
|
90
|
-
yield(klass.#{attribute_name}[key])
|
91
|
-
return self if uniq
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
elsif !uniq
|
96
|
-
for klass in ancestors
|
97
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
98
|
-
klass.#{attribute_name}.#{options[:enum_with]} do |el|
|
99
|
-
yield(el)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
else
|
104
|
-
seen = Set.new
|
105
|
-
for klass in ancestors
|
106
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
107
|
-
klass.#{attribute_name}.#{options[:enum_with]} do |el|
|
108
|
-
unless seen.include?(el.first)
|
109
|
-
seen << el.first
|
110
|
-
yield(el)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
end
|
117
|
-
self
|
118
|
-
end
|
119
|
-
EOF
|
120
|
-
end
|
121
|
-
|
122
|
-
def define_inherited_enumerable_nomap_without_promotion(name, attribute_name, options)
|
123
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
124
|
-
def each_#{name}
|
125
|
-
if !block_given?
|
126
|
-
return enum_for(:each_#{name})
|
127
|
-
end
|
128
|
-
|
129
|
-
ancestors = self.ancestors
|
130
|
-
if ancestors.first != self
|
131
|
-
ancestors.unshift self
|
132
|
-
end
|
133
|
-
for klass in ancestors
|
134
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
135
|
-
klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
|
136
|
-
end
|
137
|
-
end
|
138
|
-
self
|
139
|
-
end
|
140
|
-
EOF
|
141
|
-
end
|
142
|
-
|
143
|
-
def define_inherited_enumerable_map_with_promotion(name, attribute_name, options)
|
144
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
145
|
-
def each_#{name}(key = nil, uniq = true)
|
146
|
-
if !block_given?
|
147
|
-
return enum_for(:each_#{name}, key, uniq)
|
148
|
-
end
|
149
|
-
|
150
|
-
ancestors = self.ancestors
|
151
|
-
if ancestors.first != self
|
152
|
-
ancestors.unshift self
|
153
|
-
end
|
154
|
-
if key
|
155
|
-
promotions = []
|
156
|
-
for klass in ancestors
|
157
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
158
|
-
if klass.#{attribute_name}.has_key?(key)
|
159
|
-
value = klass.#{attribute_name}[key]
|
160
|
-
for p in promotions
|
161
|
-
value = p.promote_#{name}(key, value)
|
162
|
-
end
|
163
|
-
yield(value)
|
164
|
-
return self if uniq
|
165
|
-
end
|
166
|
-
end
|
167
|
-
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
168
|
-
end
|
169
|
-
elsif !uniq
|
170
|
-
promotions = []
|
171
|
-
for klass in ancestors
|
172
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
173
|
-
klass.#{attribute_name}.#{options[:enum_with]} do |key, value|
|
174
|
-
for p in promotions
|
175
|
-
value = p.promote_#{name}(key, value)
|
176
|
-
end
|
177
|
-
yield(key, value)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
181
|
-
end
|
182
|
-
else
|
183
|
-
seen = Set.new
|
184
|
-
promotions = []
|
185
|
-
for klass in ancestors
|
186
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
187
|
-
klass.#{attribute_name}.#{options[:enum_with]} do |key, value|
|
188
|
-
unless seen.include?(key)
|
189
|
-
for p in promotions
|
190
|
-
value = p.promote_#{name}(key, value)
|
191
|
-
end
|
192
|
-
seen << key
|
193
|
-
yield(key, value)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
198
|
-
end
|
199
|
-
end
|
200
|
-
self
|
201
|
-
end
|
202
|
-
EOF
|
203
|
-
end
|
204
|
-
|
205
|
-
def define_inherited_enumerable_nomap_with_promotion(name, attribute_name, options)
|
206
|
-
class_eval <<-EOF, __FILE__, __LINE__+1
|
207
|
-
def each_#{name}
|
208
|
-
if !block_given?
|
209
|
-
return enum_for(:each_#{name})
|
210
|
-
end
|
211
|
-
|
212
|
-
ancestors = self.ancestors
|
213
|
-
if ancestors.first != self
|
214
|
-
ancestors.unshift self
|
215
|
-
end
|
216
|
-
promotions = []
|
217
|
-
for klass in ancestors
|
218
|
-
if klass.instance_variable_defined?(:@#{attribute_name})
|
219
|
-
klass.#{attribute_name}.#{options[:enum_with]} do |value|
|
220
|
-
for p in promotions
|
221
|
-
value = p.promote_#{name}(value)
|
222
|
-
end
|
223
|
-
yield(value)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
227
|
-
end
|
228
|
-
self
|
229
|
-
end
|
230
|
-
EOF
|
231
|
-
end
|
232
|
-
|
233
|
-
# Defines an attribute as being enumerable in the class instance and in the
|
234
|
-
# whole class inheritance hierarchy. More specifically, it defines a
|
235
|
-
# <tt>each_#{name}(&iterator)</tt> instance method and a <tt>each_#{name}(&iterator)</tt>
|
236
|
-
# class method which iterates (in order) on
|
237
|
-
# - the instance #{name} attribute
|
238
|
-
# - the singleton class #{name} attribute
|
239
|
-
# - the class #{name} attribute
|
240
|
-
# - the superclass #{name} attribute
|
241
|
-
# - the superclass' superclass #{name} attribute
|
242
|
-
# ...
|
243
|
-
#
|
244
|
-
# This method can be used on modules, in which case the module is used as if
|
245
|
-
# it was part of the inheritance hierarchy.
|
246
|
-
#
|
247
|
-
# The +name+ option defines the enumeration method name (+value+ will
|
248
|
-
# define a +each_value+ method). +attribute_name+ defines the attribute
|
249
|
-
# name. +init+ is a block called to initialize the attribute.
|
250
|
-
# Valid options in +options+ are:
|
251
|
-
# map::
|
252
|
-
# If true, the attribute should respond to +[]+. In that case, the
|
253
|
-
# enumeration method is each_value(key = nil, uniq = false) If +key+ is
|
254
|
-
# given, we iterate on the values given by <tt>attribute[key]</tt>. If
|
255
|
-
# +uniq+ is true, the enumeration will yield at most one value for each
|
256
|
-
# +key+ found (so, if both +key+ and +uniq+ are given, the enumeration
|
257
|
-
# yields at most one value). See the examples below
|
258
|
-
# enum_with:: the enumeration method of the enumerable, if it is not +each+
|
259
|
-
#
|
260
|
-
# === Example
|
261
|
-
# Let's define some classes and look at the ancestor chain
|
262
|
-
#
|
263
|
-
# class A; end
|
264
|
-
# module M; end
|
265
|
-
# class B < A; include M end
|
266
|
-
# A.ancestors # => [A, Object, Kernel]
|
267
|
-
# B.ancestors # => [B, M, A, Object, Kernel]
|
268
|
-
#
|
269
|
-
# ==== Attributes for which 'map' is not set
|
270
|
-
#
|
271
|
-
# class A
|
272
|
-
# inherited_enumerable("value", "values") do
|
273
|
-
# Array.new
|
274
|
-
# end
|
275
|
-
# end
|
276
|
-
# module M
|
277
|
-
# inherited_enumerable("mod") do
|
278
|
-
# Array.new
|
279
|
-
# end
|
280
|
-
# end
|
281
|
-
#
|
282
|
-
# A.values << 1 # => [1]
|
283
|
-
# B.values << 2 # => [2]
|
284
|
-
# M.mod << 1 # => [1]
|
285
|
-
# b = B.new
|
286
|
-
# class << b
|
287
|
-
# self.values << 3 # => [3]
|
288
|
-
# self.mod << 4 # => [4]
|
289
|
-
# end
|
290
|
-
# M.mod << 2 # => [1, 2]
|
291
|
-
#
|
292
|
-
# A.enum_for(:each_value).to_a # => [1]
|
293
|
-
# B.enum_for(:each_value).to_a # => [2, 1]
|
294
|
-
# b.singleton_class.enum_for(:each_value).to_a # => [3, 2, 1]
|
295
|
-
# b.singleton_class.enum_for(:each_mod).to_a # => [4, 1, 2]
|
296
|
-
#
|
297
|
-
# ==== Attributes for which 'map' is set
|
298
|
-
#
|
299
|
-
# class A
|
300
|
-
# inherited_enumerable("mapped", "map", :map => true) do
|
301
|
-
# Hash.new { |h, k| h[k] = Array.new }
|
302
|
-
# end
|
303
|
-
# end
|
304
|
-
#
|
305
|
-
# A.map['name'] = 'A' # => "A"
|
306
|
-
# A.map['universe'] = 42
|
307
|
-
# B.map['name'] = 'B' # => "B"
|
308
|
-
# B.map['half_of_it'] = 21
|
309
|
-
#
|
310
|
-
# Let's see what happens if we don't specify the key option.
|
311
|
-
# A.enum_for(:each_mapped).to_a # => [["name", "A"], ["universe", 42]]
|
312
|
-
# If the +uniq+ option is set (the default), we see only B's value for 'name'
|
313
|
-
# B.enum_for(:each_mapped).to_a # => [["half_of_it", 21], ["name", "B"], ["universe", 42]]
|
314
|
-
# If the +uniq+ option is not set, we see both values for 'name'. Note that
|
315
|
-
# since 'map' is a Hash, the order of keys in one class is not guaranteed.
|
316
|
-
# Nonetheless, we have the guarantee that values from B appear before
|
317
|
-
# those from A
|
318
|
-
# B.enum_for(:each_mapped, nil, false).to_a # => [["half_of_it", 21], ["name", "B"], ["name", "A"], ["universe", 42]]
|
319
|
-
#
|
320
|
-
#
|
321
|
-
# Now, let's see how 'key' behaves
|
322
|
-
# A.enum_for(:each_mapped, 'name').to_a # => ["A"]
|
323
|
-
# B.enum_for(:each_mapped, 'name').to_a # => ["B"]
|
324
|
-
# B.enum_for(:each_mapped, 'name', false).to_a # => ["B", "A"]
|
325
|
-
#
|
326
|
-
def inherited_enumerable(name, attribute_name = name, options = Hash.new, &init)
|
327
|
-
singleton_class.class_eval { define_inherited_enumerable(name, attribute_name, options, &init) }
|
328
|
-
|
329
|
-
if is_a?(Module) && !is_a?(Class)
|
330
|
-
unless const_defined_here?(:ClassExtension)
|
331
|
-
const_set(:ClassExtension, Module.new)
|
332
|
-
end
|
333
|
-
class_extension = const_get(:ClassExtension)
|
334
|
-
class_extension.class_eval do
|
335
|
-
define_inherited_enumerable(name, attribute_name, options, &init)
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'facets/kernel/call_stack'
|
2
|
-
require 'utilrb/object/attribute'
|
3
|
-
require 'utilrb/module/attr_predicate'
|
4
|
-
module Utilrb
|
5
|
-
module Models
|
6
|
-
|
7
|
-
# Handling of registration of model hierarchies
|
8
|
-
#
|
9
|
-
# It depends on the mixed-in object to provide a #supermodel method that
|
10
|
-
# returns the model that is parent of +self+
|
11
|
-
module Registration
|
12
|
-
# The place where this model got defined in the source code
|
13
|
-
# The tuple is (file,lineno,method), and can be obtained with
|
14
|
-
# facet's #call_stack
|
15
|
-
# @return [Array<(String,Integer,Symbol)>]
|
16
|
-
attr_accessor :definition_location
|
17
|
-
|
18
|
-
# Tells {#clear_submodels} whether this model should be removed from
|
19
|
-
# the model set or not. The default is false (it should be removed)
|
20
|
-
#
|
21
|
-
# @return [Boolean]
|
22
|
-
attr_predicate :permanent_model?, true
|
23
|
-
|
24
|
-
# [ValueSet] the set of models that are children of this one
|
25
|
-
attribute(:submodels) { ValueSet.new }
|
26
|
-
|
27
|
-
# Returns the model that is parent of this one
|
28
|
-
#
|
29
|
-
# The default implementation returns superclass if it is extended by
|
30
|
-
# this Registration module, and nil otherwise
|
31
|
-
def supermodel
|
32
|
-
if superclass.respond_to?(:register_submodel)
|
33
|
-
superclass
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Call to register a model that is a submodel of +self+
|
38
|
-
def register_submodel(klass)
|
39
|
-
if !klass.definition_location
|
40
|
-
klass.definition_location = call_stack
|
41
|
-
end
|
42
|
-
|
43
|
-
if klass.name && !klass.permanent_model?
|
44
|
-
begin
|
45
|
-
if constant("::#{klass.name}") == klass
|
46
|
-
klass.permanent_model = true
|
47
|
-
end
|
48
|
-
rescue NameError
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
submodels << klass
|
53
|
-
if m = supermodel
|
54
|
-
m.register_submodel(klass)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Enumerates all models that are submodels of this class
|
59
|
-
def each_submodel
|
60
|
-
return enum_for(:each_submodel) if !block_given?
|
61
|
-
submodels.each do |obj|
|
62
|
-
yield(obj)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Clears all registered submodels
|
67
|
-
def clear_submodels
|
68
|
-
children = self.submodels.find_all { |m| !m.permanent_model? }
|
69
|
-
if !deregister_submodels(children)
|
70
|
-
return
|
71
|
-
end
|
72
|
-
|
73
|
-
# This contains the permanent submodels
|
74
|
-
#
|
75
|
-
# We can call #clear_submodels while iterating here as it is a
|
76
|
-
# constraint that all models in #submodels are permanent (and
|
77
|
-
# will therefore not be removed)
|
78
|
-
submodels.each { |m| m.clear_submodels }
|
79
|
-
# And this the non-permanent ones
|
80
|
-
children.each { |m| m.clear_submodels }
|
81
|
-
true
|
82
|
-
end
|
83
|
-
|
84
|
-
# Deregisters a set of submodels on this model and all its
|
85
|
-
# supermodels
|
86
|
-
#
|
87
|
-
# This is usually not called directly. Use #clear_submodels instead
|
88
|
-
#
|
89
|
-
# @param [ValueSet] set the set of submodels to remove
|
90
|
-
def deregister_submodels(set)
|
91
|
-
current_size = submodels.size
|
92
|
-
submodels.difference!(set.to_value_set)
|
93
|
-
if (submodels.size != current_size)
|
94
|
-
if m = supermodel
|
95
|
-
m.deregister_submodels(set)
|
96
|
-
end
|
97
|
-
true
|
98
|
-
else false
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Registers submodels when a subclass is created (when models are
|
103
|
-
# represented as classes)
|
104
|
-
def inherited(subclass)
|
105
|
-
subclass.definition_location = call_stack
|
106
|
-
super
|
107
|
-
register_submodel(subclass)
|
108
|
-
subclass.permanent_model = true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
|
-
|
data/lib/utilrb/objectstats.rb
DELETED
@@ -1,193 +0,0 @@
|
|
1
|
-
require 'utilrb/gc/force'
|
2
|
-
require 'utilrb/object/attribute'
|
3
|
-
require 'utilrb/column_formatter'
|
4
|
-
|
5
|
-
module ObjectStats
|
6
|
-
# The count of objects currently allocated
|
7
|
-
#
|
8
|
-
# It allocates no objects, which means that if
|
9
|
-
# a = ObjectStats.count
|
10
|
-
# b = ObjectStats.count
|
11
|
-
# then a == b
|
12
|
-
def self.count
|
13
|
-
count = 0
|
14
|
-
ObjectSpace.each_object { |obj| count += 1 }
|
15
|
-
|
16
|
-
count
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns a klass => count hash counting the currently allocated objects
|
20
|
-
#
|
21
|
-
# It allocates 1 Hash, which is included in the count
|
22
|
-
def self.count_by_class(threshold = nil)
|
23
|
-
by_class = Hash.new(0)
|
24
|
-
ObjectSpace.each_object { |obj|
|
25
|
-
by_class[obj.class] += 1
|
26
|
-
by_class
|
27
|
-
}
|
28
|
-
if threshold
|
29
|
-
by_class.delete_if { |kl, count| count < threshold }
|
30
|
-
end
|
31
|
-
|
32
|
-
by_class
|
33
|
-
end
|
34
|
-
|
35
|
-
LIVE_OBJECTS_KEY = :live_objects
|
36
|
-
|
37
|
-
# Profiles how much objects has been allocated by the block. Returns a
|
38
|
-
# klass => count hash like count_by_class
|
39
|
-
#
|
40
|
-
# If alive is true, then only live objects are returned.
|
41
|
-
def self.profile(alive = false)
|
42
|
-
if alive
|
43
|
-
GC.force
|
44
|
-
profile do
|
45
|
-
yield
|
46
|
-
GC.force
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
already_disabled = GC.disable
|
51
|
-
before = count_by_class
|
52
|
-
if ObjectSpace.respond_to?(:live_objects)
|
53
|
-
before_live_objects = ObjectSpace.live_objects
|
54
|
-
end
|
55
|
-
yield
|
56
|
-
if ObjectSpace.respond_to?(:live_objects)
|
57
|
-
after_live_objects = ObjectSpace.live_objects
|
58
|
-
end
|
59
|
-
after = count_by_class
|
60
|
-
if after_live_objects
|
61
|
-
before[LIVE_OBJECTS_KEY] = before_live_objects
|
62
|
-
after[LIVE_OBJECTS_KEY] = after_live_objects - 1 # correction for yield
|
63
|
-
end
|
64
|
-
GC.enable unless already_disabled
|
65
|
-
|
66
|
-
after[Hash] -= 1 # Correction for the call of count_by_class
|
67
|
-
profile = before.
|
68
|
-
merge(after) { |klass, old, new| new - old }.
|
69
|
-
delete_if { |klass, count| count == 0 }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# BenchmarkAllocation is a Benchmark-like interface to benchmark object allocation.
|
74
|
-
#
|
75
|
-
# == Formatting
|
76
|
-
# BenchmarkAllocation formats its output in two ways (see examples below)
|
77
|
-
# * first, each part of a class path is displayed in its own line, to reduce
|
78
|
-
# the output width
|
79
|
-
# * then, output is formatted so that it does not exceed
|
80
|
-
# BenchmarkAllocation::SCREEN_WIDTH width
|
81
|
-
#
|
82
|
-
#
|
83
|
-
# == Examples
|
84
|
-
#
|
85
|
-
# For instance,
|
86
|
-
#
|
87
|
-
# require 'utilrb/objectstats'
|
88
|
-
#
|
89
|
-
# module Namespace
|
90
|
-
# class MyClass
|
91
|
-
# end
|
92
|
-
# end
|
93
|
-
#
|
94
|
-
# BenchmarkAllocation.bm do |x|
|
95
|
-
# x.report("array") { Array.new }
|
96
|
-
# x.report("hash") { Hash.new }
|
97
|
-
# x.report("myclass") { MyClass.new }
|
98
|
-
# end
|
99
|
-
#
|
100
|
-
# will produce the output
|
101
|
-
#
|
102
|
-
# Array Hash Namespace::
|
103
|
-
# MyClass
|
104
|
-
# array 1 - -
|
105
|
-
# hash - 1 -
|
106
|
-
# myclass - - 1
|
107
|
-
#
|
108
|
-
# Like Benchmark, a rehearsal benchmark method, BenchmarkAllocation.bmbm
|
109
|
-
# is provided:
|
110
|
-
#
|
111
|
-
# require 'utilrb/objectstats'
|
112
|
-
# require 'delegate'
|
113
|
-
#
|
114
|
-
# module Namespace
|
115
|
-
# class MyClass
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# delegate_klass = nil
|
120
|
-
# BenchmarkAllocation.bmbm do |x|
|
121
|
-
# x.report("array") { Array.new }
|
122
|
-
# x.report("hash") { Hash.new }
|
123
|
-
# x.report("myclass") { Namespace::MyClass.new }
|
124
|
-
# x.report("delegate") do
|
125
|
-
# delegate_klass ||= Class.new(DelegateClass(Namespace::MyClass)) do
|
126
|
-
# def self.name; "Delegate(MyClass)" end
|
127
|
-
# end
|
128
|
-
# delegate_klass.new(Namespace::MyClass.new)
|
129
|
-
# end
|
130
|
-
# end
|
131
|
-
#
|
132
|
-
# produces
|
133
|
-
#
|
134
|
-
# Rehearsal --------------------------------------------------------------------------------
|
135
|
-
#
|
136
|
-
# Array Class Delegate(MyClass) Hash Namespace:: String
|
137
|
-
# MyClass
|
138
|
-
# array 1 - - - - -
|
139
|
-
# hash - - - 1 - -
|
140
|
-
# myclass - - - - 1 -
|
141
|
-
# delegate 5 2 1 2 1 247
|
142
|
-
# ------------------------------------------------------------------------------------------
|
143
|
-
#
|
144
|
-
# Array Delegate(MyClass) Hash Namespace::
|
145
|
-
# MyClass
|
146
|
-
# array 1 - - -
|
147
|
-
# hash - - 1 -
|
148
|
-
# myclass - - - 1
|
149
|
-
# delegate - 1 - 1
|
150
|
-
#
|
151
|
-
class BenchmarkAllocation
|
152
|
-
SCREEN_WIDTH = 90
|
153
|
-
MARGIN = 2
|
154
|
-
|
155
|
-
def self.bm(label_width = nil)
|
156
|
-
yield(gather = new)
|
157
|
-
gather.format
|
158
|
-
end
|
159
|
-
def self.bmbm(label_width = nil)
|
160
|
-
yield(gather = new)
|
161
|
-
|
162
|
-
title = "Rehearsal"
|
163
|
-
puts title + " " + "-" * (SCREEN_WIDTH - title.length - 1)
|
164
|
-
gather.format
|
165
|
-
puts "-" * SCREEN_WIDTH
|
166
|
-
|
167
|
-
yield(gather = new)
|
168
|
-
gather.format
|
169
|
-
end
|
170
|
-
|
171
|
-
def format(screen_width = SCREEN_WIDTH, margin = MARGIN)
|
172
|
-
data = profiles.map do |label, line_data|
|
173
|
-
line_data['label'] = label
|
174
|
-
line_data
|
175
|
-
end
|
176
|
-
ColumnFormatter.from_hashes(data, screen_width)
|
177
|
-
end
|
178
|
-
|
179
|
-
attribute(:profiles) { Array.new }
|
180
|
-
def report(label)
|
181
|
-
result = ObjectStats.profile do
|
182
|
-
yield
|
183
|
-
end
|
184
|
-
result.inject({}) do |result, (klass, count)|
|
185
|
-
klass = klass.to_s
|
186
|
-
klass = "unknown" if !klass || klass.empty?
|
187
|
-
result[klass] = count
|
188
|
-
result
|
189
|
-
end
|
190
|
-
profiles << [label, result]
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|