utilrb 0.2
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/Changes.txt +20 -0
- data/License.txt +26 -0
- data/Manifest.txt +64 -0
- data/README.txt +44 -0
- data/Rakefile +46 -0
- data/bm/allocation.rb +7 -0
- data/bm/speed.rb +19 -0
- data/ext/extconf.rb +5 -0
- data/ext/faster.cc +48 -0
- data/ext/swap.cc +61 -0
- data/ext/value_set.cc +290 -0
- data/lib/utilrb.rb +2 -0
- data/lib/utilrb/array.rb +2 -0
- data/lib/utilrb/array/to_s.rb +16 -0
- data/lib/utilrb/common.rb +50 -0
- data/lib/utilrb/enumerable.rb +2 -0
- data/lib/utilrb/enumerable/null.rb +18 -0
- data/lib/utilrb/enumerable/random_element.rb +16 -0
- data/lib/utilrb/enumerable/sequence.rb +24 -0
- data/lib/utilrb/enumerable/uniq.rb +61 -0
- data/lib/utilrb/exception.rb +2 -0
- data/lib/utilrb/exception/full_message.rb +8 -0
- data/lib/utilrb/gc.rb +2 -0
- data/lib/utilrb/gc/force.rb +11 -0
- data/lib/utilrb/hash.rb +2 -0
- data/lib/utilrb/hash/slice.rb +6 -0
- data/lib/utilrb/hash/to_s.rb +6 -0
- data/lib/utilrb/hash/to_sym_keys.rb +6 -0
- data/lib/utilrb/kernel.rb +2 -0
- data/lib/utilrb/kernel/arity.rb +10 -0
- data/lib/utilrb/kernel/options.rb +69 -0
- data/lib/utilrb/kernel/poll.rb +24 -0
- data/lib/utilrb/kernel/require.rb +12 -0
- data/lib/utilrb/kernel/swap.rb +2 -0
- data/lib/utilrb/logger.rb +3 -0
- data/lib/utilrb/logger/forward.rb +15 -0
- data/lib/utilrb/logger/hierarchy.rb +34 -0
- data/lib/utilrb/module.rb +2 -0
- data/lib/utilrb/module/ancestor_p.rb +6 -0
- data/lib/utilrb/module/attr_enumerable.rb +28 -0
- data/lib/utilrb/module/define_method.rb +30 -0
- data/lib/utilrb/module/include.rb +30 -0
- data/lib/utilrb/module/inherited_enumerable.rb +122 -0
- data/lib/utilrb/object.rb +2 -0
- data/lib/utilrb/object/address.rb +14 -0
- data/lib/utilrb/object/attribute.rb +89 -0
- data/lib/utilrb/object/singleton_class.rb +53 -0
- data/lib/utilrb/objectstats.rb +52 -0
- data/lib/utilrb/time.rb +2 -0
- data/lib/utilrb/time/to_hms.rb +6 -0
- data/lib/utilrb/unbound_method.rb +2 -0
- data/lib/utilrb/unbound_method/call.rb +5 -0
- data/lib/utilrb/value_set.rb +17 -0
- data/test/test_array.rb +9 -0
- data/test/test_config.rb +4 -0
- data/test/test_enumerable.rb +89 -0
- data/test/test_gc.rb +39 -0
- data/test/test_hash.rb +22 -0
- data/test/test_kernel.rb +70 -0
- data/test/test_misc.rb +42 -0
- data/test/test_module.rb +127 -0
- data/test/test_object.rb +65 -0
- data/test/test_objectstats.rb +19 -0
- data/test/test_unbound_method.rb +23 -0
- metadata +128 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'utilrb/object/attribute'
|
2
|
+
|
3
|
+
class Module
|
4
|
+
# Define 'name' to be a read-only enumerable attribute. The method
|
5
|
+
# defines a +attr_name+ read-only attribute and an enumerator method
|
6
|
+
# each_#{name}. +init_block+ is used to initialize the attribute.
|
7
|
+
#
|
8
|
+
# The enumerator method accepts a +key+ argument. If the attribute is
|
9
|
+
# a key => enumerable map, then the +key+ attribute can be used to iterate
|
10
|
+
#
|
11
|
+
# +enumerator+ is the name of the enumeration method
|
12
|
+
def attr_enumerable(name, attr_name = name, enumerator = :each, &init_block)
|
13
|
+
class_eval do
|
14
|
+
attribute(attr_name, &init_block)
|
15
|
+
end
|
16
|
+
class_eval <<-EOF
|
17
|
+
def each_#{name}(key = nil, &iterator)
|
18
|
+
return unless #{attr_name}
|
19
|
+
if key
|
20
|
+
#{attr_name}[key].#{enumerator}(&iterator)
|
21
|
+
else
|
22
|
+
#{attr_name}.#{enumerator}(&iterator)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
EOF
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Module
|
2
|
+
# Emulate block-passing by converting the block into a Proc object
|
3
|
+
# and passing it to the given block as last argument
|
4
|
+
# dule)
|
5
|
+
#
|
6
|
+
# For instance
|
7
|
+
# define_method('my_method') do |a, &block|
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Is written as
|
11
|
+
# define_method_with_block('my_method') do |block, a|
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# The block is given first to allow the following construct:
|
15
|
+
#
|
16
|
+
# define_method_with_block('my_method') do |block, *args|
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# +block+ is +nil+ if no block is given on the method call
|
20
|
+
#
|
21
|
+
def define_method_with_block(name, &mdef)
|
22
|
+
class_eval <<-EOD
|
23
|
+
def #{name}(*args, &block)
|
24
|
+
dmwb_#{name}_user_definition(block, *args)
|
25
|
+
end
|
26
|
+
EOD
|
27
|
+
define_method("dmwb_#{name}_user_definition", &mdef)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Module
|
2
|
+
unless (method(:__instance_include__) rescue nil)
|
3
|
+
alias :__instance_include__ :include
|
4
|
+
end
|
5
|
+
|
6
|
+
# Includes a module in this one, with support for class extensions
|
7
|
+
#
|
8
|
+
# If a module defines a ClassExtension submodule, then
|
9
|
+
# * if it is included in a module, the target's ClassExtension
|
10
|
+
# module includes the source ClassExtension (and if there is no
|
11
|
+
# ClassExtension in the target, it is created)
|
12
|
+
# * if it is included in a Class, the ClassExtension module
|
13
|
+
# extends the class.
|
14
|
+
def include(mod)
|
15
|
+
__instance_include__ mod
|
16
|
+
return unless mod.const_defined?(:ClassExtension)
|
17
|
+
|
18
|
+
if is_a?(Module) && !is_a?(Class)
|
19
|
+
unless const_defined?(:ClassExtension)
|
20
|
+
const_set(:ClassExtension, Module.new)
|
21
|
+
end
|
22
|
+
const_get(:ClassExtension).class_eval do
|
23
|
+
__instance_include__ mod.const_get(:ClassExtension)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
extend mod.const_get(:ClassExtension)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'utilrb/object/attribute'
|
2
|
+
require 'utilrb/object/singleton_class'
|
3
|
+
require 'utilrb/enumerable/uniq'
|
4
|
+
require 'utilrb/module/include'
|
5
|
+
|
6
|
+
class Module
|
7
|
+
def define_inherited_enumerable(name, attribute_name = name, options = Hash.new, &init) # :nodoc:
|
8
|
+
# Set up the attribute accessor
|
9
|
+
attribute(attribute_name, &init)
|
10
|
+
class_eval { private "#{attribute_name}=" }
|
11
|
+
|
12
|
+
options[:enum_with] ||= :each
|
13
|
+
|
14
|
+
if options[:map]
|
15
|
+
class_eval <<-EOF
|
16
|
+
def each_#{name}(key = nil, uniq = true)
|
17
|
+
if key
|
18
|
+
if #{attribute_name}.has_key?(key)
|
19
|
+
yield(#{attribute_name}[key])
|
20
|
+
return self if uniq
|
21
|
+
end
|
22
|
+
elsif uniq
|
23
|
+
@enum_#{name}_uniq ||= enum_uniq(:each_#{name}, nil, false) { |k, v| k }
|
24
|
+
@enum_#{name}_uniq.each { |el| yield(el) }
|
25
|
+
return self
|
26
|
+
else
|
27
|
+
#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
|
28
|
+
end
|
29
|
+
if superclass = ancestors[1..-1].find { |k| k.respond_to?(:each_#{name}) }
|
30
|
+
superclass.each_#{name}(key, uniq) { |el| yield(el) }
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
def has_#{name}?(key)
|
35
|
+
return true if #{attribute_name}[key]
|
36
|
+
if superclass = ancestors[1..-1].find { |k| k.respond_to?(:has_#{name}) }
|
37
|
+
superclass.has_#{name}(key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
EOF
|
41
|
+
else
|
42
|
+
class_eval <<-EOF
|
43
|
+
def each_#{name}(&iterator)
|
44
|
+
#{attribute_name}.#{options[:enum_with]}(&iterator) if #{attribute_name}
|
45
|
+
if superclass = ancestors[1..-1].find { |k| k.respond_to?(:each_#{name}) }
|
46
|
+
superclass.each_#{name} { |el| yield(el) }
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
EOF
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Defines an attribute as being enumerable in the class instance and in the
|
55
|
+
# whole class inheritance hierarchy. More specifically, it defines a
|
56
|
+
# <tt>each_#{name}(&iterator)</tt> instance method and a <tt>each_#{name}(&iterator)</tt>
|
57
|
+
# class method which iterates (in order) on
|
58
|
+
# - the instance #{name} attribute
|
59
|
+
# - the singleton class #{name} attribute
|
60
|
+
# - the class #{name} attribute
|
61
|
+
# - the superclass #{name} attribute
|
62
|
+
# - the superclass' superclass #{name} attribute
|
63
|
+
# ...
|
64
|
+
#
|
65
|
+
# This method can be used on modules, in which case the module is used as if
|
66
|
+
# it was part of the inheritance hierarchy.
|
67
|
+
#
|
68
|
+
# The +name+ option defines the enumeration method name (+value+ will
|
69
|
+
# define a +each_value+ method). +attribute_name+ defines the attribute
|
70
|
+
# name. +init+ is a block called to initialize the attribute.
|
71
|
+
# Valid options in +options+ are:
|
72
|
+
# map:: the attribute should respond to +[]+. The enumeration method takes two
|
73
|
+
# arguments, +key+ and +uniq+. If +key+ is given, we iterate on the values
|
74
|
+
# given by <tt>attribute[key]</tt>. If +uniq+ is true, the enumeration will
|
75
|
+
# yield at most one value for each +key+ found (so, if both +key+ and +uniq+ are
|
76
|
+
# given, the enumeration yields at most one value)
|
77
|
+
#
|
78
|
+
# For instance
|
79
|
+
#
|
80
|
+
# class A
|
81
|
+
# class_inherited_enumerable("value", "enum") { Array.new }
|
82
|
+
# end
|
83
|
+
# module M
|
84
|
+
# inherited_enumerable("attr") { Array.new }
|
85
|
+
# end
|
86
|
+
# class B < A
|
87
|
+
# include M
|
88
|
+
# end
|
89
|
+
# b = B.new
|
90
|
+
#
|
91
|
+
# A.enum << 1
|
92
|
+
# B.enum << 2
|
93
|
+
# M.attr << 1
|
94
|
+
# class << b
|
95
|
+
# enum << 3
|
96
|
+
# attr << 4
|
97
|
+
# end
|
98
|
+
# M.attr << 2
|
99
|
+
#
|
100
|
+
# A.each_enum => 1
|
101
|
+
# B.each_enum => 2, 1
|
102
|
+
# b.singleton_class.each_enum => 3, 2, 1
|
103
|
+
# b.singleton_class.each_attr => 4, 1, 2
|
104
|
+
#
|
105
|
+
def inherited_enumerable(name, attribute_name = name, options = Hash.new, &init)
|
106
|
+
singleton_class.class_eval { define_inherited_enumerable(name, attribute_name, options, &init) }
|
107
|
+
|
108
|
+
if is_a?(Module) && !is_a?(Class)
|
109
|
+
unless const_defined?(:ClassExtension)
|
110
|
+
const_set(:ClassExtension, Module.new)
|
111
|
+
end
|
112
|
+
class_extension = const_get(:ClassExtension)
|
113
|
+
class_extension.class_eval do
|
114
|
+
define_inherited_enumerable(name, attribute_name, options, &init)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
class Object
|
3
|
+
# Return the object address (for non immediate
|
4
|
+
# objects).
|
5
|
+
def address; Object.address_from_id(object_id) end
|
6
|
+
|
7
|
+
# Converts the object_id of a non-immediate object
|
8
|
+
# to its 32 bits address
|
9
|
+
def self.address_from_id(id)
|
10
|
+
id = 0xFFFFFFFF - ~id if id < 0
|
11
|
+
(id * 2) & 0xFFFFFFFF
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'utilrb/common'
|
2
|
+
require 'utilrb/object/singleton_class'
|
3
|
+
|
4
|
+
Utilrb.unless_faster do
|
5
|
+
class Object
|
6
|
+
# :call-seq
|
7
|
+
# attribute :name => default_value
|
8
|
+
# attribute(:name) { default_value }
|
9
|
+
#
|
10
|
+
# In the first form, defines a read-write attribute
|
11
|
+
# named 'name' with default_value for default value.
|
12
|
+
# In the second form, the block is called if the attribute
|
13
|
+
# is read before it has been ever written, and its return
|
14
|
+
# value is used as default value.
|
15
|
+
def attribute(attr_def, &init)
|
16
|
+
if Hash === attr_def
|
17
|
+
name, defval = attr_def.to_a.flatten
|
18
|
+
else
|
19
|
+
name = attr_def
|
20
|
+
end
|
21
|
+
|
22
|
+
class_eval do
|
23
|
+
attr_writer name
|
24
|
+
define_method("#{name}_defval") do
|
25
|
+
defval || (instance_eval(&init) if init)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class_eval <<-EOD
|
30
|
+
def #{name}
|
31
|
+
if defined? @#{name} then @#{name}
|
32
|
+
else @#{name} = #{name}_defval
|
33
|
+
end
|
34
|
+
end
|
35
|
+
EOD
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Utilrb.if_faster do
|
41
|
+
class Object
|
42
|
+
# :call-seq
|
43
|
+
# attribute :name => default_value
|
44
|
+
# attribute(:name) { default_value }
|
45
|
+
#
|
46
|
+
# In the first form, defines a read-write attribute
|
47
|
+
# named 'name' with default_value for default value.
|
48
|
+
# In the second form, the block is called if the attribute
|
49
|
+
# is read before it has been ever written, and its return
|
50
|
+
# value is used as default value.
|
51
|
+
def attribute(attr_def, &init)
|
52
|
+
if Hash === attr_def
|
53
|
+
name, defval = attr_def.to_a.flatten
|
54
|
+
else
|
55
|
+
name = attr_def
|
56
|
+
end
|
57
|
+
|
58
|
+
if is_singleton? || instance_of?(Module)
|
59
|
+
class_eval do
|
60
|
+
attr_writer name
|
61
|
+
define_method("#{name}_defval") do
|
62
|
+
defval || (instance_eval(&init) if init)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class_eval <<-EOD
|
67
|
+
def #{name}
|
68
|
+
if defined? @#{name} then @#{name}
|
69
|
+
else @#{name} = #{name}_defval
|
70
|
+
end
|
71
|
+
end
|
72
|
+
EOD
|
73
|
+
else
|
74
|
+
class_eval { attr_writer name }
|
75
|
+
define_method(name) do
|
76
|
+
singleton_class.class_eval { attr_reader name }
|
77
|
+
instance_variable_set("@#{name}", defval || (instance_eval(&init) if init))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Object
|
85
|
+
def class_attribute(attr_def, &init)
|
86
|
+
singleton_class.class_eval { attribute(attr_def, &init) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'utilrb/object/address'
|
2
|
+
if RUBY_VERSION >= "1.9"
|
3
|
+
class Object
|
4
|
+
def has_singleton?; defined? @singleton_class end
|
5
|
+
def singleton_class
|
6
|
+
if defined? @singleton_class
|
7
|
+
return @singleton_class
|
8
|
+
else
|
9
|
+
@singleton_class = class << self
|
10
|
+
class << self
|
11
|
+
alias __ancestors__ ancestors
|
12
|
+
def ancestors; __ancestors__.unshift(self) end
|
13
|
+
end
|
14
|
+
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
@singleton_class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
class Object
|
24
|
+
def has_singleton?
|
25
|
+
defined? @singleton_class
|
26
|
+
end
|
27
|
+
def singleton_class
|
28
|
+
if defined? @singleton_class
|
29
|
+
return @singleton_class
|
30
|
+
else
|
31
|
+
klass = class << self; self end
|
32
|
+
instance = self
|
33
|
+
klass.class_eval do
|
34
|
+
@singleton_instance = instance
|
35
|
+
@superclass = instance.class
|
36
|
+
class << self
|
37
|
+
attr_reader :superclass
|
38
|
+
attr_reader :singleton_instance
|
39
|
+
def name
|
40
|
+
"#{@superclass.name}!0x#{@singleton_instance.address.to_s(16)}"
|
41
|
+
end
|
42
|
+
|
43
|
+
alias __ancestors__ ancestors
|
44
|
+
def ancestors; __ancestors__.unshift(self) end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@singleton_class = klass
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'utilrb/gc/force'
|
2
|
+
|
3
|
+
module ObjectStats
|
4
|
+
# Allocates no object
|
5
|
+
def self.count
|
6
|
+
count = 0
|
7
|
+
ObjectSpace.each_object { |obj| count += 1}
|
8
|
+
|
9
|
+
count
|
10
|
+
end
|
11
|
+
|
12
|
+
# Allocates 1 Hash, which is included in the count
|
13
|
+
def self.count_by_class
|
14
|
+
by_class = Hash.new(0)
|
15
|
+
ObjectSpace.each_object { |obj|
|
16
|
+
by_class[obj.class] += 1
|
17
|
+
by_class
|
18
|
+
}
|
19
|
+
by_class
|
20
|
+
end
|
21
|
+
|
22
|
+
# Profiles the memory allocation in the block
|
23
|
+
# If alive is true, then only non-gcable objects
|
24
|
+
# are returned.
|
25
|
+
def self.profile(alive = false)
|
26
|
+
already_disabled = GC.disable
|
27
|
+
before = count_by_class
|
28
|
+
yield
|
29
|
+
after = count_by_class
|
30
|
+
GC.enable unless already_disabled
|
31
|
+
|
32
|
+
after[Hash] -= 1 # Correction for the call of count_by_class
|
33
|
+
profile = before.
|
34
|
+
merge(after) { |klass, old, new| new - old }.
|
35
|
+
delete_if { |klass, count| count == 0 }
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.stats(filter = nil)
|
39
|
+
total_count = 0
|
40
|
+
output = ""
|
41
|
+
count_by_class.each do |klass, obj_count|
|
42
|
+
total_count += obj_count
|
43
|
+
if !filter || klass.name =~ filter
|
44
|
+
output << klass.name << " " << obj_count.to_s << "\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
(output << "Total object count: #{total_count}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
data/lib/utilrb/time.rb
ADDED