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/Changes.txt
CHANGED
@@ -1,74 +1,90 @@
|
|
1
|
+
=== Version 1.1
|
2
|
+
|
3
|
+
* changes and fixes:
|
4
|
+
- fix documentation here and there
|
5
|
+
- make sure all defined #to_s methods handle recursion
|
6
|
+
- defined Module#cached_enum: returns an Enumerator method for
|
7
|
+
a "each_" method, cachin the enumerator object. Works also
|
8
|
+
for iteration with one argument (caching one Enumerator by
|
9
|
+
argument)
|
10
|
+
- fix Time.from_hms when the millisecond field has leading zeroes
|
11
|
+
- optimizations here and there:
|
12
|
+
* Array#to_value_set does not rely on rb_iterate
|
13
|
+
* specific implementation of ValueSet#dup
|
14
|
+
* more generally, avoid object allocation where possible
|
15
|
+
- ColumnFormatter object: formats CSV output for display
|
16
|
+
|
1
17
|
=== Version 1.0
|
2
18
|
Bumped version number because of incompatible changes. Make it 1.0 since
|
3
19
|
having 0. version numbers for this kind of library is completely meaningless
|
4
20
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
* incompatible changes:
|
22
|
+
- Enumerable#+ acts on too many objects. Define it only on Enumerable::Enumerator
|
23
|
+
- remove automatic convertion into ValueSet for methods which were doing it
|
24
|
+
- removed Queue#get: fastthread is now the preferred threading library in
|
25
|
+
Ruby 1.8.6, so Queue#get cannot be implemented anymore
|
26
|
+
|
27
|
+
* new features:
|
28
|
+
- PkgConfig class
|
29
|
+
- define Proc#same_body?, Proc#file and Proc#line
|
30
|
+
- Module#attr_predicate
|
31
|
+
- 'since', 'until' and block parameters for Exception#full_message
|
32
|
+
- ValueSet#intersects? (more efficient than checking that the intersection
|
33
|
+
is empty)
|
34
|
+
|
35
|
+
* changes and fixes:
|
36
|
+
- properly handle singleton classes in has_ancestor?
|
37
|
+
- properly handle recursion in ValueSet#to_s and Array#to_s
|
38
|
+
- add the 'setup' task on the Rakefile to build the C extension
|
39
|
+
- make Module#has_ancestor? work event if the C extension is not built
|
24
40
|
|
25
41
|
=== Version 0.2.3
|
26
42
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
43
|
+
* new features:
|
44
|
+
- BenchmarkAllocation, a Benchmark-like interface to benchmark object
|
45
|
+
allocation
|
46
|
+
- ValueSet#delete_if
|
47
|
+
- Time#from_hms takes a time as "h:m:s.ms" and builds the corresponding
|
48
|
+
Time object. Note that "s", "s.ms", "m:s", "m:s.ms", ... are accepted
|
49
|
+
formats
|
50
|
+
|
51
|
+
* changes and fixes:
|
52
|
+
- define Queue#get only if we are using Ruby's core Queue. The current
|
53
|
+
implementation is incompatible with fastthread for instance (and
|
54
|
+
fastthhread's maintainer does not want #get on its Queue). Included
|
55
|
+
a patch to define Queue#get on fastthread
|
56
|
+
- fix ValueSet#== raising if the argument is not a ValueSet
|
57
|
+
- fix brain-dead SequenceEnumerator#+, which was changing its receiver
|
42
58
|
|
43
59
|
=== Version 0.2.2
|
44
60
|
The "don't forget to bump version number" release. 0.2 was supposed to be 0.2.1
|
45
61
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
62
|
+
* new features:
|
63
|
+
- Queue#get: waits for the queue to be not empty and returns all elements at
|
64
|
+
once. A non_block parameter is given which makes get() return [] if the
|
65
|
+
queue is empty
|
50
66
|
|
51
|
-
|
52
|
-
|
53
|
-
|
67
|
+
* changes:
|
68
|
+
- Array#to_s and Hash#to_s now enclose the result in [] and {}. The difference
|
69
|
+
with #inspect is that we call #to_s on the elements.
|
54
70
|
|
55
71
|
=== Version 0.2.1
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
72
|
+
* new features:
|
73
|
+
- Kernel#is_singleton?
|
74
|
+
- Module#inherited_enumerable (class_inherited_enumerable on steroids)
|
75
|
+
- Module#attribute() can be used in singleton classes (previously we had to
|
76
|
+
call class_attribute() in the class itself)
|
77
|
+
- UnboundMethod#call(obj, *args, &block) calls the method on obj with
|
78
|
+
the provided arguments (does m.bind(obj).call(*args, &block))
|
79
|
+
|
80
|
+
* changes:
|
81
|
+
- changed semantics of Module::include for inclusion of modules in modules:
|
82
|
+
the source_module::ClassExtension gets included in
|
83
|
+
target_module::ClassExtension. Previously, it was extending the target's
|
84
|
+
singleton class. This way, Module really acts as a mixin for both class
|
85
|
+
methods and instance methods
|
70
86
|
|
71
87
|
=== Version 0.1
|
72
|
-
|
88
|
+
* Initial release
|
73
89
|
|
74
90
|
|
data/Manifest.txt
CHANGED
@@ -13,11 +13,13 @@ ext/value_set.cc
|
|
13
13
|
lib/utilrb.rb
|
14
14
|
lib/utilrb/array.rb
|
15
15
|
lib/utilrb/array/to_s.rb
|
16
|
+
lib/utilrb/column_formatter.rb
|
16
17
|
lib/utilrb/common.rb
|
17
18
|
lib/utilrb/enumerable.rb
|
18
19
|
lib/utilrb/enumerable/null.rb
|
19
20
|
lib/utilrb/enumerable/random_element.rb
|
20
21
|
lib/utilrb/enumerable/sequence.rb
|
22
|
+
lib/utilrb/enumerable/to_s_helper.rb
|
21
23
|
lib/utilrb/enumerable/uniq.rb
|
22
24
|
lib/utilrb/exception.rb
|
23
25
|
lib/utilrb/exception/full_message.rb
|
@@ -40,6 +42,7 @@ lib/utilrb/module.rb
|
|
40
42
|
lib/utilrb/module/ancestor_p.rb
|
41
43
|
lib/utilrb/module/attr_enumerable.rb
|
42
44
|
lib/utilrb/module/attr_predicate.rb
|
45
|
+
lib/utilrb/module/cached_enum.rb
|
43
46
|
lib/utilrb/module/define_method.rb
|
44
47
|
lib/utilrb/module/include.rb
|
45
48
|
lib/utilrb/module/inherited_enumerable.rb
|
@@ -70,5 +73,6 @@ test/test_object.rb
|
|
70
73
|
test/test_objectstats.rb
|
71
74
|
test/test_pkgconfig.rb
|
72
75
|
test/test_proc.rb
|
76
|
+
test/test_set.rb
|
73
77
|
test/test_time.rb
|
74
78
|
test/test_unbound_method.rb
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ Hoe.new('utilrb', Utilrb::VERSION) do |p|
|
|
20
20
|
p.summary = 'Yet another Ruby toolkit'
|
21
21
|
p.description = p.paragraphs_of('README.txt', 3..6).join("\n\n")
|
22
22
|
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
23
|
-
p.changes = p.paragraphs_of('Changes.txt', 0..
|
23
|
+
p.changes = p.paragraphs_of('Changes.txt', 0..1).join("\n\n")
|
24
24
|
|
25
25
|
p.extra_deps << 'facets'
|
26
26
|
p.rdoc_pattern = /(ext\/.*cc$|lib)|txt/
|
data/ext/faster.cc
CHANGED
data/ext/value_set.cc
CHANGED
@@ -101,6 +101,22 @@ static VALUE value_set_include_p(VALUE vself, VALUE vother)
|
|
101
101
|
*/
|
102
102
|
static VALUE value_set_to_value_set(VALUE self) { return self; }
|
103
103
|
|
104
|
+
/* call-seq:
|
105
|
+
* set.dup => other_set
|
106
|
+
*
|
107
|
+
* Duplicates this set, without duplicating the pointed-to objects
|
108
|
+
*/
|
109
|
+
static VALUE value_set_dup(VALUE vself, VALUE vother)
|
110
|
+
{
|
111
|
+
ValueSet const& self = get_wrapped_set(vself);
|
112
|
+
VALUE vresult = rb_funcall(cValueSet, id_new, 0);
|
113
|
+
ValueSet& result = get_wrapped_set(vresult);
|
114
|
+
for (ValueSet::const_iterator it = self.begin(); it != self.end(); ++it)
|
115
|
+
result.insert(result.end(), *it);
|
116
|
+
|
117
|
+
return vresult;
|
118
|
+
}
|
119
|
+
|
104
120
|
/* call-seq:
|
105
121
|
* set.include_all?(other) => true or false
|
106
122
|
*
|
@@ -302,6 +318,24 @@ static VALUE enumerable_to_value_set_i(VALUE i, VALUE* memo)
|
|
302
318
|
return Qnil;
|
303
319
|
}
|
304
320
|
|
321
|
+
/* call-seq:
|
322
|
+
* to_value_set => value_set
|
323
|
+
*
|
324
|
+
* Converts this array into a ValueSet object
|
325
|
+
*/
|
326
|
+
static VALUE array_to_value_set(VALUE self)
|
327
|
+
{
|
328
|
+
VALUE vresult = rb_funcall(cValueSet, id_new, 0);
|
329
|
+
ValueSet& result = get_wrapped_set(vresult);
|
330
|
+
|
331
|
+
VALUE* ptr = RARRAY(self)->ptr;
|
332
|
+
long size = RARRAY(self)->len;
|
333
|
+
for (int i = 0; i < size; ++i)
|
334
|
+
result.insert(ptr[i]);
|
335
|
+
|
336
|
+
return vresult;
|
337
|
+
}
|
338
|
+
|
305
339
|
/* call-seq:
|
306
340
|
* enum.to_value_set => value_set
|
307
341
|
*
|
@@ -328,6 +362,7 @@ static VALUE enumerable_to_value_set(VALUE self)
|
|
328
362
|
extern "C" void Init_value_set()
|
329
363
|
{
|
330
364
|
rb_define_method(rb_mEnumerable, "to_value_set", RUBY_METHOD_FUNC(enumerable_to_value_set), 0);
|
365
|
+
rb_define_method(rb_cArray, "to_value_set", RUBY_METHOD_FUNC(array_to_value_set), 0);
|
331
366
|
|
332
367
|
cValueSet = rb_define_class("ValueSet", rb_cObject);
|
333
368
|
id_new = rb_intern("new");
|
@@ -344,6 +379,7 @@ extern "C" void Init_value_set()
|
|
344
379
|
rb_define_method(cValueSet, "delete", RUBY_METHOD_FUNC(value_set_delete), 1);
|
345
380
|
rb_define_method(cValueSet, "==", RUBY_METHOD_FUNC(value_set_equal), 1);
|
346
381
|
rb_define_method(cValueSet, "to_value_set", RUBY_METHOD_FUNC(value_set_to_value_set), 0);
|
382
|
+
rb_define_method(cValueSet, "dup", RUBY_METHOD_FUNC(value_set_dup), 0);
|
347
383
|
rb_define_method(cValueSet, "empty?", RUBY_METHOD_FUNC(value_set_empty_p), 0);
|
348
384
|
rb_define_method(cValueSet, "size", RUBY_METHOD_FUNC(value_set_size), 0);
|
349
385
|
rb_define_method(cValueSet, "clear", RUBY_METHOD_FUNC(value_set_clear), 0);
|
data/lib/utilrb/array/to_s.rb
CHANGED
@@ -1,17 +1,10 @@
|
|
1
|
+
require 'utilrb/enumerable/to_s_helper'
|
1
2
|
class Array
|
2
3
|
# Displays arrays as [ a, b, [c, d], ... ] instead of the standard #join
|
3
4
|
# Unlike #inspect, it calls #to_s on the elements too
|
4
5
|
def to_s
|
5
|
-
|
6
|
-
|
7
|
-
"..."
|
8
|
-
else
|
9
|
-
begin
|
10
|
-
stack.push object_id
|
11
|
-
"[" << map { |obj| obj.to_s }.join(", ") << "]"
|
12
|
-
ensure
|
13
|
-
stack.pop
|
14
|
-
end
|
6
|
+
EnumerableToString.to_s_helper(self, '[', ']') do |obj|
|
7
|
+
obj.to_s
|
15
8
|
end
|
16
9
|
end
|
17
10
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Displays a set of data into a column-formatted table
|
2
|
+
class ColumnFormatter
|
3
|
+
MARGIN = 3
|
4
|
+
SCREEN_WIDTH = 90
|
5
|
+
|
6
|
+
# data is an array of hashes. Each line of the array is a line to display,
|
7
|
+
# a hash being a column_name => value data set. The special 'label' column
|
8
|
+
# name is used to give a name to each line.
|
9
|
+
#
|
10
|
+
# If a block is given, the method yields the column names and the block must
|
11
|
+
# return the array of columns to be displayed, sorted into the order of
|
12
|
+
# the columns.
|
13
|
+
def self.from_hashes(data, io = STDOUT, margin = MARGIN, screen_width = SCREEN_WIDTH)
|
14
|
+
width = Hash.new
|
15
|
+
|
16
|
+
# First, determine the columns width and height, and
|
17
|
+
# convert data into strings
|
18
|
+
data = data.map do |line_data|
|
19
|
+
line_data = line_data.inject({}) do |h, (label, data)|
|
20
|
+
h[label.to_s] = data.to_s
|
21
|
+
h
|
22
|
+
end
|
23
|
+
|
24
|
+
line_data.each do |label, data|
|
25
|
+
unless width.has_key?(label)
|
26
|
+
width[label] = label.length
|
27
|
+
end
|
28
|
+
|
29
|
+
if width[label] < data.length
|
30
|
+
width[label] = data.length
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Then ask the user to sort the keys for us
|
36
|
+
names = width.keys.dup
|
37
|
+
names.delete('label')
|
38
|
+
names = if block_given? then yield(names)
|
39
|
+
else names.sort
|
40
|
+
end
|
41
|
+
|
42
|
+
# Finally, format and display
|
43
|
+
while !names.empty?
|
44
|
+
# we determine the set of columns to display
|
45
|
+
if width.has_key?('label')
|
46
|
+
line_n = ['label']
|
47
|
+
line_w = width['label']
|
48
|
+
format = ["% #{line_w}s"]
|
49
|
+
else
|
50
|
+
line_n = []
|
51
|
+
line_w = 0
|
52
|
+
format = []
|
53
|
+
end
|
54
|
+
|
55
|
+
while !names.empty? && (line_w < screen_width)
|
56
|
+
col_n = names.shift
|
57
|
+
col_w = width[col_n]
|
58
|
+
line_n << col_n
|
59
|
+
line_w += col_w
|
60
|
+
format << "% #{col_w}s"
|
61
|
+
end
|
62
|
+
|
63
|
+
format = format.join(" " * margin)
|
64
|
+
io.puts format % line_n
|
65
|
+
data.each do |line_data|
|
66
|
+
line_data = line_data.values_at(*line_n)
|
67
|
+
line_data.map! { |v| v || '-' }
|
68
|
+
io.puts format % line_data
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/lib/utilrb/common.rb
CHANGED
@@ -2,9 +2,9 @@ require 'enumerator'
|
|
2
2
|
|
3
3
|
# An enumerator which iterates on multiple iterators in sequence
|
4
4
|
#
|
5
|
-
# ([1, 2].enum_for + [2, 3].enum_for).
|
5
|
+
# ([1, 2].enum_for + [2, 3].enum_for).to_a # => [1, 2, 2, 3]
|
6
6
|
#
|
7
|
-
# See also Enumerator#+
|
7
|
+
# See also Enumerable::Enumerator#+
|
8
8
|
class SequenceEnumerator
|
9
9
|
def initialize; @sequence = Array.new end
|
10
10
|
|
@@ -22,7 +22,7 @@ end
|
|
22
22
|
|
23
23
|
class Enumerable::Enumerator # :nodoc
|
24
24
|
# Builds a sequence of enumeration object.
|
25
|
-
# ([1, 2].enum_for + [2, 3]).
|
25
|
+
# ([1, 2].enum_for + [2, 3].enum_for).to_a # => [1, 2, 2, 3]
|
26
26
|
def +(other_enumerator) # :nodoc
|
27
27
|
SequenceEnumerator.new << self << other_enumerator
|
28
28
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module EnumerableToString
|
2
|
+
# This method is a generic implementaion of #to_s on enumerables.
|
3
|
+
def self.to_s_helper(enumerable, start, stop)
|
4
|
+
stack = (Thread.current[:to_s_helper] ||= [])
|
5
|
+
if stack.include?(enumerable.object_id)
|
6
|
+
"..."
|
7
|
+
else
|
8
|
+
begin
|
9
|
+
stack.push enumerable.object_id
|
10
|
+
start.dup << enumerable.map { |el| yield(el) }.join(", ") << stop
|
11
|
+
ensure
|
12
|
+
stack.pop
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -2,11 +2,19 @@ require 'utilrb/common'
|
|
2
2
|
require 'enumerator'
|
3
3
|
require 'set'
|
4
4
|
|
5
|
-
# Enumerator object which removes duplicate entries.
|
6
|
-
#
|
5
|
+
# Enumerator object which removes duplicate entries.
|
6
|
+
# See also Object#enum_uniq and Enumerable#each_uniq
|
7
7
|
class UniqEnumerator < Enumerable::Enumerator
|
8
|
-
|
9
|
-
|
8
|
+
# Creates the enumerator on +obj+ using the method +enum_with+ to
|
9
|
+
# enumerate. The method will be called with the arguments in +args+.
|
10
|
+
#
|
11
|
+
# If +key+ is given, it is a proc object which should return the key on
|
12
|
+
# which we base ourselves to compare two objects. If it is not given,
|
13
|
+
# UniqEnumerator uses the object itself
|
14
|
+
#
|
15
|
+
# See also Object#enum_uniq and Enumerable#each_uniq
|
16
|
+
def initialize(obj, enum_with, args, key = nil)
|
17
|
+
super(obj, enum_with, *args)
|
10
18
|
@key = key
|
11
19
|
@result = Hash.new
|
12
20
|
end
|
@@ -29,12 +37,12 @@ class UniqEnumerator < Enumerable::Enumerator
|
|
29
37
|
self
|
30
38
|
end
|
31
39
|
end
|
32
|
-
|
33
|
-
include Enumerable
|
34
40
|
end
|
35
41
|
|
36
42
|
class Object
|
37
|
-
# Enumerate removing the duplicate
|
43
|
+
# Enumerate using the <tt>each(*args)</tt> method, removing the duplicate
|
44
|
+
# entries. If +filter+ is given, it should return an object the enumerator
|
45
|
+
# will compare for equality (instead of using the objects themselves)
|
38
46
|
def enum_uniq(enum_with = :each, *args, &filter)
|
39
47
|
UniqEnumerator.new(self, enum_with, args, filter)
|
40
48
|
end
|
@@ -43,10 +51,9 @@ end
|
|
43
51
|
Utilrb.unless_faster do
|
44
52
|
module Enumerable
|
45
53
|
# call-seq:
|
46
|
-
#
|
54
|
+
# each_uniq { |obj| ... }
|
47
55
|
#
|
48
56
|
# Yields all unique values found in +enum+
|
49
|
-
#
|
50
57
|
def each_uniq(&iterator)
|
51
58
|
seen = Set.new
|
52
59
|
each do |obj|
|