utilrb 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changes.txt +72 -56
- data/Manifest.txt +4 -0
- data/Rakefile +1 -1
- data/ext/faster.cc +1 -1
- data/ext/value_set.cc +36 -0
- data/lib/utilrb/array/to_s.rb +3 -10
- data/lib/utilrb/column_formatter.rb +73 -0
- data/lib/utilrb/common.rb +1 -1
- data/lib/utilrb/enumerable/sequence.rb +3 -3
- data/lib/utilrb/enumerable/to_s_helper.rb +17 -0
- data/lib/utilrb/enumerable/uniq.rb +16 -9
- data/lib/utilrb/hash/to_s.rb +4 -1
- data/lib/utilrb/kernel/options.rb +3 -3
- data/lib/utilrb/logger/forward.rb +4 -0
- data/lib/utilrb/module/ancestor_p.rb +2 -2
- data/lib/utilrb/module/attr_enumerable.rb +21 -7
- data/lib/utilrb/module/attr_predicate.rb +4 -3
- data/lib/utilrb/module/cached_enum.rb +26 -0
- data/lib/utilrb/module/define_method.rb +1 -1
- data/lib/utilrb/module/include.rb +5 -5
- data/lib/utilrb/module/inherited_enumerable.rb +106 -47
- data/lib/utilrb/object/attribute.rb +14 -28
- data/lib/utilrb/object/singleton_class.rb +9 -11
- data/lib/utilrb/objectstats.rb +20 -53
- data/lib/utilrb/set/to_s.rb +5 -1
- data/lib/utilrb/time/to_hms.rb +15 -12
- data/lib/utilrb/unbound_method/call.rb +2 -0
- data/lib/utilrb/value_set.rb +5 -10
- data/test/test_array.rb +6 -0
- data/test/test_enumerable.rb +14 -4
- data/test/test_hash.rb +9 -1
- data/test/test_objectstats.rb +11 -4
- data/test/test_set.rb +19 -0
- data/test/test_time.rb +4 -1
- metadata +17 -8
data/lib/utilrb/hash/to_s.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'utilrb/enumerable/to_s_helper'
|
1
2
|
class Hash
|
2
3
|
# Displays hashes as { a => A, b => B, ... } instead of the standard #join
|
3
4
|
# Unlike #inspect, it calls #to_s on the elements too
|
4
5
|
def to_s
|
5
|
-
|
6
|
+
EnumerableToString.to_s_helper(self, '{', '}') do |k, v|
|
7
|
+
"#{k} => #{v}"
|
8
|
+
end
|
6
9
|
end
|
7
10
|
end
|
8
11
|
|
@@ -6,8 +6,8 @@ module Kernel
|
|
6
6
|
# converted to symbols for consistency.
|
7
7
|
#
|
8
8
|
# The following rules apply:
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# * if a hash is given, non-nil values are treated as default values.
|
10
|
+
# * an array is equivalent to a hash where all values are 'nil'
|
11
11
|
#
|
12
12
|
# See #validate_options and #filter_and_validate_options
|
13
13
|
#
|
@@ -54,7 +54,7 @@ module Kernel
|
|
54
54
|
end
|
55
55
|
|
56
56
|
# call-seq:
|
57
|
-
# validate_option(options, name, required, message) {
|
57
|
+
# validate_option(options, name, required, message) { |v| ... }
|
58
58
|
# validate_option(options, name, required, message)
|
59
59
|
#
|
60
60
|
# Validates option +name+ in the +options+ hash. If required is true,
|
@@ -1,9 +1,13 @@
|
|
1
1
|
class Logger
|
2
2
|
# Forward logger output methods to the logger attribute, so that
|
3
3
|
# we can do
|
4
|
+
# module MyModule
|
5
|
+
# extend Logger::Forward
|
6
|
+
# end
|
4
7
|
# MyModule.debug "debug_info"
|
5
8
|
# instead of
|
6
9
|
# MyModule.logger.debug "debug_info"
|
10
|
+
#
|
7
11
|
module Forward
|
8
12
|
[ :debug, :info, :warn, :error, :fatal, :unknown ].each do |level|
|
9
13
|
class_eval <<-EOF
|
@@ -12,12 +12,12 @@ class Module
|
|
12
12
|
# singleton.has_ancestor?(MyClass) # => true
|
13
13
|
#
|
14
14
|
def has_ancestor?(klass)
|
15
|
-
self
|
15
|
+
self <= klass || (is_singleton? && superclass.has_ancestor?(klass))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
Utilrb.unless_faster do
|
19
19
|
def has_ancestor?(klass) # :nodoc:
|
20
|
-
self
|
20
|
+
self <= klass || superclass == klass || superclass < klass
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -1,14 +1,27 @@
|
|
1
1
|
require 'utilrb/object/attribute'
|
2
2
|
|
3
3
|
class Module
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# Support for attributes that are enumerables. This methods defines two
|
5
|
+
# methods:
|
6
|
+
# obj.attr_name # => enumerable
|
7
|
+
# obj.each_name(key = nil) { |value| ... } # => obj
|
7
8
|
#
|
8
|
-
# The
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
9
|
+
# The first one returns the enumerable object itself. The second one
|
10
|
+
# iterates on the values in attr_name. If +key+ is not nil, then #attr_name
|
11
|
+
# is supposed to be a hash of enumerables, and +key+ is used to select the
|
12
|
+
# enumerable to iterate on.
|
13
|
+
#
|
14
|
+
# The following calls are equivalent
|
15
|
+
# obj.attr_name.each { |value| ... }
|
16
|
+
# obj.each_name { |value| ... }
|
17
|
+
#
|
18
|
+
# And these two are equivalent:
|
19
|
+
# obj.attr_name[key].each { |value| ... }
|
20
|
+
# obj.each_name(key) { |value| ... }
|
21
|
+
#
|
22
|
+
# +enumerator+ is the name of the enumeration method we should use.
|
23
|
+
# +init_block+, if given, should return the value at which we should
|
24
|
+
# initialize #attr_name.
|
12
25
|
def attr_enumerable(name, attr_name = name, enumerator = :each, &init_block)
|
13
26
|
class_eval do
|
14
27
|
attribute(attr_name, &init_block)
|
@@ -21,6 +34,7 @@ class Module
|
|
21
34
|
else
|
22
35
|
#{attr_name}.#{enumerator}(&iterator)
|
23
36
|
end
|
37
|
+
self
|
24
38
|
end
|
25
39
|
EOF
|
26
40
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
class Module
|
2
|
-
# Defines a +name
|
3
|
-
#
|
2
|
+
# Defines a +name?+ predicate, and if writable is true a #name= method.
|
3
|
+
# Note that +name+ can end with '?', in which case the ending '?' is
|
4
|
+
# removed.
|
4
5
|
#
|
5
|
-
# The
|
6
|
+
# The methods use the @name instance variable internally
|
6
7
|
def attr_predicate(name, writable = false)
|
7
8
|
attr_name = name.to_s.gsub(/\?$/, '')
|
8
9
|
attr_reader attr_name
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Module
|
2
|
+
# Creates <tt>enum_#{name}</tt> method which returs an Enumerator object
|
3
|
+
# for the <tt>each_#{enum_name}</tt> method. This enumerator is created
|
4
|
+
# once.
|
5
|
+
#
|
6
|
+
# If +with_arg+ is true, it is supposed that the 'each_' method requires
|
7
|
+
# one argument, which is given in argument of the 'enum' method. In that
|
8
|
+
# case, an enumerator is created for each argument
|
9
|
+
def cached_enum(enum_name, name, with_arg)
|
10
|
+
if with_arg
|
11
|
+
class_eval <<-EOD
|
12
|
+
def enum_#{name}(arg)
|
13
|
+
@enum_#{name} ||= Hash.new
|
14
|
+
@enum_#{name}[arg] ||= enum_for(:each_#{enum_name}, arg)
|
15
|
+
end
|
16
|
+
EOD
|
17
|
+
else
|
18
|
+
class_eval <<-EOD
|
19
|
+
def enum_#{name}
|
20
|
+
@enum_#{name} ||= enum_for(:each_#{enum_name})
|
21
|
+
end
|
22
|
+
EOD
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -16,7 +16,7 @@ class Module
|
|
16
16
|
# define_method_with_block('my_method') do |block, *args|
|
17
17
|
# end
|
18
18
|
#
|
19
|
-
# +block+ is +nil+ if no block is given
|
19
|
+
# +block+ is +nil+ if no block is given during the method call
|
20
20
|
#
|
21
21
|
def define_method_with_block(name, &mdef)
|
22
22
|
class_eval <<-EOD
|
@@ -6,11 +6,11 @@ class Module
|
|
6
6
|
# Includes a module in this one, with support for class extensions
|
7
7
|
#
|
8
8
|
# If a module defines a ClassExtension submodule, then
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
14
|
def include(mod)
|
15
15
|
__instance_include__ mod
|
16
16
|
return unless mod.const_defined?(:ClassExtension)
|
@@ -15,37 +15,54 @@ class Module
|
|
15
15
|
class_eval <<-EOF
|
16
16
|
def each_#{name}(key = nil, uniq = true)
|
17
17
|
if key
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
for klass in ancestors
|
19
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
20
|
+
if klass.#{attribute_name}.has_key?(key)
|
21
|
+
yield(klass.#{attribute_name}[key])
|
22
|
+
return self if uniq
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
elsif !uniq
|
27
|
+
for klass in ancestors
|
28
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
29
|
+
klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
|
30
|
+
end
|
21
31
|
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
32
|
else
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
33
|
+
seen = Set.new
|
34
|
+
for klass in ancestors
|
35
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
36
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |el|
|
37
|
+
unless seen.include?(el.first)
|
38
|
+
seen << el.first
|
39
|
+
yield(el)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
31
45
|
end
|
32
46
|
self
|
33
47
|
end
|
34
48
|
def has_#{name}?(key)
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
for klass in ancestors
|
50
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
51
|
+
return true if klass.#{attribute_name}.has_key?(key)
|
52
|
+
end
|
38
53
|
end
|
54
|
+
false
|
39
55
|
end
|
40
56
|
EOF
|
41
57
|
else
|
42
58
|
class_eval <<-EOF
|
43
|
-
def each_#{name}
|
44
|
-
|
45
|
-
|
46
|
-
|
59
|
+
def each_#{name}
|
60
|
+
for klass in ancestors
|
61
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
62
|
+
klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
|
63
|
+
end
|
47
64
|
end
|
48
|
-
|
65
|
+
self
|
49
66
|
end
|
50
67
|
EOF
|
51
68
|
end
|
@@ -69,38 +86,80 @@ class Module
|
|
69
86
|
# define a +each_value+ method). +attribute_name+ defines the attribute
|
70
87
|
# name. +init+ is a block called to initialize the attribute.
|
71
88
|
# Valid options in +options+ are:
|
72
|
-
# map::
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
89
|
+
# map::
|
90
|
+
# If true, the attribute should respond to +[]+. In that case, the
|
91
|
+
# enumeration method is each_value(key = nil, uniq = false) If +key+ is
|
92
|
+
# given, we iterate on the values given by <tt>attribute[key]</tt>. If
|
93
|
+
# +uniq+ is true, the enumeration will yield at most one value for each
|
94
|
+
# +key+ found (so, if both +key+ and +uniq+ are given, the enumeration
|
95
|
+
# yields at most one value). See the examples below
|
96
|
+
# enum_with:: the enumeration method of the enumerable, if it is not +each+
|
97
|
+
#
|
98
|
+
# === Example
|
99
|
+
# Let's define some classes and look at the ancestor chain
|
100
|
+
#
|
101
|
+
# class A; end
|
102
|
+
# module M; end
|
103
|
+
# class B < A; include M end
|
104
|
+
# A.ancestors # => [A, Object, Kernel]
|
105
|
+
# B.ancestors # => [B, M, A, Object, Kernel]
|
106
|
+
#
|
107
|
+
# ==== Attributes for which 'map' is not set
|
108
|
+
#
|
109
|
+
# class A
|
110
|
+
# inherited_enumerable("value", "values") do
|
111
|
+
# Array.new
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
# module M
|
115
|
+
# inherited_enumerable("mod") do
|
116
|
+
# Array.new
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# A.values << 1 # => [1]
|
121
|
+
# B.values << 2 # => [2]
|
122
|
+
# M.mod << 1 # => [1]
|
123
|
+
# b = B.new
|
124
|
+
# class << b
|
125
|
+
# self.values << 3 # => [3]
|
126
|
+
# self.mod << 4 # => [4]
|
127
|
+
# end
|
128
|
+
# M.mod << 2 # => [1, 2]
|
129
|
+
#
|
130
|
+
# A.enum_for(:each_value).to_a # => [1]
|
131
|
+
# B.enum_for(:each_value).to_a # => [2, 1]
|
132
|
+
# b.singleton_class.enum_for(:each_value).to_a # => [3, 2, 1]
|
133
|
+
# b.singleton_class.enum_for(:each_mod).to_a # => [4, 1, 2]
|
77
134
|
#
|
78
|
-
#
|
135
|
+
# ==== Attributes for which 'map' is set
|
79
136
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
137
|
+
# class A
|
138
|
+
# inherited_enumerable("mapped", "map", :map => true) do
|
139
|
+
# Hash.new { |h, k| h[k] = Array.new }
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# A.map['name'] = 'A' # => "A"
|
144
|
+
# A.map['universe'] = 42
|
145
|
+
# B.map['name'] = 'B' # => "B"
|
146
|
+
# B.map['half_of_it'] = 21
|
147
|
+
#
|
148
|
+
# Let's see what happens if we don't specify the key option.
|
149
|
+
# A.enum_for(:each_mapped).to_a # => [["name", "A"], ["universe", 42]]
|
150
|
+
# If the +uniq+ option is set (the default), we see only B's value for 'name'
|
151
|
+
# B.enum_for(:each_mapped).to_a # => [["half_of_it", 21], ["name", "B"], ["universe", 42]]
|
152
|
+
# If the +uniq+ option is not set, we see both values for 'name'. Note that
|
153
|
+
# since 'map' is a Hash, the order of keys in one class is not guaranteed.
|
154
|
+
# Nonetheless, we have the guarantee that values from B appear before
|
155
|
+
# those from A
|
156
|
+
# B.enum_for(:each_mapped, nil, false).to_a # => [["half_of_it", 21], ["name", "B"], ["name", "A"], ["universe", 42]]
|
90
157
|
#
|
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
158
|
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
159
|
+
# Now, let's see how 'key' behaves
|
160
|
+
# A.enum_for(:each_mapped, 'name').to_a # => ["A"]
|
161
|
+
# B.enum_for(:each_mapped, 'name').to_a # => ["B"]
|
162
|
+
# B.enum_for(:each_mapped, 'name', false).to_a # => ["B", "A"]
|
104
163
|
#
|
105
164
|
def inherited_enumerable(name, attribute_name = name, options = Hash.new, &init)
|
106
165
|
singleton_class.class_eval { define_inherited_enumerable(name, attribute_name, options, &init) }
|
@@ -39,49 +39,35 @@ end
|
|
39
39
|
|
40
40
|
Utilrb.if_faster do
|
41
41
|
class Object
|
42
|
-
# :
|
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)
|
42
|
+
def attribute(attr_def, &init) # :nodoc:
|
52
43
|
if Hash === attr_def
|
53
44
|
name, defval = attr_def.to_a.flatten
|
54
45
|
else
|
55
46
|
name = attr_def
|
56
47
|
end
|
57
48
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
define_method("#{name}_defval")
|
62
|
-
|
63
|
-
|
49
|
+
class_eval do
|
50
|
+
attr_writer name
|
51
|
+
if !defval && init
|
52
|
+
define_method("#{name}_defval", &init)
|
53
|
+
else
|
54
|
+
define_method("#{name}_defval") { defval }
|
64
55
|
end
|
56
|
+
end
|
65
57
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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))
|
58
|
+
class_eval <<-EOD
|
59
|
+
def #{name}
|
60
|
+
if defined? @#{name} then @#{name}
|
61
|
+
else @#{name} = #{name}_defval
|
78
62
|
end
|
79
63
|
end
|
64
|
+
EOD
|
80
65
|
end
|
81
66
|
end
|
82
67
|
end
|
83
68
|
|
84
69
|
class Object
|
70
|
+
# Like #attribute, but on the singleton class of this object
|
85
71
|
def class_attribute(attr_def, &init)
|
86
72
|
singleton_class.class_eval { attribute(attr_def, &init) }
|
87
73
|
end
|
@@ -7,20 +7,16 @@ end
|
|
7
7
|
|
8
8
|
if RUBY_VERSION >= "1.9"
|
9
9
|
class Object
|
10
|
-
|
11
|
-
#
|
12
|
-
# The first element of #ancestors on the returned singleton class is
|
13
|
-
# the singleton class itself. A #singleton_instance accessor is also
|
14
|
-
# defined, which returns the object instance the class is the singleton
|
15
|
-
# of.
|
16
|
-
def singleton_class
|
10
|
+
def singleton_class # :nodoc:
|
17
11
|
if defined? @singleton_class
|
18
12
|
return @singleton_class
|
19
13
|
else
|
20
14
|
@singleton_class = class << self
|
21
15
|
class << self
|
22
|
-
alias __ancestors__ ancestors
|
23
|
-
def ancestors
|
16
|
+
alias __ancestors__ ancestors # :nodoc:
|
17
|
+
def ancestors # :nodoc:
|
18
|
+
__ancestors__.unshift(self)
|
19
|
+
end
|
24
20
|
end
|
25
21
|
|
26
22
|
self
|
@@ -57,8 +53,10 @@ else
|
|
57
53
|
"#{@superclass.name}!0x#{@singleton_instance.address.to_s(16)}"
|
58
54
|
end
|
59
55
|
|
60
|
-
alias __ancestors__ ancestors
|
61
|
-
def ancestors
|
56
|
+
alias __ancestors__ ancestors # :nodoc:
|
57
|
+
def ancestors # :nodoc:
|
58
|
+
__ancestors__.unshift(self)
|
59
|
+
end
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|