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 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
- * incompatible changes:
6
- - Enumerable#+ acts on too many objects. Define it only on Enumerable::Enumerator
7
- - remove automatic convertion into ValueSet for methods which were doing it
8
- - removed Queue#get: fastthread is now the preferred threading library in
9
- Ruby 1.8.6, so Queue#get cannot be implemented anymore
10
-
11
- * new features:
12
- - PkgConfig class
13
- - define Proc#same_body?, Proc#file and Proc#line
14
- - Module#attr_predicate
15
- - 'since', 'until' and block parameters for Exception#full_message
16
- - ValueSet#intersects? (more efficient than checking that the intersection
17
- is empty)
18
-
19
- * changes and fixes:
20
- - properly handle singleton classes in has_ancestor?
21
- - properly handle recursion in ValueSet#to_s and Array#to_s
22
- - add the 'setup' task on the Rakefile to build the C extension
23
- - make Module#has_ancestor? work event if the C extension is not built
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
- * new features:
28
- - BenchmarkAllocation, a Benchmark-like interface to benchmark object
29
- allocation
30
- - ValueSet#delete_if
31
- - Time#from_hms takes a time as "h:m:s.ms" and builds the corresponding
32
- Time object. Note that "s", "s.ms", "m:s", "m:s.ms", ... are accepted
33
- formats
34
-
35
- * changes and fixes:
36
- - define Queue#get only if we are using Ruby's core Queue. The current
37
- implementation is incompatible with fastthread for instance (and
38
- fastthhread's maintainer does not want #get on its Queue). Included
39
- a patch to define Queue#get on fastthread
40
- - fix ValueSet#== raising if the argument is not a ValueSet
41
- - fix brain-dead SequenceEnumerator#+, which was changing its receiver
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
- * new features:
47
- - Queue#get: waits for the queue to be not empty and returns all elements at
48
- once. A non_block parameter is given which makes get() return [] if the
49
- queue is empty
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
- * changes:
52
- - Array#to_s and Hash#to_s now enclose the result in [] and {}. The difference
53
- with #inspect is that we call #to_s on the elements.
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
- * new features:
57
- - Kernel#is_singleton?
58
- - Module#inherited_enumerable (class_inherited_enumerable on steroids)
59
- - Module#attribute() can be used in singleton classes (previously we had to
60
- call class_attribute() in the class itself)
61
- - UnboundMethod#call(obj, *args, &block) calls the method on obj with
62
- the provided arguments (does m.bind(obj).call(*args, &block))
63
-
64
- * changes:
65
- - changed semantics of Module::include for inclusion of modules in modules:
66
- the source_module::ClassExtension gets included in
67
- target_module::ClassExtension. Previously, it was extending the target's
68
- singleton class. This way, Module really acts as a mixin for both class
69
- methods and instance methods
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
- * Initial release
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..3).join("\n\n")
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
@@ -76,7 +76,7 @@ static VALUE proc_file(VALUE self)
76
76
  }
77
77
 
78
78
  /* call-seq:
79
- * proc.file
79
+ * proc.line
80
80
  *
81
81
  * Returns the line at which the proc body is defined, or nil
82
82
  */
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);
@@ -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
- stack = (Thread.current[:array_to_s] ||= [])
6
- if stack.include?(object_id)
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
@@ -1,5 +1,5 @@
1
1
  module Utilrb
2
- VERSION = "1.0" unless defined? Utilrb::VERSION
2
+ VERSION = "1.1" unless defined? Utilrb::VERSION
3
3
 
4
4
  unless defined? UTILRB_FASTER_MODE
5
5
  if ENV['UTILRB_FASTER_MODE'] == 'no'
@@ -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).each => 1, 2, 2, 3
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]).each => 1, 2, 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. See
6
- # Kernel#each_uniq
5
+ # Enumerator object which removes duplicate entries.
6
+ # See also Object#enum_uniq and Enumerable#each_uniq
7
7
  class UniqEnumerator < Enumerable::Enumerator
8
- def initialize(root, enum_with, args, key = nil)
9
- super(root, enum_with, *args)
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 entries
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
- # enum.each_uniq { |obj| ... }
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|