utilrb 1.0 → 1.1
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 +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
|
|